Mercurial > hg > mercurial-crew-with-dirclash
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 } |