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