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