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