##// END OF EJS Templates
audit: reject paths with .hg (issue 1450)...
Matt Mackall -
r7553:71be8688 default
parent child Browse files
Show More
@@ -1,1987 +1,1993
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile, traceback
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile, traceback
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18 import imp
18 import imp
19
19
20 # Python compatibility
20 # Python compatibility
21
21
22 try:
22 try:
23 set = set
23 set = set
24 frozenset = frozenset
24 frozenset = frozenset
25 except NameError:
25 except NameError:
26 from sets import Set as set, ImmutableSet as frozenset
26 from sets import Set as set, ImmutableSet as frozenset
27
27
28 _md5 = None
28 _md5 = None
29 def md5(s):
29 def md5(s):
30 global _md5
30 global _md5
31 if _md5 is None:
31 if _md5 is None:
32 try:
32 try:
33 import hashlib
33 import hashlib
34 _md5 = hashlib.md5
34 _md5 = hashlib.md5
35 except ImportError:
35 except ImportError:
36 import md5
36 import md5
37 _md5 = md5.md5
37 _md5 = md5.md5
38 return _md5(s)
38 return _md5(s)
39
39
40 _sha1 = None
40 _sha1 = None
41 def sha1(s):
41 def sha1(s):
42 global _sha1
42 global _sha1
43 if _sha1 is None:
43 if _sha1 is None:
44 try:
44 try:
45 import hashlib
45 import hashlib
46 _sha1 = hashlib.sha1
46 _sha1 = hashlib.sha1
47 except ImportError:
47 except ImportError:
48 import sha
48 import sha
49 _sha1 = sha.sha
49 _sha1 = sha.sha
50 return _sha1(s)
50 return _sha1(s)
51
51
52 try:
52 try:
53 import subprocess
53 import subprocess
54 subprocess.Popen # trigger ImportError early
54 subprocess.Popen # trigger ImportError early
55 closefds = os.name == 'posix'
55 closefds = os.name == 'posix'
56 def popen2(cmd, mode='t', bufsize=-1):
56 def popen2(cmd, mode='t', bufsize=-1):
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
58 close_fds=closefds,
58 close_fds=closefds,
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
60 return p.stdin, p.stdout
60 return p.stdin, p.stdout
61 def popen3(cmd, mode='t', bufsize=-1):
61 def popen3(cmd, mode='t', bufsize=-1):
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
63 close_fds=closefds,
63 close_fds=closefds,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
65 stderr=subprocess.PIPE)
65 stderr=subprocess.PIPE)
66 return p.stdin, p.stdout, p.stderr
66 return p.stdin, p.stdout, p.stderr
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
68 stderr = capturestderr and subprocess.PIPE or None
68 stderr = capturestderr and subprocess.PIPE or None
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
70 close_fds=closefds,
70 close_fds=closefds,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
72 stderr=stderr)
72 stderr=stderr)
73 p.fromchild = p.stdout
73 p.fromchild = p.stdout
74 p.tochild = p.stdin
74 p.tochild = p.stdin
75 p.childerr = p.stderr
75 p.childerr = p.stderr
76 return p
76 return p
77 except ImportError:
77 except ImportError:
78 subprocess = None
78 subprocess = None
79 from popen2 import Popen3
79 from popen2 import Popen3
80 popen2 = os.popen2
80 popen2 = os.popen2
81 popen3 = os.popen3
81 popen3 = os.popen3
82
82
83
83
84 _encodingfixup = {'646': 'ascii', 'ANSI_X3.4-1968': 'ascii'}
84 _encodingfixup = {'646': 'ascii', 'ANSI_X3.4-1968': 'ascii'}
85
85
86 try:
86 try:
87 _encoding = os.environ.get("HGENCODING")
87 _encoding = os.environ.get("HGENCODING")
88 if sys.platform == 'darwin' and not _encoding:
88 if sys.platform == 'darwin' and not _encoding:
89 # On darwin, getpreferredencoding ignores the locale environment and
89 # On darwin, getpreferredencoding ignores the locale environment and
90 # always returns mac-roman. We override this if the environment is
90 # always returns mac-roman. We override this if the environment is
91 # not C (has been customized by the user).
91 # not C (has been customized by the user).
92 locale.setlocale(locale.LC_CTYPE, '')
92 locale.setlocale(locale.LC_CTYPE, '')
93 _encoding = locale.getlocale()[1]
93 _encoding = locale.getlocale()[1]
94 if not _encoding:
94 if not _encoding:
95 _encoding = locale.getpreferredencoding() or 'ascii'
95 _encoding = locale.getpreferredencoding() or 'ascii'
96 _encoding = _encodingfixup.get(_encoding, _encoding)
96 _encoding = _encodingfixup.get(_encoding, _encoding)
97 except locale.Error:
97 except locale.Error:
98 _encoding = 'ascii'
98 _encoding = 'ascii'
99 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
99 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
100 _fallbackencoding = 'ISO-8859-1'
100 _fallbackencoding = 'ISO-8859-1'
101
101
102 def tolocal(s):
102 def tolocal(s):
103 """
103 """
104 Convert a string from internal UTF-8 to local encoding
104 Convert a string from internal UTF-8 to local encoding
105
105
106 All internal strings should be UTF-8 but some repos before the
106 All internal strings should be UTF-8 but some repos before the
107 implementation of locale support may contain latin1 or possibly
107 implementation of locale support may contain latin1 or possibly
108 other character sets. We attempt to decode everything strictly
108 other character sets. We attempt to decode everything strictly
109 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
109 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
110 replace unknown characters.
110 replace unknown characters.
111 """
111 """
112 for e in ('UTF-8', _fallbackencoding):
112 for e in ('UTF-8', _fallbackencoding):
113 try:
113 try:
114 u = s.decode(e) # attempt strict decoding
114 u = s.decode(e) # attempt strict decoding
115 return u.encode(_encoding, "replace")
115 return u.encode(_encoding, "replace")
116 except LookupError, k:
116 except LookupError, k:
117 raise Abort(_("%s, please check your locale settings") % k)
117 raise Abort(_("%s, please check your locale settings") % k)
118 except UnicodeDecodeError:
118 except UnicodeDecodeError:
119 pass
119 pass
120 u = s.decode("utf-8", "replace") # last ditch
120 u = s.decode("utf-8", "replace") # last ditch
121 return u.encode(_encoding, "replace")
121 return u.encode(_encoding, "replace")
122
122
123 def fromlocal(s):
123 def fromlocal(s):
124 """
124 """
125 Convert a string from the local character encoding to UTF-8
125 Convert a string from the local character encoding to UTF-8
126
126
127 We attempt to decode strings using the encoding mode set by
127 We attempt to decode strings using the encoding mode set by
128 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
128 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
129 characters will cause an error message. Other modes include
129 characters will cause an error message. Other modes include
130 'replace', which replaces unknown characters with a special
130 'replace', which replaces unknown characters with a special
131 Unicode character, and 'ignore', which drops the character.
131 Unicode character, and 'ignore', which drops the character.
132 """
132 """
133 try:
133 try:
134 return s.decode(_encoding, _encodingmode).encode("utf-8")
134 return s.decode(_encoding, _encodingmode).encode("utf-8")
135 except UnicodeDecodeError, inst:
135 except UnicodeDecodeError, inst:
136 sub = s[max(0, inst.start-10):inst.start+10]
136 sub = s[max(0, inst.start-10):inst.start+10]
137 raise Abort("decoding near '%s': %s!" % (sub, inst))
137 raise Abort("decoding near '%s': %s!" % (sub, inst))
138 except LookupError, k:
138 except LookupError, k:
139 raise Abort(_("%s, please check your locale settings") % k)
139 raise Abort(_("%s, please check your locale settings") % k)
140
140
141 def locallen(s):
141 def locallen(s):
142 """Find the length in characters of a local string"""
142 """Find the length in characters of a local string"""
143 return len(s.decode(_encoding, "replace"))
143 return len(s.decode(_encoding, "replace"))
144
144
145 # used by parsedate
145 # used by parsedate
146 defaultdateformats = (
146 defaultdateformats = (
147 '%Y-%m-%d %H:%M:%S',
147 '%Y-%m-%d %H:%M:%S',
148 '%Y-%m-%d %I:%M:%S%p',
148 '%Y-%m-%d %I:%M:%S%p',
149 '%Y-%m-%d %H:%M',
149 '%Y-%m-%d %H:%M',
150 '%Y-%m-%d %I:%M%p',
150 '%Y-%m-%d %I:%M%p',
151 '%Y-%m-%d',
151 '%Y-%m-%d',
152 '%m-%d',
152 '%m-%d',
153 '%m/%d',
153 '%m/%d',
154 '%m/%d/%y',
154 '%m/%d/%y',
155 '%m/%d/%Y',
155 '%m/%d/%Y',
156 '%a %b %d %H:%M:%S %Y',
156 '%a %b %d %H:%M:%S %Y',
157 '%a %b %d %I:%M:%S%p %Y',
157 '%a %b %d %I:%M:%S%p %Y',
158 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
158 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
159 '%b %d %H:%M:%S %Y',
159 '%b %d %H:%M:%S %Y',
160 '%b %d %I:%M:%S%p %Y',
160 '%b %d %I:%M:%S%p %Y',
161 '%b %d %H:%M:%S',
161 '%b %d %H:%M:%S',
162 '%b %d %I:%M:%S%p',
162 '%b %d %I:%M:%S%p',
163 '%b %d %H:%M',
163 '%b %d %H:%M',
164 '%b %d %I:%M%p',
164 '%b %d %I:%M%p',
165 '%b %d %Y',
165 '%b %d %Y',
166 '%b %d',
166 '%b %d',
167 '%H:%M:%S',
167 '%H:%M:%S',
168 '%I:%M:%SP',
168 '%I:%M:%SP',
169 '%H:%M',
169 '%H:%M',
170 '%I:%M%p',
170 '%I:%M%p',
171 )
171 )
172
172
173 extendeddateformats = defaultdateformats + (
173 extendeddateformats = defaultdateformats + (
174 "%Y",
174 "%Y",
175 "%Y-%m",
175 "%Y-%m",
176 "%b",
176 "%b",
177 "%b %Y",
177 "%b %Y",
178 )
178 )
179
179
180 class SignalInterrupt(Exception):
180 class SignalInterrupt(Exception):
181 """Exception raised on SIGTERM and SIGHUP."""
181 """Exception raised on SIGTERM and SIGHUP."""
182
182
183 # differences from SafeConfigParser:
183 # differences from SafeConfigParser:
184 # - case-sensitive keys
184 # - case-sensitive keys
185 # - allows values that are not strings (this means that you may not
185 # - allows values that are not strings (this means that you may not
186 # be able to save the configuration to a file)
186 # be able to save the configuration to a file)
187 class configparser(ConfigParser.SafeConfigParser):
187 class configparser(ConfigParser.SafeConfigParser):
188 def optionxform(self, optionstr):
188 def optionxform(self, optionstr):
189 return optionstr
189 return optionstr
190
190
191 def set(self, section, option, value):
191 def set(self, section, option, value):
192 return ConfigParser.ConfigParser.set(self, section, option, value)
192 return ConfigParser.ConfigParser.set(self, section, option, value)
193
193
194 def _interpolate(self, section, option, rawval, vars):
194 def _interpolate(self, section, option, rawval, vars):
195 if not isinstance(rawval, basestring):
195 if not isinstance(rawval, basestring):
196 return rawval
196 return rawval
197 return ConfigParser.SafeConfigParser._interpolate(self, section,
197 return ConfigParser.SafeConfigParser._interpolate(self, section,
198 option, rawval, vars)
198 option, rawval, vars)
199
199
200 def cachefunc(func):
200 def cachefunc(func):
201 '''cache the result of function calls'''
201 '''cache the result of function calls'''
202 # XXX doesn't handle keywords args
202 # XXX doesn't handle keywords args
203 cache = {}
203 cache = {}
204 if func.func_code.co_argcount == 1:
204 if func.func_code.co_argcount == 1:
205 # we gain a small amount of time because
205 # we gain a small amount of time because
206 # we don't need to pack/unpack the list
206 # we don't need to pack/unpack the list
207 def f(arg):
207 def f(arg):
208 if arg not in cache:
208 if arg not in cache:
209 cache[arg] = func(arg)
209 cache[arg] = func(arg)
210 return cache[arg]
210 return cache[arg]
211 else:
211 else:
212 def f(*args):
212 def f(*args):
213 if args not in cache:
213 if args not in cache:
214 cache[args] = func(*args)
214 cache[args] = func(*args)
215 return cache[args]
215 return cache[args]
216
216
217 return f
217 return f
218
218
219 def pipefilter(s, cmd):
219 def pipefilter(s, cmd):
220 '''filter string S through command CMD, returning its output'''
220 '''filter string S through command CMD, returning its output'''
221 (pin, pout) = popen2(cmd, 'b')
221 (pin, pout) = popen2(cmd, 'b')
222 def writer():
222 def writer():
223 try:
223 try:
224 pin.write(s)
224 pin.write(s)
225 pin.close()
225 pin.close()
226 except IOError, inst:
226 except IOError, inst:
227 if inst.errno != errno.EPIPE:
227 if inst.errno != errno.EPIPE:
228 raise
228 raise
229
229
230 # we should use select instead on UNIX, but this will work on most
230 # we should use select instead on UNIX, but this will work on most
231 # systems, including Windows
231 # systems, including Windows
232 w = threading.Thread(target=writer)
232 w = threading.Thread(target=writer)
233 w.start()
233 w.start()
234 f = pout.read()
234 f = pout.read()
235 pout.close()
235 pout.close()
236 w.join()
236 w.join()
237 return f
237 return f
238
238
239 def tempfilter(s, cmd):
239 def tempfilter(s, cmd):
240 '''filter string S through a pair of temporary files with CMD.
240 '''filter string S through a pair of temporary files with CMD.
241 CMD is used as a template to create the real command to be run,
241 CMD is used as a template to create the real command to be run,
242 with the strings INFILE and OUTFILE replaced by the real names of
242 with the strings INFILE and OUTFILE replaced by the real names of
243 the temporary files generated.'''
243 the temporary files generated.'''
244 inname, outname = None, None
244 inname, outname = None, None
245 try:
245 try:
246 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
246 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
247 fp = os.fdopen(infd, 'wb')
247 fp = os.fdopen(infd, 'wb')
248 fp.write(s)
248 fp.write(s)
249 fp.close()
249 fp.close()
250 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
250 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
251 os.close(outfd)
251 os.close(outfd)
252 cmd = cmd.replace('INFILE', inname)
252 cmd = cmd.replace('INFILE', inname)
253 cmd = cmd.replace('OUTFILE', outname)
253 cmd = cmd.replace('OUTFILE', outname)
254 code = os.system(cmd)
254 code = os.system(cmd)
255 if sys.platform == 'OpenVMS' and code & 1:
255 if sys.platform == 'OpenVMS' and code & 1:
256 code = 0
256 code = 0
257 if code: raise Abort(_("command '%s' failed: %s") %
257 if code: raise Abort(_("command '%s' failed: %s") %
258 (cmd, explain_exit(code)))
258 (cmd, explain_exit(code)))
259 return open(outname, 'rb').read()
259 return open(outname, 'rb').read()
260 finally:
260 finally:
261 try:
261 try:
262 if inname: os.unlink(inname)
262 if inname: os.unlink(inname)
263 except: pass
263 except: pass
264 try:
264 try:
265 if outname: os.unlink(outname)
265 if outname: os.unlink(outname)
266 except: pass
266 except: pass
267
267
268 filtertable = {
268 filtertable = {
269 'tempfile:': tempfilter,
269 'tempfile:': tempfilter,
270 'pipe:': pipefilter,
270 'pipe:': pipefilter,
271 }
271 }
272
272
273 def filter(s, cmd):
273 def filter(s, cmd):
274 "filter a string through a command that transforms its input to its output"
274 "filter a string through a command that transforms its input to its output"
275 for name, fn in filtertable.iteritems():
275 for name, fn in filtertable.iteritems():
276 if cmd.startswith(name):
276 if cmd.startswith(name):
277 return fn(s, cmd[len(name):].lstrip())
277 return fn(s, cmd[len(name):].lstrip())
278 return pipefilter(s, cmd)
278 return pipefilter(s, cmd)
279
279
280 def binary(s):
280 def binary(s):
281 """return true if a string is binary data"""
281 """return true if a string is binary data"""
282 if s and '\0' in s:
282 if s and '\0' in s:
283 return True
283 return True
284 return False
284 return False
285
285
286 def unique(g):
286 def unique(g):
287 """return the uniq elements of iterable g"""
287 """return the uniq elements of iterable g"""
288 return dict.fromkeys(g).keys()
288 return dict.fromkeys(g).keys()
289
289
290 def sort(l):
290 def sort(l):
291 if not isinstance(l, list):
291 if not isinstance(l, list):
292 l = list(l)
292 l = list(l)
293 l.sort()
293 l.sort()
294 return l
294 return l
295
295
296 def increasingchunks(source, min=1024, max=65536):
296 def increasingchunks(source, min=1024, max=65536):
297 '''return no less than min bytes per chunk while data remains,
297 '''return no less than min bytes per chunk while data remains,
298 doubling min after each chunk until it reaches max'''
298 doubling min after each chunk until it reaches max'''
299 def log2(x):
299 def log2(x):
300 if not x:
300 if not x:
301 return 0
301 return 0
302 i = 0
302 i = 0
303 while x:
303 while x:
304 x >>= 1
304 x >>= 1
305 i += 1
305 i += 1
306 return i - 1
306 return i - 1
307
307
308 buf = []
308 buf = []
309 blen = 0
309 blen = 0
310 for chunk in source:
310 for chunk in source:
311 buf.append(chunk)
311 buf.append(chunk)
312 blen += len(chunk)
312 blen += len(chunk)
313 if blen >= min:
313 if blen >= min:
314 if min < max:
314 if min < max:
315 min = min << 1
315 min = min << 1
316 nmin = 1 << log2(blen)
316 nmin = 1 << log2(blen)
317 if nmin > min:
317 if nmin > min:
318 min = nmin
318 min = nmin
319 if min > max:
319 if min > max:
320 min = max
320 min = max
321 yield ''.join(buf)
321 yield ''.join(buf)
322 blen = 0
322 blen = 0
323 buf = []
323 buf = []
324 if buf:
324 if buf:
325 yield ''.join(buf)
325 yield ''.join(buf)
326
326
327 class Abort(Exception):
327 class Abort(Exception):
328 """Raised if a command needs to print an error and exit."""
328 """Raised if a command needs to print an error and exit."""
329
329
330 class UnexpectedOutput(Abort):
330 class UnexpectedOutput(Abort):
331 """Raised to print an error with part of output and exit."""
331 """Raised to print an error with part of output and exit."""
332
332
333 def always(fn): return True
333 def always(fn): return True
334 def never(fn): return False
334 def never(fn): return False
335
335
336 def expand_glob(pats):
336 def expand_glob(pats):
337 '''On Windows, expand the implicit globs in a list of patterns'''
337 '''On Windows, expand the implicit globs in a list of patterns'''
338 if os.name != 'nt':
338 if os.name != 'nt':
339 return list(pats)
339 return list(pats)
340 ret = []
340 ret = []
341 for p in pats:
341 for p in pats:
342 kind, name = patkind(p, None)
342 kind, name = patkind(p, None)
343 if kind is None:
343 if kind is None:
344 globbed = glob.glob(name)
344 globbed = glob.glob(name)
345 if globbed:
345 if globbed:
346 ret.extend(globbed)
346 ret.extend(globbed)
347 continue
347 continue
348 # if we couldn't expand the glob, just keep it around
348 # if we couldn't expand the glob, just keep it around
349 ret.append(p)
349 ret.append(p)
350 return ret
350 return ret
351
351
352 def patkind(name, default):
352 def patkind(name, default):
353 """Split a string into an optional pattern kind prefix and the
353 """Split a string into an optional pattern kind prefix and the
354 actual pattern."""
354 actual pattern."""
355 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
355 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
356 if name.startswith(prefix + ':'): return name.split(':', 1)
356 if name.startswith(prefix + ':'): return name.split(':', 1)
357 return default, name
357 return default, name
358
358
359 def globre(pat, head='^', tail='$'):
359 def globre(pat, head='^', tail='$'):
360 "convert a glob pattern into a regexp"
360 "convert a glob pattern into a regexp"
361 i, n = 0, len(pat)
361 i, n = 0, len(pat)
362 res = ''
362 res = ''
363 group = 0
363 group = 0
364 def peek(): return i < n and pat[i]
364 def peek(): return i < n and pat[i]
365 while i < n:
365 while i < n:
366 c = pat[i]
366 c = pat[i]
367 i = i+1
367 i = i+1
368 if c == '*':
368 if c == '*':
369 if peek() == '*':
369 if peek() == '*':
370 i += 1
370 i += 1
371 res += '.*'
371 res += '.*'
372 else:
372 else:
373 res += '[^/]*'
373 res += '[^/]*'
374 elif c == '?':
374 elif c == '?':
375 res += '.'
375 res += '.'
376 elif c == '[':
376 elif c == '[':
377 j = i
377 j = i
378 if j < n and pat[j] in '!]':
378 if j < n and pat[j] in '!]':
379 j += 1
379 j += 1
380 while j < n and pat[j] != ']':
380 while j < n and pat[j] != ']':
381 j += 1
381 j += 1
382 if j >= n:
382 if j >= n:
383 res += '\\['
383 res += '\\['
384 else:
384 else:
385 stuff = pat[i:j].replace('\\','\\\\')
385 stuff = pat[i:j].replace('\\','\\\\')
386 i = j + 1
386 i = j + 1
387 if stuff[0] == '!':
387 if stuff[0] == '!':
388 stuff = '^' + stuff[1:]
388 stuff = '^' + stuff[1:]
389 elif stuff[0] == '^':
389 elif stuff[0] == '^':
390 stuff = '\\' + stuff
390 stuff = '\\' + stuff
391 res = '%s[%s]' % (res, stuff)
391 res = '%s[%s]' % (res, stuff)
392 elif c == '{':
392 elif c == '{':
393 group += 1
393 group += 1
394 res += '(?:'
394 res += '(?:'
395 elif c == '}' and group:
395 elif c == '}' and group:
396 res += ')'
396 res += ')'
397 group -= 1
397 group -= 1
398 elif c == ',' and group:
398 elif c == ',' and group:
399 res += '|'
399 res += '|'
400 elif c == '\\':
400 elif c == '\\':
401 p = peek()
401 p = peek()
402 if p:
402 if p:
403 i += 1
403 i += 1
404 res += re.escape(p)
404 res += re.escape(p)
405 else:
405 else:
406 res += re.escape(c)
406 res += re.escape(c)
407 else:
407 else:
408 res += re.escape(c)
408 res += re.escape(c)
409 return head + res + tail
409 return head + res + tail
410
410
411 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
411 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
412
412
413 def pathto(root, n1, n2):
413 def pathto(root, n1, n2):
414 '''return the relative path from one place to another.
414 '''return the relative path from one place to another.
415 root should use os.sep to separate directories
415 root should use os.sep to separate directories
416 n1 should use os.sep to separate directories
416 n1 should use os.sep to separate directories
417 n2 should use "/" to separate directories
417 n2 should use "/" to separate directories
418 returns an os.sep-separated path.
418 returns an os.sep-separated path.
419
419
420 If n1 is a relative path, it's assumed it's
420 If n1 is a relative path, it's assumed it's
421 relative to root.
421 relative to root.
422 n2 should always be relative to root.
422 n2 should always be relative to root.
423 '''
423 '''
424 if not n1: return localpath(n2)
424 if not n1: return localpath(n2)
425 if os.path.isabs(n1):
425 if os.path.isabs(n1):
426 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
426 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
427 return os.path.join(root, localpath(n2))
427 return os.path.join(root, localpath(n2))
428 n2 = '/'.join((pconvert(root), n2))
428 n2 = '/'.join((pconvert(root), n2))
429 a, b = splitpath(n1), n2.split('/')
429 a, b = splitpath(n1), n2.split('/')
430 a.reverse()
430 a.reverse()
431 b.reverse()
431 b.reverse()
432 while a and b and a[-1] == b[-1]:
432 while a and b and a[-1] == b[-1]:
433 a.pop()
433 a.pop()
434 b.pop()
434 b.pop()
435 b.reverse()
435 b.reverse()
436 return os.sep.join((['..'] * len(a)) + b) or '.'
436 return os.sep.join((['..'] * len(a)) + b) or '.'
437
437
438 def canonpath(root, cwd, myname):
438 def canonpath(root, cwd, myname):
439 """return the canonical path of myname, given cwd and root"""
439 """return the canonical path of myname, given cwd and root"""
440 if root == os.sep:
440 if root == os.sep:
441 rootsep = os.sep
441 rootsep = os.sep
442 elif endswithsep(root):
442 elif endswithsep(root):
443 rootsep = root
443 rootsep = root
444 else:
444 else:
445 rootsep = root + os.sep
445 rootsep = root + os.sep
446 name = myname
446 name = myname
447 if not os.path.isabs(name):
447 if not os.path.isabs(name):
448 name = os.path.join(root, cwd, name)
448 name = os.path.join(root, cwd, name)
449 name = os.path.normpath(name)
449 name = os.path.normpath(name)
450 audit_path = path_auditor(root)
450 audit_path = path_auditor(root)
451 if name != rootsep and name.startswith(rootsep):
451 if name != rootsep and name.startswith(rootsep):
452 name = name[len(rootsep):]
452 name = name[len(rootsep):]
453 audit_path(name)
453 audit_path(name)
454 return pconvert(name)
454 return pconvert(name)
455 elif name == root:
455 elif name == root:
456 return ''
456 return ''
457 else:
457 else:
458 # Determine whether `name' is in the hierarchy at or beneath `root',
458 # Determine whether `name' is in the hierarchy at or beneath `root',
459 # by iterating name=dirname(name) until that causes no change (can't
459 # by iterating name=dirname(name) until that causes no change (can't
460 # check name == '/', because that doesn't work on windows). For each
460 # check name == '/', because that doesn't work on windows). For each
461 # `name', compare dev/inode numbers. If they match, the list `rel'
461 # `name', compare dev/inode numbers. If they match, the list `rel'
462 # holds the reversed list of components making up the relative file
462 # holds the reversed list of components making up the relative file
463 # name we want.
463 # name we want.
464 root_st = os.stat(root)
464 root_st = os.stat(root)
465 rel = []
465 rel = []
466 while True:
466 while True:
467 try:
467 try:
468 name_st = os.stat(name)
468 name_st = os.stat(name)
469 except OSError:
469 except OSError:
470 break
470 break
471 if samestat(name_st, root_st):
471 if samestat(name_st, root_st):
472 if not rel:
472 if not rel:
473 # name was actually the same as root (maybe a symlink)
473 # name was actually the same as root (maybe a symlink)
474 return ''
474 return ''
475 rel.reverse()
475 rel.reverse()
476 name = os.path.join(*rel)
476 name = os.path.join(*rel)
477 audit_path(name)
477 audit_path(name)
478 return pconvert(name)
478 return pconvert(name)
479 dirname, basename = os.path.split(name)
479 dirname, basename = os.path.split(name)
480 rel.append(basename)
480 rel.append(basename)
481 if dirname == name:
481 if dirname == name:
482 break
482 break
483 name = dirname
483 name = dirname
484
484
485 raise Abort('%s not under root' % myname)
485 raise Abort('%s not under root' % myname)
486
486
487 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
487 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
488 """build a function to match a set of file patterns
488 """build a function to match a set of file patterns
489
489
490 arguments:
490 arguments:
491 canonroot - the canonical root of the tree you're matching against
491 canonroot - the canonical root of the tree you're matching against
492 cwd - the current working directory, if relevant
492 cwd - the current working directory, if relevant
493 names - patterns to find
493 names - patterns to find
494 inc - patterns to include
494 inc - patterns to include
495 exc - patterns to exclude
495 exc - patterns to exclude
496 dflt_pat - if a pattern in names has no explicit type, assume this one
496 dflt_pat - if a pattern in names has no explicit type, assume this one
497 src - where these patterns came from (e.g. .hgignore)
497 src - where these patterns came from (e.g. .hgignore)
498
498
499 a pattern is one of:
499 a pattern is one of:
500 'glob:<glob>' - a glob relative to cwd
500 'glob:<glob>' - a glob relative to cwd
501 're:<regexp>' - a regular expression
501 're:<regexp>' - a regular expression
502 'path:<path>' - a path relative to canonroot
502 'path:<path>' - a path relative to canonroot
503 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
503 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
504 'relpath:<path>' - a path relative to cwd
504 'relpath:<path>' - a path relative to cwd
505 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
505 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
506 '<something>' - one of the cases above, selected by the dflt_pat argument
506 '<something>' - one of the cases above, selected by the dflt_pat argument
507
507
508 returns:
508 returns:
509 a 3-tuple containing
509 a 3-tuple containing
510 - list of roots (places where one should start a recursive walk of the fs);
510 - list of roots (places where one should start a recursive walk of the fs);
511 this often matches the explicit non-pattern names passed in, but also
511 this often matches the explicit non-pattern names passed in, but also
512 includes the initial part of glob: patterns that has no glob characters
512 includes the initial part of glob: patterns that has no glob characters
513 - a bool match(filename) function
513 - a bool match(filename) function
514 - a bool indicating if any patterns were passed in
514 - a bool indicating if any patterns were passed in
515 """
515 """
516
516
517 # a common case: no patterns at all
517 # a common case: no patterns at all
518 if not names and not inc and not exc:
518 if not names and not inc and not exc:
519 return [], always, False
519 return [], always, False
520
520
521 def contains_glob(name):
521 def contains_glob(name):
522 for c in name:
522 for c in name:
523 if c in _globchars: return True
523 if c in _globchars: return True
524 return False
524 return False
525
525
526 def regex(kind, name, tail):
526 def regex(kind, name, tail):
527 '''convert a pattern into a regular expression'''
527 '''convert a pattern into a regular expression'''
528 if not name:
528 if not name:
529 return ''
529 return ''
530 if kind == 're':
530 if kind == 're':
531 return name
531 return name
532 elif kind == 'path':
532 elif kind == 'path':
533 return '^' + re.escape(name) + '(?:/|$)'
533 return '^' + re.escape(name) + '(?:/|$)'
534 elif kind == 'relglob':
534 elif kind == 'relglob':
535 return globre(name, '(?:|.*/)', tail)
535 return globre(name, '(?:|.*/)', tail)
536 elif kind == 'relpath':
536 elif kind == 'relpath':
537 return re.escape(name) + '(?:/|$)'
537 return re.escape(name) + '(?:/|$)'
538 elif kind == 'relre':
538 elif kind == 'relre':
539 if name.startswith('^'):
539 if name.startswith('^'):
540 return name
540 return name
541 return '.*' + name
541 return '.*' + name
542 return globre(name, '', tail)
542 return globre(name, '', tail)
543
543
544 def matchfn(pats, tail):
544 def matchfn(pats, tail):
545 """build a matching function from a set of patterns"""
545 """build a matching function from a set of patterns"""
546 if not pats:
546 if not pats:
547 return
547 return
548 try:
548 try:
549 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
549 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
550 if len(pat) > 20000:
550 if len(pat) > 20000:
551 raise OverflowError()
551 raise OverflowError()
552 return re.compile(pat).match
552 return re.compile(pat).match
553 except OverflowError:
553 except OverflowError:
554 # We're using a Python with a tiny regex engine and we
554 # We're using a Python with a tiny regex engine and we
555 # made it explode, so we'll divide the pattern list in two
555 # made it explode, so we'll divide the pattern list in two
556 # until it works
556 # until it works
557 l = len(pats)
557 l = len(pats)
558 if l < 2:
558 if l < 2:
559 raise
559 raise
560 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
560 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
561 return lambda s: a(s) or b(s)
561 return lambda s: a(s) or b(s)
562 except re.error:
562 except re.error:
563 for k, p in pats:
563 for k, p in pats:
564 try:
564 try:
565 re.compile('(?:%s)' % regex(k, p, tail))
565 re.compile('(?:%s)' % regex(k, p, tail))
566 except re.error:
566 except re.error:
567 if src:
567 if src:
568 raise Abort("%s: invalid pattern (%s): %s" %
568 raise Abort("%s: invalid pattern (%s): %s" %
569 (src, k, p))
569 (src, k, p))
570 else:
570 else:
571 raise Abort("invalid pattern (%s): %s" % (k, p))
571 raise Abort("invalid pattern (%s): %s" % (k, p))
572 raise Abort("invalid pattern")
572 raise Abort("invalid pattern")
573
573
574 def globprefix(pat):
574 def globprefix(pat):
575 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
575 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
576 root = []
576 root = []
577 for p in pat.split('/'):
577 for p in pat.split('/'):
578 if contains_glob(p): break
578 if contains_glob(p): break
579 root.append(p)
579 root.append(p)
580 return '/'.join(root) or '.'
580 return '/'.join(root) or '.'
581
581
582 def normalizepats(names, default):
582 def normalizepats(names, default):
583 pats = []
583 pats = []
584 roots = []
584 roots = []
585 anypats = False
585 anypats = False
586 for kind, name in [patkind(p, default) for p in names]:
586 for kind, name in [patkind(p, default) for p in names]:
587 if kind in ('glob', 'relpath'):
587 if kind in ('glob', 'relpath'):
588 name = canonpath(canonroot, cwd, name)
588 name = canonpath(canonroot, cwd, name)
589 elif kind in ('relglob', 'path'):
589 elif kind in ('relglob', 'path'):
590 name = normpath(name)
590 name = normpath(name)
591
591
592 pats.append((kind, name))
592 pats.append((kind, name))
593
593
594 if kind in ('glob', 're', 'relglob', 'relre'):
594 if kind in ('glob', 're', 'relglob', 'relre'):
595 anypats = True
595 anypats = True
596
596
597 if kind == 'glob':
597 if kind == 'glob':
598 root = globprefix(name)
598 root = globprefix(name)
599 roots.append(root)
599 roots.append(root)
600 elif kind in ('relpath', 'path'):
600 elif kind in ('relpath', 'path'):
601 roots.append(name or '.')
601 roots.append(name or '.')
602 elif kind == 'relglob':
602 elif kind == 'relglob':
603 roots.append('.')
603 roots.append('.')
604 return roots, pats, anypats
604 return roots, pats, anypats
605
605
606 roots, pats, anypats = normalizepats(names, dflt_pat)
606 roots, pats, anypats = normalizepats(names, dflt_pat)
607
607
608 patmatch = matchfn(pats, '$') or always
608 patmatch = matchfn(pats, '$') or always
609 incmatch = always
609 incmatch = always
610 if inc:
610 if inc:
611 dummy, inckinds, dummy = normalizepats(inc, 'glob')
611 dummy, inckinds, dummy = normalizepats(inc, 'glob')
612 incmatch = matchfn(inckinds, '(?:/|$)')
612 incmatch = matchfn(inckinds, '(?:/|$)')
613 excmatch = never
613 excmatch = never
614 if exc:
614 if exc:
615 dummy, exckinds, dummy = normalizepats(exc, 'glob')
615 dummy, exckinds, dummy = normalizepats(exc, 'glob')
616 excmatch = matchfn(exckinds, '(?:/|$)')
616 excmatch = matchfn(exckinds, '(?:/|$)')
617
617
618 if not names and inc and not exc:
618 if not names and inc and not exc:
619 # common case: hgignore patterns
619 # common case: hgignore patterns
620 match = incmatch
620 match = incmatch
621 else:
621 else:
622 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
622 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
623
623
624 return (roots, match, (inc or exc or anypats) and True)
624 return (roots, match, (inc or exc or anypats) and True)
625
625
626 _hgexecutable = None
626 _hgexecutable = None
627
627
628 def main_is_frozen():
628 def main_is_frozen():
629 """return True if we are a frozen executable.
629 """return True if we are a frozen executable.
630
630
631 The code supports py2exe (most common, Windows only) and tools/freeze
631 The code supports py2exe (most common, Windows only) and tools/freeze
632 (portable, not much used).
632 (portable, not much used).
633 """
633 """
634 return (hasattr(sys, "frozen") or # new py2exe
634 return (hasattr(sys, "frozen") or # new py2exe
635 hasattr(sys, "importers") or # old py2exe
635 hasattr(sys, "importers") or # old py2exe
636 imp.is_frozen("__main__")) # tools/freeze
636 imp.is_frozen("__main__")) # tools/freeze
637
637
638 def hgexecutable():
638 def hgexecutable():
639 """return location of the 'hg' executable.
639 """return location of the 'hg' executable.
640
640
641 Defaults to $HG or 'hg' in the search path.
641 Defaults to $HG or 'hg' in the search path.
642 """
642 """
643 if _hgexecutable is None:
643 if _hgexecutable is None:
644 hg = os.environ.get('HG')
644 hg = os.environ.get('HG')
645 if hg:
645 if hg:
646 set_hgexecutable(hg)
646 set_hgexecutable(hg)
647 elif main_is_frozen():
647 elif main_is_frozen():
648 set_hgexecutable(sys.executable)
648 set_hgexecutable(sys.executable)
649 else:
649 else:
650 set_hgexecutable(find_exe('hg', 'hg'))
650 set_hgexecutable(find_exe('hg', 'hg'))
651 return _hgexecutable
651 return _hgexecutable
652
652
653 def set_hgexecutable(path):
653 def set_hgexecutable(path):
654 """set location of the 'hg' executable"""
654 """set location of the 'hg' executable"""
655 global _hgexecutable
655 global _hgexecutable
656 _hgexecutable = path
656 _hgexecutable = path
657
657
658 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
658 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
659 '''enhanced shell command execution.
659 '''enhanced shell command execution.
660 run with environment maybe modified, maybe in different dir.
660 run with environment maybe modified, maybe in different dir.
661
661
662 if command fails and onerr is None, return status. if ui object,
662 if command fails and onerr is None, return status. if ui object,
663 print error message and return status, else raise onerr object as
663 print error message and return status, else raise onerr object as
664 exception.'''
664 exception.'''
665 def py2shell(val):
665 def py2shell(val):
666 'convert python object into string that is useful to shell'
666 'convert python object into string that is useful to shell'
667 if val in (None, False):
667 if val in (None, False):
668 return '0'
668 return '0'
669 if val == True:
669 if val == True:
670 return '1'
670 return '1'
671 return str(val)
671 return str(val)
672 oldenv = {}
672 oldenv = {}
673 for k in environ:
673 for k in environ:
674 oldenv[k] = os.environ.get(k)
674 oldenv[k] = os.environ.get(k)
675 if cwd is not None:
675 if cwd is not None:
676 oldcwd = os.getcwd()
676 oldcwd = os.getcwd()
677 origcmd = cmd
677 origcmd = cmd
678 if os.name == 'nt':
678 if os.name == 'nt':
679 cmd = '"%s"' % cmd
679 cmd = '"%s"' % cmd
680 try:
680 try:
681 for k, v in environ.iteritems():
681 for k, v in environ.iteritems():
682 os.environ[k] = py2shell(v)
682 os.environ[k] = py2shell(v)
683 os.environ['HG'] = hgexecutable()
683 os.environ['HG'] = hgexecutable()
684 if cwd is not None and oldcwd != cwd:
684 if cwd is not None and oldcwd != cwd:
685 os.chdir(cwd)
685 os.chdir(cwd)
686 rc = os.system(cmd)
686 rc = os.system(cmd)
687 if sys.platform == 'OpenVMS' and rc & 1:
687 if sys.platform == 'OpenVMS' and rc & 1:
688 rc = 0
688 rc = 0
689 if rc and onerr:
689 if rc and onerr:
690 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
690 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
691 explain_exit(rc)[0])
691 explain_exit(rc)[0])
692 if errprefix:
692 if errprefix:
693 errmsg = '%s: %s' % (errprefix, errmsg)
693 errmsg = '%s: %s' % (errprefix, errmsg)
694 try:
694 try:
695 onerr.warn(errmsg + '\n')
695 onerr.warn(errmsg + '\n')
696 except AttributeError:
696 except AttributeError:
697 raise onerr(errmsg)
697 raise onerr(errmsg)
698 return rc
698 return rc
699 finally:
699 finally:
700 for k, v in oldenv.iteritems():
700 for k, v in oldenv.iteritems():
701 if v is None:
701 if v is None:
702 del os.environ[k]
702 del os.environ[k]
703 else:
703 else:
704 os.environ[k] = v
704 os.environ[k] = v
705 if cwd is not None and oldcwd != cwd:
705 if cwd is not None and oldcwd != cwd:
706 os.chdir(oldcwd)
706 os.chdir(oldcwd)
707
707
708 class SignatureError:
708 class SignatureError:
709 pass
709 pass
710
710
711 def checksignature(func):
711 def checksignature(func):
712 '''wrap a function with code to check for calling errors'''
712 '''wrap a function with code to check for calling errors'''
713 def check(*args, **kwargs):
713 def check(*args, **kwargs):
714 try:
714 try:
715 return func(*args, **kwargs)
715 return func(*args, **kwargs)
716 except TypeError:
716 except TypeError:
717 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
717 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
718 raise SignatureError
718 raise SignatureError
719 raise
719 raise
720
720
721 return check
721 return check
722
722
723 # os.path.lexists is not available on python2.3
723 # os.path.lexists is not available on python2.3
724 def lexists(filename):
724 def lexists(filename):
725 "test whether a file with this name exists. does not follow symlinks"
725 "test whether a file with this name exists. does not follow symlinks"
726 try:
726 try:
727 os.lstat(filename)
727 os.lstat(filename)
728 except:
728 except:
729 return False
729 return False
730 return True
730 return True
731
731
732 def rename(src, dst):
732 def rename(src, dst):
733 """forcibly rename a file"""
733 """forcibly rename a file"""
734 try:
734 try:
735 os.rename(src, dst)
735 os.rename(src, dst)
736 except OSError, err: # FIXME: check err (EEXIST ?)
736 except OSError, err: # FIXME: check err (EEXIST ?)
737 # on windows, rename to existing file is not allowed, so we
737 # on windows, rename to existing file is not allowed, so we
738 # must delete destination first. but if file is open, unlink
738 # must delete destination first. but if file is open, unlink
739 # schedules it for delete but does not delete it. rename
739 # schedules it for delete but does not delete it. rename
740 # happens immediately even for open files, so we create
740 # happens immediately even for open files, so we create
741 # temporary file, delete it, rename destination to that name,
741 # temporary file, delete it, rename destination to that name,
742 # then delete that. then rename is safe to do.
742 # then delete that. then rename is safe to do.
743 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
743 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
744 os.close(fd)
744 os.close(fd)
745 os.unlink(temp)
745 os.unlink(temp)
746 os.rename(dst, temp)
746 os.rename(dst, temp)
747 os.unlink(temp)
747 os.unlink(temp)
748 os.rename(src, dst)
748 os.rename(src, dst)
749
749
750 def unlink(f):
750 def unlink(f):
751 """unlink and remove the directory if it is empty"""
751 """unlink and remove the directory if it is empty"""
752 os.unlink(f)
752 os.unlink(f)
753 # try removing directories that might now be empty
753 # try removing directories that might now be empty
754 try:
754 try:
755 os.removedirs(os.path.dirname(f))
755 os.removedirs(os.path.dirname(f))
756 except OSError:
756 except OSError:
757 pass
757 pass
758
758
759 def copyfile(src, dest):
759 def copyfile(src, dest):
760 "copy a file, preserving mode"
760 "copy a file, preserving mode"
761 if os.path.islink(src):
761 if os.path.islink(src):
762 try:
762 try:
763 os.unlink(dest)
763 os.unlink(dest)
764 except:
764 except:
765 pass
765 pass
766 os.symlink(os.readlink(src), dest)
766 os.symlink(os.readlink(src), dest)
767 else:
767 else:
768 try:
768 try:
769 shutil.copyfile(src, dest)
769 shutil.copyfile(src, dest)
770 shutil.copymode(src, dest)
770 shutil.copymode(src, dest)
771 except shutil.Error, inst:
771 except shutil.Error, inst:
772 raise Abort(str(inst))
772 raise Abort(str(inst))
773
773
774 def copyfiles(src, dst, hardlink=None):
774 def copyfiles(src, dst, hardlink=None):
775 """Copy a directory tree using hardlinks if possible"""
775 """Copy a directory tree using hardlinks if possible"""
776
776
777 if hardlink is None:
777 if hardlink is None:
778 hardlink = (os.stat(src).st_dev ==
778 hardlink = (os.stat(src).st_dev ==
779 os.stat(os.path.dirname(dst)).st_dev)
779 os.stat(os.path.dirname(dst)).st_dev)
780
780
781 if os.path.isdir(src):
781 if os.path.isdir(src):
782 os.mkdir(dst)
782 os.mkdir(dst)
783 for name, kind in osutil.listdir(src):
783 for name, kind in osutil.listdir(src):
784 srcname = os.path.join(src, name)
784 srcname = os.path.join(src, name)
785 dstname = os.path.join(dst, name)
785 dstname = os.path.join(dst, name)
786 copyfiles(srcname, dstname, hardlink)
786 copyfiles(srcname, dstname, hardlink)
787 else:
787 else:
788 if hardlink:
788 if hardlink:
789 try:
789 try:
790 os_link(src, dst)
790 os_link(src, dst)
791 except (IOError, OSError):
791 except (IOError, OSError):
792 hardlink = False
792 hardlink = False
793 shutil.copy(src, dst)
793 shutil.copy(src, dst)
794 else:
794 else:
795 shutil.copy(src, dst)
795 shutil.copy(src, dst)
796
796
797 class path_auditor(object):
797 class path_auditor(object):
798 '''ensure that a filesystem path contains no banned components.
798 '''ensure that a filesystem path contains no banned components.
799 the following properties of a path are checked:
799 the following properties of a path are checked:
800
800
801 - under top-level .hg
801 - under top-level .hg
802 - starts at the root of a windows drive
802 - starts at the root of a windows drive
803 - contains ".."
803 - contains ".."
804 - traverses a symlink (e.g. a/symlink_here/b)
804 - traverses a symlink (e.g. a/symlink_here/b)
805 - inside a nested repository'''
805 - inside a nested repository'''
806
806
807 def __init__(self, root):
807 def __init__(self, root):
808 self.audited = set()
808 self.audited = set()
809 self.auditeddir = set()
809 self.auditeddir = set()
810 self.root = root
810 self.root = root
811
811
812 def __call__(self, path):
812 def __call__(self, path):
813 if path in self.audited:
813 if path in self.audited:
814 return
814 return
815 normpath = os.path.normcase(path)
815 normpath = os.path.normcase(path)
816 parts = splitpath(normpath)
816 parts = splitpath(normpath)
817 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
817 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '.hg.', '')
818 or os.pardir in parts):
818 or os.pardir in parts):
819 raise Abort(_("path contains illegal component: %s") % path)
819 raise Abort(_("path contains illegal component: %s") % path)
820 if '.hg' in path:
821 for p in '.hg', '.hg.':
822 if p in parts[1:-1]:
823 pos = parts.index(p)
824 base = os.path.join(*parts[:pos])
825 raise Abort(_('path %r is inside repo %r') % (path, base))
820 def check(prefix):
826 def check(prefix):
821 curpath = os.path.join(self.root, prefix)
827 curpath = os.path.join(self.root, prefix)
822 try:
828 try:
823 st = os.lstat(curpath)
829 st = os.lstat(curpath)
824 except OSError, err:
830 except OSError, err:
825 # EINVAL can be raised as invalid path syntax under win32.
831 # EINVAL can be raised as invalid path syntax under win32.
826 # They must be ignored for patterns can be checked too.
832 # They must be ignored for patterns can be checked too.
827 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
833 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
828 raise
834 raise
829 else:
835 else:
830 if stat.S_ISLNK(st.st_mode):
836 if stat.S_ISLNK(st.st_mode):
831 raise Abort(_('path %r traverses symbolic link %r') %
837 raise Abort(_('path %r traverses symbolic link %r') %
832 (path, prefix))
838 (path, prefix))
833 elif (stat.S_ISDIR(st.st_mode) and
839 elif (stat.S_ISDIR(st.st_mode) and
834 os.path.isdir(os.path.join(curpath, '.hg'))):
840 os.path.isdir(os.path.join(curpath, '.hg'))):
835 raise Abort(_('path %r is inside repo %r') %
841 raise Abort(_('path %r is inside repo %r') %
836 (path, prefix))
842 (path, prefix))
837 parts.pop()
843 parts.pop()
838 prefixes = []
844 prefixes = []
839 for n in range(len(parts)):
845 for n in range(len(parts)):
840 prefix = os.sep.join(parts)
846 prefix = os.sep.join(parts)
841 if prefix in self.auditeddir:
847 if prefix in self.auditeddir:
842 break
848 break
843 check(prefix)
849 check(prefix)
844 prefixes.append(prefix)
850 prefixes.append(prefix)
845 parts.pop()
851 parts.pop()
846
852
847 self.audited.add(path)
853 self.audited.add(path)
848 # only add prefixes to the cache after checking everything: we don't
854 # only add prefixes to the cache after checking everything: we don't
849 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
855 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
850 self.auditeddir.update(prefixes)
856 self.auditeddir.update(prefixes)
851
857
852 def _makelock_file(info, pathname):
858 def _makelock_file(info, pathname):
853 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
859 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
854 os.write(ld, info)
860 os.write(ld, info)
855 os.close(ld)
861 os.close(ld)
856
862
857 def _readlock_file(pathname):
863 def _readlock_file(pathname):
858 return posixfile(pathname).read()
864 return posixfile(pathname).read()
859
865
860 def nlinks(pathname):
866 def nlinks(pathname):
861 """Return number of hardlinks for the given file."""
867 """Return number of hardlinks for the given file."""
862 return os.lstat(pathname).st_nlink
868 return os.lstat(pathname).st_nlink
863
869
864 if hasattr(os, 'link'):
870 if hasattr(os, 'link'):
865 os_link = os.link
871 os_link = os.link
866 else:
872 else:
867 def os_link(src, dst):
873 def os_link(src, dst):
868 raise OSError(0, _("Hardlinks not supported"))
874 raise OSError(0, _("Hardlinks not supported"))
869
875
870 def fstat(fp):
876 def fstat(fp):
871 '''stat file object that may not have fileno method.'''
877 '''stat file object that may not have fileno method.'''
872 try:
878 try:
873 return os.fstat(fp.fileno())
879 return os.fstat(fp.fileno())
874 except AttributeError:
880 except AttributeError:
875 return os.stat(fp.name)
881 return os.stat(fp.name)
876
882
877 posixfile = file
883 posixfile = file
878
884
879 def openhardlinks():
885 def openhardlinks():
880 '''return true if it is safe to hold open file handles to hardlinks'''
886 '''return true if it is safe to hold open file handles to hardlinks'''
881 return True
887 return True
882
888
883 def _statfiles(files):
889 def _statfiles(files):
884 'Stat each file in files and yield stat or None if file does not exist.'
890 'Stat each file in files and yield stat or None if file does not exist.'
885 lstat = os.lstat
891 lstat = os.lstat
886 for nf in files:
892 for nf in files:
887 try:
893 try:
888 st = lstat(nf)
894 st = lstat(nf)
889 except OSError, err:
895 except OSError, err:
890 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
896 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
891 raise
897 raise
892 st = None
898 st = None
893 yield st
899 yield st
894
900
895 def _statfiles_clustered(files):
901 def _statfiles_clustered(files):
896 '''Stat each file in files and yield stat or None if file does not exist.
902 '''Stat each file in files and yield stat or None if file does not exist.
897 Cluster and cache stat per directory to minimize number of OS stat calls.'''
903 Cluster and cache stat per directory to minimize number of OS stat calls.'''
898 lstat = os.lstat
904 lstat = os.lstat
899 ncase = os.path.normcase
905 ncase = os.path.normcase
900 sep = os.sep
906 sep = os.sep
901 dircache = {} # dirname -> filename -> status | None if file does not exist
907 dircache = {} # dirname -> filename -> status | None if file does not exist
902 for nf in files:
908 for nf in files:
903 nf = ncase(nf)
909 nf = ncase(nf)
904 pos = nf.rfind(sep)
910 pos = nf.rfind(sep)
905 if pos == -1:
911 if pos == -1:
906 dir, base = '.', nf
912 dir, base = '.', nf
907 else:
913 else:
908 dir, base = nf[:pos+1], nf[pos+1:]
914 dir, base = nf[:pos+1], nf[pos+1:]
909 cache = dircache.get(dir, None)
915 cache = dircache.get(dir, None)
910 if cache is None:
916 if cache is None:
911 try:
917 try:
912 dmap = dict([(ncase(n), s)
918 dmap = dict([(ncase(n), s)
913 for n, k, s in osutil.listdir(dir, True)])
919 for n, k, s in osutil.listdir(dir, True)])
914 except OSError, err:
920 except OSError, err:
915 # handle directory not found in Python version prior to 2.5
921 # handle directory not found in Python version prior to 2.5
916 # Python <= 2.4 returns native Windows code 3 in errno
922 # Python <= 2.4 returns native Windows code 3 in errno
917 # Python >= 2.5 returns ENOENT and adds winerror field
923 # Python >= 2.5 returns ENOENT and adds winerror field
918 # EINVAL is raised if dir is not a directory.
924 # EINVAL is raised if dir is not a directory.
919 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
925 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
920 errno.ENOTDIR):
926 errno.ENOTDIR):
921 raise
927 raise
922 dmap = {}
928 dmap = {}
923 cache = dircache.setdefault(dir, dmap)
929 cache = dircache.setdefault(dir, dmap)
924 yield cache.get(base, None)
930 yield cache.get(base, None)
925
931
926 if sys.platform == 'win32':
932 if sys.platform == 'win32':
927 statfiles = _statfiles_clustered
933 statfiles = _statfiles_clustered
928 else:
934 else:
929 statfiles = _statfiles
935 statfiles = _statfiles
930
936
931 getuser_fallback = None
937 getuser_fallback = None
932
938
933 def getuser():
939 def getuser():
934 '''return name of current user'''
940 '''return name of current user'''
935 try:
941 try:
936 return getpass.getuser()
942 return getpass.getuser()
937 except ImportError:
943 except ImportError:
938 # import of pwd will fail on windows - try fallback
944 # import of pwd will fail on windows - try fallback
939 if getuser_fallback:
945 if getuser_fallback:
940 return getuser_fallback()
946 return getuser_fallback()
941 # raised if win32api not available
947 # raised if win32api not available
942 raise Abort(_('user name not available - set USERNAME '
948 raise Abort(_('user name not available - set USERNAME '
943 'environment variable'))
949 'environment variable'))
944
950
945 def username(uid=None):
951 def username(uid=None):
946 """Return the name of the user with the given uid.
952 """Return the name of the user with the given uid.
947
953
948 If uid is None, return the name of the current user."""
954 If uid is None, return the name of the current user."""
949 try:
955 try:
950 import pwd
956 import pwd
951 if uid is None:
957 if uid is None:
952 uid = os.getuid()
958 uid = os.getuid()
953 try:
959 try:
954 return pwd.getpwuid(uid)[0]
960 return pwd.getpwuid(uid)[0]
955 except KeyError:
961 except KeyError:
956 return str(uid)
962 return str(uid)
957 except ImportError:
963 except ImportError:
958 return None
964 return None
959
965
960 def groupname(gid=None):
966 def groupname(gid=None):
961 """Return the name of the group with the given gid.
967 """Return the name of the group with the given gid.
962
968
963 If gid is None, return the name of the current group."""
969 If gid is None, return the name of the current group."""
964 try:
970 try:
965 import grp
971 import grp
966 if gid is None:
972 if gid is None:
967 gid = os.getgid()
973 gid = os.getgid()
968 try:
974 try:
969 return grp.getgrgid(gid)[0]
975 return grp.getgrgid(gid)[0]
970 except KeyError:
976 except KeyError:
971 return str(gid)
977 return str(gid)
972 except ImportError:
978 except ImportError:
973 return None
979 return None
974
980
975 # File system features
981 # File system features
976
982
977 def checkcase(path):
983 def checkcase(path):
978 """
984 """
979 Check whether the given path is on a case-sensitive filesystem
985 Check whether the given path is on a case-sensitive filesystem
980
986
981 Requires a path (like /foo/.hg) ending with a foldable final
987 Requires a path (like /foo/.hg) ending with a foldable final
982 directory component.
988 directory component.
983 """
989 """
984 s1 = os.stat(path)
990 s1 = os.stat(path)
985 d, b = os.path.split(path)
991 d, b = os.path.split(path)
986 p2 = os.path.join(d, b.upper())
992 p2 = os.path.join(d, b.upper())
987 if path == p2:
993 if path == p2:
988 p2 = os.path.join(d, b.lower())
994 p2 = os.path.join(d, b.lower())
989 try:
995 try:
990 s2 = os.stat(p2)
996 s2 = os.stat(p2)
991 if s2 == s1:
997 if s2 == s1:
992 return False
998 return False
993 return True
999 return True
994 except:
1000 except:
995 return True
1001 return True
996
1002
997 _fspathcache = {}
1003 _fspathcache = {}
998 def fspath(name, root):
1004 def fspath(name, root):
999 '''Get name in the case stored in the filesystem
1005 '''Get name in the case stored in the filesystem
1000
1006
1001 The name is either relative to root, or it is an absolute path starting
1007 The name is either relative to root, or it is an absolute path starting
1002 with root. Note that this function is unnecessary, and should not be
1008 with root. Note that this function is unnecessary, and should not be
1003 called, for case-sensitive filesystems (simply because it's expensive).
1009 called, for case-sensitive filesystems (simply because it's expensive).
1004 '''
1010 '''
1005 # If name is absolute, make it relative
1011 # If name is absolute, make it relative
1006 if name.lower().startswith(root.lower()):
1012 if name.lower().startswith(root.lower()):
1007 l = len(root)
1013 l = len(root)
1008 if name[l] == os.sep or name[l] == os.altsep:
1014 if name[l] == os.sep or name[l] == os.altsep:
1009 l = l + 1
1015 l = l + 1
1010 name = name[l:]
1016 name = name[l:]
1011
1017
1012 if not os.path.exists(os.path.join(root, name)):
1018 if not os.path.exists(os.path.join(root, name)):
1013 return None
1019 return None
1014
1020
1015 seps = os.sep
1021 seps = os.sep
1016 if os.altsep:
1022 if os.altsep:
1017 seps = seps + os.altsep
1023 seps = seps + os.altsep
1018 # Protect backslashes. This gets silly very quickly.
1024 # Protect backslashes. This gets silly very quickly.
1019 seps.replace('\\','\\\\')
1025 seps.replace('\\','\\\\')
1020 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1026 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1021 dir = os.path.normcase(os.path.normpath(root))
1027 dir = os.path.normcase(os.path.normpath(root))
1022 result = []
1028 result = []
1023 for part, sep in pattern.findall(name):
1029 for part, sep in pattern.findall(name):
1024 if sep:
1030 if sep:
1025 result.append(sep)
1031 result.append(sep)
1026 continue
1032 continue
1027
1033
1028 if dir not in _fspathcache:
1034 if dir not in _fspathcache:
1029 _fspathcache[dir] = os.listdir(dir)
1035 _fspathcache[dir] = os.listdir(dir)
1030 contents = _fspathcache[dir]
1036 contents = _fspathcache[dir]
1031
1037
1032 lpart = part.lower()
1038 lpart = part.lower()
1033 for n in contents:
1039 for n in contents:
1034 if n.lower() == lpart:
1040 if n.lower() == lpart:
1035 result.append(n)
1041 result.append(n)
1036 break
1042 break
1037 else:
1043 else:
1038 # Cannot happen, as the file exists!
1044 # Cannot happen, as the file exists!
1039 result.append(part)
1045 result.append(part)
1040 dir = os.path.join(dir, lpart)
1046 dir = os.path.join(dir, lpart)
1041
1047
1042 return ''.join(result)
1048 return ''.join(result)
1043
1049
1044 def checkexec(path):
1050 def checkexec(path):
1045 """
1051 """
1046 Check whether the given path is on a filesystem with UNIX-like exec flags
1052 Check whether the given path is on a filesystem with UNIX-like exec flags
1047
1053
1048 Requires a directory (like /foo/.hg)
1054 Requires a directory (like /foo/.hg)
1049 """
1055 """
1050
1056
1051 # VFAT on some Linux versions can flip mode but it doesn't persist
1057 # VFAT on some Linux versions can flip mode but it doesn't persist
1052 # a FS remount. Frequently we can detect it if files are created
1058 # a FS remount. Frequently we can detect it if files are created
1053 # with exec bit on.
1059 # with exec bit on.
1054
1060
1055 try:
1061 try:
1056 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1062 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1057 fh, fn = tempfile.mkstemp("", "", path)
1063 fh, fn = tempfile.mkstemp("", "", path)
1058 try:
1064 try:
1059 os.close(fh)
1065 os.close(fh)
1060 m = os.stat(fn).st_mode & 0777
1066 m = os.stat(fn).st_mode & 0777
1061 new_file_has_exec = m & EXECFLAGS
1067 new_file_has_exec = m & EXECFLAGS
1062 os.chmod(fn, m ^ EXECFLAGS)
1068 os.chmod(fn, m ^ EXECFLAGS)
1063 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1069 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1064 finally:
1070 finally:
1065 os.unlink(fn)
1071 os.unlink(fn)
1066 except (IOError, OSError):
1072 except (IOError, OSError):
1067 # we don't care, the user probably won't be able to commit anyway
1073 # we don't care, the user probably won't be able to commit anyway
1068 return False
1074 return False
1069 return not (new_file_has_exec or exec_flags_cannot_flip)
1075 return not (new_file_has_exec or exec_flags_cannot_flip)
1070
1076
1071 def checklink(path):
1077 def checklink(path):
1072 """check whether the given path is on a symlink-capable filesystem"""
1078 """check whether the given path is on a symlink-capable filesystem"""
1073 # mktemp is not racy because symlink creation will fail if the
1079 # mktemp is not racy because symlink creation will fail if the
1074 # file already exists
1080 # file already exists
1075 name = tempfile.mktemp(dir=path)
1081 name = tempfile.mktemp(dir=path)
1076 try:
1082 try:
1077 os.symlink(".", name)
1083 os.symlink(".", name)
1078 os.unlink(name)
1084 os.unlink(name)
1079 return True
1085 return True
1080 except (OSError, AttributeError):
1086 except (OSError, AttributeError):
1081 return False
1087 return False
1082
1088
1083 _umask = os.umask(0)
1089 _umask = os.umask(0)
1084 os.umask(_umask)
1090 os.umask(_umask)
1085
1091
1086 def needbinarypatch():
1092 def needbinarypatch():
1087 """return True if patches should be applied in binary mode by default."""
1093 """return True if patches should be applied in binary mode by default."""
1088 return os.name == 'nt'
1094 return os.name == 'nt'
1089
1095
1090 def endswithsep(path):
1096 def endswithsep(path):
1091 '''Check path ends with os.sep or os.altsep.'''
1097 '''Check path ends with os.sep or os.altsep.'''
1092 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1098 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1093
1099
1094 def splitpath(path):
1100 def splitpath(path):
1095 '''Split path by os.sep.
1101 '''Split path by os.sep.
1096 Note that this function does not use os.altsep because this is
1102 Note that this function does not use os.altsep because this is
1097 an alternative of simple "xxx.split(os.sep)".
1103 an alternative of simple "xxx.split(os.sep)".
1098 It is recommended to use os.path.normpath() before using this
1104 It is recommended to use os.path.normpath() before using this
1099 function if need.'''
1105 function if need.'''
1100 return path.split(os.sep)
1106 return path.split(os.sep)
1101
1107
1102 def gui():
1108 def gui():
1103 '''Are we running in a GUI?'''
1109 '''Are we running in a GUI?'''
1104 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1110 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1105
1111
1106 def lookup_reg(key, name=None, scope=None):
1112 def lookup_reg(key, name=None, scope=None):
1107 return None
1113 return None
1108
1114
1109 # Platform specific variants
1115 # Platform specific variants
1110 if os.name == 'nt':
1116 if os.name == 'nt':
1111 import msvcrt
1117 import msvcrt
1112 nulldev = 'NUL:'
1118 nulldev = 'NUL:'
1113
1119
1114 class winstdout:
1120 class winstdout:
1115 '''stdout on windows misbehaves if sent through a pipe'''
1121 '''stdout on windows misbehaves if sent through a pipe'''
1116
1122
1117 def __init__(self, fp):
1123 def __init__(self, fp):
1118 self.fp = fp
1124 self.fp = fp
1119
1125
1120 def __getattr__(self, key):
1126 def __getattr__(self, key):
1121 return getattr(self.fp, key)
1127 return getattr(self.fp, key)
1122
1128
1123 def close(self):
1129 def close(self):
1124 try:
1130 try:
1125 self.fp.close()
1131 self.fp.close()
1126 except: pass
1132 except: pass
1127
1133
1128 def write(self, s):
1134 def write(self, s):
1129 try:
1135 try:
1130 # This is workaround for "Not enough space" error on
1136 # This is workaround for "Not enough space" error on
1131 # writing large size of data to console.
1137 # writing large size of data to console.
1132 limit = 16000
1138 limit = 16000
1133 l = len(s)
1139 l = len(s)
1134 start = 0
1140 start = 0
1135 while start < l:
1141 while start < l:
1136 end = start + limit
1142 end = start + limit
1137 self.fp.write(s[start:end])
1143 self.fp.write(s[start:end])
1138 start = end
1144 start = end
1139 except IOError, inst:
1145 except IOError, inst:
1140 if inst.errno != 0: raise
1146 if inst.errno != 0: raise
1141 self.close()
1147 self.close()
1142 raise IOError(errno.EPIPE, 'Broken pipe')
1148 raise IOError(errno.EPIPE, 'Broken pipe')
1143
1149
1144 def flush(self):
1150 def flush(self):
1145 try:
1151 try:
1146 return self.fp.flush()
1152 return self.fp.flush()
1147 except IOError, inst:
1153 except IOError, inst:
1148 if inst.errno != errno.EINVAL: raise
1154 if inst.errno != errno.EINVAL: raise
1149 self.close()
1155 self.close()
1150 raise IOError(errno.EPIPE, 'Broken pipe')
1156 raise IOError(errno.EPIPE, 'Broken pipe')
1151
1157
1152 sys.stdout = winstdout(sys.stdout)
1158 sys.stdout = winstdout(sys.stdout)
1153
1159
1154 def _is_win_9x():
1160 def _is_win_9x():
1155 '''return true if run on windows 95, 98 or me.'''
1161 '''return true if run on windows 95, 98 or me.'''
1156 try:
1162 try:
1157 return sys.getwindowsversion()[3] == 1
1163 return sys.getwindowsversion()[3] == 1
1158 except AttributeError:
1164 except AttributeError:
1159 return 'command' in os.environ.get('comspec', '')
1165 return 'command' in os.environ.get('comspec', '')
1160
1166
1161 def openhardlinks():
1167 def openhardlinks():
1162 return not _is_win_9x and "win32api" in locals()
1168 return not _is_win_9x and "win32api" in locals()
1163
1169
1164 def system_rcpath():
1170 def system_rcpath():
1165 try:
1171 try:
1166 return system_rcpath_win32()
1172 return system_rcpath_win32()
1167 except:
1173 except:
1168 return [r'c:\mercurial\mercurial.ini']
1174 return [r'c:\mercurial\mercurial.ini']
1169
1175
1170 def user_rcpath():
1176 def user_rcpath():
1171 '''return os-specific hgrc search path to the user dir'''
1177 '''return os-specific hgrc search path to the user dir'''
1172 try:
1178 try:
1173 path = user_rcpath_win32()
1179 path = user_rcpath_win32()
1174 except:
1180 except:
1175 home = os.path.expanduser('~')
1181 home = os.path.expanduser('~')
1176 path = [os.path.join(home, 'mercurial.ini'),
1182 path = [os.path.join(home, 'mercurial.ini'),
1177 os.path.join(home, '.hgrc')]
1183 os.path.join(home, '.hgrc')]
1178 userprofile = os.environ.get('USERPROFILE')
1184 userprofile = os.environ.get('USERPROFILE')
1179 if userprofile:
1185 if userprofile:
1180 path.append(os.path.join(userprofile, 'mercurial.ini'))
1186 path.append(os.path.join(userprofile, 'mercurial.ini'))
1181 path.append(os.path.join(userprofile, '.hgrc'))
1187 path.append(os.path.join(userprofile, '.hgrc'))
1182 return path
1188 return path
1183
1189
1184 def parse_patch_output(output_line):
1190 def parse_patch_output(output_line):
1185 """parses the output produced by patch and returns the file name"""
1191 """parses the output produced by patch and returns the file name"""
1186 pf = output_line[14:]
1192 pf = output_line[14:]
1187 if pf[0] == '`':
1193 if pf[0] == '`':
1188 pf = pf[1:-1] # Remove the quotes
1194 pf = pf[1:-1] # Remove the quotes
1189 return pf
1195 return pf
1190
1196
1191 def sshargs(sshcmd, host, user, port):
1197 def sshargs(sshcmd, host, user, port):
1192 '''Build argument list for ssh or Plink'''
1198 '''Build argument list for ssh or Plink'''
1193 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1199 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1194 args = user and ("%s@%s" % (user, host)) or host
1200 args = user and ("%s@%s" % (user, host)) or host
1195 return port and ("%s %s %s" % (args, pflag, port)) or args
1201 return port and ("%s %s %s" % (args, pflag, port)) or args
1196
1202
1197 def testpid(pid):
1203 def testpid(pid):
1198 '''return False if pid dead, True if running or not known'''
1204 '''return False if pid dead, True if running or not known'''
1199 return True
1205 return True
1200
1206
1201 def set_flags(f, l, x):
1207 def set_flags(f, l, x):
1202 pass
1208 pass
1203
1209
1204 def set_binary(fd):
1210 def set_binary(fd):
1205 # When run without console, pipes may expose invalid
1211 # When run without console, pipes may expose invalid
1206 # fileno(), usually set to -1.
1212 # fileno(), usually set to -1.
1207 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1213 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1208 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1214 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1209
1215
1210 def pconvert(path):
1216 def pconvert(path):
1211 return '/'.join(splitpath(path))
1217 return '/'.join(splitpath(path))
1212
1218
1213 def localpath(path):
1219 def localpath(path):
1214 return path.replace('/', '\\')
1220 return path.replace('/', '\\')
1215
1221
1216 def normpath(path):
1222 def normpath(path):
1217 return pconvert(os.path.normpath(path))
1223 return pconvert(os.path.normpath(path))
1218
1224
1219 makelock = _makelock_file
1225 makelock = _makelock_file
1220 readlock = _readlock_file
1226 readlock = _readlock_file
1221
1227
1222 def samestat(s1, s2):
1228 def samestat(s1, s2):
1223 return False
1229 return False
1224
1230
1225 # A sequence of backslashes is special iff it precedes a double quote:
1231 # A sequence of backslashes is special iff it precedes a double quote:
1226 # - if there's an even number of backslashes, the double quote is not
1232 # - if there's an even number of backslashes, the double quote is not
1227 # quoted (i.e. it ends the quoted region)
1233 # quoted (i.e. it ends the quoted region)
1228 # - if there's an odd number of backslashes, the double quote is quoted
1234 # - if there's an odd number of backslashes, the double quote is quoted
1229 # - in both cases, every pair of backslashes is unquoted into a single
1235 # - in both cases, every pair of backslashes is unquoted into a single
1230 # backslash
1236 # backslash
1231 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1237 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1232 # So, to quote a string, we must surround it in double quotes, double
1238 # So, to quote a string, we must surround it in double quotes, double
1233 # the number of backslashes that preceed double quotes and add another
1239 # the number of backslashes that preceed double quotes and add another
1234 # backslash before every double quote (being careful with the double
1240 # backslash before every double quote (being careful with the double
1235 # quote we've appended to the end)
1241 # quote we've appended to the end)
1236 _quotere = None
1242 _quotere = None
1237 def shellquote(s):
1243 def shellquote(s):
1238 global _quotere
1244 global _quotere
1239 if _quotere is None:
1245 if _quotere is None:
1240 _quotere = re.compile(r'(\\*)("|\\$)')
1246 _quotere = re.compile(r'(\\*)("|\\$)')
1241 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1247 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1242
1248
1243 def quotecommand(cmd):
1249 def quotecommand(cmd):
1244 """Build a command string suitable for os.popen* calls."""
1250 """Build a command string suitable for os.popen* calls."""
1245 # The extra quotes are needed because popen* runs the command
1251 # The extra quotes are needed because popen* runs the command
1246 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1252 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1247 return '"' + cmd + '"'
1253 return '"' + cmd + '"'
1248
1254
1249 def popen(command, mode='r'):
1255 def popen(command, mode='r'):
1250 # Work around "popen spawned process may not write to stdout
1256 # Work around "popen spawned process may not write to stdout
1251 # under windows"
1257 # under windows"
1252 # http://bugs.python.org/issue1366
1258 # http://bugs.python.org/issue1366
1253 command += " 2> %s" % nulldev
1259 command += " 2> %s" % nulldev
1254 return os.popen(quotecommand(command), mode)
1260 return os.popen(quotecommand(command), mode)
1255
1261
1256 def explain_exit(code):
1262 def explain_exit(code):
1257 return _("exited with status %d") % code, code
1263 return _("exited with status %d") % code, code
1258
1264
1259 # if you change this stub into a real check, please try to implement the
1265 # if you change this stub into a real check, please try to implement the
1260 # username and groupname functions above, too.
1266 # username and groupname functions above, too.
1261 def isowner(fp, st=None):
1267 def isowner(fp, st=None):
1262 return True
1268 return True
1263
1269
1264 def find_in_path(name, path, default=None):
1270 def find_in_path(name, path, default=None):
1265 '''find name in search path. path can be string (will be split
1271 '''find name in search path. path can be string (will be split
1266 with os.pathsep), or iterable thing that returns strings. if name
1272 with os.pathsep), or iterable thing that returns strings. if name
1267 found, return path to name. else return default. name is looked up
1273 found, return path to name. else return default. name is looked up
1268 using cmd.exe rules, using PATHEXT.'''
1274 using cmd.exe rules, using PATHEXT.'''
1269 if isinstance(path, str):
1275 if isinstance(path, str):
1270 path = path.split(os.pathsep)
1276 path = path.split(os.pathsep)
1271
1277
1272 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1278 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1273 pathext = pathext.lower().split(os.pathsep)
1279 pathext = pathext.lower().split(os.pathsep)
1274 isexec = os.path.splitext(name)[1].lower() in pathext
1280 isexec = os.path.splitext(name)[1].lower() in pathext
1275
1281
1276 for p in path:
1282 for p in path:
1277 p_name = os.path.join(p, name)
1283 p_name = os.path.join(p, name)
1278
1284
1279 if isexec and os.path.exists(p_name):
1285 if isexec and os.path.exists(p_name):
1280 return p_name
1286 return p_name
1281
1287
1282 for ext in pathext:
1288 for ext in pathext:
1283 p_name_ext = p_name + ext
1289 p_name_ext = p_name + ext
1284 if os.path.exists(p_name_ext):
1290 if os.path.exists(p_name_ext):
1285 return p_name_ext
1291 return p_name_ext
1286 return default
1292 return default
1287
1293
1288 def set_signal_handler():
1294 def set_signal_handler():
1289 try:
1295 try:
1290 set_signal_handler_win32()
1296 set_signal_handler_win32()
1291 except NameError:
1297 except NameError:
1292 pass
1298 pass
1293
1299
1294 try:
1300 try:
1295 # override functions with win32 versions if possible
1301 # override functions with win32 versions if possible
1296 from util_win32 import *
1302 from util_win32 import *
1297 if not _is_win_9x():
1303 if not _is_win_9x():
1298 posixfile = posixfile_nt
1304 posixfile = posixfile_nt
1299 except ImportError:
1305 except ImportError:
1300 pass
1306 pass
1301
1307
1302 else:
1308 else:
1303 nulldev = '/dev/null'
1309 nulldev = '/dev/null'
1304
1310
1305 def rcfiles(path):
1311 def rcfiles(path):
1306 rcs = [os.path.join(path, 'hgrc')]
1312 rcs = [os.path.join(path, 'hgrc')]
1307 rcdir = os.path.join(path, 'hgrc.d')
1313 rcdir = os.path.join(path, 'hgrc.d')
1308 try:
1314 try:
1309 rcs.extend([os.path.join(rcdir, f)
1315 rcs.extend([os.path.join(rcdir, f)
1310 for f, kind in osutil.listdir(rcdir)
1316 for f, kind in osutil.listdir(rcdir)
1311 if f.endswith(".rc")])
1317 if f.endswith(".rc")])
1312 except OSError:
1318 except OSError:
1313 pass
1319 pass
1314 return rcs
1320 return rcs
1315
1321
1316 def system_rcpath():
1322 def system_rcpath():
1317 path = []
1323 path = []
1318 # old mod_python does not set sys.argv
1324 # old mod_python does not set sys.argv
1319 if len(getattr(sys, 'argv', [])) > 0:
1325 if len(getattr(sys, 'argv', [])) > 0:
1320 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1326 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1321 '/../etc/mercurial'))
1327 '/../etc/mercurial'))
1322 path.extend(rcfiles('/etc/mercurial'))
1328 path.extend(rcfiles('/etc/mercurial'))
1323 return path
1329 return path
1324
1330
1325 def user_rcpath():
1331 def user_rcpath():
1326 return [os.path.expanduser('~/.hgrc')]
1332 return [os.path.expanduser('~/.hgrc')]
1327
1333
1328 def parse_patch_output(output_line):
1334 def parse_patch_output(output_line):
1329 """parses the output produced by patch and returns the file name"""
1335 """parses the output produced by patch and returns the file name"""
1330 pf = output_line[14:]
1336 pf = output_line[14:]
1331 if os.sys.platform == 'OpenVMS':
1337 if os.sys.platform == 'OpenVMS':
1332 if pf[0] == '`':
1338 if pf[0] == '`':
1333 pf = pf[1:-1] # Remove the quotes
1339 pf = pf[1:-1] # Remove the quotes
1334 else:
1340 else:
1335 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1341 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1336 pf = pf[1:-1] # Remove the quotes
1342 pf = pf[1:-1] # Remove the quotes
1337 return pf
1343 return pf
1338
1344
1339 def sshargs(sshcmd, host, user, port):
1345 def sshargs(sshcmd, host, user, port):
1340 '''Build argument list for ssh'''
1346 '''Build argument list for ssh'''
1341 args = user and ("%s@%s" % (user, host)) or host
1347 args = user and ("%s@%s" % (user, host)) or host
1342 return port and ("%s -p %s" % (args, port)) or args
1348 return port and ("%s -p %s" % (args, port)) or args
1343
1349
1344 def is_exec(f):
1350 def is_exec(f):
1345 """check whether a file is executable"""
1351 """check whether a file is executable"""
1346 return (os.lstat(f).st_mode & 0100 != 0)
1352 return (os.lstat(f).st_mode & 0100 != 0)
1347
1353
1348 def set_flags(f, l, x):
1354 def set_flags(f, l, x):
1349 s = os.lstat(f).st_mode
1355 s = os.lstat(f).st_mode
1350 if l:
1356 if l:
1351 if not stat.S_ISLNK(s):
1357 if not stat.S_ISLNK(s):
1352 # switch file to link
1358 # switch file to link
1353 data = file(f).read()
1359 data = file(f).read()
1354 os.unlink(f)
1360 os.unlink(f)
1355 try:
1361 try:
1356 os.symlink(data, f)
1362 os.symlink(data, f)
1357 except:
1363 except:
1358 # failed to make a link, rewrite file
1364 # failed to make a link, rewrite file
1359 file(f, "w").write(data)
1365 file(f, "w").write(data)
1360 # no chmod needed at this point
1366 # no chmod needed at this point
1361 return
1367 return
1362 if stat.S_ISLNK(s):
1368 if stat.S_ISLNK(s):
1363 # switch link to file
1369 # switch link to file
1364 data = os.readlink(f)
1370 data = os.readlink(f)
1365 os.unlink(f)
1371 os.unlink(f)
1366 file(f, "w").write(data)
1372 file(f, "w").write(data)
1367 s = 0666 & ~_umask # avoid restatting for chmod
1373 s = 0666 & ~_umask # avoid restatting for chmod
1368
1374
1369 sx = s & 0100
1375 sx = s & 0100
1370 if x and not sx:
1376 if x and not sx:
1371 # Turn on +x for every +r bit when making a file executable
1377 # Turn on +x for every +r bit when making a file executable
1372 # and obey umask.
1378 # and obey umask.
1373 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1379 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1374 elif not x and sx:
1380 elif not x and sx:
1375 # Turn off all +x bits
1381 # Turn off all +x bits
1376 os.chmod(f, s & 0666)
1382 os.chmod(f, s & 0666)
1377
1383
1378 def set_binary(fd):
1384 def set_binary(fd):
1379 pass
1385 pass
1380
1386
1381 def pconvert(path):
1387 def pconvert(path):
1382 return path
1388 return path
1383
1389
1384 def localpath(path):
1390 def localpath(path):
1385 return path
1391 return path
1386
1392
1387 normpath = os.path.normpath
1393 normpath = os.path.normpath
1388 samestat = os.path.samestat
1394 samestat = os.path.samestat
1389
1395
1390 def makelock(info, pathname):
1396 def makelock(info, pathname):
1391 try:
1397 try:
1392 os.symlink(info, pathname)
1398 os.symlink(info, pathname)
1393 except OSError, why:
1399 except OSError, why:
1394 if why.errno == errno.EEXIST:
1400 if why.errno == errno.EEXIST:
1395 raise
1401 raise
1396 else:
1402 else:
1397 _makelock_file(info, pathname)
1403 _makelock_file(info, pathname)
1398
1404
1399 def readlock(pathname):
1405 def readlock(pathname):
1400 try:
1406 try:
1401 return os.readlink(pathname)
1407 return os.readlink(pathname)
1402 except OSError, why:
1408 except OSError, why:
1403 if why.errno in (errno.EINVAL, errno.ENOSYS):
1409 if why.errno in (errno.EINVAL, errno.ENOSYS):
1404 return _readlock_file(pathname)
1410 return _readlock_file(pathname)
1405 else:
1411 else:
1406 raise
1412 raise
1407
1413
1408 def shellquote(s):
1414 def shellquote(s):
1409 if os.sys.platform == 'OpenVMS':
1415 if os.sys.platform == 'OpenVMS':
1410 return '"%s"' % s
1416 return '"%s"' % s
1411 else:
1417 else:
1412 return "'%s'" % s.replace("'", "'\\''")
1418 return "'%s'" % s.replace("'", "'\\''")
1413
1419
1414 def quotecommand(cmd):
1420 def quotecommand(cmd):
1415 return cmd
1421 return cmd
1416
1422
1417 def popen(command, mode='r'):
1423 def popen(command, mode='r'):
1418 return os.popen(command, mode)
1424 return os.popen(command, mode)
1419
1425
1420 def testpid(pid):
1426 def testpid(pid):
1421 '''return False if pid dead, True if running or not sure'''
1427 '''return False if pid dead, True if running or not sure'''
1422 if os.sys.platform == 'OpenVMS':
1428 if os.sys.platform == 'OpenVMS':
1423 return True
1429 return True
1424 try:
1430 try:
1425 os.kill(pid, 0)
1431 os.kill(pid, 0)
1426 return True
1432 return True
1427 except OSError, inst:
1433 except OSError, inst:
1428 return inst.errno != errno.ESRCH
1434 return inst.errno != errno.ESRCH
1429
1435
1430 def explain_exit(code):
1436 def explain_exit(code):
1431 """return a 2-tuple (desc, code) describing a process's status"""
1437 """return a 2-tuple (desc, code) describing a process's status"""
1432 if os.WIFEXITED(code):
1438 if os.WIFEXITED(code):
1433 val = os.WEXITSTATUS(code)
1439 val = os.WEXITSTATUS(code)
1434 return _("exited with status %d") % val, val
1440 return _("exited with status %d") % val, val
1435 elif os.WIFSIGNALED(code):
1441 elif os.WIFSIGNALED(code):
1436 val = os.WTERMSIG(code)
1442 val = os.WTERMSIG(code)
1437 return _("killed by signal %d") % val, val
1443 return _("killed by signal %d") % val, val
1438 elif os.WIFSTOPPED(code):
1444 elif os.WIFSTOPPED(code):
1439 val = os.WSTOPSIG(code)
1445 val = os.WSTOPSIG(code)
1440 return _("stopped by signal %d") % val, val
1446 return _("stopped by signal %d") % val, val
1441 raise ValueError(_("invalid exit code"))
1447 raise ValueError(_("invalid exit code"))
1442
1448
1443 def isowner(fp, st=None):
1449 def isowner(fp, st=None):
1444 """Return True if the file object f belongs to the current user.
1450 """Return True if the file object f belongs to the current user.
1445
1451
1446 The return value of a util.fstat(f) may be passed as the st argument.
1452 The return value of a util.fstat(f) may be passed as the st argument.
1447 """
1453 """
1448 if st is None:
1454 if st is None:
1449 st = fstat(fp)
1455 st = fstat(fp)
1450 return st.st_uid == os.getuid()
1456 return st.st_uid == os.getuid()
1451
1457
1452 def find_in_path(name, path, default=None):
1458 def find_in_path(name, path, default=None):
1453 '''find name in search path. path can be string (will be split
1459 '''find name in search path. path can be string (will be split
1454 with os.pathsep), or iterable thing that returns strings. if name
1460 with os.pathsep), or iterable thing that returns strings. if name
1455 found, return path to name. else return default.'''
1461 found, return path to name. else return default.'''
1456 if isinstance(path, str):
1462 if isinstance(path, str):
1457 path = path.split(os.pathsep)
1463 path = path.split(os.pathsep)
1458 for p in path:
1464 for p in path:
1459 p_name = os.path.join(p, name)
1465 p_name = os.path.join(p, name)
1460 if os.path.exists(p_name):
1466 if os.path.exists(p_name):
1461 return p_name
1467 return p_name
1462 return default
1468 return default
1463
1469
1464 def set_signal_handler():
1470 def set_signal_handler():
1465 pass
1471 pass
1466
1472
1467 def find_exe(name, default=None):
1473 def find_exe(name, default=None):
1468 '''find path of an executable.
1474 '''find path of an executable.
1469 if name contains a path component, return it as is. otherwise,
1475 if name contains a path component, return it as is. otherwise,
1470 use normal executable search path.'''
1476 use normal executable search path.'''
1471
1477
1472 if os.sep in name or sys.platform == 'OpenVMS':
1478 if os.sep in name or sys.platform == 'OpenVMS':
1473 # don't check the executable bit. if the file isn't
1479 # don't check the executable bit. if the file isn't
1474 # executable, whoever tries to actually run it will give a
1480 # executable, whoever tries to actually run it will give a
1475 # much more useful error message.
1481 # much more useful error message.
1476 return name
1482 return name
1477 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1483 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1478
1484
1479 def mktempcopy(name, emptyok=False, createmode=None):
1485 def mktempcopy(name, emptyok=False, createmode=None):
1480 """Create a temporary file with the same contents from name
1486 """Create a temporary file with the same contents from name
1481
1487
1482 The permission bits are copied from the original file.
1488 The permission bits are copied from the original file.
1483
1489
1484 If the temporary file is going to be truncated immediately, you
1490 If the temporary file is going to be truncated immediately, you
1485 can use emptyok=True as an optimization.
1491 can use emptyok=True as an optimization.
1486
1492
1487 Returns the name of the temporary file.
1493 Returns the name of the temporary file.
1488 """
1494 """
1489 d, fn = os.path.split(name)
1495 d, fn = os.path.split(name)
1490 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1496 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1491 os.close(fd)
1497 os.close(fd)
1492 # Temporary files are created with mode 0600, which is usually not
1498 # Temporary files are created with mode 0600, which is usually not
1493 # what we want. If the original file already exists, just copy
1499 # what we want. If the original file already exists, just copy
1494 # its mode. Otherwise, manually obey umask.
1500 # its mode. Otherwise, manually obey umask.
1495 try:
1501 try:
1496 st_mode = os.lstat(name).st_mode & 0777
1502 st_mode = os.lstat(name).st_mode & 0777
1497 except OSError, inst:
1503 except OSError, inst:
1498 if inst.errno != errno.ENOENT:
1504 if inst.errno != errno.ENOENT:
1499 raise
1505 raise
1500 st_mode = createmode
1506 st_mode = createmode
1501 if st_mode is None:
1507 if st_mode is None:
1502 st_mode = ~_umask
1508 st_mode = ~_umask
1503 st_mode &= 0666
1509 st_mode &= 0666
1504 os.chmod(temp, st_mode)
1510 os.chmod(temp, st_mode)
1505 if emptyok:
1511 if emptyok:
1506 return temp
1512 return temp
1507 try:
1513 try:
1508 try:
1514 try:
1509 ifp = posixfile(name, "rb")
1515 ifp = posixfile(name, "rb")
1510 except IOError, inst:
1516 except IOError, inst:
1511 if inst.errno == errno.ENOENT:
1517 if inst.errno == errno.ENOENT:
1512 return temp
1518 return temp
1513 if not getattr(inst, 'filename', None):
1519 if not getattr(inst, 'filename', None):
1514 inst.filename = name
1520 inst.filename = name
1515 raise
1521 raise
1516 ofp = posixfile(temp, "wb")
1522 ofp = posixfile(temp, "wb")
1517 for chunk in filechunkiter(ifp):
1523 for chunk in filechunkiter(ifp):
1518 ofp.write(chunk)
1524 ofp.write(chunk)
1519 ifp.close()
1525 ifp.close()
1520 ofp.close()
1526 ofp.close()
1521 except:
1527 except:
1522 try: os.unlink(temp)
1528 try: os.unlink(temp)
1523 except: pass
1529 except: pass
1524 raise
1530 raise
1525 return temp
1531 return temp
1526
1532
1527 class atomictempfile(posixfile):
1533 class atomictempfile(posixfile):
1528 """file-like object that atomically updates a file
1534 """file-like object that atomically updates a file
1529
1535
1530 All writes will be redirected to a temporary copy of the original
1536 All writes will be redirected to a temporary copy of the original
1531 file. When rename is called, the copy is renamed to the original
1537 file. When rename is called, the copy is renamed to the original
1532 name, making the changes visible.
1538 name, making the changes visible.
1533 """
1539 """
1534 def __init__(self, name, mode, createmode):
1540 def __init__(self, name, mode, createmode):
1535 self.__name = name
1541 self.__name = name
1536 self.temp = mktempcopy(name, emptyok=('w' in mode),
1542 self.temp = mktempcopy(name, emptyok=('w' in mode),
1537 createmode=createmode)
1543 createmode=createmode)
1538 posixfile.__init__(self, self.temp, mode)
1544 posixfile.__init__(self, self.temp, mode)
1539
1545
1540 def rename(self):
1546 def rename(self):
1541 if not self.closed:
1547 if not self.closed:
1542 posixfile.close(self)
1548 posixfile.close(self)
1543 rename(self.temp, localpath(self.__name))
1549 rename(self.temp, localpath(self.__name))
1544
1550
1545 def __del__(self):
1551 def __del__(self):
1546 if not self.closed:
1552 if not self.closed:
1547 try:
1553 try:
1548 os.unlink(self.temp)
1554 os.unlink(self.temp)
1549 except: pass
1555 except: pass
1550 posixfile.close(self)
1556 posixfile.close(self)
1551
1557
1552 def makedirs(name, mode=None):
1558 def makedirs(name, mode=None):
1553 """recursive directory creation with parent mode inheritance"""
1559 """recursive directory creation with parent mode inheritance"""
1554 try:
1560 try:
1555 os.mkdir(name)
1561 os.mkdir(name)
1556 if mode is not None:
1562 if mode is not None:
1557 os.chmod(name, mode)
1563 os.chmod(name, mode)
1558 return
1564 return
1559 except OSError, err:
1565 except OSError, err:
1560 if err.errno == errno.EEXIST:
1566 if err.errno == errno.EEXIST:
1561 return
1567 return
1562 if err.errno != errno.ENOENT:
1568 if err.errno != errno.ENOENT:
1563 raise
1569 raise
1564 parent = os.path.abspath(os.path.dirname(name))
1570 parent = os.path.abspath(os.path.dirname(name))
1565 makedirs(parent, mode)
1571 makedirs(parent, mode)
1566 makedirs(name, mode)
1572 makedirs(name, mode)
1567
1573
1568 class opener(object):
1574 class opener(object):
1569 """Open files relative to a base directory
1575 """Open files relative to a base directory
1570
1576
1571 This class is used to hide the details of COW semantics and
1577 This class is used to hide the details of COW semantics and
1572 remote file access from higher level code.
1578 remote file access from higher level code.
1573 """
1579 """
1574 def __init__(self, base, audit=True):
1580 def __init__(self, base, audit=True):
1575 self.base = base
1581 self.base = base
1576 if audit:
1582 if audit:
1577 self.audit_path = path_auditor(base)
1583 self.audit_path = path_auditor(base)
1578 else:
1584 else:
1579 self.audit_path = always
1585 self.audit_path = always
1580 self.createmode = None
1586 self.createmode = None
1581
1587
1582 def __getattr__(self, name):
1588 def __getattr__(self, name):
1583 if name == '_can_symlink':
1589 if name == '_can_symlink':
1584 self._can_symlink = checklink(self.base)
1590 self._can_symlink = checklink(self.base)
1585 return self._can_symlink
1591 return self._can_symlink
1586 raise AttributeError(name)
1592 raise AttributeError(name)
1587
1593
1588 def _fixfilemode(self, name):
1594 def _fixfilemode(self, name):
1589 if self.createmode is None:
1595 if self.createmode is None:
1590 return
1596 return
1591 os.chmod(name, self.createmode & 0666)
1597 os.chmod(name, self.createmode & 0666)
1592
1598
1593 def __call__(self, path, mode="r", text=False, atomictemp=False):
1599 def __call__(self, path, mode="r", text=False, atomictemp=False):
1594 self.audit_path(path)
1600 self.audit_path(path)
1595 f = os.path.join(self.base, path)
1601 f = os.path.join(self.base, path)
1596
1602
1597 if not text and "b" not in mode:
1603 if not text and "b" not in mode:
1598 mode += "b" # for that other OS
1604 mode += "b" # for that other OS
1599
1605
1600 nlink = -1
1606 nlink = -1
1601 if mode not in ("r", "rb"):
1607 if mode not in ("r", "rb"):
1602 try:
1608 try:
1603 nlink = nlinks(f)
1609 nlink = nlinks(f)
1604 except OSError:
1610 except OSError:
1605 nlink = 0
1611 nlink = 0
1606 d = os.path.dirname(f)
1612 d = os.path.dirname(f)
1607 if not os.path.isdir(d):
1613 if not os.path.isdir(d):
1608 makedirs(d, self.createmode)
1614 makedirs(d, self.createmode)
1609 if atomictemp:
1615 if atomictemp:
1610 return atomictempfile(f, mode, self.createmode)
1616 return atomictempfile(f, mode, self.createmode)
1611 if nlink > 1:
1617 if nlink > 1:
1612 rename(mktempcopy(f), f)
1618 rename(mktempcopy(f), f)
1613 fp = posixfile(f, mode)
1619 fp = posixfile(f, mode)
1614 if nlink == 0:
1620 if nlink == 0:
1615 self._fixfilemode(f)
1621 self._fixfilemode(f)
1616 return fp
1622 return fp
1617
1623
1618 def symlink(self, src, dst):
1624 def symlink(self, src, dst):
1619 self.audit_path(dst)
1625 self.audit_path(dst)
1620 linkname = os.path.join(self.base, dst)
1626 linkname = os.path.join(self.base, dst)
1621 try:
1627 try:
1622 os.unlink(linkname)
1628 os.unlink(linkname)
1623 except OSError:
1629 except OSError:
1624 pass
1630 pass
1625
1631
1626 dirname = os.path.dirname(linkname)
1632 dirname = os.path.dirname(linkname)
1627 if not os.path.exists(dirname):
1633 if not os.path.exists(dirname):
1628 makedirs(dirname, self.createmode)
1634 makedirs(dirname, self.createmode)
1629
1635
1630 if self._can_symlink:
1636 if self._can_symlink:
1631 try:
1637 try:
1632 os.symlink(src, linkname)
1638 os.symlink(src, linkname)
1633 except OSError, err:
1639 except OSError, err:
1634 raise OSError(err.errno, _('could not symlink to %r: %s') %
1640 raise OSError(err.errno, _('could not symlink to %r: %s') %
1635 (src, err.strerror), linkname)
1641 (src, err.strerror), linkname)
1636 else:
1642 else:
1637 f = self(dst, "w")
1643 f = self(dst, "w")
1638 f.write(src)
1644 f.write(src)
1639 f.close()
1645 f.close()
1640 self._fixfilemode(dst)
1646 self._fixfilemode(dst)
1641
1647
1642 class chunkbuffer(object):
1648 class chunkbuffer(object):
1643 """Allow arbitrary sized chunks of data to be efficiently read from an
1649 """Allow arbitrary sized chunks of data to be efficiently read from an
1644 iterator over chunks of arbitrary size."""
1650 iterator over chunks of arbitrary size."""
1645
1651
1646 def __init__(self, in_iter):
1652 def __init__(self, in_iter):
1647 """in_iter is the iterator that's iterating over the input chunks.
1653 """in_iter is the iterator that's iterating over the input chunks.
1648 targetsize is how big a buffer to try to maintain."""
1654 targetsize is how big a buffer to try to maintain."""
1649 self.iter = iter(in_iter)
1655 self.iter = iter(in_iter)
1650 self.buf = ''
1656 self.buf = ''
1651 self.targetsize = 2**16
1657 self.targetsize = 2**16
1652
1658
1653 def read(self, l):
1659 def read(self, l):
1654 """Read L bytes of data from the iterator of chunks of data.
1660 """Read L bytes of data from the iterator of chunks of data.
1655 Returns less than L bytes if the iterator runs dry."""
1661 Returns less than L bytes if the iterator runs dry."""
1656 if l > len(self.buf) and self.iter:
1662 if l > len(self.buf) and self.iter:
1657 # Clamp to a multiple of self.targetsize
1663 # Clamp to a multiple of self.targetsize
1658 targetsize = max(l, self.targetsize)
1664 targetsize = max(l, self.targetsize)
1659 collector = cStringIO.StringIO()
1665 collector = cStringIO.StringIO()
1660 collector.write(self.buf)
1666 collector.write(self.buf)
1661 collected = len(self.buf)
1667 collected = len(self.buf)
1662 for chunk in self.iter:
1668 for chunk in self.iter:
1663 collector.write(chunk)
1669 collector.write(chunk)
1664 collected += len(chunk)
1670 collected += len(chunk)
1665 if collected >= targetsize:
1671 if collected >= targetsize:
1666 break
1672 break
1667 if collected < targetsize:
1673 if collected < targetsize:
1668 self.iter = False
1674 self.iter = False
1669 self.buf = collector.getvalue()
1675 self.buf = collector.getvalue()
1670 if len(self.buf) == l:
1676 if len(self.buf) == l:
1671 s, self.buf = str(self.buf), ''
1677 s, self.buf = str(self.buf), ''
1672 else:
1678 else:
1673 s, self.buf = self.buf[:l], buffer(self.buf, l)
1679 s, self.buf = self.buf[:l], buffer(self.buf, l)
1674 return s
1680 return s
1675
1681
1676 def filechunkiter(f, size=65536, limit=None):
1682 def filechunkiter(f, size=65536, limit=None):
1677 """Create a generator that produces the data in the file size
1683 """Create a generator that produces the data in the file size
1678 (default 65536) bytes at a time, up to optional limit (default is
1684 (default 65536) bytes at a time, up to optional limit (default is
1679 to read all data). Chunks may be less than size bytes if the
1685 to read all data). Chunks may be less than size bytes if the
1680 chunk is the last chunk in the file, or the file is a socket or
1686 chunk is the last chunk in the file, or the file is a socket or
1681 some other type of file that sometimes reads less data than is
1687 some other type of file that sometimes reads less data than is
1682 requested."""
1688 requested."""
1683 assert size >= 0
1689 assert size >= 0
1684 assert limit is None or limit >= 0
1690 assert limit is None or limit >= 0
1685 while True:
1691 while True:
1686 if limit is None: nbytes = size
1692 if limit is None: nbytes = size
1687 else: nbytes = min(limit, size)
1693 else: nbytes = min(limit, size)
1688 s = nbytes and f.read(nbytes)
1694 s = nbytes and f.read(nbytes)
1689 if not s: break
1695 if not s: break
1690 if limit: limit -= len(s)
1696 if limit: limit -= len(s)
1691 yield s
1697 yield s
1692
1698
1693 def makedate():
1699 def makedate():
1694 lt = time.localtime()
1700 lt = time.localtime()
1695 if lt[8] == 1 and time.daylight:
1701 if lt[8] == 1 and time.daylight:
1696 tz = time.altzone
1702 tz = time.altzone
1697 else:
1703 else:
1698 tz = time.timezone
1704 tz = time.timezone
1699 return time.mktime(lt), tz
1705 return time.mktime(lt), tz
1700
1706
1701 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1707 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1702 """represent a (unixtime, offset) tuple as a localized time.
1708 """represent a (unixtime, offset) tuple as a localized time.
1703 unixtime is seconds since the epoch, and offset is the time zone's
1709 unixtime is seconds since the epoch, and offset is the time zone's
1704 number of seconds away from UTC. if timezone is false, do not
1710 number of seconds away from UTC. if timezone is false, do not
1705 append time zone to string."""
1711 append time zone to string."""
1706 t, tz = date or makedate()
1712 t, tz = date or makedate()
1707 if "%1" in format or "%2" in format:
1713 if "%1" in format or "%2" in format:
1708 sign = (tz > 0) and "-" or "+"
1714 sign = (tz > 0) and "-" or "+"
1709 minutes = abs(tz) / 60
1715 minutes = abs(tz) / 60
1710 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1716 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1711 format = format.replace("%2", "%02d" % (minutes % 60))
1717 format = format.replace("%2", "%02d" % (minutes % 60))
1712 s = time.strftime(format, time.gmtime(float(t) - tz))
1718 s = time.strftime(format, time.gmtime(float(t) - tz))
1713 return s
1719 return s
1714
1720
1715 def shortdate(date=None):
1721 def shortdate(date=None):
1716 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1722 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1717 return datestr(date, format='%Y-%m-%d')
1723 return datestr(date, format='%Y-%m-%d')
1718
1724
1719 def strdate(string, format, defaults=[]):
1725 def strdate(string, format, defaults=[]):
1720 """parse a localized time string and return a (unixtime, offset) tuple.
1726 """parse a localized time string and return a (unixtime, offset) tuple.
1721 if the string cannot be parsed, ValueError is raised."""
1727 if the string cannot be parsed, ValueError is raised."""
1722 def timezone(string):
1728 def timezone(string):
1723 tz = string.split()[-1]
1729 tz = string.split()[-1]
1724 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1730 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1725 sign = (tz[0] == "+") and 1 or -1
1731 sign = (tz[0] == "+") and 1 or -1
1726 hours = int(tz[1:3])
1732 hours = int(tz[1:3])
1727 minutes = int(tz[3:5])
1733 minutes = int(tz[3:5])
1728 return -sign * (hours * 60 + minutes) * 60
1734 return -sign * (hours * 60 + minutes) * 60
1729 if tz == "GMT" or tz == "UTC":
1735 if tz == "GMT" or tz == "UTC":
1730 return 0
1736 return 0
1731 return None
1737 return None
1732
1738
1733 # NOTE: unixtime = localunixtime + offset
1739 # NOTE: unixtime = localunixtime + offset
1734 offset, date = timezone(string), string
1740 offset, date = timezone(string), string
1735 if offset != None:
1741 if offset != None:
1736 date = " ".join(string.split()[:-1])
1742 date = " ".join(string.split()[:-1])
1737
1743
1738 # add missing elements from defaults
1744 # add missing elements from defaults
1739 for part in defaults:
1745 for part in defaults:
1740 found = [True for p in part if ("%"+p) in format]
1746 found = [True for p in part if ("%"+p) in format]
1741 if not found:
1747 if not found:
1742 date += "@" + defaults[part]
1748 date += "@" + defaults[part]
1743 format += "@%" + part[0]
1749 format += "@%" + part[0]
1744
1750
1745 timetuple = time.strptime(date, format)
1751 timetuple = time.strptime(date, format)
1746 localunixtime = int(calendar.timegm(timetuple))
1752 localunixtime = int(calendar.timegm(timetuple))
1747 if offset is None:
1753 if offset is None:
1748 # local timezone
1754 # local timezone
1749 unixtime = int(time.mktime(timetuple))
1755 unixtime = int(time.mktime(timetuple))
1750 offset = unixtime - localunixtime
1756 offset = unixtime - localunixtime
1751 else:
1757 else:
1752 unixtime = localunixtime + offset
1758 unixtime = localunixtime + offset
1753 return unixtime, offset
1759 return unixtime, offset
1754
1760
1755 def parsedate(date, formats=None, defaults=None):
1761 def parsedate(date, formats=None, defaults=None):
1756 """parse a localized date/time string and return a (unixtime, offset) tuple.
1762 """parse a localized date/time string and return a (unixtime, offset) tuple.
1757
1763
1758 The date may be a "unixtime offset" string or in one of the specified
1764 The date may be a "unixtime offset" string or in one of the specified
1759 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1765 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1760 """
1766 """
1761 if not date:
1767 if not date:
1762 return 0, 0
1768 return 0, 0
1763 if isinstance(date, tuple) and len(date) == 2:
1769 if isinstance(date, tuple) and len(date) == 2:
1764 return date
1770 return date
1765 if not formats:
1771 if not formats:
1766 formats = defaultdateformats
1772 formats = defaultdateformats
1767 date = date.strip()
1773 date = date.strip()
1768 try:
1774 try:
1769 when, offset = map(int, date.split(' '))
1775 when, offset = map(int, date.split(' '))
1770 except ValueError:
1776 except ValueError:
1771 # fill out defaults
1777 # fill out defaults
1772 if not defaults:
1778 if not defaults:
1773 defaults = {}
1779 defaults = {}
1774 now = makedate()
1780 now = makedate()
1775 for part in "d mb yY HI M S".split():
1781 for part in "d mb yY HI M S".split():
1776 if part not in defaults:
1782 if part not in defaults:
1777 if part[0] in "HMS":
1783 if part[0] in "HMS":
1778 defaults[part] = "00"
1784 defaults[part] = "00"
1779 else:
1785 else:
1780 defaults[part] = datestr(now, "%" + part[0])
1786 defaults[part] = datestr(now, "%" + part[0])
1781
1787
1782 for format in formats:
1788 for format in formats:
1783 try:
1789 try:
1784 when, offset = strdate(date, format, defaults)
1790 when, offset = strdate(date, format, defaults)
1785 except (ValueError, OverflowError):
1791 except (ValueError, OverflowError):
1786 pass
1792 pass
1787 else:
1793 else:
1788 break
1794 break
1789 else:
1795 else:
1790 raise Abort(_('invalid date: %r ') % date)
1796 raise Abort(_('invalid date: %r ') % date)
1791 # validate explicit (probably user-specified) date and
1797 # validate explicit (probably user-specified) date and
1792 # time zone offset. values must fit in signed 32 bits for
1798 # time zone offset. values must fit in signed 32 bits for
1793 # current 32-bit linux runtimes. timezones go from UTC-12
1799 # current 32-bit linux runtimes. timezones go from UTC-12
1794 # to UTC+14
1800 # to UTC+14
1795 if abs(when) > 0x7fffffff:
1801 if abs(when) > 0x7fffffff:
1796 raise Abort(_('date exceeds 32 bits: %d') % when)
1802 raise Abort(_('date exceeds 32 bits: %d') % when)
1797 if offset < -50400 or offset > 43200:
1803 if offset < -50400 or offset > 43200:
1798 raise Abort(_('impossible time zone offset: %d') % offset)
1804 raise Abort(_('impossible time zone offset: %d') % offset)
1799 return when, offset
1805 return when, offset
1800
1806
1801 def matchdate(date):
1807 def matchdate(date):
1802 """Return a function that matches a given date match specifier
1808 """Return a function that matches a given date match specifier
1803
1809
1804 Formats include:
1810 Formats include:
1805
1811
1806 '{date}' match a given date to the accuracy provided
1812 '{date}' match a given date to the accuracy provided
1807
1813
1808 '<{date}' on or before a given date
1814 '<{date}' on or before a given date
1809
1815
1810 '>{date}' on or after a given date
1816 '>{date}' on or after a given date
1811
1817
1812 """
1818 """
1813
1819
1814 def lower(date):
1820 def lower(date):
1815 d = dict(mb="1", d="1")
1821 d = dict(mb="1", d="1")
1816 return parsedate(date, extendeddateformats, d)[0]
1822 return parsedate(date, extendeddateformats, d)[0]
1817
1823
1818 def upper(date):
1824 def upper(date):
1819 d = dict(mb="12", HI="23", M="59", S="59")
1825 d = dict(mb="12", HI="23", M="59", S="59")
1820 for days in "31 30 29".split():
1826 for days in "31 30 29".split():
1821 try:
1827 try:
1822 d["d"] = days
1828 d["d"] = days
1823 return parsedate(date, extendeddateformats, d)[0]
1829 return parsedate(date, extendeddateformats, d)[0]
1824 except:
1830 except:
1825 pass
1831 pass
1826 d["d"] = "28"
1832 d["d"] = "28"
1827 return parsedate(date, extendeddateformats, d)[0]
1833 return parsedate(date, extendeddateformats, d)[0]
1828
1834
1829 if date[0] == "<":
1835 if date[0] == "<":
1830 when = upper(date[1:])
1836 when = upper(date[1:])
1831 return lambda x: x <= when
1837 return lambda x: x <= when
1832 elif date[0] == ">":
1838 elif date[0] == ">":
1833 when = lower(date[1:])
1839 when = lower(date[1:])
1834 return lambda x: x >= when
1840 return lambda x: x >= when
1835 elif date[0] == "-":
1841 elif date[0] == "-":
1836 try:
1842 try:
1837 days = int(date[1:])
1843 days = int(date[1:])
1838 except ValueError:
1844 except ValueError:
1839 raise Abort(_("invalid day spec: %s") % date[1:])
1845 raise Abort(_("invalid day spec: %s") % date[1:])
1840 when = makedate()[0] - days * 3600 * 24
1846 when = makedate()[0] - days * 3600 * 24
1841 return lambda x: x >= when
1847 return lambda x: x >= when
1842 elif " to " in date:
1848 elif " to " in date:
1843 a, b = date.split(" to ")
1849 a, b = date.split(" to ")
1844 start, stop = lower(a), upper(b)
1850 start, stop = lower(a), upper(b)
1845 return lambda x: x >= start and x <= stop
1851 return lambda x: x >= start and x <= stop
1846 else:
1852 else:
1847 start, stop = lower(date), upper(date)
1853 start, stop = lower(date), upper(date)
1848 return lambda x: x >= start and x <= stop
1854 return lambda x: x >= start and x <= stop
1849
1855
1850 def shortuser(user):
1856 def shortuser(user):
1851 """Return a short representation of a user name or email address."""
1857 """Return a short representation of a user name or email address."""
1852 f = user.find('@')
1858 f = user.find('@')
1853 if f >= 0:
1859 if f >= 0:
1854 user = user[:f]
1860 user = user[:f]
1855 f = user.find('<')
1861 f = user.find('<')
1856 if f >= 0:
1862 if f >= 0:
1857 user = user[f+1:]
1863 user = user[f+1:]
1858 f = user.find(' ')
1864 f = user.find(' ')
1859 if f >= 0:
1865 if f >= 0:
1860 user = user[:f]
1866 user = user[:f]
1861 f = user.find('.')
1867 f = user.find('.')
1862 if f >= 0:
1868 if f >= 0:
1863 user = user[:f]
1869 user = user[:f]
1864 return user
1870 return user
1865
1871
1866 def email(author):
1872 def email(author):
1867 '''get email of author.'''
1873 '''get email of author.'''
1868 r = author.find('>')
1874 r = author.find('>')
1869 if r == -1: r = None
1875 if r == -1: r = None
1870 return author[author.find('<')+1:r]
1876 return author[author.find('<')+1:r]
1871
1877
1872 def ellipsis(text, maxlength=400):
1878 def ellipsis(text, maxlength=400):
1873 """Trim string to at most maxlength (default: 400) characters."""
1879 """Trim string to at most maxlength (default: 400) characters."""
1874 if len(text) <= maxlength:
1880 if len(text) <= maxlength:
1875 return text
1881 return text
1876 else:
1882 else:
1877 return "%s..." % (text[:maxlength-3])
1883 return "%s..." % (text[:maxlength-3])
1878
1884
1879 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1885 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1880 '''yield every hg repository under path, recursively.'''
1886 '''yield every hg repository under path, recursively.'''
1881 def errhandler(err):
1887 def errhandler(err):
1882 if err.filename == path:
1888 if err.filename == path:
1883 raise err
1889 raise err
1884 if followsym and hasattr(os.path, 'samestat'):
1890 if followsym and hasattr(os.path, 'samestat'):
1885 def _add_dir_if_not_there(dirlst, dirname):
1891 def _add_dir_if_not_there(dirlst, dirname):
1886 match = False
1892 match = False
1887 samestat = os.path.samestat
1893 samestat = os.path.samestat
1888 dirstat = os.stat(dirname)
1894 dirstat = os.stat(dirname)
1889 for lstdirstat in dirlst:
1895 for lstdirstat in dirlst:
1890 if samestat(dirstat, lstdirstat):
1896 if samestat(dirstat, lstdirstat):
1891 match = True
1897 match = True
1892 break
1898 break
1893 if not match:
1899 if not match:
1894 dirlst.append(dirstat)
1900 dirlst.append(dirstat)
1895 return not match
1901 return not match
1896 else:
1902 else:
1897 followsym = False
1903 followsym = False
1898
1904
1899 if (seen_dirs is None) and followsym:
1905 if (seen_dirs is None) and followsym:
1900 seen_dirs = []
1906 seen_dirs = []
1901 _add_dir_if_not_there(seen_dirs, path)
1907 _add_dir_if_not_there(seen_dirs, path)
1902 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1908 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1903 if '.hg' in dirs:
1909 if '.hg' in dirs:
1904 yield root # found a repository
1910 yield root # found a repository
1905 qroot = os.path.join(root, '.hg', 'patches')
1911 qroot = os.path.join(root, '.hg', 'patches')
1906 if os.path.isdir(os.path.join(qroot, '.hg')):
1912 if os.path.isdir(os.path.join(qroot, '.hg')):
1907 yield qroot # we have a patch queue repo here
1913 yield qroot # we have a patch queue repo here
1908 if recurse:
1914 if recurse:
1909 # avoid recursing inside the .hg directory
1915 # avoid recursing inside the .hg directory
1910 dirs.remove('.hg')
1916 dirs.remove('.hg')
1911 else:
1917 else:
1912 dirs[:] = [] # don't descend further
1918 dirs[:] = [] # don't descend further
1913 elif followsym:
1919 elif followsym:
1914 newdirs = []
1920 newdirs = []
1915 for d in dirs:
1921 for d in dirs:
1916 fname = os.path.join(root, d)
1922 fname = os.path.join(root, d)
1917 if _add_dir_if_not_there(seen_dirs, fname):
1923 if _add_dir_if_not_there(seen_dirs, fname):
1918 if os.path.islink(fname):
1924 if os.path.islink(fname):
1919 for hgname in walkrepos(fname, True, seen_dirs):
1925 for hgname in walkrepos(fname, True, seen_dirs):
1920 yield hgname
1926 yield hgname
1921 else:
1927 else:
1922 newdirs.append(d)
1928 newdirs.append(d)
1923 dirs[:] = newdirs
1929 dirs[:] = newdirs
1924
1930
1925 _rcpath = None
1931 _rcpath = None
1926
1932
1927 def os_rcpath():
1933 def os_rcpath():
1928 '''return default os-specific hgrc search path'''
1934 '''return default os-specific hgrc search path'''
1929 path = system_rcpath()
1935 path = system_rcpath()
1930 path.extend(user_rcpath())
1936 path.extend(user_rcpath())
1931 path = [os.path.normpath(f) for f in path]
1937 path = [os.path.normpath(f) for f in path]
1932 return path
1938 return path
1933
1939
1934 def rcpath():
1940 def rcpath():
1935 '''return hgrc search path. if env var HGRCPATH is set, use it.
1941 '''return hgrc search path. if env var HGRCPATH is set, use it.
1936 for each item in path, if directory, use files ending in .rc,
1942 for each item in path, if directory, use files ending in .rc,
1937 else use item.
1943 else use item.
1938 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1944 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1939 if no HGRCPATH, use default os-specific path.'''
1945 if no HGRCPATH, use default os-specific path.'''
1940 global _rcpath
1946 global _rcpath
1941 if _rcpath is None:
1947 if _rcpath is None:
1942 if 'HGRCPATH' in os.environ:
1948 if 'HGRCPATH' in os.environ:
1943 _rcpath = []
1949 _rcpath = []
1944 for p in os.environ['HGRCPATH'].split(os.pathsep):
1950 for p in os.environ['HGRCPATH'].split(os.pathsep):
1945 if not p: continue
1951 if not p: continue
1946 if os.path.isdir(p):
1952 if os.path.isdir(p):
1947 for f, kind in osutil.listdir(p):
1953 for f, kind in osutil.listdir(p):
1948 if f.endswith('.rc'):
1954 if f.endswith('.rc'):
1949 _rcpath.append(os.path.join(p, f))
1955 _rcpath.append(os.path.join(p, f))
1950 else:
1956 else:
1951 _rcpath.append(p)
1957 _rcpath.append(p)
1952 else:
1958 else:
1953 _rcpath = os_rcpath()
1959 _rcpath = os_rcpath()
1954 return _rcpath
1960 return _rcpath
1955
1961
1956 def bytecount(nbytes):
1962 def bytecount(nbytes):
1957 '''return byte count formatted as readable string, with units'''
1963 '''return byte count formatted as readable string, with units'''
1958
1964
1959 units = (
1965 units = (
1960 (100, 1<<30, _('%.0f GB')),
1966 (100, 1<<30, _('%.0f GB')),
1961 (10, 1<<30, _('%.1f GB')),
1967 (10, 1<<30, _('%.1f GB')),
1962 (1, 1<<30, _('%.2f GB')),
1968 (1, 1<<30, _('%.2f GB')),
1963 (100, 1<<20, _('%.0f MB')),
1969 (100, 1<<20, _('%.0f MB')),
1964 (10, 1<<20, _('%.1f MB')),
1970 (10, 1<<20, _('%.1f MB')),
1965 (1, 1<<20, _('%.2f MB')),
1971 (1, 1<<20, _('%.2f MB')),
1966 (100, 1<<10, _('%.0f KB')),
1972 (100, 1<<10, _('%.0f KB')),
1967 (10, 1<<10, _('%.1f KB')),
1973 (10, 1<<10, _('%.1f KB')),
1968 (1, 1<<10, _('%.2f KB')),
1974 (1, 1<<10, _('%.2f KB')),
1969 (1, 1, _('%.0f bytes')),
1975 (1, 1, _('%.0f bytes')),
1970 )
1976 )
1971
1977
1972 for multiplier, divisor, format in units:
1978 for multiplier, divisor, format in units:
1973 if nbytes >= divisor * multiplier:
1979 if nbytes >= divisor * multiplier:
1974 return format % (nbytes / float(divisor))
1980 return format % (nbytes / float(divisor))
1975 return units[-1][2] % nbytes
1981 return units[-1][2] % nbytes
1976
1982
1977 def drop_scheme(scheme, path):
1983 def drop_scheme(scheme, path):
1978 sc = scheme + ':'
1984 sc = scheme + ':'
1979 if path.startswith(sc):
1985 if path.startswith(sc):
1980 path = path[len(sc):]
1986 path = path[len(sc):]
1981 if path.startswith('//'):
1987 if path.startswith('//'):
1982 path = path[2:]
1988 path = path[2:]
1983 return path
1989 return path
1984
1990
1985 def uirepr(s):
1991 def uirepr(s):
1986 # Avoid double backslash in Windows path repr()
1992 # Avoid double backslash in Windows path repr()
1987 return repr(s).replace('\\\\', '\\')
1993 return repr(s).replace('\\\\', '\\')
General Comments 0
You need to be logged in to leave comments. Login now