##// END OF EJS Templates
util: concentrate quoting knowledge to windows.py quotecommand()...
Steve Borho -
r13188:6c9345f9 stable
parent child Browse files
Show More
@@ -1,1485 +1,1483 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 if os.name == 'nt' and sys.version_info < (2, 7, 1):
394 cmd = quotecommand(cmd)
395 # Python versions since 2.7.1 do this extra quoting themselves
396 cmd = '"%s"' % cmd
397 env = dict(os.environ)
395 env = dict(os.environ)
398 env.update((k, py2shell(v)) for k, v in environ.iteritems())
396 env.update((k, py2shell(v)) for k, v in environ.iteritems())
399 env['HG'] = hgexecutable()
397 env['HG'] = hgexecutable()
400 if out is None:
398 if out is None:
401 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
399 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
402 env=env, cwd=cwd)
400 env=env, cwd=cwd)
403 else:
401 else:
404 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
402 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
405 env=env, cwd=cwd, stdout=subprocess.PIPE,
403 env=env, cwd=cwd, stdout=subprocess.PIPE,
406 stderr=subprocess.STDOUT)
404 stderr=subprocess.STDOUT)
407 for line in proc.stdout:
405 for line in proc.stdout:
408 out.write(line)
406 out.write(line)
409 proc.wait()
407 proc.wait()
410 rc = proc.returncode
408 rc = proc.returncode
411 if sys.platform == 'OpenVMS' and rc & 1:
409 if sys.platform == 'OpenVMS' and rc & 1:
412 rc = 0
410 rc = 0
413 if rc and onerr:
411 if rc and onerr:
414 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
412 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
415 explain_exit(rc)[0])
413 explain_exit(rc)[0])
416 if errprefix:
414 if errprefix:
417 errmsg = '%s: %s' % (errprefix, errmsg)
415 errmsg = '%s: %s' % (errprefix, errmsg)
418 try:
416 try:
419 onerr.warn(errmsg + '\n')
417 onerr.warn(errmsg + '\n')
420 except AttributeError:
418 except AttributeError:
421 raise onerr(errmsg)
419 raise onerr(errmsg)
422 return rc
420 return rc
423
421
424 def checksignature(func):
422 def checksignature(func):
425 '''wrap a function with code to check for calling errors'''
423 '''wrap a function with code to check for calling errors'''
426 def check(*args, **kwargs):
424 def check(*args, **kwargs):
427 try:
425 try:
428 return func(*args, **kwargs)
426 return func(*args, **kwargs)
429 except TypeError:
427 except TypeError:
430 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
428 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
431 raise error.SignatureError
429 raise error.SignatureError
432 raise
430 raise
433
431
434 return check
432 return check
435
433
436 def unlink(f):
434 def unlink(f):
437 """unlink and remove the directory if it is empty"""
435 """unlink and remove the directory if it is empty"""
438 os.unlink(f)
436 os.unlink(f)
439 # try removing directories that might now be empty
437 # try removing directories that might now be empty
440 try:
438 try:
441 os.removedirs(os.path.dirname(f))
439 os.removedirs(os.path.dirname(f))
442 except OSError:
440 except OSError:
443 pass
441 pass
444
442
445 def copyfile(src, dest):
443 def copyfile(src, dest):
446 "copy a file, preserving mode and atime/mtime"
444 "copy a file, preserving mode and atime/mtime"
447 if os.path.islink(src):
445 if os.path.islink(src):
448 try:
446 try:
449 os.unlink(dest)
447 os.unlink(dest)
450 except:
448 except:
451 pass
449 pass
452 os.symlink(os.readlink(src), dest)
450 os.symlink(os.readlink(src), dest)
453 else:
451 else:
454 try:
452 try:
455 shutil.copyfile(src, dest)
453 shutil.copyfile(src, dest)
456 shutil.copystat(src, dest)
454 shutil.copystat(src, dest)
457 except shutil.Error, inst:
455 except shutil.Error, inst:
458 raise Abort(str(inst))
456 raise Abort(str(inst))
459
457
460 def copyfiles(src, dst, hardlink=None):
458 def copyfiles(src, dst, hardlink=None):
461 """Copy a directory tree using hardlinks if possible"""
459 """Copy a directory tree using hardlinks if possible"""
462
460
463 if hardlink is None:
461 if hardlink is None:
464 hardlink = (os.stat(src).st_dev ==
462 hardlink = (os.stat(src).st_dev ==
465 os.stat(os.path.dirname(dst)).st_dev)
463 os.stat(os.path.dirname(dst)).st_dev)
466
464
467 num = 0
465 num = 0
468 if os.path.isdir(src):
466 if os.path.isdir(src):
469 os.mkdir(dst)
467 os.mkdir(dst)
470 for name, kind in osutil.listdir(src):
468 for name, kind in osutil.listdir(src):
471 srcname = os.path.join(src, name)
469 srcname = os.path.join(src, name)
472 dstname = os.path.join(dst, name)
470 dstname = os.path.join(dst, name)
473 hardlink, n = copyfiles(srcname, dstname, hardlink)
471 hardlink, n = copyfiles(srcname, dstname, hardlink)
474 num += n
472 num += n
475 else:
473 else:
476 if hardlink:
474 if hardlink:
477 try:
475 try:
478 os_link(src, dst)
476 os_link(src, dst)
479 except (IOError, OSError):
477 except (IOError, OSError):
480 hardlink = False
478 hardlink = False
481 shutil.copy(src, dst)
479 shutil.copy(src, dst)
482 else:
480 else:
483 shutil.copy(src, dst)
481 shutil.copy(src, dst)
484 num += 1
482 num += 1
485
483
486 return hardlink, num
484 return hardlink, num
487
485
488 class path_auditor(object):
486 class path_auditor(object):
489 '''ensure that a filesystem path contains no banned components.
487 '''ensure that a filesystem path contains no banned components.
490 the following properties of a path are checked:
488 the following properties of a path are checked:
491
489
492 - under top-level .hg
490 - under top-level .hg
493 - starts at the root of a windows drive
491 - starts at the root of a windows drive
494 - contains ".."
492 - contains ".."
495 - traverses a symlink (e.g. a/symlink_here/b)
493 - traverses a symlink (e.g. a/symlink_here/b)
496 - inside a nested repository (a callback can be used to approve
494 - inside a nested repository (a callback can be used to approve
497 some nested repositories, e.g., subrepositories)
495 some nested repositories, e.g., subrepositories)
498 '''
496 '''
499
497
500 def __init__(self, root, callback=None):
498 def __init__(self, root, callback=None):
501 self.audited = set()
499 self.audited = set()
502 self.auditeddir = set()
500 self.auditeddir = set()
503 self.root = root
501 self.root = root
504 self.callback = callback
502 self.callback = callback
505
503
506 def __call__(self, path):
504 def __call__(self, path):
507 if path in self.audited:
505 if path in self.audited:
508 return
506 return
509 normpath = os.path.normcase(path)
507 normpath = os.path.normcase(path)
510 parts = splitpath(normpath)
508 parts = splitpath(normpath)
511 if (os.path.splitdrive(path)[0]
509 if (os.path.splitdrive(path)[0]
512 or parts[0].lower() in ('.hg', '.hg.', '')
510 or parts[0].lower() in ('.hg', '.hg.', '')
513 or os.pardir in parts):
511 or os.pardir in parts):
514 raise Abort(_("path contains illegal component: %s") % path)
512 raise Abort(_("path contains illegal component: %s") % path)
515 if '.hg' in path.lower():
513 if '.hg' in path.lower():
516 lparts = [p.lower() for p in parts]
514 lparts = [p.lower() for p in parts]
517 for p in '.hg', '.hg.':
515 for p in '.hg', '.hg.':
518 if p in lparts[1:]:
516 if p in lparts[1:]:
519 pos = lparts.index(p)
517 pos = lparts.index(p)
520 base = os.path.join(*parts[:pos])
518 base = os.path.join(*parts[:pos])
521 raise Abort(_('path %r is inside repo %r') % (path, base))
519 raise Abort(_('path %r is inside repo %r') % (path, base))
522 def check(prefix):
520 def check(prefix):
523 curpath = os.path.join(self.root, prefix)
521 curpath = os.path.join(self.root, prefix)
524 try:
522 try:
525 st = os.lstat(curpath)
523 st = os.lstat(curpath)
526 except OSError, err:
524 except OSError, err:
527 # EINVAL can be raised as invalid path syntax under win32.
525 # EINVAL can be raised as invalid path syntax under win32.
528 # They must be ignored for patterns can be checked too.
526 # They must be ignored for patterns can be checked too.
529 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
527 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
530 raise
528 raise
531 else:
529 else:
532 if stat.S_ISLNK(st.st_mode):
530 if stat.S_ISLNK(st.st_mode):
533 raise Abort(_('path %r traverses symbolic link %r') %
531 raise Abort(_('path %r traverses symbolic link %r') %
534 (path, prefix))
532 (path, prefix))
535 elif (stat.S_ISDIR(st.st_mode) and
533 elif (stat.S_ISDIR(st.st_mode) and
536 os.path.isdir(os.path.join(curpath, '.hg'))):
534 os.path.isdir(os.path.join(curpath, '.hg'))):
537 if not self.callback or not self.callback(curpath):
535 if not self.callback or not self.callback(curpath):
538 raise Abort(_('path %r is inside repo %r') %
536 raise Abort(_('path %r is inside repo %r') %
539 (path, prefix))
537 (path, prefix))
540 parts.pop()
538 parts.pop()
541 prefixes = []
539 prefixes = []
542 while parts:
540 while parts:
543 prefix = os.sep.join(parts)
541 prefix = os.sep.join(parts)
544 if prefix in self.auditeddir:
542 if prefix in self.auditeddir:
545 break
543 break
546 check(prefix)
544 check(prefix)
547 prefixes.append(prefix)
545 prefixes.append(prefix)
548 parts.pop()
546 parts.pop()
549
547
550 self.audited.add(path)
548 self.audited.add(path)
551 # only add prefixes to the cache after checking everything: we don't
549 # only add prefixes to the cache after checking everything: we don't
552 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
550 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
553 self.auditeddir.update(prefixes)
551 self.auditeddir.update(prefixes)
554
552
555 def nlinks(pathname):
553 def nlinks(pathname):
556 """Return number of hardlinks for the given file."""
554 """Return number of hardlinks for the given file."""
557 return os.lstat(pathname).st_nlink
555 return os.lstat(pathname).st_nlink
558
556
559 if hasattr(os, 'link'):
557 if hasattr(os, 'link'):
560 os_link = os.link
558 os_link = os.link
561 else:
559 else:
562 def os_link(src, dst):
560 def os_link(src, dst):
563 raise OSError(0, _("Hardlinks not supported"))
561 raise OSError(0, _("Hardlinks not supported"))
564
562
565 def lookup_reg(key, name=None, scope=None):
563 def lookup_reg(key, name=None, scope=None):
566 return None
564 return None
567
565
568 def hidewindow():
566 def hidewindow():
569 """Hide current shell window.
567 """Hide current shell window.
570
568
571 Used to hide the window opened when starting asynchronous
569 Used to hide the window opened when starting asynchronous
572 child process under Windows, unneeded on other systems.
570 child process under Windows, unneeded on other systems.
573 """
571 """
574 pass
572 pass
575
573
576 if os.name == 'nt':
574 if os.name == 'nt':
577 from windows import *
575 from windows import *
578 else:
576 else:
579 from posix import *
577 from posix import *
580
578
581 def makelock(info, pathname):
579 def makelock(info, pathname):
582 try:
580 try:
583 return os.symlink(info, pathname)
581 return os.symlink(info, pathname)
584 except OSError, why:
582 except OSError, why:
585 if why.errno == errno.EEXIST:
583 if why.errno == errno.EEXIST:
586 raise
584 raise
587 except AttributeError: # no symlink in os
585 except AttributeError: # no symlink in os
588 pass
586 pass
589
587
590 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
588 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
591 os.write(ld, info)
589 os.write(ld, info)
592 os.close(ld)
590 os.close(ld)
593
591
594 def readlock(pathname):
592 def readlock(pathname):
595 try:
593 try:
596 return os.readlink(pathname)
594 return os.readlink(pathname)
597 except OSError, why:
595 except OSError, why:
598 if why.errno not in (errno.EINVAL, errno.ENOSYS):
596 if why.errno not in (errno.EINVAL, errno.ENOSYS):
599 raise
597 raise
600 except AttributeError: # no symlink in os
598 except AttributeError: # no symlink in os
601 pass
599 pass
602 return posixfile(pathname).read()
600 return posixfile(pathname).read()
603
601
604 def fstat(fp):
602 def fstat(fp):
605 '''stat file object that may not have fileno method.'''
603 '''stat file object that may not have fileno method.'''
606 try:
604 try:
607 return os.fstat(fp.fileno())
605 return os.fstat(fp.fileno())
608 except AttributeError:
606 except AttributeError:
609 return os.stat(fp.name)
607 return os.stat(fp.name)
610
608
611 # File system features
609 # File system features
612
610
613 def checkcase(path):
611 def checkcase(path):
614 """
612 """
615 Check whether the given path is on a case-sensitive filesystem
613 Check whether the given path is on a case-sensitive filesystem
616
614
617 Requires a path (like /foo/.hg) ending with a foldable final
615 Requires a path (like /foo/.hg) ending with a foldable final
618 directory component.
616 directory component.
619 """
617 """
620 s1 = os.stat(path)
618 s1 = os.stat(path)
621 d, b = os.path.split(path)
619 d, b = os.path.split(path)
622 p2 = os.path.join(d, b.upper())
620 p2 = os.path.join(d, b.upper())
623 if path == p2:
621 if path == p2:
624 p2 = os.path.join(d, b.lower())
622 p2 = os.path.join(d, b.lower())
625 try:
623 try:
626 s2 = os.stat(p2)
624 s2 = os.stat(p2)
627 if s2 == s1:
625 if s2 == s1:
628 return False
626 return False
629 return True
627 return True
630 except:
628 except:
631 return True
629 return True
632
630
633 _fspathcache = {}
631 _fspathcache = {}
634 def fspath(name, root):
632 def fspath(name, root):
635 '''Get name in the case stored in the filesystem
633 '''Get name in the case stored in the filesystem
636
634
637 The name is either relative to root, or it is an absolute path starting
635 The name is either relative to root, or it is an absolute path starting
638 with root. Note that this function is unnecessary, and should not be
636 with root. Note that this function is unnecessary, and should not be
639 called, for case-sensitive filesystems (simply because it's expensive).
637 called, for case-sensitive filesystems (simply because it's expensive).
640 '''
638 '''
641 # If name is absolute, make it relative
639 # If name is absolute, make it relative
642 if name.lower().startswith(root.lower()):
640 if name.lower().startswith(root.lower()):
643 l = len(root)
641 l = len(root)
644 if name[l] == os.sep or name[l] == os.altsep:
642 if name[l] == os.sep or name[l] == os.altsep:
645 l = l + 1
643 l = l + 1
646 name = name[l:]
644 name = name[l:]
647
645
648 if not os.path.lexists(os.path.join(root, name)):
646 if not os.path.lexists(os.path.join(root, name)):
649 return None
647 return None
650
648
651 seps = os.sep
649 seps = os.sep
652 if os.altsep:
650 if os.altsep:
653 seps = seps + os.altsep
651 seps = seps + os.altsep
654 # Protect backslashes. This gets silly very quickly.
652 # Protect backslashes. This gets silly very quickly.
655 seps.replace('\\','\\\\')
653 seps.replace('\\','\\\\')
656 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
654 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
657 dir = os.path.normcase(os.path.normpath(root))
655 dir = os.path.normcase(os.path.normpath(root))
658 result = []
656 result = []
659 for part, sep in pattern.findall(name):
657 for part, sep in pattern.findall(name):
660 if sep:
658 if sep:
661 result.append(sep)
659 result.append(sep)
662 continue
660 continue
663
661
664 if dir not in _fspathcache:
662 if dir not in _fspathcache:
665 _fspathcache[dir] = os.listdir(dir)
663 _fspathcache[dir] = os.listdir(dir)
666 contents = _fspathcache[dir]
664 contents = _fspathcache[dir]
667
665
668 lpart = part.lower()
666 lpart = part.lower()
669 lenp = len(part)
667 lenp = len(part)
670 for n in contents:
668 for n in contents:
671 if lenp == len(n) and n.lower() == lpart:
669 if lenp == len(n) and n.lower() == lpart:
672 result.append(n)
670 result.append(n)
673 break
671 break
674 else:
672 else:
675 # Cannot happen, as the file exists!
673 # Cannot happen, as the file exists!
676 result.append(part)
674 result.append(part)
677 dir = os.path.join(dir, lpart)
675 dir = os.path.join(dir, lpart)
678
676
679 return ''.join(result)
677 return ''.join(result)
680
678
681 def checkexec(path):
679 def checkexec(path):
682 """
680 """
683 Check whether the given path is on a filesystem with UNIX-like exec flags
681 Check whether the given path is on a filesystem with UNIX-like exec flags
684
682
685 Requires a directory (like /foo/.hg)
683 Requires a directory (like /foo/.hg)
686 """
684 """
687
685
688 # VFAT on some Linux versions can flip mode but it doesn't persist
686 # VFAT on some Linux versions can flip mode but it doesn't persist
689 # a FS remount. Frequently we can detect it if files are created
687 # a FS remount. Frequently we can detect it if files are created
690 # with exec bit on.
688 # with exec bit on.
691
689
692 try:
690 try:
693 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
691 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
694 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
692 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
695 try:
693 try:
696 os.close(fh)
694 os.close(fh)
697 m = os.stat(fn).st_mode & 0777
695 m = os.stat(fn).st_mode & 0777
698 new_file_has_exec = m & EXECFLAGS
696 new_file_has_exec = m & EXECFLAGS
699 os.chmod(fn, m ^ EXECFLAGS)
697 os.chmod(fn, m ^ EXECFLAGS)
700 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
698 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
701 finally:
699 finally:
702 os.unlink(fn)
700 os.unlink(fn)
703 except (IOError, OSError):
701 except (IOError, OSError):
704 # we don't care, the user probably won't be able to commit anyway
702 # we don't care, the user probably won't be able to commit anyway
705 return False
703 return False
706 return not (new_file_has_exec or exec_flags_cannot_flip)
704 return not (new_file_has_exec or exec_flags_cannot_flip)
707
705
708 def checklink(path):
706 def checklink(path):
709 """check whether the given path is on a symlink-capable filesystem"""
707 """check whether the given path is on a symlink-capable filesystem"""
710 # mktemp is not racy because symlink creation will fail if the
708 # mktemp is not racy because symlink creation will fail if the
711 # file already exists
709 # file already exists
712 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
710 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
713 try:
711 try:
714 os.symlink(".", name)
712 os.symlink(".", name)
715 os.unlink(name)
713 os.unlink(name)
716 return True
714 return True
717 except (OSError, AttributeError):
715 except (OSError, AttributeError):
718 return False
716 return False
719
717
720 def checknlink(testfile):
718 def checknlink(testfile):
721 '''check whether hardlink count reporting works properly'''
719 '''check whether hardlink count reporting works properly'''
722 f = testfile + ".hgtmp"
720 f = testfile + ".hgtmp"
723
721
724 try:
722 try:
725 os_link(testfile, f)
723 os_link(testfile, f)
726 except OSError:
724 except OSError:
727 return False
725 return False
728
726
729 try:
727 try:
730 # nlinks() may behave differently for files on Windows shares if
728 # nlinks() may behave differently for files on Windows shares if
731 # the file is open.
729 # the file is open.
732 fd = open(f)
730 fd = open(f)
733 return nlinks(f) > 1
731 return nlinks(f) > 1
734 finally:
732 finally:
735 fd.close()
733 fd.close()
736 os.unlink(f)
734 os.unlink(f)
737
735
738 return False
736 return False
739
737
740 def endswithsep(path):
738 def endswithsep(path):
741 '''Check path ends with os.sep or os.altsep.'''
739 '''Check path ends with os.sep or os.altsep.'''
742 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
740 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
743
741
744 def splitpath(path):
742 def splitpath(path):
745 '''Split path by os.sep.
743 '''Split path by os.sep.
746 Note that this function does not use os.altsep because this is
744 Note that this function does not use os.altsep because this is
747 an alternative of simple "xxx.split(os.sep)".
745 an alternative of simple "xxx.split(os.sep)".
748 It is recommended to use os.path.normpath() before using this
746 It is recommended to use os.path.normpath() before using this
749 function if need.'''
747 function if need.'''
750 return path.split(os.sep)
748 return path.split(os.sep)
751
749
752 def gui():
750 def gui():
753 '''Are we running in a GUI?'''
751 '''Are we running in a GUI?'''
754 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
752 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
755
753
756 def mktempcopy(name, emptyok=False, createmode=None):
754 def mktempcopy(name, emptyok=False, createmode=None):
757 """Create a temporary file with the same contents from name
755 """Create a temporary file with the same contents from name
758
756
759 The permission bits are copied from the original file.
757 The permission bits are copied from the original file.
760
758
761 If the temporary file is going to be truncated immediately, you
759 If the temporary file is going to be truncated immediately, you
762 can use emptyok=True as an optimization.
760 can use emptyok=True as an optimization.
763
761
764 Returns the name of the temporary file.
762 Returns the name of the temporary file.
765 """
763 """
766 d, fn = os.path.split(name)
764 d, fn = os.path.split(name)
767 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
765 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
768 os.close(fd)
766 os.close(fd)
769 # Temporary files are created with mode 0600, which is usually not
767 # Temporary files are created with mode 0600, which is usually not
770 # what we want. If the original file already exists, just copy
768 # what we want. If the original file already exists, just copy
771 # its mode. Otherwise, manually obey umask.
769 # its mode. Otherwise, manually obey umask.
772 try:
770 try:
773 st_mode = os.lstat(name).st_mode & 0777
771 st_mode = os.lstat(name).st_mode & 0777
774 except OSError, inst:
772 except OSError, inst:
775 if inst.errno != errno.ENOENT:
773 if inst.errno != errno.ENOENT:
776 raise
774 raise
777 st_mode = createmode
775 st_mode = createmode
778 if st_mode is None:
776 if st_mode is None:
779 st_mode = ~umask
777 st_mode = ~umask
780 st_mode &= 0666
778 st_mode &= 0666
781 os.chmod(temp, st_mode)
779 os.chmod(temp, st_mode)
782 if emptyok:
780 if emptyok:
783 return temp
781 return temp
784 try:
782 try:
785 try:
783 try:
786 ifp = posixfile(name, "rb")
784 ifp = posixfile(name, "rb")
787 except IOError, inst:
785 except IOError, inst:
788 if inst.errno == errno.ENOENT:
786 if inst.errno == errno.ENOENT:
789 return temp
787 return temp
790 if not getattr(inst, 'filename', None):
788 if not getattr(inst, 'filename', None):
791 inst.filename = name
789 inst.filename = name
792 raise
790 raise
793 ofp = posixfile(temp, "wb")
791 ofp = posixfile(temp, "wb")
794 for chunk in filechunkiter(ifp):
792 for chunk in filechunkiter(ifp):
795 ofp.write(chunk)
793 ofp.write(chunk)
796 ifp.close()
794 ifp.close()
797 ofp.close()
795 ofp.close()
798 except:
796 except:
799 try: os.unlink(temp)
797 try: os.unlink(temp)
800 except: pass
798 except: pass
801 raise
799 raise
802 return temp
800 return temp
803
801
804 class atomictempfile(object):
802 class atomictempfile(object):
805 """file-like object that atomically updates a file
803 """file-like object that atomically updates a file
806
804
807 All writes will be redirected to a temporary copy of the original
805 All writes will be redirected to a temporary copy of the original
808 file. When rename is called, the copy is renamed to the original
806 file. When rename is called, the copy is renamed to the original
809 name, making the changes visible.
807 name, making the changes visible.
810 """
808 """
811 def __init__(self, name, mode='w+b', createmode=None):
809 def __init__(self, name, mode='w+b', createmode=None):
812 self.__name = name
810 self.__name = name
813 self._fp = None
811 self._fp = None
814 self.temp = mktempcopy(name, emptyok=('w' in mode),
812 self.temp = mktempcopy(name, emptyok=('w' in mode),
815 createmode=createmode)
813 createmode=createmode)
816 self._fp = posixfile(self.temp, mode)
814 self._fp = posixfile(self.temp, mode)
817
815
818 def __getattr__(self, name):
816 def __getattr__(self, name):
819 return getattr(self._fp, name)
817 return getattr(self._fp, name)
820
818
821 def rename(self):
819 def rename(self):
822 if not self._fp.closed:
820 if not self._fp.closed:
823 self._fp.close()
821 self._fp.close()
824 rename(self.temp, localpath(self.__name))
822 rename(self.temp, localpath(self.__name))
825
823
826 def __del__(self):
824 def __del__(self):
827 if not self._fp:
825 if not self._fp:
828 return
826 return
829 if not self._fp.closed:
827 if not self._fp.closed:
830 try:
828 try:
831 os.unlink(self.temp)
829 os.unlink(self.temp)
832 except: pass
830 except: pass
833 self._fp.close()
831 self._fp.close()
834
832
835 def makedirs(name, mode=None):
833 def makedirs(name, mode=None):
836 """recursive directory creation with parent mode inheritance"""
834 """recursive directory creation with parent mode inheritance"""
837 parent = os.path.abspath(os.path.dirname(name))
835 parent = os.path.abspath(os.path.dirname(name))
838 try:
836 try:
839 os.mkdir(name)
837 os.mkdir(name)
840 if mode is not None:
838 if mode is not None:
841 os.chmod(name, mode)
839 os.chmod(name, mode)
842 return
840 return
843 except OSError, err:
841 except OSError, err:
844 if err.errno == errno.EEXIST:
842 if err.errno == errno.EEXIST:
845 return
843 return
846 if not name or parent == name or err.errno != errno.ENOENT:
844 if not name or parent == name or err.errno != errno.ENOENT:
847 raise
845 raise
848 makedirs(parent, mode)
846 makedirs(parent, mode)
849 makedirs(name, mode)
847 makedirs(name, mode)
850
848
851 class opener(object):
849 class opener(object):
852 """Open files relative to a base directory
850 """Open files relative to a base directory
853
851
854 This class is used to hide the details of COW semantics and
852 This class is used to hide the details of COW semantics and
855 remote file access from higher level code.
853 remote file access from higher level code.
856 """
854 """
857 def __init__(self, base, audit=True):
855 def __init__(self, base, audit=True):
858 self.base = base
856 self.base = base
859 if audit:
857 if audit:
860 self.auditor = path_auditor(base)
858 self.auditor = path_auditor(base)
861 else:
859 else:
862 self.auditor = always
860 self.auditor = always
863 self.createmode = None
861 self.createmode = None
864 self._trustnlink = None
862 self._trustnlink = None
865
863
866 @propertycache
864 @propertycache
867 def _can_symlink(self):
865 def _can_symlink(self):
868 return checklink(self.base)
866 return checklink(self.base)
869
867
870 def _fixfilemode(self, name):
868 def _fixfilemode(self, name):
871 if self.createmode is None:
869 if self.createmode is None:
872 return
870 return
873 os.chmod(name, self.createmode & 0666)
871 os.chmod(name, self.createmode & 0666)
874
872
875 def __call__(self, path, mode="r", text=False, atomictemp=False):
873 def __call__(self, path, mode="r", text=False, atomictemp=False):
876 self.auditor(path)
874 self.auditor(path)
877 f = os.path.join(self.base, path)
875 f = os.path.join(self.base, path)
878
876
879 if not text and "b" not in mode:
877 if not text and "b" not in mode:
880 mode += "b" # for that other OS
878 mode += "b" # for that other OS
881
879
882 nlink = -1
880 nlink = -1
883 st_mode = None
881 st_mode = None
884 dirname, basename = os.path.split(f)
882 dirname, basename = os.path.split(f)
885 # If basename is empty, then the path is malformed because it points
883 # If basename is empty, then the path is malformed because it points
886 # to a directory. Let the posixfile() call below raise IOError.
884 # to a directory. Let the posixfile() call below raise IOError.
887 if basename and mode not in ('r', 'rb'):
885 if basename and mode not in ('r', 'rb'):
888 if atomictemp:
886 if atomictemp:
889 if not os.path.isdir(dirname):
887 if not os.path.isdir(dirname):
890 makedirs(dirname, self.createmode)
888 makedirs(dirname, self.createmode)
891 return atomictempfile(f, mode, self.createmode)
889 return atomictempfile(f, mode, self.createmode)
892 try:
890 try:
893 if 'w' in mode:
891 if 'w' in mode:
894 st_mode = os.lstat(f).st_mode & 0777
892 st_mode = os.lstat(f).st_mode & 0777
895 os.unlink(f)
893 os.unlink(f)
896 nlink = 0
894 nlink = 0
897 else:
895 else:
898 # nlinks() may behave differently for files on Windows
896 # nlinks() may behave differently for files on Windows
899 # shares if the file is open.
897 # shares if the file is open.
900 fd = open(f)
898 fd = open(f)
901 nlink = nlinks(f)
899 nlink = nlinks(f)
902 fd.close()
900 fd.close()
903 except (OSError, IOError):
901 except (OSError, IOError):
904 nlink = 0
902 nlink = 0
905 if not os.path.isdir(dirname):
903 if not os.path.isdir(dirname):
906 makedirs(dirname, self.createmode)
904 makedirs(dirname, self.createmode)
907 if nlink > 0:
905 if nlink > 0:
908 if self._trustnlink is None:
906 if self._trustnlink is None:
909 self._trustnlink = nlink > 1 or checknlink(f)
907 self._trustnlink = nlink > 1 or checknlink(f)
910 if nlink > 1 or not self._trustnlink:
908 if nlink > 1 or not self._trustnlink:
911 rename(mktempcopy(f), f)
909 rename(mktempcopy(f), f)
912 fp = posixfile(f, mode)
910 fp = posixfile(f, mode)
913 if nlink == 0:
911 if nlink == 0:
914 if st_mode is None:
912 if st_mode is None:
915 self._fixfilemode(f)
913 self._fixfilemode(f)
916 else:
914 else:
917 os.chmod(f, st_mode)
915 os.chmod(f, st_mode)
918 return fp
916 return fp
919
917
920 def symlink(self, src, dst):
918 def symlink(self, src, dst):
921 self.auditor(dst)
919 self.auditor(dst)
922 linkname = os.path.join(self.base, dst)
920 linkname = os.path.join(self.base, dst)
923 try:
921 try:
924 os.unlink(linkname)
922 os.unlink(linkname)
925 except OSError:
923 except OSError:
926 pass
924 pass
927
925
928 dirname = os.path.dirname(linkname)
926 dirname = os.path.dirname(linkname)
929 if not os.path.exists(dirname):
927 if not os.path.exists(dirname):
930 makedirs(dirname, self.createmode)
928 makedirs(dirname, self.createmode)
931
929
932 if self._can_symlink:
930 if self._can_symlink:
933 try:
931 try:
934 os.symlink(src, linkname)
932 os.symlink(src, linkname)
935 except OSError, err:
933 except OSError, err:
936 raise OSError(err.errno, _('could not symlink to %r: %s') %
934 raise OSError(err.errno, _('could not symlink to %r: %s') %
937 (src, err.strerror), linkname)
935 (src, err.strerror), linkname)
938 else:
936 else:
939 f = self(dst, "w")
937 f = self(dst, "w")
940 f.write(src)
938 f.write(src)
941 f.close()
939 f.close()
942 self._fixfilemode(dst)
940 self._fixfilemode(dst)
943
941
944 class chunkbuffer(object):
942 class chunkbuffer(object):
945 """Allow arbitrary sized chunks of data to be efficiently read from an
943 """Allow arbitrary sized chunks of data to be efficiently read from an
946 iterator over chunks of arbitrary size."""
944 iterator over chunks of arbitrary size."""
947
945
948 def __init__(self, in_iter):
946 def __init__(self, in_iter):
949 """in_iter is the iterator that's iterating over the input chunks.
947 """in_iter is the iterator that's iterating over the input chunks.
950 targetsize is how big a buffer to try to maintain."""
948 targetsize is how big a buffer to try to maintain."""
951 def splitbig(chunks):
949 def splitbig(chunks):
952 for chunk in chunks:
950 for chunk in chunks:
953 if len(chunk) > 2**20:
951 if len(chunk) > 2**20:
954 pos = 0
952 pos = 0
955 while pos < len(chunk):
953 while pos < len(chunk):
956 end = pos + 2 ** 18
954 end = pos + 2 ** 18
957 yield chunk[pos:end]
955 yield chunk[pos:end]
958 pos = end
956 pos = end
959 else:
957 else:
960 yield chunk
958 yield chunk
961 self.iter = splitbig(in_iter)
959 self.iter = splitbig(in_iter)
962 self._queue = []
960 self._queue = []
963
961
964 def read(self, l):
962 def read(self, l):
965 """Read L bytes of data from the iterator of chunks of data.
963 """Read L bytes of data from the iterator of chunks of data.
966 Returns less than L bytes if the iterator runs dry."""
964 Returns less than L bytes if the iterator runs dry."""
967 left = l
965 left = l
968 buf = ''
966 buf = ''
969 queue = self._queue
967 queue = self._queue
970 while left > 0:
968 while left > 0:
971 # refill the queue
969 # refill the queue
972 if not queue:
970 if not queue:
973 target = 2**18
971 target = 2**18
974 for chunk in self.iter:
972 for chunk in self.iter:
975 queue.append(chunk)
973 queue.append(chunk)
976 target -= len(chunk)
974 target -= len(chunk)
977 if target <= 0:
975 if target <= 0:
978 break
976 break
979 if not queue:
977 if not queue:
980 break
978 break
981
979
982 chunk = queue.pop(0)
980 chunk = queue.pop(0)
983 left -= len(chunk)
981 left -= len(chunk)
984 if left < 0:
982 if left < 0:
985 queue.insert(0, chunk[left:])
983 queue.insert(0, chunk[left:])
986 buf += chunk[:left]
984 buf += chunk[:left]
987 else:
985 else:
988 buf += chunk
986 buf += chunk
989
987
990 return buf
988 return buf
991
989
992 def filechunkiter(f, size=65536, limit=None):
990 def filechunkiter(f, size=65536, limit=None):
993 """Create a generator that produces the data in the file size
991 """Create a generator that produces the data in the file size
994 (default 65536) bytes at a time, up to optional limit (default is
992 (default 65536) bytes at a time, up to optional limit (default is
995 to read all data). Chunks may be less than size bytes if the
993 to read all data). Chunks may be less than size bytes if the
996 chunk is the last chunk in the file, or the file is a socket or
994 chunk is the last chunk in the file, or the file is a socket or
997 some other type of file that sometimes reads less data than is
995 some other type of file that sometimes reads less data than is
998 requested."""
996 requested."""
999 assert size >= 0
997 assert size >= 0
1000 assert limit is None or limit >= 0
998 assert limit is None or limit >= 0
1001 while True:
999 while True:
1002 if limit is None:
1000 if limit is None:
1003 nbytes = size
1001 nbytes = size
1004 else:
1002 else:
1005 nbytes = min(limit, size)
1003 nbytes = min(limit, size)
1006 s = nbytes and f.read(nbytes)
1004 s = nbytes and f.read(nbytes)
1007 if not s:
1005 if not s:
1008 break
1006 break
1009 if limit:
1007 if limit:
1010 limit -= len(s)
1008 limit -= len(s)
1011 yield s
1009 yield s
1012
1010
1013 def makedate():
1011 def makedate():
1014 lt = time.localtime()
1012 lt = time.localtime()
1015 if lt[8] == 1 and time.daylight:
1013 if lt[8] == 1 and time.daylight:
1016 tz = time.altzone
1014 tz = time.altzone
1017 else:
1015 else:
1018 tz = time.timezone
1016 tz = time.timezone
1019 t = time.mktime(lt)
1017 t = time.mktime(lt)
1020 if t < 0:
1018 if t < 0:
1021 hint = _("check your clock")
1019 hint = _("check your clock")
1022 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1020 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1023 return t, tz
1021 return t, tz
1024
1022
1025 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1023 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1026 """represent a (unixtime, offset) tuple as a localized time.
1024 """represent a (unixtime, offset) tuple as a localized time.
1027 unixtime is seconds since the epoch, and offset is the time zone's
1025 unixtime is seconds since the epoch, and offset is the time zone's
1028 number of seconds away from UTC. if timezone is false, do not
1026 number of seconds away from UTC. if timezone is false, do not
1029 append time zone to string."""
1027 append time zone to string."""
1030 t, tz = date or makedate()
1028 t, tz = date or makedate()
1031 if t < 0:
1029 if t < 0:
1032 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1030 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1033 tz = 0
1031 tz = 0
1034 if "%1" in format or "%2" in format:
1032 if "%1" in format or "%2" in format:
1035 sign = (tz > 0) and "-" or "+"
1033 sign = (tz > 0) and "-" or "+"
1036 minutes = abs(tz) // 60
1034 minutes = abs(tz) // 60
1037 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1035 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1038 format = format.replace("%2", "%02d" % (minutes % 60))
1036 format = format.replace("%2", "%02d" % (minutes % 60))
1039 s = time.strftime(format, time.gmtime(float(t) - tz))
1037 s = time.strftime(format, time.gmtime(float(t) - tz))
1040 return s
1038 return s
1041
1039
1042 def shortdate(date=None):
1040 def shortdate(date=None):
1043 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1041 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1044 return datestr(date, format='%Y-%m-%d')
1042 return datestr(date, format='%Y-%m-%d')
1045
1043
1046 def strdate(string, format, defaults=[]):
1044 def strdate(string, format, defaults=[]):
1047 """parse a localized time string and return a (unixtime, offset) tuple.
1045 """parse a localized time string and return a (unixtime, offset) tuple.
1048 if the string cannot be parsed, ValueError is raised."""
1046 if the string cannot be parsed, ValueError is raised."""
1049 def timezone(string):
1047 def timezone(string):
1050 tz = string.split()[-1]
1048 tz = string.split()[-1]
1051 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1049 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1052 sign = (tz[0] == "+") and 1 or -1
1050 sign = (tz[0] == "+") and 1 or -1
1053 hours = int(tz[1:3])
1051 hours = int(tz[1:3])
1054 minutes = int(tz[3:5])
1052 minutes = int(tz[3:5])
1055 return -sign * (hours * 60 + minutes) * 60
1053 return -sign * (hours * 60 + minutes) * 60
1056 if tz == "GMT" or tz == "UTC":
1054 if tz == "GMT" or tz == "UTC":
1057 return 0
1055 return 0
1058 return None
1056 return None
1059
1057
1060 # NOTE: unixtime = localunixtime + offset
1058 # NOTE: unixtime = localunixtime + offset
1061 offset, date = timezone(string), string
1059 offset, date = timezone(string), string
1062 if offset != None:
1060 if offset != None:
1063 date = " ".join(string.split()[:-1])
1061 date = " ".join(string.split()[:-1])
1064
1062
1065 # add missing elements from defaults
1063 # add missing elements from defaults
1066 for part in defaults:
1064 for part in defaults:
1067 found = [True for p in part if ("%"+p) in format]
1065 found = [True for p in part if ("%"+p) in format]
1068 if not found:
1066 if not found:
1069 date += "@" + defaults[part]
1067 date += "@" + defaults[part]
1070 format += "@%" + part[0]
1068 format += "@%" + part[0]
1071
1069
1072 timetuple = time.strptime(date, format)
1070 timetuple = time.strptime(date, format)
1073 localunixtime = int(calendar.timegm(timetuple))
1071 localunixtime = int(calendar.timegm(timetuple))
1074 if offset is None:
1072 if offset is None:
1075 # local timezone
1073 # local timezone
1076 unixtime = int(time.mktime(timetuple))
1074 unixtime = int(time.mktime(timetuple))
1077 offset = unixtime - localunixtime
1075 offset = unixtime - localunixtime
1078 else:
1076 else:
1079 unixtime = localunixtime + offset
1077 unixtime = localunixtime + offset
1080 return unixtime, offset
1078 return unixtime, offset
1081
1079
1082 def parsedate(date, formats=None, defaults=None):
1080 def parsedate(date, formats=None, defaults=None):
1083 """parse a localized date/time string and return a (unixtime, offset) tuple.
1081 """parse a localized date/time string and return a (unixtime, offset) tuple.
1084
1082
1085 The date may be a "unixtime offset" string or in one of the specified
1083 The date may be a "unixtime offset" string or in one of the specified
1086 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1084 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1087 """
1085 """
1088 if not date:
1086 if not date:
1089 return 0, 0
1087 return 0, 0
1090 if isinstance(date, tuple) and len(date) == 2:
1088 if isinstance(date, tuple) and len(date) == 2:
1091 return date
1089 return date
1092 if not formats:
1090 if not formats:
1093 formats = defaultdateformats
1091 formats = defaultdateformats
1094 date = date.strip()
1092 date = date.strip()
1095 try:
1093 try:
1096 when, offset = map(int, date.split(' '))
1094 when, offset = map(int, date.split(' '))
1097 except ValueError:
1095 except ValueError:
1098 # fill out defaults
1096 # fill out defaults
1099 if not defaults:
1097 if not defaults:
1100 defaults = {}
1098 defaults = {}
1101 now = makedate()
1099 now = makedate()
1102 for part in "d mb yY HI M S".split():
1100 for part in "d mb yY HI M S".split():
1103 if part not in defaults:
1101 if part not in defaults:
1104 if part[0] in "HMS":
1102 if part[0] in "HMS":
1105 defaults[part] = "00"
1103 defaults[part] = "00"
1106 else:
1104 else:
1107 defaults[part] = datestr(now, "%" + part[0])
1105 defaults[part] = datestr(now, "%" + part[0])
1108
1106
1109 for format in formats:
1107 for format in formats:
1110 try:
1108 try:
1111 when, offset = strdate(date, format, defaults)
1109 when, offset = strdate(date, format, defaults)
1112 except (ValueError, OverflowError):
1110 except (ValueError, OverflowError):
1113 pass
1111 pass
1114 else:
1112 else:
1115 break
1113 break
1116 else:
1114 else:
1117 raise Abort(_('invalid date: %r') % date)
1115 raise Abort(_('invalid date: %r') % date)
1118 # validate explicit (probably user-specified) date and
1116 # validate explicit (probably user-specified) date and
1119 # time zone offset. values must fit in signed 32 bits for
1117 # time zone offset. values must fit in signed 32 bits for
1120 # current 32-bit linux runtimes. timezones go from UTC-12
1118 # current 32-bit linux runtimes. timezones go from UTC-12
1121 # to UTC+14
1119 # to UTC+14
1122 if abs(when) > 0x7fffffff:
1120 if abs(when) > 0x7fffffff:
1123 raise Abort(_('date exceeds 32 bits: %d') % when)
1121 raise Abort(_('date exceeds 32 bits: %d') % when)
1124 if when < 0:
1122 if when < 0:
1125 raise Abort(_('negative date value: %d') % when)
1123 raise Abort(_('negative date value: %d') % when)
1126 if offset < -50400 or offset > 43200:
1124 if offset < -50400 or offset > 43200:
1127 raise Abort(_('impossible time zone offset: %d') % offset)
1125 raise Abort(_('impossible time zone offset: %d') % offset)
1128 return when, offset
1126 return when, offset
1129
1127
1130 def matchdate(date):
1128 def matchdate(date):
1131 """Return a function that matches a given date match specifier
1129 """Return a function that matches a given date match specifier
1132
1130
1133 Formats include:
1131 Formats include:
1134
1132
1135 '{date}' match a given date to the accuracy provided
1133 '{date}' match a given date to the accuracy provided
1136
1134
1137 '<{date}' on or before a given date
1135 '<{date}' on or before a given date
1138
1136
1139 '>{date}' on or after a given date
1137 '>{date}' on or after a given date
1140
1138
1141 """
1139 """
1142
1140
1143 def lower(date):
1141 def lower(date):
1144 d = dict(mb="1", d="1")
1142 d = dict(mb="1", d="1")
1145 return parsedate(date, extendeddateformats, d)[0]
1143 return parsedate(date, extendeddateformats, d)[0]
1146
1144
1147 def upper(date):
1145 def upper(date):
1148 d = dict(mb="12", HI="23", M="59", S="59")
1146 d = dict(mb="12", HI="23", M="59", S="59")
1149 for days in "31 30 29".split():
1147 for days in "31 30 29".split():
1150 try:
1148 try:
1151 d["d"] = days
1149 d["d"] = days
1152 return parsedate(date, extendeddateformats, d)[0]
1150 return parsedate(date, extendeddateformats, d)[0]
1153 except:
1151 except:
1154 pass
1152 pass
1155 d["d"] = "28"
1153 d["d"] = "28"
1156 return parsedate(date, extendeddateformats, d)[0]
1154 return parsedate(date, extendeddateformats, d)[0]
1157
1155
1158 date = date.strip()
1156 date = date.strip()
1159 if date[0] == "<":
1157 if date[0] == "<":
1160 when = upper(date[1:])
1158 when = upper(date[1:])
1161 return lambda x: x <= when
1159 return lambda x: x <= when
1162 elif date[0] == ">":
1160 elif date[0] == ">":
1163 when = lower(date[1:])
1161 when = lower(date[1:])
1164 return lambda x: x >= when
1162 return lambda x: x >= when
1165 elif date[0] == "-":
1163 elif date[0] == "-":
1166 try:
1164 try:
1167 days = int(date[1:])
1165 days = int(date[1:])
1168 except ValueError:
1166 except ValueError:
1169 raise Abort(_("invalid day spec: %s") % date[1:])
1167 raise Abort(_("invalid day spec: %s") % date[1:])
1170 when = makedate()[0] - days * 3600 * 24
1168 when = makedate()[0] - days * 3600 * 24
1171 return lambda x: x >= when
1169 return lambda x: x >= when
1172 elif " to " in date:
1170 elif " to " in date:
1173 a, b = date.split(" to ")
1171 a, b = date.split(" to ")
1174 start, stop = lower(a), upper(b)
1172 start, stop = lower(a), upper(b)
1175 return lambda x: x >= start and x <= stop
1173 return lambda x: x >= start and x <= stop
1176 else:
1174 else:
1177 start, stop = lower(date), upper(date)
1175 start, stop = lower(date), upper(date)
1178 return lambda x: x >= start and x <= stop
1176 return lambda x: x >= start and x <= stop
1179
1177
1180 def shortuser(user):
1178 def shortuser(user):
1181 """Return a short representation of a user name or email address."""
1179 """Return a short representation of a user name or email address."""
1182 f = user.find('@')
1180 f = user.find('@')
1183 if f >= 0:
1181 if f >= 0:
1184 user = user[:f]
1182 user = user[:f]
1185 f = user.find('<')
1183 f = user.find('<')
1186 if f >= 0:
1184 if f >= 0:
1187 user = user[f + 1:]
1185 user = user[f + 1:]
1188 f = user.find(' ')
1186 f = user.find(' ')
1189 if f >= 0:
1187 if f >= 0:
1190 user = user[:f]
1188 user = user[:f]
1191 f = user.find('.')
1189 f = user.find('.')
1192 if f >= 0:
1190 if f >= 0:
1193 user = user[:f]
1191 user = user[:f]
1194 return user
1192 return user
1195
1193
1196 def email(author):
1194 def email(author):
1197 '''get email of author.'''
1195 '''get email of author.'''
1198 r = author.find('>')
1196 r = author.find('>')
1199 if r == -1:
1197 if r == -1:
1200 r = None
1198 r = None
1201 return author[author.find('<') + 1:r]
1199 return author[author.find('<') + 1:r]
1202
1200
1203 def ellipsis(text, maxlength=400):
1201 def ellipsis(text, maxlength=400):
1204 """Trim string to at most maxlength (default: 400) characters."""
1202 """Trim string to at most maxlength (default: 400) characters."""
1205 if len(text) <= maxlength:
1203 if len(text) <= maxlength:
1206 return text
1204 return text
1207 else:
1205 else:
1208 return "%s..." % (text[:maxlength - 3])
1206 return "%s..." % (text[:maxlength - 3])
1209
1207
1210 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1208 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1211 '''yield every hg repository under path, recursively.'''
1209 '''yield every hg repository under path, recursively.'''
1212 def errhandler(err):
1210 def errhandler(err):
1213 if err.filename == path:
1211 if err.filename == path:
1214 raise err
1212 raise err
1215 if followsym and hasattr(os.path, 'samestat'):
1213 if followsym and hasattr(os.path, 'samestat'):
1216 def _add_dir_if_not_there(dirlst, dirname):
1214 def _add_dir_if_not_there(dirlst, dirname):
1217 match = False
1215 match = False
1218 samestat = os.path.samestat
1216 samestat = os.path.samestat
1219 dirstat = os.stat(dirname)
1217 dirstat = os.stat(dirname)
1220 for lstdirstat in dirlst:
1218 for lstdirstat in dirlst:
1221 if samestat(dirstat, lstdirstat):
1219 if samestat(dirstat, lstdirstat):
1222 match = True
1220 match = True
1223 break
1221 break
1224 if not match:
1222 if not match:
1225 dirlst.append(dirstat)
1223 dirlst.append(dirstat)
1226 return not match
1224 return not match
1227 else:
1225 else:
1228 followsym = False
1226 followsym = False
1229
1227
1230 if (seen_dirs is None) and followsym:
1228 if (seen_dirs is None) and followsym:
1231 seen_dirs = []
1229 seen_dirs = []
1232 _add_dir_if_not_there(seen_dirs, path)
1230 _add_dir_if_not_there(seen_dirs, path)
1233 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1231 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1234 dirs.sort()
1232 dirs.sort()
1235 if '.hg' in dirs:
1233 if '.hg' in dirs:
1236 yield root # found a repository
1234 yield root # found a repository
1237 qroot = os.path.join(root, '.hg', 'patches')
1235 qroot = os.path.join(root, '.hg', 'patches')
1238 if os.path.isdir(os.path.join(qroot, '.hg')):
1236 if os.path.isdir(os.path.join(qroot, '.hg')):
1239 yield qroot # we have a patch queue repo here
1237 yield qroot # we have a patch queue repo here
1240 if recurse:
1238 if recurse:
1241 # avoid recursing inside the .hg directory
1239 # avoid recursing inside the .hg directory
1242 dirs.remove('.hg')
1240 dirs.remove('.hg')
1243 else:
1241 else:
1244 dirs[:] = [] # don't descend further
1242 dirs[:] = [] # don't descend further
1245 elif followsym:
1243 elif followsym:
1246 newdirs = []
1244 newdirs = []
1247 for d in dirs:
1245 for d in dirs:
1248 fname = os.path.join(root, d)
1246 fname = os.path.join(root, d)
1249 if _add_dir_if_not_there(seen_dirs, fname):
1247 if _add_dir_if_not_there(seen_dirs, fname):
1250 if os.path.islink(fname):
1248 if os.path.islink(fname):
1251 for hgname in walkrepos(fname, True, seen_dirs):
1249 for hgname in walkrepos(fname, True, seen_dirs):
1252 yield hgname
1250 yield hgname
1253 else:
1251 else:
1254 newdirs.append(d)
1252 newdirs.append(d)
1255 dirs[:] = newdirs
1253 dirs[:] = newdirs
1256
1254
1257 _rcpath = None
1255 _rcpath = None
1258
1256
1259 def os_rcpath():
1257 def os_rcpath():
1260 '''return default os-specific hgrc search path'''
1258 '''return default os-specific hgrc search path'''
1261 path = system_rcpath()
1259 path = system_rcpath()
1262 path.extend(user_rcpath())
1260 path.extend(user_rcpath())
1263 path = [os.path.normpath(f) for f in path]
1261 path = [os.path.normpath(f) for f in path]
1264 return path
1262 return path
1265
1263
1266 def rcpath():
1264 def rcpath():
1267 '''return hgrc search path. if env var HGRCPATH is set, use it.
1265 '''return hgrc search path. if env var HGRCPATH is set, use it.
1268 for each item in path, if directory, use files ending in .rc,
1266 for each item in path, if directory, use files ending in .rc,
1269 else use item.
1267 else use item.
1270 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1268 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1271 if no HGRCPATH, use default os-specific path.'''
1269 if no HGRCPATH, use default os-specific path.'''
1272 global _rcpath
1270 global _rcpath
1273 if _rcpath is None:
1271 if _rcpath is None:
1274 if 'HGRCPATH' in os.environ:
1272 if 'HGRCPATH' in os.environ:
1275 _rcpath = []
1273 _rcpath = []
1276 for p in os.environ['HGRCPATH'].split(os.pathsep):
1274 for p in os.environ['HGRCPATH'].split(os.pathsep):
1277 if not p:
1275 if not p:
1278 continue
1276 continue
1279 p = expandpath(p)
1277 p = expandpath(p)
1280 if os.path.isdir(p):
1278 if os.path.isdir(p):
1281 for f, kind in osutil.listdir(p):
1279 for f, kind in osutil.listdir(p):
1282 if f.endswith('.rc'):
1280 if f.endswith('.rc'):
1283 _rcpath.append(os.path.join(p, f))
1281 _rcpath.append(os.path.join(p, f))
1284 else:
1282 else:
1285 _rcpath.append(p)
1283 _rcpath.append(p)
1286 else:
1284 else:
1287 _rcpath = os_rcpath()
1285 _rcpath = os_rcpath()
1288 return _rcpath
1286 return _rcpath
1289
1287
1290 def bytecount(nbytes):
1288 def bytecount(nbytes):
1291 '''return byte count formatted as readable string, with units'''
1289 '''return byte count formatted as readable string, with units'''
1292
1290
1293 units = (
1291 units = (
1294 (100, 1 << 30, _('%.0f GB')),
1292 (100, 1 << 30, _('%.0f GB')),
1295 (10, 1 << 30, _('%.1f GB')),
1293 (10, 1 << 30, _('%.1f GB')),
1296 (1, 1 << 30, _('%.2f GB')),
1294 (1, 1 << 30, _('%.2f GB')),
1297 (100, 1 << 20, _('%.0f MB')),
1295 (100, 1 << 20, _('%.0f MB')),
1298 (10, 1 << 20, _('%.1f MB')),
1296 (10, 1 << 20, _('%.1f MB')),
1299 (1, 1 << 20, _('%.2f MB')),
1297 (1, 1 << 20, _('%.2f MB')),
1300 (100, 1 << 10, _('%.0f KB')),
1298 (100, 1 << 10, _('%.0f KB')),
1301 (10, 1 << 10, _('%.1f KB')),
1299 (10, 1 << 10, _('%.1f KB')),
1302 (1, 1 << 10, _('%.2f KB')),
1300 (1, 1 << 10, _('%.2f KB')),
1303 (1, 1, _('%.0f bytes')),
1301 (1, 1, _('%.0f bytes')),
1304 )
1302 )
1305
1303
1306 for multiplier, divisor, format in units:
1304 for multiplier, divisor, format in units:
1307 if nbytes >= divisor * multiplier:
1305 if nbytes >= divisor * multiplier:
1308 return format % (nbytes / float(divisor))
1306 return format % (nbytes / float(divisor))
1309 return units[-1][2] % nbytes
1307 return units[-1][2] % nbytes
1310
1308
1311 def drop_scheme(scheme, path):
1309 def drop_scheme(scheme, path):
1312 sc = scheme + ':'
1310 sc = scheme + ':'
1313 if path.startswith(sc):
1311 if path.startswith(sc):
1314 path = path[len(sc):]
1312 path = path[len(sc):]
1315 if path.startswith('//'):
1313 if path.startswith('//'):
1316 if scheme == 'file':
1314 if scheme == 'file':
1317 i = path.find('/', 2)
1315 i = path.find('/', 2)
1318 if i == -1:
1316 if i == -1:
1319 return ''
1317 return ''
1320 # On Windows, absolute paths are rooted at the current drive
1318 # On Windows, absolute paths are rooted at the current drive
1321 # root. On POSIX they are rooted at the file system root.
1319 # root. On POSIX they are rooted at the file system root.
1322 if os.name == 'nt':
1320 if os.name == 'nt':
1323 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1321 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1324 path = os.path.join(droot, path[i + 1:])
1322 path = os.path.join(droot, path[i + 1:])
1325 else:
1323 else:
1326 path = path[i:]
1324 path = path[i:]
1327 else:
1325 else:
1328 path = path[2:]
1326 path = path[2:]
1329 return path
1327 return path
1330
1328
1331 def uirepr(s):
1329 def uirepr(s):
1332 # Avoid double backslash in Windows path repr()
1330 # Avoid double backslash in Windows path repr()
1333 return repr(s).replace('\\\\', '\\')
1331 return repr(s).replace('\\\\', '\\')
1334
1332
1335 #### naming convention of below implementation follows 'textwrap' module
1333 #### naming convention of below implementation follows 'textwrap' module
1336
1334
1337 class MBTextWrapper(textwrap.TextWrapper):
1335 class MBTextWrapper(textwrap.TextWrapper):
1338 def __init__(self, **kwargs):
1336 def __init__(self, **kwargs):
1339 textwrap.TextWrapper.__init__(self, **kwargs)
1337 textwrap.TextWrapper.__init__(self, **kwargs)
1340
1338
1341 def _cutdown(self, str, space_left):
1339 def _cutdown(self, str, space_left):
1342 l = 0
1340 l = 0
1343 ucstr = unicode(str, encoding.encoding)
1341 ucstr = unicode(str, encoding.encoding)
1344 w = unicodedata.east_asian_width
1342 w = unicodedata.east_asian_width
1345 for i in xrange(len(ucstr)):
1343 for i in xrange(len(ucstr)):
1346 l += w(ucstr[i]) in 'WFA' and 2 or 1
1344 l += w(ucstr[i]) in 'WFA' and 2 or 1
1347 if space_left < l:
1345 if space_left < l:
1348 return (ucstr[:i].encode(encoding.encoding),
1346 return (ucstr[:i].encode(encoding.encoding),
1349 ucstr[i:].encode(encoding.encoding))
1347 ucstr[i:].encode(encoding.encoding))
1350 return str, ''
1348 return str, ''
1351
1349
1352 # ----------------------------------------
1350 # ----------------------------------------
1353 # overriding of base class
1351 # overriding of base class
1354
1352
1355 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1353 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1356 space_left = max(width - cur_len, 1)
1354 space_left = max(width - cur_len, 1)
1357
1355
1358 if self.break_long_words:
1356 if self.break_long_words:
1359 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1357 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1360 cur_line.append(cut)
1358 cur_line.append(cut)
1361 reversed_chunks[-1] = res
1359 reversed_chunks[-1] = res
1362 elif not cur_line:
1360 elif not cur_line:
1363 cur_line.append(reversed_chunks.pop())
1361 cur_line.append(reversed_chunks.pop())
1364
1362
1365 #### naming convention of above implementation follows 'textwrap' module
1363 #### naming convention of above implementation follows 'textwrap' module
1366
1364
1367 def wrap(line, width, initindent='', hangindent=''):
1365 def wrap(line, width, initindent='', hangindent=''):
1368 maxindent = max(len(hangindent), len(initindent))
1366 maxindent = max(len(hangindent), len(initindent))
1369 if width <= maxindent:
1367 if width <= maxindent:
1370 # adjust for weird terminal size
1368 # adjust for weird terminal size
1371 width = max(78, maxindent + 1)
1369 width = max(78, maxindent + 1)
1372 wrapper = MBTextWrapper(width=width,
1370 wrapper = MBTextWrapper(width=width,
1373 initial_indent=initindent,
1371 initial_indent=initindent,
1374 subsequent_indent=hangindent)
1372 subsequent_indent=hangindent)
1375 return wrapper.fill(line)
1373 return wrapper.fill(line)
1376
1374
1377 def iterlines(iterator):
1375 def iterlines(iterator):
1378 for chunk in iterator:
1376 for chunk in iterator:
1379 for line in chunk.splitlines():
1377 for line in chunk.splitlines():
1380 yield line
1378 yield line
1381
1379
1382 def expandpath(path):
1380 def expandpath(path):
1383 return os.path.expanduser(os.path.expandvars(path))
1381 return os.path.expanduser(os.path.expandvars(path))
1384
1382
1385 def hgcmd():
1383 def hgcmd():
1386 """Return the command used to execute current hg
1384 """Return the command used to execute current hg
1387
1385
1388 This is different from hgexecutable() because on Windows we want
1386 This is different from hgexecutable() because on Windows we want
1389 to avoid things opening new shell windows like batch files, so we
1387 to avoid things opening new shell windows like batch files, so we
1390 get either the python call or current executable.
1388 get either the python call or current executable.
1391 """
1389 """
1392 if main_is_frozen():
1390 if main_is_frozen():
1393 return [sys.executable]
1391 return [sys.executable]
1394 return gethgcmd()
1392 return gethgcmd()
1395
1393
1396 def rundetached(args, condfn):
1394 def rundetached(args, condfn):
1397 """Execute the argument list in a detached process.
1395 """Execute the argument list in a detached process.
1398
1396
1399 condfn is a callable which is called repeatedly and should return
1397 condfn is a callable which is called repeatedly and should return
1400 True once the child process is known to have started successfully.
1398 True once the child process is known to have started successfully.
1401 At this point, the child process PID is returned. If the child
1399 At this point, the child process PID is returned. If the child
1402 process fails to start or finishes before condfn() evaluates to
1400 process fails to start or finishes before condfn() evaluates to
1403 True, return -1.
1401 True, return -1.
1404 """
1402 """
1405 # Windows case is easier because the child process is either
1403 # Windows case is easier because the child process is either
1406 # successfully starting and validating the condition or exiting
1404 # successfully starting and validating the condition or exiting
1407 # on failure. We just poll on its PID. On Unix, if the child
1405 # on failure. We just poll on its PID. On Unix, if the child
1408 # process fails to start, it will be left in a zombie state until
1406 # process fails to start, it will be left in a zombie state until
1409 # the parent wait on it, which we cannot do since we expect a long
1407 # the parent wait on it, which we cannot do since we expect a long
1410 # running process on success. Instead we listen for SIGCHLD telling
1408 # running process on success. Instead we listen for SIGCHLD telling
1411 # us our child process terminated.
1409 # us our child process terminated.
1412 terminated = set()
1410 terminated = set()
1413 def handler(signum, frame):
1411 def handler(signum, frame):
1414 terminated.add(os.wait())
1412 terminated.add(os.wait())
1415 prevhandler = None
1413 prevhandler = None
1416 if hasattr(signal, 'SIGCHLD'):
1414 if hasattr(signal, 'SIGCHLD'):
1417 prevhandler = signal.signal(signal.SIGCHLD, handler)
1415 prevhandler = signal.signal(signal.SIGCHLD, handler)
1418 try:
1416 try:
1419 pid = spawndetached(args)
1417 pid = spawndetached(args)
1420 while not condfn():
1418 while not condfn():
1421 if ((pid in terminated or not testpid(pid))
1419 if ((pid in terminated or not testpid(pid))
1422 and not condfn()):
1420 and not condfn()):
1423 return -1
1421 return -1
1424 time.sleep(0.1)
1422 time.sleep(0.1)
1425 return pid
1423 return pid
1426 finally:
1424 finally:
1427 if prevhandler is not None:
1425 if prevhandler is not None:
1428 signal.signal(signal.SIGCHLD, prevhandler)
1426 signal.signal(signal.SIGCHLD, prevhandler)
1429
1427
1430 try:
1428 try:
1431 any, all = any, all
1429 any, all = any, all
1432 except NameError:
1430 except NameError:
1433 def any(iterable):
1431 def any(iterable):
1434 for i in iterable:
1432 for i in iterable:
1435 if i:
1433 if i:
1436 return True
1434 return True
1437 return False
1435 return False
1438
1436
1439 def all(iterable):
1437 def all(iterable):
1440 for i in iterable:
1438 for i in iterable:
1441 if not i:
1439 if not i:
1442 return False
1440 return False
1443 return True
1441 return True
1444
1442
1445 def interpolate(prefix, mapping, s, fn=None):
1443 def interpolate(prefix, mapping, s, fn=None):
1446 """Return the result of interpolating items in the mapping into string s.
1444 """Return the result of interpolating items in the mapping into string s.
1447
1445
1448 prefix is a single character string, or a two character string with
1446 prefix is a single character string, or a two character string with
1449 a backslash as the first character if the prefix needs to be escaped in
1447 a backslash as the first character if the prefix needs to be escaped in
1450 a regular expression.
1448 a regular expression.
1451
1449
1452 fn is an optional function that will be applied to the replacement text
1450 fn is an optional function that will be applied to the replacement text
1453 just before replacement.
1451 just before replacement.
1454 """
1452 """
1455 fn = fn or (lambda s: s)
1453 fn = fn or (lambda s: s)
1456 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1454 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1457 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1455 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1458
1456
1459 def getport(port):
1457 def getport(port):
1460 """Return the port for a given network service.
1458 """Return the port for a given network service.
1461
1459
1462 If port is an integer, it's returned as is. If it's a string, it's
1460 If port is an integer, it's returned as is. If it's a string, it's
1463 looked up using socket.getservbyname(). If there's no matching
1461 looked up using socket.getservbyname(). If there's no matching
1464 service, util.Abort is raised.
1462 service, util.Abort is raised.
1465 """
1463 """
1466 try:
1464 try:
1467 return int(port)
1465 return int(port)
1468 except ValueError:
1466 except ValueError:
1469 pass
1467 pass
1470
1468
1471 try:
1469 try:
1472 return socket.getservbyname(port)
1470 return socket.getservbyname(port)
1473 except socket.error:
1471 except socket.error:
1474 raise Abort(_("no port number associated with service '%s'") % port)
1472 raise Abort(_("no port number associated with service '%s'") % port)
1475
1473
1476 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1474 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1477 '0': False, 'no': False, 'false': False, 'off': False,
1475 '0': False, 'no': False, 'false': False, 'off': False,
1478 'never': False}
1476 'never': False}
1479
1477
1480 def parsebool(s):
1478 def parsebool(s):
1481 """Parse s into a boolean.
1479 """Parse s into a boolean.
1482
1480
1483 If s is not a valid boolean, returns None.
1481 If s is not a valid boolean, returns None.
1484 """
1482 """
1485 return _booleans.get(s.lower(), None)
1483 return _booleans.get(s.lower(), None)
@@ -1,376 +1,377 b''
1 # windows.py - Windows utility function implementations for Mercurial
1 # windows.py - Windows utility function implementations for Mercurial
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import osutil, error
9 import osutil, error
10 import errno, msvcrt, os, re, sys, random, subprocess
10 import errno, msvcrt, os, re, sys, random, subprocess
11
11
12 nulldev = 'NUL:'
12 nulldev = 'NUL:'
13 umask = 002
13 umask = 002
14
14
15 # wrap osutil.posixfile to provide friendlier exceptions
15 # wrap osutil.posixfile to provide friendlier exceptions
16 def posixfile(name, mode='r', buffering=-1):
16 def posixfile(name, mode='r', buffering=-1):
17 try:
17 try:
18 return osutil.posixfile(name, mode, buffering)
18 return osutil.posixfile(name, mode, buffering)
19 except WindowsError, err:
19 except WindowsError, err:
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
20 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
21 posixfile.__doc__ = osutil.posixfile.__doc__
21 posixfile.__doc__ = osutil.posixfile.__doc__
22
22
23 class winstdout(object):
23 class winstdout(object):
24 '''stdout on windows misbehaves if sent through a pipe'''
24 '''stdout on windows misbehaves if sent through a pipe'''
25
25
26 def __init__(self, fp):
26 def __init__(self, fp):
27 self.fp = fp
27 self.fp = fp
28
28
29 def __getattr__(self, key):
29 def __getattr__(self, key):
30 return getattr(self.fp, key)
30 return getattr(self.fp, key)
31
31
32 def close(self):
32 def close(self):
33 try:
33 try:
34 self.fp.close()
34 self.fp.close()
35 except: pass
35 except: pass
36
36
37 def write(self, s):
37 def write(self, s):
38 try:
38 try:
39 # This is workaround for "Not enough space" error on
39 # This is workaround for "Not enough space" error on
40 # writing large size of data to console.
40 # writing large size of data to console.
41 limit = 16000
41 limit = 16000
42 l = len(s)
42 l = len(s)
43 start = 0
43 start = 0
44 self.softspace = 0
44 self.softspace = 0
45 while start < l:
45 while start < l:
46 end = start + limit
46 end = start + limit
47 self.fp.write(s[start:end])
47 self.fp.write(s[start:end])
48 start = end
48 start = end
49 except IOError, inst:
49 except IOError, inst:
50 if inst.errno != 0:
50 if inst.errno != 0:
51 raise
51 raise
52 self.close()
52 self.close()
53 raise IOError(errno.EPIPE, 'Broken pipe')
53 raise IOError(errno.EPIPE, 'Broken pipe')
54
54
55 def flush(self):
55 def flush(self):
56 try:
56 try:
57 return self.fp.flush()
57 return self.fp.flush()
58 except IOError, inst:
58 except IOError, inst:
59 if inst.errno != errno.EINVAL:
59 if inst.errno != errno.EINVAL:
60 raise
60 raise
61 self.close()
61 self.close()
62 raise IOError(errno.EPIPE, 'Broken pipe')
62 raise IOError(errno.EPIPE, 'Broken pipe')
63
63
64 sys.stdout = winstdout(sys.stdout)
64 sys.stdout = winstdout(sys.stdout)
65
65
66 def _is_win_9x():
66 def _is_win_9x():
67 '''return true if run on windows 95, 98 or me.'''
67 '''return true if run on windows 95, 98 or me.'''
68 try:
68 try:
69 return sys.getwindowsversion()[3] == 1
69 return sys.getwindowsversion()[3] == 1
70 except AttributeError:
70 except AttributeError:
71 return 'command' in os.environ.get('comspec', '')
71 return 'command' in os.environ.get('comspec', '')
72
72
73 def openhardlinks():
73 def openhardlinks():
74 return not _is_win_9x() and "win32api" in globals()
74 return not _is_win_9x() and "win32api" in globals()
75
75
76 def system_rcpath():
76 def system_rcpath():
77 try:
77 try:
78 return system_rcpath_win32()
78 return system_rcpath_win32()
79 except:
79 except:
80 return [r'c:\mercurial\mercurial.ini']
80 return [r'c:\mercurial\mercurial.ini']
81
81
82 def user_rcpath():
82 def user_rcpath():
83 '''return os-specific hgrc search path to the user dir'''
83 '''return os-specific hgrc search path to the user dir'''
84 try:
84 try:
85 path = user_rcpath_win32()
85 path = user_rcpath_win32()
86 except:
86 except:
87 home = os.path.expanduser('~')
87 home = os.path.expanduser('~')
88 path = [os.path.join(home, 'mercurial.ini'),
88 path = [os.path.join(home, 'mercurial.ini'),
89 os.path.join(home, '.hgrc')]
89 os.path.join(home, '.hgrc')]
90 userprofile = os.environ.get('USERPROFILE')
90 userprofile = os.environ.get('USERPROFILE')
91 if userprofile:
91 if userprofile:
92 path.append(os.path.join(userprofile, 'mercurial.ini'))
92 path.append(os.path.join(userprofile, 'mercurial.ini'))
93 path.append(os.path.join(userprofile, '.hgrc'))
93 path.append(os.path.join(userprofile, '.hgrc'))
94 return path
94 return path
95
95
96 def parse_patch_output(output_line):
96 def parse_patch_output(output_line):
97 """parses the output produced by patch and returns the filename"""
97 """parses the output produced by patch and returns the filename"""
98 pf = output_line[14:]
98 pf = output_line[14:]
99 if pf[0] == '`':
99 if pf[0] == '`':
100 pf = pf[1:-1] # Remove the quotes
100 pf = pf[1:-1] # Remove the quotes
101 return pf
101 return pf
102
102
103 def sshargs(sshcmd, host, user, port):
103 def sshargs(sshcmd, host, user, port):
104 '''Build argument list for ssh or Plink'''
104 '''Build argument list for ssh or Plink'''
105 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
105 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
106 args = user and ("%s@%s" % (user, host)) or host
106 args = user and ("%s@%s" % (user, host)) or host
107 return port and ("%s %s %s" % (args, pflag, port)) or args
107 return port and ("%s %s %s" % (args, pflag, port)) or args
108
108
109 def testpid(pid):
109 def testpid(pid):
110 '''return False if pid dead, True if running or not known'''
110 '''return False if pid dead, True if running or not known'''
111 return True
111 return True
112
112
113 def set_flags(f, l, x):
113 def set_flags(f, l, x):
114 pass
114 pass
115
115
116 def set_binary(fd):
116 def set_binary(fd):
117 # When run without console, pipes may expose invalid
117 # When run without console, pipes may expose invalid
118 # fileno(), usually set to -1.
118 # fileno(), usually set to -1.
119 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
119 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
120 msvcrt.setmode(fd.fileno(), os.O_BINARY)
120 msvcrt.setmode(fd.fileno(), os.O_BINARY)
121
121
122 def pconvert(path):
122 def pconvert(path):
123 return '/'.join(path.split(os.sep))
123 return '/'.join(path.split(os.sep))
124
124
125 def localpath(path):
125 def localpath(path):
126 return path.replace('/', '\\')
126 return path.replace('/', '\\')
127
127
128 def normpath(path):
128 def normpath(path):
129 return pconvert(os.path.normpath(path))
129 return pconvert(os.path.normpath(path))
130
130
131 def realpath(path):
131 def realpath(path):
132 '''
132 '''
133 Returns the true, canonical file system path equivalent to the given
133 Returns the true, canonical file system path equivalent to the given
134 path.
134 path.
135 '''
135 '''
136 # TODO: There may be a more clever way to do this that also handles other,
136 # TODO: There may be a more clever way to do this that also handles other,
137 # less common file systems.
137 # less common file systems.
138 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
138 return os.path.normpath(os.path.normcase(os.path.realpath(path)))
139
139
140 def samestat(s1, s2):
140 def samestat(s1, s2):
141 return False
141 return False
142
142
143 # A sequence of backslashes is special iff it precedes a double quote:
143 # A sequence of backslashes is special iff it precedes a double quote:
144 # - if there's an even number of backslashes, the double quote is not
144 # - if there's an even number of backslashes, the double quote is not
145 # quoted (i.e. it ends the quoted region)
145 # quoted (i.e. it ends the quoted region)
146 # - if there's an odd number of backslashes, the double quote is quoted
146 # - if there's an odd number of backslashes, the double quote is quoted
147 # - in both cases, every pair of backslashes is unquoted into a single
147 # - in both cases, every pair of backslashes is unquoted into a single
148 # backslash
148 # backslash
149 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
149 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
150 # So, to quote a string, we must surround it in double quotes, double
150 # So, to quote a string, we must surround it in double quotes, double
151 # the number of backslashes that preceed double quotes and add another
151 # the number of backslashes that preceed double quotes and add another
152 # backslash before every double quote (being careful with the double
152 # backslash before every double quote (being careful with the double
153 # quote we've appended to the end)
153 # quote we've appended to the end)
154 _quotere = None
154 _quotere = None
155 def shellquote(s):
155 def shellquote(s):
156 global _quotere
156 global _quotere
157 if _quotere is None:
157 if _quotere is None:
158 _quotere = re.compile(r'(\\*)("|\\$)')
158 _quotere = re.compile(r'(\\*)("|\\$)')
159 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
159 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
160
160
161 def quotecommand(cmd):
161 def quotecommand(cmd):
162 """Build a command string suitable for os.popen* calls."""
162 """Build a command string suitable for os.popen* calls."""
163 # The extra quotes are needed because popen* runs the command
163 if sys.version_info < (2, 7, 1):
164 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
164 # Python versions since 2.7.1 do this extra quoting themselves
165 return '"' + cmd + '"'
165 return '"' + cmd + '"'
166 return cmd
166
167
167 def popen(command, mode='r'):
168 def popen(command, mode='r'):
168 # Work around "popen spawned process may not write to stdout
169 # Work around "popen spawned process may not write to stdout
169 # under windows"
170 # under windows"
170 # http://bugs.python.org/issue1366
171 # http://bugs.python.org/issue1366
171 command += " 2> %s" % nulldev
172 command += " 2> %s" % nulldev
172 return os.popen(quotecommand(command), mode)
173 return os.popen(quotecommand(command), mode)
173
174
174 def explain_exit(code):
175 def explain_exit(code):
175 return _("exited with status %d") % code, code
176 return _("exited with status %d") % code, code
176
177
177 # if you change this stub into a real check, please try to implement the
178 # if you change this stub into a real check, please try to implement the
178 # username and groupname functions above, too.
179 # username and groupname functions above, too.
179 def isowner(st):
180 def isowner(st):
180 return True
181 return True
181
182
182 def find_exe(command):
183 def find_exe(command):
183 '''Find executable for command searching like cmd.exe does.
184 '''Find executable for command searching like cmd.exe does.
184 If command is a basename then PATH is searched for command.
185 If command is a basename then PATH is searched for command.
185 PATH isn't searched if command is an absolute or relative path.
186 PATH isn't searched if command is an absolute or relative path.
186 An extension from PATHEXT is found and added if not present.
187 An extension from PATHEXT is found and added if not present.
187 If command isn't found None is returned.'''
188 If command isn't found None is returned.'''
188 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
189 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
189 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
190 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
190 if os.path.splitext(command)[1].lower() in pathexts:
191 if os.path.splitext(command)[1].lower() in pathexts:
191 pathexts = ['']
192 pathexts = ['']
192
193
193 def findexisting(pathcommand):
194 def findexisting(pathcommand):
194 'Will append extension (if needed) and return existing file'
195 'Will append extension (if needed) and return existing file'
195 for ext in pathexts:
196 for ext in pathexts:
196 executable = pathcommand + ext
197 executable = pathcommand + ext
197 if os.path.exists(executable):
198 if os.path.exists(executable):
198 return executable
199 return executable
199 return None
200 return None
200
201
201 if os.sep in command:
202 if os.sep in command:
202 return findexisting(command)
203 return findexisting(command)
203
204
204 for path in os.environ.get('PATH', '').split(os.pathsep):
205 for path in os.environ.get('PATH', '').split(os.pathsep):
205 executable = findexisting(os.path.join(path, command))
206 executable = findexisting(os.path.join(path, command))
206 if executable is not None:
207 if executable is not None:
207 return executable
208 return executable
208 return findexisting(os.path.expanduser(os.path.expandvars(command)))
209 return findexisting(os.path.expanduser(os.path.expandvars(command)))
209
210
210 def set_signal_handler():
211 def set_signal_handler():
211 try:
212 try:
212 set_signal_handler_win32()
213 set_signal_handler_win32()
213 except NameError:
214 except NameError:
214 pass
215 pass
215
216
216 def statfiles(files):
217 def statfiles(files):
217 '''Stat each file in files and yield stat or None if file does not exist.
218 '''Stat each file in files and yield stat or None if file does not exist.
218 Cluster and cache stat per directory to minimize number of OS stat calls.'''
219 Cluster and cache stat per directory to minimize number of OS stat calls.'''
219 ncase = os.path.normcase
220 ncase = os.path.normcase
220 dircache = {} # dirname -> filename -> status | None if file does not exist
221 dircache = {} # dirname -> filename -> status | None if file does not exist
221 for nf in files:
222 for nf in files:
222 nf = ncase(nf)
223 nf = ncase(nf)
223 dir, base = os.path.split(nf)
224 dir, base = os.path.split(nf)
224 if not dir:
225 if not dir:
225 dir = '.'
226 dir = '.'
226 cache = dircache.get(dir, None)
227 cache = dircache.get(dir, None)
227 if cache is None:
228 if cache is None:
228 try:
229 try:
229 dmap = dict([(ncase(n), s)
230 dmap = dict([(ncase(n), s)
230 for n, k, s in osutil.listdir(dir, True)])
231 for n, k, s in osutil.listdir(dir, True)])
231 except OSError, err:
232 except OSError, err:
232 # handle directory not found in Python version prior to 2.5
233 # handle directory not found in Python version prior to 2.5
233 # Python <= 2.4 returns native Windows code 3 in errno
234 # Python <= 2.4 returns native Windows code 3 in errno
234 # Python >= 2.5 returns ENOENT and adds winerror field
235 # Python >= 2.5 returns ENOENT and adds winerror field
235 # EINVAL is raised if dir is not a directory.
236 # EINVAL is raised if dir is not a directory.
236 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
237 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
237 errno.ENOTDIR):
238 errno.ENOTDIR):
238 raise
239 raise
239 dmap = {}
240 dmap = {}
240 cache = dircache.setdefault(dir, dmap)
241 cache = dircache.setdefault(dir, dmap)
241 yield cache.get(base, None)
242 yield cache.get(base, None)
242
243
243 def getuser():
244 def getuser():
244 '''return name of current user'''
245 '''return name of current user'''
245 raise error.Abort(_('user name not available - set USERNAME '
246 raise error.Abort(_('user name not available - set USERNAME '
246 'environment variable'))
247 'environment variable'))
247
248
248 def username(uid=None):
249 def username(uid=None):
249 """Return the name of the user with the given uid.
250 """Return the name of the user with the given uid.
250
251
251 If uid is None, return the name of the current user."""
252 If uid is None, return the name of the current user."""
252 return None
253 return None
253
254
254 def groupname(gid=None):
255 def groupname(gid=None):
255 """Return the name of the group with the given gid.
256 """Return the name of the group with the given gid.
256
257
257 If gid is None, return the name of the current group."""
258 If gid is None, return the name of the current group."""
258 return None
259 return None
259
260
260 def _removedirs(name):
261 def _removedirs(name):
261 """special version of os.removedirs that does not remove symlinked
262 """special version of os.removedirs that does not remove symlinked
262 directories or junction points if they actually contain files"""
263 directories or junction points if they actually contain files"""
263 if osutil.listdir(name):
264 if osutil.listdir(name):
264 return
265 return
265 os.rmdir(name)
266 os.rmdir(name)
266 head, tail = os.path.split(name)
267 head, tail = os.path.split(name)
267 if not tail:
268 if not tail:
268 head, tail = os.path.split(head)
269 head, tail = os.path.split(head)
269 while head and tail:
270 while head and tail:
270 try:
271 try:
271 if osutil.listdir(head):
272 if osutil.listdir(head):
272 return
273 return
273 os.rmdir(head)
274 os.rmdir(head)
274 except:
275 except:
275 break
276 break
276 head, tail = os.path.split(head)
277 head, tail = os.path.split(head)
277
278
278 def unlink(f):
279 def unlink(f):
279 """unlink and remove the directory if it is empty"""
280 """unlink and remove the directory if it is empty"""
280 os.unlink(f)
281 os.unlink(f)
281 # try removing directories that might now be empty
282 # try removing directories that might now be empty
282 try:
283 try:
283 _removedirs(os.path.dirname(f))
284 _removedirs(os.path.dirname(f))
284 except OSError:
285 except OSError:
285 pass
286 pass
286
287
287 def rename(src, dst):
288 def rename(src, dst):
288 '''atomically rename file src to dst, replacing dst if it exists'''
289 '''atomically rename file src to dst, replacing dst if it exists'''
289 try:
290 try:
290 os.rename(src, dst)
291 os.rename(src, dst)
291 except OSError: # FIXME: check err (EEXIST ?)
292 except OSError: # FIXME: check err (EEXIST ?)
292
293
293 # On windows, rename to existing file is not allowed, so we
294 # On windows, rename to existing file is not allowed, so we
294 # must delete destination first. But if a file is open, unlink
295 # must delete destination first. But if a file is open, unlink
295 # schedules it for delete but does not delete it. Rename
296 # schedules it for delete but does not delete it. Rename
296 # happens immediately even for open files, so we rename
297 # happens immediately even for open files, so we rename
297 # destination to a temporary name, then delete that. Then
298 # destination to a temporary name, then delete that. Then
298 # rename is safe to do.
299 # rename is safe to do.
299 # The temporary name is chosen at random to avoid the situation
300 # The temporary name is chosen at random to avoid the situation
300 # where a file is left lying around from a previous aborted run.
301 # where a file is left lying around from a previous aborted run.
301 # The usual race condition this introduces can't be avoided as
302 # The usual race condition this introduces can't be avoided as
302 # we need the name to rename into, and not the file itself. Due
303 # we need the name to rename into, and not the file itself. Due
303 # to the nature of the operation however, any races will at worst
304 # to the nature of the operation however, any races will at worst
304 # lead to the rename failing and the current operation aborting.
305 # lead to the rename failing and the current operation aborting.
305
306
306 def tempname(prefix):
307 def tempname(prefix):
307 for tries in xrange(10):
308 for tries in xrange(10):
308 temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
309 temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
309 if not os.path.exists(temp):
310 if not os.path.exists(temp):
310 return temp
311 return temp
311 raise IOError, (errno.EEXIST, "No usable temporary filename found")
312 raise IOError, (errno.EEXIST, "No usable temporary filename found")
312
313
313 temp = tempname(dst)
314 temp = tempname(dst)
314 os.rename(dst, temp)
315 os.rename(dst, temp)
315 try:
316 try:
316 os.unlink(temp)
317 os.unlink(temp)
317 except:
318 except:
318 # Some rude AV-scanners on Windows may cause the unlink to
319 # Some rude AV-scanners on Windows may cause the unlink to
319 # fail. Not aborting here just leaks the temp file, whereas
320 # fail. Not aborting here just leaks the temp file, whereas
320 # aborting at this point may leave serious inconsistencies.
321 # aborting at this point may leave serious inconsistencies.
321 # Ideally, we would notify the user here.
322 # Ideally, we would notify the user here.
322 pass
323 pass
323 os.rename(src, dst)
324 os.rename(src, dst)
324
325
325 def spawndetached(args):
326 def spawndetached(args):
326 # No standard library function really spawns a fully detached
327 # No standard library function really spawns a fully detached
327 # process under win32 because they allocate pipes or other objects
328 # process under win32 because they allocate pipes or other objects
328 # to handle standard streams communications. Passing these objects
329 # to handle standard streams communications. Passing these objects
329 # to the child process requires handle inheritance to be enabled
330 # to the child process requires handle inheritance to be enabled
330 # which makes really detached processes impossible.
331 # which makes really detached processes impossible.
331 class STARTUPINFO:
332 class STARTUPINFO:
332 dwFlags = subprocess.STARTF_USESHOWWINDOW
333 dwFlags = subprocess.STARTF_USESHOWWINDOW
333 hStdInput = None
334 hStdInput = None
334 hStdOutput = None
335 hStdOutput = None
335 hStdError = None
336 hStdError = None
336 wShowWindow = subprocess.SW_HIDE
337 wShowWindow = subprocess.SW_HIDE
337
338
338 args = subprocess.list2cmdline(args)
339 args = subprocess.list2cmdline(args)
339 # Not running the command in shell mode makes python26 hang when
340 # Not running the command in shell mode makes python26 hang when
340 # writing to hgweb output socket.
341 # writing to hgweb output socket.
341 comspec = os.environ.get("COMSPEC", "cmd.exe")
342 comspec = os.environ.get("COMSPEC", "cmd.exe")
342 args = comspec + " /c " + args
343 args = comspec + " /c " + args
343 hp, ht, pid, tid = subprocess.CreateProcess(
344 hp, ht, pid, tid = subprocess.CreateProcess(
344 None, args,
345 None, args,
345 # no special security
346 # no special security
346 None, None,
347 None, None,
347 # Do not inherit handles
348 # Do not inherit handles
348 0,
349 0,
349 # DETACHED_PROCESS
350 # DETACHED_PROCESS
350 0x00000008,
351 0x00000008,
351 os.environ,
352 os.environ,
352 os.getcwd(),
353 os.getcwd(),
353 STARTUPINFO())
354 STARTUPINFO())
354 return pid
355 return pid
355
356
356 def gethgcmd():
357 def gethgcmd():
357 return [sys.executable] + sys.argv[:1]
358 return [sys.executable] + sys.argv[:1]
358
359
359 def termwidth():
360 def termwidth():
360 # cmd.exe does not handle CR like a unix console, the CR is
361 # cmd.exe does not handle CR like a unix console, the CR is
361 # counted in the line length. On 80 columns consoles, if 80
362 # counted in the line length. On 80 columns consoles, if 80
362 # characters are written, the following CR won't apply on the
363 # characters are written, the following CR won't apply on the
363 # current line but on the new one. Keep room for it.
364 # current line but on the new one. Keep room for it.
364 return 79
365 return 79
365
366
366 def groupmembers(name):
367 def groupmembers(name):
367 # Don't support groups on Windows for now
368 # Don't support groups on Windows for now
368 raise KeyError()
369 raise KeyError()
369
370
370 try:
371 try:
371 # override functions with win32 versions if possible
372 # override functions with win32 versions if possible
372 from win32 import *
373 from win32 import *
373 except ImportError:
374 except ImportError:
374 pass
375 pass
375
376
376 expandglobs = True
377 expandglobs = True
General Comments 0
You need to be logged in to leave comments. Login now