comparison mercurial/osutil.c @ 5396:5105b119edd2

Add osutil module, containing a listdir function. This is similar to os.listdir, only it returns a sorted list of tuples.
author Bryan O'Sullivan <bos@serpentine.com>
date Fri, 05 Oct 2007 15:01:06 -0700
parents
children 11caa374f497
comparison
equal deleted inserted replaced
5395:e73a83af7926 5396:5105b119edd2
1 /*
2 osutil.c - native operating system services
3
4 Copyright 2007 Matt Mackall and others
5
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
8 */
9
10 #define _ATFILE_SOURCE
11 #define _LARGEFILE64_SOURCE
12 #include <alloca.h>
13 #include <dirent.h>
14 #include <fcntl.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19
20 #include "Python.h"
21
22 struct listdir_stat {
23 PyObject_HEAD
24 struct stat st;
25 };
26
27 #define listdir_slot(name) \
28 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
29 { \
30 return PyInt_FromLong(((struct listdir_stat *) self)->st.name); \
31 }
32
33 listdir_slot(st_dev);
34 listdir_slot(st_mode);
35 listdir_slot(st_nlink);
36 listdir_slot(st_size);
37 listdir_slot(st_mtime);
38 listdir_slot(st_ctime);
39
40 static struct PyGetSetDef listdir_stat_getsets[] = {
41 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
42 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
43 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
44 {"st_size", listdir_stat_st_size, 0, 0, 0},
45 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
46 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
47 {0, 0, 0, 0, 0}
48 };
49
50 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
51 {
52 return (*t->tp_alloc)(t, 0);
53 }
54
55 static void listdir_stat_dealloc(PyObject *o)
56 {
57 (*o->ob_type->tp_free)(o);
58 }
59
60 static PyTypeObject listdir_stat_type = {
61 PyObject_HEAD_INIT(NULL)
62 0, /*ob_size*/
63 "osutil.stat", /*tp_name*/
64 sizeof(struct listdir_stat), /*tp_basicsize*/
65 0, /*tp_itemsize*/
66 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
67 0, /*tp_print*/
68 0, /*tp_getattr*/
69 0, /*tp_setattr*/
70 0, /*tp_compare*/
71 0, /*tp_repr*/
72 0, /*tp_as_number*/
73 0, /*tp_as_sequence*/
74 0, /*tp_as_mapping*/
75 0, /*tp_hash */
76 0, /*tp_call*/
77 0, /*tp_str*/
78 0, /*tp_getattro*/
79 0, /*tp_setattro*/
80 0, /*tp_as_buffer*/
81 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
82 "stat objects", /* tp_doc */
83 0, /* tp_traverse */
84 0, /* tp_clear */
85 0, /* tp_richcompare */
86 0, /* tp_weaklistoffset */
87 0, /* tp_iter */
88 0, /* tp_iternext */
89 0, /* tp_methods */
90 0, /* tp_members */
91 listdir_stat_getsets, /* tp_getset */
92 0, /* tp_base */
93 0, /* tp_dict */
94 0, /* tp_descr_get */
95 0, /* tp_descr_set */
96 0, /* tp_dictoffset */
97 0, /* tp_init */
98 0, /* tp_alloc */
99 listdir_stat_new, /* tp_new */
100 };
101
102 static inline int mode_to_kind(int mode)
103 {
104 if (S_ISREG(mode)) return S_IFREG;
105 if (S_ISDIR(mode)) return S_IFDIR;
106 if (S_ISLNK(mode)) return S_IFLNK;
107 if (S_ISBLK(mode)) return S_IFBLK;
108 if (S_ISCHR(mode)) return S_IFCHR;
109 if (S_ISFIFO(mode)) return S_IFIFO;
110 if (S_ISSOCK(mode)) return S_IFSOCK;
111 return mode;
112 }
113
114 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
115 {
116 DIR *dir = NULL;
117 struct dirent64 *ent;
118 PyObject *list = NULL;
119 PyObject *ctor_args = NULL;
120 int all_kinds = 1;
121 char *full_path;
122 int path_len;
123 int do_stat;
124 char *path;
125 int ret;
126
127 {
128 static char *kwlist[] = { "path", "stat", NULL };
129 PyObject *statobj = NULL;
130
131 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|O:listdir", kwlist,
132 &path, &path_len, &statobj))
133 goto bail;
134
135 do_stat = statobj && PyObject_IsTrue(statobj);
136 }
137
138 if ((dir = opendir(path)) == NULL) {
139 list = PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
140 goto bail;
141 }
142
143 if ((list = PyList_New(0)) == NULL)
144 goto bail;
145
146 full_path = alloca(path_len + PATH_MAX + 2);
147 memcpy(full_path, path, path_len);
148 full_path[path_len] = '/';
149
150 while ((ent = readdir64(dir))) {
151 PyObject *name = NULL;
152 PyObject *py_kind = NULL;
153 PyObject *val = NULL;
154 unsigned char d_type;
155 int kind = -1;
156
157 if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
158 continue;
159
160 #ifdef DT_REG
161 if (do_stat)
162 d_type = 0;
163 else
164 d_type = ent->d_type;
165 #else
166 d_type = 0;
167 #endif
168
169 switch (d_type) {
170 #ifdef DT_REG
171 case DT_REG: kind = S_IFREG; break;
172 case DT_DIR: kind = S_IFDIR; break;
173 case DT_LNK: kind = S_IFLNK; break;
174 case DT_BLK: kind = S_IFBLK; break;
175 case DT_CHR: kind = S_IFCHR; break;
176 case DT_FIFO: kind = S_IFIFO; break;
177 case DT_SOCK: kind = S_IFSOCK; break;
178 #endif
179 default:
180 if (all_kinds)
181 all_kinds = 0;
182 break;
183 }
184
185 name = PyString_FromString(ent->d_name);
186 if (kind != -1)
187 py_kind = PyInt_FromLong(kind);
188 else {
189 py_kind = Py_None;
190 Py_INCREF(Py_None);
191 }
192
193 val = PyTuple_New(do_stat ? 3 : 2);
194
195 if (name == NULL || py_kind == NULL || val == NULL) {
196 Py_XDECREF(name);
197 Py_XDECREF(py_kind);
198 Py_XDECREF(val);
199
200 goto bail;
201 }
202
203 PyTuple_SET_ITEM(val, 0, name);
204 PyTuple_SET_ITEM(val, 1, py_kind);
205 if (do_stat) {
206 PyTuple_SET_ITEM(val, 2, Py_None);
207 Py_INCREF(Py_None);
208 }
209
210 PyList_Append(list, val);
211 Py_DECREF(val);
212 }
213
214 PyList_Sort(list);
215
216 if (do_stat || !all_kinds) {
217 ssize_t size = PyList_Size(list);
218 ssize_t i;
219 #ifdef AT_SYMLINK_NOFOLLOW
220 int dfd = dirfd(dir);
221 #endif
222
223 for (i = 0; i < size; i++) {
224 PyObject *elt = PyList_GetItem(list, i);
225 char *name = PyString_AsString(PyTuple_GET_ITEM(elt, 0));
226 PyObject *py_st = NULL;
227 PyObject *py_kind = PyTuple_GET_ITEM(elt, 1);
228 int kind;
229
230 kind = py_kind == Py_None ? -1 : PyInt_AsLong(py_kind);
231
232 if (kind != -1 && !do_stat)
233 continue;
234
235 strcpy(full_path + path_len + 1, name);
236
237 if (do_stat) {
238 struct listdir_stat *st;
239
240 if (ctor_args == NULL) {
241 ctor_args = PyTuple_New(0);
242 if (ctor_args == NULL)
243 goto bail;
244 }
245
246 st = (struct listdir_stat *)
247 PyObject_CallObject((PyObject *) &listdir_stat_type,
248 ctor_args);
249 if (st == NULL)
250 goto bail;
251 #ifdef AT_SYMLINK_NOFOLLOW
252 ret = fstatat(dfd, name, &st->st, AT_SYMLINK_NOFOLLOW);
253 #else
254 ret = lstat(full_path, &st->st);
255 #endif
256 if (ret == -1) {
257 list = PyErr_SetFromErrnoWithFilename(PyExc_OSError,
258 full_path);
259 goto bail;
260 }
261 if (kind == -1)
262 kind = mode_to_kind(st->st.st_mode);
263 py_st = (PyObject *) st;
264 } else {
265 struct stat buf;
266 #ifdef AT_SYMLINK_NOFOLLOW
267 ret = fstatat(dfd, ent->d_name, &buf, AT_SYMLINK_NOFOLLOW);
268 #else
269 ret = lstat(full_path, &buf);
270 #endif
271 if (ret == -1) {
272 list = PyErr_SetFromErrnoWithFilename(PyExc_OSError,
273 full_path);
274 goto bail;
275 }
276 if (kind == -1)
277 kind = mode_to_kind(buf.st_mode);
278 }
279
280 if (py_kind == Py_None && kind != -1) {
281 py_kind = PyInt_FromLong(kind);
282 if (py_kind == NULL)
283 goto bail;
284 Py_XDECREF(Py_None);
285 PyTuple_SET_ITEM(elt, 1, py_kind);
286 }
287
288 if (do_stat) {
289 if (py_st == NULL) {
290 py_st = Py_None;
291 Py_INCREF(Py_None);
292 }
293 PyTuple_SET_ITEM(elt, 2, py_st);
294 }
295 }
296 }
297
298 goto done;
299
300 bail:
301 Py_XDECREF(list);
302
303 done:
304 Py_XDECREF(ctor_args);
305 if (dir)
306 closedir(dir);
307
308 return list;
309 }
310
311 static char osutil_doc[] = "Native operating system services.";
312
313 static PyMethodDef methods[] = {
314 {"listdir", (PyCFunction) listdir, METH_VARARGS | METH_KEYWORDS,
315 "list a directory\n"},
316 {NULL, NULL}
317 };
318
319 PyMODINIT_FUNC initosutil(void)
320 {
321 if (PyType_Ready(&listdir_stat_type) == -1)
322 return;
323
324 Py_InitModule3("osutil", methods, osutil_doc);
325 }