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