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