##// END OF EJS Templates
checkwinfilename: use %r in format string
Adrian Buehlmann -
r13947:d2d1ef6a default
parent child Browse files
Show More
@@ -1,1585 +1,1585 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, socket
20 import imp, socket
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 if sys.version_info >= (2, 5):
31 if sys.version_info >= (2, 5):
32 from hashlib import sha1 as _sha1
32 from hashlib import sha1 as _sha1
33 else:
33 else:
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 fp = open(outname, 'rb')
201 fp = open(outname, 'rb')
202 r = fp.read()
202 r = fp.read()
203 fp.close()
203 fp.close()
204 return r
204 return r
205 finally:
205 finally:
206 try:
206 try:
207 if inname:
207 if inname:
208 os.unlink(inname)
208 os.unlink(inname)
209 except:
209 except:
210 pass
210 pass
211 try:
211 try:
212 if outname:
212 if outname:
213 os.unlink(outname)
213 os.unlink(outname)
214 except:
214 except:
215 pass
215 pass
216
216
217 filtertable = {
217 filtertable = {
218 'tempfile:': tempfilter,
218 'tempfile:': tempfilter,
219 'pipe:': pipefilter,
219 'pipe:': pipefilter,
220 }
220 }
221
221
222 def filter(s, cmd):
222 def filter(s, cmd):
223 "filter a string through a command that transforms its input to its output"
223 "filter a string through a command that transforms its input to its output"
224 for name, fn in filtertable.iteritems():
224 for name, fn in filtertable.iteritems():
225 if cmd.startswith(name):
225 if cmd.startswith(name):
226 return fn(s, cmd[len(name):].lstrip())
226 return fn(s, cmd[len(name):].lstrip())
227 return pipefilter(s, cmd)
227 return pipefilter(s, cmd)
228
228
229 def binary(s):
229 def binary(s):
230 """return true if a string is binary data"""
230 """return true if a string is binary data"""
231 return bool(s and '\0' in s)
231 return bool(s and '\0' in s)
232
232
233 def increasingchunks(source, min=1024, max=65536):
233 def increasingchunks(source, min=1024, max=65536):
234 '''return no less than min bytes per chunk while data remains,
234 '''return no less than min bytes per chunk while data remains,
235 doubling min after each chunk until it reaches max'''
235 doubling min after each chunk until it reaches max'''
236 def log2(x):
236 def log2(x):
237 if not x:
237 if not x:
238 return 0
238 return 0
239 i = 0
239 i = 0
240 while x:
240 while x:
241 x >>= 1
241 x >>= 1
242 i += 1
242 i += 1
243 return i - 1
243 return i - 1
244
244
245 buf = []
245 buf = []
246 blen = 0
246 blen = 0
247 for chunk in source:
247 for chunk in source:
248 buf.append(chunk)
248 buf.append(chunk)
249 blen += len(chunk)
249 blen += len(chunk)
250 if blen >= min:
250 if blen >= min:
251 if min < max:
251 if min < max:
252 min = min << 1
252 min = min << 1
253 nmin = 1 << log2(blen)
253 nmin = 1 << log2(blen)
254 if nmin > min:
254 if nmin > min:
255 min = nmin
255 min = nmin
256 if min > max:
256 if min > max:
257 min = max
257 min = max
258 yield ''.join(buf)
258 yield ''.join(buf)
259 blen = 0
259 blen = 0
260 buf = []
260 buf = []
261 if buf:
261 if buf:
262 yield ''.join(buf)
262 yield ''.join(buf)
263
263
264 Abort = error.Abort
264 Abort = error.Abort
265
265
266 def always(fn):
266 def always(fn):
267 return True
267 return True
268
268
269 def never(fn):
269 def never(fn):
270 return False
270 return False
271
271
272 def pathto(root, n1, n2):
272 def pathto(root, n1, n2):
273 '''return the relative path from one place to another.
273 '''return the relative path from one place to another.
274 root should use os.sep to separate directories
274 root should use os.sep to separate directories
275 n1 should use os.sep to separate directories
275 n1 should use os.sep to separate directories
276 n2 should use "/" to separate directories
276 n2 should use "/" to separate directories
277 returns an os.sep-separated path.
277 returns an os.sep-separated path.
278
278
279 If n1 is a relative path, it's assumed it's
279 If n1 is a relative path, it's assumed it's
280 relative to root.
280 relative to root.
281 n2 should always be relative to root.
281 n2 should always be relative to root.
282 '''
282 '''
283 if not n1:
283 if not n1:
284 return localpath(n2)
284 return localpath(n2)
285 if os.path.isabs(n1):
285 if os.path.isabs(n1):
286 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
286 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
287 return os.path.join(root, localpath(n2))
287 return os.path.join(root, localpath(n2))
288 n2 = '/'.join((pconvert(root), n2))
288 n2 = '/'.join((pconvert(root), n2))
289 a, b = splitpath(n1), n2.split('/')
289 a, b = splitpath(n1), n2.split('/')
290 a.reverse()
290 a.reverse()
291 b.reverse()
291 b.reverse()
292 while a and b and a[-1] == b[-1]:
292 while a and b and a[-1] == b[-1]:
293 a.pop()
293 a.pop()
294 b.pop()
294 b.pop()
295 b.reverse()
295 b.reverse()
296 return os.sep.join((['..'] * len(a)) + b) or '.'
296 return os.sep.join((['..'] * len(a)) + b) or '.'
297
297
298 def canonpath(root, cwd, myname, auditor=None):
298 def canonpath(root, cwd, myname, auditor=None):
299 """return the canonical path of myname, given cwd and root"""
299 """return the canonical path of myname, given cwd and root"""
300 if endswithsep(root):
300 if endswithsep(root):
301 rootsep = root
301 rootsep = root
302 else:
302 else:
303 rootsep = root + os.sep
303 rootsep = root + os.sep
304 name = myname
304 name = myname
305 if not os.path.isabs(name):
305 if not os.path.isabs(name):
306 name = os.path.join(root, cwd, name)
306 name = os.path.join(root, cwd, name)
307 name = os.path.normpath(name)
307 name = os.path.normpath(name)
308 if auditor is None:
308 if auditor is None:
309 auditor = path_auditor(root)
309 auditor = path_auditor(root)
310 if name != rootsep and name.startswith(rootsep):
310 if name != rootsep and name.startswith(rootsep):
311 name = name[len(rootsep):]
311 name = name[len(rootsep):]
312 auditor(name)
312 auditor(name)
313 return pconvert(name)
313 return pconvert(name)
314 elif name == root:
314 elif name == root:
315 return ''
315 return ''
316 else:
316 else:
317 # Determine whether `name' is in the hierarchy at or beneath `root',
317 # Determine whether `name' is in the hierarchy at or beneath `root',
318 # by iterating name=dirname(name) until that causes no change (can't
318 # by iterating name=dirname(name) until that causes no change (can't
319 # check name == '/', because that doesn't work on windows). For each
319 # check name == '/', because that doesn't work on windows). For each
320 # `name', compare dev/inode numbers. If they match, the list `rel'
320 # `name', compare dev/inode numbers. If they match, the list `rel'
321 # holds the reversed list of components making up the relative file
321 # holds the reversed list of components making up the relative file
322 # name we want.
322 # name we want.
323 root_st = os.stat(root)
323 root_st = os.stat(root)
324 rel = []
324 rel = []
325 while True:
325 while True:
326 try:
326 try:
327 name_st = os.stat(name)
327 name_st = os.stat(name)
328 except OSError:
328 except OSError:
329 break
329 break
330 if samestat(name_st, root_st):
330 if samestat(name_st, root_st):
331 if not rel:
331 if not rel:
332 # name was actually the same as root (maybe a symlink)
332 # name was actually the same as root (maybe a symlink)
333 return ''
333 return ''
334 rel.reverse()
334 rel.reverse()
335 name = os.path.join(*rel)
335 name = os.path.join(*rel)
336 auditor(name)
336 auditor(name)
337 return pconvert(name)
337 return pconvert(name)
338 dirname, basename = os.path.split(name)
338 dirname, basename = os.path.split(name)
339 rel.append(basename)
339 rel.append(basename)
340 if dirname == name:
340 if dirname == name:
341 break
341 break
342 name = dirname
342 name = dirname
343
343
344 raise Abort('%s not under root' % myname)
344 raise Abort('%s not under root' % myname)
345
345
346 _hgexecutable = None
346 _hgexecutable = None
347
347
348 def main_is_frozen():
348 def main_is_frozen():
349 """return True if we are a frozen executable.
349 """return True if we are a frozen executable.
350
350
351 The code supports py2exe (most common, Windows only) and tools/freeze
351 The code supports py2exe (most common, Windows only) and tools/freeze
352 (portable, not much used).
352 (portable, not much used).
353 """
353 """
354 return (hasattr(sys, "frozen") or # new py2exe
354 return (hasattr(sys, "frozen") or # new py2exe
355 hasattr(sys, "importers") or # old py2exe
355 hasattr(sys, "importers") or # old py2exe
356 imp.is_frozen("__main__")) # tools/freeze
356 imp.is_frozen("__main__")) # tools/freeze
357
357
358 def hgexecutable():
358 def hgexecutable():
359 """return location of the 'hg' executable.
359 """return location of the 'hg' executable.
360
360
361 Defaults to $HG or 'hg' in the search path.
361 Defaults to $HG or 'hg' in the search path.
362 """
362 """
363 if _hgexecutable is None:
363 if _hgexecutable is None:
364 hg = os.environ.get('HG')
364 hg = os.environ.get('HG')
365 if hg:
365 if hg:
366 set_hgexecutable(hg)
366 set_hgexecutable(hg)
367 elif main_is_frozen():
367 elif main_is_frozen():
368 set_hgexecutable(sys.executable)
368 set_hgexecutable(sys.executable)
369 else:
369 else:
370 exe = find_exe('hg') or os.path.basename(sys.argv[0])
370 exe = find_exe('hg') or os.path.basename(sys.argv[0])
371 set_hgexecutable(exe)
371 set_hgexecutable(exe)
372 return _hgexecutable
372 return _hgexecutable
373
373
374 def set_hgexecutable(path):
374 def set_hgexecutable(path):
375 """set location of the 'hg' executable"""
375 """set location of the 'hg' executable"""
376 global _hgexecutable
376 global _hgexecutable
377 _hgexecutable = path
377 _hgexecutable = path
378
378
379 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
379 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
380 '''enhanced shell command execution.
380 '''enhanced shell command execution.
381 run with environment maybe modified, maybe in different dir.
381 run with environment maybe modified, maybe in different dir.
382
382
383 if command fails and onerr is None, return status. if ui object,
383 if command fails and onerr is None, return status. if ui object,
384 print error message and return status, else raise onerr object as
384 print error message and return status, else raise onerr object as
385 exception.
385 exception.
386
386
387 if out is specified, it is assumed to be a file-like object that has a
387 if out is specified, it is assumed to be a file-like object that has a
388 write() method. stdout and stderr will be redirected to out.'''
388 write() method. stdout and stderr will be redirected to out.'''
389 try:
389 try:
390 sys.stdout.flush()
390 sys.stdout.flush()
391 except Exception:
391 except Exception:
392 pass
392 pass
393 def py2shell(val):
393 def py2shell(val):
394 'convert python object into string that is useful to shell'
394 'convert python object into string that is useful to shell'
395 if val is None or val is False:
395 if val is None or val is False:
396 return '0'
396 return '0'
397 if val is True:
397 if val is True:
398 return '1'
398 return '1'
399 return str(val)
399 return str(val)
400 origcmd = cmd
400 origcmd = cmd
401 cmd = quotecommand(cmd)
401 cmd = quotecommand(cmd)
402 env = dict(os.environ)
402 env = dict(os.environ)
403 env.update((k, py2shell(v)) for k, v in environ.iteritems())
403 env.update((k, py2shell(v)) for k, v in environ.iteritems())
404 env['HG'] = hgexecutable()
404 env['HG'] = hgexecutable()
405 if out is None:
405 if out is None:
406 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
406 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
407 env=env, cwd=cwd)
407 env=env, cwd=cwd)
408 else:
408 else:
409 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
409 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
410 env=env, cwd=cwd, stdout=subprocess.PIPE,
410 env=env, cwd=cwd, stdout=subprocess.PIPE,
411 stderr=subprocess.STDOUT)
411 stderr=subprocess.STDOUT)
412 for line in proc.stdout:
412 for line in proc.stdout:
413 out.write(line)
413 out.write(line)
414 proc.wait()
414 proc.wait()
415 rc = proc.returncode
415 rc = proc.returncode
416 if sys.platform == 'OpenVMS' and rc & 1:
416 if sys.platform == 'OpenVMS' and rc & 1:
417 rc = 0
417 rc = 0
418 if rc and onerr:
418 if rc and onerr:
419 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
419 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
420 explain_exit(rc)[0])
420 explain_exit(rc)[0])
421 if errprefix:
421 if errprefix:
422 errmsg = '%s: %s' % (errprefix, errmsg)
422 errmsg = '%s: %s' % (errprefix, errmsg)
423 try:
423 try:
424 onerr.warn(errmsg + '\n')
424 onerr.warn(errmsg + '\n')
425 except AttributeError:
425 except AttributeError:
426 raise onerr(errmsg)
426 raise onerr(errmsg)
427 return rc
427 return rc
428
428
429 def checksignature(func):
429 def checksignature(func):
430 '''wrap a function with code to check for calling errors'''
430 '''wrap a function with code to check for calling errors'''
431 def check(*args, **kwargs):
431 def check(*args, **kwargs):
432 try:
432 try:
433 return func(*args, **kwargs)
433 return func(*args, **kwargs)
434 except TypeError:
434 except TypeError:
435 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
435 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
436 raise error.SignatureError
436 raise error.SignatureError
437 raise
437 raise
438
438
439 return check
439 return check
440
440
441 def makedir(path, notindexed):
441 def makedir(path, notindexed):
442 os.mkdir(path)
442 os.mkdir(path)
443
443
444 def unlinkpath(f):
444 def unlinkpath(f):
445 """unlink and remove the directory if it is empty"""
445 """unlink and remove the directory if it is empty"""
446 os.unlink(f)
446 os.unlink(f)
447 # try removing directories that might now be empty
447 # try removing directories that might now be empty
448 try:
448 try:
449 os.removedirs(os.path.dirname(f))
449 os.removedirs(os.path.dirname(f))
450 except OSError:
450 except OSError:
451 pass
451 pass
452
452
453 def copyfile(src, dest):
453 def copyfile(src, dest):
454 "copy a file, preserving mode and atime/mtime"
454 "copy a file, preserving mode and atime/mtime"
455 if os.path.islink(src):
455 if os.path.islink(src):
456 try:
456 try:
457 os.unlink(dest)
457 os.unlink(dest)
458 except:
458 except:
459 pass
459 pass
460 os.symlink(os.readlink(src), dest)
460 os.symlink(os.readlink(src), dest)
461 else:
461 else:
462 try:
462 try:
463 shutil.copyfile(src, dest)
463 shutil.copyfile(src, dest)
464 shutil.copymode(src, dest)
464 shutil.copymode(src, dest)
465 except shutil.Error, inst:
465 except shutil.Error, inst:
466 raise Abort(str(inst))
466 raise Abort(str(inst))
467
467
468 def copyfiles(src, dst, hardlink=None):
468 def copyfiles(src, dst, hardlink=None):
469 """Copy a directory tree using hardlinks if possible"""
469 """Copy a directory tree using hardlinks if possible"""
470
470
471 if hardlink is None:
471 if hardlink is None:
472 hardlink = (os.stat(src).st_dev ==
472 hardlink = (os.stat(src).st_dev ==
473 os.stat(os.path.dirname(dst)).st_dev)
473 os.stat(os.path.dirname(dst)).st_dev)
474
474
475 num = 0
475 num = 0
476 if os.path.isdir(src):
476 if os.path.isdir(src):
477 os.mkdir(dst)
477 os.mkdir(dst)
478 for name, kind in osutil.listdir(src):
478 for name, kind in osutil.listdir(src):
479 srcname = os.path.join(src, name)
479 srcname = os.path.join(src, name)
480 dstname = os.path.join(dst, name)
480 dstname = os.path.join(dst, name)
481 hardlink, n = copyfiles(srcname, dstname, hardlink)
481 hardlink, n = copyfiles(srcname, dstname, hardlink)
482 num += n
482 num += n
483 else:
483 else:
484 if hardlink:
484 if hardlink:
485 try:
485 try:
486 os_link(src, dst)
486 os_link(src, dst)
487 except (IOError, OSError):
487 except (IOError, OSError):
488 hardlink = False
488 hardlink = False
489 shutil.copy(src, dst)
489 shutil.copy(src, dst)
490 else:
490 else:
491 shutil.copy(src, dst)
491 shutil.copy(src, dst)
492 num += 1
492 num += 1
493
493
494 return hardlink, num
494 return hardlink, num
495
495
496 def checkfilename(f):
496 def checkfilename(f):
497 '''Check that the filename f is an acceptable filename for a tracked file'''
497 '''Check that the filename f is an acceptable filename for a tracked file'''
498 if '\r' in f or '\n' in f:
498 if '\r' in f or '\n' in f:
499 raise Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
499 raise Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
500
500
501 _windows_reserved_filenames = '''con prn aux nul
501 _windows_reserved_filenames = '''con prn aux nul
502 com1 com2 com3 com4 com5 com6 com7 com8 com9
502 com1 com2 com3 com4 com5 com6 com7 com8 com9
503 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
503 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
504 _windows_reserved_chars = ':*?"<>|'
504 _windows_reserved_chars = ':*?"<>|'
505 def checkwinfilename(path):
505 def checkwinfilename(path):
506 '''Check that the base-relative path is a valid filename on Windows.
506 '''Check that the base-relative path is a valid filename on Windows.
507 Returns None if the path is ok, or a UI string describing the problem.
507 Returns None if the path is ok, or a UI string describing the problem.
508
508
509 >>> checkwinfilename("just/a/normal/path")
509 >>> checkwinfilename("just/a/normal/path")
510 >>> checkwinfilename("foo/bar/con.xml")
510 >>> checkwinfilename("foo/bar/con.xml")
511 "filename contains 'con', which is reserved on Windows"
511 "filename contains 'con', which is reserved on Windows"
512 >>> checkwinfilename("foo/con.xml/bar")
512 >>> checkwinfilename("foo/con.xml/bar")
513 "filename contains 'con', which is reserved on Windows"
513 "filename contains 'con', which is reserved on Windows"
514 >>> checkwinfilename("foo/bar/xml.con")
514 >>> checkwinfilename("foo/bar/xml.con")
515 >>> checkwinfilename("foo/bar/AUX/bla.txt")
515 >>> checkwinfilename("foo/bar/AUX/bla.txt")
516 "filename contains 'AUX', which is reserved on Windows"
516 "filename contains 'AUX', which is reserved on Windows"
517 >>> checkwinfilename("foo/bar/bla:.txt")
517 >>> checkwinfilename("foo/bar/bla:.txt")
518 "filename contains ':', which is reserved on Windows"
518 "filename contains ':', which is reserved on Windows"
519 >>> checkwinfilename("foo/bar/b\07la.txt")
519 >>> checkwinfilename("foo/bar/b\07la.txt")
520 "filename contains '\\x07', which is invalid on Windows"
520 "filename contains '\\\\x07', which is invalid on Windows"
521 >>> checkwinfilename("foo/bar/bla ")
521 >>> checkwinfilename("foo/bar/bla ")
522 "filename ends with ' ', which is not allowed on Windows"
522 "filename ends with ' ', which is not allowed on Windows"
523 '''
523 '''
524 for n in path.replace('\\', '/').split('/'):
524 for n in path.replace('\\', '/').split('/'):
525 if not n:
525 if not n:
526 continue
526 continue
527 for c in n:
527 for c in n:
528 if c in _windows_reserved_chars:
528 if c in _windows_reserved_chars:
529 return _("filename contains '%s', which is reserved "
529 return _("filename contains '%s', which is reserved "
530 "on Windows") % c
530 "on Windows") % c
531 if ord(c) <= 31:
531 if ord(c) <= 31:
532 return _("filename contains '%s', which is invalid "
532 return _("filename contains %r, which is invalid "
533 "on Windows") % c
533 "on Windows") % c
534 base = n.split('.')[0]
534 base = n.split('.')[0]
535 if base and base.lower() in _windows_reserved_filenames:
535 if base and base.lower() in _windows_reserved_filenames:
536 return _("filename contains '%s', which is reserved "
536 return _("filename contains '%s', which is reserved "
537 "on Windows") % base
537 "on Windows") % base
538 t = n[-1]
538 t = n[-1]
539 if t in '. ':
539 if t in '. ':
540 return _("filename ends with '%s', which is not allowed "
540 return _("filename ends with '%s', which is not allowed "
541 "on Windows") % t
541 "on Windows") % t
542
542
543 class path_auditor(object):
543 class path_auditor(object):
544 '''ensure that a filesystem path contains no banned components.
544 '''ensure that a filesystem path contains no banned components.
545 the following properties of a path are checked:
545 the following properties of a path are checked:
546
546
547 - ends with a directory separator
547 - ends with a directory separator
548 - under top-level .hg
548 - under top-level .hg
549 - starts at the root of a windows drive
549 - starts at the root of a windows drive
550 - contains ".."
550 - contains ".."
551 - traverses a symlink (e.g. a/symlink_here/b)
551 - traverses a symlink (e.g. a/symlink_here/b)
552 - inside a nested repository (a callback can be used to approve
552 - inside a nested repository (a callback can be used to approve
553 some nested repositories, e.g., subrepositories)
553 some nested repositories, e.g., subrepositories)
554 '''
554 '''
555
555
556 def __init__(self, root, callback=None):
556 def __init__(self, root, callback=None):
557 self.audited = set()
557 self.audited = set()
558 self.auditeddir = set()
558 self.auditeddir = set()
559 self.root = root
559 self.root = root
560 self.callback = callback
560 self.callback = callback
561
561
562 def __call__(self, path):
562 def __call__(self, path):
563 '''Check the relative path.
563 '''Check the relative path.
564 path may contain a pattern (e.g. foodir/**.txt)'''
564 path may contain a pattern (e.g. foodir/**.txt)'''
565
565
566 if path in self.audited:
566 if path in self.audited:
567 return
567 return
568 # AIX ignores "/" at end of path, others raise EISDIR.
568 # AIX ignores "/" at end of path, others raise EISDIR.
569 if endswithsep(path):
569 if endswithsep(path):
570 raise Abort(_("path ends in directory separator: %s") % path)
570 raise Abort(_("path ends in directory separator: %s") % path)
571 normpath = os.path.normcase(path)
571 normpath = os.path.normcase(path)
572 parts = splitpath(normpath)
572 parts = splitpath(normpath)
573 if (os.path.splitdrive(path)[0]
573 if (os.path.splitdrive(path)[0]
574 or parts[0].lower() in ('.hg', '.hg.', '')
574 or parts[0].lower() in ('.hg', '.hg.', '')
575 or os.pardir in parts):
575 or os.pardir in parts):
576 raise Abort(_("path contains illegal component: %s") % path)
576 raise Abort(_("path contains illegal component: %s") % path)
577 if '.hg' in path.lower():
577 if '.hg' in path.lower():
578 lparts = [p.lower() for p in parts]
578 lparts = [p.lower() for p in parts]
579 for p in '.hg', '.hg.':
579 for p in '.hg', '.hg.':
580 if p in lparts[1:]:
580 if p in lparts[1:]:
581 pos = lparts.index(p)
581 pos = lparts.index(p)
582 base = os.path.join(*parts[:pos])
582 base = os.path.join(*parts[:pos])
583 raise Abort(_('path %r is inside nested repo %r')
583 raise Abort(_('path %r is inside nested repo %r')
584 % (path, base))
584 % (path, base))
585
585
586 parts.pop()
586 parts.pop()
587 prefixes = []
587 prefixes = []
588 while parts:
588 while parts:
589 prefix = os.sep.join(parts)
589 prefix = os.sep.join(parts)
590 if prefix in self.auditeddir:
590 if prefix in self.auditeddir:
591 break
591 break
592 curpath = os.path.join(self.root, prefix)
592 curpath = os.path.join(self.root, prefix)
593 try:
593 try:
594 st = os.lstat(curpath)
594 st = os.lstat(curpath)
595 except OSError, err:
595 except OSError, err:
596 # EINVAL can be raised as invalid path syntax under win32.
596 # EINVAL can be raised as invalid path syntax under win32.
597 # They must be ignored for patterns can be checked too.
597 # They must be ignored for patterns can be checked too.
598 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
598 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
599 raise
599 raise
600 else:
600 else:
601 if stat.S_ISLNK(st.st_mode):
601 if stat.S_ISLNK(st.st_mode):
602 raise Abort(_('path %r traverses symbolic link %r') %
602 raise Abort(_('path %r traverses symbolic link %r') %
603 (path, prefix))
603 (path, prefix))
604 elif (stat.S_ISDIR(st.st_mode) and
604 elif (stat.S_ISDIR(st.st_mode) and
605 os.path.isdir(os.path.join(curpath, '.hg'))):
605 os.path.isdir(os.path.join(curpath, '.hg'))):
606 if not self.callback or not self.callback(curpath):
606 if not self.callback or not self.callback(curpath):
607 raise Abort(_('path %r is inside nested repo %r') %
607 raise Abort(_('path %r is inside nested repo %r') %
608 (path, prefix))
608 (path, prefix))
609 prefixes.append(prefix)
609 prefixes.append(prefix)
610 parts.pop()
610 parts.pop()
611
611
612 self.audited.add(path)
612 self.audited.add(path)
613 # only add prefixes to the cache after checking everything: we don't
613 # only add prefixes to the cache after checking everything: we don't
614 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
614 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
615 self.auditeddir.update(prefixes)
615 self.auditeddir.update(prefixes)
616
616
617 def lookup_reg(key, name=None, scope=None):
617 def lookup_reg(key, name=None, scope=None):
618 return None
618 return None
619
619
620 def hidewindow():
620 def hidewindow():
621 """Hide current shell window.
621 """Hide current shell window.
622
622
623 Used to hide the window opened when starting asynchronous
623 Used to hide the window opened when starting asynchronous
624 child process under Windows, unneeded on other systems.
624 child process under Windows, unneeded on other systems.
625 """
625 """
626 pass
626 pass
627
627
628 if os.name == 'nt':
628 if os.name == 'nt':
629 checkosfilename = checkwinfilename
629 checkosfilename = checkwinfilename
630 from windows import *
630 from windows import *
631 else:
631 else:
632 from posix import *
632 from posix import *
633
633
634 def makelock(info, pathname):
634 def makelock(info, pathname):
635 try:
635 try:
636 return os.symlink(info, pathname)
636 return os.symlink(info, pathname)
637 except OSError, why:
637 except OSError, why:
638 if why.errno == errno.EEXIST:
638 if why.errno == errno.EEXIST:
639 raise
639 raise
640 except AttributeError: # no symlink in os
640 except AttributeError: # no symlink in os
641 pass
641 pass
642
642
643 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
643 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
644 os.write(ld, info)
644 os.write(ld, info)
645 os.close(ld)
645 os.close(ld)
646
646
647 def readlock(pathname):
647 def readlock(pathname):
648 try:
648 try:
649 return os.readlink(pathname)
649 return os.readlink(pathname)
650 except OSError, why:
650 except OSError, why:
651 if why.errno not in (errno.EINVAL, errno.ENOSYS):
651 if why.errno not in (errno.EINVAL, errno.ENOSYS):
652 raise
652 raise
653 except AttributeError: # no symlink in os
653 except AttributeError: # no symlink in os
654 pass
654 pass
655 fp = posixfile(pathname)
655 fp = posixfile(pathname)
656 r = fp.read()
656 r = fp.read()
657 fp.close()
657 fp.close()
658 return r
658 return r
659
659
660 def fstat(fp):
660 def fstat(fp):
661 '''stat file object that may not have fileno method.'''
661 '''stat file object that may not have fileno method.'''
662 try:
662 try:
663 return os.fstat(fp.fileno())
663 return os.fstat(fp.fileno())
664 except AttributeError:
664 except AttributeError:
665 return os.stat(fp.name)
665 return os.stat(fp.name)
666
666
667 # File system features
667 # File system features
668
668
669 def checkcase(path):
669 def checkcase(path):
670 """
670 """
671 Check whether the given path is on a case-sensitive filesystem
671 Check whether the given path is on a case-sensitive filesystem
672
672
673 Requires a path (like /foo/.hg) ending with a foldable final
673 Requires a path (like /foo/.hg) ending with a foldable final
674 directory component.
674 directory component.
675 """
675 """
676 s1 = os.stat(path)
676 s1 = os.stat(path)
677 d, b = os.path.split(path)
677 d, b = os.path.split(path)
678 p2 = os.path.join(d, b.upper())
678 p2 = os.path.join(d, b.upper())
679 if path == p2:
679 if path == p2:
680 p2 = os.path.join(d, b.lower())
680 p2 = os.path.join(d, b.lower())
681 try:
681 try:
682 s2 = os.stat(p2)
682 s2 = os.stat(p2)
683 if s2 == s1:
683 if s2 == s1:
684 return False
684 return False
685 return True
685 return True
686 except:
686 except:
687 return True
687 return True
688
688
689 _fspathcache = {}
689 _fspathcache = {}
690 def fspath(name, root):
690 def fspath(name, root):
691 '''Get name in the case stored in the filesystem
691 '''Get name in the case stored in the filesystem
692
692
693 The name is either relative to root, or it is an absolute path starting
693 The name is either relative to root, or it is an absolute path starting
694 with root. Note that this function is unnecessary, and should not be
694 with root. Note that this function is unnecessary, and should not be
695 called, for case-sensitive filesystems (simply because it's expensive).
695 called, for case-sensitive filesystems (simply because it's expensive).
696 '''
696 '''
697 # If name is absolute, make it relative
697 # If name is absolute, make it relative
698 if name.lower().startswith(root.lower()):
698 if name.lower().startswith(root.lower()):
699 l = len(root)
699 l = len(root)
700 if name[l] == os.sep or name[l] == os.altsep:
700 if name[l] == os.sep or name[l] == os.altsep:
701 l = l + 1
701 l = l + 1
702 name = name[l:]
702 name = name[l:]
703
703
704 if not os.path.lexists(os.path.join(root, name)):
704 if not os.path.lexists(os.path.join(root, name)):
705 return None
705 return None
706
706
707 seps = os.sep
707 seps = os.sep
708 if os.altsep:
708 if os.altsep:
709 seps = seps + os.altsep
709 seps = seps + os.altsep
710 # Protect backslashes. This gets silly very quickly.
710 # Protect backslashes. This gets silly very quickly.
711 seps.replace('\\','\\\\')
711 seps.replace('\\','\\\\')
712 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
712 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
713 dir = os.path.normcase(os.path.normpath(root))
713 dir = os.path.normcase(os.path.normpath(root))
714 result = []
714 result = []
715 for part, sep in pattern.findall(name):
715 for part, sep in pattern.findall(name):
716 if sep:
716 if sep:
717 result.append(sep)
717 result.append(sep)
718 continue
718 continue
719
719
720 if dir not in _fspathcache:
720 if dir not in _fspathcache:
721 _fspathcache[dir] = os.listdir(dir)
721 _fspathcache[dir] = os.listdir(dir)
722 contents = _fspathcache[dir]
722 contents = _fspathcache[dir]
723
723
724 lpart = part.lower()
724 lpart = part.lower()
725 lenp = len(part)
725 lenp = len(part)
726 for n in contents:
726 for n in contents:
727 if lenp == len(n) and n.lower() == lpart:
727 if lenp == len(n) and n.lower() == lpart:
728 result.append(n)
728 result.append(n)
729 break
729 break
730 else:
730 else:
731 # Cannot happen, as the file exists!
731 # Cannot happen, as the file exists!
732 result.append(part)
732 result.append(part)
733 dir = os.path.join(dir, lpart)
733 dir = os.path.join(dir, lpart)
734
734
735 return ''.join(result)
735 return ''.join(result)
736
736
737 def checknlink(testfile):
737 def checknlink(testfile):
738 '''check whether hardlink count reporting works properly'''
738 '''check whether hardlink count reporting works properly'''
739
739
740 # testfile may be open, so we need a separate file for checking to
740 # testfile may be open, so we need a separate file for checking to
741 # work around issue2543 (or testfile may get lost on Samba shares)
741 # work around issue2543 (or testfile may get lost on Samba shares)
742 f1 = testfile + ".hgtmp1"
742 f1 = testfile + ".hgtmp1"
743 if os.path.lexists(f1):
743 if os.path.lexists(f1):
744 return False
744 return False
745 try:
745 try:
746 posixfile(f1, 'w').close()
746 posixfile(f1, 'w').close()
747 except IOError:
747 except IOError:
748 return False
748 return False
749
749
750 f2 = testfile + ".hgtmp2"
750 f2 = testfile + ".hgtmp2"
751 fd = None
751 fd = None
752 try:
752 try:
753 try:
753 try:
754 os_link(f1, f2)
754 os_link(f1, f2)
755 except OSError:
755 except OSError:
756 return False
756 return False
757
757
758 # nlinks() may behave differently for files on Windows shares if
758 # nlinks() may behave differently for files on Windows shares if
759 # the file is open.
759 # the file is open.
760 fd = posixfile(f2)
760 fd = posixfile(f2)
761 return nlinks(f2) > 1
761 return nlinks(f2) > 1
762 finally:
762 finally:
763 if fd is not None:
763 if fd is not None:
764 fd.close()
764 fd.close()
765 for f in (f1, f2):
765 for f in (f1, f2):
766 try:
766 try:
767 os.unlink(f)
767 os.unlink(f)
768 except OSError:
768 except OSError:
769 pass
769 pass
770
770
771 return False
771 return False
772
772
773 def endswithsep(path):
773 def endswithsep(path):
774 '''Check path ends with os.sep or os.altsep.'''
774 '''Check path ends with os.sep or os.altsep.'''
775 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
775 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
776
776
777 def splitpath(path):
777 def splitpath(path):
778 '''Split path by os.sep.
778 '''Split path by os.sep.
779 Note that this function does not use os.altsep because this is
779 Note that this function does not use os.altsep because this is
780 an alternative of simple "xxx.split(os.sep)".
780 an alternative of simple "xxx.split(os.sep)".
781 It is recommended to use os.path.normpath() before using this
781 It is recommended to use os.path.normpath() before using this
782 function if need.'''
782 function if need.'''
783 return path.split(os.sep)
783 return path.split(os.sep)
784
784
785 def gui():
785 def gui():
786 '''Are we running in a GUI?'''
786 '''Are we running in a GUI?'''
787 if sys.platform == 'darwin':
787 if sys.platform == 'darwin':
788 if 'SSH_CONNECTION' in os.environ:
788 if 'SSH_CONNECTION' in os.environ:
789 # handle SSH access to a box where the user is logged in
789 # handle SSH access to a box where the user is logged in
790 return False
790 return False
791 elif getattr(osutil, 'isgui', None):
791 elif getattr(osutil, 'isgui', None):
792 # check if a CoreGraphics session is available
792 # check if a CoreGraphics session is available
793 return osutil.isgui()
793 return osutil.isgui()
794 else:
794 else:
795 # pure build; use a safe default
795 # pure build; use a safe default
796 return True
796 return True
797 else:
797 else:
798 return os.name == "nt" or os.environ.get("DISPLAY")
798 return os.name == "nt" or os.environ.get("DISPLAY")
799
799
800 def mktempcopy(name, emptyok=False, createmode=None):
800 def mktempcopy(name, emptyok=False, createmode=None):
801 """Create a temporary file with the same contents from name
801 """Create a temporary file with the same contents from name
802
802
803 The permission bits are copied from the original file.
803 The permission bits are copied from the original file.
804
804
805 If the temporary file is going to be truncated immediately, you
805 If the temporary file is going to be truncated immediately, you
806 can use emptyok=True as an optimization.
806 can use emptyok=True as an optimization.
807
807
808 Returns the name of the temporary file.
808 Returns the name of the temporary file.
809 """
809 """
810 d, fn = os.path.split(name)
810 d, fn = os.path.split(name)
811 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
811 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
812 os.close(fd)
812 os.close(fd)
813 # Temporary files are created with mode 0600, which is usually not
813 # Temporary files are created with mode 0600, which is usually not
814 # what we want. If the original file already exists, just copy
814 # what we want. If the original file already exists, just copy
815 # its mode. Otherwise, manually obey umask.
815 # its mode. Otherwise, manually obey umask.
816 try:
816 try:
817 st_mode = os.lstat(name).st_mode & 0777
817 st_mode = os.lstat(name).st_mode & 0777
818 except OSError, inst:
818 except OSError, inst:
819 if inst.errno != errno.ENOENT:
819 if inst.errno != errno.ENOENT:
820 raise
820 raise
821 st_mode = createmode
821 st_mode = createmode
822 if st_mode is None:
822 if st_mode is None:
823 st_mode = ~umask
823 st_mode = ~umask
824 st_mode &= 0666
824 st_mode &= 0666
825 os.chmod(temp, st_mode)
825 os.chmod(temp, st_mode)
826 if emptyok:
826 if emptyok:
827 return temp
827 return temp
828 try:
828 try:
829 try:
829 try:
830 ifp = posixfile(name, "rb")
830 ifp = posixfile(name, "rb")
831 except IOError, inst:
831 except IOError, inst:
832 if inst.errno == errno.ENOENT:
832 if inst.errno == errno.ENOENT:
833 return temp
833 return temp
834 if not getattr(inst, 'filename', None):
834 if not getattr(inst, 'filename', None):
835 inst.filename = name
835 inst.filename = name
836 raise
836 raise
837 ofp = posixfile(temp, "wb")
837 ofp = posixfile(temp, "wb")
838 for chunk in filechunkiter(ifp):
838 for chunk in filechunkiter(ifp):
839 ofp.write(chunk)
839 ofp.write(chunk)
840 ifp.close()
840 ifp.close()
841 ofp.close()
841 ofp.close()
842 except:
842 except:
843 try: os.unlink(temp)
843 try: os.unlink(temp)
844 except: pass
844 except: pass
845 raise
845 raise
846 return temp
846 return temp
847
847
848 class atomictempfile(object):
848 class atomictempfile(object):
849 """file-like object that atomically updates a file
849 """file-like object that atomically updates a file
850
850
851 All writes will be redirected to a temporary copy of the original
851 All writes will be redirected to a temporary copy of the original
852 file. When rename is called, the copy is renamed to the original
852 file. When rename is called, the copy is renamed to the original
853 name, making the changes visible.
853 name, making the changes visible.
854 """
854 """
855 def __init__(self, name, mode='w+b', createmode=None):
855 def __init__(self, name, mode='w+b', createmode=None):
856 self.__name = name
856 self.__name = name
857 self._fp = None
857 self._fp = None
858 self.temp = mktempcopy(name, emptyok=('w' in mode),
858 self.temp = mktempcopy(name, emptyok=('w' in mode),
859 createmode=createmode)
859 createmode=createmode)
860 self._fp = posixfile(self.temp, mode)
860 self._fp = posixfile(self.temp, mode)
861
861
862 def __getattr__(self, name):
862 def __getattr__(self, name):
863 return getattr(self._fp, name)
863 return getattr(self._fp, name)
864
864
865 def rename(self):
865 def rename(self):
866 if not self._fp.closed:
866 if not self._fp.closed:
867 self._fp.close()
867 self._fp.close()
868 rename(self.temp, localpath(self.__name))
868 rename(self.temp, localpath(self.__name))
869
869
870 def close(self):
870 def close(self):
871 if not self._fp:
871 if not self._fp:
872 return
872 return
873 if not self._fp.closed:
873 if not self._fp.closed:
874 try:
874 try:
875 os.unlink(self.temp)
875 os.unlink(self.temp)
876 except: pass
876 except: pass
877 self._fp.close()
877 self._fp.close()
878
878
879 def __del__(self):
879 def __del__(self):
880 self.close()
880 self.close()
881
881
882 def makedirs(name, mode=None):
882 def makedirs(name, mode=None):
883 """recursive directory creation with parent mode inheritance"""
883 """recursive directory creation with parent mode inheritance"""
884 parent = os.path.abspath(os.path.dirname(name))
884 parent = os.path.abspath(os.path.dirname(name))
885 try:
885 try:
886 os.mkdir(name)
886 os.mkdir(name)
887 if mode is not None:
887 if mode is not None:
888 os.chmod(name, mode)
888 os.chmod(name, mode)
889 return
889 return
890 except OSError, err:
890 except OSError, err:
891 if err.errno == errno.EEXIST:
891 if err.errno == errno.EEXIST:
892 return
892 return
893 if not name or parent == name or err.errno != errno.ENOENT:
893 if not name or parent == name or err.errno != errno.ENOENT:
894 raise
894 raise
895 makedirs(parent, mode)
895 makedirs(parent, mode)
896 makedirs(name, mode)
896 makedirs(name, mode)
897
897
898 class opener(object):
898 class opener(object):
899 """Open files relative to a base directory
899 """Open files relative to a base directory
900
900
901 This class is used to hide the details of COW semantics and
901 This class is used to hide the details of COW semantics and
902 remote file access from higher level code.
902 remote file access from higher level code.
903 """
903 """
904 def __init__(self, base, audit=True):
904 def __init__(self, base, audit=True):
905 self.base = base
905 self.base = base
906 if audit:
906 if audit:
907 self.auditor = path_auditor(base)
907 self.auditor = path_auditor(base)
908 else:
908 else:
909 self.auditor = always
909 self.auditor = always
910 self.createmode = None
910 self.createmode = None
911 self._trustnlink = None
911 self._trustnlink = None
912
912
913 @propertycache
913 @propertycache
914 def _can_symlink(self):
914 def _can_symlink(self):
915 return checklink(self.base)
915 return checklink(self.base)
916
916
917 def _fixfilemode(self, name):
917 def _fixfilemode(self, name):
918 if self.createmode is None:
918 if self.createmode is None:
919 return
919 return
920 os.chmod(name, self.createmode & 0666)
920 os.chmod(name, self.createmode & 0666)
921
921
922 def __call__(self, path, mode="r", text=False, atomictemp=False):
922 def __call__(self, path, mode="r", text=False, atomictemp=False):
923 r = checkosfilename(path)
923 r = checkosfilename(path)
924 if r:
924 if r:
925 raise Abort("%s: %r" % (r, path))
925 raise Abort("%s: %r" % (r, path))
926 self.auditor(path)
926 self.auditor(path)
927 f = os.path.join(self.base, path)
927 f = os.path.join(self.base, path)
928
928
929 if not text and "b" not in mode:
929 if not text and "b" not in mode:
930 mode += "b" # for that other OS
930 mode += "b" # for that other OS
931
931
932 nlink = -1
932 nlink = -1
933 dirname, basename = os.path.split(f)
933 dirname, basename = os.path.split(f)
934 # If basename is empty, then the path is malformed because it points
934 # If basename is empty, then the path is malformed because it points
935 # to a directory. Let the posixfile() call below raise IOError.
935 # to a directory. Let the posixfile() call below raise IOError.
936 if basename and mode not in ('r', 'rb'):
936 if basename and mode not in ('r', 'rb'):
937 if atomictemp:
937 if atomictemp:
938 if not os.path.isdir(dirname):
938 if not os.path.isdir(dirname):
939 makedirs(dirname, self.createmode)
939 makedirs(dirname, self.createmode)
940 return atomictempfile(f, mode, self.createmode)
940 return atomictempfile(f, mode, self.createmode)
941 try:
941 try:
942 if 'w' in mode:
942 if 'w' in mode:
943 unlink(f)
943 unlink(f)
944 nlink = 0
944 nlink = 0
945 else:
945 else:
946 # nlinks() may behave differently for files on Windows
946 # nlinks() may behave differently for files on Windows
947 # shares if the file is open.
947 # shares if the file is open.
948 fd = posixfile(f)
948 fd = posixfile(f)
949 nlink = nlinks(f)
949 nlink = nlinks(f)
950 if nlink < 1:
950 if nlink < 1:
951 nlink = 2 # force mktempcopy (issue1922)
951 nlink = 2 # force mktempcopy (issue1922)
952 fd.close()
952 fd.close()
953 except (OSError, IOError), e:
953 except (OSError, IOError), e:
954 if e.errno != errno.ENOENT:
954 if e.errno != errno.ENOENT:
955 raise
955 raise
956 nlink = 0
956 nlink = 0
957 if not os.path.isdir(dirname):
957 if not os.path.isdir(dirname):
958 makedirs(dirname, self.createmode)
958 makedirs(dirname, self.createmode)
959 if nlink > 0:
959 if nlink > 0:
960 if self._trustnlink is None:
960 if self._trustnlink is None:
961 self._trustnlink = nlink > 1 or checknlink(f)
961 self._trustnlink = nlink > 1 or checknlink(f)
962 if nlink > 1 or not self._trustnlink:
962 if nlink > 1 or not self._trustnlink:
963 rename(mktempcopy(f), f)
963 rename(mktempcopy(f), f)
964 fp = posixfile(f, mode)
964 fp = posixfile(f, mode)
965 if nlink == 0:
965 if nlink == 0:
966 self._fixfilemode(f)
966 self._fixfilemode(f)
967 return fp
967 return fp
968
968
969 def symlink(self, src, dst):
969 def symlink(self, src, dst):
970 self.auditor(dst)
970 self.auditor(dst)
971 linkname = os.path.join(self.base, dst)
971 linkname = os.path.join(self.base, dst)
972 try:
972 try:
973 os.unlink(linkname)
973 os.unlink(linkname)
974 except OSError:
974 except OSError:
975 pass
975 pass
976
976
977 dirname = os.path.dirname(linkname)
977 dirname = os.path.dirname(linkname)
978 if not os.path.exists(dirname):
978 if not os.path.exists(dirname):
979 makedirs(dirname, self.createmode)
979 makedirs(dirname, self.createmode)
980
980
981 if self._can_symlink:
981 if self._can_symlink:
982 try:
982 try:
983 os.symlink(src, linkname)
983 os.symlink(src, linkname)
984 except OSError, err:
984 except OSError, err:
985 raise OSError(err.errno, _('could not symlink to %r: %s') %
985 raise OSError(err.errno, _('could not symlink to %r: %s') %
986 (src, err.strerror), linkname)
986 (src, err.strerror), linkname)
987 else:
987 else:
988 f = self(dst, "w")
988 f = self(dst, "w")
989 f.write(src)
989 f.write(src)
990 f.close()
990 f.close()
991 self._fixfilemode(dst)
991 self._fixfilemode(dst)
992
992
993 class chunkbuffer(object):
993 class chunkbuffer(object):
994 """Allow arbitrary sized chunks of data to be efficiently read from an
994 """Allow arbitrary sized chunks of data to be efficiently read from an
995 iterator over chunks of arbitrary size."""
995 iterator over chunks of arbitrary size."""
996
996
997 def __init__(self, in_iter):
997 def __init__(self, in_iter):
998 """in_iter is the iterator that's iterating over the input chunks.
998 """in_iter is the iterator that's iterating over the input chunks.
999 targetsize is how big a buffer to try to maintain."""
999 targetsize is how big a buffer to try to maintain."""
1000 def splitbig(chunks):
1000 def splitbig(chunks):
1001 for chunk in chunks:
1001 for chunk in chunks:
1002 if len(chunk) > 2**20:
1002 if len(chunk) > 2**20:
1003 pos = 0
1003 pos = 0
1004 while pos < len(chunk):
1004 while pos < len(chunk):
1005 end = pos + 2 ** 18
1005 end = pos + 2 ** 18
1006 yield chunk[pos:end]
1006 yield chunk[pos:end]
1007 pos = end
1007 pos = end
1008 else:
1008 else:
1009 yield chunk
1009 yield chunk
1010 self.iter = splitbig(in_iter)
1010 self.iter = splitbig(in_iter)
1011 self._queue = []
1011 self._queue = []
1012
1012
1013 def read(self, l):
1013 def read(self, l):
1014 """Read L bytes of data from the iterator of chunks of data.
1014 """Read L bytes of data from the iterator of chunks of data.
1015 Returns less than L bytes if the iterator runs dry."""
1015 Returns less than L bytes if the iterator runs dry."""
1016 left = l
1016 left = l
1017 buf = ''
1017 buf = ''
1018 queue = self._queue
1018 queue = self._queue
1019 while left > 0:
1019 while left > 0:
1020 # refill the queue
1020 # refill the queue
1021 if not queue:
1021 if not queue:
1022 target = 2**18
1022 target = 2**18
1023 for chunk in self.iter:
1023 for chunk in self.iter:
1024 queue.append(chunk)
1024 queue.append(chunk)
1025 target -= len(chunk)
1025 target -= len(chunk)
1026 if target <= 0:
1026 if target <= 0:
1027 break
1027 break
1028 if not queue:
1028 if not queue:
1029 break
1029 break
1030
1030
1031 chunk = queue.pop(0)
1031 chunk = queue.pop(0)
1032 left -= len(chunk)
1032 left -= len(chunk)
1033 if left < 0:
1033 if left < 0:
1034 queue.insert(0, chunk[left:])
1034 queue.insert(0, chunk[left:])
1035 buf += chunk[:left]
1035 buf += chunk[:left]
1036 else:
1036 else:
1037 buf += chunk
1037 buf += chunk
1038
1038
1039 return buf
1039 return buf
1040
1040
1041 def filechunkiter(f, size=65536, limit=None):
1041 def filechunkiter(f, size=65536, limit=None):
1042 """Create a generator that produces the data in the file size
1042 """Create a generator that produces the data in the file size
1043 (default 65536) bytes at a time, up to optional limit (default is
1043 (default 65536) bytes at a time, up to optional limit (default is
1044 to read all data). Chunks may be less than size bytes if the
1044 to read all data). Chunks may be less than size bytes if the
1045 chunk is the last chunk in the file, or the file is a socket or
1045 chunk is the last chunk in the file, or the file is a socket or
1046 some other type of file that sometimes reads less data than is
1046 some other type of file that sometimes reads less data than is
1047 requested."""
1047 requested."""
1048 assert size >= 0
1048 assert size >= 0
1049 assert limit is None or limit >= 0
1049 assert limit is None or limit >= 0
1050 while True:
1050 while True:
1051 if limit is None:
1051 if limit is None:
1052 nbytes = size
1052 nbytes = size
1053 else:
1053 else:
1054 nbytes = min(limit, size)
1054 nbytes = min(limit, size)
1055 s = nbytes and f.read(nbytes)
1055 s = nbytes and f.read(nbytes)
1056 if not s:
1056 if not s:
1057 break
1057 break
1058 if limit:
1058 if limit:
1059 limit -= len(s)
1059 limit -= len(s)
1060 yield s
1060 yield s
1061
1061
1062 def makedate():
1062 def makedate():
1063 lt = time.localtime()
1063 lt = time.localtime()
1064 if lt[8] == 1 and time.daylight:
1064 if lt[8] == 1 and time.daylight:
1065 tz = time.altzone
1065 tz = time.altzone
1066 else:
1066 else:
1067 tz = time.timezone
1067 tz = time.timezone
1068 t = time.mktime(lt)
1068 t = time.mktime(lt)
1069 if t < 0:
1069 if t < 0:
1070 hint = _("check your clock")
1070 hint = _("check your clock")
1071 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1071 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1072 return t, tz
1072 return t, tz
1073
1073
1074 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1074 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1075 """represent a (unixtime, offset) tuple as a localized time.
1075 """represent a (unixtime, offset) tuple as a localized time.
1076 unixtime is seconds since the epoch, and offset is the time zone's
1076 unixtime is seconds since the epoch, and offset is the time zone's
1077 number of seconds away from UTC. if timezone is false, do not
1077 number of seconds away from UTC. if timezone is false, do not
1078 append time zone to string."""
1078 append time zone to string."""
1079 t, tz = date or makedate()
1079 t, tz = date or makedate()
1080 if t < 0:
1080 if t < 0:
1081 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1081 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1082 tz = 0
1082 tz = 0
1083 if "%1" in format or "%2" in format:
1083 if "%1" in format or "%2" in format:
1084 sign = (tz > 0) and "-" or "+"
1084 sign = (tz > 0) and "-" or "+"
1085 minutes = abs(tz) // 60
1085 minutes = abs(tz) // 60
1086 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1086 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1087 format = format.replace("%2", "%02d" % (minutes % 60))
1087 format = format.replace("%2", "%02d" % (minutes % 60))
1088 s = time.strftime(format, time.gmtime(float(t) - tz))
1088 s = time.strftime(format, time.gmtime(float(t) - tz))
1089 return s
1089 return s
1090
1090
1091 def shortdate(date=None):
1091 def shortdate(date=None):
1092 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1092 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1093 return datestr(date, format='%Y-%m-%d')
1093 return datestr(date, format='%Y-%m-%d')
1094
1094
1095 def strdate(string, format, defaults=[]):
1095 def strdate(string, format, defaults=[]):
1096 """parse a localized time string and return a (unixtime, offset) tuple.
1096 """parse a localized time string and return a (unixtime, offset) tuple.
1097 if the string cannot be parsed, ValueError is raised."""
1097 if the string cannot be parsed, ValueError is raised."""
1098 def timezone(string):
1098 def timezone(string):
1099 tz = string.split()[-1]
1099 tz = string.split()[-1]
1100 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1100 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1101 sign = (tz[0] == "+") and 1 or -1
1101 sign = (tz[0] == "+") and 1 or -1
1102 hours = int(tz[1:3])
1102 hours = int(tz[1:3])
1103 minutes = int(tz[3:5])
1103 minutes = int(tz[3:5])
1104 return -sign * (hours * 60 + minutes) * 60
1104 return -sign * (hours * 60 + minutes) * 60
1105 if tz == "GMT" or tz == "UTC":
1105 if tz == "GMT" or tz == "UTC":
1106 return 0
1106 return 0
1107 return None
1107 return None
1108
1108
1109 # NOTE: unixtime = localunixtime + offset
1109 # NOTE: unixtime = localunixtime + offset
1110 offset, date = timezone(string), string
1110 offset, date = timezone(string), string
1111 if offset is not None:
1111 if offset is not None:
1112 date = " ".join(string.split()[:-1])
1112 date = " ".join(string.split()[:-1])
1113
1113
1114 # add missing elements from defaults
1114 # add missing elements from defaults
1115 usenow = False # default to using biased defaults
1115 usenow = False # default to using biased defaults
1116 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1116 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1117 found = [True for p in part if ("%"+p) in format]
1117 found = [True for p in part if ("%"+p) in format]
1118 if not found:
1118 if not found:
1119 date += "@" + defaults[part][usenow]
1119 date += "@" + defaults[part][usenow]
1120 format += "@%" + part[0]
1120 format += "@%" + part[0]
1121 else:
1121 else:
1122 # We've found a specific time element, less specific time
1122 # We've found a specific time element, less specific time
1123 # elements are relative to today
1123 # elements are relative to today
1124 usenow = True
1124 usenow = True
1125
1125
1126 timetuple = time.strptime(date, format)
1126 timetuple = time.strptime(date, format)
1127 localunixtime = int(calendar.timegm(timetuple))
1127 localunixtime = int(calendar.timegm(timetuple))
1128 if offset is None:
1128 if offset is None:
1129 # local timezone
1129 # local timezone
1130 unixtime = int(time.mktime(timetuple))
1130 unixtime = int(time.mktime(timetuple))
1131 offset = unixtime - localunixtime
1131 offset = unixtime - localunixtime
1132 else:
1132 else:
1133 unixtime = localunixtime + offset
1133 unixtime = localunixtime + offset
1134 return unixtime, offset
1134 return unixtime, offset
1135
1135
1136 def parsedate(date, formats=None, bias={}):
1136 def parsedate(date, formats=None, bias={}):
1137 """parse a localized date/time and return a (unixtime, offset) tuple.
1137 """parse a localized date/time and return a (unixtime, offset) tuple.
1138
1138
1139 The date may be a "unixtime offset" string or in one of the specified
1139 The date may be a "unixtime offset" string or in one of the specified
1140 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1140 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1141 """
1141 """
1142 if not date:
1142 if not date:
1143 return 0, 0
1143 return 0, 0
1144 if isinstance(date, tuple) and len(date) == 2:
1144 if isinstance(date, tuple) and len(date) == 2:
1145 return date
1145 return date
1146 if not formats:
1146 if not formats:
1147 formats = defaultdateformats
1147 formats = defaultdateformats
1148 date = date.strip()
1148 date = date.strip()
1149 try:
1149 try:
1150 when, offset = map(int, date.split(' '))
1150 when, offset = map(int, date.split(' '))
1151 except ValueError:
1151 except ValueError:
1152 # fill out defaults
1152 # fill out defaults
1153 now = makedate()
1153 now = makedate()
1154 defaults = {}
1154 defaults = {}
1155 nowmap = {}
1155 nowmap = {}
1156 for part in ("d", "mb", "yY", "HI", "M", "S"):
1156 for part in ("d", "mb", "yY", "HI", "M", "S"):
1157 # this piece is for rounding the specific end of unknowns
1157 # this piece is for rounding the specific end of unknowns
1158 b = bias.get(part)
1158 b = bias.get(part)
1159 if b is None:
1159 if b is None:
1160 if part[0] in "HMS":
1160 if part[0] in "HMS":
1161 b = "00"
1161 b = "00"
1162 else:
1162 else:
1163 b = "0"
1163 b = "0"
1164
1164
1165 # this piece is for matching the generic end to today's date
1165 # this piece is for matching the generic end to today's date
1166 n = datestr(now, "%" + part[0])
1166 n = datestr(now, "%" + part[0])
1167
1167
1168 defaults[part] = (b, n)
1168 defaults[part] = (b, n)
1169
1169
1170 for format in formats:
1170 for format in formats:
1171 try:
1171 try:
1172 when, offset = strdate(date, format, defaults)
1172 when, offset = strdate(date, format, defaults)
1173 except (ValueError, OverflowError):
1173 except (ValueError, OverflowError):
1174 pass
1174 pass
1175 else:
1175 else:
1176 break
1176 break
1177 else:
1177 else:
1178 raise Abort(_('invalid date: %r') % date)
1178 raise Abort(_('invalid date: %r') % date)
1179 # validate explicit (probably user-specified) date and
1179 # validate explicit (probably user-specified) date and
1180 # time zone offset. values must fit in signed 32 bits for
1180 # time zone offset. values must fit in signed 32 bits for
1181 # current 32-bit linux runtimes. timezones go from UTC-12
1181 # current 32-bit linux runtimes. timezones go from UTC-12
1182 # to UTC+14
1182 # to UTC+14
1183 if abs(when) > 0x7fffffff:
1183 if abs(when) > 0x7fffffff:
1184 raise Abort(_('date exceeds 32 bits: %d') % when)
1184 raise Abort(_('date exceeds 32 bits: %d') % when)
1185 if when < 0:
1185 if when < 0:
1186 raise Abort(_('negative date value: %d') % when)
1186 raise Abort(_('negative date value: %d') % when)
1187 if offset < -50400 or offset > 43200:
1187 if offset < -50400 or offset > 43200:
1188 raise Abort(_('impossible time zone offset: %d') % offset)
1188 raise Abort(_('impossible time zone offset: %d') % offset)
1189 return when, offset
1189 return when, offset
1190
1190
1191 def matchdate(date):
1191 def matchdate(date):
1192 """Return a function that matches a given date match specifier
1192 """Return a function that matches a given date match specifier
1193
1193
1194 Formats include:
1194 Formats include:
1195
1195
1196 '{date}' match a given date to the accuracy provided
1196 '{date}' match a given date to the accuracy provided
1197
1197
1198 '<{date}' on or before a given date
1198 '<{date}' on or before a given date
1199
1199
1200 '>{date}' on or after a given date
1200 '>{date}' on or after a given date
1201
1201
1202 >>> p1 = parsedate("10:29:59")
1202 >>> p1 = parsedate("10:29:59")
1203 >>> p2 = parsedate("10:30:00")
1203 >>> p2 = parsedate("10:30:00")
1204 >>> p3 = parsedate("10:30:59")
1204 >>> p3 = parsedate("10:30:59")
1205 >>> p4 = parsedate("10:31:00")
1205 >>> p4 = parsedate("10:31:00")
1206 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1206 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1207 >>> f = matchdate("10:30")
1207 >>> f = matchdate("10:30")
1208 >>> f(p1[0])
1208 >>> f(p1[0])
1209 False
1209 False
1210 >>> f(p2[0])
1210 >>> f(p2[0])
1211 True
1211 True
1212 >>> f(p3[0])
1212 >>> f(p3[0])
1213 True
1213 True
1214 >>> f(p4[0])
1214 >>> f(p4[0])
1215 False
1215 False
1216 >>> f(p5[0])
1216 >>> f(p5[0])
1217 False
1217 False
1218 """
1218 """
1219
1219
1220 def lower(date):
1220 def lower(date):
1221 d = dict(mb="1", d="1")
1221 d = dict(mb="1", d="1")
1222 return parsedate(date, extendeddateformats, d)[0]
1222 return parsedate(date, extendeddateformats, d)[0]
1223
1223
1224 def upper(date):
1224 def upper(date):
1225 d = dict(mb="12", HI="23", M="59", S="59")
1225 d = dict(mb="12", HI="23", M="59", S="59")
1226 for days in ("31", "30", "29"):
1226 for days in ("31", "30", "29"):
1227 try:
1227 try:
1228 d["d"] = days
1228 d["d"] = days
1229 return parsedate(date, extendeddateformats, d)[0]
1229 return parsedate(date, extendeddateformats, d)[0]
1230 except:
1230 except:
1231 pass
1231 pass
1232 d["d"] = "28"
1232 d["d"] = "28"
1233 return parsedate(date, extendeddateformats, d)[0]
1233 return parsedate(date, extendeddateformats, d)[0]
1234
1234
1235 date = date.strip()
1235 date = date.strip()
1236
1236
1237 if not date:
1237 if not date:
1238 raise Abort(_("dates cannot consist entirely of whitespace"))
1238 raise Abort(_("dates cannot consist entirely of whitespace"))
1239 elif date[0] == "<":
1239 elif date[0] == "<":
1240 if not date[1:]:
1240 if not date[1:]:
1241 raise Abort(_("invalid day spec, use '<DATE'"))
1241 raise Abort(_("invalid day spec, use '<DATE'"))
1242 when = upper(date[1:])
1242 when = upper(date[1:])
1243 return lambda x: x <= when
1243 return lambda x: x <= when
1244 elif date[0] == ">":
1244 elif date[0] == ">":
1245 if not date[1:]:
1245 if not date[1:]:
1246 raise Abort(_("invalid day spec, use '>DATE'"))
1246 raise Abort(_("invalid day spec, use '>DATE'"))
1247 when = lower(date[1:])
1247 when = lower(date[1:])
1248 return lambda x: x >= when
1248 return lambda x: x >= when
1249 elif date[0] == "-":
1249 elif date[0] == "-":
1250 try:
1250 try:
1251 days = int(date[1:])
1251 days = int(date[1:])
1252 except ValueError:
1252 except ValueError:
1253 raise Abort(_("invalid day spec: %s") % date[1:])
1253 raise Abort(_("invalid day spec: %s") % date[1:])
1254 if days < 0:
1254 if days < 0:
1255 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1255 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1256 % date[1:])
1256 % date[1:])
1257 when = makedate()[0] - days * 3600 * 24
1257 when = makedate()[0] - days * 3600 * 24
1258 return lambda x: x >= when
1258 return lambda x: x >= when
1259 elif " to " in date:
1259 elif " to " in date:
1260 a, b = date.split(" to ")
1260 a, b = date.split(" to ")
1261 start, stop = lower(a), upper(b)
1261 start, stop = lower(a), upper(b)
1262 return lambda x: x >= start and x <= stop
1262 return lambda x: x >= start and x <= stop
1263 else:
1263 else:
1264 start, stop = lower(date), upper(date)
1264 start, stop = lower(date), upper(date)
1265 return lambda x: x >= start and x <= stop
1265 return lambda x: x >= start and x <= stop
1266
1266
1267 def shortuser(user):
1267 def shortuser(user):
1268 """Return a short representation of a user name or email address."""
1268 """Return a short representation of a user name or email address."""
1269 f = user.find('@')
1269 f = user.find('@')
1270 if f >= 0:
1270 if f >= 0:
1271 user = user[:f]
1271 user = user[:f]
1272 f = user.find('<')
1272 f = user.find('<')
1273 if f >= 0:
1273 if f >= 0:
1274 user = user[f + 1:]
1274 user = user[f + 1:]
1275 f = user.find(' ')
1275 f = user.find(' ')
1276 if f >= 0:
1276 if f >= 0:
1277 user = user[:f]
1277 user = user[:f]
1278 f = user.find('.')
1278 f = user.find('.')
1279 if f >= 0:
1279 if f >= 0:
1280 user = user[:f]
1280 user = user[:f]
1281 return user
1281 return user
1282
1282
1283 def email(author):
1283 def email(author):
1284 '''get email of author.'''
1284 '''get email of author.'''
1285 r = author.find('>')
1285 r = author.find('>')
1286 if r == -1:
1286 if r == -1:
1287 r = None
1287 r = None
1288 return author[author.find('<') + 1:r]
1288 return author[author.find('<') + 1:r]
1289
1289
1290 def _ellipsis(text, maxlength):
1290 def _ellipsis(text, maxlength):
1291 if len(text) <= maxlength:
1291 if len(text) <= maxlength:
1292 return text, False
1292 return text, False
1293 else:
1293 else:
1294 return "%s..." % (text[:maxlength - 3]), True
1294 return "%s..." % (text[:maxlength - 3]), True
1295
1295
1296 def ellipsis(text, maxlength=400):
1296 def ellipsis(text, maxlength=400):
1297 """Trim string to at most maxlength (default: 400) characters."""
1297 """Trim string to at most maxlength (default: 400) characters."""
1298 try:
1298 try:
1299 # use unicode not to split at intermediate multi-byte sequence
1299 # use unicode not to split at intermediate multi-byte sequence
1300 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1300 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1301 maxlength)
1301 maxlength)
1302 if not truncated:
1302 if not truncated:
1303 return text
1303 return text
1304 return utext.encode(encoding.encoding)
1304 return utext.encode(encoding.encoding)
1305 except (UnicodeDecodeError, UnicodeEncodeError):
1305 except (UnicodeDecodeError, UnicodeEncodeError):
1306 return _ellipsis(text, maxlength)[0]
1306 return _ellipsis(text, maxlength)[0]
1307
1307
1308 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1308 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1309 '''yield every hg repository under path, recursively.'''
1309 '''yield every hg repository under path, recursively.'''
1310 def errhandler(err):
1310 def errhandler(err):
1311 if err.filename == path:
1311 if err.filename == path:
1312 raise err
1312 raise err
1313 if followsym and hasattr(os.path, 'samestat'):
1313 if followsym and hasattr(os.path, 'samestat'):
1314 def _add_dir_if_not_there(dirlst, dirname):
1314 def _add_dir_if_not_there(dirlst, dirname):
1315 match = False
1315 match = False
1316 samestat = os.path.samestat
1316 samestat = os.path.samestat
1317 dirstat = os.stat(dirname)
1317 dirstat = os.stat(dirname)
1318 for lstdirstat in dirlst:
1318 for lstdirstat in dirlst:
1319 if samestat(dirstat, lstdirstat):
1319 if samestat(dirstat, lstdirstat):
1320 match = True
1320 match = True
1321 break
1321 break
1322 if not match:
1322 if not match:
1323 dirlst.append(dirstat)
1323 dirlst.append(dirstat)
1324 return not match
1324 return not match
1325 else:
1325 else:
1326 followsym = False
1326 followsym = False
1327
1327
1328 if (seen_dirs is None) and followsym:
1328 if (seen_dirs is None) and followsym:
1329 seen_dirs = []
1329 seen_dirs = []
1330 _add_dir_if_not_there(seen_dirs, path)
1330 _add_dir_if_not_there(seen_dirs, path)
1331 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1331 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1332 dirs.sort()
1332 dirs.sort()
1333 if '.hg' in dirs:
1333 if '.hg' in dirs:
1334 yield root # found a repository
1334 yield root # found a repository
1335 qroot = os.path.join(root, '.hg', 'patches')
1335 qroot = os.path.join(root, '.hg', 'patches')
1336 if os.path.isdir(os.path.join(qroot, '.hg')):
1336 if os.path.isdir(os.path.join(qroot, '.hg')):
1337 yield qroot # we have a patch queue repo here
1337 yield qroot # we have a patch queue repo here
1338 if recurse:
1338 if recurse:
1339 # avoid recursing inside the .hg directory
1339 # avoid recursing inside the .hg directory
1340 dirs.remove('.hg')
1340 dirs.remove('.hg')
1341 else:
1341 else:
1342 dirs[:] = [] # don't descend further
1342 dirs[:] = [] # don't descend further
1343 elif followsym:
1343 elif followsym:
1344 newdirs = []
1344 newdirs = []
1345 for d in dirs:
1345 for d in dirs:
1346 fname = os.path.join(root, d)
1346 fname = os.path.join(root, d)
1347 if _add_dir_if_not_there(seen_dirs, fname):
1347 if _add_dir_if_not_there(seen_dirs, fname):
1348 if os.path.islink(fname):
1348 if os.path.islink(fname):
1349 for hgname in walkrepos(fname, True, seen_dirs):
1349 for hgname in walkrepos(fname, True, seen_dirs):
1350 yield hgname
1350 yield hgname
1351 else:
1351 else:
1352 newdirs.append(d)
1352 newdirs.append(d)
1353 dirs[:] = newdirs
1353 dirs[:] = newdirs
1354
1354
1355 _rcpath = None
1355 _rcpath = None
1356
1356
1357 def os_rcpath():
1357 def os_rcpath():
1358 '''return default os-specific hgrc search path'''
1358 '''return default os-specific hgrc search path'''
1359 path = system_rcpath()
1359 path = system_rcpath()
1360 path.extend(user_rcpath())
1360 path.extend(user_rcpath())
1361 path = [os.path.normpath(f) for f in path]
1361 path = [os.path.normpath(f) for f in path]
1362 return path
1362 return path
1363
1363
1364 def rcpath():
1364 def rcpath():
1365 '''return hgrc search path. if env var HGRCPATH is set, use it.
1365 '''return hgrc search path. if env var HGRCPATH is set, use it.
1366 for each item in path, if directory, use files ending in .rc,
1366 for each item in path, if directory, use files ending in .rc,
1367 else use item.
1367 else use item.
1368 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1368 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1369 if no HGRCPATH, use default os-specific path.'''
1369 if no HGRCPATH, use default os-specific path.'''
1370 global _rcpath
1370 global _rcpath
1371 if _rcpath is None:
1371 if _rcpath is None:
1372 if 'HGRCPATH' in os.environ:
1372 if 'HGRCPATH' in os.environ:
1373 _rcpath = []
1373 _rcpath = []
1374 for p in os.environ['HGRCPATH'].split(os.pathsep):
1374 for p in os.environ['HGRCPATH'].split(os.pathsep):
1375 if not p:
1375 if not p:
1376 continue
1376 continue
1377 p = expandpath(p)
1377 p = expandpath(p)
1378 if os.path.isdir(p):
1378 if os.path.isdir(p):
1379 for f, kind in osutil.listdir(p):
1379 for f, kind in osutil.listdir(p):
1380 if f.endswith('.rc'):
1380 if f.endswith('.rc'):
1381 _rcpath.append(os.path.join(p, f))
1381 _rcpath.append(os.path.join(p, f))
1382 else:
1382 else:
1383 _rcpath.append(p)
1383 _rcpath.append(p)
1384 else:
1384 else:
1385 _rcpath = os_rcpath()
1385 _rcpath = os_rcpath()
1386 return _rcpath
1386 return _rcpath
1387
1387
1388 def bytecount(nbytes):
1388 def bytecount(nbytes):
1389 '''return byte count formatted as readable string, with units'''
1389 '''return byte count formatted as readable string, with units'''
1390
1390
1391 units = (
1391 units = (
1392 (100, 1 << 30, _('%.0f GB')),
1392 (100, 1 << 30, _('%.0f GB')),
1393 (10, 1 << 30, _('%.1f GB')),
1393 (10, 1 << 30, _('%.1f GB')),
1394 (1, 1 << 30, _('%.2f GB')),
1394 (1, 1 << 30, _('%.2f GB')),
1395 (100, 1 << 20, _('%.0f MB')),
1395 (100, 1 << 20, _('%.0f MB')),
1396 (10, 1 << 20, _('%.1f MB')),
1396 (10, 1 << 20, _('%.1f MB')),
1397 (1, 1 << 20, _('%.2f MB')),
1397 (1, 1 << 20, _('%.2f MB')),
1398 (100, 1 << 10, _('%.0f KB')),
1398 (100, 1 << 10, _('%.0f KB')),
1399 (10, 1 << 10, _('%.1f KB')),
1399 (10, 1 << 10, _('%.1f KB')),
1400 (1, 1 << 10, _('%.2f KB')),
1400 (1, 1 << 10, _('%.2f KB')),
1401 (1, 1, _('%.0f bytes')),
1401 (1, 1, _('%.0f bytes')),
1402 )
1402 )
1403
1403
1404 for multiplier, divisor, format in units:
1404 for multiplier, divisor, format in units:
1405 if nbytes >= divisor * multiplier:
1405 if nbytes >= divisor * multiplier:
1406 return format % (nbytes / float(divisor))
1406 return format % (nbytes / float(divisor))
1407 return units[-1][2] % nbytes
1407 return units[-1][2] % nbytes
1408
1408
1409 def uirepr(s):
1409 def uirepr(s):
1410 # Avoid double backslash in Windows path repr()
1410 # Avoid double backslash in Windows path repr()
1411 return repr(s).replace('\\\\', '\\')
1411 return repr(s).replace('\\\\', '\\')
1412
1412
1413 # delay import of textwrap
1413 # delay import of textwrap
1414 def MBTextWrapper(**kwargs):
1414 def MBTextWrapper(**kwargs):
1415 class tw(textwrap.TextWrapper):
1415 class tw(textwrap.TextWrapper):
1416 """
1416 """
1417 Extend TextWrapper for double-width characters.
1417 Extend TextWrapper for double-width characters.
1418
1418
1419 Some Asian characters use two terminal columns instead of one.
1419 Some Asian characters use two terminal columns instead of one.
1420 A good example of this behavior can be seen with u'\u65e5\u672c',
1420 A good example of this behavior can be seen with u'\u65e5\u672c',
1421 the two Japanese characters for "Japan":
1421 the two Japanese characters for "Japan":
1422 len() returns 2, but when printed to a terminal, they eat 4 columns.
1422 len() returns 2, but when printed to a terminal, they eat 4 columns.
1423
1423
1424 (Note that this has nothing to do whatsoever with unicode
1424 (Note that this has nothing to do whatsoever with unicode
1425 representation, or encoding of the underlying string)
1425 representation, or encoding of the underlying string)
1426 """
1426 """
1427 def __init__(self, **kwargs):
1427 def __init__(self, **kwargs):
1428 textwrap.TextWrapper.__init__(self, **kwargs)
1428 textwrap.TextWrapper.__init__(self, **kwargs)
1429
1429
1430 def _cutdown(self, str, space_left):
1430 def _cutdown(self, str, space_left):
1431 l = 0
1431 l = 0
1432 ucstr = unicode(str, encoding.encoding)
1432 ucstr = unicode(str, encoding.encoding)
1433 colwidth = unicodedata.east_asian_width
1433 colwidth = unicodedata.east_asian_width
1434 for i in xrange(len(ucstr)):
1434 for i in xrange(len(ucstr)):
1435 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1435 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1436 if space_left < l:
1436 if space_left < l:
1437 return (ucstr[:i].encode(encoding.encoding),
1437 return (ucstr[:i].encode(encoding.encoding),
1438 ucstr[i:].encode(encoding.encoding))
1438 ucstr[i:].encode(encoding.encoding))
1439 return str, ''
1439 return str, ''
1440
1440
1441 # overriding of base class
1441 # overriding of base class
1442 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1442 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1443 space_left = max(width - cur_len, 1)
1443 space_left = max(width - cur_len, 1)
1444
1444
1445 if self.break_long_words:
1445 if self.break_long_words:
1446 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1446 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1447 cur_line.append(cut)
1447 cur_line.append(cut)
1448 reversed_chunks[-1] = res
1448 reversed_chunks[-1] = res
1449 elif not cur_line:
1449 elif not cur_line:
1450 cur_line.append(reversed_chunks.pop())
1450 cur_line.append(reversed_chunks.pop())
1451
1451
1452 global MBTextWrapper
1452 global MBTextWrapper
1453 MBTextWrapper = tw
1453 MBTextWrapper = tw
1454 return tw(**kwargs)
1454 return tw(**kwargs)
1455
1455
1456 def wrap(line, width, initindent='', hangindent=''):
1456 def wrap(line, width, initindent='', hangindent=''):
1457 maxindent = max(len(hangindent), len(initindent))
1457 maxindent = max(len(hangindent), len(initindent))
1458 if width <= maxindent:
1458 if width <= maxindent:
1459 # adjust for weird terminal size
1459 # adjust for weird terminal size
1460 width = max(78, maxindent + 1)
1460 width = max(78, maxindent + 1)
1461 wrapper = MBTextWrapper(width=width,
1461 wrapper = MBTextWrapper(width=width,
1462 initial_indent=initindent,
1462 initial_indent=initindent,
1463 subsequent_indent=hangindent)
1463 subsequent_indent=hangindent)
1464 return wrapper.fill(line)
1464 return wrapper.fill(line)
1465
1465
1466 def iterlines(iterator):
1466 def iterlines(iterator):
1467 for chunk in iterator:
1467 for chunk in iterator:
1468 for line in chunk.splitlines():
1468 for line in chunk.splitlines():
1469 yield line
1469 yield line
1470
1470
1471 def expandpath(path):
1471 def expandpath(path):
1472 return os.path.expanduser(os.path.expandvars(path))
1472 return os.path.expanduser(os.path.expandvars(path))
1473
1473
1474 def hgcmd():
1474 def hgcmd():
1475 """Return the command used to execute current hg
1475 """Return the command used to execute current hg
1476
1476
1477 This is different from hgexecutable() because on Windows we want
1477 This is different from hgexecutable() because on Windows we want
1478 to avoid things opening new shell windows like batch files, so we
1478 to avoid things opening new shell windows like batch files, so we
1479 get either the python call or current executable.
1479 get either the python call or current executable.
1480 """
1480 """
1481 if main_is_frozen():
1481 if main_is_frozen():
1482 return [sys.executable]
1482 return [sys.executable]
1483 return gethgcmd()
1483 return gethgcmd()
1484
1484
1485 def rundetached(args, condfn):
1485 def rundetached(args, condfn):
1486 """Execute the argument list in a detached process.
1486 """Execute the argument list in a detached process.
1487
1487
1488 condfn is a callable which is called repeatedly and should return
1488 condfn is a callable which is called repeatedly and should return
1489 True once the child process is known to have started successfully.
1489 True once the child process is known to have started successfully.
1490 At this point, the child process PID is returned. If the child
1490 At this point, the child process PID is returned. If the child
1491 process fails to start or finishes before condfn() evaluates to
1491 process fails to start or finishes before condfn() evaluates to
1492 True, return -1.
1492 True, return -1.
1493 """
1493 """
1494 # Windows case is easier because the child process is either
1494 # Windows case is easier because the child process is either
1495 # successfully starting and validating the condition or exiting
1495 # successfully starting and validating the condition or exiting
1496 # on failure. We just poll on its PID. On Unix, if the child
1496 # on failure. We just poll on its PID. On Unix, if the child
1497 # process fails to start, it will be left in a zombie state until
1497 # process fails to start, it will be left in a zombie state until
1498 # the parent wait on it, which we cannot do since we expect a long
1498 # the parent wait on it, which we cannot do since we expect a long
1499 # running process on success. Instead we listen for SIGCHLD telling
1499 # running process on success. Instead we listen for SIGCHLD telling
1500 # us our child process terminated.
1500 # us our child process terminated.
1501 terminated = set()
1501 terminated = set()
1502 def handler(signum, frame):
1502 def handler(signum, frame):
1503 terminated.add(os.wait())
1503 terminated.add(os.wait())
1504 prevhandler = None
1504 prevhandler = None
1505 if hasattr(signal, 'SIGCHLD'):
1505 if hasattr(signal, 'SIGCHLD'):
1506 prevhandler = signal.signal(signal.SIGCHLD, handler)
1506 prevhandler = signal.signal(signal.SIGCHLD, handler)
1507 try:
1507 try:
1508 pid = spawndetached(args)
1508 pid = spawndetached(args)
1509 while not condfn():
1509 while not condfn():
1510 if ((pid in terminated or not testpid(pid))
1510 if ((pid in terminated or not testpid(pid))
1511 and not condfn()):
1511 and not condfn()):
1512 return -1
1512 return -1
1513 time.sleep(0.1)
1513 time.sleep(0.1)
1514 return pid
1514 return pid
1515 finally:
1515 finally:
1516 if prevhandler is not None:
1516 if prevhandler is not None:
1517 signal.signal(signal.SIGCHLD, prevhandler)
1517 signal.signal(signal.SIGCHLD, prevhandler)
1518
1518
1519 try:
1519 try:
1520 any, all = any, all
1520 any, all = any, all
1521 except NameError:
1521 except NameError:
1522 def any(iterable):
1522 def any(iterable):
1523 for i in iterable:
1523 for i in iterable:
1524 if i:
1524 if i:
1525 return True
1525 return True
1526 return False
1526 return False
1527
1527
1528 def all(iterable):
1528 def all(iterable):
1529 for i in iterable:
1529 for i in iterable:
1530 if not i:
1530 if not i:
1531 return False
1531 return False
1532 return True
1532 return True
1533
1533
1534 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1534 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1535 """Return the result of interpolating items in the mapping into string s.
1535 """Return the result of interpolating items in the mapping into string s.
1536
1536
1537 prefix is a single character string, or a two character string with
1537 prefix is a single character string, or a two character string with
1538 a backslash as the first character if the prefix needs to be escaped in
1538 a backslash as the first character if the prefix needs to be escaped in
1539 a regular expression.
1539 a regular expression.
1540
1540
1541 fn is an optional function that will be applied to the replacement text
1541 fn is an optional function that will be applied to the replacement text
1542 just before replacement.
1542 just before replacement.
1543
1543
1544 escape_prefix is an optional flag that allows using doubled prefix for
1544 escape_prefix is an optional flag that allows using doubled prefix for
1545 its escaping.
1545 its escaping.
1546 """
1546 """
1547 fn = fn or (lambda s: s)
1547 fn = fn or (lambda s: s)
1548 patterns = '|'.join(mapping.keys())
1548 patterns = '|'.join(mapping.keys())
1549 if escape_prefix:
1549 if escape_prefix:
1550 patterns += '|' + prefix
1550 patterns += '|' + prefix
1551 if len(prefix) > 1:
1551 if len(prefix) > 1:
1552 prefix_char = prefix[1:]
1552 prefix_char = prefix[1:]
1553 else:
1553 else:
1554 prefix_char = prefix
1554 prefix_char = prefix
1555 mapping[prefix_char] = prefix_char
1555 mapping[prefix_char] = prefix_char
1556 r = re.compile(r'%s(%s)' % (prefix, patterns))
1556 r = re.compile(r'%s(%s)' % (prefix, patterns))
1557 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1557 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1558
1558
1559 def getport(port):
1559 def getport(port):
1560 """Return the port for a given network service.
1560 """Return the port for a given network service.
1561
1561
1562 If port is an integer, it's returned as is. If it's a string, it's
1562 If port is an integer, it's returned as is. If it's a string, it's
1563 looked up using socket.getservbyname(). If there's no matching
1563 looked up using socket.getservbyname(). If there's no matching
1564 service, util.Abort is raised.
1564 service, util.Abort is raised.
1565 """
1565 """
1566 try:
1566 try:
1567 return int(port)
1567 return int(port)
1568 except ValueError:
1568 except ValueError:
1569 pass
1569 pass
1570
1570
1571 try:
1571 try:
1572 return socket.getservbyname(port)
1572 return socket.getservbyname(port)
1573 except socket.error:
1573 except socket.error:
1574 raise Abort(_("no port number associated with service '%s'") % port)
1574 raise Abort(_("no port number associated with service '%s'") % port)
1575
1575
1576 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1576 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1577 '0': False, 'no': False, 'false': False, 'off': False,
1577 '0': False, 'no': False, 'false': False, 'off': False,
1578 'never': False}
1578 'never': False}
1579
1579
1580 def parsebool(s):
1580 def parsebool(s):
1581 """Parse s into a boolean.
1581 """Parse s into a boolean.
1582
1582
1583 If s is not a valid boolean, returns None.
1583 If s is not a valid boolean, returns None.
1584 """
1584 """
1585 return _booleans.get(s.lower(), None)
1585 return _booleans.get(s.lower(), None)
General Comments 0
You need to be logged in to leave comments. Login now