##// END OF EJS Templates
util: improved the check for the existence of the 'buffer' builtin...
Renato Cunha -
r11565:7546d4a2 default
parent child Browse files
Show More
@@ -1,1390 +1,1392 b''
1 # util.py - Mercurial utility functions and platform specfic implementations
1 # util.py - Mercurial utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specfic implementations.
10 """Mercurial utility functions and platform specfic implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from i18n import _
16 from i18n import _
17 import error, osutil, encoding
17 import error, osutil, encoding
18 import cStringIO, errno, re, shutil, sys, tempfile, traceback
18 import cStringIO, errno, re, shutil, sys, tempfile, traceback
19 import os, stat, time, calendar, textwrap, unicodedata, signal
19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 import imp
20 import imp
21
21
22 # Python compatibility
22 # Python compatibility
23
23
24 def sha1(s):
24 def sha1(s):
25 return _fastsha1(s)
25 return _fastsha1(s)
26
26
27 def _fastsha1(s):
27 def _fastsha1(s):
28 # This function will import sha1 from hashlib or sha (whichever is
28 # This function will import sha1 from hashlib or sha (whichever is
29 # available) and overwrite itself with it on the first call.
29 # available) and overwrite itself with it on the first call.
30 # Subsequent calls will go directly to the imported function.
30 # Subsequent calls will go directly to the imported function.
31 try:
31 try:
32 from hashlib import sha1 as _sha1
32 from hashlib import sha1 as _sha1
33 except ImportError:
33 except ImportError:
34 from sha import sha as _sha1
34 from sha import sha as _sha1
35 global _fastsha1, sha1
35 global _fastsha1, sha1
36 _fastsha1 = sha1 = _sha1
36 _fastsha1 = sha1 = _sha1
37 return _sha1(s)
37 return _sha1(s)
38
38
39 import __builtin__
39 import __builtin__
40
40
41 def fakebuffer(sliceable, offset=0):
41 def fakebuffer(sliceable, offset=0):
42 return sliceable[offset:]
42 return sliceable[offset:]
43 if not hasattr(__builtin__, 'buffer'):
43 try:
44 buffer
45 except NameError:
44 __builtin__.buffer = fakebuffer
46 __builtin__.buffer = fakebuffer
45
47
46 import subprocess
48 import subprocess
47 closefds = os.name == 'posix'
49 closefds = os.name == 'posix'
48
50
49 def popen2(cmd, env=None, newlines=False):
51 def popen2(cmd, env=None, newlines=False):
50 # Setting bufsize to -1 lets the system decide the buffer size.
52 # Setting bufsize to -1 lets the system decide the buffer size.
51 # The default for bufsize is 0, meaning unbuffered. This leads to
53 # The default for bufsize is 0, meaning unbuffered. This leads to
52 # poor performance on Mac OS X: http://bugs.python.org/issue4194
54 # poor performance on Mac OS X: http://bugs.python.org/issue4194
53 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
55 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
54 close_fds=closefds,
56 close_fds=closefds,
55 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
57 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
56 universal_newlines=newlines,
58 universal_newlines=newlines,
57 env=env)
59 env=env)
58 return p.stdin, p.stdout
60 return p.stdin, p.stdout
59
61
60 def popen3(cmd, env=None, newlines=False):
62 def popen3(cmd, env=None, newlines=False):
61 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
63 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
62 close_fds=closefds,
64 close_fds=closefds,
63 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
65 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
64 stderr=subprocess.PIPE,
66 stderr=subprocess.PIPE,
65 universal_newlines=newlines,
67 universal_newlines=newlines,
66 env=env)
68 env=env)
67 return p.stdin, p.stdout, p.stderr
69 return p.stdin, p.stdout, p.stderr
68
70
69 def version():
71 def version():
70 """Return version information if available."""
72 """Return version information if available."""
71 try:
73 try:
72 import __version__
74 import __version__
73 return __version__.version
75 return __version__.version
74 except ImportError:
76 except ImportError:
75 return 'unknown'
77 return 'unknown'
76
78
77 # used by parsedate
79 # used by parsedate
78 defaultdateformats = (
80 defaultdateformats = (
79 '%Y-%m-%d %H:%M:%S',
81 '%Y-%m-%d %H:%M:%S',
80 '%Y-%m-%d %I:%M:%S%p',
82 '%Y-%m-%d %I:%M:%S%p',
81 '%Y-%m-%d %H:%M',
83 '%Y-%m-%d %H:%M',
82 '%Y-%m-%d %I:%M%p',
84 '%Y-%m-%d %I:%M%p',
83 '%Y-%m-%d',
85 '%Y-%m-%d',
84 '%m-%d',
86 '%m-%d',
85 '%m/%d',
87 '%m/%d',
86 '%m/%d/%y',
88 '%m/%d/%y',
87 '%m/%d/%Y',
89 '%m/%d/%Y',
88 '%a %b %d %H:%M:%S %Y',
90 '%a %b %d %H:%M:%S %Y',
89 '%a %b %d %I:%M:%S%p %Y',
91 '%a %b %d %I:%M:%S%p %Y',
90 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
92 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
91 '%b %d %H:%M:%S %Y',
93 '%b %d %H:%M:%S %Y',
92 '%b %d %I:%M:%S%p %Y',
94 '%b %d %I:%M:%S%p %Y',
93 '%b %d %H:%M:%S',
95 '%b %d %H:%M:%S',
94 '%b %d %I:%M:%S%p',
96 '%b %d %I:%M:%S%p',
95 '%b %d %H:%M',
97 '%b %d %H:%M',
96 '%b %d %I:%M%p',
98 '%b %d %I:%M%p',
97 '%b %d %Y',
99 '%b %d %Y',
98 '%b %d',
100 '%b %d',
99 '%H:%M:%S',
101 '%H:%M:%S',
100 '%I:%M:%S%p',
102 '%I:%M:%S%p',
101 '%H:%M',
103 '%H:%M',
102 '%I:%M%p',
104 '%I:%M%p',
103 )
105 )
104
106
105 extendeddateformats = defaultdateformats + (
107 extendeddateformats = defaultdateformats + (
106 "%Y",
108 "%Y",
107 "%Y-%m",
109 "%Y-%m",
108 "%b",
110 "%b",
109 "%b %Y",
111 "%b %Y",
110 )
112 )
111
113
112 def cachefunc(func):
114 def cachefunc(func):
113 '''cache the result of function calls'''
115 '''cache the result of function calls'''
114 # XXX doesn't handle keywords args
116 # XXX doesn't handle keywords args
115 cache = {}
117 cache = {}
116 if func.func_code.co_argcount == 1:
118 if func.func_code.co_argcount == 1:
117 # we gain a small amount of time because
119 # we gain a small amount of time because
118 # we don't need to pack/unpack the list
120 # we don't need to pack/unpack the list
119 def f(arg):
121 def f(arg):
120 if arg not in cache:
122 if arg not in cache:
121 cache[arg] = func(arg)
123 cache[arg] = func(arg)
122 return cache[arg]
124 return cache[arg]
123 else:
125 else:
124 def f(*args):
126 def f(*args):
125 if args not in cache:
127 if args not in cache:
126 cache[args] = func(*args)
128 cache[args] = func(*args)
127 return cache[args]
129 return cache[args]
128
130
129 return f
131 return f
130
132
131 def lrucachefunc(func):
133 def lrucachefunc(func):
132 '''cache most recent results of function calls'''
134 '''cache most recent results of function calls'''
133 cache = {}
135 cache = {}
134 order = []
136 order = []
135 if func.func_code.co_argcount == 1:
137 if func.func_code.co_argcount == 1:
136 def f(arg):
138 def f(arg):
137 if arg not in cache:
139 if arg not in cache:
138 if len(cache) > 20:
140 if len(cache) > 20:
139 del cache[order.pop(0)]
141 del cache[order.pop(0)]
140 cache[arg] = func(arg)
142 cache[arg] = func(arg)
141 else:
143 else:
142 order.remove(arg)
144 order.remove(arg)
143 order.append(arg)
145 order.append(arg)
144 return cache[arg]
146 return cache[arg]
145 else:
147 else:
146 def f(*args):
148 def f(*args):
147 if args not in cache:
149 if args not in cache:
148 if len(cache) > 20:
150 if len(cache) > 20:
149 del cache[order.pop(0)]
151 del cache[order.pop(0)]
150 cache[args] = func(*args)
152 cache[args] = func(*args)
151 else:
153 else:
152 order.remove(args)
154 order.remove(args)
153 order.append(args)
155 order.append(args)
154 return cache[args]
156 return cache[args]
155
157
156 return f
158 return f
157
159
158 class propertycache(object):
160 class propertycache(object):
159 def __init__(self, func):
161 def __init__(self, func):
160 self.func = func
162 self.func = func
161 self.name = func.__name__
163 self.name = func.__name__
162 def __get__(self, obj, type=None):
164 def __get__(self, obj, type=None):
163 result = self.func(obj)
165 result = self.func(obj)
164 setattr(obj, self.name, result)
166 setattr(obj, self.name, result)
165 return result
167 return result
166
168
167 def pipefilter(s, cmd):
169 def pipefilter(s, cmd):
168 '''filter string S through command CMD, returning its output'''
170 '''filter string S through command CMD, returning its output'''
169 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
171 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
170 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
172 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
171 pout, perr = p.communicate(s)
173 pout, perr = p.communicate(s)
172 return pout
174 return pout
173
175
174 def tempfilter(s, cmd):
176 def tempfilter(s, cmd):
175 '''filter string S through a pair of temporary files with CMD.
177 '''filter string S through a pair of temporary files with CMD.
176 CMD is used as a template to create the real command to be run,
178 CMD is used as a template to create the real command to be run,
177 with the strings INFILE and OUTFILE replaced by the real names of
179 with the strings INFILE and OUTFILE replaced by the real names of
178 the temporary files generated.'''
180 the temporary files generated.'''
179 inname, outname = None, None
181 inname, outname = None, None
180 try:
182 try:
181 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
183 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
182 fp = os.fdopen(infd, 'wb')
184 fp = os.fdopen(infd, 'wb')
183 fp.write(s)
185 fp.write(s)
184 fp.close()
186 fp.close()
185 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
187 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
186 os.close(outfd)
188 os.close(outfd)
187 cmd = cmd.replace('INFILE', inname)
189 cmd = cmd.replace('INFILE', inname)
188 cmd = cmd.replace('OUTFILE', outname)
190 cmd = cmd.replace('OUTFILE', outname)
189 code = os.system(cmd)
191 code = os.system(cmd)
190 if sys.platform == 'OpenVMS' and code & 1:
192 if sys.platform == 'OpenVMS' and code & 1:
191 code = 0
193 code = 0
192 if code:
194 if code:
193 raise Abort(_("command '%s' failed: %s") %
195 raise Abort(_("command '%s' failed: %s") %
194 (cmd, explain_exit(code)))
196 (cmd, explain_exit(code)))
195 return open(outname, 'rb').read()
197 return open(outname, 'rb').read()
196 finally:
198 finally:
197 try:
199 try:
198 if inname:
200 if inname:
199 os.unlink(inname)
201 os.unlink(inname)
200 except:
202 except:
201 pass
203 pass
202 try:
204 try:
203 if outname:
205 if outname:
204 os.unlink(outname)
206 os.unlink(outname)
205 except:
207 except:
206 pass
208 pass
207
209
208 filtertable = {
210 filtertable = {
209 'tempfile:': tempfilter,
211 'tempfile:': tempfilter,
210 'pipe:': pipefilter,
212 'pipe:': pipefilter,
211 }
213 }
212
214
213 def filter(s, cmd):
215 def filter(s, cmd):
214 "filter a string through a command that transforms its input to its output"
216 "filter a string through a command that transforms its input to its output"
215 for name, fn in filtertable.iteritems():
217 for name, fn in filtertable.iteritems():
216 if cmd.startswith(name):
218 if cmd.startswith(name):
217 return fn(s, cmd[len(name):].lstrip())
219 return fn(s, cmd[len(name):].lstrip())
218 return pipefilter(s, cmd)
220 return pipefilter(s, cmd)
219
221
220 def binary(s):
222 def binary(s):
221 """return true if a string is binary data"""
223 """return true if a string is binary data"""
222 return bool(s and '\0' in s)
224 return bool(s and '\0' in s)
223
225
224 def increasingchunks(source, min=1024, max=65536):
226 def increasingchunks(source, min=1024, max=65536):
225 '''return no less than min bytes per chunk while data remains,
227 '''return no less than min bytes per chunk while data remains,
226 doubling min after each chunk until it reaches max'''
228 doubling min after each chunk until it reaches max'''
227 def log2(x):
229 def log2(x):
228 if not x:
230 if not x:
229 return 0
231 return 0
230 i = 0
232 i = 0
231 while x:
233 while x:
232 x >>= 1
234 x >>= 1
233 i += 1
235 i += 1
234 return i - 1
236 return i - 1
235
237
236 buf = []
238 buf = []
237 blen = 0
239 blen = 0
238 for chunk in source:
240 for chunk in source:
239 buf.append(chunk)
241 buf.append(chunk)
240 blen += len(chunk)
242 blen += len(chunk)
241 if blen >= min:
243 if blen >= min:
242 if min < max:
244 if min < max:
243 min = min << 1
245 min = min << 1
244 nmin = 1 << log2(blen)
246 nmin = 1 << log2(blen)
245 if nmin > min:
247 if nmin > min:
246 min = nmin
248 min = nmin
247 if min > max:
249 if min > max:
248 min = max
250 min = max
249 yield ''.join(buf)
251 yield ''.join(buf)
250 blen = 0
252 blen = 0
251 buf = []
253 buf = []
252 if buf:
254 if buf:
253 yield ''.join(buf)
255 yield ''.join(buf)
254
256
255 Abort = error.Abort
257 Abort = error.Abort
256
258
257 def always(fn):
259 def always(fn):
258 return True
260 return True
259
261
260 def never(fn):
262 def never(fn):
261 return False
263 return False
262
264
263 def pathto(root, n1, n2):
265 def pathto(root, n1, n2):
264 '''return the relative path from one place to another.
266 '''return the relative path from one place to another.
265 root should use os.sep to separate directories
267 root should use os.sep to separate directories
266 n1 should use os.sep to separate directories
268 n1 should use os.sep to separate directories
267 n2 should use "/" to separate directories
269 n2 should use "/" to separate directories
268 returns an os.sep-separated path.
270 returns an os.sep-separated path.
269
271
270 If n1 is a relative path, it's assumed it's
272 If n1 is a relative path, it's assumed it's
271 relative to root.
273 relative to root.
272 n2 should always be relative to root.
274 n2 should always be relative to root.
273 '''
275 '''
274 if not n1:
276 if not n1:
275 return localpath(n2)
277 return localpath(n2)
276 if os.path.isabs(n1):
278 if os.path.isabs(n1):
277 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
279 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
278 return os.path.join(root, localpath(n2))
280 return os.path.join(root, localpath(n2))
279 n2 = '/'.join((pconvert(root), n2))
281 n2 = '/'.join((pconvert(root), n2))
280 a, b = splitpath(n1), n2.split('/')
282 a, b = splitpath(n1), n2.split('/')
281 a.reverse()
283 a.reverse()
282 b.reverse()
284 b.reverse()
283 while a and b and a[-1] == b[-1]:
285 while a and b and a[-1] == b[-1]:
284 a.pop()
286 a.pop()
285 b.pop()
287 b.pop()
286 b.reverse()
288 b.reverse()
287 return os.sep.join((['..'] * len(a)) + b) or '.'
289 return os.sep.join((['..'] * len(a)) + b) or '.'
288
290
289 def canonpath(root, cwd, myname):
291 def canonpath(root, cwd, myname):
290 """return the canonical path of myname, given cwd and root"""
292 """return the canonical path of myname, given cwd and root"""
291 if endswithsep(root):
293 if endswithsep(root):
292 rootsep = root
294 rootsep = root
293 else:
295 else:
294 rootsep = root + os.sep
296 rootsep = root + os.sep
295 name = myname
297 name = myname
296 if not os.path.isabs(name):
298 if not os.path.isabs(name):
297 name = os.path.join(root, cwd, name)
299 name = os.path.join(root, cwd, name)
298 name = os.path.normpath(name)
300 name = os.path.normpath(name)
299 audit_path = path_auditor(root)
301 audit_path = path_auditor(root)
300 if name != rootsep and name.startswith(rootsep):
302 if name != rootsep and name.startswith(rootsep):
301 name = name[len(rootsep):]
303 name = name[len(rootsep):]
302 audit_path(name)
304 audit_path(name)
303 return pconvert(name)
305 return pconvert(name)
304 elif name == root:
306 elif name == root:
305 return ''
307 return ''
306 else:
308 else:
307 # Determine whether `name' is in the hierarchy at or beneath `root',
309 # Determine whether `name' is in the hierarchy at or beneath `root',
308 # by iterating name=dirname(name) until that causes no change (can't
310 # by iterating name=dirname(name) until that causes no change (can't
309 # check name == '/', because that doesn't work on windows). For each
311 # check name == '/', because that doesn't work on windows). For each
310 # `name', compare dev/inode numbers. If they match, the list `rel'
312 # `name', compare dev/inode numbers. If they match, the list `rel'
311 # holds the reversed list of components making up the relative file
313 # holds the reversed list of components making up the relative file
312 # name we want.
314 # name we want.
313 root_st = os.stat(root)
315 root_st = os.stat(root)
314 rel = []
316 rel = []
315 while True:
317 while True:
316 try:
318 try:
317 name_st = os.stat(name)
319 name_st = os.stat(name)
318 except OSError:
320 except OSError:
319 break
321 break
320 if samestat(name_st, root_st):
322 if samestat(name_st, root_st):
321 if not rel:
323 if not rel:
322 # name was actually the same as root (maybe a symlink)
324 # name was actually the same as root (maybe a symlink)
323 return ''
325 return ''
324 rel.reverse()
326 rel.reverse()
325 name = os.path.join(*rel)
327 name = os.path.join(*rel)
326 audit_path(name)
328 audit_path(name)
327 return pconvert(name)
329 return pconvert(name)
328 dirname, basename = os.path.split(name)
330 dirname, basename = os.path.split(name)
329 rel.append(basename)
331 rel.append(basename)
330 if dirname == name:
332 if dirname == name:
331 break
333 break
332 name = dirname
334 name = dirname
333
335
334 raise Abort('%s not under root' % myname)
336 raise Abort('%s not under root' % myname)
335
337
336 _hgexecutable = None
338 _hgexecutable = None
337
339
338 def main_is_frozen():
340 def main_is_frozen():
339 """return True if we are a frozen executable.
341 """return True if we are a frozen executable.
340
342
341 The code supports py2exe (most common, Windows only) and tools/freeze
343 The code supports py2exe (most common, Windows only) and tools/freeze
342 (portable, not much used).
344 (portable, not much used).
343 """
345 """
344 return (hasattr(sys, "frozen") or # new py2exe
346 return (hasattr(sys, "frozen") or # new py2exe
345 hasattr(sys, "importers") or # old py2exe
347 hasattr(sys, "importers") or # old py2exe
346 imp.is_frozen("__main__")) # tools/freeze
348 imp.is_frozen("__main__")) # tools/freeze
347
349
348 def hgexecutable():
350 def hgexecutable():
349 """return location of the 'hg' executable.
351 """return location of the 'hg' executable.
350
352
351 Defaults to $HG or 'hg' in the search path.
353 Defaults to $HG or 'hg' in the search path.
352 """
354 """
353 if _hgexecutable is None:
355 if _hgexecutable is None:
354 hg = os.environ.get('HG')
356 hg = os.environ.get('HG')
355 if hg:
357 if hg:
356 set_hgexecutable(hg)
358 set_hgexecutable(hg)
357 elif main_is_frozen():
359 elif main_is_frozen():
358 set_hgexecutable(sys.executable)
360 set_hgexecutable(sys.executable)
359 else:
361 else:
360 exe = find_exe('hg') or os.path.basename(sys.argv[0])
362 exe = find_exe('hg') or os.path.basename(sys.argv[0])
361 set_hgexecutable(exe)
363 set_hgexecutable(exe)
362 return _hgexecutable
364 return _hgexecutable
363
365
364 def set_hgexecutable(path):
366 def set_hgexecutable(path):
365 """set location of the 'hg' executable"""
367 """set location of the 'hg' executable"""
366 global _hgexecutable
368 global _hgexecutable
367 _hgexecutable = path
369 _hgexecutable = path
368
370
369 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
371 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
370 '''enhanced shell command execution.
372 '''enhanced shell command execution.
371 run with environment maybe modified, maybe in different dir.
373 run with environment maybe modified, maybe in different dir.
372
374
373 if command fails and onerr is None, return status. if ui object,
375 if command fails and onerr is None, return status. if ui object,
374 print error message and return status, else raise onerr object as
376 print error message and return status, else raise onerr object as
375 exception.
377 exception.
376
378
377 if out is specified, it is assumed to be a file-like object that has a
379 if out is specified, it is assumed to be a file-like object that has a
378 write() method. stdout and stderr will be redirected to out.'''
380 write() method. stdout and stderr will be redirected to out.'''
379 def py2shell(val):
381 def py2shell(val):
380 'convert python object into string that is useful to shell'
382 'convert python object into string that is useful to shell'
381 if val is None or val is False:
383 if val is None or val is False:
382 return '0'
384 return '0'
383 if val is True:
385 if val is True:
384 return '1'
386 return '1'
385 return str(val)
387 return str(val)
386 origcmd = cmd
388 origcmd = cmd
387 if os.name == 'nt':
389 if os.name == 'nt':
388 cmd = '"%s"' % cmd
390 cmd = '"%s"' % cmd
389 env = dict(os.environ)
391 env = dict(os.environ)
390 env.update((k, py2shell(v)) for k, v in environ.iteritems())
392 env.update((k, py2shell(v)) for k, v in environ.iteritems())
391 env['HG'] = hgexecutable()
393 env['HG'] = hgexecutable()
392 if out is None:
394 if out is None:
393 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
395 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
394 env=env, cwd=cwd)
396 env=env, cwd=cwd)
395 else:
397 else:
396 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
398 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
397 env=env, cwd=cwd, stdout=subprocess.PIPE,
399 env=env, cwd=cwd, stdout=subprocess.PIPE,
398 stderr=subprocess.STDOUT)
400 stderr=subprocess.STDOUT)
399 for line in proc.stdout:
401 for line in proc.stdout:
400 out.write(line)
402 out.write(line)
401 proc.wait()
403 proc.wait()
402 rc = proc.returncode
404 rc = proc.returncode
403 if sys.platform == 'OpenVMS' and rc & 1:
405 if sys.platform == 'OpenVMS' and rc & 1:
404 rc = 0
406 rc = 0
405 if rc and onerr:
407 if rc and onerr:
406 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
408 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
407 explain_exit(rc)[0])
409 explain_exit(rc)[0])
408 if errprefix:
410 if errprefix:
409 errmsg = '%s: %s' % (errprefix, errmsg)
411 errmsg = '%s: %s' % (errprefix, errmsg)
410 try:
412 try:
411 onerr.warn(errmsg + '\n')
413 onerr.warn(errmsg + '\n')
412 except AttributeError:
414 except AttributeError:
413 raise onerr(errmsg)
415 raise onerr(errmsg)
414 return rc
416 return rc
415
417
416 def checksignature(func):
418 def checksignature(func):
417 '''wrap a function with code to check for calling errors'''
419 '''wrap a function with code to check for calling errors'''
418 def check(*args, **kwargs):
420 def check(*args, **kwargs):
419 try:
421 try:
420 return func(*args, **kwargs)
422 return func(*args, **kwargs)
421 except TypeError:
423 except TypeError:
422 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
424 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
423 raise error.SignatureError
425 raise error.SignatureError
424 raise
426 raise
425
427
426 return check
428 return check
427
429
428 # os.path.lexists is not available on python2.3
430 # os.path.lexists is not available on python2.3
429 def lexists(filename):
431 def lexists(filename):
430 "test whether a file with this name exists. does not follow symlinks"
432 "test whether a file with this name exists. does not follow symlinks"
431 try:
433 try:
432 os.lstat(filename)
434 os.lstat(filename)
433 except:
435 except:
434 return False
436 return False
435 return True
437 return True
436
438
437 def unlink(f):
439 def unlink(f):
438 """unlink and remove the directory if it is empty"""
440 """unlink and remove the directory if it is empty"""
439 os.unlink(f)
441 os.unlink(f)
440 # try removing directories that might now be empty
442 # try removing directories that might now be empty
441 try:
443 try:
442 os.removedirs(os.path.dirname(f))
444 os.removedirs(os.path.dirname(f))
443 except OSError:
445 except OSError:
444 pass
446 pass
445
447
446 def copyfile(src, dest):
448 def copyfile(src, dest):
447 "copy a file, preserving mode and atime/mtime"
449 "copy a file, preserving mode and atime/mtime"
448 if os.path.islink(src):
450 if os.path.islink(src):
449 try:
451 try:
450 os.unlink(dest)
452 os.unlink(dest)
451 except:
453 except:
452 pass
454 pass
453 os.symlink(os.readlink(src), dest)
455 os.symlink(os.readlink(src), dest)
454 else:
456 else:
455 try:
457 try:
456 shutil.copyfile(src, dest)
458 shutil.copyfile(src, dest)
457 shutil.copystat(src, dest)
459 shutil.copystat(src, dest)
458 except shutil.Error, inst:
460 except shutil.Error, inst:
459 raise Abort(str(inst))
461 raise Abort(str(inst))
460
462
461 def copyfiles(src, dst, hardlink=None):
463 def copyfiles(src, dst, hardlink=None):
462 """Copy a directory tree using hardlinks if possible"""
464 """Copy a directory tree using hardlinks if possible"""
463
465
464 if hardlink is None:
466 if hardlink is None:
465 hardlink = (os.stat(src).st_dev ==
467 hardlink = (os.stat(src).st_dev ==
466 os.stat(os.path.dirname(dst)).st_dev)
468 os.stat(os.path.dirname(dst)).st_dev)
467
469
468 num = 0
470 num = 0
469 if os.path.isdir(src):
471 if os.path.isdir(src):
470 os.mkdir(dst)
472 os.mkdir(dst)
471 for name, kind in osutil.listdir(src):
473 for name, kind in osutil.listdir(src):
472 srcname = os.path.join(src, name)
474 srcname = os.path.join(src, name)
473 dstname = os.path.join(dst, name)
475 dstname = os.path.join(dst, name)
474 hardlink, n = copyfiles(srcname, dstname, hardlink)
476 hardlink, n = copyfiles(srcname, dstname, hardlink)
475 num += n
477 num += n
476 else:
478 else:
477 if hardlink:
479 if hardlink:
478 try:
480 try:
479 os_link(src, dst)
481 os_link(src, dst)
480 except (IOError, OSError):
482 except (IOError, OSError):
481 hardlink = False
483 hardlink = False
482 shutil.copy(src, dst)
484 shutil.copy(src, dst)
483 else:
485 else:
484 shutil.copy(src, dst)
486 shutil.copy(src, dst)
485 num += 1
487 num += 1
486
488
487 return hardlink, num
489 return hardlink, num
488
490
489 class path_auditor(object):
491 class path_auditor(object):
490 '''ensure that a filesystem path contains no banned components.
492 '''ensure that a filesystem path contains no banned components.
491 the following properties of a path are checked:
493 the following properties of a path are checked:
492
494
493 - under top-level .hg
495 - under top-level .hg
494 - starts at the root of a windows drive
496 - starts at the root of a windows drive
495 - contains ".."
497 - contains ".."
496 - traverses a symlink (e.g. a/symlink_here/b)
498 - traverses a symlink (e.g. a/symlink_here/b)
497 - inside a nested repository'''
499 - inside a nested repository'''
498
500
499 def __init__(self, root):
501 def __init__(self, root):
500 self.audited = set()
502 self.audited = set()
501 self.auditeddir = set()
503 self.auditeddir = set()
502 self.root = root
504 self.root = root
503
505
504 def __call__(self, path):
506 def __call__(self, path):
505 if path in self.audited:
507 if path in self.audited:
506 return
508 return
507 normpath = os.path.normcase(path)
509 normpath = os.path.normcase(path)
508 parts = splitpath(normpath)
510 parts = splitpath(normpath)
509 if (os.path.splitdrive(path)[0]
511 if (os.path.splitdrive(path)[0]
510 or parts[0].lower() in ('.hg', '.hg.', '')
512 or parts[0].lower() in ('.hg', '.hg.', '')
511 or os.pardir in parts):
513 or os.pardir in parts):
512 raise Abort(_("path contains illegal component: %s") % path)
514 raise Abort(_("path contains illegal component: %s") % path)
513 if '.hg' in path.lower():
515 if '.hg' in path.lower():
514 lparts = [p.lower() for p in parts]
516 lparts = [p.lower() for p in parts]
515 for p in '.hg', '.hg.':
517 for p in '.hg', '.hg.':
516 if p in lparts[1:]:
518 if p in lparts[1:]:
517 pos = lparts.index(p)
519 pos = lparts.index(p)
518 base = os.path.join(*parts[:pos])
520 base = os.path.join(*parts[:pos])
519 raise Abort(_('path %r is inside repo %r') % (path, base))
521 raise Abort(_('path %r is inside repo %r') % (path, base))
520 def check(prefix):
522 def check(prefix):
521 curpath = os.path.join(self.root, prefix)
523 curpath = os.path.join(self.root, prefix)
522 try:
524 try:
523 st = os.lstat(curpath)
525 st = os.lstat(curpath)
524 except OSError, err:
526 except OSError, err:
525 # EINVAL can be raised as invalid path syntax under win32.
527 # EINVAL can be raised as invalid path syntax under win32.
526 # They must be ignored for patterns can be checked too.
528 # They must be ignored for patterns can be checked too.
527 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
529 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
528 raise
530 raise
529 else:
531 else:
530 if stat.S_ISLNK(st.st_mode):
532 if stat.S_ISLNK(st.st_mode):
531 raise Abort(_('path %r traverses symbolic link %r') %
533 raise Abort(_('path %r traverses symbolic link %r') %
532 (path, prefix))
534 (path, prefix))
533 elif (stat.S_ISDIR(st.st_mode) and
535 elif (stat.S_ISDIR(st.st_mode) and
534 os.path.isdir(os.path.join(curpath, '.hg'))):
536 os.path.isdir(os.path.join(curpath, '.hg'))):
535 raise Abort(_('path %r is inside repo %r') %
537 raise Abort(_('path %r is inside repo %r') %
536 (path, prefix))
538 (path, prefix))
537 parts.pop()
539 parts.pop()
538 prefixes = []
540 prefixes = []
539 while parts:
541 while parts:
540 prefix = os.sep.join(parts)
542 prefix = os.sep.join(parts)
541 if prefix in self.auditeddir:
543 if prefix in self.auditeddir:
542 break
544 break
543 check(prefix)
545 check(prefix)
544 prefixes.append(prefix)
546 prefixes.append(prefix)
545 parts.pop()
547 parts.pop()
546
548
547 self.audited.add(path)
549 self.audited.add(path)
548 # only add prefixes to the cache after checking everything: we don't
550 # only add prefixes to the cache after checking everything: we don't
549 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
551 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
550 self.auditeddir.update(prefixes)
552 self.auditeddir.update(prefixes)
551
553
552 def nlinks(pathname):
554 def nlinks(pathname):
553 """Return number of hardlinks for the given file."""
555 """Return number of hardlinks for the given file."""
554 return os.lstat(pathname).st_nlink
556 return os.lstat(pathname).st_nlink
555
557
556 if hasattr(os, 'link'):
558 if hasattr(os, 'link'):
557 os_link = os.link
559 os_link = os.link
558 else:
560 else:
559 def os_link(src, dst):
561 def os_link(src, dst):
560 raise OSError(0, _("Hardlinks not supported"))
562 raise OSError(0, _("Hardlinks not supported"))
561
563
562 def lookup_reg(key, name=None, scope=None):
564 def lookup_reg(key, name=None, scope=None):
563 return None
565 return None
564
566
565 def hidewindow():
567 def hidewindow():
566 """Hide current shell window.
568 """Hide current shell window.
567
569
568 Used to hide the window opened when starting asynchronous
570 Used to hide the window opened when starting asynchronous
569 child process under Windows, unneeded on other systems.
571 child process under Windows, unneeded on other systems.
570 """
572 """
571 pass
573 pass
572
574
573 if os.name == 'nt':
575 if os.name == 'nt':
574 from windows import *
576 from windows import *
575 else:
577 else:
576 from posix import *
578 from posix import *
577
579
578 def makelock(info, pathname):
580 def makelock(info, pathname):
579 try:
581 try:
580 return os.symlink(info, pathname)
582 return os.symlink(info, pathname)
581 except OSError, why:
583 except OSError, why:
582 if why.errno == errno.EEXIST:
584 if why.errno == errno.EEXIST:
583 raise
585 raise
584 except AttributeError: # no symlink in os
586 except AttributeError: # no symlink in os
585 pass
587 pass
586
588
587 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
589 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
588 os.write(ld, info)
590 os.write(ld, info)
589 os.close(ld)
591 os.close(ld)
590
592
591 def readlock(pathname):
593 def readlock(pathname):
592 try:
594 try:
593 return os.readlink(pathname)
595 return os.readlink(pathname)
594 except OSError, why:
596 except OSError, why:
595 if why.errno not in (errno.EINVAL, errno.ENOSYS):
597 if why.errno not in (errno.EINVAL, errno.ENOSYS):
596 raise
598 raise
597 except AttributeError: # no symlink in os
599 except AttributeError: # no symlink in os
598 pass
600 pass
599 return posixfile(pathname).read()
601 return posixfile(pathname).read()
600
602
601 def fstat(fp):
603 def fstat(fp):
602 '''stat file object that may not have fileno method.'''
604 '''stat file object that may not have fileno method.'''
603 try:
605 try:
604 return os.fstat(fp.fileno())
606 return os.fstat(fp.fileno())
605 except AttributeError:
607 except AttributeError:
606 return os.stat(fp.name)
608 return os.stat(fp.name)
607
609
608 # File system features
610 # File system features
609
611
610 def checkcase(path):
612 def checkcase(path):
611 """
613 """
612 Check whether the given path is on a case-sensitive filesystem
614 Check whether the given path is on a case-sensitive filesystem
613
615
614 Requires a path (like /foo/.hg) ending with a foldable final
616 Requires a path (like /foo/.hg) ending with a foldable final
615 directory component.
617 directory component.
616 """
618 """
617 s1 = os.stat(path)
619 s1 = os.stat(path)
618 d, b = os.path.split(path)
620 d, b = os.path.split(path)
619 p2 = os.path.join(d, b.upper())
621 p2 = os.path.join(d, b.upper())
620 if path == p2:
622 if path == p2:
621 p2 = os.path.join(d, b.lower())
623 p2 = os.path.join(d, b.lower())
622 try:
624 try:
623 s2 = os.stat(p2)
625 s2 = os.stat(p2)
624 if s2 == s1:
626 if s2 == s1:
625 return False
627 return False
626 return True
628 return True
627 except:
629 except:
628 return True
630 return True
629
631
630 _fspathcache = {}
632 _fspathcache = {}
631 def fspath(name, root):
633 def fspath(name, root):
632 '''Get name in the case stored in the filesystem
634 '''Get name in the case stored in the filesystem
633
635
634 The name is either relative to root, or it is an absolute path starting
636 The name is either relative to root, or it is an absolute path starting
635 with root. Note that this function is unnecessary, and should not be
637 with root. Note that this function is unnecessary, and should not be
636 called, for case-sensitive filesystems (simply because it's expensive).
638 called, for case-sensitive filesystems (simply because it's expensive).
637 '''
639 '''
638 # If name is absolute, make it relative
640 # If name is absolute, make it relative
639 if name.lower().startswith(root.lower()):
641 if name.lower().startswith(root.lower()):
640 l = len(root)
642 l = len(root)
641 if name[l] == os.sep or name[l] == os.altsep:
643 if name[l] == os.sep or name[l] == os.altsep:
642 l = l + 1
644 l = l + 1
643 name = name[l:]
645 name = name[l:]
644
646
645 if not os.path.exists(os.path.join(root, name)):
647 if not os.path.exists(os.path.join(root, name)):
646 return None
648 return None
647
649
648 seps = os.sep
650 seps = os.sep
649 if os.altsep:
651 if os.altsep:
650 seps = seps + os.altsep
652 seps = seps + os.altsep
651 # Protect backslashes. This gets silly very quickly.
653 # Protect backslashes. This gets silly very quickly.
652 seps.replace('\\','\\\\')
654 seps.replace('\\','\\\\')
653 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
655 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
654 dir = os.path.normcase(os.path.normpath(root))
656 dir = os.path.normcase(os.path.normpath(root))
655 result = []
657 result = []
656 for part, sep in pattern.findall(name):
658 for part, sep in pattern.findall(name):
657 if sep:
659 if sep:
658 result.append(sep)
660 result.append(sep)
659 continue
661 continue
660
662
661 if dir not in _fspathcache:
663 if dir not in _fspathcache:
662 _fspathcache[dir] = os.listdir(dir)
664 _fspathcache[dir] = os.listdir(dir)
663 contents = _fspathcache[dir]
665 contents = _fspathcache[dir]
664
666
665 lpart = part.lower()
667 lpart = part.lower()
666 lenp = len(part)
668 lenp = len(part)
667 for n in contents:
669 for n in contents:
668 if lenp == len(n) and n.lower() == lpart:
670 if lenp == len(n) and n.lower() == lpart:
669 result.append(n)
671 result.append(n)
670 break
672 break
671 else:
673 else:
672 # Cannot happen, as the file exists!
674 # Cannot happen, as the file exists!
673 result.append(part)
675 result.append(part)
674 dir = os.path.join(dir, lpart)
676 dir = os.path.join(dir, lpart)
675
677
676 return ''.join(result)
678 return ''.join(result)
677
679
678 def checkexec(path):
680 def checkexec(path):
679 """
681 """
680 Check whether the given path is on a filesystem with UNIX-like exec flags
682 Check whether the given path is on a filesystem with UNIX-like exec flags
681
683
682 Requires a directory (like /foo/.hg)
684 Requires a directory (like /foo/.hg)
683 """
685 """
684
686
685 # VFAT on some Linux versions can flip mode but it doesn't persist
687 # VFAT on some Linux versions can flip mode but it doesn't persist
686 # a FS remount. Frequently we can detect it if files are created
688 # a FS remount. Frequently we can detect it if files are created
687 # with exec bit on.
689 # with exec bit on.
688
690
689 try:
691 try:
690 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
692 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
691 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
693 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
692 try:
694 try:
693 os.close(fh)
695 os.close(fh)
694 m = os.stat(fn).st_mode & 0777
696 m = os.stat(fn).st_mode & 0777
695 new_file_has_exec = m & EXECFLAGS
697 new_file_has_exec = m & EXECFLAGS
696 os.chmod(fn, m ^ EXECFLAGS)
698 os.chmod(fn, m ^ EXECFLAGS)
697 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
699 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
698 finally:
700 finally:
699 os.unlink(fn)
701 os.unlink(fn)
700 except (IOError, OSError):
702 except (IOError, OSError):
701 # we don't care, the user probably won't be able to commit anyway
703 # we don't care, the user probably won't be able to commit anyway
702 return False
704 return False
703 return not (new_file_has_exec or exec_flags_cannot_flip)
705 return not (new_file_has_exec or exec_flags_cannot_flip)
704
706
705 def checklink(path):
707 def checklink(path):
706 """check whether the given path is on a symlink-capable filesystem"""
708 """check whether the given path is on a symlink-capable filesystem"""
707 # mktemp is not racy because symlink creation will fail if the
709 # mktemp is not racy because symlink creation will fail if the
708 # file already exists
710 # file already exists
709 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
711 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
710 try:
712 try:
711 os.symlink(".", name)
713 os.symlink(".", name)
712 os.unlink(name)
714 os.unlink(name)
713 return True
715 return True
714 except (OSError, AttributeError):
716 except (OSError, AttributeError):
715 return False
717 return False
716
718
717 def needbinarypatch():
719 def needbinarypatch():
718 """return True if patches should be applied in binary mode by default."""
720 """return True if patches should be applied in binary mode by default."""
719 return os.name == 'nt'
721 return os.name == 'nt'
720
722
721 def endswithsep(path):
723 def endswithsep(path):
722 '''Check path ends with os.sep or os.altsep.'''
724 '''Check path ends with os.sep or os.altsep.'''
723 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
725 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
724
726
725 def splitpath(path):
727 def splitpath(path):
726 '''Split path by os.sep.
728 '''Split path by os.sep.
727 Note that this function does not use os.altsep because this is
729 Note that this function does not use os.altsep because this is
728 an alternative of simple "xxx.split(os.sep)".
730 an alternative of simple "xxx.split(os.sep)".
729 It is recommended to use os.path.normpath() before using this
731 It is recommended to use os.path.normpath() before using this
730 function if need.'''
732 function if need.'''
731 return path.split(os.sep)
733 return path.split(os.sep)
732
734
733 def gui():
735 def gui():
734 '''Are we running in a GUI?'''
736 '''Are we running in a GUI?'''
735 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
737 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
736
738
737 def mktempcopy(name, emptyok=False, createmode=None):
739 def mktempcopy(name, emptyok=False, createmode=None):
738 """Create a temporary file with the same contents from name
740 """Create a temporary file with the same contents from name
739
741
740 The permission bits are copied from the original file.
742 The permission bits are copied from the original file.
741
743
742 If the temporary file is going to be truncated immediately, you
744 If the temporary file is going to be truncated immediately, you
743 can use emptyok=True as an optimization.
745 can use emptyok=True as an optimization.
744
746
745 Returns the name of the temporary file.
747 Returns the name of the temporary file.
746 """
748 """
747 d, fn = os.path.split(name)
749 d, fn = os.path.split(name)
748 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
750 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
749 os.close(fd)
751 os.close(fd)
750 # Temporary files are created with mode 0600, which is usually not
752 # Temporary files are created with mode 0600, which is usually not
751 # what we want. If the original file already exists, just copy
753 # what we want. If the original file already exists, just copy
752 # its mode. Otherwise, manually obey umask.
754 # its mode. Otherwise, manually obey umask.
753 try:
755 try:
754 st_mode = os.lstat(name).st_mode & 0777
756 st_mode = os.lstat(name).st_mode & 0777
755 except OSError, inst:
757 except OSError, inst:
756 if inst.errno != errno.ENOENT:
758 if inst.errno != errno.ENOENT:
757 raise
759 raise
758 st_mode = createmode
760 st_mode = createmode
759 if st_mode is None:
761 if st_mode is None:
760 st_mode = ~umask
762 st_mode = ~umask
761 st_mode &= 0666
763 st_mode &= 0666
762 os.chmod(temp, st_mode)
764 os.chmod(temp, st_mode)
763 if emptyok:
765 if emptyok:
764 return temp
766 return temp
765 try:
767 try:
766 try:
768 try:
767 ifp = posixfile(name, "rb")
769 ifp = posixfile(name, "rb")
768 except IOError, inst:
770 except IOError, inst:
769 if inst.errno == errno.ENOENT:
771 if inst.errno == errno.ENOENT:
770 return temp
772 return temp
771 if not getattr(inst, 'filename', None):
773 if not getattr(inst, 'filename', None):
772 inst.filename = name
774 inst.filename = name
773 raise
775 raise
774 ofp = posixfile(temp, "wb")
776 ofp = posixfile(temp, "wb")
775 for chunk in filechunkiter(ifp):
777 for chunk in filechunkiter(ifp):
776 ofp.write(chunk)
778 ofp.write(chunk)
777 ifp.close()
779 ifp.close()
778 ofp.close()
780 ofp.close()
779 except:
781 except:
780 try: os.unlink(temp)
782 try: os.unlink(temp)
781 except: pass
783 except: pass
782 raise
784 raise
783 return temp
785 return temp
784
786
785 class atomictempfile(object):
787 class atomictempfile(object):
786 """file-like object that atomically updates a file
788 """file-like object that atomically updates a file
787
789
788 All writes will be redirected to a temporary copy of the original
790 All writes will be redirected to a temporary copy of the original
789 file. When rename is called, the copy is renamed to the original
791 file. When rename is called, the copy is renamed to the original
790 name, making the changes visible.
792 name, making the changes visible.
791 """
793 """
792 def __init__(self, name, mode='w+b', createmode=None):
794 def __init__(self, name, mode='w+b', createmode=None):
793 self.__name = name
795 self.__name = name
794 self._fp = None
796 self._fp = None
795 self.temp = mktempcopy(name, emptyok=('w' in mode),
797 self.temp = mktempcopy(name, emptyok=('w' in mode),
796 createmode=createmode)
798 createmode=createmode)
797 self._fp = posixfile(self.temp, mode)
799 self._fp = posixfile(self.temp, mode)
798
800
799 def __getattr__(self, name):
801 def __getattr__(self, name):
800 return getattr(self._fp, name)
802 return getattr(self._fp, name)
801
803
802 def rename(self):
804 def rename(self):
803 if not self._fp.closed:
805 if not self._fp.closed:
804 self._fp.close()
806 self._fp.close()
805 rename(self.temp, localpath(self.__name))
807 rename(self.temp, localpath(self.__name))
806
808
807 def __del__(self):
809 def __del__(self):
808 if not self._fp:
810 if not self._fp:
809 return
811 return
810 if not self._fp.closed:
812 if not self._fp.closed:
811 try:
813 try:
812 os.unlink(self.temp)
814 os.unlink(self.temp)
813 except: pass
815 except: pass
814 self._fp.close()
816 self._fp.close()
815
817
816 def makedirs(name, mode=None):
818 def makedirs(name, mode=None):
817 """recursive directory creation with parent mode inheritance"""
819 """recursive directory creation with parent mode inheritance"""
818 try:
820 try:
819 os.mkdir(name)
821 os.mkdir(name)
820 if mode is not None:
822 if mode is not None:
821 os.chmod(name, mode)
823 os.chmod(name, mode)
822 return
824 return
823 except OSError, err:
825 except OSError, err:
824 if err.errno == errno.EEXIST:
826 if err.errno == errno.EEXIST:
825 return
827 return
826 if err.errno != errno.ENOENT:
828 if err.errno != errno.ENOENT:
827 raise
829 raise
828 parent = os.path.abspath(os.path.dirname(name))
830 parent = os.path.abspath(os.path.dirname(name))
829 makedirs(parent, mode)
831 makedirs(parent, mode)
830 makedirs(name, mode)
832 makedirs(name, mode)
831
833
832 class opener(object):
834 class opener(object):
833 """Open files relative to a base directory
835 """Open files relative to a base directory
834
836
835 This class is used to hide the details of COW semantics and
837 This class is used to hide the details of COW semantics and
836 remote file access from higher level code.
838 remote file access from higher level code.
837 """
839 """
838 def __init__(self, base, audit=True):
840 def __init__(self, base, audit=True):
839 self.base = base
841 self.base = base
840 if audit:
842 if audit:
841 self.audit_path = path_auditor(base)
843 self.audit_path = path_auditor(base)
842 else:
844 else:
843 self.audit_path = always
845 self.audit_path = always
844 self.createmode = None
846 self.createmode = None
845
847
846 @propertycache
848 @propertycache
847 def _can_symlink(self):
849 def _can_symlink(self):
848 return checklink(self.base)
850 return checklink(self.base)
849
851
850 def _fixfilemode(self, name):
852 def _fixfilemode(self, name):
851 if self.createmode is None:
853 if self.createmode is None:
852 return
854 return
853 os.chmod(name, self.createmode & 0666)
855 os.chmod(name, self.createmode & 0666)
854
856
855 def __call__(self, path, mode="r", text=False, atomictemp=False):
857 def __call__(self, path, mode="r", text=False, atomictemp=False):
856 self.audit_path(path)
858 self.audit_path(path)
857 f = os.path.join(self.base, path)
859 f = os.path.join(self.base, path)
858
860
859 if not text and "b" not in mode:
861 if not text and "b" not in mode:
860 mode += "b" # for that other OS
862 mode += "b" # for that other OS
861
863
862 nlink = -1
864 nlink = -1
863 if mode not in ("r", "rb"):
865 if mode not in ("r", "rb"):
864 try:
866 try:
865 nlink = nlinks(f)
867 nlink = nlinks(f)
866 except OSError:
868 except OSError:
867 nlink = 0
869 nlink = 0
868 d = os.path.dirname(f)
870 d = os.path.dirname(f)
869 if not os.path.isdir(d):
871 if not os.path.isdir(d):
870 makedirs(d, self.createmode)
872 makedirs(d, self.createmode)
871 if atomictemp:
873 if atomictemp:
872 return atomictempfile(f, mode, self.createmode)
874 return atomictempfile(f, mode, self.createmode)
873 if nlink > 1:
875 if nlink > 1:
874 rename(mktempcopy(f), f)
876 rename(mktempcopy(f), f)
875 fp = posixfile(f, mode)
877 fp = posixfile(f, mode)
876 if nlink == 0:
878 if nlink == 0:
877 self._fixfilemode(f)
879 self._fixfilemode(f)
878 return fp
880 return fp
879
881
880 def symlink(self, src, dst):
882 def symlink(self, src, dst):
881 self.audit_path(dst)
883 self.audit_path(dst)
882 linkname = os.path.join(self.base, dst)
884 linkname = os.path.join(self.base, dst)
883 try:
885 try:
884 os.unlink(linkname)
886 os.unlink(linkname)
885 except OSError:
887 except OSError:
886 pass
888 pass
887
889
888 dirname = os.path.dirname(linkname)
890 dirname = os.path.dirname(linkname)
889 if not os.path.exists(dirname):
891 if not os.path.exists(dirname):
890 makedirs(dirname, self.createmode)
892 makedirs(dirname, self.createmode)
891
893
892 if self._can_symlink:
894 if self._can_symlink:
893 try:
895 try:
894 os.symlink(src, linkname)
896 os.symlink(src, linkname)
895 except OSError, err:
897 except OSError, err:
896 raise OSError(err.errno, _('could not symlink to %r: %s') %
898 raise OSError(err.errno, _('could not symlink to %r: %s') %
897 (src, err.strerror), linkname)
899 (src, err.strerror), linkname)
898 else:
900 else:
899 f = self(dst, "w")
901 f = self(dst, "w")
900 f.write(src)
902 f.write(src)
901 f.close()
903 f.close()
902 self._fixfilemode(dst)
904 self._fixfilemode(dst)
903
905
904 class chunkbuffer(object):
906 class chunkbuffer(object):
905 """Allow arbitrary sized chunks of data to be efficiently read from an
907 """Allow arbitrary sized chunks of data to be efficiently read from an
906 iterator over chunks of arbitrary size."""
908 iterator over chunks of arbitrary size."""
907
909
908 def __init__(self, in_iter):
910 def __init__(self, in_iter):
909 """in_iter is the iterator that's iterating over the input chunks.
911 """in_iter is the iterator that's iterating over the input chunks.
910 targetsize is how big a buffer to try to maintain."""
912 targetsize is how big a buffer to try to maintain."""
911 self.iter = iter(in_iter)
913 self.iter = iter(in_iter)
912 self.buf = ''
914 self.buf = ''
913 self.targetsize = 2**16
915 self.targetsize = 2**16
914
916
915 def read(self, l):
917 def read(self, l):
916 """Read L bytes of data from the iterator of chunks of data.
918 """Read L bytes of data from the iterator of chunks of data.
917 Returns less than L bytes if the iterator runs dry."""
919 Returns less than L bytes if the iterator runs dry."""
918 if l > len(self.buf) and self.iter:
920 if l > len(self.buf) and self.iter:
919 # Clamp to a multiple of self.targetsize
921 # Clamp to a multiple of self.targetsize
920 targetsize = max(l, self.targetsize)
922 targetsize = max(l, self.targetsize)
921 collector = cStringIO.StringIO()
923 collector = cStringIO.StringIO()
922 collector.write(self.buf)
924 collector.write(self.buf)
923 collected = len(self.buf)
925 collected = len(self.buf)
924 for chunk in self.iter:
926 for chunk in self.iter:
925 collector.write(chunk)
927 collector.write(chunk)
926 collected += len(chunk)
928 collected += len(chunk)
927 if collected >= targetsize:
929 if collected >= targetsize:
928 break
930 break
929 if collected < targetsize:
931 if collected < targetsize:
930 self.iter = False
932 self.iter = False
931 self.buf = collector.getvalue()
933 self.buf = collector.getvalue()
932 if len(self.buf) == l:
934 if len(self.buf) == l:
933 s, self.buf = str(self.buf), ''
935 s, self.buf = str(self.buf), ''
934 else:
936 else:
935 s, self.buf = self.buf[:l], buffer(self.buf, l)
937 s, self.buf = self.buf[:l], buffer(self.buf, l)
936 return s
938 return s
937
939
938 def filechunkiter(f, size=65536, limit=None):
940 def filechunkiter(f, size=65536, limit=None):
939 """Create a generator that produces the data in the file size
941 """Create a generator that produces the data in the file size
940 (default 65536) bytes at a time, up to optional limit (default is
942 (default 65536) bytes at a time, up to optional limit (default is
941 to read all data). Chunks may be less than size bytes if the
943 to read all data). Chunks may be less than size bytes if the
942 chunk is the last chunk in the file, or the file is a socket or
944 chunk is the last chunk in the file, or the file is a socket or
943 some other type of file that sometimes reads less data than is
945 some other type of file that sometimes reads less data than is
944 requested."""
946 requested."""
945 assert size >= 0
947 assert size >= 0
946 assert limit is None or limit >= 0
948 assert limit is None or limit >= 0
947 while True:
949 while True:
948 if limit is None:
950 if limit is None:
949 nbytes = size
951 nbytes = size
950 else:
952 else:
951 nbytes = min(limit, size)
953 nbytes = min(limit, size)
952 s = nbytes and f.read(nbytes)
954 s = nbytes and f.read(nbytes)
953 if not s:
955 if not s:
954 break
956 break
955 if limit:
957 if limit:
956 limit -= len(s)
958 limit -= len(s)
957 yield s
959 yield s
958
960
959 def makedate():
961 def makedate():
960 lt = time.localtime()
962 lt = time.localtime()
961 if lt[8] == 1 and time.daylight:
963 if lt[8] == 1 and time.daylight:
962 tz = time.altzone
964 tz = time.altzone
963 else:
965 else:
964 tz = time.timezone
966 tz = time.timezone
965 return time.mktime(lt), tz
967 return time.mktime(lt), tz
966
968
967 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
969 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
968 """represent a (unixtime, offset) tuple as a localized time.
970 """represent a (unixtime, offset) tuple as a localized time.
969 unixtime is seconds since the epoch, and offset is the time zone's
971 unixtime is seconds since the epoch, and offset is the time zone's
970 number of seconds away from UTC. if timezone is false, do not
972 number of seconds away from UTC. if timezone is false, do not
971 append time zone to string."""
973 append time zone to string."""
972 t, tz = date or makedate()
974 t, tz = date or makedate()
973 if "%1" in format or "%2" in format:
975 if "%1" in format or "%2" in format:
974 sign = (tz > 0) and "-" or "+"
976 sign = (tz > 0) and "-" or "+"
975 minutes = abs(tz) // 60
977 minutes = abs(tz) // 60
976 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
978 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
977 format = format.replace("%2", "%02d" % (minutes % 60))
979 format = format.replace("%2", "%02d" % (minutes % 60))
978 s = time.strftime(format, time.gmtime(float(t) - tz))
980 s = time.strftime(format, time.gmtime(float(t) - tz))
979 return s
981 return s
980
982
981 def shortdate(date=None):
983 def shortdate(date=None):
982 """turn (timestamp, tzoff) tuple into iso 8631 date."""
984 """turn (timestamp, tzoff) tuple into iso 8631 date."""
983 return datestr(date, format='%Y-%m-%d')
985 return datestr(date, format='%Y-%m-%d')
984
986
985 def strdate(string, format, defaults=[]):
987 def strdate(string, format, defaults=[]):
986 """parse a localized time string and return a (unixtime, offset) tuple.
988 """parse a localized time string and return a (unixtime, offset) tuple.
987 if the string cannot be parsed, ValueError is raised."""
989 if the string cannot be parsed, ValueError is raised."""
988 def timezone(string):
990 def timezone(string):
989 tz = string.split()[-1]
991 tz = string.split()[-1]
990 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
992 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
991 sign = (tz[0] == "+") and 1 or -1
993 sign = (tz[0] == "+") and 1 or -1
992 hours = int(tz[1:3])
994 hours = int(tz[1:3])
993 minutes = int(tz[3:5])
995 minutes = int(tz[3:5])
994 return -sign * (hours * 60 + minutes) * 60
996 return -sign * (hours * 60 + minutes) * 60
995 if tz == "GMT" or tz == "UTC":
997 if tz == "GMT" or tz == "UTC":
996 return 0
998 return 0
997 return None
999 return None
998
1000
999 # NOTE: unixtime = localunixtime + offset
1001 # NOTE: unixtime = localunixtime + offset
1000 offset, date = timezone(string), string
1002 offset, date = timezone(string), string
1001 if offset != None:
1003 if offset != None:
1002 date = " ".join(string.split()[:-1])
1004 date = " ".join(string.split()[:-1])
1003
1005
1004 # add missing elements from defaults
1006 # add missing elements from defaults
1005 for part in defaults:
1007 for part in defaults:
1006 found = [True for p in part if ("%"+p) in format]
1008 found = [True for p in part if ("%"+p) in format]
1007 if not found:
1009 if not found:
1008 date += "@" + defaults[part]
1010 date += "@" + defaults[part]
1009 format += "@%" + part[0]
1011 format += "@%" + part[0]
1010
1012
1011 timetuple = time.strptime(date, format)
1013 timetuple = time.strptime(date, format)
1012 localunixtime = int(calendar.timegm(timetuple))
1014 localunixtime = int(calendar.timegm(timetuple))
1013 if offset is None:
1015 if offset is None:
1014 # local timezone
1016 # local timezone
1015 unixtime = int(time.mktime(timetuple))
1017 unixtime = int(time.mktime(timetuple))
1016 offset = unixtime - localunixtime
1018 offset = unixtime - localunixtime
1017 else:
1019 else:
1018 unixtime = localunixtime + offset
1020 unixtime = localunixtime + offset
1019 return unixtime, offset
1021 return unixtime, offset
1020
1022
1021 def parsedate(date, formats=None, defaults=None):
1023 def parsedate(date, formats=None, defaults=None):
1022 """parse a localized date/time string and return a (unixtime, offset) tuple.
1024 """parse a localized date/time string and return a (unixtime, offset) tuple.
1023
1025
1024 The date may be a "unixtime offset" string or in one of the specified
1026 The date may be a "unixtime offset" string or in one of the specified
1025 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1027 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1026 """
1028 """
1027 if not date:
1029 if not date:
1028 return 0, 0
1030 return 0, 0
1029 if isinstance(date, tuple) and len(date) == 2:
1031 if isinstance(date, tuple) and len(date) == 2:
1030 return date
1032 return date
1031 if not formats:
1033 if not formats:
1032 formats = defaultdateformats
1034 formats = defaultdateformats
1033 date = date.strip()
1035 date = date.strip()
1034 try:
1036 try:
1035 when, offset = map(int, date.split(' '))
1037 when, offset = map(int, date.split(' '))
1036 except ValueError:
1038 except ValueError:
1037 # fill out defaults
1039 # fill out defaults
1038 if not defaults:
1040 if not defaults:
1039 defaults = {}
1041 defaults = {}
1040 now = makedate()
1042 now = makedate()
1041 for part in "d mb yY HI M S".split():
1043 for part in "d mb yY HI M S".split():
1042 if part not in defaults:
1044 if part not in defaults:
1043 if part[0] in "HMS":
1045 if part[0] in "HMS":
1044 defaults[part] = "00"
1046 defaults[part] = "00"
1045 else:
1047 else:
1046 defaults[part] = datestr(now, "%" + part[0])
1048 defaults[part] = datestr(now, "%" + part[0])
1047
1049
1048 for format in formats:
1050 for format in formats:
1049 try:
1051 try:
1050 when, offset = strdate(date, format, defaults)
1052 when, offset = strdate(date, format, defaults)
1051 except (ValueError, OverflowError):
1053 except (ValueError, OverflowError):
1052 pass
1054 pass
1053 else:
1055 else:
1054 break
1056 break
1055 else:
1057 else:
1056 raise Abort(_('invalid date: %r ') % date)
1058 raise Abort(_('invalid date: %r ') % date)
1057 # validate explicit (probably user-specified) date and
1059 # validate explicit (probably user-specified) date and
1058 # time zone offset. values must fit in signed 32 bits for
1060 # time zone offset. values must fit in signed 32 bits for
1059 # current 32-bit linux runtimes. timezones go from UTC-12
1061 # current 32-bit linux runtimes. timezones go from UTC-12
1060 # to UTC+14
1062 # to UTC+14
1061 if abs(when) > 0x7fffffff:
1063 if abs(when) > 0x7fffffff:
1062 raise Abort(_('date exceeds 32 bits: %d') % when)
1064 raise Abort(_('date exceeds 32 bits: %d') % when)
1063 if offset < -50400 or offset > 43200:
1065 if offset < -50400 or offset > 43200:
1064 raise Abort(_('impossible time zone offset: %d') % offset)
1066 raise Abort(_('impossible time zone offset: %d') % offset)
1065 return when, offset
1067 return when, offset
1066
1068
1067 def matchdate(date):
1069 def matchdate(date):
1068 """Return a function that matches a given date match specifier
1070 """Return a function that matches a given date match specifier
1069
1071
1070 Formats include:
1072 Formats include:
1071
1073
1072 '{date}' match a given date to the accuracy provided
1074 '{date}' match a given date to the accuracy provided
1073
1075
1074 '<{date}' on or before a given date
1076 '<{date}' on or before a given date
1075
1077
1076 '>{date}' on or after a given date
1078 '>{date}' on or after a given date
1077
1079
1078 """
1080 """
1079
1081
1080 def lower(date):
1082 def lower(date):
1081 d = dict(mb="1", d="1")
1083 d = dict(mb="1", d="1")
1082 return parsedate(date, extendeddateformats, d)[0]
1084 return parsedate(date, extendeddateformats, d)[0]
1083
1085
1084 def upper(date):
1086 def upper(date):
1085 d = dict(mb="12", HI="23", M="59", S="59")
1087 d = dict(mb="12", HI="23", M="59", S="59")
1086 for days in "31 30 29".split():
1088 for days in "31 30 29".split():
1087 try:
1089 try:
1088 d["d"] = days
1090 d["d"] = days
1089 return parsedate(date, extendeddateformats, d)[0]
1091 return parsedate(date, extendeddateformats, d)[0]
1090 except:
1092 except:
1091 pass
1093 pass
1092 d["d"] = "28"
1094 d["d"] = "28"
1093 return parsedate(date, extendeddateformats, d)[0]
1095 return parsedate(date, extendeddateformats, d)[0]
1094
1096
1095 date = date.strip()
1097 date = date.strip()
1096 if date[0] == "<":
1098 if date[0] == "<":
1097 when = upper(date[1:])
1099 when = upper(date[1:])
1098 return lambda x: x <= when
1100 return lambda x: x <= when
1099 elif date[0] == ">":
1101 elif date[0] == ">":
1100 when = lower(date[1:])
1102 when = lower(date[1:])
1101 return lambda x: x >= when
1103 return lambda x: x >= when
1102 elif date[0] == "-":
1104 elif date[0] == "-":
1103 try:
1105 try:
1104 days = int(date[1:])
1106 days = int(date[1:])
1105 except ValueError:
1107 except ValueError:
1106 raise Abort(_("invalid day spec: %s") % date[1:])
1108 raise Abort(_("invalid day spec: %s") % date[1:])
1107 when = makedate()[0] - days * 3600 * 24
1109 when = makedate()[0] - days * 3600 * 24
1108 return lambda x: x >= when
1110 return lambda x: x >= when
1109 elif " to " in date:
1111 elif " to " in date:
1110 a, b = date.split(" to ")
1112 a, b = date.split(" to ")
1111 start, stop = lower(a), upper(b)
1113 start, stop = lower(a), upper(b)
1112 return lambda x: x >= start and x <= stop
1114 return lambda x: x >= start and x <= stop
1113 else:
1115 else:
1114 start, stop = lower(date), upper(date)
1116 start, stop = lower(date), upper(date)
1115 return lambda x: x >= start and x <= stop
1117 return lambda x: x >= start and x <= stop
1116
1118
1117 def shortuser(user):
1119 def shortuser(user):
1118 """Return a short representation of a user name or email address."""
1120 """Return a short representation of a user name or email address."""
1119 f = user.find('@')
1121 f = user.find('@')
1120 if f >= 0:
1122 if f >= 0:
1121 user = user[:f]
1123 user = user[:f]
1122 f = user.find('<')
1124 f = user.find('<')
1123 if f >= 0:
1125 if f >= 0:
1124 user = user[f + 1:]
1126 user = user[f + 1:]
1125 f = user.find(' ')
1127 f = user.find(' ')
1126 if f >= 0:
1128 if f >= 0:
1127 user = user[:f]
1129 user = user[:f]
1128 f = user.find('.')
1130 f = user.find('.')
1129 if f >= 0:
1131 if f >= 0:
1130 user = user[:f]
1132 user = user[:f]
1131 return user
1133 return user
1132
1134
1133 def email(author):
1135 def email(author):
1134 '''get email of author.'''
1136 '''get email of author.'''
1135 r = author.find('>')
1137 r = author.find('>')
1136 if r == -1:
1138 if r == -1:
1137 r = None
1139 r = None
1138 return author[author.find('<') + 1:r]
1140 return author[author.find('<') + 1:r]
1139
1141
1140 def ellipsis(text, maxlength=400):
1142 def ellipsis(text, maxlength=400):
1141 """Trim string to at most maxlength (default: 400) characters."""
1143 """Trim string to at most maxlength (default: 400) characters."""
1142 if len(text) <= maxlength:
1144 if len(text) <= maxlength:
1143 return text
1145 return text
1144 else:
1146 else:
1145 return "%s..." % (text[:maxlength - 3])
1147 return "%s..." % (text[:maxlength - 3])
1146
1148
1147 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1149 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1148 '''yield every hg repository under path, recursively.'''
1150 '''yield every hg repository under path, recursively.'''
1149 def errhandler(err):
1151 def errhandler(err):
1150 if err.filename == path:
1152 if err.filename == path:
1151 raise err
1153 raise err
1152 if followsym and hasattr(os.path, 'samestat'):
1154 if followsym and hasattr(os.path, 'samestat'):
1153 def _add_dir_if_not_there(dirlst, dirname):
1155 def _add_dir_if_not_there(dirlst, dirname):
1154 match = False
1156 match = False
1155 samestat = os.path.samestat
1157 samestat = os.path.samestat
1156 dirstat = os.stat(dirname)
1158 dirstat = os.stat(dirname)
1157 for lstdirstat in dirlst:
1159 for lstdirstat in dirlst:
1158 if samestat(dirstat, lstdirstat):
1160 if samestat(dirstat, lstdirstat):
1159 match = True
1161 match = True
1160 break
1162 break
1161 if not match:
1163 if not match:
1162 dirlst.append(dirstat)
1164 dirlst.append(dirstat)
1163 return not match
1165 return not match
1164 else:
1166 else:
1165 followsym = False
1167 followsym = False
1166
1168
1167 if (seen_dirs is None) and followsym:
1169 if (seen_dirs is None) and followsym:
1168 seen_dirs = []
1170 seen_dirs = []
1169 _add_dir_if_not_there(seen_dirs, path)
1171 _add_dir_if_not_there(seen_dirs, path)
1170 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1172 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1171 dirs.sort()
1173 dirs.sort()
1172 if '.hg' in dirs:
1174 if '.hg' in dirs:
1173 yield root # found a repository
1175 yield root # found a repository
1174 qroot = os.path.join(root, '.hg', 'patches')
1176 qroot = os.path.join(root, '.hg', 'patches')
1175 if os.path.isdir(os.path.join(qroot, '.hg')):
1177 if os.path.isdir(os.path.join(qroot, '.hg')):
1176 yield qroot # we have a patch queue repo here
1178 yield qroot # we have a patch queue repo here
1177 if recurse:
1179 if recurse:
1178 # avoid recursing inside the .hg directory
1180 # avoid recursing inside the .hg directory
1179 dirs.remove('.hg')
1181 dirs.remove('.hg')
1180 else:
1182 else:
1181 dirs[:] = [] # don't descend further
1183 dirs[:] = [] # don't descend further
1182 elif followsym:
1184 elif followsym:
1183 newdirs = []
1185 newdirs = []
1184 for d in dirs:
1186 for d in dirs:
1185 fname = os.path.join(root, d)
1187 fname = os.path.join(root, d)
1186 if _add_dir_if_not_there(seen_dirs, fname):
1188 if _add_dir_if_not_there(seen_dirs, fname):
1187 if os.path.islink(fname):
1189 if os.path.islink(fname):
1188 for hgname in walkrepos(fname, True, seen_dirs):
1190 for hgname in walkrepos(fname, True, seen_dirs):
1189 yield hgname
1191 yield hgname
1190 else:
1192 else:
1191 newdirs.append(d)
1193 newdirs.append(d)
1192 dirs[:] = newdirs
1194 dirs[:] = newdirs
1193
1195
1194 _rcpath = None
1196 _rcpath = None
1195
1197
1196 def os_rcpath():
1198 def os_rcpath():
1197 '''return default os-specific hgrc search path'''
1199 '''return default os-specific hgrc search path'''
1198 path = system_rcpath()
1200 path = system_rcpath()
1199 path.extend(user_rcpath())
1201 path.extend(user_rcpath())
1200 path = [os.path.normpath(f) for f in path]
1202 path = [os.path.normpath(f) for f in path]
1201 return path
1203 return path
1202
1204
1203 def rcpath():
1205 def rcpath():
1204 '''return hgrc search path. if env var HGRCPATH is set, use it.
1206 '''return hgrc search path. if env var HGRCPATH is set, use it.
1205 for each item in path, if directory, use files ending in .rc,
1207 for each item in path, if directory, use files ending in .rc,
1206 else use item.
1208 else use item.
1207 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1209 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1208 if no HGRCPATH, use default os-specific path.'''
1210 if no HGRCPATH, use default os-specific path.'''
1209 global _rcpath
1211 global _rcpath
1210 if _rcpath is None:
1212 if _rcpath is None:
1211 if 'HGRCPATH' in os.environ:
1213 if 'HGRCPATH' in os.environ:
1212 _rcpath = []
1214 _rcpath = []
1213 for p in os.environ['HGRCPATH'].split(os.pathsep):
1215 for p in os.environ['HGRCPATH'].split(os.pathsep):
1214 if not p:
1216 if not p:
1215 continue
1217 continue
1216 p = expandpath(p)
1218 p = expandpath(p)
1217 if os.path.isdir(p):
1219 if os.path.isdir(p):
1218 for f, kind in osutil.listdir(p):
1220 for f, kind in osutil.listdir(p):
1219 if f.endswith('.rc'):
1221 if f.endswith('.rc'):
1220 _rcpath.append(os.path.join(p, f))
1222 _rcpath.append(os.path.join(p, f))
1221 else:
1223 else:
1222 _rcpath.append(p)
1224 _rcpath.append(p)
1223 else:
1225 else:
1224 _rcpath = os_rcpath()
1226 _rcpath = os_rcpath()
1225 return _rcpath
1227 return _rcpath
1226
1228
1227 def bytecount(nbytes):
1229 def bytecount(nbytes):
1228 '''return byte count formatted as readable string, with units'''
1230 '''return byte count formatted as readable string, with units'''
1229
1231
1230 units = (
1232 units = (
1231 (100, 1 << 30, _('%.0f GB')),
1233 (100, 1 << 30, _('%.0f GB')),
1232 (10, 1 << 30, _('%.1f GB')),
1234 (10, 1 << 30, _('%.1f GB')),
1233 (1, 1 << 30, _('%.2f GB')),
1235 (1, 1 << 30, _('%.2f GB')),
1234 (100, 1 << 20, _('%.0f MB')),
1236 (100, 1 << 20, _('%.0f MB')),
1235 (10, 1 << 20, _('%.1f MB')),
1237 (10, 1 << 20, _('%.1f MB')),
1236 (1, 1 << 20, _('%.2f MB')),
1238 (1, 1 << 20, _('%.2f MB')),
1237 (100, 1 << 10, _('%.0f KB')),
1239 (100, 1 << 10, _('%.0f KB')),
1238 (10, 1 << 10, _('%.1f KB')),
1240 (10, 1 << 10, _('%.1f KB')),
1239 (1, 1 << 10, _('%.2f KB')),
1241 (1, 1 << 10, _('%.2f KB')),
1240 (1, 1, _('%.0f bytes')),
1242 (1, 1, _('%.0f bytes')),
1241 )
1243 )
1242
1244
1243 for multiplier, divisor, format in units:
1245 for multiplier, divisor, format in units:
1244 if nbytes >= divisor * multiplier:
1246 if nbytes >= divisor * multiplier:
1245 return format % (nbytes / float(divisor))
1247 return format % (nbytes / float(divisor))
1246 return units[-1][2] % nbytes
1248 return units[-1][2] % nbytes
1247
1249
1248 def drop_scheme(scheme, path):
1250 def drop_scheme(scheme, path):
1249 sc = scheme + ':'
1251 sc = scheme + ':'
1250 if path.startswith(sc):
1252 if path.startswith(sc):
1251 path = path[len(sc):]
1253 path = path[len(sc):]
1252 if path.startswith('//'):
1254 if path.startswith('//'):
1253 if scheme == 'file':
1255 if scheme == 'file':
1254 i = path.find('/', 2)
1256 i = path.find('/', 2)
1255 if i == -1:
1257 if i == -1:
1256 return ''
1258 return ''
1257 # On Windows, absolute paths are rooted at the current drive
1259 # On Windows, absolute paths are rooted at the current drive
1258 # root. On POSIX they are rooted at the file system root.
1260 # root. On POSIX they are rooted at the file system root.
1259 if os.name == 'nt':
1261 if os.name == 'nt':
1260 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1262 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1261 path = os.path.join(droot, path[i + 1:])
1263 path = os.path.join(droot, path[i + 1:])
1262 else:
1264 else:
1263 path = path[i:]
1265 path = path[i:]
1264 else:
1266 else:
1265 path = path[2:]
1267 path = path[2:]
1266 return path
1268 return path
1267
1269
1268 def uirepr(s):
1270 def uirepr(s):
1269 # Avoid double backslash in Windows path repr()
1271 # Avoid double backslash in Windows path repr()
1270 return repr(s).replace('\\\\', '\\')
1272 return repr(s).replace('\\\\', '\\')
1271
1273
1272 #### naming convention of below implementation follows 'textwrap' module
1274 #### naming convention of below implementation follows 'textwrap' module
1273
1275
1274 class MBTextWrapper(textwrap.TextWrapper):
1276 class MBTextWrapper(textwrap.TextWrapper):
1275 def __init__(self, **kwargs):
1277 def __init__(self, **kwargs):
1276 textwrap.TextWrapper.__init__(self, **kwargs)
1278 textwrap.TextWrapper.__init__(self, **kwargs)
1277
1279
1278 def _cutdown(self, str, space_left):
1280 def _cutdown(self, str, space_left):
1279 l = 0
1281 l = 0
1280 ucstr = unicode(str, encoding.encoding)
1282 ucstr = unicode(str, encoding.encoding)
1281 w = unicodedata.east_asian_width
1283 w = unicodedata.east_asian_width
1282 for i in xrange(len(ucstr)):
1284 for i in xrange(len(ucstr)):
1283 l += w(ucstr[i]) in 'WFA' and 2 or 1
1285 l += w(ucstr[i]) in 'WFA' and 2 or 1
1284 if space_left < l:
1286 if space_left < l:
1285 return (ucstr[:i].encode(encoding.encoding),
1287 return (ucstr[:i].encode(encoding.encoding),
1286 ucstr[i:].encode(encoding.encoding))
1288 ucstr[i:].encode(encoding.encoding))
1287 return str, ''
1289 return str, ''
1288
1290
1289 # ----------------------------------------
1291 # ----------------------------------------
1290 # overriding of base class
1292 # overriding of base class
1291
1293
1292 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1294 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1293 space_left = max(width - cur_len, 1)
1295 space_left = max(width - cur_len, 1)
1294
1296
1295 if self.break_long_words:
1297 if self.break_long_words:
1296 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1298 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1297 cur_line.append(cut)
1299 cur_line.append(cut)
1298 reversed_chunks[-1] = res
1300 reversed_chunks[-1] = res
1299 elif not cur_line:
1301 elif not cur_line:
1300 cur_line.append(reversed_chunks.pop())
1302 cur_line.append(reversed_chunks.pop())
1301
1303
1302 #### naming convention of above implementation follows 'textwrap' module
1304 #### naming convention of above implementation follows 'textwrap' module
1303
1305
1304 def wrap(line, width=None, initindent='', hangindent=''):
1306 def wrap(line, width=None, initindent='', hangindent=''):
1305 if width is None:
1307 if width is None:
1306 width = termwidth() - 2
1308 width = termwidth() - 2
1307 maxindent = max(len(hangindent), len(initindent))
1309 maxindent = max(len(hangindent), len(initindent))
1308 if width <= maxindent:
1310 if width <= maxindent:
1309 # adjust for weird terminal size
1311 # adjust for weird terminal size
1310 width = max(78, maxindent + 1)
1312 width = max(78, maxindent + 1)
1311 wrapper = MBTextWrapper(width=width,
1313 wrapper = MBTextWrapper(width=width,
1312 initial_indent=initindent,
1314 initial_indent=initindent,
1313 subsequent_indent=hangindent)
1315 subsequent_indent=hangindent)
1314 return wrapper.fill(line)
1316 return wrapper.fill(line)
1315
1317
1316 def iterlines(iterator):
1318 def iterlines(iterator):
1317 for chunk in iterator:
1319 for chunk in iterator:
1318 for line in chunk.splitlines():
1320 for line in chunk.splitlines():
1319 yield line
1321 yield line
1320
1322
1321 def expandpath(path):
1323 def expandpath(path):
1322 return os.path.expanduser(os.path.expandvars(path))
1324 return os.path.expanduser(os.path.expandvars(path))
1323
1325
1324 def hgcmd():
1326 def hgcmd():
1325 """Return the command used to execute current hg
1327 """Return the command used to execute current hg
1326
1328
1327 This is different from hgexecutable() because on Windows we want
1329 This is different from hgexecutable() because on Windows we want
1328 to avoid things opening new shell windows like batch files, so we
1330 to avoid things opening new shell windows like batch files, so we
1329 get either the python call or current executable.
1331 get either the python call or current executable.
1330 """
1332 """
1331 if main_is_frozen():
1333 if main_is_frozen():
1332 return [sys.executable]
1334 return [sys.executable]
1333 return gethgcmd()
1335 return gethgcmd()
1334
1336
1335 def rundetached(args, condfn):
1337 def rundetached(args, condfn):
1336 """Execute the argument list in a detached process.
1338 """Execute the argument list in a detached process.
1337
1339
1338 condfn is a callable which is called repeatedly and should return
1340 condfn is a callable which is called repeatedly and should return
1339 True once the child process is known to have started successfully.
1341 True once the child process is known to have started successfully.
1340 At this point, the child process PID is returned. If the child
1342 At this point, the child process PID is returned. If the child
1341 process fails to start or finishes before condfn() evaluates to
1343 process fails to start or finishes before condfn() evaluates to
1342 True, return -1.
1344 True, return -1.
1343 """
1345 """
1344 # Windows case is easier because the child process is either
1346 # Windows case is easier because the child process is either
1345 # successfully starting and validating the condition or exiting
1347 # successfully starting and validating the condition or exiting
1346 # on failure. We just poll on its PID. On Unix, if the child
1348 # on failure. We just poll on its PID. On Unix, if the child
1347 # process fails to start, it will be left in a zombie state until
1349 # process fails to start, it will be left in a zombie state until
1348 # the parent wait on it, which we cannot do since we expect a long
1350 # the parent wait on it, which we cannot do since we expect a long
1349 # running process on success. Instead we listen for SIGCHLD telling
1351 # running process on success. Instead we listen for SIGCHLD telling
1350 # us our child process terminated.
1352 # us our child process terminated.
1351 terminated = set()
1353 terminated = set()
1352 def handler(signum, frame):
1354 def handler(signum, frame):
1353 terminated.add(os.wait())
1355 terminated.add(os.wait())
1354 prevhandler = None
1356 prevhandler = None
1355 if hasattr(signal, 'SIGCHLD'):
1357 if hasattr(signal, 'SIGCHLD'):
1356 prevhandler = signal.signal(signal.SIGCHLD, handler)
1358 prevhandler = signal.signal(signal.SIGCHLD, handler)
1357 try:
1359 try:
1358 pid = spawndetached(args)
1360 pid = spawndetached(args)
1359 while not condfn():
1361 while not condfn():
1360 if ((pid in terminated or not testpid(pid))
1362 if ((pid in terminated or not testpid(pid))
1361 and not condfn()):
1363 and not condfn()):
1362 return -1
1364 return -1
1363 time.sleep(0.1)
1365 time.sleep(0.1)
1364 return pid
1366 return pid
1365 finally:
1367 finally:
1366 if prevhandler is not None:
1368 if prevhandler is not None:
1367 signal.signal(signal.SIGCHLD, prevhandler)
1369 signal.signal(signal.SIGCHLD, prevhandler)
1368
1370
1369 try:
1371 try:
1370 any, all = any, all
1372 any, all = any, all
1371 except NameError:
1373 except NameError:
1372 def any(iterable):
1374 def any(iterable):
1373 for i in iterable:
1375 for i in iterable:
1374 if i:
1376 if i:
1375 return True
1377 return True
1376 return False
1378 return False
1377
1379
1378 def all(iterable):
1380 def all(iterable):
1379 for i in iterable:
1381 for i in iterable:
1380 if not i:
1382 if not i:
1381 return False
1383 return False
1382 return True
1384 return True
1383
1385
1384 def termwidth():
1386 def termwidth():
1385 if 'COLUMNS' in os.environ:
1387 if 'COLUMNS' in os.environ:
1386 try:
1388 try:
1387 return int(os.environ['COLUMNS'])
1389 return int(os.environ['COLUMNS'])
1388 except ValueError:
1390 except ValueError:
1389 pass
1391 pass
1390 return termwidth_()
1392 return termwidth_()
General Comments 0
You need to be logged in to leave comments. Login now