##// END OF EJS Templates
Take advantage of fstat calls clustering per directory if OS support it....
Petr Kodl -
r7118:619ebf82 default
parent child Browse files
Show More
@@ -1,590 +1,584 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, stat, util, errno, ignore
12 import struct, os, stat, util, errno, ignore
13 import cStringIO, osutil, sys, parsers
13 import cStringIO, osutil, sys, parsers
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 def _incdirs(dirs, path):
24 def _incdirs(dirs, path):
25 for base in _finddirs(path):
25 for base in _finddirs(path):
26 if base in dirs:
26 if base in dirs:
27 dirs[base] += 1
27 dirs[base] += 1
28 return
28 return
29 dirs[base] = 1
29 dirs[base] = 1
30
30
31 def _decdirs(dirs, path):
31 def _decdirs(dirs, path):
32 for base in _finddirs(path):
32 for base in _finddirs(path):
33 if dirs[base] > 1:
33 if dirs[base] > 1:
34 dirs[base] -= 1
34 dirs[base] -= 1
35 return
35 return
36 del dirs[base]
36 del dirs[base]
37
37
38 class dirstate(object):
38 class dirstate(object):
39
39
40 def __init__(self, opener, ui, root):
40 def __init__(self, opener, ui, root):
41 self._opener = opener
41 self._opener = opener
42 self._root = root
42 self._root = root
43 self._rootdir = os.path.join(root, '')
43 self._rootdir = os.path.join(root, '')
44 self._dirty = False
44 self._dirty = False
45 self._dirtypl = False
45 self._dirtypl = False
46 self._ui = ui
46 self._ui = ui
47
47
48 def __getattr__(self, name):
48 def __getattr__(self, name):
49 if name == '_map':
49 if name == '_map':
50 self._read()
50 self._read()
51 return self._map
51 return self._map
52 elif name == '_copymap':
52 elif name == '_copymap':
53 self._read()
53 self._read()
54 return self._copymap
54 return self._copymap
55 elif name == '_foldmap':
55 elif name == '_foldmap':
56 _foldmap = {}
56 _foldmap = {}
57 for name in self._map:
57 for name in self._map:
58 norm = os.path.normcase(name)
58 norm = os.path.normcase(name)
59 _foldmap[norm] = name
59 _foldmap[norm] = name
60 self._foldmap = _foldmap
60 self._foldmap = _foldmap
61 return self._foldmap
61 return self._foldmap
62 elif name == '_branch':
62 elif name == '_branch':
63 try:
63 try:
64 self._branch = (self._opener("branch").read().strip()
64 self._branch = (self._opener("branch").read().strip()
65 or "default")
65 or "default")
66 except IOError:
66 except IOError:
67 self._branch = "default"
67 self._branch = "default"
68 return self._branch
68 return self._branch
69 elif name == '_pl':
69 elif name == '_pl':
70 self._pl = [nullid, nullid]
70 self._pl = [nullid, nullid]
71 try:
71 try:
72 st = self._opener("dirstate").read(40)
72 st = self._opener("dirstate").read(40)
73 if len(st) == 40:
73 if len(st) == 40:
74 self._pl = st[:20], st[20:40]
74 self._pl = st[:20], st[20:40]
75 except IOError, err:
75 except IOError, err:
76 if err.errno != errno.ENOENT: raise
76 if err.errno != errno.ENOENT: raise
77 return self._pl
77 return self._pl
78 elif name == '_dirs':
78 elif name == '_dirs':
79 dirs = {}
79 dirs = {}
80 for f,s in self._map.iteritems():
80 for f,s in self._map.iteritems():
81 if s[0] != 'r':
81 if s[0] != 'r':
82 _incdirs(dirs, f)
82 _incdirs(dirs, f)
83 self._dirs = dirs
83 self._dirs = dirs
84 return self._dirs
84 return self._dirs
85 elif name == '_ignore':
85 elif name == '_ignore':
86 files = [self._join('.hgignore')]
86 files = [self._join('.hgignore')]
87 for name, path in self._ui.configitems("ui"):
87 for name, path in self._ui.configitems("ui"):
88 if name == 'ignore' or name.startswith('ignore.'):
88 if name == 'ignore' or name.startswith('ignore.'):
89 files.append(os.path.expanduser(path))
89 files.append(os.path.expanduser(path))
90 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
90 self._ignore = ignore.ignore(self._root, files, self._ui.warn)
91 return self._ignore
91 return self._ignore
92 elif name == '_slash':
92 elif name == '_slash':
93 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
93 self._slash = self._ui.configbool('ui', 'slash') and os.sep != '/'
94 return self._slash
94 return self._slash
95 elif name == '_checklink':
95 elif name == '_checklink':
96 self._checklink = util.checklink(self._root)
96 self._checklink = util.checklink(self._root)
97 return self._checklink
97 return self._checklink
98 elif name == '_checkexec':
98 elif name == '_checkexec':
99 self._checkexec = util.checkexec(self._root)
99 self._checkexec = util.checkexec(self._root)
100 return self._checkexec
100 return self._checkexec
101 elif name == '_checkcase':
101 elif name == '_checkcase':
102 self._checkcase = not util.checkcase(self._join('.hg'))
102 self._checkcase = not util.checkcase(self._join('.hg'))
103 return self._checkcase
103 return self._checkcase
104 elif name == 'normalize':
104 elif name == 'normalize':
105 if self._checkcase:
105 if self._checkcase:
106 self.normalize = self._normalize
106 self.normalize = self._normalize
107 else:
107 else:
108 self.normalize = lambda x, y=False: x
108 self.normalize = lambda x, y=False: x
109 return self.normalize
109 return self.normalize
110 else:
110 else:
111 raise AttributeError(name)
111 raise AttributeError(name)
112
112
113 def _join(self, f):
113 def _join(self, f):
114 # much faster than os.path.join()
114 # much faster than os.path.join()
115 # it's safe because f is always a relative path
115 # it's safe because f is always a relative path
116 return self._rootdir + f
116 return self._rootdir + f
117
117
118 def flagfunc(self, fallback):
118 def flagfunc(self, fallback):
119 if self._checklink:
119 if self._checklink:
120 if self._checkexec:
120 if self._checkexec:
121 def f(x):
121 def f(x):
122 p = self._join(x)
122 p = self._join(x)
123 if os.path.islink(p):
123 if os.path.islink(p):
124 return 'l'
124 return 'l'
125 if util.is_exec(p):
125 if util.is_exec(p):
126 return 'x'
126 return 'x'
127 return ''
127 return ''
128 return f
128 return f
129 def f(x):
129 def f(x):
130 if os.path.islink(self._join(x)):
130 if os.path.islink(self._join(x)):
131 return 'l'
131 return 'l'
132 if 'x' in fallback(x):
132 if 'x' in fallback(x):
133 return 'x'
133 return 'x'
134 return ''
134 return ''
135 return f
135 return f
136 if self._checkexec:
136 if self._checkexec:
137 def f(x):
137 def f(x):
138 if 'l' in fallback(x):
138 if 'l' in fallback(x):
139 return 'l'
139 return 'l'
140 if util.is_exec(self._join(x)):
140 if util.is_exec(self._join(x)):
141 return 'x'
141 return 'x'
142 return ''
142 return ''
143 return f
143 return f
144 return fallback
144 return fallback
145
145
146 def getcwd(self):
146 def getcwd(self):
147 cwd = os.getcwd()
147 cwd = os.getcwd()
148 if cwd == self._root: return ''
148 if cwd == self._root: return ''
149 # self._root ends with a path separator if self._root is '/' or 'C:\'
149 # self._root ends with a path separator if self._root is '/' or 'C:\'
150 rootsep = self._root
150 rootsep = self._root
151 if not util.endswithsep(rootsep):
151 if not util.endswithsep(rootsep):
152 rootsep += os.sep
152 rootsep += os.sep
153 if cwd.startswith(rootsep):
153 if cwd.startswith(rootsep):
154 return cwd[len(rootsep):]
154 return cwd[len(rootsep):]
155 else:
155 else:
156 # we're outside the repo. return an absolute path.
156 # we're outside the repo. return an absolute path.
157 return cwd
157 return cwd
158
158
159 def pathto(self, f, cwd=None):
159 def pathto(self, f, cwd=None):
160 if cwd is None:
160 if cwd is None:
161 cwd = self.getcwd()
161 cwd = self.getcwd()
162 path = util.pathto(self._root, cwd, f)
162 path = util.pathto(self._root, cwd, f)
163 if self._slash:
163 if self._slash:
164 return util.normpath(path)
164 return util.normpath(path)
165 return path
165 return path
166
166
167 def __getitem__(self, key):
167 def __getitem__(self, key):
168 ''' current states:
168 ''' current states:
169 n normal
169 n normal
170 m needs merging
170 m needs merging
171 r marked for removal
171 r marked for removal
172 a marked for addition
172 a marked for addition
173 ? not tracked'''
173 ? not tracked'''
174 return self._map.get(key, ("?",))[0]
174 return self._map.get(key, ("?",))[0]
175
175
176 def __contains__(self, key):
176 def __contains__(self, key):
177 return key in self._map
177 return key in self._map
178
178
179 def __iter__(self):
179 def __iter__(self):
180 for x in util.sort(self._map):
180 for x in util.sort(self._map):
181 yield x
181 yield x
182
182
183 def parents(self):
183 def parents(self):
184 return self._pl
184 return self._pl
185
185
186 def branch(self):
186 def branch(self):
187 return self._branch
187 return self._branch
188
188
189 def setparents(self, p1, p2=nullid):
189 def setparents(self, p1, p2=nullid):
190 self._dirty = self._dirtypl = True
190 self._dirty = self._dirtypl = True
191 self._pl = p1, p2
191 self._pl = p1, p2
192
192
193 def setbranch(self, branch):
193 def setbranch(self, branch):
194 self._branch = branch
194 self._branch = branch
195 self._opener("branch", "w").write(branch + '\n')
195 self._opener("branch", "w").write(branch + '\n')
196
196
197 def _read(self):
197 def _read(self):
198 self._map = {}
198 self._map = {}
199 self._copymap = {}
199 self._copymap = {}
200 try:
200 try:
201 st = self._opener("dirstate").read()
201 st = self._opener("dirstate").read()
202 except IOError, err:
202 except IOError, err:
203 if err.errno != errno.ENOENT: raise
203 if err.errno != errno.ENOENT: raise
204 return
204 return
205 if not st:
205 if not st:
206 return
206 return
207
207
208 p = parsers.parse_dirstate(self._map, self._copymap, st);
208 p = parsers.parse_dirstate(self._map, self._copymap, st);
209 if not self._dirtypl:
209 if not self._dirtypl:
210 self._pl = p
210 self._pl = p
211
211
212 def invalidate(self):
212 def invalidate(self):
213 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
213 for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
214 if a in self.__dict__:
214 if a in self.__dict__:
215 delattr(self, a)
215 delattr(self, a)
216 self._dirty = False
216 self._dirty = False
217
217
218 def copy(self, source, dest):
218 def copy(self, source, dest):
219 if source == dest:
219 if source == dest:
220 return
220 return
221 self._dirty = True
221 self._dirty = True
222 self._copymap[dest] = source
222 self._copymap[dest] = source
223
223
224 def copied(self, file):
224 def copied(self, file):
225 return self._copymap.get(file, None)
225 return self._copymap.get(file, None)
226
226
227 def copies(self):
227 def copies(self):
228 return self._copymap
228 return self._copymap
229
229
230 def _droppath(self, f):
230 def _droppath(self, f):
231 if self[f] not in "?r" and "_dirs" in self.__dict__:
231 if self[f] not in "?r" and "_dirs" in self.__dict__:
232 _decdirs(self._dirs, f)
232 _decdirs(self._dirs, f)
233
233
234 def _addpath(self, f, check=False):
234 def _addpath(self, f, check=False):
235 oldstate = self[f]
235 oldstate = self[f]
236 if check or oldstate == "r":
236 if check or oldstate == "r":
237 if '\r' in f or '\n' in f:
237 if '\r' in f or '\n' in f:
238 raise util.Abort(
238 raise util.Abort(
239 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
239 _("'\\n' and '\\r' disallowed in filenames: %r") % f)
240 if f in self._dirs:
240 if f in self._dirs:
241 raise util.Abort(_('directory %r already in dirstate') % f)
241 raise util.Abort(_('directory %r already in dirstate') % f)
242 # shadows
242 # shadows
243 for d in _finddirs(f):
243 for d in _finddirs(f):
244 if d in self._dirs:
244 if d in self._dirs:
245 break
245 break
246 if d in self._map and self[d] != 'r':
246 if d in self._map and self[d] != 'r':
247 raise util.Abort(
247 raise util.Abort(
248 _('file %r in dirstate clashes with %r') % (d, f))
248 _('file %r in dirstate clashes with %r') % (d, f))
249 if oldstate in "?r" and "_dirs" in self.__dict__:
249 if oldstate in "?r" and "_dirs" in self.__dict__:
250 _incdirs(self._dirs, f)
250 _incdirs(self._dirs, f)
251
251
252 def normal(self, f):
252 def normal(self, f):
253 'mark a file normal and clean'
253 'mark a file normal and clean'
254 self._dirty = True
254 self._dirty = True
255 self._addpath(f)
255 self._addpath(f)
256 s = os.lstat(self._join(f))
256 s = os.lstat(self._join(f))
257 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime)
257 self._map[f] = ('n', s.st_mode, s.st_size, s.st_mtime)
258 if f in self._copymap:
258 if f in self._copymap:
259 del self._copymap[f]
259 del self._copymap[f]
260
260
261 def normallookup(self, f):
261 def normallookup(self, f):
262 'mark a file normal, but possibly dirty'
262 'mark a file normal, but possibly dirty'
263 if self._pl[1] != nullid and f in self._map:
263 if self._pl[1] != nullid and f in self._map:
264 # if there is a merge going on and the file was either
264 # if there is a merge going on and the file was either
265 # in state 'm' or dirty before being removed, restore that state.
265 # in state 'm' or dirty before being removed, restore that state.
266 entry = self._map[f]
266 entry = self._map[f]
267 if entry[0] == 'r' and entry[2] in (-1, -2):
267 if entry[0] == 'r' and entry[2] in (-1, -2):
268 source = self._copymap.get(f)
268 source = self._copymap.get(f)
269 if entry[2] == -1:
269 if entry[2] == -1:
270 self.merge(f)
270 self.merge(f)
271 elif entry[2] == -2:
271 elif entry[2] == -2:
272 self.normaldirty(f)
272 self.normaldirty(f)
273 if source:
273 if source:
274 self.copy(source, f)
274 self.copy(source, f)
275 return
275 return
276 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
276 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
277 return
277 return
278 self._dirty = True
278 self._dirty = True
279 self._addpath(f)
279 self._addpath(f)
280 self._map[f] = ('n', 0, -1, -1)
280 self._map[f] = ('n', 0, -1, -1)
281 if f in self._copymap:
281 if f in self._copymap:
282 del self._copymap[f]
282 del self._copymap[f]
283
283
284 def normaldirty(self, f):
284 def normaldirty(self, f):
285 'mark a file normal, but dirty'
285 'mark a file normal, but dirty'
286 self._dirty = True
286 self._dirty = True
287 self._addpath(f)
287 self._addpath(f)
288 self._map[f] = ('n', 0, -2, -1)
288 self._map[f] = ('n', 0, -2, -1)
289 if f in self._copymap:
289 if f in self._copymap:
290 del self._copymap[f]
290 del self._copymap[f]
291
291
292 def add(self, f):
292 def add(self, f):
293 'mark a file added'
293 'mark a file added'
294 self._dirty = True
294 self._dirty = True
295 self._addpath(f, True)
295 self._addpath(f, True)
296 self._map[f] = ('a', 0, -1, -1)
296 self._map[f] = ('a', 0, -1, -1)
297 if f in self._copymap:
297 if f in self._copymap:
298 del self._copymap[f]
298 del self._copymap[f]
299
299
300 def remove(self, f):
300 def remove(self, f):
301 'mark a file removed'
301 'mark a file removed'
302 self._dirty = True
302 self._dirty = True
303 self._droppath(f)
303 self._droppath(f)
304 size = 0
304 size = 0
305 if self._pl[1] != nullid and f in self._map:
305 if self._pl[1] != nullid and f in self._map:
306 entry = self._map[f]
306 entry = self._map[f]
307 if entry[0] == 'm':
307 if entry[0] == 'm':
308 size = -1
308 size = -1
309 elif entry[0] == 'n' and entry[2] == -2:
309 elif entry[0] == 'n' and entry[2] == -2:
310 size = -2
310 size = -2
311 self._map[f] = ('r', 0, size, 0)
311 self._map[f] = ('r', 0, size, 0)
312 if size == 0 and f in self._copymap:
312 if size == 0 and f in self._copymap:
313 del self._copymap[f]
313 del self._copymap[f]
314
314
315 def merge(self, f):
315 def merge(self, f):
316 'mark a file merged'
316 'mark a file merged'
317 self._dirty = True
317 self._dirty = True
318 s = os.lstat(self._join(f))
318 s = os.lstat(self._join(f))
319 self._addpath(f)
319 self._addpath(f)
320 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime)
320 self._map[f] = ('m', s.st_mode, s.st_size, s.st_mtime)
321 if f in self._copymap:
321 if f in self._copymap:
322 del self._copymap[f]
322 del self._copymap[f]
323
323
324 def forget(self, f):
324 def forget(self, f):
325 'forget a file'
325 'forget a file'
326 self._dirty = True
326 self._dirty = True
327 try:
327 try:
328 self._droppath(f)
328 self._droppath(f)
329 del self._map[f]
329 del self._map[f]
330 except KeyError:
330 except KeyError:
331 self._ui.warn(_("not in dirstate: %s\n") % f)
331 self._ui.warn(_("not in dirstate: %s\n") % f)
332
332
333 def _normalize(self, path, knownpath=False):
333 def _normalize(self, path, knownpath=False):
334 norm_path = os.path.normcase(path)
334 norm_path = os.path.normcase(path)
335 fold_path = self._foldmap.get(norm_path, None)
335 fold_path = self._foldmap.get(norm_path, None)
336 if fold_path is None:
336 if fold_path is None:
337 if knownpath or not os.path.exists(os.path.join(self._root, path)):
337 if knownpath or not os.path.exists(os.path.join(self._root, path)):
338 fold_path = path
338 fold_path = path
339 else:
339 else:
340 fold_path = self._foldmap.setdefault(norm_path,
340 fold_path = self._foldmap.setdefault(norm_path,
341 util.fspath(path, self._root))
341 util.fspath(path, self._root))
342 return fold_path
342 return fold_path
343
343
344 def clear(self):
344 def clear(self):
345 self._map = {}
345 self._map = {}
346 if "_dirs" in self.__dict__:
346 if "_dirs" in self.__dict__:
347 delattr(self, "_dirs");
347 delattr(self, "_dirs");
348 self._copymap = {}
348 self._copymap = {}
349 self._pl = [nullid, nullid]
349 self._pl = [nullid, nullid]
350 self._dirty = True
350 self._dirty = True
351
351
352 def rebuild(self, parent, files):
352 def rebuild(self, parent, files):
353 self.clear()
353 self.clear()
354 for f in files:
354 for f in files:
355 if 'x' in files.flags(f):
355 if 'x' in files.flags(f):
356 self._map[f] = ('n', 0777, -1, 0)
356 self._map[f] = ('n', 0777, -1, 0)
357 else:
357 else:
358 self._map[f] = ('n', 0666, -1, 0)
358 self._map[f] = ('n', 0666, -1, 0)
359 self._pl = (parent, nullid)
359 self._pl = (parent, nullid)
360 self._dirty = True
360 self._dirty = True
361
361
362 def write(self):
362 def write(self):
363 if not self._dirty:
363 if not self._dirty:
364 return
364 return
365 st = self._opener("dirstate", "w", atomictemp=True)
365 st = self._opener("dirstate", "w", atomictemp=True)
366
366
367 try:
367 try:
368 gran = int(self._ui.config('dirstate', 'granularity', 1))
368 gran = int(self._ui.config('dirstate', 'granularity', 1))
369 except ValueError:
369 except ValueError:
370 gran = 1
370 gran = 1
371 limit = sys.maxint
371 limit = sys.maxint
372 if gran > 0:
372 if gran > 0:
373 limit = util.fstat(st).st_mtime - gran
373 limit = util.fstat(st).st_mtime - gran
374
374
375 cs = cStringIO.StringIO()
375 cs = cStringIO.StringIO()
376 copymap = self._copymap
376 copymap = self._copymap
377 pack = struct.pack
377 pack = struct.pack
378 write = cs.write
378 write = cs.write
379 write("".join(self._pl))
379 write("".join(self._pl))
380 for f, e in self._map.iteritems():
380 for f, e in self._map.iteritems():
381 if f in copymap:
381 if f in copymap:
382 f = "%s\0%s" % (f, copymap[f])
382 f = "%s\0%s" % (f, copymap[f])
383 if e[3] > limit and e[0] == 'n':
383 if e[3] > limit and e[0] == 'n':
384 e = (e[0], 0, -1, -1)
384 e = (e[0], 0, -1, -1)
385 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
385 e = pack(_format, e[0], e[1], e[2], e[3], len(f))
386 write(e)
386 write(e)
387 write(f)
387 write(f)
388 st.write(cs.getvalue())
388 st.write(cs.getvalue())
389 st.rename()
389 st.rename()
390 self._dirty = self._dirtypl = False
390 self._dirty = self._dirtypl = False
391
391
392 def _dirignore(self, f):
392 def _dirignore(self, f):
393 if f == '.':
393 if f == '.':
394 return False
394 return False
395 if self._ignore(f):
395 if self._ignore(f):
396 return True
396 return True
397 for p in _finddirs(f):
397 for p in _finddirs(f):
398 if self._ignore(p):
398 if self._ignore(p):
399 return True
399 return True
400 return False
400 return False
401
401
402 def walk(self, match, unknown, ignored):
402 def walk(self, match, unknown, ignored):
403 '''
403 '''
404 walk recursively through the directory tree, finding all files
404 walk recursively through the directory tree, finding all files
405 matched by the match function
405 matched by the match function
406
406
407 results are yielded in a tuple (filename, stat), where stat
407 results are yielded in a tuple (filename, stat), where stat
408 and st is the stat result if the file was found in the directory.
408 and st is the stat result if the file was found in the directory.
409 '''
409 '''
410
410
411 def fwarn(f, msg):
411 def fwarn(f, msg):
412 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
412 self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
413 return False
413 return False
414 badfn = fwarn
414 badfn = fwarn
415 if hasattr(match, 'bad'):
415 if hasattr(match, 'bad'):
416 badfn = match.bad
416 badfn = match.bad
417
417
418 def badtype(f, mode):
418 def badtype(f, mode):
419 kind = 'unknown'
419 kind = 'unknown'
420 if stat.S_ISCHR(mode): kind = _('character device')
420 if stat.S_ISCHR(mode): kind = _('character device')
421 elif stat.S_ISBLK(mode): kind = _('block device')
421 elif stat.S_ISBLK(mode): kind = _('block device')
422 elif stat.S_ISFIFO(mode): kind = _('fifo')
422 elif stat.S_ISFIFO(mode): kind = _('fifo')
423 elif stat.S_ISSOCK(mode): kind = _('socket')
423 elif stat.S_ISSOCK(mode): kind = _('socket')
424 elif stat.S_ISDIR(mode): kind = _('directory')
424 elif stat.S_ISDIR(mode): kind = _('directory')
425 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
425 self._ui.warn(_('%s: unsupported file type (type is %s)\n')
426 % (self.pathto(f), kind))
426 % (self.pathto(f), kind))
427
427
428 ignore = self._ignore
428 ignore = self._ignore
429 dirignore = self._dirignore
429 dirignore = self._dirignore
430 if ignored:
430 if ignored:
431 ignore = util.never
431 ignore = util.never
432 dirignore = util.never
432 dirignore = util.never
433 elif not unknown:
433 elif not unknown:
434 # if unknown and ignored are False, skip step 2
434 # if unknown and ignored are False, skip step 2
435 ignore = util.always
435 ignore = util.always
436 dirignore = util.always
436 dirignore = util.always
437
437
438 matchfn = match.matchfn
438 matchfn = match.matchfn
439 dmap = self._map
439 dmap = self._map
440 normpath = util.normpath
440 normpath = util.normpath
441 normalize = self.normalize
441 normalize = self.normalize
442 listdir = osutil.listdir
442 listdir = osutil.listdir
443 lstat = os.lstat
443 lstat = os.lstat
444 pconvert = util.pconvert
444 pconvert = util.pconvert
445 getkind = stat.S_IFMT
445 getkind = stat.S_IFMT
446 dirkind = stat.S_IFDIR
446 dirkind = stat.S_IFDIR
447 regkind = stat.S_IFREG
447 regkind = stat.S_IFREG
448 lnkkind = stat.S_IFLNK
448 lnkkind = stat.S_IFLNK
449 join = self._join
449 join = self._join
450 work = []
450 work = []
451 wadd = work.append
451 wadd = work.append
452
452
453 files = util.unique(match.files())
453 files = util.unique(match.files())
454 if not files or '.' in files:
454 if not files or '.' in files:
455 files = ['']
455 files = ['']
456 results = {'.hg': None}
456 results = {'.hg': None}
457
457
458 # step 1: find all explicit files
458 # step 1: find all explicit files
459 for ff in util.sort(files):
459 for ff in util.sort(files):
460 nf = normalize(normpath(ff))
460 nf = normalize(normpath(ff))
461 if nf in results:
461 if nf in results:
462 continue
462 continue
463
463
464 try:
464 try:
465 st = lstat(join(nf))
465 st = lstat(join(nf))
466 kind = getkind(st.st_mode)
466 kind = getkind(st.st_mode)
467 if kind == dirkind:
467 if kind == dirkind:
468 if not dirignore(nf):
468 if not dirignore(nf):
469 wadd(nf)
469 wadd(nf)
470 elif kind == regkind or kind == lnkkind:
470 elif kind == regkind or kind == lnkkind:
471 results[nf] = st
471 results[nf] = st
472 else:
472 else:
473 badtype(ff, kind)
473 badtype(ff, kind)
474 if nf in dmap:
474 if nf in dmap:
475 results[nf] = None
475 results[nf] = None
476 except OSError, inst:
476 except OSError, inst:
477 keep = False
477 keep = False
478 prefix = nf + "/"
478 prefix = nf + "/"
479 for fn in dmap:
479 for fn in dmap:
480 if nf == fn or fn.startswith(prefix):
480 if nf == fn or fn.startswith(prefix):
481 keep = True
481 keep = True
482 break
482 break
483 if not keep:
483 if not keep:
484 if inst.errno != errno.ENOENT:
484 if inst.errno != errno.ENOENT:
485 fwarn(ff, inst.strerror)
485 fwarn(ff, inst.strerror)
486 elif badfn(ff, inst.strerror):
486 elif badfn(ff, inst.strerror):
487 if (nf in dmap or not ignore(nf)) and matchfn(nf):
487 if (nf in dmap or not ignore(nf)) and matchfn(nf):
488 results[nf] = None
488 results[nf] = None
489
489
490 # step 2: visit subdirectories
490 # step 2: visit subdirectories
491 while work:
491 while work:
492 nd = work.pop()
492 nd = work.pop()
493 if hasattr(match, 'dir'):
493 if hasattr(match, 'dir'):
494 match.dir(nd)
494 match.dir(nd)
495 skip = None
495 skip = None
496 if nd == '.':
496 if nd == '.':
497 nd = ''
497 nd = ''
498 else:
498 else:
499 skip = '.hg'
499 skip = '.hg'
500 try:
500 try:
501 entries = listdir(join(nd), stat=True, skip=skip)
501 entries = listdir(join(nd), stat=True, skip=skip)
502 except OSError, inst:
502 except OSError, inst:
503 if inst.errno == errno.EACCES:
503 if inst.errno == errno.EACCES:
504 fwarn(nd, inst.strerror)
504 fwarn(nd, inst.strerror)
505 continue
505 continue
506 raise
506 raise
507 for f, kind, st in entries:
507 for f, kind, st in entries:
508 nf = normalize(nd and (nd + "/" + f) or f, True)
508 nf = normalize(nd and (nd + "/" + f) or f, True)
509 if nf not in results:
509 if nf not in results:
510 if kind == dirkind:
510 if kind == dirkind:
511 if not ignore(nf):
511 if not ignore(nf):
512 wadd(nf)
512 wadd(nf)
513 if nf in dmap and matchfn(nf):
513 if nf in dmap and matchfn(nf):
514 results[nf] = None
514 results[nf] = None
515 elif kind == regkind or kind == lnkkind:
515 elif kind == regkind or kind == lnkkind:
516 if nf in dmap:
516 if nf in dmap:
517 if matchfn(nf):
517 if matchfn(nf):
518 results[nf] = st
518 results[nf] = st
519 elif matchfn(nf) and not ignore(nf):
519 elif matchfn(nf) and not ignore(nf):
520 results[nf] = st
520 results[nf] = st
521 elif nf in dmap and matchfn(nf):
521 elif nf in dmap and matchfn(nf):
522 results[nf] = None
522 results[nf] = None
523
523
524 # step 3: report unseen items in the dmap hash
524 # step 3: report unseen items in the dmap hash
525 visit = [f for f in dmap if f not in results and match(f)]
525 visit = util.sort([f for f in dmap if f not in results and match(f)])
526 for nf in util.sort(visit):
526 for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
527 results[nf] = None
527 if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
528 try:
528 st = None
529 st = lstat(join(nf))
530 kind = getkind(st.st_mode)
531 if kind == regkind or kind == lnkkind:
532 results[nf] = st
529 results[nf] = st
533 except OSError, inst:
534 if inst.errno not in (errno.ENOENT, errno.ENOTDIR):
535 raise
536
530
537 del results['.hg']
531 del results['.hg']
538 return results
532 return results
539
533
540 def status(self, match, ignored, clean, unknown):
534 def status(self, match, ignored, clean, unknown):
541 listignored, listclean, listunknown = ignored, clean, unknown
535 listignored, listclean, listunknown = ignored, clean, unknown
542 lookup, modified, added, unknown, ignored = [], [], [], [], []
536 lookup, modified, added, unknown, ignored = [], [], [], [], []
543 removed, deleted, clean = [], [], []
537 removed, deleted, clean = [], [], []
544
538
545 _join = self._join
539 _join = self._join
546 lstat = os.lstat
540 lstat = os.lstat
547 cmap = self._copymap
541 cmap = self._copymap
548 dmap = self._map
542 dmap = self._map
549 ladd = lookup.append
543 ladd = lookup.append
550 madd = modified.append
544 madd = modified.append
551 aadd = added.append
545 aadd = added.append
552 uadd = unknown.append
546 uadd = unknown.append
553 iadd = ignored.append
547 iadd = ignored.append
554 radd = removed.append
548 radd = removed.append
555 dadd = deleted.append
549 dadd = deleted.append
556 cadd = clean.append
550 cadd = clean.append
557
551
558 for fn, st in self.walk(match, listunknown, listignored).iteritems():
552 for fn, st in self.walk(match, listunknown, listignored).iteritems():
559 if fn not in dmap:
553 if fn not in dmap:
560 if (listignored or match.exact(fn)) and self._dirignore(fn):
554 if (listignored or match.exact(fn)) and self._dirignore(fn):
561 if listignored:
555 if listignored:
562 iadd(fn)
556 iadd(fn)
563 elif listunknown:
557 elif listunknown:
564 uadd(fn)
558 uadd(fn)
565 continue
559 continue
566
560
567 state, mode, size, time = dmap[fn]
561 state, mode, size, time = dmap[fn]
568
562
569 if not st and state in "nma":
563 if not st and state in "nma":
570 dadd(fn)
564 dadd(fn)
571 elif state == 'n':
565 elif state == 'n':
572 if (size >= 0 and
566 if (size >= 0 and
573 (size != st.st_size
567 (size != st.st_size
574 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
568 or ((mode ^ st.st_mode) & 0100 and self._checkexec))
575 or size == -2
569 or size == -2
576 or fn in self._copymap):
570 or fn in self._copymap):
577 madd(fn)
571 madd(fn)
578 elif time != int(st.st_mtime):
572 elif time != int(st.st_mtime):
579 ladd(fn)
573 ladd(fn)
580 elif listclean:
574 elif listclean:
581 cadd(fn)
575 cadd(fn)
582 elif state == 'm':
576 elif state == 'm':
583 madd(fn)
577 madd(fn)
584 elif state == 'a':
578 elif state == 'a':
585 aadd(fn)
579 aadd(fn)
586 elif state == 'r':
580 elif state == 'r':
587 radd(fn)
581 radd(fn)
588
582
589 return (lookup, modified, added, removed, deleted, unknown, ignored,
583 return (lookup, modified, added, removed, deleted, unknown, ignored,
590 clean)
584 clean)
@@ -1,1893 +1,1939 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18 import imp, urlparse
18 import imp, urlparse
19
19
20 # Python compatibility
20 # Python compatibility
21
21
22 try:
22 try:
23 set = set
23 set = set
24 frozenset = frozenset
24 frozenset = frozenset
25 except NameError:
25 except NameError:
26 from sets import Set as set, ImmutableSet as frozenset
26 from sets import Set as set, ImmutableSet as frozenset
27
27
28 _md5 = None
28 _md5 = None
29 def md5(s):
29 def md5(s):
30 global _md5
30 global _md5
31 if _md5 is None:
31 if _md5 is None:
32 try:
32 try:
33 import hashlib
33 import hashlib
34 _md5 = hashlib.md5
34 _md5 = hashlib.md5
35 except ImportError:
35 except ImportError:
36 import md5
36 import md5
37 _md5 = md5.md5
37 _md5 = md5.md5
38 return _md5(s)
38 return _md5(s)
39
39
40 _sha1 = None
40 _sha1 = None
41 def sha1(s):
41 def sha1(s):
42 global _sha1
42 global _sha1
43 if _sha1 is None:
43 if _sha1 is None:
44 try:
44 try:
45 import hashlib
45 import hashlib
46 _sha1 = hashlib.sha1
46 _sha1 = hashlib.sha1
47 except ImportError:
47 except ImportError:
48 import sha
48 import sha
49 _sha1 = sha.sha
49 _sha1 = sha.sha
50 return _sha1(s)
50 return _sha1(s)
51
51
52 try:
52 try:
53 import subprocess
53 import subprocess
54 def popen2(cmd, mode='t', bufsize=-1):
54 def popen2(cmd, mode='t', bufsize=-1):
55 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize, close_fds=True,
55 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize, close_fds=True,
56 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
56 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
57 return p.stdin, p.stdout
57 return p.stdin, p.stdout
58 def popen3(cmd, mode='t', bufsize=-1):
58 def popen3(cmd, mode='t', bufsize=-1):
59 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize, close_fds=True,
59 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize, close_fds=True,
60 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
60 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
61 stderr=subprocess.PIPE)
61 stderr=subprocess.PIPE)
62 return p.stdin, p.stdout, p.stderr
62 return p.stdin, p.stdout, p.stderr
63 def Popen3(cmd, capturestderr=False, bufsize=-1):
63 def Popen3(cmd, capturestderr=False, bufsize=-1):
64 stderr = capturestderr and subprocess.PIPE or None
64 stderr = capturestderr and subprocess.PIPE or None
65 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize, close_fds=True,
65 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize, close_fds=True,
66 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
66 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
67 stderr=stderr)
67 stderr=stderr)
68 p.fromchild = p.stdout
68 p.fromchild = p.stdout
69 p.tochild = p.stdin
69 p.tochild = p.stdin
70 p.childerr = p.stderr
70 p.childerr = p.stderr
71 return p
71 return p
72 except ImportError:
72 except ImportError:
73 subprocess = None
73 subprocess = None
74 import popen2 as _popen2
74 import popen2 as _popen2
75 popen2 = _popen2.popen2
75 popen2 = _popen2.popen2
76 Popen3 = _popen2.Popen3
76 Popen3 = _popen2.Popen3
77
77
78
78
79 try:
79 try:
80 _encoding = os.environ.get("HGENCODING")
80 _encoding = os.environ.get("HGENCODING")
81 if sys.platform == 'darwin' and not _encoding:
81 if sys.platform == 'darwin' and not _encoding:
82 # On darwin, getpreferredencoding ignores the locale environment and
82 # On darwin, getpreferredencoding ignores the locale environment and
83 # always returns mac-roman. We override this if the environment is
83 # always returns mac-roman. We override this if the environment is
84 # not C (has been customized by the user).
84 # not C (has been customized by the user).
85 locale.setlocale(locale.LC_CTYPE, '')
85 locale.setlocale(locale.LC_CTYPE, '')
86 _encoding = locale.getlocale()[1]
86 _encoding = locale.getlocale()[1]
87 if not _encoding:
87 if not _encoding:
88 _encoding = locale.getpreferredencoding() or 'ascii'
88 _encoding = locale.getpreferredencoding() or 'ascii'
89 except locale.Error:
89 except locale.Error:
90 _encoding = 'ascii'
90 _encoding = 'ascii'
91 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
91 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
92 _fallbackencoding = 'ISO-8859-1'
92 _fallbackencoding = 'ISO-8859-1'
93
93
94 def tolocal(s):
94 def tolocal(s):
95 """
95 """
96 Convert a string from internal UTF-8 to local encoding
96 Convert a string from internal UTF-8 to local encoding
97
97
98 All internal strings should be UTF-8 but some repos before the
98 All internal strings should be UTF-8 but some repos before the
99 implementation of locale support may contain latin1 or possibly
99 implementation of locale support may contain latin1 or possibly
100 other character sets. We attempt to decode everything strictly
100 other character sets. We attempt to decode everything strictly
101 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
101 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
102 replace unknown characters.
102 replace unknown characters.
103 """
103 """
104 for e in ('UTF-8', _fallbackencoding):
104 for e in ('UTF-8', _fallbackencoding):
105 try:
105 try:
106 u = s.decode(e) # attempt strict decoding
106 u = s.decode(e) # attempt strict decoding
107 return u.encode(_encoding, "replace")
107 return u.encode(_encoding, "replace")
108 except LookupError, k:
108 except LookupError, k:
109 raise Abort(_("%s, please check your locale settings") % k)
109 raise Abort(_("%s, please check your locale settings") % k)
110 except UnicodeDecodeError:
110 except UnicodeDecodeError:
111 pass
111 pass
112 u = s.decode("utf-8", "replace") # last ditch
112 u = s.decode("utf-8", "replace") # last ditch
113 return u.encode(_encoding, "replace")
113 return u.encode(_encoding, "replace")
114
114
115 def fromlocal(s):
115 def fromlocal(s):
116 """
116 """
117 Convert a string from the local character encoding to UTF-8
117 Convert a string from the local character encoding to UTF-8
118
118
119 We attempt to decode strings using the encoding mode set by
119 We attempt to decode strings using the encoding mode set by
120 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
120 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
121 characters will cause an error message. Other modes include
121 characters will cause an error message. Other modes include
122 'replace', which replaces unknown characters with a special
122 'replace', which replaces unknown characters with a special
123 Unicode character, and 'ignore', which drops the character.
123 Unicode character, and 'ignore', which drops the character.
124 """
124 """
125 try:
125 try:
126 return s.decode(_encoding, _encodingmode).encode("utf-8")
126 return s.decode(_encoding, _encodingmode).encode("utf-8")
127 except UnicodeDecodeError, inst:
127 except UnicodeDecodeError, inst:
128 sub = s[max(0, inst.start-10):inst.start+10]
128 sub = s[max(0, inst.start-10):inst.start+10]
129 raise Abort("decoding near '%s': %s!" % (sub, inst))
129 raise Abort("decoding near '%s': %s!" % (sub, inst))
130 except LookupError, k:
130 except LookupError, k:
131 raise Abort(_("%s, please check your locale settings") % k)
131 raise Abort(_("%s, please check your locale settings") % k)
132
132
133 def locallen(s):
133 def locallen(s):
134 """Find the length in characters of a local string"""
134 """Find the length in characters of a local string"""
135 return len(s.decode(_encoding, "replace"))
135 return len(s.decode(_encoding, "replace"))
136
136
137 # used by parsedate
137 # used by parsedate
138 defaultdateformats = (
138 defaultdateformats = (
139 '%Y-%m-%d %H:%M:%S',
139 '%Y-%m-%d %H:%M:%S',
140 '%Y-%m-%d %I:%M:%S%p',
140 '%Y-%m-%d %I:%M:%S%p',
141 '%Y-%m-%d %H:%M',
141 '%Y-%m-%d %H:%M',
142 '%Y-%m-%d %I:%M%p',
142 '%Y-%m-%d %I:%M%p',
143 '%Y-%m-%d',
143 '%Y-%m-%d',
144 '%m-%d',
144 '%m-%d',
145 '%m/%d',
145 '%m/%d',
146 '%m/%d/%y',
146 '%m/%d/%y',
147 '%m/%d/%Y',
147 '%m/%d/%Y',
148 '%a %b %d %H:%M:%S %Y',
148 '%a %b %d %H:%M:%S %Y',
149 '%a %b %d %I:%M:%S%p %Y',
149 '%a %b %d %I:%M:%S%p %Y',
150 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
150 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
151 '%b %d %H:%M:%S %Y',
151 '%b %d %H:%M:%S %Y',
152 '%b %d %I:%M:%S%p %Y',
152 '%b %d %I:%M:%S%p %Y',
153 '%b %d %H:%M:%S',
153 '%b %d %H:%M:%S',
154 '%b %d %I:%M:%S%p',
154 '%b %d %I:%M:%S%p',
155 '%b %d %H:%M',
155 '%b %d %H:%M',
156 '%b %d %I:%M%p',
156 '%b %d %I:%M%p',
157 '%b %d %Y',
157 '%b %d %Y',
158 '%b %d',
158 '%b %d',
159 '%H:%M:%S',
159 '%H:%M:%S',
160 '%I:%M:%SP',
160 '%I:%M:%SP',
161 '%H:%M',
161 '%H:%M',
162 '%I:%M%p',
162 '%I:%M%p',
163 )
163 )
164
164
165 extendeddateformats = defaultdateformats + (
165 extendeddateformats = defaultdateformats + (
166 "%Y",
166 "%Y",
167 "%Y-%m",
167 "%Y-%m",
168 "%b",
168 "%b",
169 "%b %Y",
169 "%b %Y",
170 )
170 )
171
171
172 class SignalInterrupt(Exception):
172 class SignalInterrupt(Exception):
173 """Exception raised on SIGTERM and SIGHUP."""
173 """Exception raised on SIGTERM and SIGHUP."""
174
174
175 # differences from SafeConfigParser:
175 # differences from SafeConfigParser:
176 # - case-sensitive keys
176 # - case-sensitive keys
177 # - allows values that are not strings (this means that you may not
177 # - allows values that are not strings (this means that you may not
178 # be able to save the configuration to a file)
178 # be able to save the configuration to a file)
179 class configparser(ConfigParser.SafeConfigParser):
179 class configparser(ConfigParser.SafeConfigParser):
180 def optionxform(self, optionstr):
180 def optionxform(self, optionstr):
181 return optionstr
181 return optionstr
182
182
183 def set(self, section, option, value):
183 def set(self, section, option, value):
184 return ConfigParser.ConfigParser.set(self, section, option, value)
184 return ConfigParser.ConfigParser.set(self, section, option, value)
185
185
186 def _interpolate(self, section, option, rawval, vars):
186 def _interpolate(self, section, option, rawval, vars):
187 if not isinstance(rawval, basestring):
187 if not isinstance(rawval, basestring):
188 return rawval
188 return rawval
189 return ConfigParser.SafeConfigParser._interpolate(self, section,
189 return ConfigParser.SafeConfigParser._interpolate(self, section,
190 option, rawval, vars)
190 option, rawval, vars)
191
191
192 def cachefunc(func):
192 def cachefunc(func):
193 '''cache the result of function calls'''
193 '''cache the result of function calls'''
194 # XXX doesn't handle keywords args
194 # XXX doesn't handle keywords args
195 cache = {}
195 cache = {}
196 if func.func_code.co_argcount == 1:
196 if func.func_code.co_argcount == 1:
197 # we gain a small amount of time because
197 # we gain a small amount of time because
198 # we don't need to pack/unpack the list
198 # we don't need to pack/unpack the list
199 def f(arg):
199 def f(arg):
200 if arg not in cache:
200 if arg not in cache:
201 cache[arg] = func(arg)
201 cache[arg] = func(arg)
202 return cache[arg]
202 return cache[arg]
203 else:
203 else:
204 def f(*args):
204 def f(*args):
205 if args not in cache:
205 if args not in cache:
206 cache[args] = func(*args)
206 cache[args] = func(*args)
207 return cache[args]
207 return cache[args]
208
208
209 return f
209 return f
210
210
211 def pipefilter(s, cmd):
211 def pipefilter(s, cmd):
212 '''filter string S through command CMD, returning its output'''
212 '''filter string S through command CMD, returning its output'''
213 (pin, pout) = popen2(cmd, 'b')
213 (pin, pout) = popen2(cmd, 'b')
214 def writer():
214 def writer():
215 try:
215 try:
216 pin.write(s)
216 pin.write(s)
217 pin.close()
217 pin.close()
218 except IOError, inst:
218 except IOError, inst:
219 if inst.errno != errno.EPIPE:
219 if inst.errno != errno.EPIPE:
220 raise
220 raise
221
221
222 # we should use select instead on UNIX, but this will work on most
222 # we should use select instead on UNIX, but this will work on most
223 # systems, including Windows
223 # systems, including Windows
224 w = threading.Thread(target=writer)
224 w = threading.Thread(target=writer)
225 w.start()
225 w.start()
226 f = pout.read()
226 f = pout.read()
227 pout.close()
227 pout.close()
228 w.join()
228 w.join()
229 return f
229 return f
230
230
231 def tempfilter(s, cmd):
231 def tempfilter(s, cmd):
232 '''filter string S through a pair of temporary files with CMD.
232 '''filter string S through a pair of temporary files with CMD.
233 CMD is used as a template to create the real command to be run,
233 CMD is used as a template to create the real command to be run,
234 with the strings INFILE and OUTFILE replaced by the real names of
234 with the strings INFILE and OUTFILE replaced by the real names of
235 the temporary files generated.'''
235 the temporary files generated.'''
236 inname, outname = None, None
236 inname, outname = None, None
237 try:
237 try:
238 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
238 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
239 fp = os.fdopen(infd, 'wb')
239 fp = os.fdopen(infd, 'wb')
240 fp.write(s)
240 fp.write(s)
241 fp.close()
241 fp.close()
242 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
242 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
243 os.close(outfd)
243 os.close(outfd)
244 cmd = cmd.replace('INFILE', inname)
244 cmd = cmd.replace('INFILE', inname)
245 cmd = cmd.replace('OUTFILE', outname)
245 cmd = cmd.replace('OUTFILE', outname)
246 code = os.system(cmd)
246 code = os.system(cmd)
247 if sys.platform == 'OpenVMS' and code & 1:
247 if sys.platform == 'OpenVMS' and code & 1:
248 code = 0
248 code = 0
249 if code: raise Abort(_("command '%s' failed: %s") %
249 if code: raise Abort(_("command '%s' failed: %s") %
250 (cmd, explain_exit(code)))
250 (cmd, explain_exit(code)))
251 return open(outname, 'rb').read()
251 return open(outname, 'rb').read()
252 finally:
252 finally:
253 try:
253 try:
254 if inname: os.unlink(inname)
254 if inname: os.unlink(inname)
255 except: pass
255 except: pass
256 try:
256 try:
257 if outname: os.unlink(outname)
257 if outname: os.unlink(outname)
258 except: pass
258 except: pass
259
259
260 filtertable = {
260 filtertable = {
261 'tempfile:': tempfilter,
261 'tempfile:': tempfilter,
262 'pipe:': pipefilter,
262 'pipe:': pipefilter,
263 }
263 }
264
264
265 def filter(s, cmd):
265 def filter(s, cmd):
266 "filter a string through a command that transforms its input to its output"
266 "filter a string through a command that transforms its input to its output"
267 for name, fn in filtertable.iteritems():
267 for name, fn in filtertable.iteritems():
268 if cmd.startswith(name):
268 if cmd.startswith(name):
269 return fn(s, cmd[len(name):].lstrip())
269 return fn(s, cmd[len(name):].lstrip())
270 return pipefilter(s, cmd)
270 return pipefilter(s, cmd)
271
271
272 def binary(s):
272 def binary(s):
273 """return true if a string is binary data"""
273 """return true if a string is binary data"""
274 if s and '\0' in s:
274 if s and '\0' in s:
275 return True
275 return True
276 return False
276 return False
277
277
278 def unique(g):
278 def unique(g):
279 """return the uniq elements of iterable g"""
279 """return the uniq elements of iterable g"""
280 return dict.fromkeys(g).keys()
280 return dict.fromkeys(g).keys()
281
281
282 def sort(l):
282 def sort(l):
283 if not isinstance(l, list):
283 if not isinstance(l, list):
284 l = list(l)
284 l = list(l)
285 l.sort()
285 l.sort()
286 return l
286 return l
287
287
288 class Abort(Exception):
288 class Abort(Exception):
289 """Raised if a command needs to print an error and exit."""
289 """Raised if a command needs to print an error and exit."""
290
290
291 class UnexpectedOutput(Abort):
291 class UnexpectedOutput(Abort):
292 """Raised to print an error with part of output and exit."""
292 """Raised to print an error with part of output and exit."""
293
293
294 def always(fn): return True
294 def always(fn): return True
295 def never(fn): return False
295 def never(fn): return False
296
296
297 def expand_glob(pats):
297 def expand_glob(pats):
298 '''On Windows, expand the implicit globs in a list of patterns'''
298 '''On Windows, expand the implicit globs in a list of patterns'''
299 if os.name != 'nt':
299 if os.name != 'nt':
300 return list(pats)
300 return list(pats)
301 ret = []
301 ret = []
302 for p in pats:
302 for p in pats:
303 kind, name = patkind(p, None)
303 kind, name = patkind(p, None)
304 if kind is None:
304 if kind is None:
305 globbed = glob.glob(name)
305 globbed = glob.glob(name)
306 if globbed:
306 if globbed:
307 ret.extend(globbed)
307 ret.extend(globbed)
308 continue
308 continue
309 # if we couldn't expand the glob, just keep it around
309 # if we couldn't expand the glob, just keep it around
310 ret.append(p)
310 ret.append(p)
311 return ret
311 return ret
312
312
313 def patkind(name, default):
313 def patkind(name, default):
314 """Split a string into an optional pattern kind prefix and the
314 """Split a string into an optional pattern kind prefix and the
315 actual pattern."""
315 actual pattern."""
316 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
316 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
317 if name.startswith(prefix + ':'): return name.split(':', 1)
317 if name.startswith(prefix + ':'): return name.split(':', 1)
318 return default, name
318 return default, name
319
319
320 def globre(pat, head='^', tail='$'):
320 def globre(pat, head='^', tail='$'):
321 "convert a glob pattern into a regexp"
321 "convert a glob pattern into a regexp"
322 i, n = 0, len(pat)
322 i, n = 0, len(pat)
323 res = ''
323 res = ''
324 group = 0
324 group = 0
325 def peek(): return i < n and pat[i]
325 def peek(): return i < n and pat[i]
326 while i < n:
326 while i < n:
327 c = pat[i]
327 c = pat[i]
328 i = i+1
328 i = i+1
329 if c == '*':
329 if c == '*':
330 if peek() == '*':
330 if peek() == '*':
331 i += 1
331 i += 1
332 res += '.*'
332 res += '.*'
333 else:
333 else:
334 res += '[^/]*'
334 res += '[^/]*'
335 elif c == '?':
335 elif c == '?':
336 res += '.'
336 res += '.'
337 elif c == '[':
337 elif c == '[':
338 j = i
338 j = i
339 if j < n and pat[j] in '!]':
339 if j < n and pat[j] in '!]':
340 j += 1
340 j += 1
341 while j < n and pat[j] != ']':
341 while j < n and pat[j] != ']':
342 j += 1
342 j += 1
343 if j >= n:
343 if j >= n:
344 res += '\\['
344 res += '\\['
345 else:
345 else:
346 stuff = pat[i:j].replace('\\','\\\\')
346 stuff = pat[i:j].replace('\\','\\\\')
347 i = j + 1
347 i = j + 1
348 if stuff[0] == '!':
348 if stuff[0] == '!':
349 stuff = '^' + stuff[1:]
349 stuff = '^' + stuff[1:]
350 elif stuff[0] == '^':
350 elif stuff[0] == '^':
351 stuff = '\\' + stuff
351 stuff = '\\' + stuff
352 res = '%s[%s]' % (res, stuff)
352 res = '%s[%s]' % (res, stuff)
353 elif c == '{':
353 elif c == '{':
354 group += 1
354 group += 1
355 res += '(?:'
355 res += '(?:'
356 elif c == '}' and group:
356 elif c == '}' and group:
357 res += ')'
357 res += ')'
358 group -= 1
358 group -= 1
359 elif c == ',' and group:
359 elif c == ',' and group:
360 res += '|'
360 res += '|'
361 elif c == '\\':
361 elif c == '\\':
362 p = peek()
362 p = peek()
363 if p:
363 if p:
364 i += 1
364 i += 1
365 res += re.escape(p)
365 res += re.escape(p)
366 else:
366 else:
367 res += re.escape(c)
367 res += re.escape(c)
368 else:
368 else:
369 res += re.escape(c)
369 res += re.escape(c)
370 return head + res + tail
370 return head + res + tail
371
371
372 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
372 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
373
373
374 def pathto(root, n1, n2):
374 def pathto(root, n1, n2):
375 '''return the relative path from one place to another.
375 '''return the relative path from one place to another.
376 root should use os.sep to separate directories
376 root should use os.sep to separate directories
377 n1 should use os.sep to separate directories
377 n1 should use os.sep to separate directories
378 n2 should use "/" to separate directories
378 n2 should use "/" to separate directories
379 returns an os.sep-separated path.
379 returns an os.sep-separated path.
380
380
381 If n1 is a relative path, it's assumed it's
381 If n1 is a relative path, it's assumed it's
382 relative to root.
382 relative to root.
383 n2 should always be relative to root.
383 n2 should always be relative to root.
384 '''
384 '''
385 if not n1: return localpath(n2)
385 if not n1: return localpath(n2)
386 if os.path.isabs(n1):
386 if os.path.isabs(n1):
387 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
387 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
388 return os.path.join(root, localpath(n2))
388 return os.path.join(root, localpath(n2))
389 n2 = '/'.join((pconvert(root), n2))
389 n2 = '/'.join((pconvert(root), n2))
390 a, b = splitpath(n1), n2.split('/')
390 a, b = splitpath(n1), n2.split('/')
391 a.reverse()
391 a.reverse()
392 b.reverse()
392 b.reverse()
393 while a and b and a[-1] == b[-1]:
393 while a and b and a[-1] == b[-1]:
394 a.pop()
394 a.pop()
395 b.pop()
395 b.pop()
396 b.reverse()
396 b.reverse()
397 return os.sep.join((['..'] * len(a)) + b) or '.'
397 return os.sep.join((['..'] * len(a)) + b) or '.'
398
398
399 def canonpath(root, cwd, myname):
399 def canonpath(root, cwd, myname):
400 """return the canonical path of myname, given cwd and root"""
400 """return the canonical path of myname, given cwd and root"""
401 if root == os.sep:
401 if root == os.sep:
402 rootsep = os.sep
402 rootsep = os.sep
403 elif endswithsep(root):
403 elif endswithsep(root):
404 rootsep = root
404 rootsep = root
405 else:
405 else:
406 rootsep = root + os.sep
406 rootsep = root + os.sep
407 name = myname
407 name = myname
408 if not os.path.isabs(name):
408 if not os.path.isabs(name):
409 name = os.path.join(root, cwd, name)
409 name = os.path.join(root, cwd, name)
410 name = os.path.normpath(name)
410 name = os.path.normpath(name)
411 audit_path = path_auditor(root)
411 audit_path = path_auditor(root)
412 if name != rootsep and name.startswith(rootsep):
412 if name != rootsep and name.startswith(rootsep):
413 name = name[len(rootsep):]
413 name = name[len(rootsep):]
414 audit_path(name)
414 audit_path(name)
415 return pconvert(name)
415 return pconvert(name)
416 elif name == root:
416 elif name == root:
417 return ''
417 return ''
418 else:
418 else:
419 # Determine whether `name' is in the hierarchy at or beneath `root',
419 # Determine whether `name' is in the hierarchy at or beneath `root',
420 # by iterating name=dirname(name) until that causes no change (can't
420 # by iterating name=dirname(name) until that causes no change (can't
421 # check name == '/', because that doesn't work on windows). For each
421 # check name == '/', because that doesn't work on windows). For each
422 # `name', compare dev/inode numbers. If they match, the list `rel'
422 # `name', compare dev/inode numbers. If they match, the list `rel'
423 # holds the reversed list of components making up the relative file
423 # holds the reversed list of components making up the relative file
424 # name we want.
424 # name we want.
425 root_st = os.stat(root)
425 root_st = os.stat(root)
426 rel = []
426 rel = []
427 while True:
427 while True:
428 try:
428 try:
429 name_st = os.stat(name)
429 name_st = os.stat(name)
430 except OSError:
430 except OSError:
431 break
431 break
432 if samestat(name_st, root_st):
432 if samestat(name_st, root_st):
433 if not rel:
433 if not rel:
434 # name was actually the same as root (maybe a symlink)
434 # name was actually the same as root (maybe a symlink)
435 return ''
435 return ''
436 rel.reverse()
436 rel.reverse()
437 name = os.path.join(*rel)
437 name = os.path.join(*rel)
438 audit_path(name)
438 audit_path(name)
439 return pconvert(name)
439 return pconvert(name)
440 dirname, basename = os.path.split(name)
440 dirname, basename = os.path.split(name)
441 rel.append(basename)
441 rel.append(basename)
442 if dirname == name:
442 if dirname == name:
443 break
443 break
444 name = dirname
444 name = dirname
445
445
446 raise Abort('%s not under root' % myname)
446 raise Abort('%s not under root' % myname)
447
447
448 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
448 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
449 """build a function to match a set of file patterns
449 """build a function to match a set of file patterns
450
450
451 arguments:
451 arguments:
452 canonroot - the canonical root of the tree you're matching against
452 canonroot - the canonical root of the tree you're matching against
453 cwd - the current working directory, if relevant
453 cwd - the current working directory, if relevant
454 names - patterns to find
454 names - patterns to find
455 inc - patterns to include
455 inc - patterns to include
456 exc - patterns to exclude
456 exc - patterns to exclude
457 dflt_pat - if a pattern in names has no explicit type, assume this one
457 dflt_pat - if a pattern in names has no explicit type, assume this one
458 src - where these patterns came from (e.g. .hgignore)
458 src - where these patterns came from (e.g. .hgignore)
459
459
460 a pattern is one of:
460 a pattern is one of:
461 'glob:<glob>' - a glob relative to cwd
461 'glob:<glob>' - a glob relative to cwd
462 're:<regexp>' - a regular expression
462 're:<regexp>' - a regular expression
463 'path:<path>' - a path relative to canonroot
463 'path:<path>' - a path relative to canonroot
464 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
464 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
465 'relpath:<path>' - a path relative to cwd
465 'relpath:<path>' - a path relative to cwd
466 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
466 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
467 '<something>' - one of the cases above, selected by the dflt_pat argument
467 '<something>' - one of the cases above, selected by the dflt_pat argument
468
468
469 returns:
469 returns:
470 a 3-tuple containing
470 a 3-tuple containing
471 - list of roots (places where one should start a recursive walk of the fs);
471 - list of roots (places where one should start a recursive walk of the fs);
472 this often matches the explicit non-pattern names passed in, but also
472 this often matches the explicit non-pattern names passed in, but also
473 includes the initial part of glob: patterns that has no glob characters
473 includes the initial part of glob: patterns that has no glob characters
474 - a bool match(filename) function
474 - a bool match(filename) function
475 - a bool indicating if any patterns were passed in
475 - a bool indicating if any patterns were passed in
476 """
476 """
477
477
478 # a common case: no patterns at all
478 # a common case: no patterns at all
479 if not names and not inc and not exc:
479 if not names and not inc and not exc:
480 return [], always, False
480 return [], always, False
481
481
482 def contains_glob(name):
482 def contains_glob(name):
483 for c in name:
483 for c in name:
484 if c in _globchars: return True
484 if c in _globchars: return True
485 return False
485 return False
486
486
487 def regex(kind, name, tail):
487 def regex(kind, name, tail):
488 '''convert a pattern into a regular expression'''
488 '''convert a pattern into a regular expression'''
489 if not name:
489 if not name:
490 return ''
490 return ''
491 if kind == 're':
491 if kind == 're':
492 return name
492 return name
493 elif kind == 'path':
493 elif kind == 'path':
494 return '^' + re.escape(name) + '(?:/|$)'
494 return '^' + re.escape(name) + '(?:/|$)'
495 elif kind == 'relglob':
495 elif kind == 'relglob':
496 return globre(name, '(?:|.*/)', tail)
496 return globre(name, '(?:|.*/)', tail)
497 elif kind == 'relpath':
497 elif kind == 'relpath':
498 return re.escape(name) + '(?:/|$)'
498 return re.escape(name) + '(?:/|$)'
499 elif kind == 'relre':
499 elif kind == 'relre':
500 if name.startswith('^'):
500 if name.startswith('^'):
501 return name
501 return name
502 return '.*' + name
502 return '.*' + name
503 return globre(name, '', tail)
503 return globre(name, '', tail)
504
504
505 def matchfn(pats, tail):
505 def matchfn(pats, tail):
506 """build a matching function from a set of patterns"""
506 """build a matching function from a set of patterns"""
507 if not pats:
507 if not pats:
508 return
508 return
509 try:
509 try:
510 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
510 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
511 if len(pat) > 20000:
511 if len(pat) > 20000:
512 raise OverflowError()
512 raise OverflowError()
513 return re.compile(pat).match
513 return re.compile(pat).match
514 except OverflowError:
514 except OverflowError:
515 # We're using a Python with a tiny regex engine and we
515 # We're using a Python with a tiny regex engine and we
516 # made it explode, so we'll divide the pattern list in two
516 # made it explode, so we'll divide the pattern list in two
517 # until it works
517 # until it works
518 l = len(pats)
518 l = len(pats)
519 if l < 2:
519 if l < 2:
520 raise
520 raise
521 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
521 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
522 return lambda s: a(s) or b(s)
522 return lambda s: a(s) or b(s)
523 except re.error:
523 except re.error:
524 for k, p in pats:
524 for k, p in pats:
525 try:
525 try:
526 re.compile('(?:%s)' % regex(k, p, tail))
526 re.compile('(?:%s)' % regex(k, p, tail))
527 except re.error:
527 except re.error:
528 if src:
528 if src:
529 raise Abort("%s: invalid pattern (%s): %s" %
529 raise Abort("%s: invalid pattern (%s): %s" %
530 (src, k, p))
530 (src, k, p))
531 else:
531 else:
532 raise Abort("invalid pattern (%s): %s" % (k, p))
532 raise Abort("invalid pattern (%s): %s" % (k, p))
533 raise Abort("invalid pattern")
533 raise Abort("invalid pattern")
534
534
535 def globprefix(pat):
535 def globprefix(pat):
536 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
536 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
537 root = []
537 root = []
538 for p in pat.split('/'):
538 for p in pat.split('/'):
539 if contains_glob(p): break
539 if contains_glob(p): break
540 root.append(p)
540 root.append(p)
541 return '/'.join(root) or '.'
541 return '/'.join(root) or '.'
542
542
543 def normalizepats(names, default):
543 def normalizepats(names, default):
544 pats = []
544 pats = []
545 roots = []
545 roots = []
546 anypats = False
546 anypats = False
547 for kind, name in [patkind(p, default) for p in names]:
547 for kind, name in [patkind(p, default) for p in names]:
548 if kind in ('glob', 'relpath'):
548 if kind in ('glob', 'relpath'):
549 name = canonpath(canonroot, cwd, name)
549 name = canonpath(canonroot, cwd, name)
550 elif kind in ('relglob', 'path'):
550 elif kind in ('relglob', 'path'):
551 name = normpath(name)
551 name = normpath(name)
552
552
553 pats.append((kind, name))
553 pats.append((kind, name))
554
554
555 if kind in ('glob', 're', 'relglob', 'relre'):
555 if kind in ('glob', 're', 'relglob', 'relre'):
556 anypats = True
556 anypats = True
557
557
558 if kind == 'glob':
558 if kind == 'glob':
559 root = globprefix(name)
559 root = globprefix(name)
560 roots.append(root)
560 roots.append(root)
561 elif kind in ('relpath', 'path'):
561 elif kind in ('relpath', 'path'):
562 roots.append(name or '.')
562 roots.append(name or '.')
563 elif kind == 'relglob':
563 elif kind == 'relglob':
564 roots.append('.')
564 roots.append('.')
565 return roots, pats, anypats
565 return roots, pats, anypats
566
566
567 roots, pats, anypats = normalizepats(names, dflt_pat)
567 roots, pats, anypats = normalizepats(names, dflt_pat)
568
568
569 patmatch = matchfn(pats, '$') or always
569 patmatch = matchfn(pats, '$') or always
570 incmatch = always
570 incmatch = always
571 if inc:
571 if inc:
572 dummy, inckinds, dummy = normalizepats(inc, 'glob')
572 dummy, inckinds, dummy = normalizepats(inc, 'glob')
573 incmatch = matchfn(inckinds, '(?:/|$)')
573 incmatch = matchfn(inckinds, '(?:/|$)')
574 excmatch = lambda fn: False
574 excmatch = lambda fn: False
575 if exc:
575 if exc:
576 dummy, exckinds, dummy = normalizepats(exc, 'glob')
576 dummy, exckinds, dummy = normalizepats(exc, 'glob')
577 excmatch = matchfn(exckinds, '(?:/|$)')
577 excmatch = matchfn(exckinds, '(?:/|$)')
578
578
579 if not names and inc and not exc:
579 if not names and inc and not exc:
580 # common case: hgignore patterns
580 # common case: hgignore patterns
581 match = incmatch
581 match = incmatch
582 else:
582 else:
583 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
583 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
584
584
585 return (roots, match, (inc or exc or anypats) and True)
585 return (roots, match, (inc or exc or anypats) and True)
586
586
587 _hgexecutable = None
587 _hgexecutable = None
588
588
589 def main_is_frozen():
589 def main_is_frozen():
590 """return True if we are a frozen executable.
590 """return True if we are a frozen executable.
591
591
592 The code supports py2exe (most common, Windows only) and tools/freeze
592 The code supports py2exe (most common, Windows only) and tools/freeze
593 (portable, not much used).
593 (portable, not much used).
594 """
594 """
595 return (hasattr(sys, "frozen") or # new py2exe
595 return (hasattr(sys, "frozen") or # new py2exe
596 hasattr(sys, "importers") or # old py2exe
596 hasattr(sys, "importers") or # old py2exe
597 imp.is_frozen("__main__")) # tools/freeze
597 imp.is_frozen("__main__")) # tools/freeze
598
598
599 def hgexecutable():
599 def hgexecutable():
600 """return location of the 'hg' executable.
600 """return location of the 'hg' executable.
601
601
602 Defaults to $HG or 'hg' in the search path.
602 Defaults to $HG or 'hg' in the search path.
603 """
603 """
604 if _hgexecutable is None:
604 if _hgexecutable is None:
605 hg = os.environ.get('HG')
605 hg = os.environ.get('HG')
606 if hg:
606 if hg:
607 set_hgexecutable(hg)
607 set_hgexecutable(hg)
608 elif main_is_frozen():
608 elif main_is_frozen():
609 set_hgexecutable(sys.executable)
609 set_hgexecutable(sys.executable)
610 else:
610 else:
611 set_hgexecutable(find_exe('hg', 'hg'))
611 set_hgexecutable(find_exe('hg', 'hg'))
612 return _hgexecutable
612 return _hgexecutable
613
613
614 def set_hgexecutable(path):
614 def set_hgexecutable(path):
615 """set location of the 'hg' executable"""
615 """set location of the 'hg' executable"""
616 global _hgexecutable
616 global _hgexecutable
617 _hgexecutable = path
617 _hgexecutable = path
618
618
619 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
619 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
620 '''enhanced shell command execution.
620 '''enhanced shell command execution.
621 run with environment maybe modified, maybe in different dir.
621 run with environment maybe modified, maybe in different dir.
622
622
623 if command fails and onerr is None, return status. if ui object,
623 if command fails and onerr is None, return status. if ui object,
624 print error message and return status, else raise onerr object as
624 print error message and return status, else raise onerr object as
625 exception.'''
625 exception.'''
626 def py2shell(val):
626 def py2shell(val):
627 'convert python object into string that is useful to shell'
627 'convert python object into string that is useful to shell'
628 if val in (None, False):
628 if val in (None, False):
629 return '0'
629 return '0'
630 if val == True:
630 if val == True:
631 return '1'
631 return '1'
632 return str(val)
632 return str(val)
633 oldenv = {}
633 oldenv = {}
634 for k in environ:
634 for k in environ:
635 oldenv[k] = os.environ.get(k)
635 oldenv[k] = os.environ.get(k)
636 if cwd is not None:
636 if cwd is not None:
637 oldcwd = os.getcwd()
637 oldcwd = os.getcwd()
638 origcmd = cmd
638 origcmd = cmd
639 if os.name == 'nt':
639 if os.name == 'nt':
640 cmd = '"%s"' % cmd
640 cmd = '"%s"' % cmd
641 try:
641 try:
642 for k, v in environ.iteritems():
642 for k, v in environ.iteritems():
643 os.environ[k] = py2shell(v)
643 os.environ[k] = py2shell(v)
644 os.environ['HG'] = hgexecutable()
644 os.environ['HG'] = hgexecutable()
645 if cwd is not None and oldcwd != cwd:
645 if cwd is not None and oldcwd != cwd:
646 os.chdir(cwd)
646 os.chdir(cwd)
647 rc = os.system(cmd)
647 rc = os.system(cmd)
648 if sys.platform == 'OpenVMS' and rc & 1:
648 if sys.platform == 'OpenVMS' and rc & 1:
649 rc = 0
649 rc = 0
650 if rc and onerr:
650 if rc and onerr:
651 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
651 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
652 explain_exit(rc)[0])
652 explain_exit(rc)[0])
653 if errprefix:
653 if errprefix:
654 errmsg = '%s: %s' % (errprefix, errmsg)
654 errmsg = '%s: %s' % (errprefix, errmsg)
655 try:
655 try:
656 onerr.warn(errmsg + '\n')
656 onerr.warn(errmsg + '\n')
657 except AttributeError:
657 except AttributeError:
658 raise onerr(errmsg)
658 raise onerr(errmsg)
659 return rc
659 return rc
660 finally:
660 finally:
661 for k, v in oldenv.iteritems():
661 for k, v in oldenv.iteritems():
662 if v is None:
662 if v is None:
663 del os.environ[k]
663 del os.environ[k]
664 else:
664 else:
665 os.environ[k] = v
665 os.environ[k] = v
666 if cwd is not None and oldcwd != cwd:
666 if cwd is not None and oldcwd != cwd:
667 os.chdir(oldcwd)
667 os.chdir(oldcwd)
668
668
669 # os.path.lexists is not available on python2.3
669 # os.path.lexists is not available on python2.3
670 def lexists(filename):
670 def lexists(filename):
671 "test whether a file with this name exists. does not follow symlinks"
671 "test whether a file with this name exists. does not follow symlinks"
672 try:
672 try:
673 os.lstat(filename)
673 os.lstat(filename)
674 except:
674 except:
675 return False
675 return False
676 return True
676 return True
677
677
678 def rename(src, dst):
678 def rename(src, dst):
679 """forcibly rename a file"""
679 """forcibly rename a file"""
680 try:
680 try:
681 os.rename(src, dst)
681 os.rename(src, dst)
682 except OSError, err: # FIXME: check err (EEXIST ?)
682 except OSError, err: # FIXME: check err (EEXIST ?)
683 # on windows, rename to existing file is not allowed, so we
683 # on windows, rename to existing file is not allowed, so we
684 # must delete destination first. but if file is open, unlink
684 # must delete destination first. but if file is open, unlink
685 # schedules it for delete but does not delete it. rename
685 # schedules it for delete but does not delete it. rename
686 # happens immediately even for open files, so we create
686 # happens immediately even for open files, so we create
687 # temporary file, delete it, rename destination to that name,
687 # temporary file, delete it, rename destination to that name,
688 # then delete that. then rename is safe to do.
688 # then delete that. then rename is safe to do.
689 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
689 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
690 os.close(fd)
690 os.close(fd)
691 os.unlink(temp)
691 os.unlink(temp)
692 os.rename(dst, temp)
692 os.rename(dst, temp)
693 os.unlink(temp)
693 os.unlink(temp)
694 os.rename(src, dst)
694 os.rename(src, dst)
695
695
696 def unlink(f):
696 def unlink(f):
697 """unlink and remove the directory if it is empty"""
697 """unlink and remove the directory if it is empty"""
698 os.unlink(f)
698 os.unlink(f)
699 # try removing directories that might now be empty
699 # try removing directories that might now be empty
700 try:
700 try:
701 os.removedirs(os.path.dirname(f))
701 os.removedirs(os.path.dirname(f))
702 except OSError:
702 except OSError:
703 pass
703 pass
704
704
705 def copyfile(src, dest):
705 def copyfile(src, dest):
706 "copy a file, preserving mode"
706 "copy a file, preserving mode"
707 if os.path.islink(src):
707 if os.path.islink(src):
708 try:
708 try:
709 os.unlink(dest)
709 os.unlink(dest)
710 except:
710 except:
711 pass
711 pass
712 os.symlink(os.readlink(src), dest)
712 os.symlink(os.readlink(src), dest)
713 else:
713 else:
714 try:
714 try:
715 shutil.copyfile(src, dest)
715 shutil.copyfile(src, dest)
716 shutil.copymode(src, dest)
716 shutil.copymode(src, dest)
717 except shutil.Error, inst:
717 except shutil.Error, inst:
718 raise Abort(str(inst))
718 raise Abort(str(inst))
719
719
720 def copyfiles(src, dst, hardlink=None):
720 def copyfiles(src, dst, hardlink=None):
721 """Copy a directory tree using hardlinks if possible"""
721 """Copy a directory tree using hardlinks if possible"""
722
722
723 if hardlink is None:
723 if hardlink is None:
724 hardlink = (os.stat(src).st_dev ==
724 hardlink = (os.stat(src).st_dev ==
725 os.stat(os.path.dirname(dst)).st_dev)
725 os.stat(os.path.dirname(dst)).st_dev)
726
726
727 if os.path.isdir(src):
727 if os.path.isdir(src):
728 os.mkdir(dst)
728 os.mkdir(dst)
729 for name, kind in osutil.listdir(src):
729 for name, kind in osutil.listdir(src):
730 srcname = os.path.join(src, name)
730 srcname = os.path.join(src, name)
731 dstname = os.path.join(dst, name)
731 dstname = os.path.join(dst, name)
732 copyfiles(srcname, dstname, hardlink)
732 copyfiles(srcname, dstname, hardlink)
733 else:
733 else:
734 if hardlink:
734 if hardlink:
735 try:
735 try:
736 os_link(src, dst)
736 os_link(src, dst)
737 except (IOError, OSError):
737 except (IOError, OSError):
738 hardlink = False
738 hardlink = False
739 shutil.copy(src, dst)
739 shutil.copy(src, dst)
740 else:
740 else:
741 shutil.copy(src, dst)
741 shutil.copy(src, dst)
742
742
743 class path_auditor(object):
743 class path_auditor(object):
744 '''ensure that a filesystem path contains no banned components.
744 '''ensure that a filesystem path contains no banned components.
745 the following properties of a path are checked:
745 the following properties of a path are checked:
746
746
747 - under top-level .hg
747 - under top-level .hg
748 - starts at the root of a windows drive
748 - starts at the root of a windows drive
749 - contains ".."
749 - contains ".."
750 - traverses a symlink (e.g. a/symlink_here/b)
750 - traverses a symlink (e.g. a/symlink_here/b)
751 - inside a nested repository'''
751 - inside a nested repository'''
752
752
753 def __init__(self, root):
753 def __init__(self, root):
754 self.audited = set()
754 self.audited = set()
755 self.auditeddir = set()
755 self.auditeddir = set()
756 self.root = root
756 self.root = root
757
757
758 def __call__(self, path):
758 def __call__(self, path):
759 if path in self.audited:
759 if path in self.audited:
760 return
760 return
761 normpath = os.path.normcase(path)
761 normpath = os.path.normcase(path)
762 parts = splitpath(normpath)
762 parts = splitpath(normpath)
763 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
763 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
764 or os.pardir in parts):
764 or os.pardir in parts):
765 raise Abort(_("path contains illegal component: %s") % path)
765 raise Abort(_("path contains illegal component: %s") % path)
766 def check(prefix):
766 def check(prefix):
767 curpath = os.path.join(self.root, prefix)
767 curpath = os.path.join(self.root, prefix)
768 try:
768 try:
769 st = os.lstat(curpath)
769 st = os.lstat(curpath)
770 except OSError, err:
770 except OSError, err:
771 # EINVAL can be raised as invalid path syntax under win32.
771 # EINVAL can be raised as invalid path syntax under win32.
772 # They must be ignored for patterns can be checked too.
772 # They must be ignored for patterns can be checked too.
773 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
773 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
774 raise
774 raise
775 else:
775 else:
776 if stat.S_ISLNK(st.st_mode):
776 if stat.S_ISLNK(st.st_mode):
777 raise Abort(_('path %r traverses symbolic link %r') %
777 raise Abort(_('path %r traverses symbolic link %r') %
778 (path, prefix))
778 (path, prefix))
779 elif (stat.S_ISDIR(st.st_mode) and
779 elif (stat.S_ISDIR(st.st_mode) and
780 os.path.isdir(os.path.join(curpath, '.hg'))):
780 os.path.isdir(os.path.join(curpath, '.hg'))):
781 raise Abort(_('path %r is inside repo %r') %
781 raise Abort(_('path %r is inside repo %r') %
782 (path, prefix))
782 (path, prefix))
783 parts.pop()
783 parts.pop()
784 prefixes = []
784 prefixes = []
785 for n in range(len(parts)):
785 for n in range(len(parts)):
786 prefix = os.sep.join(parts)
786 prefix = os.sep.join(parts)
787 if prefix in self.auditeddir:
787 if prefix in self.auditeddir:
788 break
788 break
789 check(prefix)
789 check(prefix)
790 prefixes.append(prefix)
790 prefixes.append(prefix)
791 parts.pop()
791 parts.pop()
792
792
793 self.audited.add(path)
793 self.audited.add(path)
794 # only add prefixes to the cache after checking everything: we don't
794 # only add prefixes to the cache after checking everything: we don't
795 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
795 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
796 self.auditeddir.update(prefixes)
796 self.auditeddir.update(prefixes)
797
797
798 def _makelock_file(info, pathname):
798 def _makelock_file(info, pathname):
799 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
799 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
800 os.write(ld, info)
800 os.write(ld, info)
801 os.close(ld)
801 os.close(ld)
802
802
803 def _readlock_file(pathname):
803 def _readlock_file(pathname):
804 return posixfile(pathname).read()
804 return posixfile(pathname).read()
805
805
806 def nlinks(pathname):
806 def nlinks(pathname):
807 """Return number of hardlinks for the given file."""
807 """Return number of hardlinks for the given file."""
808 return os.lstat(pathname).st_nlink
808 return os.lstat(pathname).st_nlink
809
809
810 if hasattr(os, 'link'):
810 if hasattr(os, 'link'):
811 os_link = os.link
811 os_link = os.link
812 else:
812 else:
813 def os_link(src, dst):
813 def os_link(src, dst):
814 raise OSError(0, _("Hardlinks not supported"))
814 raise OSError(0, _("Hardlinks not supported"))
815
815
816 def fstat(fp):
816 def fstat(fp):
817 '''stat file object that may not have fileno method.'''
817 '''stat file object that may not have fileno method.'''
818 try:
818 try:
819 return os.fstat(fp.fileno())
819 return os.fstat(fp.fileno())
820 except AttributeError:
820 except AttributeError:
821 return os.stat(fp.name)
821 return os.stat(fp.name)
822
822
823 posixfile = file
823 posixfile = file
824
824
825 def openhardlinks():
825 def openhardlinks():
826 '''return true if it is safe to hold open file handles to hardlinks'''
826 '''return true if it is safe to hold open file handles to hardlinks'''
827 return True
827 return True
828
828
829 def _statfiles(files):
830 'Stat each file in files and yield stat or None if file does not exist.'
831 lstat = os.lstat
832 for nf in files:
833 try:
834 st = lstat(nf)
835 except OSError, err:
836 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
837 raise
838 st = None
839 yield st
840
841 def _statfiles_clustered(files):
842 '''Stat each file in files and yield stat or None if file does not exist.
843 Cluster and cache stat per directory to minimize number of OS stat calls.'''
844 lstat = os.lstat
845 ncase = os.path.normcase
846 sep = os.sep
847 dircache = {} # dirname -> filename -> status | None if file does not exist
848 for nf in files:
849 nf = ncase(nf)
850 pos = nf.rfind(sep)
851 if pos == -1:
852 dir, base = '.', nf
853 else:
854 dir, base = nf[:pos], nf[pos+1:]
855 cache = dircache.get(dir, None)
856 if cache is None:
857 try:
858 dmap = dict([(ncase(n), s)
859 for n, k, s in osutil.listdir(dir, True)])
860 except OSError, err:
861 # handle directory not found in Python version prior to 2.5
862 # Python <= 2.4 returns native Windows code 3 in errno
863 # Python >= 2.5 returns ENOENT and adds winerror field
864 if err.errno not in (3, errno.ENOENT, errno.ENOTDIR):
865 raise
866 dmap = {}
867 cache = dircache.setdefault(dir, dmap)
868 yield cache.get(base, None)
869
870 if sys.platform == 'win32':
871 statfiles = _statfiles_clustered
872 else:
873 statfiles = _statfiles
874
829 getuser_fallback = None
875 getuser_fallback = None
830
876
831 def getuser():
877 def getuser():
832 '''return name of current user'''
878 '''return name of current user'''
833 try:
879 try:
834 return getpass.getuser()
880 return getpass.getuser()
835 except ImportError:
881 except ImportError:
836 # import of pwd will fail on windows - try fallback
882 # import of pwd will fail on windows - try fallback
837 if getuser_fallback:
883 if getuser_fallback:
838 return getuser_fallback()
884 return getuser_fallback()
839 # raised if win32api not available
885 # raised if win32api not available
840 raise Abort(_('user name not available - set USERNAME '
886 raise Abort(_('user name not available - set USERNAME '
841 'environment variable'))
887 'environment variable'))
842
888
843 def username(uid=None):
889 def username(uid=None):
844 """Return the name of the user with the given uid.
890 """Return the name of the user with the given uid.
845
891
846 If uid is None, return the name of the current user."""
892 If uid is None, return the name of the current user."""
847 try:
893 try:
848 import pwd
894 import pwd
849 if uid is None:
895 if uid is None:
850 uid = os.getuid()
896 uid = os.getuid()
851 try:
897 try:
852 return pwd.getpwuid(uid)[0]
898 return pwd.getpwuid(uid)[0]
853 except KeyError:
899 except KeyError:
854 return str(uid)
900 return str(uid)
855 except ImportError:
901 except ImportError:
856 return None
902 return None
857
903
858 def groupname(gid=None):
904 def groupname(gid=None):
859 """Return the name of the group with the given gid.
905 """Return the name of the group with the given gid.
860
906
861 If gid is None, return the name of the current group."""
907 If gid is None, return the name of the current group."""
862 try:
908 try:
863 import grp
909 import grp
864 if gid is None:
910 if gid is None:
865 gid = os.getgid()
911 gid = os.getgid()
866 try:
912 try:
867 return grp.getgrgid(gid)[0]
913 return grp.getgrgid(gid)[0]
868 except KeyError:
914 except KeyError:
869 return str(gid)
915 return str(gid)
870 except ImportError:
916 except ImportError:
871 return None
917 return None
872
918
873 # File system features
919 # File system features
874
920
875 def checkcase(path):
921 def checkcase(path):
876 """
922 """
877 Check whether the given path is on a case-sensitive filesystem
923 Check whether the given path is on a case-sensitive filesystem
878
924
879 Requires a path (like /foo/.hg) ending with a foldable final
925 Requires a path (like /foo/.hg) ending with a foldable final
880 directory component.
926 directory component.
881 """
927 """
882 s1 = os.stat(path)
928 s1 = os.stat(path)
883 d, b = os.path.split(path)
929 d, b = os.path.split(path)
884 p2 = os.path.join(d, b.upper())
930 p2 = os.path.join(d, b.upper())
885 if path == p2:
931 if path == p2:
886 p2 = os.path.join(d, b.lower())
932 p2 = os.path.join(d, b.lower())
887 try:
933 try:
888 s2 = os.stat(p2)
934 s2 = os.stat(p2)
889 if s2 == s1:
935 if s2 == s1:
890 return False
936 return False
891 return True
937 return True
892 except:
938 except:
893 return True
939 return True
894
940
895 _fspathcache = {}
941 _fspathcache = {}
896 def fspath(name, root):
942 def fspath(name, root):
897 '''Get name in the case stored in the filesystem
943 '''Get name in the case stored in the filesystem
898
944
899 The name is either relative to root, or it is an absolute path starting
945 The name is either relative to root, or it is an absolute path starting
900 with root. Note that this function is unnecessary, and should not be
946 with root. Note that this function is unnecessary, and should not be
901 called, for case-sensitive filesystems (simply because it's expensive).
947 called, for case-sensitive filesystems (simply because it's expensive).
902 '''
948 '''
903 # If name is absolute, make it relative
949 # If name is absolute, make it relative
904 if name.lower().startswith(root.lower()):
950 if name.lower().startswith(root.lower()):
905 l = len(root)
951 l = len(root)
906 if name[l] == os.sep or name[l] == os.altsep:
952 if name[l] == os.sep or name[l] == os.altsep:
907 l = l + 1
953 l = l + 1
908 name = name[l:]
954 name = name[l:]
909
955
910 if not os.path.exists(os.path.join(root, name)):
956 if not os.path.exists(os.path.join(root, name)):
911 return None
957 return None
912
958
913 seps = os.sep
959 seps = os.sep
914 if os.altsep:
960 if os.altsep:
915 seps = seps + os.altsep
961 seps = seps + os.altsep
916 # Protect backslashes. This gets silly very quickly.
962 # Protect backslashes. This gets silly very quickly.
917 seps.replace('\\','\\\\')
963 seps.replace('\\','\\\\')
918 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
964 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
919 dir = os.path.normcase(os.path.normpath(root))
965 dir = os.path.normcase(os.path.normpath(root))
920 result = []
966 result = []
921 for part, sep in pattern.findall(name):
967 for part, sep in pattern.findall(name):
922 if sep:
968 if sep:
923 result.append(sep)
969 result.append(sep)
924 continue
970 continue
925
971
926 if dir not in _fspathcache:
972 if dir not in _fspathcache:
927 _fspathcache[dir] = os.listdir(dir)
973 _fspathcache[dir] = os.listdir(dir)
928 contents = _fspathcache[dir]
974 contents = _fspathcache[dir]
929
975
930 lpart = part.lower()
976 lpart = part.lower()
931 for n in contents:
977 for n in contents:
932 if n.lower() == lpart:
978 if n.lower() == lpart:
933 result.append(n)
979 result.append(n)
934 break
980 break
935 else:
981 else:
936 # Cannot happen, as the file exists!
982 # Cannot happen, as the file exists!
937 result.append(part)
983 result.append(part)
938 dir = os.path.join(dir, lpart)
984 dir = os.path.join(dir, lpart)
939
985
940 return ''.join(result)
986 return ''.join(result)
941
987
942 def checkexec(path):
988 def checkexec(path):
943 """
989 """
944 Check whether the given path is on a filesystem with UNIX-like exec flags
990 Check whether the given path is on a filesystem with UNIX-like exec flags
945
991
946 Requires a directory (like /foo/.hg)
992 Requires a directory (like /foo/.hg)
947 """
993 """
948
994
949 # VFAT on some Linux versions can flip mode but it doesn't persist
995 # VFAT on some Linux versions can flip mode but it doesn't persist
950 # a FS remount. Frequently we can detect it if files are created
996 # a FS remount. Frequently we can detect it if files are created
951 # with exec bit on.
997 # with exec bit on.
952
998
953 try:
999 try:
954 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1000 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
955 fh, fn = tempfile.mkstemp("", "", path)
1001 fh, fn = tempfile.mkstemp("", "", path)
956 try:
1002 try:
957 os.close(fh)
1003 os.close(fh)
958 m = os.stat(fn).st_mode & 0777
1004 m = os.stat(fn).st_mode & 0777
959 new_file_has_exec = m & EXECFLAGS
1005 new_file_has_exec = m & EXECFLAGS
960 os.chmod(fn, m ^ EXECFLAGS)
1006 os.chmod(fn, m ^ EXECFLAGS)
961 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1007 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
962 finally:
1008 finally:
963 os.unlink(fn)
1009 os.unlink(fn)
964 except (IOError, OSError):
1010 except (IOError, OSError):
965 # we don't care, the user probably won't be able to commit anyway
1011 # we don't care, the user probably won't be able to commit anyway
966 return False
1012 return False
967 return not (new_file_has_exec or exec_flags_cannot_flip)
1013 return not (new_file_has_exec or exec_flags_cannot_flip)
968
1014
969 def checklink(path):
1015 def checklink(path):
970 """check whether the given path is on a symlink-capable filesystem"""
1016 """check whether the given path is on a symlink-capable filesystem"""
971 # mktemp is not racy because symlink creation will fail if the
1017 # mktemp is not racy because symlink creation will fail if the
972 # file already exists
1018 # file already exists
973 name = tempfile.mktemp(dir=path)
1019 name = tempfile.mktemp(dir=path)
974 try:
1020 try:
975 os.symlink(".", name)
1021 os.symlink(".", name)
976 os.unlink(name)
1022 os.unlink(name)
977 return True
1023 return True
978 except (OSError, AttributeError):
1024 except (OSError, AttributeError):
979 return False
1025 return False
980
1026
981 _umask = os.umask(0)
1027 _umask = os.umask(0)
982 os.umask(_umask)
1028 os.umask(_umask)
983
1029
984 def needbinarypatch():
1030 def needbinarypatch():
985 """return True if patches should be applied in binary mode by default."""
1031 """return True if patches should be applied in binary mode by default."""
986 return os.name == 'nt'
1032 return os.name == 'nt'
987
1033
988 def endswithsep(path):
1034 def endswithsep(path):
989 '''Check path ends with os.sep or os.altsep.'''
1035 '''Check path ends with os.sep or os.altsep.'''
990 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1036 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
991
1037
992 def splitpath(path):
1038 def splitpath(path):
993 '''Split path by os.sep.
1039 '''Split path by os.sep.
994 Note that this function does not use os.altsep because this is
1040 Note that this function does not use os.altsep because this is
995 an alternative of simple "xxx.split(os.sep)".
1041 an alternative of simple "xxx.split(os.sep)".
996 It is recommended to use os.path.normpath() before using this
1042 It is recommended to use os.path.normpath() before using this
997 function if need.'''
1043 function if need.'''
998 return path.split(os.sep)
1044 return path.split(os.sep)
999
1045
1000 def gui():
1046 def gui():
1001 '''Are we running in a GUI?'''
1047 '''Are we running in a GUI?'''
1002 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1048 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1003
1049
1004 def lookup_reg(key, name=None, scope=None):
1050 def lookup_reg(key, name=None, scope=None):
1005 return None
1051 return None
1006
1052
1007 # Platform specific variants
1053 # Platform specific variants
1008 if os.name == 'nt':
1054 if os.name == 'nt':
1009 import msvcrt
1055 import msvcrt
1010 nulldev = 'NUL:'
1056 nulldev = 'NUL:'
1011
1057
1012 class winstdout:
1058 class winstdout:
1013 '''stdout on windows misbehaves if sent through a pipe'''
1059 '''stdout on windows misbehaves if sent through a pipe'''
1014
1060
1015 def __init__(self, fp):
1061 def __init__(self, fp):
1016 self.fp = fp
1062 self.fp = fp
1017
1063
1018 def __getattr__(self, key):
1064 def __getattr__(self, key):
1019 return getattr(self.fp, key)
1065 return getattr(self.fp, key)
1020
1066
1021 def close(self):
1067 def close(self):
1022 try:
1068 try:
1023 self.fp.close()
1069 self.fp.close()
1024 except: pass
1070 except: pass
1025
1071
1026 def write(self, s):
1072 def write(self, s):
1027 try:
1073 try:
1028 # This is workaround for "Not enough space" error on
1074 # This is workaround for "Not enough space" error on
1029 # writing large size of data to console.
1075 # writing large size of data to console.
1030 limit = 16000
1076 limit = 16000
1031 l = len(s)
1077 l = len(s)
1032 start = 0
1078 start = 0
1033 while start < l:
1079 while start < l:
1034 end = start + limit
1080 end = start + limit
1035 self.fp.write(s[start:end])
1081 self.fp.write(s[start:end])
1036 start = end
1082 start = end
1037 except IOError, inst:
1083 except IOError, inst:
1038 if inst.errno != 0: raise
1084 if inst.errno != 0: raise
1039 self.close()
1085 self.close()
1040 raise IOError(errno.EPIPE, 'Broken pipe')
1086 raise IOError(errno.EPIPE, 'Broken pipe')
1041
1087
1042 def flush(self):
1088 def flush(self):
1043 try:
1089 try:
1044 return self.fp.flush()
1090 return self.fp.flush()
1045 except IOError, inst:
1091 except IOError, inst:
1046 if inst.errno != errno.EINVAL: raise
1092 if inst.errno != errno.EINVAL: raise
1047 self.close()
1093 self.close()
1048 raise IOError(errno.EPIPE, 'Broken pipe')
1094 raise IOError(errno.EPIPE, 'Broken pipe')
1049
1095
1050 sys.stdout = winstdout(sys.stdout)
1096 sys.stdout = winstdout(sys.stdout)
1051
1097
1052 def _is_win_9x():
1098 def _is_win_9x():
1053 '''return true if run on windows 95, 98 or me.'''
1099 '''return true if run on windows 95, 98 or me.'''
1054 try:
1100 try:
1055 return sys.getwindowsversion()[3] == 1
1101 return sys.getwindowsversion()[3] == 1
1056 except AttributeError:
1102 except AttributeError:
1057 return 'command' in os.environ.get('comspec', '')
1103 return 'command' in os.environ.get('comspec', '')
1058
1104
1059 def openhardlinks():
1105 def openhardlinks():
1060 return not _is_win_9x and "win32api" in locals()
1106 return not _is_win_9x and "win32api" in locals()
1061
1107
1062 def system_rcpath():
1108 def system_rcpath():
1063 try:
1109 try:
1064 return system_rcpath_win32()
1110 return system_rcpath_win32()
1065 except:
1111 except:
1066 return [r'c:\mercurial\mercurial.ini']
1112 return [r'c:\mercurial\mercurial.ini']
1067
1113
1068 def user_rcpath():
1114 def user_rcpath():
1069 '''return os-specific hgrc search path to the user dir'''
1115 '''return os-specific hgrc search path to the user dir'''
1070 try:
1116 try:
1071 path = user_rcpath_win32()
1117 path = user_rcpath_win32()
1072 except:
1118 except:
1073 home = os.path.expanduser('~')
1119 home = os.path.expanduser('~')
1074 path = [os.path.join(home, 'mercurial.ini'),
1120 path = [os.path.join(home, 'mercurial.ini'),
1075 os.path.join(home, '.hgrc')]
1121 os.path.join(home, '.hgrc')]
1076 userprofile = os.environ.get('USERPROFILE')
1122 userprofile = os.environ.get('USERPROFILE')
1077 if userprofile:
1123 if userprofile:
1078 path.append(os.path.join(userprofile, 'mercurial.ini'))
1124 path.append(os.path.join(userprofile, 'mercurial.ini'))
1079 path.append(os.path.join(userprofile, '.hgrc'))
1125 path.append(os.path.join(userprofile, '.hgrc'))
1080 return path
1126 return path
1081
1127
1082 def parse_patch_output(output_line):
1128 def parse_patch_output(output_line):
1083 """parses the output produced by patch and returns the file name"""
1129 """parses the output produced by patch and returns the file name"""
1084 pf = output_line[14:]
1130 pf = output_line[14:]
1085 if pf[0] == '`':
1131 if pf[0] == '`':
1086 pf = pf[1:-1] # Remove the quotes
1132 pf = pf[1:-1] # Remove the quotes
1087 return pf
1133 return pf
1088
1134
1089 def sshargs(sshcmd, host, user, port):
1135 def sshargs(sshcmd, host, user, port):
1090 '''Build argument list for ssh or Plink'''
1136 '''Build argument list for ssh or Plink'''
1091 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1137 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1092 args = user and ("%s@%s" % (user, host)) or host
1138 args = user and ("%s@%s" % (user, host)) or host
1093 return port and ("%s %s %s" % (args, pflag, port)) or args
1139 return port and ("%s %s %s" % (args, pflag, port)) or args
1094
1140
1095 def testpid(pid):
1141 def testpid(pid):
1096 '''return False if pid dead, True if running or not known'''
1142 '''return False if pid dead, True if running or not known'''
1097 return True
1143 return True
1098
1144
1099 def set_flags(f, l, x):
1145 def set_flags(f, l, x):
1100 pass
1146 pass
1101
1147
1102 def set_binary(fd):
1148 def set_binary(fd):
1103 # When run without console, pipes may expose invalid
1149 # When run without console, pipes may expose invalid
1104 # fileno(), usually set to -1.
1150 # fileno(), usually set to -1.
1105 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1151 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1106 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1152 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1107
1153
1108 def pconvert(path):
1154 def pconvert(path):
1109 return '/'.join(splitpath(path))
1155 return '/'.join(splitpath(path))
1110
1156
1111 def localpath(path):
1157 def localpath(path):
1112 return path.replace('/', '\\')
1158 return path.replace('/', '\\')
1113
1159
1114 def normpath(path):
1160 def normpath(path):
1115 return pconvert(os.path.normpath(path))
1161 return pconvert(os.path.normpath(path))
1116
1162
1117 makelock = _makelock_file
1163 makelock = _makelock_file
1118 readlock = _readlock_file
1164 readlock = _readlock_file
1119
1165
1120 def samestat(s1, s2):
1166 def samestat(s1, s2):
1121 return False
1167 return False
1122
1168
1123 # A sequence of backslashes is special iff it precedes a double quote:
1169 # A sequence of backslashes is special iff it precedes a double quote:
1124 # - if there's an even number of backslashes, the double quote is not
1170 # - if there's an even number of backslashes, the double quote is not
1125 # quoted (i.e. it ends the quoted region)
1171 # quoted (i.e. it ends the quoted region)
1126 # - if there's an odd number of backslashes, the double quote is quoted
1172 # - if there's an odd number of backslashes, the double quote is quoted
1127 # - in both cases, every pair of backslashes is unquoted into a single
1173 # - in both cases, every pair of backslashes is unquoted into a single
1128 # backslash
1174 # backslash
1129 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1175 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1130 # So, to quote a string, we must surround it in double quotes, double
1176 # So, to quote a string, we must surround it in double quotes, double
1131 # the number of backslashes that preceed double quotes and add another
1177 # the number of backslashes that preceed double quotes and add another
1132 # backslash before every double quote (being careful with the double
1178 # backslash before every double quote (being careful with the double
1133 # quote we've appended to the end)
1179 # quote we've appended to the end)
1134 _quotere = None
1180 _quotere = None
1135 def shellquote(s):
1181 def shellquote(s):
1136 global _quotere
1182 global _quotere
1137 if _quotere is None:
1183 if _quotere is None:
1138 _quotere = re.compile(r'(\\*)("|\\$)')
1184 _quotere = re.compile(r'(\\*)("|\\$)')
1139 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1185 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1140
1186
1141 def quotecommand(cmd):
1187 def quotecommand(cmd):
1142 """Build a command string suitable for os.popen* calls."""
1188 """Build a command string suitable for os.popen* calls."""
1143 # The extra quotes are needed because popen* runs the command
1189 # The extra quotes are needed because popen* runs the command
1144 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1190 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1145 return '"' + cmd + '"'
1191 return '"' + cmd + '"'
1146
1192
1147 def popen(command, mode='r'):
1193 def popen(command, mode='r'):
1148 # Work around "popen spawned process may not write to stdout
1194 # Work around "popen spawned process may not write to stdout
1149 # under windows"
1195 # under windows"
1150 # http://bugs.python.org/issue1366
1196 # http://bugs.python.org/issue1366
1151 command += " 2> %s" % nulldev
1197 command += " 2> %s" % nulldev
1152 return os.popen(quotecommand(command), mode)
1198 return os.popen(quotecommand(command), mode)
1153
1199
1154 def explain_exit(code):
1200 def explain_exit(code):
1155 return _("exited with status %d") % code, code
1201 return _("exited with status %d") % code, code
1156
1202
1157 # if you change this stub into a real check, please try to implement the
1203 # if you change this stub into a real check, please try to implement the
1158 # username and groupname functions above, too.
1204 # username and groupname functions above, too.
1159 def isowner(fp, st=None):
1205 def isowner(fp, st=None):
1160 return True
1206 return True
1161
1207
1162 def find_in_path(name, path, default=None):
1208 def find_in_path(name, path, default=None):
1163 '''find name in search path. path can be string (will be split
1209 '''find name in search path. path can be string (will be split
1164 with os.pathsep), or iterable thing that returns strings. if name
1210 with os.pathsep), or iterable thing that returns strings. if name
1165 found, return path to name. else return default. name is looked up
1211 found, return path to name. else return default. name is looked up
1166 using cmd.exe rules, using PATHEXT.'''
1212 using cmd.exe rules, using PATHEXT.'''
1167 if isinstance(path, str):
1213 if isinstance(path, str):
1168 path = path.split(os.pathsep)
1214 path = path.split(os.pathsep)
1169
1215
1170 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1216 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1171 pathext = pathext.lower().split(os.pathsep)
1217 pathext = pathext.lower().split(os.pathsep)
1172 isexec = os.path.splitext(name)[1].lower() in pathext
1218 isexec = os.path.splitext(name)[1].lower() in pathext
1173
1219
1174 for p in path:
1220 for p in path:
1175 p_name = os.path.join(p, name)
1221 p_name = os.path.join(p, name)
1176
1222
1177 if isexec and os.path.exists(p_name):
1223 if isexec and os.path.exists(p_name):
1178 return p_name
1224 return p_name
1179
1225
1180 for ext in pathext:
1226 for ext in pathext:
1181 p_name_ext = p_name + ext
1227 p_name_ext = p_name + ext
1182 if os.path.exists(p_name_ext):
1228 if os.path.exists(p_name_ext):
1183 return p_name_ext
1229 return p_name_ext
1184 return default
1230 return default
1185
1231
1186 def set_signal_handler():
1232 def set_signal_handler():
1187 try:
1233 try:
1188 set_signal_handler_win32()
1234 set_signal_handler_win32()
1189 except NameError:
1235 except NameError:
1190 pass
1236 pass
1191
1237
1192 try:
1238 try:
1193 # override functions with win32 versions if possible
1239 # override functions with win32 versions if possible
1194 from util_win32 import *
1240 from util_win32 import *
1195 if not _is_win_9x():
1241 if not _is_win_9x():
1196 posixfile = posixfile_nt
1242 posixfile = posixfile_nt
1197 except ImportError:
1243 except ImportError:
1198 pass
1244 pass
1199
1245
1200 else:
1246 else:
1201 nulldev = '/dev/null'
1247 nulldev = '/dev/null'
1202
1248
1203 def rcfiles(path):
1249 def rcfiles(path):
1204 rcs = [os.path.join(path, 'hgrc')]
1250 rcs = [os.path.join(path, 'hgrc')]
1205 rcdir = os.path.join(path, 'hgrc.d')
1251 rcdir = os.path.join(path, 'hgrc.d')
1206 try:
1252 try:
1207 rcs.extend([os.path.join(rcdir, f)
1253 rcs.extend([os.path.join(rcdir, f)
1208 for f, kind in osutil.listdir(rcdir)
1254 for f, kind in osutil.listdir(rcdir)
1209 if f.endswith(".rc")])
1255 if f.endswith(".rc")])
1210 except OSError:
1256 except OSError:
1211 pass
1257 pass
1212 return rcs
1258 return rcs
1213
1259
1214 def system_rcpath():
1260 def system_rcpath():
1215 path = []
1261 path = []
1216 # old mod_python does not set sys.argv
1262 # old mod_python does not set sys.argv
1217 if len(getattr(sys, 'argv', [])) > 0:
1263 if len(getattr(sys, 'argv', [])) > 0:
1218 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1264 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1219 '/../etc/mercurial'))
1265 '/../etc/mercurial'))
1220 path.extend(rcfiles('/etc/mercurial'))
1266 path.extend(rcfiles('/etc/mercurial'))
1221 return path
1267 return path
1222
1268
1223 def user_rcpath():
1269 def user_rcpath():
1224 return [os.path.expanduser('~/.hgrc')]
1270 return [os.path.expanduser('~/.hgrc')]
1225
1271
1226 def parse_patch_output(output_line):
1272 def parse_patch_output(output_line):
1227 """parses the output produced by patch and returns the file name"""
1273 """parses the output produced by patch and returns the file name"""
1228 pf = output_line[14:]
1274 pf = output_line[14:]
1229 if os.sys.platform == 'OpenVMS':
1275 if os.sys.platform == 'OpenVMS':
1230 if pf[0] == '`':
1276 if pf[0] == '`':
1231 pf = pf[1:-1] # Remove the quotes
1277 pf = pf[1:-1] # Remove the quotes
1232 else:
1278 else:
1233 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1279 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1234 pf = pf[1:-1] # Remove the quotes
1280 pf = pf[1:-1] # Remove the quotes
1235 return pf
1281 return pf
1236
1282
1237 def sshargs(sshcmd, host, user, port):
1283 def sshargs(sshcmd, host, user, port):
1238 '''Build argument list for ssh'''
1284 '''Build argument list for ssh'''
1239 args = user and ("%s@%s" % (user, host)) or host
1285 args = user and ("%s@%s" % (user, host)) or host
1240 return port and ("%s -p %s" % (args, port)) or args
1286 return port and ("%s -p %s" % (args, port)) or args
1241
1287
1242 def is_exec(f):
1288 def is_exec(f):
1243 """check whether a file is executable"""
1289 """check whether a file is executable"""
1244 return (os.lstat(f).st_mode & 0100 != 0)
1290 return (os.lstat(f).st_mode & 0100 != 0)
1245
1291
1246 def set_flags(f, l, x):
1292 def set_flags(f, l, x):
1247 s = os.lstat(f).st_mode
1293 s = os.lstat(f).st_mode
1248 if l:
1294 if l:
1249 if not stat.S_ISLNK(s):
1295 if not stat.S_ISLNK(s):
1250 # switch file to link
1296 # switch file to link
1251 data = file(f).read()
1297 data = file(f).read()
1252 os.unlink(f)
1298 os.unlink(f)
1253 try:
1299 try:
1254 os.symlink(data, f)
1300 os.symlink(data, f)
1255 except:
1301 except:
1256 # failed to make a link, rewrite file
1302 # failed to make a link, rewrite file
1257 file(f, "w").write(data)
1303 file(f, "w").write(data)
1258 # no chmod needed at this point
1304 # no chmod needed at this point
1259 return
1305 return
1260 if stat.S_ISLNK(s):
1306 if stat.S_ISLNK(s):
1261 # switch link to file
1307 # switch link to file
1262 data = os.readlink(f)
1308 data = os.readlink(f)
1263 os.unlink(f)
1309 os.unlink(f)
1264 file(f, "w").write(data)
1310 file(f, "w").write(data)
1265 s = 0666 & ~_umask # avoid restatting for chmod
1311 s = 0666 & ~_umask # avoid restatting for chmod
1266
1312
1267 sx = s & 0100
1313 sx = s & 0100
1268 if x and not sx:
1314 if x and not sx:
1269 # Turn on +x for every +r bit when making a file executable
1315 # Turn on +x for every +r bit when making a file executable
1270 # and obey umask.
1316 # and obey umask.
1271 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1317 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1272 elif not x and sx:
1318 elif not x and sx:
1273 # Turn off all +x bits
1319 # Turn off all +x bits
1274 os.chmod(f, s & 0666)
1320 os.chmod(f, s & 0666)
1275
1321
1276 def set_binary(fd):
1322 def set_binary(fd):
1277 pass
1323 pass
1278
1324
1279 def pconvert(path):
1325 def pconvert(path):
1280 return path
1326 return path
1281
1327
1282 def localpath(path):
1328 def localpath(path):
1283 return path
1329 return path
1284
1330
1285 normpath = os.path.normpath
1331 normpath = os.path.normpath
1286 samestat = os.path.samestat
1332 samestat = os.path.samestat
1287
1333
1288 def makelock(info, pathname):
1334 def makelock(info, pathname):
1289 try:
1335 try:
1290 os.symlink(info, pathname)
1336 os.symlink(info, pathname)
1291 except OSError, why:
1337 except OSError, why:
1292 if why.errno == errno.EEXIST:
1338 if why.errno == errno.EEXIST:
1293 raise
1339 raise
1294 else:
1340 else:
1295 _makelock_file(info, pathname)
1341 _makelock_file(info, pathname)
1296
1342
1297 def readlock(pathname):
1343 def readlock(pathname):
1298 try:
1344 try:
1299 return os.readlink(pathname)
1345 return os.readlink(pathname)
1300 except OSError, why:
1346 except OSError, why:
1301 if why.errno in (errno.EINVAL, errno.ENOSYS):
1347 if why.errno in (errno.EINVAL, errno.ENOSYS):
1302 return _readlock_file(pathname)
1348 return _readlock_file(pathname)
1303 else:
1349 else:
1304 raise
1350 raise
1305
1351
1306 def shellquote(s):
1352 def shellquote(s):
1307 if os.sys.platform == 'OpenVMS':
1353 if os.sys.platform == 'OpenVMS':
1308 return '"%s"' % s
1354 return '"%s"' % s
1309 else:
1355 else:
1310 return "'%s'" % s.replace("'", "'\\''")
1356 return "'%s'" % s.replace("'", "'\\''")
1311
1357
1312 def quotecommand(cmd):
1358 def quotecommand(cmd):
1313 return cmd
1359 return cmd
1314
1360
1315 def popen(command, mode='r'):
1361 def popen(command, mode='r'):
1316 return os.popen(command, mode)
1362 return os.popen(command, mode)
1317
1363
1318 def testpid(pid):
1364 def testpid(pid):
1319 '''return False if pid dead, True if running or not sure'''
1365 '''return False if pid dead, True if running or not sure'''
1320 if os.sys.platform == 'OpenVMS':
1366 if os.sys.platform == 'OpenVMS':
1321 return True
1367 return True
1322 try:
1368 try:
1323 os.kill(pid, 0)
1369 os.kill(pid, 0)
1324 return True
1370 return True
1325 except OSError, inst:
1371 except OSError, inst:
1326 return inst.errno != errno.ESRCH
1372 return inst.errno != errno.ESRCH
1327
1373
1328 def explain_exit(code):
1374 def explain_exit(code):
1329 """return a 2-tuple (desc, code) describing a process's status"""
1375 """return a 2-tuple (desc, code) describing a process's status"""
1330 if os.WIFEXITED(code):
1376 if os.WIFEXITED(code):
1331 val = os.WEXITSTATUS(code)
1377 val = os.WEXITSTATUS(code)
1332 return _("exited with status %d") % val, val
1378 return _("exited with status %d") % val, val
1333 elif os.WIFSIGNALED(code):
1379 elif os.WIFSIGNALED(code):
1334 val = os.WTERMSIG(code)
1380 val = os.WTERMSIG(code)
1335 return _("killed by signal %d") % val, val
1381 return _("killed by signal %d") % val, val
1336 elif os.WIFSTOPPED(code):
1382 elif os.WIFSTOPPED(code):
1337 val = os.WSTOPSIG(code)
1383 val = os.WSTOPSIG(code)
1338 return _("stopped by signal %d") % val, val
1384 return _("stopped by signal %d") % val, val
1339 raise ValueError(_("invalid exit code"))
1385 raise ValueError(_("invalid exit code"))
1340
1386
1341 def isowner(fp, st=None):
1387 def isowner(fp, st=None):
1342 """Return True if the file object f belongs to the current user.
1388 """Return True if the file object f belongs to the current user.
1343
1389
1344 The return value of a util.fstat(f) may be passed as the st argument.
1390 The return value of a util.fstat(f) may be passed as the st argument.
1345 """
1391 """
1346 if st is None:
1392 if st is None:
1347 st = fstat(fp)
1393 st = fstat(fp)
1348 return st.st_uid == os.getuid()
1394 return st.st_uid == os.getuid()
1349
1395
1350 def find_in_path(name, path, default=None):
1396 def find_in_path(name, path, default=None):
1351 '''find name in search path. path can be string (will be split
1397 '''find name in search path. path can be string (will be split
1352 with os.pathsep), or iterable thing that returns strings. if name
1398 with os.pathsep), or iterable thing that returns strings. if name
1353 found, return path to name. else return default.'''
1399 found, return path to name. else return default.'''
1354 if isinstance(path, str):
1400 if isinstance(path, str):
1355 path = path.split(os.pathsep)
1401 path = path.split(os.pathsep)
1356 for p in path:
1402 for p in path:
1357 p_name = os.path.join(p, name)
1403 p_name = os.path.join(p, name)
1358 if os.path.exists(p_name):
1404 if os.path.exists(p_name):
1359 return p_name
1405 return p_name
1360 return default
1406 return default
1361
1407
1362 def set_signal_handler():
1408 def set_signal_handler():
1363 pass
1409 pass
1364
1410
1365 def find_exe(name, default=None):
1411 def find_exe(name, default=None):
1366 '''find path of an executable.
1412 '''find path of an executable.
1367 if name contains a path component, return it as is. otherwise,
1413 if name contains a path component, return it as is. otherwise,
1368 use normal executable search path.'''
1414 use normal executable search path.'''
1369
1415
1370 if os.sep in name or sys.platform == 'OpenVMS':
1416 if os.sep in name or sys.platform == 'OpenVMS':
1371 # don't check the executable bit. if the file isn't
1417 # don't check the executable bit. if the file isn't
1372 # executable, whoever tries to actually run it will give a
1418 # executable, whoever tries to actually run it will give a
1373 # much more useful error message.
1419 # much more useful error message.
1374 return name
1420 return name
1375 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1421 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1376
1422
1377 def mktempcopy(name, emptyok=False, createmode=None):
1423 def mktempcopy(name, emptyok=False, createmode=None):
1378 """Create a temporary file with the same contents from name
1424 """Create a temporary file with the same contents from name
1379
1425
1380 The permission bits are copied from the original file.
1426 The permission bits are copied from the original file.
1381
1427
1382 If the temporary file is going to be truncated immediately, you
1428 If the temporary file is going to be truncated immediately, you
1383 can use emptyok=True as an optimization.
1429 can use emptyok=True as an optimization.
1384
1430
1385 Returns the name of the temporary file.
1431 Returns the name of the temporary file.
1386 """
1432 """
1387 d, fn = os.path.split(name)
1433 d, fn = os.path.split(name)
1388 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1434 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1389 os.close(fd)
1435 os.close(fd)
1390 # Temporary files are created with mode 0600, which is usually not
1436 # Temporary files are created with mode 0600, which is usually not
1391 # what we want. If the original file already exists, just copy
1437 # what we want. If the original file already exists, just copy
1392 # its mode. Otherwise, manually obey umask.
1438 # its mode. Otherwise, manually obey umask.
1393 try:
1439 try:
1394 st_mode = os.lstat(name).st_mode & 0777
1440 st_mode = os.lstat(name).st_mode & 0777
1395 except OSError, inst:
1441 except OSError, inst:
1396 if inst.errno != errno.ENOENT:
1442 if inst.errno != errno.ENOENT:
1397 raise
1443 raise
1398 st_mode = createmode
1444 st_mode = createmode
1399 if st_mode is None:
1445 if st_mode is None:
1400 st_mode = ~_umask
1446 st_mode = ~_umask
1401 st_mode &= 0666
1447 st_mode &= 0666
1402 os.chmod(temp, st_mode)
1448 os.chmod(temp, st_mode)
1403 if emptyok:
1449 if emptyok:
1404 return temp
1450 return temp
1405 try:
1451 try:
1406 try:
1452 try:
1407 ifp = posixfile(name, "rb")
1453 ifp = posixfile(name, "rb")
1408 except IOError, inst:
1454 except IOError, inst:
1409 if inst.errno == errno.ENOENT:
1455 if inst.errno == errno.ENOENT:
1410 return temp
1456 return temp
1411 if not getattr(inst, 'filename', None):
1457 if not getattr(inst, 'filename', None):
1412 inst.filename = name
1458 inst.filename = name
1413 raise
1459 raise
1414 ofp = posixfile(temp, "wb")
1460 ofp = posixfile(temp, "wb")
1415 for chunk in filechunkiter(ifp):
1461 for chunk in filechunkiter(ifp):
1416 ofp.write(chunk)
1462 ofp.write(chunk)
1417 ifp.close()
1463 ifp.close()
1418 ofp.close()
1464 ofp.close()
1419 except:
1465 except:
1420 try: os.unlink(temp)
1466 try: os.unlink(temp)
1421 except: pass
1467 except: pass
1422 raise
1468 raise
1423 return temp
1469 return temp
1424
1470
1425 class atomictempfile(posixfile):
1471 class atomictempfile(posixfile):
1426 """file-like object that atomically updates a file
1472 """file-like object that atomically updates a file
1427
1473
1428 All writes will be redirected to a temporary copy of the original
1474 All writes will be redirected to a temporary copy of the original
1429 file. When rename is called, the copy is renamed to the original
1475 file. When rename is called, the copy is renamed to the original
1430 name, making the changes visible.
1476 name, making the changes visible.
1431 """
1477 """
1432 def __init__(self, name, mode, createmode):
1478 def __init__(self, name, mode, createmode):
1433 self.__name = name
1479 self.__name = name
1434 self.temp = mktempcopy(name, emptyok=('w' in mode),
1480 self.temp = mktempcopy(name, emptyok=('w' in mode),
1435 createmode=createmode)
1481 createmode=createmode)
1436 posixfile.__init__(self, self.temp, mode)
1482 posixfile.__init__(self, self.temp, mode)
1437
1483
1438 def rename(self):
1484 def rename(self):
1439 if not self.closed:
1485 if not self.closed:
1440 posixfile.close(self)
1486 posixfile.close(self)
1441 rename(self.temp, localpath(self.__name))
1487 rename(self.temp, localpath(self.__name))
1442
1488
1443 def __del__(self):
1489 def __del__(self):
1444 if not self.closed:
1490 if not self.closed:
1445 try:
1491 try:
1446 os.unlink(self.temp)
1492 os.unlink(self.temp)
1447 except: pass
1493 except: pass
1448 posixfile.close(self)
1494 posixfile.close(self)
1449
1495
1450 def makedirs(name, mode=None):
1496 def makedirs(name, mode=None):
1451 """recursive directory creation with parent mode inheritance"""
1497 """recursive directory creation with parent mode inheritance"""
1452 try:
1498 try:
1453 os.mkdir(name)
1499 os.mkdir(name)
1454 if mode is not None:
1500 if mode is not None:
1455 os.chmod(name, mode)
1501 os.chmod(name, mode)
1456 return
1502 return
1457 except OSError, err:
1503 except OSError, err:
1458 if err.errno == errno.EEXIST:
1504 if err.errno == errno.EEXIST:
1459 return
1505 return
1460 if err.errno != errno.ENOENT:
1506 if err.errno != errno.ENOENT:
1461 raise
1507 raise
1462 parent = os.path.abspath(os.path.dirname(name))
1508 parent = os.path.abspath(os.path.dirname(name))
1463 makedirs(parent, mode)
1509 makedirs(parent, mode)
1464 makedirs(name, mode)
1510 makedirs(name, mode)
1465
1511
1466 class opener(object):
1512 class opener(object):
1467 """Open files relative to a base directory
1513 """Open files relative to a base directory
1468
1514
1469 This class is used to hide the details of COW semantics and
1515 This class is used to hide the details of COW semantics and
1470 remote file access from higher level code.
1516 remote file access from higher level code.
1471 """
1517 """
1472 def __init__(self, base, audit=True):
1518 def __init__(self, base, audit=True):
1473 self.base = base
1519 self.base = base
1474 if audit:
1520 if audit:
1475 self.audit_path = path_auditor(base)
1521 self.audit_path = path_auditor(base)
1476 else:
1522 else:
1477 self.audit_path = always
1523 self.audit_path = always
1478 self.createmode = None
1524 self.createmode = None
1479
1525
1480 def __getattr__(self, name):
1526 def __getattr__(self, name):
1481 if name == '_can_symlink':
1527 if name == '_can_symlink':
1482 self._can_symlink = checklink(self.base)
1528 self._can_symlink = checklink(self.base)
1483 return self._can_symlink
1529 return self._can_symlink
1484 raise AttributeError(name)
1530 raise AttributeError(name)
1485
1531
1486 def _fixfilemode(self, name):
1532 def _fixfilemode(self, name):
1487 if self.createmode is None:
1533 if self.createmode is None:
1488 return
1534 return
1489 os.chmod(name, self.createmode & 0666)
1535 os.chmod(name, self.createmode & 0666)
1490
1536
1491 def __call__(self, path, mode="r", text=False, atomictemp=False):
1537 def __call__(self, path, mode="r", text=False, atomictemp=False):
1492 self.audit_path(path)
1538 self.audit_path(path)
1493 f = os.path.join(self.base, path)
1539 f = os.path.join(self.base, path)
1494
1540
1495 if not text and "b" not in mode:
1541 if not text and "b" not in mode:
1496 mode += "b" # for that other OS
1542 mode += "b" # for that other OS
1497
1543
1498 nlink = -1
1544 nlink = -1
1499 if mode not in ("r", "rb"):
1545 if mode not in ("r", "rb"):
1500 try:
1546 try:
1501 nlink = nlinks(f)
1547 nlink = nlinks(f)
1502 except OSError:
1548 except OSError:
1503 nlink = 0
1549 nlink = 0
1504 d = os.path.dirname(f)
1550 d = os.path.dirname(f)
1505 if not os.path.isdir(d):
1551 if not os.path.isdir(d):
1506 makedirs(d, self.createmode)
1552 makedirs(d, self.createmode)
1507 if atomictemp:
1553 if atomictemp:
1508 return atomictempfile(f, mode, self.createmode)
1554 return atomictempfile(f, mode, self.createmode)
1509 if nlink > 1:
1555 if nlink > 1:
1510 rename(mktempcopy(f), f)
1556 rename(mktempcopy(f), f)
1511 fp = posixfile(f, mode)
1557 fp = posixfile(f, mode)
1512 if nlink == 0:
1558 if nlink == 0:
1513 self._fixfilemode(f)
1559 self._fixfilemode(f)
1514 return fp
1560 return fp
1515
1561
1516 def symlink(self, src, dst):
1562 def symlink(self, src, dst):
1517 self.audit_path(dst)
1563 self.audit_path(dst)
1518 linkname = os.path.join(self.base, dst)
1564 linkname = os.path.join(self.base, dst)
1519 try:
1565 try:
1520 os.unlink(linkname)
1566 os.unlink(linkname)
1521 except OSError:
1567 except OSError:
1522 pass
1568 pass
1523
1569
1524 dirname = os.path.dirname(linkname)
1570 dirname = os.path.dirname(linkname)
1525 if not os.path.exists(dirname):
1571 if not os.path.exists(dirname):
1526 makedirs(dirname, self.createmode)
1572 makedirs(dirname, self.createmode)
1527
1573
1528 if self._can_symlink:
1574 if self._can_symlink:
1529 try:
1575 try:
1530 os.symlink(src, linkname)
1576 os.symlink(src, linkname)
1531 except OSError, err:
1577 except OSError, err:
1532 raise OSError(err.errno, _('could not symlink to %r: %s') %
1578 raise OSError(err.errno, _('could not symlink to %r: %s') %
1533 (src, err.strerror), linkname)
1579 (src, err.strerror), linkname)
1534 else:
1580 else:
1535 f = self(dst, "w")
1581 f = self(dst, "w")
1536 f.write(src)
1582 f.write(src)
1537 f.close()
1583 f.close()
1538 self._fixfilemode(dst)
1584 self._fixfilemode(dst)
1539
1585
1540 class chunkbuffer(object):
1586 class chunkbuffer(object):
1541 """Allow arbitrary sized chunks of data to be efficiently read from an
1587 """Allow arbitrary sized chunks of data to be efficiently read from an
1542 iterator over chunks of arbitrary size."""
1588 iterator over chunks of arbitrary size."""
1543
1589
1544 def __init__(self, in_iter):
1590 def __init__(self, in_iter):
1545 """in_iter is the iterator that's iterating over the input chunks.
1591 """in_iter is the iterator that's iterating over the input chunks.
1546 targetsize is how big a buffer to try to maintain."""
1592 targetsize is how big a buffer to try to maintain."""
1547 self.iter = iter(in_iter)
1593 self.iter = iter(in_iter)
1548 self.buf = ''
1594 self.buf = ''
1549 self.targetsize = 2**16
1595 self.targetsize = 2**16
1550
1596
1551 def read(self, l):
1597 def read(self, l):
1552 """Read L bytes of data from the iterator of chunks of data.
1598 """Read L bytes of data from the iterator of chunks of data.
1553 Returns less than L bytes if the iterator runs dry."""
1599 Returns less than L bytes if the iterator runs dry."""
1554 if l > len(self.buf) and self.iter:
1600 if l > len(self.buf) and self.iter:
1555 # Clamp to a multiple of self.targetsize
1601 # Clamp to a multiple of self.targetsize
1556 targetsize = max(l, self.targetsize)
1602 targetsize = max(l, self.targetsize)
1557 collector = cStringIO.StringIO()
1603 collector = cStringIO.StringIO()
1558 collector.write(self.buf)
1604 collector.write(self.buf)
1559 collected = len(self.buf)
1605 collected = len(self.buf)
1560 for chunk in self.iter:
1606 for chunk in self.iter:
1561 collector.write(chunk)
1607 collector.write(chunk)
1562 collected += len(chunk)
1608 collected += len(chunk)
1563 if collected >= targetsize:
1609 if collected >= targetsize:
1564 break
1610 break
1565 if collected < targetsize:
1611 if collected < targetsize:
1566 self.iter = False
1612 self.iter = False
1567 self.buf = collector.getvalue()
1613 self.buf = collector.getvalue()
1568 if len(self.buf) == l:
1614 if len(self.buf) == l:
1569 s, self.buf = str(self.buf), ''
1615 s, self.buf = str(self.buf), ''
1570 else:
1616 else:
1571 s, self.buf = self.buf[:l], buffer(self.buf, l)
1617 s, self.buf = self.buf[:l], buffer(self.buf, l)
1572 return s
1618 return s
1573
1619
1574 def filechunkiter(f, size=65536, limit=None):
1620 def filechunkiter(f, size=65536, limit=None):
1575 """Create a generator that produces the data in the file size
1621 """Create a generator that produces the data in the file size
1576 (default 65536) bytes at a time, up to optional limit (default is
1622 (default 65536) bytes at a time, up to optional limit (default is
1577 to read all data). Chunks may be less than size bytes if the
1623 to read all data). Chunks may be less than size bytes if the
1578 chunk is the last chunk in the file, or the file is a socket or
1624 chunk is the last chunk in the file, or the file is a socket or
1579 some other type of file that sometimes reads less data than is
1625 some other type of file that sometimes reads less data than is
1580 requested."""
1626 requested."""
1581 assert size >= 0
1627 assert size >= 0
1582 assert limit is None or limit >= 0
1628 assert limit is None or limit >= 0
1583 while True:
1629 while True:
1584 if limit is None: nbytes = size
1630 if limit is None: nbytes = size
1585 else: nbytes = min(limit, size)
1631 else: nbytes = min(limit, size)
1586 s = nbytes and f.read(nbytes)
1632 s = nbytes and f.read(nbytes)
1587 if not s: break
1633 if not s: break
1588 if limit: limit -= len(s)
1634 if limit: limit -= len(s)
1589 yield s
1635 yield s
1590
1636
1591 def makedate():
1637 def makedate():
1592 lt = time.localtime()
1638 lt = time.localtime()
1593 if lt[8] == 1 and time.daylight:
1639 if lt[8] == 1 and time.daylight:
1594 tz = time.altzone
1640 tz = time.altzone
1595 else:
1641 else:
1596 tz = time.timezone
1642 tz = time.timezone
1597 return time.mktime(lt), tz
1643 return time.mktime(lt), tz
1598
1644
1599 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1645 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1600 """represent a (unixtime, offset) tuple as a localized time.
1646 """represent a (unixtime, offset) tuple as a localized time.
1601 unixtime is seconds since the epoch, and offset is the time zone's
1647 unixtime is seconds since the epoch, and offset is the time zone's
1602 number of seconds away from UTC. if timezone is false, do not
1648 number of seconds away from UTC. if timezone is false, do not
1603 append time zone to string."""
1649 append time zone to string."""
1604 t, tz = date or makedate()
1650 t, tz = date or makedate()
1605 if "%1" in format or "%2" in format:
1651 if "%1" in format or "%2" in format:
1606 sign = (tz > 0) and "-" or "+"
1652 sign = (tz > 0) and "-" or "+"
1607 minutes = abs(tz) / 60
1653 minutes = abs(tz) / 60
1608 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1654 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1609 format = format.replace("%2", "%02d" % (minutes % 60))
1655 format = format.replace("%2", "%02d" % (minutes % 60))
1610 s = time.strftime(format, time.gmtime(float(t) - tz))
1656 s = time.strftime(format, time.gmtime(float(t) - tz))
1611 return s
1657 return s
1612
1658
1613 def shortdate(date=None):
1659 def shortdate(date=None):
1614 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1660 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1615 return datestr(date, format='%Y-%m-%d')
1661 return datestr(date, format='%Y-%m-%d')
1616
1662
1617 def strdate(string, format, defaults=[]):
1663 def strdate(string, format, defaults=[]):
1618 """parse a localized time string and return a (unixtime, offset) tuple.
1664 """parse a localized time string and return a (unixtime, offset) tuple.
1619 if the string cannot be parsed, ValueError is raised."""
1665 if the string cannot be parsed, ValueError is raised."""
1620 def timezone(string):
1666 def timezone(string):
1621 tz = string.split()[-1]
1667 tz = string.split()[-1]
1622 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1668 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1623 sign = (tz[0] == "+") and 1 or -1
1669 sign = (tz[0] == "+") and 1 or -1
1624 hours = int(tz[1:3])
1670 hours = int(tz[1:3])
1625 minutes = int(tz[3:5])
1671 minutes = int(tz[3:5])
1626 return -sign * (hours * 60 + minutes) * 60
1672 return -sign * (hours * 60 + minutes) * 60
1627 if tz == "GMT" or tz == "UTC":
1673 if tz == "GMT" or tz == "UTC":
1628 return 0
1674 return 0
1629 return None
1675 return None
1630
1676
1631 # NOTE: unixtime = localunixtime + offset
1677 # NOTE: unixtime = localunixtime + offset
1632 offset, date = timezone(string), string
1678 offset, date = timezone(string), string
1633 if offset != None:
1679 if offset != None:
1634 date = " ".join(string.split()[:-1])
1680 date = " ".join(string.split()[:-1])
1635
1681
1636 # add missing elements from defaults
1682 # add missing elements from defaults
1637 for part in defaults:
1683 for part in defaults:
1638 found = [True for p in part if ("%"+p) in format]
1684 found = [True for p in part if ("%"+p) in format]
1639 if not found:
1685 if not found:
1640 date += "@" + defaults[part]
1686 date += "@" + defaults[part]
1641 format += "@%" + part[0]
1687 format += "@%" + part[0]
1642
1688
1643 timetuple = time.strptime(date, format)
1689 timetuple = time.strptime(date, format)
1644 localunixtime = int(calendar.timegm(timetuple))
1690 localunixtime = int(calendar.timegm(timetuple))
1645 if offset is None:
1691 if offset is None:
1646 # local timezone
1692 # local timezone
1647 unixtime = int(time.mktime(timetuple))
1693 unixtime = int(time.mktime(timetuple))
1648 offset = unixtime - localunixtime
1694 offset = unixtime - localunixtime
1649 else:
1695 else:
1650 unixtime = localunixtime + offset
1696 unixtime = localunixtime + offset
1651 return unixtime, offset
1697 return unixtime, offset
1652
1698
1653 def parsedate(date, formats=None, defaults=None):
1699 def parsedate(date, formats=None, defaults=None):
1654 """parse a localized date/time string and return a (unixtime, offset) tuple.
1700 """parse a localized date/time string and return a (unixtime, offset) tuple.
1655
1701
1656 The date may be a "unixtime offset" string or in one of the specified
1702 The date may be a "unixtime offset" string or in one of the specified
1657 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1703 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1658 """
1704 """
1659 if not date:
1705 if not date:
1660 return 0, 0
1706 return 0, 0
1661 if isinstance(date, tuple) and len(date) == 2:
1707 if isinstance(date, tuple) and len(date) == 2:
1662 return date
1708 return date
1663 if not formats:
1709 if not formats:
1664 formats = defaultdateformats
1710 formats = defaultdateformats
1665 date = date.strip()
1711 date = date.strip()
1666 try:
1712 try:
1667 when, offset = map(int, date.split(' '))
1713 when, offset = map(int, date.split(' '))
1668 except ValueError:
1714 except ValueError:
1669 # fill out defaults
1715 # fill out defaults
1670 if not defaults:
1716 if not defaults:
1671 defaults = {}
1717 defaults = {}
1672 now = makedate()
1718 now = makedate()
1673 for part in "d mb yY HI M S".split():
1719 for part in "d mb yY HI M S".split():
1674 if part not in defaults:
1720 if part not in defaults:
1675 if part[0] in "HMS":
1721 if part[0] in "HMS":
1676 defaults[part] = "00"
1722 defaults[part] = "00"
1677 else:
1723 else:
1678 defaults[part] = datestr(now, "%" + part[0])
1724 defaults[part] = datestr(now, "%" + part[0])
1679
1725
1680 for format in formats:
1726 for format in formats:
1681 try:
1727 try:
1682 when, offset = strdate(date, format, defaults)
1728 when, offset = strdate(date, format, defaults)
1683 except (ValueError, OverflowError):
1729 except (ValueError, OverflowError):
1684 pass
1730 pass
1685 else:
1731 else:
1686 break
1732 break
1687 else:
1733 else:
1688 raise Abort(_('invalid date: %r ') % date)
1734 raise Abort(_('invalid date: %r ') % date)
1689 # validate explicit (probably user-specified) date and
1735 # validate explicit (probably user-specified) date and
1690 # time zone offset. values must fit in signed 32 bits for
1736 # time zone offset. values must fit in signed 32 bits for
1691 # current 32-bit linux runtimes. timezones go from UTC-12
1737 # current 32-bit linux runtimes. timezones go from UTC-12
1692 # to UTC+14
1738 # to UTC+14
1693 if abs(when) > 0x7fffffff:
1739 if abs(when) > 0x7fffffff:
1694 raise Abort(_('date exceeds 32 bits: %d') % when)
1740 raise Abort(_('date exceeds 32 bits: %d') % when)
1695 if offset < -50400 or offset > 43200:
1741 if offset < -50400 or offset > 43200:
1696 raise Abort(_('impossible time zone offset: %d') % offset)
1742 raise Abort(_('impossible time zone offset: %d') % offset)
1697 return when, offset
1743 return when, offset
1698
1744
1699 def matchdate(date):
1745 def matchdate(date):
1700 """Return a function that matches a given date match specifier
1746 """Return a function that matches a given date match specifier
1701
1747
1702 Formats include:
1748 Formats include:
1703
1749
1704 '{date}' match a given date to the accuracy provided
1750 '{date}' match a given date to the accuracy provided
1705
1751
1706 '<{date}' on or before a given date
1752 '<{date}' on or before a given date
1707
1753
1708 '>{date}' on or after a given date
1754 '>{date}' on or after a given date
1709
1755
1710 """
1756 """
1711
1757
1712 def lower(date):
1758 def lower(date):
1713 d = dict(mb="1", d="1")
1759 d = dict(mb="1", d="1")
1714 return parsedate(date, extendeddateformats, d)[0]
1760 return parsedate(date, extendeddateformats, d)[0]
1715
1761
1716 def upper(date):
1762 def upper(date):
1717 d = dict(mb="12", HI="23", M="59", S="59")
1763 d = dict(mb="12", HI="23", M="59", S="59")
1718 for days in "31 30 29".split():
1764 for days in "31 30 29".split():
1719 try:
1765 try:
1720 d["d"] = days
1766 d["d"] = days
1721 return parsedate(date, extendeddateformats, d)[0]
1767 return parsedate(date, extendeddateformats, d)[0]
1722 except:
1768 except:
1723 pass
1769 pass
1724 d["d"] = "28"
1770 d["d"] = "28"
1725 return parsedate(date, extendeddateformats, d)[0]
1771 return parsedate(date, extendeddateformats, d)[0]
1726
1772
1727 if date[0] == "<":
1773 if date[0] == "<":
1728 when = upper(date[1:])
1774 when = upper(date[1:])
1729 return lambda x: x <= when
1775 return lambda x: x <= when
1730 elif date[0] == ">":
1776 elif date[0] == ">":
1731 when = lower(date[1:])
1777 when = lower(date[1:])
1732 return lambda x: x >= when
1778 return lambda x: x >= when
1733 elif date[0] == "-":
1779 elif date[0] == "-":
1734 try:
1780 try:
1735 days = int(date[1:])
1781 days = int(date[1:])
1736 except ValueError:
1782 except ValueError:
1737 raise Abort(_("invalid day spec: %s") % date[1:])
1783 raise Abort(_("invalid day spec: %s") % date[1:])
1738 when = makedate()[0] - days * 3600 * 24
1784 when = makedate()[0] - days * 3600 * 24
1739 return lambda x: x >= when
1785 return lambda x: x >= when
1740 elif " to " in date:
1786 elif " to " in date:
1741 a, b = date.split(" to ")
1787 a, b = date.split(" to ")
1742 start, stop = lower(a), upper(b)
1788 start, stop = lower(a), upper(b)
1743 return lambda x: x >= start and x <= stop
1789 return lambda x: x >= start and x <= stop
1744 else:
1790 else:
1745 start, stop = lower(date), upper(date)
1791 start, stop = lower(date), upper(date)
1746 return lambda x: x >= start and x <= stop
1792 return lambda x: x >= start and x <= stop
1747
1793
1748 def shortuser(user):
1794 def shortuser(user):
1749 """Return a short representation of a user name or email address."""
1795 """Return a short representation of a user name or email address."""
1750 f = user.find('@')
1796 f = user.find('@')
1751 if f >= 0:
1797 if f >= 0:
1752 user = user[:f]
1798 user = user[:f]
1753 f = user.find('<')
1799 f = user.find('<')
1754 if f >= 0:
1800 if f >= 0:
1755 user = user[f+1:]
1801 user = user[f+1:]
1756 f = user.find(' ')
1802 f = user.find(' ')
1757 if f >= 0:
1803 if f >= 0:
1758 user = user[:f]
1804 user = user[:f]
1759 f = user.find('.')
1805 f = user.find('.')
1760 if f >= 0:
1806 if f >= 0:
1761 user = user[:f]
1807 user = user[:f]
1762 return user
1808 return user
1763
1809
1764 def email(author):
1810 def email(author):
1765 '''get email of author.'''
1811 '''get email of author.'''
1766 r = author.find('>')
1812 r = author.find('>')
1767 if r == -1: r = None
1813 if r == -1: r = None
1768 return author[author.find('<')+1:r]
1814 return author[author.find('<')+1:r]
1769
1815
1770 def ellipsis(text, maxlength=400):
1816 def ellipsis(text, maxlength=400):
1771 """Trim string to at most maxlength (default: 400) characters."""
1817 """Trim string to at most maxlength (default: 400) characters."""
1772 if len(text) <= maxlength:
1818 if len(text) <= maxlength:
1773 return text
1819 return text
1774 else:
1820 else:
1775 return "%s..." % (text[:maxlength-3])
1821 return "%s..." % (text[:maxlength-3])
1776
1822
1777 def walkrepos(path, followsym=False, seen_dirs=None):
1823 def walkrepos(path, followsym=False, seen_dirs=None):
1778 '''yield every hg repository under path, recursively.'''
1824 '''yield every hg repository under path, recursively.'''
1779 def errhandler(err):
1825 def errhandler(err):
1780 if err.filename == path:
1826 if err.filename == path:
1781 raise err
1827 raise err
1782 if followsym and hasattr(os.path, 'samestat'):
1828 if followsym and hasattr(os.path, 'samestat'):
1783 def _add_dir_if_not_there(dirlst, dirname):
1829 def _add_dir_if_not_there(dirlst, dirname):
1784 match = False
1830 match = False
1785 samestat = os.path.samestat
1831 samestat = os.path.samestat
1786 dirstat = os.stat(dirname)
1832 dirstat = os.stat(dirname)
1787 for lstdirstat in dirlst:
1833 for lstdirstat in dirlst:
1788 if samestat(dirstat, lstdirstat):
1834 if samestat(dirstat, lstdirstat):
1789 match = True
1835 match = True
1790 break
1836 break
1791 if not match:
1837 if not match:
1792 dirlst.append(dirstat)
1838 dirlst.append(dirstat)
1793 return not match
1839 return not match
1794 else:
1840 else:
1795 followsym = False
1841 followsym = False
1796
1842
1797 if (seen_dirs is None) and followsym:
1843 if (seen_dirs is None) and followsym:
1798 seen_dirs = []
1844 seen_dirs = []
1799 _add_dir_if_not_there(seen_dirs, path)
1845 _add_dir_if_not_there(seen_dirs, path)
1800 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1846 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1801 if '.hg' in dirs:
1847 if '.hg' in dirs:
1802 dirs[:] = [] # don't descend further
1848 dirs[:] = [] # don't descend further
1803 yield root # found a repository
1849 yield root # found a repository
1804 qroot = os.path.join(root, '.hg', 'patches')
1850 qroot = os.path.join(root, '.hg', 'patches')
1805 if os.path.isdir(os.path.join(qroot, '.hg')):
1851 if os.path.isdir(os.path.join(qroot, '.hg')):
1806 yield qroot # we have a patch queue repo here
1852 yield qroot # we have a patch queue repo here
1807 elif followsym:
1853 elif followsym:
1808 newdirs = []
1854 newdirs = []
1809 for d in dirs:
1855 for d in dirs:
1810 fname = os.path.join(root, d)
1856 fname = os.path.join(root, d)
1811 if _add_dir_if_not_there(seen_dirs, fname):
1857 if _add_dir_if_not_there(seen_dirs, fname):
1812 if os.path.islink(fname):
1858 if os.path.islink(fname):
1813 for hgname in walkrepos(fname, True, seen_dirs):
1859 for hgname in walkrepos(fname, True, seen_dirs):
1814 yield hgname
1860 yield hgname
1815 else:
1861 else:
1816 newdirs.append(d)
1862 newdirs.append(d)
1817 dirs[:] = newdirs
1863 dirs[:] = newdirs
1818
1864
1819 _rcpath = None
1865 _rcpath = None
1820
1866
1821 def os_rcpath():
1867 def os_rcpath():
1822 '''return default os-specific hgrc search path'''
1868 '''return default os-specific hgrc search path'''
1823 path = system_rcpath()
1869 path = system_rcpath()
1824 path.extend(user_rcpath())
1870 path.extend(user_rcpath())
1825 path = [os.path.normpath(f) for f in path]
1871 path = [os.path.normpath(f) for f in path]
1826 return path
1872 return path
1827
1873
1828 def rcpath():
1874 def rcpath():
1829 '''return hgrc search path. if env var HGRCPATH is set, use it.
1875 '''return hgrc search path. if env var HGRCPATH is set, use it.
1830 for each item in path, if directory, use files ending in .rc,
1876 for each item in path, if directory, use files ending in .rc,
1831 else use item.
1877 else use item.
1832 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1878 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1833 if no HGRCPATH, use default os-specific path.'''
1879 if no HGRCPATH, use default os-specific path.'''
1834 global _rcpath
1880 global _rcpath
1835 if _rcpath is None:
1881 if _rcpath is None:
1836 if 'HGRCPATH' in os.environ:
1882 if 'HGRCPATH' in os.environ:
1837 _rcpath = []
1883 _rcpath = []
1838 for p in os.environ['HGRCPATH'].split(os.pathsep):
1884 for p in os.environ['HGRCPATH'].split(os.pathsep):
1839 if not p: continue
1885 if not p: continue
1840 if os.path.isdir(p):
1886 if os.path.isdir(p):
1841 for f, kind in osutil.listdir(p):
1887 for f, kind in osutil.listdir(p):
1842 if f.endswith('.rc'):
1888 if f.endswith('.rc'):
1843 _rcpath.append(os.path.join(p, f))
1889 _rcpath.append(os.path.join(p, f))
1844 else:
1890 else:
1845 _rcpath.append(p)
1891 _rcpath.append(p)
1846 else:
1892 else:
1847 _rcpath = os_rcpath()
1893 _rcpath = os_rcpath()
1848 return _rcpath
1894 return _rcpath
1849
1895
1850 def bytecount(nbytes):
1896 def bytecount(nbytes):
1851 '''return byte count formatted as readable string, with units'''
1897 '''return byte count formatted as readable string, with units'''
1852
1898
1853 units = (
1899 units = (
1854 (100, 1<<30, _('%.0f GB')),
1900 (100, 1<<30, _('%.0f GB')),
1855 (10, 1<<30, _('%.1f GB')),
1901 (10, 1<<30, _('%.1f GB')),
1856 (1, 1<<30, _('%.2f GB')),
1902 (1, 1<<30, _('%.2f GB')),
1857 (100, 1<<20, _('%.0f MB')),
1903 (100, 1<<20, _('%.0f MB')),
1858 (10, 1<<20, _('%.1f MB')),
1904 (10, 1<<20, _('%.1f MB')),
1859 (1, 1<<20, _('%.2f MB')),
1905 (1, 1<<20, _('%.2f MB')),
1860 (100, 1<<10, _('%.0f KB')),
1906 (100, 1<<10, _('%.0f KB')),
1861 (10, 1<<10, _('%.1f KB')),
1907 (10, 1<<10, _('%.1f KB')),
1862 (1, 1<<10, _('%.2f KB')),
1908 (1, 1<<10, _('%.2f KB')),
1863 (1, 1, _('%.0f bytes')),
1909 (1, 1, _('%.0f bytes')),
1864 )
1910 )
1865
1911
1866 for multiplier, divisor, format in units:
1912 for multiplier, divisor, format in units:
1867 if nbytes >= divisor * multiplier:
1913 if nbytes >= divisor * multiplier:
1868 return format % (nbytes / float(divisor))
1914 return format % (nbytes / float(divisor))
1869 return units[-1][2] % nbytes
1915 return units[-1][2] % nbytes
1870
1916
1871 def drop_scheme(scheme, path):
1917 def drop_scheme(scheme, path):
1872 sc = scheme + ':'
1918 sc = scheme + ':'
1873 if path.startswith(sc):
1919 if path.startswith(sc):
1874 path = path[len(sc):]
1920 path = path[len(sc):]
1875 if path.startswith('//'):
1921 if path.startswith('//'):
1876 path = path[2:]
1922 path = path[2:]
1877 return path
1923 return path
1878
1924
1879 def uirepr(s):
1925 def uirepr(s):
1880 # Avoid double backslash in Windows path repr()
1926 # Avoid double backslash in Windows path repr()
1881 return repr(s).replace('\\\\', '\\')
1927 return repr(s).replace('\\\\', '\\')
1882
1928
1883 def hidepassword(url):
1929 def hidepassword(url):
1884 '''hide user credential in a url string'''
1930 '''hide user credential in a url string'''
1885 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1931 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1886 netloc = re.sub('([^:]*):([^@]*)@(.*)', r'\1:***@\3', netloc)
1932 netloc = re.sub('([^:]*):([^@]*)@(.*)', r'\1:***@\3', netloc)
1887 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
1933 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
1888
1934
1889 def removeauth(url):
1935 def removeauth(url):
1890 '''remove all authentication information from a url string'''
1936 '''remove all authentication information from a url string'''
1891 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1937 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1892 netloc = netloc[netloc.find('@')+1:]
1938 netloc = netloc[netloc.find('@')+1:]
1893 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
1939 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
General Comments 0
You need to be logged in to leave comments. Login now