##// END OF EJS Templates
Add osutil module, containing a listdir function....
Bryan O'Sullivan -
r5396:5105b119 default
parent child Browse files
Show More
@@ -0,0 +1,325 b''
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 }
@@ -0,0 +1,37 b''
1 import os, stat
2
3 def _mode_to_kind(mode):
4 if stat.S_ISREG(mode): return stat.S_IFREG
5 if stat.S_ISDIR(mode): return stat.S_IFDIR
6 if stat.S_ISLNK(mode): return stat.S_IFLNK
7 if stat.S_ISBLK(mode): return stat.S_IFBLK
8 if stat.S_ISCHR(mode): return stat.S_IFCHR
9 if stat.S_ISFIFO(mode): return stat.S_IFIFO
10 if stat.S_ISSOCK(mode): return stat.S_IFSOCK
11 return mode
12
13 def listdir(path, stat=False):
14 '''listdir(path, stat=False) -> list_of_tuples
15
16 Return a sorted list containing information about the entries
17 in the directory.
18
19 If stat is True, each element is a 3-tuple:
20
21 (name, type, stat object)
22
23 Otherwise, each element is a 2-tuple:
24
25 (name, type)
26 '''
27 result = []
28 prefix = path + os.sep
29 names = os.listdir(path)
30 names.sort()
31 for fn in names:
32 st = os.lstat(prefix + fn)
33 if stat:
34 result.append((fn, _mode_to_kind(st.st_mode), st))
35 else:
36 result.append((fn, _mode_to_kind(st.st_mode)))
37 return result
@@ -10,7 +10,7 b' of the GNU General Public License, incor'
10 from node import *
10 from node import *
11 from i18n import _
11 from i18n import _
12 import struct, os, time, bisect, stat, strutil, util, re, errno, ignore
12 import struct, os, time, bisect, stat, strutil, util, re, errno, ignore
13 import cStringIO
13 import cStringIO, osutil
14
14
15 _unknown = ('?', 0, 0, 0)
15 _unknown = ('?', 0, 0, 0)
16 _format = ">cllll"
16 _format = ">cllll"
@@ -389,7 +389,7 b' class dirstate(object):'
389 common_prefix_len += 1
389 common_prefix_len += 1
390
390
391 normpath = util.normpath
391 normpath = util.normpath
392 listdir = os.listdir
392 listdir = osutil.listdir
393 lstat = os.lstat
393 lstat = os.lstat
394 bisect_left = bisect.bisect_left
394 bisect_left = bisect.bisect_left
395 isdir = os.path.isdir
395 isdir = os.path.isdir
@@ -410,8 +410,7 b' class dirstate(object):'
410 add((normpath(s[common_prefix_len:]), 'd', lstat(s)))
410 add((normpath(s[common_prefix_len:]), 'd', lstat(s)))
411 while work:
411 while work:
412 top = work.pop()
412 top = work.pop()
413 names = listdir(top)
413 entries = listdir(top, stat=True)
414 names.sort()
415 # nd is the top of the repository dir tree
414 # nd is the top of the repository dir tree
416 nd = normpath(top[common_prefix_len:])
415 nd = normpath(top[common_prefix_len:])
417 if nd == '.':
416 if nd == '.':
@@ -420,19 +419,19 b' class dirstate(object):'
420 # do not recurse into a repo contained in this
419 # do not recurse into a repo contained in this
421 # one. use bisect to find .hg directory so speed
420 # one. use bisect to find .hg directory so speed
422 # is good on big directory.
421 # is good on big directory.
422 names = [e[0] for e in entries]
423 hg = bisect_left(names, '.hg')
423 hg = bisect_left(names, '.hg')
424 if hg < len(names) and names[hg] == '.hg':
424 if hg < len(names) and names[hg] == '.hg':
425 if isdir(join(top, '.hg')):
425 if isdir(join(top, '.hg')):
426 continue
426 continue
427 for f in names:
427 for f, kind, st in entries:
428 np = pconvert(join(nd, f))
428 np = pconvert(join(nd, f))
429 if np in known:
429 if np in known:
430 continue
430 continue
431 known[np] = 1
431 known[np] = 1
432 p = join(top, f)
432 p = join(top, f)
433 # don't trip over symlinks
433 # don't trip over symlinks
434 st = lstat(p)
434 if kind == stat.S_IFDIR:
435 if s_isdir(st.st_mode):
436 if not ignore(np):
435 if not ignore(np):
437 wadd(p)
436 wadd(p)
438 if directories:
437 if directories:
@@ -6,7 +6,7 b''
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from i18n import _
8 from i18n import _
9 import os, stat, util, lock
9 import os, osutil, stat, util, lock
10
10
11 # if server supports streaming clone, it advertises "stream"
11 # if server supports streaming clone, it advertises "stream"
12 # capability with value that is version+flags of repo it is serving.
12 # capability with value that is version+flags of repo it is serving.
@@ -19,17 +19,14 b' def walkrepo(root):'
19
19
20 strip_count = len(root) + len(os.sep)
20 strip_count = len(root) + len(os.sep)
21 def walk(path, recurse):
21 def walk(path, recurse):
22 ents = os.listdir(path)
22 for e, kind, st in osutil.listdir(path, stat=True):
23 ents.sort()
24 for e in ents:
25 pe = os.path.join(path, e)
23 pe = os.path.join(path, e)
26 st = os.lstat(pe)
24 if kind == stat.S_IFDIR:
27 if stat.S_ISDIR(st.st_mode):
28 if recurse:
25 if recurse:
29 for x in walk(pe, True):
26 for x in walk(pe, True):
30 yield x
27 yield x
31 else:
28 else:
32 if not stat.S_ISREG(st.st_mode) or len(e) < 2:
29 if kind != stat.S_IFREG or len(e) < 2:
33 continue
30 continue
34 sfx = e[-2:]
31 sfx = e[-2:]
35 if sfx in ('.d', '.i'):
32 if sfx in ('.d', '.i'):
@@ -14,7 +14,7 b' platform-specific details from the core.'
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile, strutil
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile, strutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18
18
19 try:
19 try:
20 set = set
20 set = set
@@ -676,7 +676,7 b' def copyfiles(src, dst, hardlink=None):'
676
676
677 if os.path.isdir(src):
677 if os.path.isdir(src):
678 os.mkdir(dst)
678 os.mkdir(dst)
679 for name in os.listdir(src):
679 for name, kind in osutil.listdir(src):
680 srcname = os.path.join(src, name)
680 srcname = os.path.join(src, name)
681 dstname = os.path.join(dst, name)
681 dstname = os.path.join(dst, name)
682 copyfiles(srcname, dstname, hardlink)
682 copyfiles(srcname, dstname, hardlink)
@@ -1060,7 +1060,8 b' else:'
1060 rcs = [os.path.join(path, 'hgrc')]
1060 rcs = [os.path.join(path, 'hgrc')]
1061 rcdir = os.path.join(path, 'hgrc.d')
1061 rcdir = os.path.join(path, 'hgrc.d')
1062 try:
1062 try:
1063 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
1063 rcs.extend([os.path.join(rcdir, f)
1064 for f, kind in osutil.listdir(rcdir)
1064 if f.endswith(".rc")])
1065 if f.endswith(".rc")])
1065 except OSError:
1066 except OSError:
1066 pass
1067 pass
@@ -1653,7 +1654,7 b' def rcpath():'
1653 for p in os.environ['HGRCPATH'].split(os.pathsep):
1654 for p in os.environ['HGRCPATH'].split(os.pathsep):
1654 if not p: continue
1655 if not p: continue
1655 if os.path.isdir(p):
1656 if os.path.isdir(p):
1656 for f in os.listdir(p):
1657 for f, kind in osutil.listdir(p):
1657 if f.endswith('.rc'):
1658 if f.endswith('.rc'):
1658 _rcpath.append(os.path.join(p, f))
1659 _rcpath.append(os.path.join(p, f))
1659 else:
1660 else:
@@ -52,6 +52,19 b' class install_package_data(install_data)'
52 mercurial.version.remember_version(version)
52 mercurial.version.remember_version(version)
53 cmdclass = {'install_data': install_package_data}
53 cmdclass = {'install_data': install_package_data}
54
54
55 ext_modules=[
56 Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
57 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
58 Extension('mercurial.base85', ['mercurial/base85.c']),
59 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'])
60 ]
61
62 try:
63 import posix
64 ext_modules.append(Extension('mercurial.osutil', ['mercurial/osutil.c']))
65 except ImportError:
66 pass
67
55 setup(name='mercurial',
68 setup(name='mercurial',
56 version=mercurial.version.get_version(),
69 version=mercurial.version.get_version(),
57 author='Matt Mackall',
70 author='Matt Mackall',
@@ -60,10 +73,7 b" setup(name='mercurial',"
60 description='Scalable distributed SCM',
73 description='Scalable distributed SCM',
61 license='GNU GPL',
74 license='GNU GPL',
62 packages=['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert'],
75 packages=['mercurial', 'mercurial.hgweb', 'hgext', 'hgext.convert'],
63 ext_modules=[Extension('mercurial.mpatch', ['mercurial/mpatch.c']),
76 ext_modules=ext_modules,
64 Extension('mercurial.bdiff', ['mercurial/bdiff.c']),
65 Extension('mercurial.base85', ['mercurial/base85.c']),
66 Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'])],
67 data_files=[(os.path.join('mercurial', root),
77 data_files=[(os.path.join('mercurial', root),
68 [os.path.join(root, file_) for file_ in files])
78 [os.path.join(root, file_) for file_ in files])
69 for root, dirs, files in os.walk('templates')],
79 for root, dirs, files in os.walk('templates')],
General Comments 0
You need to be logged in to leave comments. Login now