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