##// END OF EJS Templates
util.copyfiles: don't try os_link() again if it failed before...
Adrian Buehlmann -
r11232:97f6d2a0 default
parent child Browse files
Show More
@@ -1,1345 +1,1347 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 __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):
369 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=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 def py2shell(val):
376 def py2shell(val):
377 'convert python object into string that is useful to shell'
377 'convert python object into string that is useful to shell'
378 if val is None or val is False:
378 if val is None or val is False:
379 return '0'
379 return '0'
380 if val is True:
380 if val is True:
381 return '1'
381 return '1'
382 return str(val)
382 return str(val)
383 origcmd = cmd
383 origcmd = cmd
384 if os.name == 'nt':
384 if os.name == 'nt':
385 cmd = '"%s"' % cmd
385 cmd = '"%s"' % cmd
386 env = dict(os.environ)
386 env = dict(os.environ)
387 env.update((k, py2shell(v)) for k, v in environ.iteritems())
387 env.update((k, py2shell(v)) for k, v in environ.iteritems())
388 env['HG'] = hgexecutable()
388 env['HG'] = hgexecutable()
389 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
389 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
390 env=env, cwd=cwd)
390 env=env, cwd=cwd)
391 if sys.platform == 'OpenVMS' and rc & 1:
391 if sys.platform == 'OpenVMS' and rc & 1:
392 rc = 0
392 rc = 0
393 if rc and onerr:
393 if rc and onerr:
394 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
394 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
395 explain_exit(rc)[0])
395 explain_exit(rc)[0])
396 if errprefix:
396 if errprefix:
397 errmsg = '%s: %s' % (errprefix, errmsg)
397 errmsg = '%s: %s' % (errprefix, errmsg)
398 try:
398 try:
399 onerr.warn(errmsg + '\n')
399 onerr.warn(errmsg + '\n')
400 except AttributeError:
400 except AttributeError:
401 raise onerr(errmsg)
401 raise onerr(errmsg)
402 return rc
402 return rc
403
403
404 def checksignature(func):
404 def checksignature(func):
405 '''wrap a function with code to check for calling errors'''
405 '''wrap a function with code to check for calling errors'''
406 def check(*args, **kwargs):
406 def check(*args, **kwargs):
407 try:
407 try:
408 return func(*args, **kwargs)
408 return func(*args, **kwargs)
409 except TypeError:
409 except TypeError:
410 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
410 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
411 raise error.SignatureError
411 raise error.SignatureError
412 raise
412 raise
413
413
414 return check
414 return check
415
415
416 # os.path.lexists is not available on python2.3
416 # os.path.lexists is not available on python2.3
417 def lexists(filename):
417 def lexists(filename):
418 "test whether a file with this name exists. does not follow symlinks"
418 "test whether a file with this name exists. does not follow symlinks"
419 try:
419 try:
420 os.lstat(filename)
420 os.lstat(filename)
421 except:
421 except:
422 return False
422 return False
423 return True
423 return True
424
424
425 def unlink(f):
425 def unlink(f):
426 """unlink and remove the directory if it is empty"""
426 """unlink and remove the directory if it is empty"""
427 os.unlink(f)
427 os.unlink(f)
428 # try removing directories that might now be empty
428 # try removing directories that might now be empty
429 try:
429 try:
430 os.removedirs(os.path.dirname(f))
430 os.removedirs(os.path.dirname(f))
431 except OSError:
431 except OSError:
432 pass
432 pass
433
433
434 def copyfile(src, dest):
434 def copyfile(src, dest):
435 "copy a file, preserving mode and atime/mtime"
435 "copy a file, preserving mode and atime/mtime"
436 if os.path.islink(src):
436 if os.path.islink(src):
437 try:
437 try:
438 os.unlink(dest)
438 os.unlink(dest)
439 except:
439 except:
440 pass
440 pass
441 os.symlink(os.readlink(src), dest)
441 os.symlink(os.readlink(src), dest)
442 else:
442 else:
443 try:
443 try:
444 shutil.copyfile(src, dest)
444 shutil.copyfile(src, dest)
445 shutil.copystat(src, dest)
445 shutil.copystat(src, dest)
446 except shutil.Error, inst:
446 except shutil.Error, inst:
447 raise Abort(str(inst))
447 raise Abort(str(inst))
448
448
449 def copyfiles(src, dst, hardlink=None):
449 def copyfiles(src, dst, hardlink=None):
450 """Copy a directory tree using hardlinks if possible"""
450 """Copy a directory tree using hardlinks if possible"""
451
451
452 if hardlink is None:
452 if hardlink is None:
453 hardlink = (os.stat(src).st_dev ==
453 hardlink = (os.stat(src).st_dev ==
454 os.stat(os.path.dirname(dst)).st_dev)
454 os.stat(os.path.dirname(dst)).st_dev)
455
455
456 if os.path.isdir(src):
456 if os.path.isdir(src):
457 os.mkdir(dst)
457 os.mkdir(dst)
458 for name, kind in osutil.listdir(src):
458 for name, kind in osutil.listdir(src):
459 srcname = os.path.join(src, name)
459 srcname = os.path.join(src, name)
460 dstname = os.path.join(dst, name)
460 dstname = os.path.join(dst, name)
461 copyfiles(srcname, dstname, hardlink)
461 hardlink = copyfiles(srcname, dstname, hardlink)
462 else:
462 else:
463 if hardlink:
463 if hardlink:
464 try:
464 try:
465 os_link(src, dst)
465 os_link(src, dst)
466 except (IOError, OSError):
466 except (IOError, OSError):
467 hardlink = False
467 hardlink = False
468 shutil.copy(src, dst)
468 shutil.copy(src, dst)
469 else:
469 else:
470 shutil.copy(src, dst)
470 shutil.copy(src, dst)
471
471
472 return hardlink
473
472 class path_auditor(object):
474 class path_auditor(object):
473 '''ensure that a filesystem path contains no banned components.
475 '''ensure that a filesystem path contains no banned components.
474 the following properties of a path are checked:
476 the following properties of a path are checked:
475
477
476 - under top-level .hg
478 - under top-level .hg
477 - starts at the root of a windows drive
479 - starts at the root of a windows drive
478 - contains ".."
480 - contains ".."
479 - traverses a symlink (e.g. a/symlink_here/b)
481 - traverses a symlink (e.g. a/symlink_here/b)
480 - inside a nested repository'''
482 - inside a nested repository'''
481
483
482 def __init__(self, root):
484 def __init__(self, root):
483 self.audited = set()
485 self.audited = set()
484 self.auditeddir = set()
486 self.auditeddir = set()
485 self.root = root
487 self.root = root
486
488
487 def __call__(self, path):
489 def __call__(self, path):
488 if path in self.audited:
490 if path in self.audited:
489 return
491 return
490 normpath = os.path.normcase(path)
492 normpath = os.path.normcase(path)
491 parts = splitpath(normpath)
493 parts = splitpath(normpath)
492 if (os.path.splitdrive(path)[0]
494 if (os.path.splitdrive(path)[0]
493 or parts[0].lower() in ('.hg', '.hg.', '')
495 or parts[0].lower() in ('.hg', '.hg.', '')
494 or os.pardir in parts):
496 or os.pardir in parts):
495 raise Abort(_("path contains illegal component: %s") % path)
497 raise Abort(_("path contains illegal component: %s") % path)
496 if '.hg' in path.lower():
498 if '.hg' in path.lower():
497 lparts = [p.lower() for p in parts]
499 lparts = [p.lower() for p in parts]
498 for p in '.hg', '.hg.':
500 for p in '.hg', '.hg.':
499 if p in lparts[1:]:
501 if p in lparts[1:]:
500 pos = lparts.index(p)
502 pos = lparts.index(p)
501 base = os.path.join(*parts[:pos])
503 base = os.path.join(*parts[:pos])
502 raise Abort(_('path %r is inside repo %r') % (path, base))
504 raise Abort(_('path %r is inside repo %r') % (path, base))
503 def check(prefix):
505 def check(prefix):
504 curpath = os.path.join(self.root, prefix)
506 curpath = os.path.join(self.root, prefix)
505 try:
507 try:
506 st = os.lstat(curpath)
508 st = os.lstat(curpath)
507 except OSError, err:
509 except OSError, err:
508 # EINVAL can be raised as invalid path syntax under win32.
510 # EINVAL can be raised as invalid path syntax under win32.
509 # They must be ignored for patterns can be checked too.
511 # They must be ignored for patterns can be checked too.
510 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
512 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
511 raise
513 raise
512 else:
514 else:
513 if stat.S_ISLNK(st.st_mode):
515 if stat.S_ISLNK(st.st_mode):
514 raise Abort(_('path %r traverses symbolic link %r') %
516 raise Abort(_('path %r traverses symbolic link %r') %
515 (path, prefix))
517 (path, prefix))
516 elif (stat.S_ISDIR(st.st_mode) and
518 elif (stat.S_ISDIR(st.st_mode) and
517 os.path.isdir(os.path.join(curpath, '.hg'))):
519 os.path.isdir(os.path.join(curpath, '.hg'))):
518 raise Abort(_('path %r is inside repo %r') %
520 raise Abort(_('path %r is inside repo %r') %
519 (path, prefix))
521 (path, prefix))
520 parts.pop()
522 parts.pop()
521 prefixes = []
523 prefixes = []
522 while parts:
524 while parts:
523 prefix = os.sep.join(parts)
525 prefix = os.sep.join(parts)
524 if prefix in self.auditeddir:
526 if prefix in self.auditeddir:
525 break
527 break
526 check(prefix)
528 check(prefix)
527 prefixes.append(prefix)
529 prefixes.append(prefix)
528 parts.pop()
530 parts.pop()
529
531
530 self.audited.add(path)
532 self.audited.add(path)
531 # only add prefixes to the cache after checking everything: we don't
533 # only add prefixes to the cache after checking everything: we don't
532 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
534 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
533 self.auditeddir.update(prefixes)
535 self.auditeddir.update(prefixes)
534
536
535 def nlinks(pathname):
537 def nlinks(pathname):
536 """Return number of hardlinks for the given file."""
538 """Return number of hardlinks for the given file."""
537 return os.lstat(pathname).st_nlink
539 return os.lstat(pathname).st_nlink
538
540
539 if hasattr(os, 'link'):
541 if hasattr(os, 'link'):
540 os_link = os.link
542 os_link = os.link
541 else:
543 else:
542 def os_link(src, dst):
544 def os_link(src, dst):
543 raise OSError(0, _("Hardlinks not supported"))
545 raise OSError(0, _("Hardlinks not supported"))
544
546
545 def lookup_reg(key, name=None, scope=None):
547 def lookup_reg(key, name=None, scope=None):
546 return None
548 return None
547
549
548 def hidewindow():
550 def hidewindow():
549 """Hide current shell window.
551 """Hide current shell window.
550
552
551 Used to hide the window opened when starting asynchronous
553 Used to hide the window opened when starting asynchronous
552 child process under Windows, unneeded on other systems.
554 child process under Windows, unneeded on other systems.
553 """
555 """
554 pass
556 pass
555
557
556 if os.name == 'nt':
558 if os.name == 'nt':
557 from windows import *
559 from windows import *
558 else:
560 else:
559 from posix import *
561 from posix import *
560
562
561 def makelock(info, pathname):
563 def makelock(info, pathname):
562 try:
564 try:
563 return os.symlink(info, pathname)
565 return os.symlink(info, pathname)
564 except OSError, why:
566 except OSError, why:
565 if why.errno == errno.EEXIST:
567 if why.errno == errno.EEXIST:
566 raise
568 raise
567 except AttributeError: # no symlink in os
569 except AttributeError: # no symlink in os
568 pass
570 pass
569
571
570 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
572 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
571 os.write(ld, info)
573 os.write(ld, info)
572 os.close(ld)
574 os.close(ld)
573
575
574 def readlock(pathname):
576 def readlock(pathname):
575 try:
577 try:
576 return os.readlink(pathname)
578 return os.readlink(pathname)
577 except OSError, why:
579 except OSError, why:
578 if why.errno not in (errno.EINVAL, errno.ENOSYS):
580 if why.errno not in (errno.EINVAL, errno.ENOSYS):
579 raise
581 raise
580 except AttributeError: # no symlink in os
582 except AttributeError: # no symlink in os
581 pass
583 pass
582 return posixfile(pathname).read()
584 return posixfile(pathname).read()
583
585
584 def fstat(fp):
586 def fstat(fp):
585 '''stat file object that may not have fileno method.'''
587 '''stat file object that may not have fileno method.'''
586 try:
588 try:
587 return os.fstat(fp.fileno())
589 return os.fstat(fp.fileno())
588 except AttributeError:
590 except AttributeError:
589 return os.stat(fp.name)
591 return os.stat(fp.name)
590
592
591 # File system features
593 # File system features
592
594
593 def checkcase(path):
595 def checkcase(path):
594 """
596 """
595 Check whether the given path is on a case-sensitive filesystem
597 Check whether the given path is on a case-sensitive filesystem
596
598
597 Requires a path (like /foo/.hg) ending with a foldable final
599 Requires a path (like /foo/.hg) ending with a foldable final
598 directory component.
600 directory component.
599 """
601 """
600 s1 = os.stat(path)
602 s1 = os.stat(path)
601 d, b = os.path.split(path)
603 d, b = os.path.split(path)
602 p2 = os.path.join(d, b.upper())
604 p2 = os.path.join(d, b.upper())
603 if path == p2:
605 if path == p2:
604 p2 = os.path.join(d, b.lower())
606 p2 = os.path.join(d, b.lower())
605 try:
607 try:
606 s2 = os.stat(p2)
608 s2 = os.stat(p2)
607 if s2 == s1:
609 if s2 == s1:
608 return False
610 return False
609 return True
611 return True
610 except:
612 except:
611 return True
613 return True
612
614
613 _fspathcache = {}
615 _fspathcache = {}
614 def fspath(name, root):
616 def fspath(name, root):
615 '''Get name in the case stored in the filesystem
617 '''Get name in the case stored in the filesystem
616
618
617 The name is either relative to root, or it is an absolute path starting
619 The name is either relative to root, or it is an absolute path starting
618 with root. Note that this function is unnecessary, and should not be
620 with root. Note that this function is unnecessary, and should not be
619 called, for case-sensitive filesystems (simply because it's expensive).
621 called, for case-sensitive filesystems (simply because it's expensive).
620 '''
622 '''
621 # If name is absolute, make it relative
623 # If name is absolute, make it relative
622 if name.lower().startswith(root.lower()):
624 if name.lower().startswith(root.lower()):
623 l = len(root)
625 l = len(root)
624 if name[l] == os.sep or name[l] == os.altsep:
626 if name[l] == os.sep or name[l] == os.altsep:
625 l = l + 1
627 l = l + 1
626 name = name[l:]
628 name = name[l:]
627
629
628 if not os.path.exists(os.path.join(root, name)):
630 if not os.path.exists(os.path.join(root, name)):
629 return None
631 return None
630
632
631 seps = os.sep
633 seps = os.sep
632 if os.altsep:
634 if os.altsep:
633 seps = seps + os.altsep
635 seps = seps + os.altsep
634 # Protect backslashes. This gets silly very quickly.
636 # Protect backslashes. This gets silly very quickly.
635 seps.replace('\\','\\\\')
637 seps.replace('\\','\\\\')
636 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
638 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
637 dir = os.path.normcase(os.path.normpath(root))
639 dir = os.path.normcase(os.path.normpath(root))
638 result = []
640 result = []
639 for part, sep in pattern.findall(name):
641 for part, sep in pattern.findall(name):
640 if sep:
642 if sep:
641 result.append(sep)
643 result.append(sep)
642 continue
644 continue
643
645
644 if dir not in _fspathcache:
646 if dir not in _fspathcache:
645 _fspathcache[dir] = os.listdir(dir)
647 _fspathcache[dir] = os.listdir(dir)
646 contents = _fspathcache[dir]
648 contents = _fspathcache[dir]
647
649
648 lpart = part.lower()
650 lpart = part.lower()
649 lenp = len(part)
651 lenp = len(part)
650 for n in contents:
652 for n in contents:
651 if lenp == len(n) and n.lower() == lpart:
653 if lenp == len(n) and n.lower() == lpart:
652 result.append(n)
654 result.append(n)
653 break
655 break
654 else:
656 else:
655 # Cannot happen, as the file exists!
657 # Cannot happen, as the file exists!
656 result.append(part)
658 result.append(part)
657 dir = os.path.join(dir, lpart)
659 dir = os.path.join(dir, lpart)
658
660
659 return ''.join(result)
661 return ''.join(result)
660
662
661 def checkexec(path):
663 def checkexec(path):
662 """
664 """
663 Check whether the given path is on a filesystem with UNIX-like exec flags
665 Check whether the given path is on a filesystem with UNIX-like exec flags
664
666
665 Requires a directory (like /foo/.hg)
667 Requires a directory (like /foo/.hg)
666 """
668 """
667
669
668 # VFAT on some Linux versions can flip mode but it doesn't persist
670 # VFAT on some Linux versions can flip mode but it doesn't persist
669 # a FS remount. Frequently we can detect it if files are created
671 # a FS remount. Frequently we can detect it if files are created
670 # with exec bit on.
672 # with exec bit on.
671
673
672 try:
674 try:
673 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
675 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
674 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
676 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
675 try:
677 try:
676 os.close(fh)
678 os.close(fh)
677 m = os.stat(fn).st_mode & 0777
679 m = os.stat(fn).st_mode & 0777
678 new_file_has_exec = m & EXECFLAGS
680 new_file_has_exec = m & EXECFLAGS
679 os.chmod(fn, m ^ EXECFLAGS)
681 os.chmod(fn, m ^ EXECFLAGS)
680 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
682 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
681 finally:
683 finally:
682 os.unlink(fn)
684 os.unlink(fn)
683 except (IOError, OSError):
685 except (IOError, OSError):
684 # we don't care, the user probably won't be able to commit anyway
686 # we don't care, the user probably won't be able to commit anyway
685 return False
687 return False
686 return not (new_file_has_exec or exec_flags_cannot_flip)
688 return not (new_file_has_exec or exec_flags_cannot_flip)
687
689
688 def checklink(path):
690 def checklink(path):
689 """check whether the given path is on a symlink-capable filesystem"""
691 """check whether the given path is on a symlink-capable filesystem"""
690 # mktemp is not racy because symlink creation will fail if the
692 # mktemp is not racy because symlink creation will fail if the
691 # file already exists
693 # file already exists
692 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
694 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
693 try:
695 try:
694 os.symlink(".", name)
696 os.symlink(".", name)
695 os.unlink(name)
697 os.unlink(name)
696 return True
698 return True
697 except (OSError, AttributeError):
699 except (OSError, AttributeError):
698 return False
700 return False
699
701
700 def needbinarypatch():
702 def needbinarypatch():
701 """return True if patches should be applied in binary mode by default."""
703 """return True if patches should be applied in binary mode by default."""
702 return os.name == 'nt'
704 return os.name == 'nt'
703
705
704 def endswithsep(path):
706 def endswithsep(path):
705 '''Check path ends with os.sep or os.altsep.'''
707 '''Check path ends with os.sep or os.altsep.'''
706 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
708 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
707
709
708 def splitpath(path):
710 def splitpath(path):
709 '''Split path by os.sep.
711 '''Split path by os.sep.
710 Note that this function does not use os.altsep because this is
712 Note that this function does not use os.altsep because this is
711 an alternative of simple "xxx.split(os.sep)".
713 an alternative of simple "xxx.split(os.sep)".
712 It is recommended to use os.path.normpath() before using this
714 It is recommended to use os.path.normpath() before using this
713 function if need.'''
715 function if need.'''
714 return path.split(os.sep)
716 return path.split(os.sep)
715
717
716 def gui():
718 def gui():
717 '''Are we running in a GUI?'''
719 '''Are we running in a GUI?'''
718 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
720 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
719
721
720 def mktempcopy(name, emptyok=False, createmode=None):
722 def mktempcopy(name, emptyok=False, createmode=None):
721 """Create a temporary file with the same contents from name
723 """Create a temporary file with the same contents from name
722
724
723 The permission bits are copied from the original file.
725 The permission bits are copied from the original file.
724
726
725 If the temporary file is going to be truncated immediately, you
727 If the temporary file is going to be truncated immediately, you
726 can use emptyok=True as an optimization.
728 can use emptyok=True as an optimization.
727
729
728 Returns the name of the temporary file.
730 Returns the name of the temporary file.
729 """
731 """
730 d, fn = os.path.split(name)
732 d, fn = os.path.split(name)
731 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
733 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
732 os.close(fd)
734 os.close(fd)
733 # Temporary files are created with mode 0600, which is usually not
735 # Temporary files are created with mode 0600, which is usually not
734 # what we want. If the original file already exists, just copy
736 # what we want. If the original file already exists, just copy
735 # its mode. Otherwise, manually obey umask.
737 # its mode. Otherwise, manually obey umask.
736 try:
738 try:
737 st_mode = os.lstat(name).st_mode & 0777
739 st_mode = os.lstat(name).st_mode & 0777
738 except OSError, inst:
740 except OSError, inst:
739 if inst.errno != errno.ENOENT:
741 if inst.errno != errno.ENOENT:
740 raise
742 raise
741 st_mode = createmode
743 st_mode = createmode
742 if st_mode is None:
744 if st_mode is None:
743 st_mode = ~umask
745 st_mode = ~umask
744 st_mode &= 0666
746 st_mode &= 0666
745 os.chmod(temp, st_mode)
747 os.chmod(temp, st_mode)
746 if emptyok:
748 if emptyok:
747 return temp
749 return temp
748 try:
750 try:
749 try:
751 try:
750 ifp = posixfile(name, "rb")
752 ifp = posixfile(name, "rb")
751 except IOError, inst:
753 except IOError, inst:
752 if inst.errno == errno.ENOENT:
754 if inst.errno == errno.ENOENT:
753 return temp
755 return temp
754 if not getattr(inst, 'filename', None):
756 if not getattr(inst, 'filename', None):
755 inst.filename = name
757 inst.filename = name
756 raise
758 raise
757 ofp = posixfile(temp, "wb")
759 ofp = posixfile(temp, "wb")
758 for chunk in filechunkiter(ifp):
760 for chunk in filechunkiter(ifp):
759 ofp.write(chunk)
761 ofp.write(chunk)
760 ifp.close()
762 ifp.close()
761 ofp.close()
763 ofp.close()
762 except:
764 except:
763 try: os.unlink(temp)
765 try: os.unlink(temp)
764 except: pass
766 except: pass
765 raise
767 raise
766 return temp
768 return temp
767
769
768 class atomictempfile(object):
770 class atomictempfile(object):
769 """file-like object that atomically updates a file
771 """file-like object that atomically updates a file
770
772
771 All writes will be redirected to a temporary copy of the original
773 All writes will be redirected to a temporary copy of the original
772 file. When rename is called, the copy is renamed to the original
774 file. When rename is called, the copy is renamed to the original
773 name, making the changes visible.
775 name, making the changes visible.
774 """
776 """
775 def __init__(self, name, mode='w+b', createmode=None):
777 def __init__(self, name, mode='w+b', createmode=None):
776 self.__name = name
778 self.__name = name
777 self._fp = None
779 self._fp = None
778 self.temp = mktempcopy(name, emptyok=('w' in mode),
780 self.temp = mktempcopy(name, emptyok=('w' in mode),
779 createmode=createmode)
781 createmode=createmode)
780 self._fp = posixfile(self.temp, mode)
782 self._fp = posixfile(self.temp, mode)
781
783
782 def __getattr__(self, name):
784 def __getattr__(self, name):
783 return getattr(self._fp, name)
785 return getattr(self._fp, name)
784
786
785 def rename(self):
787 def rename(self):
786 if not self._fp.closed:
788 if not self._fp.closed:
787 self._fp.close()
789 self._fp.close()
788 rename(self.temp, localpath(self.__name))
790 rename(self.temp, localpath(self.__name))
789
791
790 def __del__(self):
792 def __del__(self):
791 if not self._fp:
793 if not self._fp:
792 return
794 return
793 if not self._fp.closed:
795 if not self._fp.closed:
794 try:
796 try:
795 os.unlink(self.temp)
797 os.unlink(self.temp)
796 except: pass
798 except: pass
797 self._fp.close()
799 self._fp.close()
798
800
799 def makedirs(name, mode=None):
801 def makedirs(name, mode=None):
800 """recursive directory creation with parent mode inheritance"""
802 """recursive directory creation with parent mode inheritance"""
801 try:
803 try:
802 os.mkdir(name)
804 os.mkdir(name)
803 if mode is not None:
805 if mode is not None:
804 os.chmod(name, mode)
806 os.chmod(name, mode)
805 return
807 return
806 except OSError, err:
808 except OSError, err:
807 if err.errno == errno.EEXIST:
809 if err.errno == errno.EEXIST:
808 return
810 return
809 if err.errno != errno.ENOENT:
811 if err.errno != errno.ENOENT:
810 raise
812 raise
811 parent = os.path.abspath(os.path.dirname(name))
813 parent = os.path.abspath(os.path.dirname(name))
812 makedirs(parent, mode)
814 makedirs(parent, mode)
813 makedirs(name, mode)
815 makedirs(name, mode)
814
816
815 class opener(object):
817 class opener(object):
816 """Open files relative to a base directory
818 """Open files relative to a base directory
817
819
818 This class is used to hide the details of COW semantics and
820 This class is used to hide the details of COW semantics and
819 remote file access from higher level code.
821 remote file access from higher level code.
820 """
822 """
821 def __init__(self, base, audit=True):
823 def __init__(self, base, audit=True):
822 self.base = base
824 self.base = base
823 if audit:
825 if audit:
824 self.audit_path = path_auditor(base)
826 self.audit_path = path_auditor(base)
825 else:
827 else:
826 self.audit_path = always
828 self.audit_path = always
827 self.createmode = None
829 self.createmode = None
828
830
829 @propertycache
831 @propertycache
830 def _can_symlink(self):
832 def _can_symlink(self):
831 return checklink(self.base)
833 return checklink(self.base)
832
834
833 def _fixfilemode(self, name):
835 def _fixfilemode(self, name):
834 if self.createmode is None:
836 if self.createmode is None:
835 return
837 return
836 os.chmod(name, self.createmode & 0666)
838 os.chmod(name, self.createmode & 0666)
837
839
838 def __call__(self, path, mode="r", text=False, atomictemp=False):
840 def __call__(self, path, mode="r", text=False, atomictemp=False):
839 self.audit_path(path)
841 self.audit_path(path)
840 f = os.path.join(self.base, path)
842 f = os.path.join(self.base, path)
841
843
842 if not text and "b" not in mode:
844 if not text and "b" not in mode:
843 mode += "b" # for that other OS
845 mode += "b" # for that other OS
844
846
845 nlink = -1
847 nlink = -1
846 if mode not in ("r", "rb"):
848 if mode not in ("r", "rb"):
847 try:
849 try:
848 nlink = nlinks(f)
850 nlink = nlinks(f)
849 except OSError:
851 except OSError:
850 nlink = 0
852 nlink = 0
851 d = os.path.dirname(f)
853 d = os.path.dirname(f)
852 if not os.path.isdir(d):
854 if not os.path.isdir(d):
853 makedirs(d, self.createmode)
855 makedirs(d, self.createmode)
854 if atomictemp:
856 if atomictemp:
855 return atomictempfile(f, mode, self.createmode)
857 return atomictempfile(f, mode, self.createmode)
856 if nlink > 1:
858 if nlink > 1:
857 rename(mktempcopy(f), f)
859 rename(mktempcopy(f), f)
858 fp = posixfile(f, mode)
860 fp = posixfile(f, mode)
859 if nlink == 0:
861 if nlink == 0:
860 self._fixfilemode(f)
862 self._fixfilemode(f)
861 return fp
863 return fp
862
864
863 def symlink(self, src, dst):
865 def symlink(self, src, dst):
864 self.audit_path(dst)
866 self.audit_path(dst)
865 linkname = os.path.join(self.base, dst)
867 linkname = os.path.join(self.base, dst)
866 try:
868 try:
867 os.unlink(linkname)
869 os.unlink(linkname)
868 except OSError:
870 except OSError:
869 pass
871 pass
870
872
871 dirname = os.path.dirname(linkname)
873 dirname = os.path.dirname(linkname)
872 if not os.path.exists(dirname):
874 if not os.path.exists(dirname):
873 makedirs(dirname, self.createmode)
875 makedirs(dirname, self.createmode)
874
876
875 if self._can_symlink:
877 if self._can_symlink:
876 try:
878 try:
877 os.symlink(src, linkname)
879 os.symlink(src, linkname)
878 except OSError, err:
880 except OSError, err:
879 raise OSError(err.errno, _('could not symlink to %r: %s') %
881 raise OSError(err.errno, _('could not symlink to %r: %s') %
880 (src, err.strerror), linkname)
882 (src, err.strerror), linkname)
881 else:
883 else:
882 f = self(dst, "w")
884 f = self(dst, "w")
883 f.write(src)
885 f.write(src)
884 f.close()
886 f.close()
885 self._fixfilemode(dst)
887 self._fixfilemode(dst)
886
888
887 class chunkbuffer(object):
889 class chunkbuffer(object):
888 """Allow arbitrary sized chunks of data to be efficiently read from an
890 """Allow arbitrary sized chunks of data to be efficiently read from an
889 iterator over chunks of arbitrary size."""
891 iterator over chunks of arbitrary size."""
890
892
891 def __init__(self, in_iter):
893 def __init__(self, in_iter):
892 """in_iter is the iterator that's iterating over the input chunks.
894 """in_iter is the iterator that's iterating over the input chunks.
893 targetsize is how big a buffer to try to maintain."""
895 targetsize is how big a buffer to try to maintain."""
894 self.iter = iter(in_iter)
896 self.iter = iter(in_iter)
895 self.buf = ''
897 self.buf = ''
896 self.targetsize = 2**16
898 self.targetsize = 2**16
897
899
898 def read(self, l):
900 def read(self, l):
899 """Read L bytes of data from the iterator of chunks of data.
901 """Read L bytes of data from the iterator of chunks of data.
900 Returns less than L bytes if the iterator runs dry."""
902 Returns less than L bytes if the iterator runs dry."""
901 if l > len(self.buf) and self.iter:
903 if l > len(self.buf) and self.iter:
902 # Clamp to a multiple of self.targetsize
904 # Clamp to a multiple of self.targetsize
903 targetsize = max(l, self.targetsize)
905 targetsize = max(l, self.targetsize)
904 collector = cStringIO.StringIO()
906 collector = cStringIO.StringIO()
905 collector.write(self.buf)
907 collector.write(self.buf)
906 collected = len(self.buf)
908 collected = len(self.buf)
907 for chunk in self.iter:
909 for chunk in self.iter:
908 collector.write(chunk)
910 collector.write(chunk)
909 collected += len(chunk)
911 collected += len(chunk)
910 if collected >= targetsize:
912 if collected >= targetsize:
911 break
913 break
912 if collected < targetsize:
914 if collected < targetsize:
913 self.iter = False
915 self.iter = False
914 self.buf = collector.getvalue()
916 self.buf = collector.getvalue()
915 if len(self.buf) == l:
917 if len(self.buf) == l:
916 s, self.buf = str(self.buf), ''
918 s, self.buf = str(self.buf), ''
917 else:
919 else:
918 s, self.buf = self.buf[:l], buffer(self.buf, l)
920 s, self.buf = self.buf[:l], buffer(self.buf, l)
919 return s
921 return s
920
922
921 def filechunkiter(f, size=65536, limit=None):
923 def filechunkiter(f, size=65536, limit=None):
922 """Create a generator that produces the data in the file size
924 """Create a generator that produces the data in the file size
923 (default 65536) bytes at a time, up to optional limit (default is
925 (default 65536) bytes at a time, up to optional limit (default is
924 to read all data). Chunks may be less than size bytes if the
926 to read all data). Chunks may be less than size bytes if the
925 chunk is the last chunk in the file, or the file is a socket or
927 chunk is the last chunk in the file, or the file is a socket or
926 some other type of file that sometimes reads less data than is
928 some other type of file that sometimes reads less data than is
927 requested."""
929 requested."""
928 assert size >= 0
930 assert size >= 0
929 assert limit is None or limit >= 0
931 assert limit is None or limit >= 0
930 while True:
932 while True:
931 if limit is None:
933 if limit is None:
932 nbytes = size
934 nbytes = size
933 else:
935 else:
934 nbytes = min(limit, size)
936 nbytes = min(limit, size)
935 s = nbytes and f.read(nbytes)
937 s = nbytes and f.read(nbytes)
936 if not s:
938 if not s:
937 break
939 break
938 if limit:
940 if limit:
939 limit -= len(s)
941 limit -= len(s)
940 yield s
942 yield s
941
943
942 def makedate():
944 def makedate():
943 lt = time.localtime()
945 lt = time.localtime()
944 if lt[8] == 1 and time.daylight:
946 if lt[8] == 1 and time.daylight:
945 tz = time.altzone
947 tz = time.altzone
946 else:
948 else:
947 tz = time.timezone
949 tz = time.timezone
948 return time.mktime(lt), tz
950 return time.mktime(lt), tz
949
951
950 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
952 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
951 """represent a (unixtime, offset) tuple as a localized time.
953 """represent a (unixtime, offset) tuple as a localized time.
952 unixtime is seconds since the epoch, and offset is the time zone's
954 unixtime is seconds since the epoch, and offset is the time zone's
953 number of seconds away from UTC. if timezone is false, do not
955 number of seconds away from UTC. if timezone is false, do not
954 append time zone to string."""
956 append time zone to string."""
955 t, tz = date or makedate()
957 t, tz = date or makedate()
956 if "%1" in format or "%2" in format:
958 if "%1" in format or "%2" in format:
957 sign = (tz > 0) and "-" or "+"
959 sign = (tz > 0) and "-" or "+"
958 minutes = abs(tz) // 60
960 minutes = abs(tz) // 60
959 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
961 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
960 format = format.replace("%2", "%02d" % (minutes % 60))
962 format = format.replace("%2", "%02d" % (minutes % 60))
961 s = time.strftime(format, time.gmtime(float(t) - tz))
963 s = time.strftime(format, time.gmtime(float(t) - tz))
962 return s
964 return s
963
965
964 def shortdate(date=None):
966 def shortdate(date=None):
965 """turn (timestamp, tzoff) tuple into iso 8631 date."""
967 """turn (timestamp, tzoff) tuple into iso 8631 date."""
966 return datestr(date, format='%Y-%m-%d')
968 return datestr(date, format='%Y-%m-%d')
967
969
968 def strdate(string, format, defaults=[]):
970 def strdate(string, format, defaults=[]):
969 """parse a localized time string and return a (unixtime, offset) tuple.
971 """parse a localized time string and return a (unixtime, offset) tuple.
970 if the string cannot be parsed, ValueError is raised."""
972 if the string cannot be parsed, ValueError is raised."""
971 def timezone(string):
973 def timezone(string):
972 tz = string.split()[-1]
974 tz = string.split()[-1]
973 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
975 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
974 sign = (tz[0] == "+") and 1 or -1
976 sign = (tz[0] == "+") and 1 or -1
975 hours = int(tz[1:3])
977 hours = int(tz[1:3])
976 minutes = int(tz[3:5])
978 minutes = int(tz[3:5])
977 return -sign * (hours * 60 + minutes) * 60
979 return -sign * (hours * 60 + minutes) * 60
978 if tz == "GMT" or tz == "UTC":
980 if tz == "GMT" or tz == "UTC":
979 return 0
981 return 0
980 return None
982 return None
981
983
982 # NOTE: unixtime = localunixtime + offset
984 # NOTE: unixtime = localunixtime + offset
983 offset, date = timezone(string), string
985 offset, date = timezone(string), string
984 if offset != None:
986 if offset != None:
985 date = " ".join(string.split()[:-1])
987 date = " ".join(string.split()[:-1])
986
988
987 # add missing elements from defaults
989 # add missing elements from defaults
988 for part in defaults:
990 for part in defaults:
989 found = [True for p in part if ("%"+p) in format]
991 found = [True for p in part if ("%"+p) in format]
990 if not found:
992 if not found:
991 date += "@" + defaults[part]
993 date += "@" + defaults[part]
992 format += "@%" + part[0]
994 format += "@%" + part[0]
993
995
994 timetuple = time.strptime(date, format)
996 timetuple = time.strptime(date, format)
995 localunixtime = int(calendar.timegm(timetuple))
997 localunixtime = int(calendar.timegm(timetuple))
996 if offset is None:
998 if offset is None:
997 # local timezone
999 # local timezone
998 unixtime = int(time.mktime(timetuple))
1000 unixtime = int(time.mktime(timetuple))
999 offset = unixtime - localunixtime
1001 offset = unixtime - localunixtime
1000 else:
1002 else:
1001 unixtime = localunixtime + offset
1003 unixtime = localunixtime + offset
1002 return unixtime, offset
1004 return unixtime, offset
1003
1005
1004 def parsedate(date, formats=None, defaults=None):
1006 def parsedate(date, formats=None, defaults=None):
1005 """parse a localized date/time string and return a (unixtime, offset) tuple.
1007 """parse a localized date/time string and return a (unixtime, offset) tuple.
1006
1008
1007 The date may be a "unixtime offset" string or in one of the specified
1009 The date may be a "unixtime offset" string or in one of the specified
1008 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1010 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1009 """
1011 """
1010 if not date:
1012 if not date:
1011 return 0, 0
1013 return 0, 0
1012 if isinstance(date, tuple) and len(date) == 2:
1014 if isinstance(date, tuple) and len(date) == 2:
1013 return date
1015 return date
1014 if not formats:
1016 if not formats:
1015 formats = defaultdateformats
1017 formats = defaultdateformats
1016 date = date.strip()
1018 date = date.strip()
1017 try:
1019 try:
1018 when, offset = map(int, date.split(' '))
1020 when, offset = map(int, date.split(' '))
1019 except ValueError:
1021 except ValueError:
1020 # fill out defaults
1022 # fill out defaults
1021 if not defaults:
1023 if not defaults:
1022 defaults = {}
1024 defaults = {}
1023 now = makedate()
1025 now = makedate()
1024 for part in "d mb yY HI M S".split():
1026 for part in "d mb yY HI M S".split():
1025 if part not in defaults:
1027 if part not in defaults:
1026 if part[0] in "HMS":
1028 if part[0] in "HMS":
1027 defaults[part] = "00"
1029 defaults[part] = "00"
1028 else:
1030 else:
1029 defaults[part] = datestr(now, "%" + part[0])
1031 defaults[part] = datestr(now, "%" + part[0])
1030
1032
1031 for format in formats:
1033 for format in formats:
1032 try:
1034 try:
1033 when, offset = strdate(date, format, defaults)
1035 when, offset = strdate(date, format, defaults)
1034 except (ValueError, OverflowError):
1036 except (ValueError, OverflowError):
1035 pass
1037 pass
1036 else:
1038 else:
1037 break
1039 break
1038 else:
1040 else:
1039 raise Abort(_('invalid date: %r ') % date)
1041 raise Abort(_('invalid date: %r ') % date)
1040 # validate explicit (probably user-specified) date and
1042 # validate explicit (probably user-specified) date and
1041 # time zone offset. values must fit in signed 32 bits for
1043 # time zone offset. values must fit in signed 32 bits for
1042 # current 32-bit linux runtimes. timezones go from UTC-12
1044 # current 32-bit linux runtimes. timezones go from UTC-12
1043 # to UTC+14
1045 # to UTC+14
1044 if abs(when) > 0x7fffffff:
1046 if abs(when) > 0x7fffffff:
1045 raise Abort(_('date exceeds 32 bits: %d') % when)
1047 raise Abort(_('date exceeds 32 bits: %d') % when)
1046 if offset < -50400 or offset > 43200:
1048 if offset < -50400 or offset > 43200:
1047 raise Abort(_('impossible time zone offset: %d') % offset)
1049 raise Abort(_('impossible time zone offset: %d') % offset)
1048 return when, offset
1050 return when, offset
1049
1051
1050 def matchdate(date):
1052 def matchdate(date):
1051 """Return a function that matches a given date match specifier
1053 """Return a function that matches a given date match specifier
1052
1054
1053 Formats include:
1055 Formats include:
1054
1056
1055 '{date}' match a given date to the accuracy provided
1057 '{date}' match a given date to the accuracy provided
1056
1058
1057 '<{date}' on or before a given date
1059 '<{date}' on or before a given date
1058
1060
1059 '>{date}' on or after a given date
1061 '>{date}' on or after a given date
1060
1062
1061 """
1063 """
1062
1064
1063 def lower(date):
1065 def lower(date):
1064 d = dict(mb="1", d="1")
1066 d = dict(mb="1", d="1")
1065 return parsedate(date, extendeddateformats, d)[0]
1067 return parsedate(date, extendeddateformats, d)[0]
1066
1068
1067 def upper(date):
1069 def upper(date):
1068 d = dict(mb="12", HI="23", M="59", S="59")
1070 d = dict(mb="12", HI="23", M="59", S="59")
1069 for days in "31 30 29".split():
1071 for days in "31 30 29".split():
1070 try:
1072 try:
1071 d["d"] = days
1073 d["d"] = days
1072 return parsedate(date, extendeddateformats, d)[0]
1074 return parsedate(date, extendeddateformats, d)[0]
1073 except:
1075 except:
1074 pass
1076 pass
1075 d["d"] = "28"
1077 d["d"] = "28"
1076 return parsedate(date, extendeddateformats, d)[0]
1078 return parsedate(date, extendeddateformats, d)[0]
1077
1079
1078 date = date.strip()
1080 date = date.strip()
1079 if date[0] == "<":
1081 if date[0] == "<":
1080 when = upper(date[1:])
1082 when = upper(date[1:])
1081 return lambda x: x <= when
1083 return lambda x: x <= when
1082 elif date[0] == ">":
1084 elif date[0] == ">":
1083 when = lower(date[1:])
1085 when = lower(date[1:])
1084 return lambda x: x >= when
1086 return lambda x: x >= when
1085 elif date[0] == "-":
1087 elif date[0] == "-":
1086 try:
1088 try:
1087 days = int(date[1:])
1089 days = int(date[1:])
1088 except ValueError:
1090 except ValueError:
1089 raise Abort(_("invalid day spec: %s") % date[1:])
1091 raise Abort(_("invalid day spec: %s") % date[1:])
1090 when = makedate()[0] - days * 3600 * 24
1092 when = makedate()[0] - days * 3600 * 24
1091 return lambda x: x >= when
1093 return lambda x: x >= when
1092 elif " to " in date:
1094 elif " to " in date:
1093 a, b = date.split(" to ")
1095 a, b = date.split(" to ")
1094 start, stop = lower(a), upper(b)
1096 start, stop = lower(a), upper(b)
1095 return lambda x: x >= start and x <= stop
1097 return lambda x: x >= start and x <= stop
1096 else:
1098 else:
1097 start, stop = lower(date), upper(date)
1099 start, stop = lower(date), upper(date)
1098 return lambda x: x >= start and x <= stop
1100 return lambda x: x >= start and x <= stop
1099
1101
1100 def shortuser(user):
1102 def shortuser(user):
1101 """Return a short representation of a user name or email address."""
1103 """Return a short representation of a user name or email address."""
1102 f = user.find('@')
1104 f = user.find('@')
1103 if f >= 0:
1105 if f >= 0:
1104 user = user[:f]
1106 user = user[:f]
1105 f = user.find('<')
1107 f = user.find('<')
1106 if f >= 0:
1108 if f >= 0:
1107 user = user[f + 1:]
1109 user = user[f + 1:]
1108 f = user.find(' ')
1110 f = user.find(' ')
1109 if f >= 0:
1111 if f >= 0:
1110 user = user[:f]
1112 user = user[:f]
1111 f = user.find('.')
1113 f = user.find('.')
1112 if f >= 0:
1114 if f >= 0:
1113 user = user[:f]
1115 user = user[:f]
1114 return user
1116 return user
1115
1117
1116 def email(author):
1118 def email(author):
1117 '''get email of author.'''
1119 '''get email of author.'''
1118 r = author.find('>')
1120 r = author.find('>')
1119 if r == -1:
1121 if r == -1:
1120 r = None
1122 r = None
1121 return author[author.find('<') + 1:r]
1123 return author[author.find('<') + 1:r]
1122
1124
1123 def ellipsis(text, maxlength=400):
1125 def ellipsis(text, maxlength=400):
1124 """Trim string to at most maxlength (default: 400) characters."""
1126 """Trim string to at most maxlength (default: 400) characters."""
1125 if len(text) <= maxlength:
1127 if len(text) <= maxlength:
1126 return text
1128 return text
1127 else:
1129 else:
1128 return "%s..." % (text[:maxlength - 3])
1130 return "%s..." % (text[:maxlength - 3])
1129
1131
1130 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1132 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1131 '''yield every hg repository under path, recursively.'''
1133 '''yield every hg repository under path, recursively.'''
1132 def errhandler(err):
1134 def errhandler(err):
1133 if err.filename == path:
1135 if err.filename == path:
1134 raise err
1136 raise err
1135 if followsym and hasattr(os.path, 'samestat'):
1137 if followsym and hasattr(os.path, 'samestat'):
1136 def _add_dir_if_not_there(dirlst, dirname):
1138 def _add_dir_if_not_there(dirlst, dirname):
1137 match = False
1139 match = False
1138 samestat = os.path.samestat
1140 samestat = os.path.samestat
1139 dirstat = os.stat(dirname)
1141 dirstat = os.stat(dirname)
1140 for lstdirstat in dirlst:
1142 for lstdirstat in dirlst:
1141 if samestat(dirstat, lstdirstat):
1143 if samestat(dirstat, lstdirstat):
1142 match = True
1144 match = True
1143 break
1145 break
1144 if not match:
1146 if not match:
1145 dirlst.append(dirstat)
1147 dirlst.append(dirstat)
1146 return not match
1148 return not match
1147 else:
1149 else:
1148 followsym = False
1150 followsym = False
1149
1151
1150 if (seen_dirs is None) and followsym:
1152 if (seen_dirs is None) and followsym:
1151 seen_dirs = []
1153 seen_dirs = []
1152 _add_dir_if_not_there(seen_dirs, path)
1154 _add_dir_if_not_there(seen_dirs, path)
1153 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1155 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1154 dirs.sort()
1156 dirs.sort()
1155 if '.hg' in dirs:
1157 if '.hg' in dirs:
1156 yield root # found a repository
1158 yield root # found a repository
1157 qroot = os.path.join(root, '.hg', 'patches')
1159 qroot = os.path.join(root, '.hg', 'patches')
1158 if os.path.isdir(os.path.join(qroot, '.hg')):
1160 if os.path.isdir(os.path.join(qroot, '.hg')):
1159 yield qroot # we have a patch queue repo here
1161 yield qroot # we have a patch queue repo here
1160 if recurse:
1162 if recurse:
1161 # avoid recursing inside the .hg directory
1163 # avoid recursing inside the .hg directory
1162 dirs.remove('.hg')
1164 dirs.remove('.hg')
1163 else:
1165 else:
1164 dirs[:] = [] # don't descend further
1166 dirs[:] = [] # don't descend further
1165 elif followsym:
1167 elif followsym:
1166 newdirs = []
1168 newdirs = []
1167 for d in dirs:
1169 for d in dirs:
1168 fname = os.path.join(root, d)
1170 fname = os.path.join(root, d)
1169 if _add_dir_if_not_there(seen_dirs, fname):
1171 if _add_dir_if_not_there(seen_dirs, fname):
1170 if os.path.islink(fname):
1172 if os.path.islink(fname):
1171 for hgname in walkrepos(fname, True, seen_dirs):
1173 for hgname in walkrepos(fname, True, seen_dirs):
1172 yield hgname
1174 yield hgname
1173 else:
1175 else:
1174 newdirs.append(d)
1176 newdirs.append(d)
1175 dirs[:] = newdirs
1177 dirs[:] = newdirs
1176
1178
1177 _rcpath = None
1179 _rcpath = None
1178
1180
1179 def os_rcpath():
1181 def os_rcpath():
1180 '''return default os-specific hgrc search path'''
1182 '''return default os-specific hgrc search path'''
1181 path = system_rcpath()
1183 path = system_rcpath()
1182 path.extend(user_rcpath())
1184 path.extend(user_rcpath())
1183 path = [os.path.normpath(f) for f in path]
1185 path = [os.path.normpath(f) for f in path]
1184 return path
1186 return path
1185
1187
1186 def rcpath():
1188 def rcpath():
1187 '''return hgrc search path. if env var HGRCPATH is set, use it.
1189 '''return hgrc search path. if env var HGRCPATH is set, use it.
1188 for each item in path, if directory, use files ending in .rc,
1190 for each item in path, if directory, use files ending in .rc,
1189 else use item.
1191 else use item.
1190 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1192 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1191 if no HGRCPATH, use default os-specific path.'''
1193 if no HGRCPATH, use default os-specific path.'''
1192 global _rcpath
1194 global _rcpath
1193 if _rcpath is None:
1195 if _rcpath is None:
1194 if 'HGRCPATH' in os.environ:
1196 if 'HGRCPATH' in os.environ:
1195 _rcpath = []
1197 _rcpath = []
1196 for p in os.environ['HGRCPATH'].split(os.pathsep):
1198 for p in os.environ['HGRCPATH'].split(os.pathsep):
1197 if not p:
1199 if not p:
1198 continue
1200 continue
1199 p = expandpath(p)
1201 p = expandpath(p)
1200 if os.path.isdir(p):
1202 if os.path.isdir(p):
1201 for f, kind in osutil.listdir(p):
1203 for f, kind in osutil.listdir(p):
1202 if f.endswith('.rc'):
1204 if f.endswith('.rc'):
1203 _rcpath.append(os.path.join(p, f))
1205 _rcpath.append(os.path.join(p, f))
1204 else:
1206 else:
1205 _rcpath.append(p)
1207 _rcpath.append(p)
1206 else:
1208 else:
1207 _rcpath = os_rcpath()
1209 _rcpath = os_rcpath()
1208 return _rcpath
1210 return _rcpath
1209
1211
1210 def bytecount(nbytes):
1212 def bytecount(nbytes):
1211 '''return byte count formatted as readable string, with units'''
1213 '''return byte count formatted as readable string, with units'''
1212
1214
1213 units = (
1215 units = (
1214 (100, 1 << 30, _('%.0f GB')),
1216 (100, 1 << 30, _('%.0f GB')),
1215 (10, 1 << 30, _('%.1f GB')),
1217 (10, 1 << 30, _('%.1f GB')),
1216 (1, 1 << 30, _('%.2f GB')),
1218 (1, 1 << 30, _('%.2f GB')),
1217 (100, 1 << 20, _('%.0f MB')),
1219 (100, 1 << 20, _('%.0f MB')),
1218 (10, 1 << 20, _('%.1f MB')),
1220 (10, 1 << 20, _('%.1f MB')),
1219 (1, 1 << 20, _('%.2f MB')),
1221 (1, 1 << 20, _('%.2f MB')),
1220 (100, 1 << 10, _('%.0f KB')),
1222 (100, 1 << 10, _('%.0f KB')),
1221 (10, 1 << 10, _('%.1f KB')),
1223 (10, 1 << 10, _('%.1f KB')),
1222 (1, 1 << 10, _('%.2f KB')),
1224 (1, 1 << 10, _('%.2f KB')),
1223 (1, 1, _('%.0f bytes')),
1225 (1, 1, _('%.0f bytes')),
1224 )
1226 )
1225
1227
1226 for multiplier, divisor, format in units:
1228 for multiplier, divisor, format in units:
1227 if nbytes >= divisor * multiplier:
1229 if nbytes >= divisor * multiplier:
1228 return format % (nbytes / float(divisor))
1230 return format % (nbytes / float(divisor))
1229 return units[-1][2] % nbytes
1231 return units[-1][2] % nbytes
1230
1232
1231 def drop_scheme(scheme, path):
1233 def drop_scheme(scheme, path):
1232 sc = scheme + ':'
1234 sc = scheme + ':'
1233 if path.startswith(sc):
1235 if path.startswith(sc):
1234 path = path[len(sc):]
1236 path = path[len(sc):]
1235 if path.startswith('//'):
1237 if path.startswith('//'):
1236 if scheme == 'file':
1238 if scheme == 'file':
1237 i = path.find('/', 2)
1239 i = path.find('/', 2)
1238 if i == -1:
1240 if i == -1:
1239 return ''
1241 return ''
1240 # On Windows, absolute paths are rooted at the current drive
1242 # On Windows, absolute paths are rooted at the current drive
1241 # root. On POSIX they are rooted at the file system root.
1243 # root. On POSIX they are rooted at the file system root.
1242 if os.name == 'nt':
1244 if os.name == 'nt':
1243 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1245 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1244 path = os.path.join(droot, path[i + 1:])
1246 path = os.path.join(droot, path[i + 1:])
1245 else:
1247 else:
1246 path = path[i:]
1248 path = path[i:]
1247 else:
1249 else:
1248 path = path[2:]
1250 path = path[2:]
1249 return path
1251 return path
1250
1252
1251 def uirepr(s):
1253 def uirepr(s):
1252 # Avoid double backslash in Windows path repr()
1254 # Avoid double backslash in Windows path repr()
1253 return repr(s).replace('\\\\', '\\')
1255 return repr(s).replace('\\\\', '\\')
1254
1256
1255 def wrap(line, hangindent, width=None):
1257 def wrap(line, hangindent, width=None):
1256 if width is None:
1258 if width is None:
1257 width = termwidth() - 2
1259 width = termwidth() - 2
1258 if width <= hangindent:
1260 if width <= hangindent:
1259 # adjust for weird terminal size
1261 # adjust for weird terminal size
1260 width = max(78, hangindent + 1)
1262 width = max(78, hangindent + 1)
1261 padding = '\n' + ' ' * hangindent
1263 padding = '\n' + ' ' * hangindent
1262 # To avoid corrupting multi-byte characters in line, we must wrap
1264 # To avoid corrupting multi-byte characters in line, we must wrap
1263 # a Unicode string instead of a bytestring.
1265 # a Unicode string instead of a bytestring.
1264 try:
1266 try:
1265 u = line.decode(encoding.encoding)
1267 u = line.decode(encoding.encoding)
1266 w = padding.join(textwrap.wrap(u, width=width - hangindent))
1268 w = padding.join(textwrap.wrap(u, width=width - hangindent))
1267 return w.encode(encoding.encoding)
1269 return w.encode(encoding.encoding)
1268 except UnicodeDecodeError:
1270 except UnicodeDecodeError:
1269 return padding.join(textwrap.wrap(line, width=width - hangindent))
1271 return padding.join(textwrap.wrap(line, width=width - hangindent))
1270
1272
1271 def iterlines(iterator):
1273 def iterlines(iterator):
1272 for chunk in iterator:
1274 for chunk in iterator:
1273 for line in chunk.splitlines():
1275 for line in chunk.splitlines():
1274 yield line
1276 yield line
1275
1277
1276 def expandpath(path):
1278 def expandpath(path):
1277 return os.path.expanduser(os.path.expandvars(path))
1279 return os.path.expanduser(os.path.expandvars(path))
1278
1280
1279 def hgcmd():
1281 def hgcmd():
1280 """Return the command used to execute current hg
1282 """Return the command used to execute current hg
1281
1283
1282 This is different from hgexecutable() because on Windows we want
1284 This is different from hgexecutable() because on Windows we want
1283 to avoid things opening new shell windows like batch files, so we
1285 to avoid things opening new shell windows like batch files, so we
1284 get either the python call or current executable.
1286 get either the python call or current executable.
1285 """
1287 """
1286 if main_is_frozen():
1288 if main_is_frozen():
1287 return [sys.executable]
1289 return [sys.executable]
1288 return gethgcmd()
1290 return gethgcmd()
1289
1291
1290 def rundetached(args, condfn):
1292 def rundetached(args, condfn):
1291 """Execute the argument list in a detached process.
1293 """Execute the argument list in a detached process.
1292
1294
1293 condfn is a callable which is called repeatedly and should return
1295 condfn is a callable which is called repeatedly and should return
1294 True once the child process is known to have started successfully.
1296 True once the child process is known to have started successfully.
1295 At this point, the child process PID is returned. If the child
1297 At this point, the child process PID is returned. If the child
1296 process fails to start or finishes before condfn() evaluates to
1298 process fails to start or finishes before condfn() evaluates to
1297 True, return -1.
1299 True, return -1.
1298 """
1300 """
1299 # Windows case is easier because the child process is either
1301 # Windows case is easier because the child process is either
1300 # successfully starting and validating the condition or exiting
1302 # successfully starting and validating the condition or exiting
1301 # on failure. We just poll on its PID. On Unix, if the child
1303 # on failure. We just poll on its PID. On Unix, if the child
1302 # process fails to start, it will be left in a zombie state until
1304 # process fails to start, it will be left in a zombie state until
1303 # the parent wait on it, which we cannot do since we expect a long
1305 # the parent wait on it, which we cannot do since we expect a long
1304 # running process on success. Instead we listen for SIGCHLD telling
1306 # running process on success. Instead we listen for SIGCHLD telling
1305 # us our child process terminated.
1307 # us our child process terminated.
1306 terminated = set()
1308 terminated = set()
1307 def handler(signum, frame):
1309 def handler(signum, frame):
1308 terminated.add(os.wait())
1310 terminated.add(os.wait())
1309 prevhandler = None
1311 prevhandler = None
1310 if hasattr(signal, 'SIGCHLD'):
1312 if hasattr(signal, 'SIGCHLD'):
1311 prevhandler = signal.signal(signal.SIGCHLD, handler)
1313 prevhandler = signal.signal(signal.SIGCHLD, handler)
1312 try:
1314 try:
1313 pid = spawndetached(args)
1315 pid = spawndetached(args)
1314 while not condfn():
1316 while not condfn():
1315 if ((pid in terminated or not testpid(pid))
1317 if ((pid in terminated or not testpid(pid))
1316 and not condfn()):
1318 and not condfn()):
1317 return -1
1319 return -1
1318 time.sleep(0.1)
1320 time.sleep(0.1)
1319 return pid
1321 return pid
1320 finally:
1322 finally:
1321 if prevhandler is not None:
1323 if prevhandler is not None:
1322 signal.signal(signal.SIGCHLD, prevhandler)
1324 signal.signal(signal.SIGCHLD, prevhandler)
1323
1325
1324 try:
1326 try:
1325 any, all = any, all
1327 any, all = any, all
1326 except NameError:
1328 except NameError:
1327 def any(iterable):
1329 def any(iterable):
1328 for i in iterable:
1330 for i in iterable:
1329 if i:
1331 if i:
1330 return True
1332 return True
1331 return False
1333 return False
1332
1334
1333 def all(iterable):
1335 def all(iterable):
1334 for i in iterable:
1336 for i in iterable:
1335 if not i:
1337 if not i:
1336 return False
1338 return False
1337 return True
1339 return True
1338
1340
1339 def termwidth():
1341 def termwidth():
1340 if 'COLUMNS' in os.environ:
1342 if 'COLUMNS' in os.environ:
1341 try:
1343 try:
1342 return int(os.environ['COLUMNS'])
1344 return int(os.environ['COLUMNS'])
1343 except ValueError:
1345 except ValueError:
1344 pass
1346 pass
1345 return termwidth_()
1347 return termwidth_()
General Comments 0
You need to be logged in to leave comments. Login now