##// END OF EJS Templates
listdir: add support for aborting if a certain path is found...
Matt Mackall -
r7034:0d513661 default
parent child Browse files
Show More
@@ -1,607 +1,600 b''
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 from node import nullid
10 from node import nullid
11 from i18n import _
11 from i18n import _
12 import struct, os, bisect, stat, util, errno, ignore
12 import struct, os, stat, util, errno, ignore
13 import cStringIO, osutil, sys
13 import cStringIO, osutil, sys
14
14
15 _unknown = ('?', 0, 0, 0)
15 _unknown = ('?', 0, 0, 0)
16 _format = ">cllll"
16 _format = ">cllll"
17
17
18 def _finddirs(path):
18 def _finddirs(path):
19 pos = path.rfind('/')
19 pos = path.rfind('/')
20 while pos != -1:
20 while pos != -1:
21 yield path[:pos]
21 yield path[:pos]
22 pos = path.rfind('/', 0, pos)
22 pos = path.rfind('/', 0, pos)
23
23
24 class dirstate(object):
24 class dirstate(object):
25
25
26 def __init__(self, opener, ui, root):
26 def __init__(self, opener, ui, root):
27 self._opener = opener
27 self._opener = opener
28 self._root = root
28 self._root = root
29 self._rootdir = os.path.join(root, '')
29 self._rootdir = os.path.join(root, '')
30 self._dirty = False
30 self._dirty = False
31 self._dirtypl = False
31 self._dirtypl = False
32 self._ui = ui
32 self._ui = ui
33
33
34 def __getattr__(self, name):
34 def __getattr__(self, name):
35 if name == '_map':
35 if name == '_map':
36 self._read()
36 self._read()
37 return self._map
37 return self._map
38 elif name == '_copymap':
38 elif name == '_copymap':
39 self._read()
39 self._read()
40 return self._copymap
40 return self._copymap
41 elif name == '_foldmap':
41 elif name == '_foldmap':
42 _foldmap = {}
42 _foldmap = {}
43 for name in self._map:
43 for name in self._map:
44 norm = os.path.normcase(os.path.normpath(name))
44 norm = os.path.normcase(os.path.normpath(name))
45 _foldmap[norm] = name
45 _foldmap[norm] = name
46 self._foldmap = _foldmap
46 self._foldmap = _foldmap
47 return self._foldmap
47 return self._foldmap
48 elif name == '_branch':
48 elif name == '_branch':
49 try:
49 try:
50 self._branch = (self._opener("branch").read().strip()
50 self._branch = (self._opener("branch").read().strip()
51 or "default")
51 or "default")
52 except IOError:
52 except IOError:
53 self._branch = "default"
53 self._branch = "default"
54 return self._branch
54 return self._branch
55 elif name == '_pl':
55 elif name == '_pl':
56 self._pl = [nullid, nullid]
56 self._pl = [nullid, nullid]
57 try:
57 try:
58 st = self._opener("dirstate").read(40)
58 st = self._opener("dirstate").read(40)
59 if len(st) == 40:
59 if len(st) == 40:
60 self._pl = st[:20], st[20:40]
60 self._pl = st[:20], st[20:40]
61 except IOError, err:
61 except IOError, err:
62 if err.errno != errno.ENOENT: raise
62 if err.errno != errno.ENOENT: raise
63 return self._pl
63 return self._pl
64 elif name == '_dirs':
64 elif name == '_dirs':
65 dirs = {}
65 dirs = {}
66 for f,s in self._map.iteritems():
66 for f,s in self._map.iteritems():
67 if s[0] != 'r':
67 if s[0] != 'r':
68 pos = f.rfind('/')
68 pos = f.rfind('/')
69 while pos != -1:
69 while pos != -1:
70 f = f[:pos]
70 f = f[:pos]
71 if f in dirs:
71 if f in dirs:
72 dirs[f] += 1
72 dirs[f] += 1
73 break
73 break
74 dirs[f] = 1
74 dirs[f] = 1
75 pos = f.rfind('/')
75 pos = f.rfind('/')
76 self._dirs = dirs
76 self._dirs = dirs
77 return self._dirs
77 return self._dirs
78 elif name == '_ignore':
78 elif name == '_ignore':
79 files = [self._join('.hgignore')]
79 files = [self._join('.hgignore')]
80 for name, path in self._ui.configitems("ui"):
80 for name, path in self._ui.configitems("ui"):
81 if name == 'ignore' or name.startswith('ignore.'):
81 if name == 'ignore' or name.startswith('ignore.'):
82 files.append(os.path.expanduser(path))
82 files.append(os.path.expanduser(path))
83 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
83 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
84 return self._ignore
84 return self._ignore
85 elif name == '_slash':
85 elif name == '_slash':
86 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
86 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
87 return self._slash
87 return self._slash
88 elif name == '_checklink':
88 elif name == '_checklink':
89 self._checklink = util.checklink(self._root)
89 self._checklink = util.checklink(self._root)
90 return self._checklink
90 return self._checklink
91 elif name == '_checkexec':
91 elif name == '_checkexec':
92 self._checkexec = util.checkexec(self._root)
92 self._checkexec = util.checkexec(self._root)
93 return self._checkexec
93 return self._checkexec
94 elif name == '_checkcase':
94 elif name == '_checkcase':
95 self._checkcase = not util.checkcase(self._join('.hg'))
95 self._checkcase = not util.checkcase(self._join('.hg'))
96 return self._checkcase
96 return self._checkcase
97 elif name == 'normalize':
97 elif name == 'normalize':
98 if self._checkcase:
98 if self._checkcase:
99 self.normalize = self._normalize
99 self.normalize = self._normalize
100 else:
100 else:
101 self.normalize = lambda x: x
101 self.normalize = lambda x: x
102 return self.normalize
102 return self.normalize
103 else:
103 else:
104 raise AttributeError(name)
104 raise AttributeError(name)
105
105
106 def _join(self, f):
106 def _join(self, f):
107 # much faster than os.path.join()
107 # much faster than os.path.join()
108 # it's safe because f is always a relative path
108 # it's safe because f is always a relative path
109 return self._rootdir + f
109 return self._rootdir + f
110
110
111 def flagfunc(self, fallback):
111 def flagfunc(self, fallback):
112 if self._checklink:
112 if self._checklink:
113 if self._checkexec:
113 if self._checkexec:
114 def f(x):
114 def f(x):
115 p = self._join(x)
115 p = self._join(x)
116 if os.path.islink(p):
116 if os.path.islink(p):
117 return 'l'
117 return 'l'
118 if util.is_exec(p):
118 if util.is_exec(p):
119 return 'x'
119 return 'x'
120 return ''
120 return ''
121 return f
121 return f
122 def f(x):
122 def f(x):
123 if os.path.islink(self._join(x)):
123 if os.path.islink(self._join(x)):
124 return 'l'
124 return 'l'
125 if 'x' in fallback(x):
125 if 'x' in fallback(x):
126 return 'x'
126 return 'x'
127 return ''
127 return ''
128 return f
128 return f
129 if self._checkexec:
129 if self._checkexec:
130 def f(x):
130 def f(x):
131 if 'l' in fallback(x):
131 if 'l' in fallback(x):
132 return 'l'
132 return 'l'
133 if util.is_exec(self._join(x)):
133 if util.is_exec(self._join(x)):
134 return 'x'
134 return 'x'
135 return ''
135 return ''
136 return f
136 return f
137 return fallback
137 return fallback
138
138
139 def getcwd(self):
139 def getcwd(self):
140 cwd = os.getcwd()
140 cwd = os.getcwd()
141 if cwd == self._root: return ''
141 if cwd == self._root: return ''
142 # self._root ends with a path separator if self._root is '/' or 'C:\'
142 # self._root ends with a path separator if self._root is '/' or 'C:\'
143 rootsep = self._root
143 rootsep = self._root
144 if not util.endswithsep(rootsep):
144 if not util.endswithsep(rootsep):
145 rootsep += os.sep
145 rootsep += os.sep
146 if cwd.startswith(rootsep):
146 if cwd.startswith(rootsep):
147 return cwd[len(rootsep):]
147 return cwd[len(rootsep):]
148 else:
148 else:
149 # we're outside the repo. return an absolute path.
149 # we're outside the repo. return an absolute path.
150 return cwd
150 return cwd
151
151
152 def pathto(self, f, cwd=None):
152 def pathto(self, f, cwd=None):
153 if cwd is None:
153 if cwd is None:
154 cwd = self.getcwd()
154 cwd = self.getcwd()
155 path = util.pathto(self._root, cwd, f)
155 path = util.pathto(self._root, cwd, f)
156 if self._slash:
156 if self._slash:
157 return util.normpath(path)
157 return util.normpath(path)
158 return path
158 return path
159
159
160 def __getitem__(self, key):
160 def __getitem__(self, key):
161 ''' current states:
161 ''' current states:
162 n normal
162 n normal
163 m needs merging
163 m needs merging
164 r marked for removal
164 r marked for removal
165 a marked for addition
165 a marked for addition
166 ? not tracked'''
166 ? not tracked'''
167 return self._map.get(key, ("?",))[0]
167 return self._map.get(key, ("?",))[0]
168
168
169 def __contains__(self, key):
169 def __contains__(self, key):
170 return key in self._map
170 return key in self._map
171
171
172 def __iter__(self):
172 def __iter__(self):
173 for x in util.sort(self._map):
173 for x in util.sort(self._map):
174 yield x
174 yield x
175
175
176 def parents(self):
176 def parents(self):
177 return self._pl
177 return self._pl
178
178
179 def branch(self):
179 def branch(self):
180 return self._branch
180 return self._branch
181
181
182 def setparents(self, p1, p2=nullid):
182 def setparents(self, p1, p2=nullid):
183 self._dirty = self._dirtypl = True
183 self._dirty = self._dirtypl = True
184 self._pl = p1, p2
184 self._pl = p1, p2
185
185
186 def setbranch(self, branch):
186 def setbranch(self, branch):
187 self._branch = branch
187 self._branch = branch
188 self._opener("branch", "w").write(branch + '\n')
188 self._opener("branch", "w").write(branch + '\n')
189
189
190 def _read(self):
190 def _read(self):
191 self._map = {}
191 self._map = {}
192 self._copymap = {}
192 self._copymap = {}
193 if not self._dirtypl:
193 if not self._dirtypl:
194 self._pl = [nullid, nullid]
194 self._pl = [nullid, nullid]
195 try:
195 try:
196 st = self._opener("dirstate").read()
196 st = self._opener("dirstate").read()
197 except IOError, err:
197 except IOError, err:
198 if err.errno != errno.ENOENT: raise
198 if err.errno != errno.ENOENT: raise
199 return
199 return
200 if not st:
200 if not st:
201 return
201 return
202
202
203 if not self._dirtypl:
203 if not self._dirtypl:
204 self._pl = [st[:20], st[20: 40]]
204 self._pl = [st[:20], st[20: 40]]
205
205
206 # deref fields so they will be local in loop
206 # deref fields so they will be local in loop
207 dmap = self._map
207 dmap = self._map
208 copymap = self._copymap
208 copymap = self._copymap
209 unpack = struct.unpack
209 unpack = struct.unpack
210 e_size = struct.calcsize(_format)
210 e_size = struct.calcsize(_format)
211 pos1 = 40
211 pos1 = 40
212 l = len(st)
212 l = len(st)
213
213
214 # the inner loop
214 # the inner loop
215 while pos1 < l:
215 while pos1 < l:
216 pos2 = pos1 + e_size
216 pos2 = pos1 + e_size
217 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
217 e = unpack(">cllll", st[pos1:pos2]) # a literal here is faster
218 pos1 = pos2 + e[4]
218 pos1 = pos2 + e[4]
219 f = st[pos2:pos1]
219 f = st[pos2:pos1]
220 if '\0' in f:
220 if '\0' in f:
221 f, c = f.split('\0')
221 f, c = f.split('\0')
222 copymap[f] = c
222 copymap[f] = c
223 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
223 dmap[f] = e # we hold onto e[4] because making a subtuple is slow
224
224
225 def invalidate(self):
225 def invalidate(self):
226 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
226 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
227 if a in self.__dict__:
227 if a in self.__dict__:
228 delattr(self, a)
228 delattr(self, a)
229 self._dirty = False
229 self._dirty = False
230
230
231 def copy(self, source, dest):
231 def copy(self, source, dest):
232 if source == dest:
232 if source == dest:
233 return
233 return
234 self._dirty = True
234 self._dirty = True
235 self._copymap[dest] = source
235 self._copymap[dest] = source
236
236
237 def copied(self, file):
237 def copied(self, file):
238 return self._copymap.get(file, None)
238 return self._copymap.get(file, None)
239
239
240 def copies(self):
240 def copies(self):
241 return self._copymap
241 return self._copymap
242
242
243 def _droppath(self, f):
243 def _droppath(self, f):
244 if self[f] not in "?r" and "_dirs" in self.__dict__:
244 if self[f] not in "?r" and "_dirs" in self.__dict__:
245 dirs = self._dirs
245 dirs = self._dirs
246 for base in _finddirs(f):
246 for base in _finddirs(f):
247 if dirs[base] == 1:
247 if dirs[base] == 1:
248 del dirs[base]
248 del dirs[base]
249 return
249 return
250 dirs[base] -= 1
250 dirs[base] -= 1
251
251
252 def _addpath(self, f, check=False):
252 def _addpath(self, f, check=False):
253 oldstate = self[f]
253 oldstate = self[f]
254 if check or oldstate == "r":
254 if check or oldstate == "r":
255 if '\r' in f or '\n' in f:
255 if '\r' in f or '\n' in f:
256 raise util.Abort(
256 raise util.Abort(
257 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
257 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
258 if f in self._dirs:
258 if f in self._dirs:
259 raise util.Abort(_('directory %r already in dirstate') % f)
259 raise util.Abort(_('directory %r already in dirstate') % f)
260 # shadows
260 # shadows
261 for d in _finddirs(f):
261 for d in _finddirs(f):
262 if d in self._dirs:
262 if d in self._dirs:
263 break
263 break
264 if d in self._map and self[d] != 'r':
264 if d in self._map and self[d] != 'r':
265 raise util.Abort(
265 raise util.Abort(
266 _('file %r in dirstate clashes with %r') % (d, f))
266 _('file %r in dirstate clashes with %r') % (d, f))
267 if oldstate in "?r" and "_dirs" in self.__dict__:
267 if oldstate in "?r" and "_dirs" in self.__dict__:
268 dirs = self._dirs
268 dirs = self._dirs
269 for base in _finddirs(f):
269 for base in _finddirs(f):
270 dirs[base] = dirs.get(base, 0) + 1
270 dirs[base] = dirs.get(base, 0) + 1
271
271
272 def normal(self, f):
272 def normal(self, f):
273 'mark a file normal and clean'
273 'mark a file normal and clean'
274 self._dirty = True
274 self._dirty = True
275 self._addpath(f)
275 self._addpath(f)
276 s = os.lstat(self._join(f))
276 s = os.lstat(self._join(f))
277 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
277 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime, 0)
278 if f in self._copymap:
278 if f in self._copymap:
279 del self._copymap[f]
279 del self._copymap[f]
280
280
281 def normallookup(self, f):
281 def normallookup(self, f):
282 'mark a file normal, but possibly dirty'
282 'mark a file normal, but possibly dirty'
283 if self._pl[1] != nullid and f in self._map:
283 if self._pl[1] != nullid and f in self._map:
284 # if there is a merge going on and the file was either
284 # if there is a merge going on and the file was either
285 # in state 'm' or dirty before being removed, restore that state.
285 # in state 'm' or dirty before being removed, restore that state.
286 entry = self._map[f]
286 entry = self._map[f]
287 if entry[0] == 'r' and entry[2] in (-1, -2):
287 if entry[0] == 'r' and entry[2] in (-1, -2):
288 source = self._copymap.get(f)
288 source = self._copymap.get(f)
289 if entry[2] == -1:
289 if entry[2] == -1:
290 self.merge(f)
290 self.merge(f)
291 elif entry[2] == -2:
291 elif entry[2] == -2:
292 self.normaldirty(f)
292 self.normaldirty(f)
293 if source:
293 if source:
294 self.copy(source, f)
294 self.copy(source, f)
295 return
295 return
296 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
296 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
297 return
297 return
298 self._dirty = True
298 self._dirty = True
299 self._addpath(f)
299 self._addpath(f)
300 self._map[f] = ('n', 0, -1, -1, 0)
300 self._map[f] = ('n', 0, -1, -1, 0)
301 if f in self._copymap:
301 if f in self._copymap:
302 del self._copymap[f]
302 del self._copymap[f]
303
303
304 def normaldirty(self, f):
304 def normaldirty(self, f):
305 'mark a file normal, but dirty'
305 'mark a file normal, but dirty'
306 self._dirty = True
306 self._dirty = True
307 self._addpath(f)
307 self._addpath(f)
308 self._map[f] = ('n', 0, -2, -1, 0)
308 self._map[f] = ('n', 0, -2, -1, 0)
309 if f in self._copymap:
309 if f in self._copymap:
310 del self._copymap[f]
310 del self._copymap[f]
311
311
312 def add(self, f):
312 def add(self, f):
313 'mark a file added'
313 'mark a file added'
314 self._dirty = True
314 self._dirty = True
315 self._addpath(f, True)
315 self._addpath(f, True)
316 self._map[f] = ('a', 0, -1, -1, 0)
316 self._map[f] = ('a', 0, -1, -1, 0)
317 if f in self._copymap:
317 if f in self._copymap:
318 del self._copymap[f]
318 del self._copymap[f]
319
319
320 def remove(self, f):
320 def remove(self, f):
321 'mark a file removed'
321 'mark a file removed'
322 self._dirty = True
322 self._dirty = True
323 self._droppath(f)
323 self._droppath(f)
324 size = 0
324 size = 0
325 if self._pl[1] != nullid and f in self._map:
325 if self._pl[1] != nullid and f in self._map:
326 entry = self._map[f]
326 entry = self._map[f]
327 if entry[0] == 'm':
327 if entry[0] == 'm':
328 size = -1
328 size = -1
329 elif entry[0] == 'n' and entry[2] == -2:
329 elif entry[0] == 'n' and entry[2] == -2:
330 size = -2
330 size = -2
331 self._map[f] = ('r', 0, size, 0, 0)
331 self._map[f] = ('r', 0, size, 0, 0)
332 if size == 0 and f in self._copymap:
332 if size == 0 and f in self._copymap:
333 del self._copymap[f]
333 del self._copymap[f]
334
334
335 def merge(self, f):
335 def merge(self, f):
336 'mark a file merged'
336 'mark a file merged'
337 self._dirty = True
337 self._dirty = True
338 s = os.lstat(self._join(f))
338 s = os.lstat(self._join(f))
339 self._addpath(f)
339 self._addpath(f)
340 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
340 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime, 0)
341 if f in self._copymap:
341 if f in self._copymap:
342 del self._copymap[f]
342 del self._copymap[f]
343
343
344 def forget(self, f):
344 def forget(self, f):
345 'forget a file'
345 'forget a file'
346 self._dirty = True
346 self._dirty = True
347 try:
347 try:
348 self._droppath(f)
348 self._droppath(f)
349 del self._map[f]
349 del self._map[f]
350 except KeyError:
350 except KeyError:
351 self._ui.warn(_("not in dirstate: %s\n") % f)
351 self._ui.warn(_("not in dirstate: %s\n") % f)
352
352
353 def _normalize(self, path):
353 def _normalize(self, path):
354 norm_path = os.path.normcase(os.path.normpath(path))
354 norm_path = os.path.normcase(os.path.normpath(path))
355 if norm_path not in self._foldmap:
355 if norm_path not in self._foldmap:
356 if not os.path.exists(os.path.join(self._root, path)):
356 if not os.path.exists(os.path.join(self._root, path)):
357 return path
357 return path
358 self._foldmap[norm_path] = util.fspath(path, self._root)
358 self._foldmap[norm_path] = util.fspath(path, self._root)
359 return self._foldmap[norm_path]
359 return self._foldmap[norm_path]
360
360
361 def clear(self):
361 def clear(self):
362 self._map = {}
362 self._map = {}
363 if "_dirs" in self.__dict__:
363 if "_dirs" in self.__dict__:
364 delattr(self, "_dirs");
364 delattr(self, "_dirs");
365 self._copymap = {}
365 self._copymap = {}
366 self._pl = [nullid, nullid]
366 self._pl = [nullid, nullid]
367 self._dirty = True
367 self._dirty = True
368
368
369 def rebuild(self, parent, files):
369 def rebuild(self, parent, files):
370 self.clear()
370 self.clear()
371 for f in files:
371 for f in files:
372 if 'x' in files.flags(f):
372 if 'x' in files.flags(f):
373 self._map[f] = ('n', 0777, -1, 0, 0)
373 self._map[f] = ('n', 0777, -1, 0, 0)
374 else:
374 else:
375 self._map[f] = ('n', 0666, -1, 0, 0)
375 self._map[f] = ('n', 0666, -1, 0, 0)
376 self._pl = (parent, nullid)
376 self._pl = (parent, nullid)
377 self._dirty = True
377 self._dirty = True
378
378
379 def write(self):
379 def write(self):
380 if not self._dirty:
380 if not self._dirty:
381 return
381 return
382 st = self._opener("dirstate", "w", atomictemp=True)
382 st = self._opener("dirstate", "w", atomictemp=True)
383
383
384 try:
384 try:
385 gran = int(self._ui.config('dirstate', 'granularity', 1))
385 gran = int(self._ui.config('dirstate', 'granularity', 1))
386 except ValueError:
386 except ValueError:
387 gran = 1
387 gran = 1
388 limit = sys.maxint
388 limit = sys.maxint
389 if gran > 0:
389 if gran > 0:
390 limit = util.fstat(st).st_mtime - gran
390 limit = util.fstat(st).st_mtime - gran
391
391
392 cs = cStringIO.StringIO()
392 cs = cStringIO.StringIO()
393 copymap = self._copymap
393 copymap = self._copymap
394 pack = struct.pack
394 pack = struct.pack
395 write = cs.write
395 write = cs.write
396 write("".join(self._pl))
396 write("".join(self._pl))
397 for f, e in self._map.iteritems():
397 for f, e in self._map.iteritems():
398 if f in copymap:
398 if f in copymap:
399 f = "%s\0%s" % (f, copymap[f])
399 f = "%s\0%s" % (f, copymap[f])
400 if e[3] > limit and e[0] == 'n':
400 if e[3] > limit and e[0] == 'n':
401 e = (e[0], 0, -1, -1, 0)
401 e = (e[0], 0, -1, -1, 0)
402 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
402 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
403 write(e)
403 write(e)
404 write(f)
404 write(f)
405 st.write(cs.getvalue())
405 st.write(cs.getvalue())
406 st.rename()
406 st.rename()
407 self._dirty = self._dirtypl = False
407 self._dirty = self._dirtypl = False
408
408
409 def _dirignore(self, f):
409 def _dirignore(self, f):
410 if f == '.':
410 if f == '.':
411 return False
411 return False
412 if self._ignore(f):
412 if self._ignore(f):
413 return True
413 return True
414 for p in _finddirs(f):
414 for p in _finddirs(f):
415 if self._ignore(p):
415 if self._ignore(p):
416 return True
416 return True
417 return False
417 return False
418
418
419 def walk(self, match, unknown, ignored):
419 def walk(self, match, unknown, ignored):
420 '''
420 '''
421 walk recursively through the directory tree, finding all files
421 walk recursively through the directory tree, finding all files
422 matched by the match function
422 matched by the match function
423
423
424 results are yielded in a tuple (filename, stat), where stat
424 results are yielded in a tuple (filename, stat), where stat
425 and st is the stat result if the file was found in the directory.
425 and st is the stat result if the file was found in the directory.
426 '''
426 '''
427
427
428 def fwarn(f, msg):
428 def fwarn(f, msg):
429 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
429 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
430 return False
430 return False
431 badfn = fwarn
431 badfn = fwarn
432 if hasattr(match, 'bad'):
432 if hasattr(match, 'bad'):
433 badfn = match.bad
433 badfn = match.bad
434
434
435 def badtype(f, mode):
435 def badtype(f, mode):
436 kind = 'unknown'
436 kind = 'unknown'
437 if stat.S_ISCHR(mode): kind = _('character device')
437 if stat.S_ISCHR(mode): kind = _('character device')
438 elif stat.S_ISBLK(mode): kind = _('block device')
438 elif stat.S_ISBLK(mode): kind = _('block device')
439 elif stat.S_ISFIFO(mode): kind = _('fifo')
439 elif stat.S_ISFIFO(mode): kind = _('fifo')
440 elif stat.S_ISSOCK(mode): kind = _('socket')
440 elif stat.S_ISSOCK(mode): kind = _('socket')
441 elif stat.S_ISDIR(mode): kind = _('directory')
441 elif stat.S_ISDIR(mode): kind = _('directory')
442 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
442 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
443 % (self.pathto(f), kind))
443 % (self.pathto(f), kind))
444
444
445 ignore = self._ignore
445 ignore = self._ignore
446 dirignore = self._dirignore
446 dirignore = self._dirignore
447 if ignored:
447 if ignored:
448 ignore = util.never
448 ignore = util.never
449 dirignore = util.never
449 dirignore = util.never
450 elif not unknown:
450 elif not unknown:
451 # if unknown and ignored are False, skip step 2
451 # if unknown and ignored are False, skip step 2
452 ignore = util.always
452 ignore = util.always
453 dirignore = util.always
453 dirignore = util.always
454
454
455 matchfn = match.matchfn
455 matchfn = match.matchfn
456 dmap = self._map
456 dmap = self._map
457 normpath = util.normpath
457 normpath = util.normpath
458 normalize = self.normalize
458 normalize = self.normalize
459 listdir = osutil.listdir
459 listdir = osutil.listdir
460 lstat = os.lstat
460 lstat = os.lstat
461 bisect_left = bisect.bisect_left
462 pconvert = util.pconvert
461 pconvert = util.pconvert
463 getkind = stat.S_IFMT
462 getkind = stat.S_IFMT
464 dirkind = stat.S_IFDIR
463 dirkind = stat.S_IFDIR
465 regkind = stat.S_IFREG
464 regkind = stat.S_IFREG
466 lnkkind = stat.S_IFLNK
465 lnkkind = stat.S_IFLNK
467 join = self._join
466 join = self._join
468 work = []
467 work = []
469 wadd = work.append
468 wadd = work.append
470
469
471 files = util.unique(match.files())
470 files = util.unique(match.files())
472 if not files or '.' in files:
471 if not files or '.' in files:
473 files = ['']
472 files = ['']
474 results = {'.hg': None}
473 results = {'.hg': None}
475
474
476 # step 1: find all explicit files
475 # step 1: find all explicit files
477 for ff in util.sort(files):
476 for ff in util.sort(files):
478 nf = normalize(normpath(ff))
477 nf = normalize(normpath(ff))
479 if nf in results:
478 if nf in results:
480 continue
479 continue
481
480
482 try:
481 try:
483 st = lstat(join(nf))
482 st = lstat(join(nf))
484 kind = getkind(st.st_mode)
483 kind = getkind(st.st_mode)
485 if kind == dirkind:
484 if kind == dirkind:
486 if not dirignore(nf):
485 if not dirignore(nf):
487 wadd(nf)
486 wadd(nf)
488 elif kind == regkind or kind == lnkkind:
487 elif kind == regkind or kind == lnkkind:
489 results[nf] = st
488 results[nf] = st
490 else:
489 else:
491 badtype(ff, kind)
490 badtype(ff, kind)
492 if nf in dmap:
491 if nf in dmap:
493 results[nf] = None
492 results[nf] = None
494 except OSError, inst:
493 except OSError, inst:
495 keep = False
494 keep = False
496 prefix = nf + "/"
495 prefix = nf + "/"
497 for fn in dmap:
496 for fn in dmap:
498 if nf == fn or fn.startswith(prefix):
497 if nf == fn or fn.startswith(prefix):
499 keep = True
498 keep = True
500 break
499 break
501 if not keep:
500 if not keep:
502 if inst.errno != errno.ENOENT:
501 if inst.errno != errno.ENOENT:
503 fwarn(ff, inst.strerror)
502 fwarn(ff, inst.strerror)
504 elif badfn(ff, inst.strerror):
503 elif badfn(ff, inst.strerror):
505 if (nf in dmap or not ignore(nf)) and matchfn(nf):
504 if (nf in dmap or not ignore(nf)) and matchfn(nf):
506 results[nf] = None
505 results[nf] = None
507
506
508 # step 2: visit subdirectories
507 # step 2: visit subdirectories
509 while work:
508 while work:
510 nd = work.pop()
509 nd = work.pop()
511 if hasattr(match, 'dir'):
510 if hasattr(match, 'dir'):
512 match.dir(nd)
511 match.dir(nd)
513 entries = listdir(join(nd), stat=True)
514 if nd == '.':
512 if nd == '.':
515 nd = ''
513 nd = ''
514 entries = listdir(join(nd), stat=True)
516 else:
515 else:
517 # do not recurse into a repo contained in this
516 entries = listdir(join(nd), stat=True, skip ='.hg')
518 # one. use bisect to find .hg directory so speed
519 # is good on big directory.
520 hg = bisect_left(entries, ('.hg'))
521 if hg < len(entries) and entries[hg][0] == '.hg' \
522 and entries[hg][1] == dirkind:
523 continue
524 for f, kind, st in entries:
517 for f, kind, st in entries:
525 nf = normalize(nd and (nd + "/" + f) or f)
518 nf = normalize(nd and (nd + "/" + f) or f)
526 if nf not in results:
519 if nf not in results:
527 if kind == dirkind:
520 if kind == dirkind:
528 if not ignore(nf):
521 if not ignore(nf):
529 wadd(nf)
522 wadd(nf)
530 if nf in dmap and matchfn(nf):
523 if nf in dmap and matchfn(nf):
531 results[nf] = None
524 results[nf] = None
532 elif kind == regkind or kind == lnkkind:
525 elif kind == regkind or kind == lnkkind:
533 if nf in dmap:
526 if nf in dmap:
534 if matchfn(nf):
527 if matchfn(nf):
535 results[nf] = st
528 results[nf] = st
536 elif matchfn(nf) and not ignore(nf):
529 elif matchfn(nf) and not ignore(nf):
537 results[nf] = st
530 results[nf] = st
538 elif nf in dmap and matchfn(nf):
531 elif nf in dmap and matchfn(nf):
539 results[nf] = None
532 results[nf] = None
540
533
541 # step 3: report unseen items in the dmap hash
534 # step 3: report unseen items in the dmap hash
542 visit = [f for f in dmap if f not in results and match(f)]
535 visit = [f for f in dmap if f not in results and match(f)]
543 for nf in util.sort(visit):
536 for nf in util.sort(visit):
544 results[nf] = None
537 results[nf] = None
545 try:
538 try:
546 st = lstat(join(nf))
539 st = lstat(join(nf))
547 kind = getkind(st.st_mode)
540 kind = getkind(st.st_mode)
548 if kind == regkind or kind == lnkkind:
541 if kind == regkind or kind == lnkkind:
549 results[nf] = st
542 results[nf] = st
550 except OSError, inst:
543 except OSError, inst:
551 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
544 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
552 raise
545 raise
553
546
554 del results['.hg']
547 del results['.hg']
555 return results
548 return results
556
549
557 def status(self, match, ignored, clean, unknown):
550 def status(self, match, ignored, clean, unknown):
558 listignored, listclean, listunknown = ignored, clean, unknown
551 listignored, listclean, listunknown = ignored, clean, unknown
559 lookup, modified, added, unknown, ignored = [], [], [], [], []
552 lookup, modified, added, unknown, ignored = [], [], [], [], []
560 removed, deleted, clean = [], [], []
553 removed, deleted, clean = [], [], []
561
554
562 _join = self._join
555 _join = self._join
563 lstat = os.lstat
556 lstat = os.lstat
564 cmap = self._copymap
557 cmap = self._copymap
565 dmap = self._map
558 dmap = self._map
566 ladd = lookup.append
559 ladd = lookup.append
567 madd = modified.append
560 madd = modified.append
568 aadd = added.append
561 aadd = added.append
569 uadd = unknown.append
562 uadd = unknown.append
570 iadd = ignored.append
563 iadd = ignored.append
571 radd = removed.append
564 radd = removed.append
572 dadd = deleted.append
565 dadd = deleted.append
573 cadd = clean.append
566 cadd = clean.append
574
567
575 for fn, st in self.walk(match, listunknown, listignored).iteritems():
568 for fn, st in self.walk(match, listunknown, listignored).iteritems():
576 if fn not in dmap:
569 if fn not in dmap:
577 if (listignored or match.exact(fn)) and self._dirignore(fn):
570 if (listignored or match.exact(fn)) and self._dirignore(fn):
578 if listignored:
571 if listignored:
579 iadd(fn)
572 iadd(fn)
580 elif listunknown:
573 elif listunknown:
581 uadd(fn)
574 uadd(fn)
582 continue
575 continue
583
576
584 state, mode, size, time, foo = dmap[fn]
577 state, mode, size, time, foo = dmap[fn]
585
578
586 if not st and state in "nma":
579 if not st and state in "nma":
587 dadd(fn)
580 dadd(fn)
588 elif state == 'n':
581 elif state == 'n':
589 if (size >= 0 and
582 if (size >= 0 and
590 (size != st.st_size
583 (size != st.st_size
591 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
584 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
592 or size == -2
585 or size == -2
593 or fn in self._copymap):
586 or fn in self._copymap):
594 madd(fn)
587 madd(fn)
595 elif time != int(st.st_mtime):
588 elif time != int(st.st_mtime):
596 ladd(fn)
589 ladd(fn)
597 elif listclean:
590 elif listclean:
598 cadd(fn)
591 cadd(fn)
599 elif state == 'm':
592 elif state == 'm':
600 madd(fn)
593 madd(fn)
601 elif state == 'a':
594 elif state == 'a':
602 aadd(fn)
595 aadd(fn)
603 elif state == 'r':
596 elif state == 'r':
604 radd(fn)
597 radd(fn)
605
598
606 return (lookup, modified, added, removed, deleted, unknown, ignored,
599 return (lookup, modified, added, removed, deleted, unknown, ignored,
607 clean)
600 clean)
@@ -1,225 +1,231 b''
1 /*
1 /*
2 osutil.c - native operating system services
2 osutil.c - native operating system services
3
3
4 Copyright 2007 Matt Mackall and others
4 Copyright 2007 Matt Mackall and others
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8 */
8 */
9
9
10 #define _ATFILE_SOURCE
10 #define _ATFILE_SOURCE
11 #include <Python.h>
11 #include <Python.h>
12 #include <dirent.h>
12 #include <dirent.h>
13 #include <fcntl.h>
13 #include <fcntl.h>
14 #include <string.h>
14 #include <string.h>
15 #include <sys/stat.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
16 #include <sys/types.h>
17 #include <unistd.h>
17 #include <unistd.h>
18
18
19 struct listdir_stat {
19 struct listdir_stat {
20 PyObject_HEAD
20 PyObject_HEAD
21 struct stat st;
21 struct stat st;
22 };
22 };
23
23
24 #define listdir_slot(name) \
24 #define listdir_slot(name) \
25 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
25 static PyObject *listdir_stat_##name(PyObject *self, void *x) \
26 { \
26 { \
27 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
27 return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \
28 }
28 }
29
29
30 listdir_slot(st_dev)
30 listdir_slot(st_dev)
31 listdir_slot(st_mode)
31 listdir_slot(st_mode)
32 listdir_slot(st_nlink)
32 listdir_slot(st_nlink)
33 listdir_slot(st_size)
33 listdir_slot(st_size)
34 listdir_slot(st_mtime)
34 listdir_slot(st_mtime)
35 listdir_slot(st_ctime)
35 listdir_slot(st_ctime)
36
36
37 static struct PyGetSetDef listdir_stat_getsets[] = {
37 static struct PyGetSetDef listdir_stat_getsets[] = {
38 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
38 {"st_dev", listdir_stat_st_dev, 0, 0, 0},
39 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
39 {"st_mode", listdir_stat_st_mode, 0, 0, 0},
40 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
40 {"st_nlink", listdir_stat_st_nlink, 0, 0, 0},
41 {"st_size", listdir_stat_st_size, 0, 0, 0},
41 {"st_size", listdir_stat_st_size, 0, 0, 0},
42 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
42 {"st_mtime", listdir_stat_st_mtime, 0, 0, 0},
43 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
43 {"st_ctime", listdir_stat_st_ctime, 0, 0, 0},
44 {0, 0, 0, 0, 0}
44 {0, 0, 0, 0, 0}
45 };
45 };
46
46
47 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
47 static PyObject *listdir_stat_new(PyTypeObject *t, PyObject *a, PyObject *k)
48 {
48 {
49 return t->tp_alloc(t, 0);
49 return t->tp_alloc(t, 0);
50 }
50 }
51
51
52 static void listdir_stat_dealloc(PyObject *o)
52 static void listdir_stat_dealloc(PyObject *o)
53 {
53 {
54 o->ob_type->tp_free(o);
54 o->ob_type->tp_free(o);
55 }
55 }
56
56
57 static PyTypeObject listdir_stat_type = {
57 static PyTypeObject listdir_stat_type = {
58 PyObject_HEAD_INIT(NULL)
58 PyObject_HEAD_INIT(NULL)
59 0, /*ob_size*/
59 0, /*ob_size*/
60 "osutil.stat", /*tp_name*/
60 "osutil.stat", /*tp_name*/
61 sizeof(struct listdir_stat), /*tp_basicsize*/
61 sizeof(struct listdir_stat), /*tp_basicsize*/
62 0, /*tp_itemsize*/
62 0, /*tp_itemsize*/
63 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
63 (destructor)listdir_stat_dealloc, /*tp_dealloc*/
64 0, /*tp_print*/
64 0, /*tp_print*/
65 0, /*tp_getattr*/
65 0, /*tp_getattr*/
66 0, /*tp_setattr*/
66 0, /*tp_setattr*/
67 0, /*tp_compare*/
67 0, /*tp_compare*/
68 0, /*tp_repr*/
68 0, /*tp_repr*/
69 0, /*tp_as_number*/
69 0, /*tp_as_number*/
70 0, /*tp_as_sequence*/
70 0, /*tp_as_sequence*/
71 0, /*tp_as_mapping*/
71 0, /*tp_as_mapping*/
72 0, /*tp_hash */
72 0, /*tp_hash */
73 0, /*tp_call*/
73 0, /*tp_call*/
74 0, /*tp_str*/
74 0, /*tp_str*/
75 0, /*tp_getattro*/
75 0, /*tp_getattro*/
76 0, /*tp_setattro*/
76 0, /*tp_setattro*/
77 0, /*tp_as_buffer*/
77 0, /*tp_as_buffer*/
78 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
78 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
79 "stat objects", /* tp_doc */
79 "stat objects", /* tp_doc */
80 0, /* tp_traverse */
80 0, /* tp_traverse */
81 0, /* tp_clear */
81 0, /* tp_clear */
82 0, /* tp_richcompare */
82 0, /* tp_richcompare */
83 0, /* tp_weaklistoffset */
83 0, /* tp_weaklistoffset */
84 0, /* tp_iter */
84 0, /* tp_iter */
85 0, /* tp_iternext */
85 0, /* tp_iternext */
86 0, /* tp_methods */
86 0, /* tp_methods */
87 0, /* tp_members */
87 0, /* tp_members */
88 listdir_stat_getsets, /* tp_getset */
88 listdir_stat_getsets, /* tp_getset */
89 0, /* tp_base */
89 0, /* tp_base */
90 0, /* tp_dict */
90 0, /* tp_dict */
91 0, /* tp_descr_get */
91 0, /* tp_descr_get */
92 0, /* tp_descr_set */
92 0, /* tp_descr_set */
93 0, /* tp_dictoffset */
93 0, /* tp_dictoffset */
94 0, /* tp_init */
94 0, /* tp_init */
95 0, /* tp_alloc */
95 0, /* tp_alloc */
96 listdir_stat_new, /* tp_new */
96 listdir_stat_new, /* tp_new */
97 };
97 };
98
98
99 int entkind(struct dirent *ent)
99 int entkind(struct dirent *ent)
100 {
100 {
101 #ifdef DT_REG
101 #ifdef DT_REG
102 switch (ent->d_type) {
102 switch (ent->d_type) {
103 case DT_REG: return S_IFREG;
103 case DT_REG: return S_IFREG;
104 case DT_DIR: return S_IFDIR;
104 case DT_DIR: return S_IFDIR;
105 case DT_LNK: return S_IFLNK;
105 case DT_LNK: return S_IFLNK;
106 case DT_BLK: return S_IFBLK;
106 case DT_BLK: return S_IFBLK;
107 case DT_CHR: return S_IFCHR;
107 case DT_CHR: return S_IFCHR;
108 case DT_FIFO: return S_IFIFO;
108 case DT_FIFO: return S_IFIFO;
109 case DT_SOCK: return S_IFSOCK;
109 case DT_SOCK: return S_IFSOCK;
110 }
110 }
111 #endif
111 #endif
112 return -1;
112 return -1;
113 }
113 }
114
114
115 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
115 static PyObject *listdir(PyObject *self, PyObject *args, PyObject *kwargs)
116 {
116 {
117 static char *kwlist[] = { "path", "stat", NULL };
117 static char *kwlist[] = { "path", "stat", "skip", NULL };
118 PyObject *statflag = NULL, *list, *elem, *stat, *ret = NULL;
118 PyObject *statflag = NULL, *list, *elem, *stat, *ret = NULL;
119 char fullpath[PATH_MAX + 10], *path;
119 char fullpath[PATH_MAX + 10], *path, *skip = NULL;
120 int pathlen, keepstat, kind, dfd = -1, err;
120 int pathlen, keepstat, kind, dfd = -1, err;
121 struct stat st;
121 struct stat st;
122 struct dirent *ent;
122 struct dirent *ent;
123 DIR *dir;
123 DIR *dir;
124
124
125 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|O:listdir", kwlist,
125 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|Os:listdir", kwlist,
126 &path, &pathlen, &statflag))
126 &path, &pathlen, &statflag, &skip))
127 goto error_parse;
127 goto error_parse;
128
128 if (pathlen >= PATH_MAX)
129 if (pathlen >= PATH_MAX)
129 goto error_parse;
130 goto error_parse;
130
131
131 strncpy(fullpath, path, PATH_MAX);
132 strncpy(fullpath, path, PATH_MAX);
132 fullpath[pathlen] = '/';
133 fullpath[pathlen] = '/';
133 keepstat = statflag && PyObject_IsTrue(statflag);
134 keepstat = statflag && PyObject_IsTrue(statflag);
134
135
135 #ifdef AT_SYMLINK_NOFOLLOW
136 #ifdef AT_SYMLINK_NOFOLLOW
136 dfd = open(path, O_RDONLY);
137 dfd = open(path, O_RDONLY);
137 if (dfd == -1) {
138 if (dfd == -1) {
138 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
139 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
139 goto error_parse;
140 goto error_parse;
140 }
141 }
141 dir = fdopendir(dfd);
142 dir = fdopendir(dfd);
142 #else
143 #else
143 dir = opendir(path);
144 dir = opendir(path);
144 #endif
145 #endif
145 if (!dir) {
146 if (!dir) {
146 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
147 PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
147 goto error_dir;
148 goto error_dir;
148 }
149 }
149
150
150 list = PyList_New(0);
151 list = PyList_New(0);
151 if (!list)
152 if (!list)
152 goto error_list;
153 goto error_list;
153
154
154 while ((ent = readdir(dir))) {
155 while ((ent = readdir(dir))) {
155 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
156 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
156 continue;
157 continue;
157
158
158 kind = entkind(ent);
159 kind = entkind(ent);
159 if (kind == -1 || keepstat) {
160 if (kind == -1 || keepstat) {
160 #ifdef AT_SYMLINK_NOFOLLOW
161 #ifdef AT_SYMLINK_NOFOLLOW
161 err = fstatat(dfd, ent->d_name, &st,
162 err = fstatat(dfd, ent->d_name, &st,
162 AT_SYMLINK_NOFOLLOW);
163 AT_SYMLINK_NOFOLLOW);
163 #else
164 #else
164 strncpy(fullpath + pathlen + 1, ent->d_name,
165 strncpy(fullpath + pathlen + 1, ent->d_name,
165 PATH_MAX - pathlen);
166 PATH_MAX - pathlen);
166 fullpath[PATH_MAX] = 0;
167 fullpath[PATH_MAX] = 0;
167 err = lstat(fullpath, &st);
168 err = lstat(fullpath, &st);
168 #endif
169 #endif
169 if (err == -1) {
170 if (err == -1) {
170 strncpy(fullpath + pathlen + 1, ent->d_name,
171 strncpy(fullpath + pathlen + 1, ent->d_name,
171 PATH_MAX - pathlen);
172 PATH_MAX - pathlen);
172 fullpath[PATH_MAX] = 0;
173 fullpath[PATH_MAX] = 0;
173 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
174 PyErr_SetFromErrnoWithFilename(PyExc_OSError,
174 fullpath);
175 fullpath);
175 goto error;
176 goto error;
176 }
177 }
177 kind = st.st_mode & S_IFMT;
178 kind = st.st_mode & S_IFMT;
178 }
179 }
179
180
181 /* quit early? */
182 if (skip && kind == S_IFDIR && !strcmp(ent->d_name, skip)) {
183 ret = PyList_New(0);
184 goto error;
185 }
186
180 if (keepstat) {
187 if (keepstat) {
181 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
188 stat = PyObject_CallObject((PyObject *)&listdir_stat_type, NULL);
182 if (!stat)
189 if (!stat)
183 goto error;
190 goto error;
184 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
191 memcpy(&((struct listdir_stat *)stat)->st, &st, sizeof(st));
185 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
192 elem = Py_BuildValue("siN", ent->d_name, kind, stat);
186 } else
193 } else
187 elem = Py_BuildValue("si", ent->d_name, kind);
194 elem = Py_BuildValue("si", ent->d_name, kind);
188 if (!elem)
195 if (!elem)
189 goto error;
196 goto error;
190
197
191 PyList_Append(list, elem);
198 PyList_Append(list, elem);
192 Py_DECREF(elem);
199 Py_DECREF(elem);
193 }
200 }
194
201
195 PyList_Sort(list);
196 ret = list;
202 ret = list;
197 Py_INCREF(ret);
203 Py_INCREF(ret);
198
204
199 error:
205 error:
200 Py_DECREF(list);
206 Py_DECREF(list);
201 error_list:
207 error_list:
202 closedir(dir);
208 closedir(dir);
203 error_dir:
209 error_dir:
204 #ifdef AT_SYMLINK_NOFOLLOW
210 #ifdef AT_SYMLINK_NOFOLLOW
205 close(dfd);
211 close(dfd);
206 #endif
212 #endif
207 error_parse:
213 error_parse:
208 return ret;
214 return ret;
209 }
215 }
210
216
211 static char osutil_doc[] = "Native operating system services.";
217 static char osutil_doc[] = "Native operating system services.";
212
218
213 static PyMethodDef methods[] = {
219 static PyMethodDef methods[] = {
214 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
220 {"listdir", (PyCFunction)listdir, METH_VARARGS | METH_KEYWORDS,
215 "list a directory\n"},
221 "list a directory\n"},
216 {NULL, NULL}
222 {NULL, NULL}
217 };
223 };
218
224
219 PyMODINIT_FUNC initosutil(void)
225 PyMODINIT_FUNC initosutil(void)
220 {
226 {
221 if (PyType_Ready(&listdir_stat_type) == -1)
227 if (PyType_Ready(&listdir_stat_type) == -1)
222 return;
228 return;
223
229
224 Py_InitModule3("osutil", methods, osutil_doc);
230 Py_InitModule3("osutil", methods, osutil_doc);
225 }
231 }
@@ -1,37 +1,39 b''
1 import os, stat
1 import os, stat
2
2
3 def _mode_to_kind(mode):
3 def _mode_to_kind(mode):
4 if stat.S_ISREG(mode): return stat.S_IFREG
4 if stat.S_ISREG(mode): return stat.S_IFREG
5 if stat.S_ISDIR(mode): return stat.S_IFDIR
5 if stat.S_ISDIR(mode): return stat.S_IFDIR
6 if stat.S_ISLNK(mode): return stat.S_IFLNK
6 if stat.S_ISLNK(mode): return stat.S_IFLNK
7 if stat.S_ISBLK(mode): return stat.S_IFBLK
7 if stat.S_ISBLK(mode): return stat.S_IFBLK
8 if stat.S_ISCHR(mode): return stat.S_IFCHR
8 if stat.S_ISCHR(mode): return stat.S_IFCHR
9 if stat.S_ISFIFO(mode): return stat.S_IFIFO
9 if stat.S_ISFIFO(mode): return stat.S_IFIFO
10 if stat.S_ISSOCK(mode): return stat.S_IFSOCK
10 if stat.S_ISSOCK(mode): return stat.S_IFSOCK
11 return mode
11 return mode
12
12
13 def listdir(path, stat=False):
13 def listdir(path, stat=False, skip=None):
14 '''listdir(path, stat=False) -> list_of_tuples
14 '''listdir(path, stat=False) -> list_of_tuples
15
15
16 Return a sorted list containing information about the entries
16 Return a sorted list containing information about the entries
17 in the directory.
17 in the directory.
18
18
19 If stat is True, each element is a 3-tuple:
19 If stat is True, each element is a 3-tuple:
20
20
21 (name, type, stat object)
21 (name, type, stat object)
22
22
23 Otherwise, each element is a 2-tuple:
23 Otherwise, each element is a 2-tuple:
24
24
25 (name, type)
25 (name, type)
26 '''
26 '''
27 result = []
27 result = []
28 prefix = path + os.sep
28 prefix = path + os.sep
29 names = os.listdir(path)
29 names = os.listdir(path)
30 names.sort()
30 names.sort()
31 for fn in names:
31 for fn in names:
32 st = os.lstat(prefix + fn)
32 st = os.lstat(prefix + fn)
33 if fn == skip and stat.S_ISDIR(st.st_mode):
34 return []
33 if stat:
35 if stat:
34 result.append((fn, _mode_to_kind(st.st_mode), st))
36 result.append((fn, _mode_to_kind(st.st_mode), st))
35 else:
37 else:
36 result.append((fn, _mode_to_kind(st.st_mode)))
38 result.append((fn, _mode_to_kind(st.st_mode)))
37 return result
39 return result
General Comments 0
You need to be logged in to leave comments. Login now