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