##// END OF EJS Templates
move util.Abort to error.py
Matt Mackall -
r7947:a454eeb1 default
parent child Browse files
Show More
@@ -1,64 +1,67
1 """
1 """
2 error.py - Mercurial exceptions
2 error.py - Mercurial exceptions
3
3
4 This allows us to catch exceptions at higher levels without forcing imports
4 This allows us to catch exceptions at higher levels without forcing imports
5
5
6 Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
6 Copyright 2005-2008 Matt Mackall <mpm@selenic.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
11
12 # Do not import anything here, please
12 # Do not import anything here, please
13
13
14 class RevlogError(Exception):
14 class RevlogError(Exception):
15 pass
15 pass
16
16
17 class LookupError(RevlogError, KeyError):
17 class LookupError(RevlogError, KeyError):
18 def __init__(self, name, index, message):
18 def __init__(self, name, index, message):
19 self.name = name
19 self.name = name
20 if isinstance(name, str) and len(name) == 20:
20 if isinstance(name, str) and len(name) == 20:
21 from node import short
21 from node import short
22 name = short(name)
22 name = short(name)
23 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
23 RevlogError.__init__(self, '%s@%s: %s' % (index, name, message))
24
24
25 def __str__(self):
25 def __str__(self):
26 return RevlogError.__str__(self)
26 return RevlogError.__str__(self)
27
27
28 class ParseError(Exception):
28 class ParseError(Exception):
29 """Exception raised on errors in parsing the command line."""
29 """Exception raised on errors in parsing the command line."""
30
30
31 class RepoError(Exception):
31 class RepoError(Exception):
32 pass
32 pass
33
33
34 class CapabilityError(RepoError):
34 class CapabilityError(RepoError):
35 pass
35 pass
36
36
37 class LockError(IOError):
37 class LockError(IOError):
38 def __init__(self, errno, strerror, filename, desc):
38 def __init__(self, errno, strerror, filename, desc):
39 IOError.__init__(self, errno, strerror, filename)
39 IOError.__init__(self, errno, strerror, filename)
40 self.desc = desc
40 self.desc = desc
41
41
42 class LockHeld(LockError):
42 class LockHeld(LockError):
43 def __init__(self, errno, filename, desc, locker):
43 def __init__(self, errno, filename, desc, locker):
44 LockError.__init__(self, errno, 'Lock held', filename, desc)
44 LockError.__init__(self, errno, 'Lock held', filename, desc)
45 self.locker = locker
45 self.locker = locker
46
46
47 class LockUnavailable(LockError):
47 class LockUnavailable(LockError):
48 pass
48 pass
49
49
50 class ResponseError(Exception):
50 class ResponseError(Exception):
51 """Raised to print an error with part of output and exit."""
51 """Raised to print an error with part of output and exit."""
52
52
53 class UnknownCommand(Exception):
53 class UnknownCommand(Exception):
54 """Exception raised if command is not in the command table."""
54 """Exception raised if command is not in the command table."""
55
55
56 class AmbiguousCommand(Exception):
56 class AmbiguousCommand(Exception):
57 """Exception raised if command shortcut matches more than one command."""
57 """Exception raised if command shortcut matches more than one command."""
58
58
59 # derived from KeyboardInterrupt to simplify some breakout code
59 # derived from KeyboardInterrupt to simplify some breakout code
60 class SignalInterrupt(KeyboardInterrupt):
60 class SignalInterrupt(KeyboardInterrupt):
61 """Exception raised on SIGTERM and SIGHUP."""
61 """Exception raised on SIGTERM and SIGHUP."""
62
62
63 class SignatureError(Exception):
63 class SignatureError(Exception):
64 pass
64 pass
65
66 class Abort(Exception):
67 """Raised if a command needs to print an error and exit."""
@@ -1,1568 +1,1567
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, re, shutil, sys, tempfile, traceback, error
16 import cStringIO, errno, 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 Abort = error.Abort
337 """Raised if a command needs to print an error and exit."""
338
337
339 def always(fn): return True
338 def always(fn): return True
340 def never(fn): return False
339 def never(fn): return False
341
340
342 def patkind(name, default):
341 def patkind(name, default):
343 """Split a string into an optional pattern kind prefix and the
342 """Split a string into an optional pattern kind prefix and the
344 actual pattern."""
343 actual pattern."""
345 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
344 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
346 if name.startswith(prefix + ':'): return name.split(':', 1)
345 if name.startswith(prefix + ':'): return name.split(':', 1)
347 return default, name
346 return default, name
348
347
349 def globre(pat, head='^', tail='$'):
348 def globre(pat, head='^', tail='$'):
350 "convert a glob pattern into a regexp"
349 "convert a glob pattern into a regexp"
351 i, n = 0, len(pat)
350 i, n = 0, len(pat)
352 res = ''
351 res = ''
353 group = 0
352 group = 0
354 def peek(): return i < n and pat[i]
353 def peek(): return i < n and pat[i]
355 while i < n:
354 while i < n:
356 c = pat[i]
355 c = pat[i]
357 i = i+1
356 i = i+1
358 if c == '*':
357 if c == '*':
359 if peek() == '*':
358 if peek() == '*':
360 i += 1
359 i += 1
361 res += '.*'
360 res += '.*'
362 else:
361 else:
363 res += '[^/]*'
362 res += '[^/]*'
364 elif c == '?':
363 elif c == '?':
365 res += '.'
364 res += '.'
366 elif c == '[':
365 elif c == '[':
367 j = i
366 j = i
368 if j < n and pat[j] in '!]':
367 if j < n and pat[j] in '!]':
369 j += 1
368 j += 1
370 while j < n and pat[j] != ']':
369 while j < n and pat[j] != ']':
371 j += 1
370 j += 1
372 if j >= n:
371 if j >= n:
373 res += '\\['
372 res += '\\['
374 else:
373 else:
375 stuff = pat[i:j].replace('\\','\\\\')
374 stuff = pat[i:j].replace('\\','\\\\')
376 i = j + 1
375 i = j + 1
377 if stuff[0] == '!':
376 if stuff[0] == '!':
378 stuff = '^' + stuff[1:]
377 stuff = '^' + stuff[1:]
379 elif stuff[0] == '^':
378 elif stuff[0] == '^':
380 stuff = '\\' + stuff
379 stuff = '\\' + stuff
381 res = '%s[%s]' % (res, stuff)
380 res = '%s[%s]' % (res, stuff)
382 elif c == '{':
381 elif c == '{':
383 group += 1
382 group += 1
384 res += '(?:'
383 res += '(?:'
385 elif c == '}' and group:
384 elif c == '}' and group:
386 res += ')'
385 res += ')'
387 group -= 1
386 group -= 1
388 elif c == ',' and group:
387 elif c == ',' and group:
389 res += '|'
388 res += '|'
390 elif c == '\\':
389 elif c == '\\':
391 p = peek()
390 p = peek()
392 if p:
391 if p:
393 i += 1
392 i += 1
394 res += re.escape(p)
393 res += re.escape(p)
395 else:
394 else:
396 res += re.escape(c)
395 res += re.escape(c)
397 else:
396 else:
398 res += re.escape(c)
397 res += re.escape(c)
399 return head + res + tail
398 return head + res + tail
400
399
401 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
400 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
402
401
403 def pathto(root, n1, n2):
402 def pathto(root, n1, n2):
404 '''return the relative path from one place to another.
403 '''return the relative path from one place to another.
405 root should use os.sep to separate directories
404 root should use os.sep to separate directories
406 n1 should use os.sep to separate directories
405 n1 should use os.sep to separate directories
407 n2 should use "/" to separate directories
406 n2 should use "/" to separate directories
408 returns an os.sep-separated path.
407 returns an os.sep-separated path.
409
408
410 If n1 is a relative path, it's assumed it's
409 If n1 is a relative path, it's assumed it's
411 relative to root.
410 relative to root.
412 n2 should always be relative to root.
411 n2 should always be relative to root.
413 '''
412 '''
414 if not n1: return localpath(n2)
413 if not n1: return localpath(n2)
415 if os.path.isabs(n1):
414 if os.path.isabs(n1):
416 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
415 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
417 return os.path.join(root, localpath(n2))
416 return os.path.join(root, localpath(n2))
418 n2 = '/'.join((pconvert(root), n2))
417 n2 = '/'.join((pconvert(root), n2))
419 a, b = splitpath(n1), n2.split('/')
418 a, b = splitpath(n1), n2.split('/')
420 a.reverse()
419 a.reverse()
421 b.reverse()
420 b.reverse()
422 while a and b and a[-1] == b[-1]:
421 while a and b and a[-1] == b[-1]:
423 a.pop()
422 a.pop()
424 b.pop()
423 b.pop()
425 b.reverse()
424 b.reverse()
426 return os.sep.join((['..'] * len(a)) + b) or '.'
425 return os.sep.join((['..'] * len(a)) + b) or '.'
427
426
428 def canonpath(root, cwd, myname):
427 def canonpath(root, cwd, myname):
429 """return the canonical path of myname, given cwd and root"""
428 """return the canonical path of myname, given cwd and root"""
430 if root == os.sep:
429 if root == os.sep:
431 rootsep = os.sep
430 rootsep = os.sep
432 elif endswithsep(root):
431 elif endswithsep(root):
433 rootsep = root
432 rootsep = root
434 else:
433 else:
435 rootsep = root + os.sep
434 rootsep = root + os.sep
436 name = myname
435 name = myname
437 if not os.path.isabs(name):
436 if not os.path.isabs(name):
438 name = os.path.join(root, cwd, name)
437 name = os.path.join(root, cwd, name)
439 name = os.path.normpath(name)
438 name = os.path.normpath(name)
440 audit_path = path_auditor(root)
439 audit_path = path_auditor(root)
441 if name != rootsep and name.startswith(rootsep):
440 if name != rootsep and name.startswith(rootsep):
442 name = name[len(rootsep):]
441 name = name[len(rootsep):]
443 audit_path(name)
442 audit_path(name)
444 return pconvert(name)
443 return pconvert(name)
445 elif name == root:
444 elif name == root:
446 return ''
445 return ''
447 else:
446 else:
448 # Determine whether `name' is in the hierarchy at or beneath `root',
447 # Determine whether `name' is in the hierarchy at or beneath `root',
449 # by iterating name=dirname(name) until that causes no change (can't
448 # by iterating name=dirname(name) until that causes no change (can't
450 # check name == '/', because that doesn't work on windows). For each
449 # check name == '/', because that doesn't work on windows). For each
451 # `name', compare dev/inode numbers. If they match, the list `rel'
450 # `name', compare dev/inode numbers. If they match, the list `rel'
452 # holds the reversed list of components making up the relative file
451 # holds the reversed list of components making up the relative file
453 # name we want.
452 # name we want.
454 root_st = os.stat(root)
453 root_st = os.stat(root)
455 rel = []
454 rel = []
456 while True:
455 while True:
457 try:
456 try:
458 name_st = os.stat(name)
457 name_st = os.stat(name)
459 except OSError:
458 except OSError:
460 break
459 break
461 if samestat(name_st, root_st):
460 if samestat(name_st, root_st):
462 if not rel:
461 if not rel:
463 # name was actually the same as root (maybe a symlink)
462 # name was actually the same as root (maybe a symlink)
464 return ''
463 return ''
465 rel.reverse()
464 rel.reverse()
466 name = os.path.join(*rel)
465 name = os.path.join(*rel)
467 audit_path(name)
466 audit_path(name)
468 return pconvert(name)
467 return pconvert(name)
469 dirname, basename = os.path.split(name)
468 dirname, basename = os.path.split(name)
470 rel.append(basename)
469 rel.append(basename)
471 if dirname == name:
470 if dirname == name:
472 break
471 break
473 name = dirname
472 name = dirname
474
473
475 raise Abort('%s not under root' % myname)
474 raise Abort('%s not under root' % myname)
476
475
477 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
476 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
478 """build a function to match a set of file patterns
477 """build a function to match a set of file patterns
479
478
480 arguments:
479 arguments:
481 canonroot - the canonical root of the tree you're matching against
480 canonroot - the canonical root of the tree you're matching against
482 cwd - the current working directory, if relevant
481 cwd - the current working directory, if relevant
483 names - patterns to find
482 names - patterns to find
484 inc - patterns to include
483 inc - patterns to include
485 exc - patterns to exclude
484 exc - patterns to exclude
486 dflt_pat - if a pattern in names has no explicit type, assume this one
485 dflt_pat - if a pattern in names has no explicit type, assume this one
487 src - where these patterns came from (e.g. .hgignore)
486 src - where these patterns came from (e.g. .hgignore)
488
487
489 a pattern is one of:
488 a pattern is one of:
490 'glob:<glob>' - a glob relative to cwd
489 'glob:<glob>' - a glob relative to cwd
491 're:<regexp>' - a regular expression
490 're:<regexp>' - a regular expression
492 'path:<path>' - a path relative to canonroot
491 'path:<path>' - a path relative to canonroot
493 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
492 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
494 'relpath:<path>' - a path relative to cwd
493 'relpath:<path>' - a path relative to cwd
495 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
494 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
496 '<something>' - one of the cases above, selected by the dflt_pat argument
495 '<something>' - one of the cases above, selected by the dflt_pat argument
497
496
498 returns:
497 returns:
499 a 3-tuple containing
498 a 3-tuple containing
500 - list of roots (places where one should start a recursive walk of the fs);
499 - list of roots (places where one should start a recursive walk of the fs);
501 this often matches the explicit non-pattern names passed in, but also
500 this often matches the explicit non-pattern names passed in, but also
502 includes the initial part of glob: patterns that has no glob characters
501 includes the initial part of glob: patterns that has no glob characters
503 - a bool match(filename) function
502 - a bool match(filename) function
504 - a bool indicating if any patterns were passed in
503 - a bool indicating if any patterns were passed in
505 """
504 """
506
505
507 # a common case: no patterns at all
506 # a common case: no patterns at all
508 if not names and not inc and not exc:
507 if not names and not inc and not exc:
509 return [], always, False
508 return [], always, False
510
509
511 def contains_glob(name):
510 def contains_glob(name):
512 for c in name:
511 for c in name:
513 if c in _globchars: return True
512 if c in _globchars: return True
514 return False
513 return False
515
514
516 def regex(kind, name, tail):
515 def regex(kind, name, tail):
517 '''convert a pattern into a regular expression'''
516 '''convert a pattern into a regular expression'''
518 if not name:
517 if not name:
519 return ''
518 return ''
520 if kind == 're':
519 if kind == 're':
521 return name
520 return name
522 elif kind == 'path':
521 elif kind == 'path':
523 return '^' + re.escape(name) + '(?:/|$)'
522 return '^' + re.escape(name) + '(?:/|$)'
524 elif kind == 'relglob':
523 elif kind == 'relglob':
525 return globre(name, '(?:|.*/)', tail)
524 return globre(name, '(?:|.*/)', tail)
526 elif kind == 'relpath':
525 elif kind == 'relpath':
527 return re.escape(name) + '(?:/|$)'
526 return re.escape(name) + '(?:/|$)'
528 elif kind == 'relre':
527 elif kind == 'relre':
529 if name.startswith('^'):
528 if name.startswith('^'):
530 return name
529 return name
531 return '.*' + name
530 return '.*' + name
532 return globre(name, '', tail)
531 return globre(name, '', tail)
533
532
534 def matchfn(pats, tail):
533 def matchfn(pats, tail):
535 """build a matching function from a set of patterns"""
534 """build a matching function from a set of patterns"""
536 if not pats:
535 if not pats:
537 return
536 return
538 try:
537 try:
539 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
538 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
540 if len(pat) > 20000:
539 if len(pat) > 20000:
541 raise OverflowError()
540 raise OverflowError()
542 return re.compile(pat).match
541 return re.compile(pat).match
543 except OverflowError:
542 except OverflowError:
544 # We're using a Python with a tiny regex engine and we
543 # We're using a Python with a tiny regex engine and we
545 # made it explode, so we'll divide the pattern list in two
544 # made it explode, so we'll divide the pattern list in two
546 # until it works
545 # until it works
547 l = len(pats)
546 l = len(pats)
548 if l < 2:
547 if l < 2:
549 raise
548 raise
550 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
549 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
551 return lambda s: a(s) or b(s)
550 return lambda s: a(s) or b(s)
552 except re.error:
551 except re.error:
553 for k, p in pats:
552 for k, p in pats:
554 try:
553 try:
555 re.compile('(?:%s)' % regex(k, p, tail))
554 re.compile('(?:%s)' % regex(k, p, tail))
556 except re.error:
555 except re.error:
557 if src:
556 if src:
558 raise Abort("%s: invalid pattern (%s): %s" %
557 raise Abort("%s: invalid pattern (%s): %s" %
559 (src, k, p))
558 (src, k, p))
560 else:
559 else:
561 raise Abort("invalid pattern (%s): %s" % (k, p))
560 raise Abort("invalid pattern (%s): %s" % (k, p))
562 raise Abort("invalid pattern")
561 raise Abort("invalid pattern")
563
562
564 def globprefix(pat):
563 def globprefix(pat):
565 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
564 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
566 root = []
565 root = []
567 for p in pat.split('/'):
566 for p in pat.split('/'):
568 if contains_glob(p): break
567 if contains_glob(p): break
569 root.append(p)
568 root.append(p)
570 return '/'.join(root) or '.'
569 return '/'.join(root) or '.'
571
570
572 def normalizepats(names, default):
571 def normalizepats(names, default):
573 pats = []
572 pats = []
574 roots = []
573 roots = []
575 anypats = False
574 anypats = False
576 for kind, name in [patkind(p, default) for p in names]:
575 for kind, name in [patkind(p, default) for p in names]:
577 if kind in ('glob', 'relpath'):
576 if kind in ('glob', 'relpath'):
578 name = canonpath(canonroot, cwd, name)
577 name = canonpath(canonroot, cwd, name)
579 elif kind in ('relglob', 'path'):
578 elif kind in ('relglob', 'path'):
580 name = normpath(name)
579 name = normpath(name)
581
580
582 pats.append((kind, name))
581 pats.append((kind, name))
583
582
584 if kind in ('glob', 're', 'relglob', 'relre'):
583 if kind in ('glob', 're', 'relglob', 'relre'):
585 anypats = True
584 anypats = True
586
585
587 if kind == 'glob':
586 if kind == 'glob':
588 root = globprefix(name)
587 root = globprefix(name)
589 roots.append(root)
588 roots.append(root)
590 elif kind in ('relpath', 'path'):
589 elif kind in ('relpath', 'path'):
591 roots.append(name or '.')
590 roots.append(name or '.')
592 elif kind == 'relglob':
591 elif kind == 'relglob':
593 roots.append('.')
592 roots.append('.')
594 return roots, pats, anypats
593 return roots, pats, anypats
595
594
596 roots, pats, anypats = normalizepats(names, dflt_pat)
595 roots, pats, anypats = normalizepats(names, dflt_pat)
597
596
598 patmatch = matchfn(pats, '$') or always
597 patmatch = matchfn(pats, '$') or always
599 incmatch = always
598 incmatch = always
600 if inc:
599 if inc:
601 dummy, inckinds, dummy = normalizepats(inc, 'glob')
600 dummy, inckinds, dummy = normalizepats(inc, 'glob')
602 incmatch = matchfn(inckinds, '(?:/|$)')
601 incmatch = matchfn(inckinds, '(?:/|$)')
603 excmatch = never
602 excmatch = never
604 if exc:
603 if exc:
605 dummy, exckinds, dummy = normalizepats(exc, 'glob')
604 dummy, exckinds, dummy = normalizepats(exc, 'glob')
606 excmatch = matchfn(exckinds, '(?:/|$)')
605 excmatch = matchfn(exckinds, '(?:/|$)')
607
606
608 if not names and inc and not exc:
607 if not names and inc and not exc:
609 # common case: hgignore patterns
608 # common case: hgignore patterns
610 match = incmatch
609 match = incmatch
611 else:
610 else:
612 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
611 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
613
612
614 return (roots, match, (inc or exc or anypats) and True)
613 return (roots, match, (inc or exc or anypats) and True)
615
614
616 _hgexecutable = None
615 _hgexecutable = None
617
616
618 def main_is_frozen():
617 def main_is_frozen():
619 """return True if we are a frozen executable.
618 """return True if we are a frozen executable.
620
619
621 The code supports py2exe (most common, Windows only) and tools/freeze
620 The code supports py2exe (most common, Windows only) and tools/freeze
622 (portable, not much used).
621 (portable, not much used).
623 """
622 """
624 return (hasattr(sys, "frozen") or # new py2exe
623 return (hasattr(sys, "frozen") or # new py2exe
625 hasattr(sys, "importers") or # old py2exe
624 hasattr(sys, "importers") or # old py2exe
626 imp.is_frozen("__main__")) # tools/freeze
625 imp.is_frozen("__main__")) # tools/freeze
627
626
628 def hgexecutable():
627 def hgexecutable():
629 """return location of the 'hg' executable.
628 """return location of the 'hg' executable.
630
629
631 Defaults to $HG or 'hg' in the search path.
630 Defaults to $HG or 'hg' in the search path.
632 """
631 """
633 if _hgexecutable is None:
632 if _hgexecutable is None:
634 hg = os.environ.get('HG')
633 hg = os.environ.get('HG')
635 if hg:
634 if hg:
636 set_hgexecutable(hg)
635 set_hgexecutable(hg)
637 elif main_is_frozen():
636 elif main_is_frozen():
638 set_hgexecutable(sys.executable)
637 set_hgexecutable(sys.executable)
639 else:
638 else:
640 set_hgexecutable(find_exe('hg') or 'hg')
639 set_hgexecutable(find_exe('hg') or 'hg')
641 return _hgexecutable
640 return _hgexecutable
642
641
643 def set_hgexecutable(path):
642 def set_hgexecutable(path):
644 """set location of the 'hg' executable"""
643 """set location of the 'hg' executable"""
645 global _hgexecutable
644 global _hgexecutable
646 _hgexecutable = path
645 _hgexecutable = path
647
646
648 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
647 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
649 '''enhanced shell command execution.
648 '''enhanced shell command execution.
650 run with environment maybe modified, maybe in different dir.
649 run with environment maybe modified, maybe in different dir.
651
650
652 if command fails and onerr is None, return status. if ui object,
651 if command fails and onerr is None, return status. if ui object,
653 print error message and return status, else raise onerr object as
652 print error message and return status, else raise onerr object as
654 exception.'''
653 exception.'''
655 def py2shell(val):
654 def py2shell(val):
656 'convert python object into string that is useful to shell'
655 'convert python object into string that is useful to shell'
657 if val in (None, False):
656 if val in (None, False):
658 return '0'
657 return '0'
659 if val == True:
658 if val == True:
660 return '1'
659 return '1'
661 return str(val)
660 return str(val)
662 oldenv = {}
661 oldenv = {}
663 for k in environ:
662 for k in environ:
664 oldenv[k] = os.environ.get(k)
663 oldenv[k] = os.environ.get(k)
665 if cwd is not None:
664 if cwd is not None:
666 oldcwd = os.getcwd()
665 oldcwd = os.getcwd()
667 origcmd = cmd
666 origcmd = cmd
668 if os.name == 'nt':
667 if os.name == 'nt':
669 cmd = '"%s"' % cmd
668 cmd = '"%s"' % cmd
670 try:
669 try:
671 for k, v in environ.iteritems():
670 for k, v in environ.iteritems():
672 os.environ[k] = py2shell(v)
671 os.environ[k] = py2shell(v)
673 os.environ['HG'] = hgexecutable()
672 os.environ['HG'] = hgexecutable()
674 if cwd is not None and oldcwd != cwd:
673 if cwd is not None and oldcwd != cwd:
675 os.chdir(cwd)
674 os.chdir(cwd)
676 rc = os.system(cmd)
675 rc = os.system(cmd)
677 if sys.platform == 'OpenVMS' and rc & 1:
676 if sys.platform == 'OpenVMS' and rc & 1:
678 rc = 0
677 rc = 0
679 if rc and onerr:
678 if rc and onerr:
680 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
679 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
681 explain_exit(rc)[0])
680 explain_exit(rc)[0])
682 if errprefix:
681 if errprefix:
683 errmsg = '%s: %s' % (errprefix, errmsg)
682 errmsg = '%s: %s' % (errprefix, errmsg)
684 try:
683 try:
685 onerr.warn(errmsg + '\n')
684 onerr.warn(errmsg + '\n')
686 except AttributeError:
685 except AttributeError:
687 raise onerr(errmsg)
686 raise onerr(errmsg)
688 return rc
687 return rc
689 finally:
688 finally:
690 for k, v in oldenv.iteritems():
689 for k, v in oldenv.iteritems():
691 if v is None:
690 if v is None:
692 del os.environ[k]
691 del os.environ[k]
693 else:
692 else:
694 os.environ[k] = v
693 os.environ[k] = v
695 if cwd is not None and oldcwd != cwd:
694 if cwd is not None and oldcwd != cwd:
696 os.chdir(oldcwd)
695 os.chdir(oldcwd)
697
696
698 def checksignature(func):
697 def checksignature(func):
699 '''wrap a function with code to check for calling errors'''
698 '''wrap a function with code to check for calling errors'''
700 def check(*args, **kwargs):
699 def check(*args, **kwargs):
701 try:
700 try:
702 return func(*args, **kwargs)
701 return func(*args, **kwargs)
703 except TypeError:
702 except TypeError:
704 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
703 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
705 raise error.SignatureError
704 raise error.SignatureError
706 raise
705 raise
707
706
708 return check
707 return check
709
708
710 # os.path.lexists is not available on python2.3
709 # os.path.lexists is not available on python2.3
711 def lexists(filename):
710 def lexists(filename):
712 "test whether a file with this name exists. does not follow symlinks"
711 "test whether a file with this name exists. does not follow symlinks"
713 try:
712 try:
714 os.lstat(filename)
713 os.lstat(filename)
715 except:
714 except:
716 return False
715 return False
717 return True
716 return True
718
717
719 def rename(src, dst):
718 def rename(src, dst):
720 """forcibly rename a file"""
719 """forcibly rename a file"""
721 try:
720 try:
722 os.rename(src, dst)
721 os.rename(src, dst)
723 except OSError, err: # FIXME: check err (EEXIST ?)
722 except OSError, err: # FIXME: check err (EEXIST ?)
724 # on windows, rename to existing file is not allowed, so we
723 # on windows, rename to existing file is not allowed, so we
725 # must delete destination first. but if file is open, unlink
724 # must delete destination first. but if file is open, unlink
726 # schedules it for delete but does not delete it. rename
725 # schedules it for delete but does not delete it. rename
727 # happens immediately even for open files, so we rename
726 # happens immediately even for open files, so we rename
728 # destination to a temporary name, then delete that. then
727 # destination to a temporary name, then delete that. then
729 # rename is safe to do.
728 # rename is safe to do.
730 temp = dst + "-force-rename"
729 temp = dst + "-force-rename"
731 os.rename(dst, temp)
730 os.rename(dst, temp)
732 os.unlink(temp)
731 os.unlink(temp)
733 os.rename(src, dst)
732 os.rename(src, dst)
734
733
735 def unlink(f):
734 def unlink(f):
736 """unlink and remove the directory if it is empty"""
735 """unlink and remove the directory if it is empty"""
737 os.unlink(f)
736 os.unlink(f)
738 # try removing directories that might now be empty
737 # try removing directories that might now be empty
739 try:
738 try:
740 os.removedirs(os.path.dirname(f))
739 os.removedirs(os.path.dirname(f))
741 except OSError:
740 except OSError:
742 pass
741 pass
743
742
744 def copyfile(src, dest):
743 def copyfile(src, dest):
745 "copy a file, preserving mode and atime/mtime"
744 "copy a file, preserving mode and atime/mtime"
746 if os.path.islink(src):
745 if os.path.islink(src):
747 try:
746 try:
748 os.unlink(dest)
747 os.unlink(dest)
749 except:
748 except:
750 pass
749 pass
751 os.symlink(os.readlink(src), dest)
750 os.symlink(os.readlink(src), dest)
752 else:
751 else:
753 try:
752 try:
754 shutil.copyfile(src, dest)
753 shutil.copyfile(src, dest)
755 shutil.copystat(src, dest)
754 shutil.copystat(src, dest)
756 except shutil.Error, inst:
755 except shutil.Error, inst:
757 raise Abort(str(inst))
756 raise Abort(str(inst))
758
757
759 def copyfiles(src, dst, hardlink=None):
758 def copyfiles(src, dst, hardlink=None):
760 """Copy a directory tree using hardlinks if possible"""
759 """Copy a directory tree using hardlinks if possible"""
761
760
762 if hardlink is None:
761 if hardlink is None:
763 hardlink = (os.stat(src).st_dev ==
762 hardlink = (os.stat(src).st_dev ==
764 os.stat(os.path.dirname(dst)).st_dev)
763 os.stat(os.path.dirname(dst)).st_dev)
765
764
766 if os.path.isdir(src):
765 if os.path.isdir(src):
767 os.mkdir(dst)
766 os.mkdir(dst)
768 for name, kind in osutil.listdir(src):
767 for name, kind in osutil.listdir(src):
769 srcname = os.path.join(src, name)
768 srcname = os.path.join(src, name)
770 dstname = os.path.join(dst, name)
769 dstname = os.path.join(dst, name)
771 copyfiles(srcname, dstname, hardlink)
770 copyfiles(srcname, dstname, hardlink)
772 else:
771 else:
773 if hardlink:
772 if hardlink:
774 try:
773 try:
775 os_link(src, dst)
774 os_link(src, dst)
776 except (IOError, OSError):
775 except (IOError, OSError):
777 hardlink = False
776 hardlink = False
778 shutil.copy(src, dst)
777 shutil.copy(src, dst)
779 else:
778 else:
780 shutil.copy(src, dst)
779 shutil.copy(src, dst)
781
780
782 class path_auditor(object):
781 class path_auditor(object):
783 '''ensure that a filesystem path contains no banned components.
782 '''ensure that a filesystem path contains no banned components.
784 the following properties of a path are checked:
783 the following properties of a path are checked:
785
784
786 - under top-level .hg
785 - under top-level .hg
787 - starts at the root of a windows drive
786 - starts at the root of a windows drive
788 - contains ".."
787 - contains ".."
789 - traverses a symlink (e.g. a/symlink_here/b)
788 - traverses a symlink (e.g. a/symlink_here/b)
790 - inside a nested repository'''
789 - inside a nested repository'''
791
790
792 def __init__(self, root):
791 def __init__(self, root):
793 self.audited = set()
792 self.audited = set()
794 self.auditeddir = set()
793 self.auditeddir = set()
795 self.root = root
794 self.root = root
796
795
797 def __call__(self, path):
796 def __call__(self, path):
798 if path in self.audited:
797 if path in self.audited:
799 return
798 return
800 normpath = os.path.normcase(path)
799 normpath = os.path.normcase(path)
801 parts = splitpath(normpath)
800 parts = splitpath(normpath)
802 if (os.path.splitdrive(path)[0]
801 if (os.path.splitdrive(path)[0]
803 or parts[0].lower() in ('.hg', '.hg.', '')
802 or parts[0].lower() in ('.hg', '.hg.', '')
804 or os.pardir in parts):
803 or os.pardir in parts):
805 raise Abort(_("path contains illegal component: %s") % path)
804 raise Abort(_("path contains illegal component: %s") % path)
806 if '.hg' in path.lower():
805 if '.hg' in path.lower():
807 lparts = [p.lower() for p in parts]
806 lparts = [p.lower() for p in parts]
808 for p in '.hg', '.hg.':
807 for p in '.hg', '.hg.':
809 if p in lparts[1:]:
808 if p in lparts[1:]:
810 pos = lparts.index(p)
809 pos = lparts.index(p)
811 base = os.path.join(*parts[:pos])
810 base = os.path.join(*parts[:pos])
812 raise Abort(_('path %r is inside repo %r') % (path, base))
811 raise Abort(_('path %r is inside repo %r') % (path, base))
813 def check(prefix):
812 def check(prefix):
814 curpath = os.path.join(self.root, prefix)
813 curpath = os.path.join(self.root, prefix)
815 try:
814 try:
816 st = os.lstat(curpath)
815 st = os.lstat(curpath)
817 except OSError, err:
816 except OSError, err:
818 # EINVAL can be raised as invalid path syntax under win32.
817 # EINVAL can be raised as invalid path syntax under win32.
819 # They must be ignored for patterns can be checked too.
818 # They must be ignored for patterns can be checked too.
820 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
819 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
821 raise
820 raise
822 else:
821 else:
823 if stat.S_ISLNK(st.st_mode):
822 if stat.S_ISLNK(st.st_mode):
824 raise Abort(_('path %r traverses symbolic link %r') %
823 raise Abort(_('path %r traverses symbolic link %r') %
825 (path, prefix))
824 (path, prefix))
826 elif (stat.S_ISDIR(st.st_mode) and
825 elif (stat.S_ISDIR(st.st_mode) and
827 os.path.isdir(os.path.join(curpath, '.hg'))):
826 os.path.isdir(os.path.join(curpath, '.hg'))):
828 raise Abort(_('path %r is inside repo %r') %
827 raise Abort(_('path %r is inside repo %r') %
829 (path, prefix))
828 (path, prefix))
830 parts.pop()
829 parts.pop()
831 prefixes = []
830 prefixes = []
832 for n in range(len(parts)):
831 for n in range(len(parts)):
833 prefix = os.sep.join(parts)
832 prefix = os.sep.join(parts)
834 if prefix in self.auditeddir:
833 if prefix in self.auditeddir:
835 break
834 break
836 check(prefix)
835 check(prefix)
837 prefixes.append(prefix)
836 prefixes.append(prefix)
838 parts.pop()
837 parts.pop()
839
838
840 self.audited.add(path)
839 self.audited.add(path)
841 # only add prefixes to the cache after checking everything: we don't
840 # only add prefixes to the cache after checking everything: we don't
842 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
841 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
843 self.auditeddir.update(prefixes)
842 self.auditeddir.update(prefixes)
844
843
845 if os.name == 'nt':
844 if os.name == 'nt':
846 from windows import *
845 from windows import *
847 def expand_glob(pats):
846 def expand_glob(pats):
848 '''On Windows, expand the implicit globs in a list of patterns'''
847 '''On Windows, expand the implicit globs in a list of patterns'''
849 ret = []
848 ret = []
850 for p in pats:
849 for p in pats:
851 kind, name = patkind(p, None)
850 kind, name = patkind(p, None)
852 if kind is None:
851 if kind is None:
853 globbed = glob.glob(name)
852 globbed = glob.glob(name)
854 if globbed:
853 if globbed:
855 ret.extend(globbed)
854 ret.extend(globbed)
856 continue
855 continue
857 # if we couldn't expand the glob, just keep it around
856 # if we couldn't expand the glob, just keep it around
858 ret.append(p)
857 ret.append(p)
859 return ret
858 return ret
860 else:
859 else:
861 from posix import *
860 from posix import *
862
861
863 def makelock(info, pathname):
862 def makelock(info, pathname):
864 try:
863 try:
865 return os.symlink(info, pathname)
864 return os.symlink(info, pathname)
866 except OSError, why:
865 except OSError, why:
867 if why.errno == errno.EEXIST:
866 if why.errno == errno.EEXIST:
868 raise
867 raise
869 except AttributeError: # no symlink in os
868 except AttributeError: # no symlink in os
870 pass
869 pass
871
870
872 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
871 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
873 os.write(ld, info)
872 os.write(ld, info)
874 os.close(ld)
873 os.close(ld)
875
874
876 def readlock(pathname):
875 def readlock(pathname):
877 try:
876 try:
878 return os.readlink(pathname)
877 return os.readlink(pathname)
879 except OSError, why:
878 except OSError, why:
880 if why.errno not in (errno.EINVAL, errno.ENOSYS):
879 if why.errno not in (errno.EINVAL, errno.ENOSYS):
881 raise
880 raise
882 except AttributeError: # no symlink in os
881 except AttributeError: # no symlink in os
883 pass
882 pass
884 return posixfile(pathname).read()
883 return posixfile(pathname).read()
885
884
886 def nlinks(pathname):
885 def nlinks(pathname):
887 """Return number of hardlinks for the given file."""
886 """Return number of hardlinks for the given file."""
888 return os.lstat(pathname).st_nlink
887 return os.lstat(pathname).st_nlink
889
888
890 if hasattr(os, 'link'):
889 if hasattr(os, 'link'):
891 os_link = os.link
890 os_link = os.link
892 else:
891 else:
893 def os_link(src, dst):
892 def os_link(src, dst):
894 raise OSError(0, _("Hardlinks not supported"))
893 raise OSError(0, _("Hardlinks not supported"))
895
894
896 def fstat(fp):
895 def fstat(fp):
897 '''stat file object that may not have fileno method.'''
896 '''stat file object that may not have fileno method.'''
898 try:
897 try:
899 return os.fstat(fp.fileno())
898 return os.fstat(fp.fileno())
900 except AttributeError:
899 except AttributeError:
901 return os.stat(fp.name)
900 return os.stat(fp.name)
902
901
903 # File system features
902 # File system features
904
903
905 def checkcase(path):
904 def checkcase(path):
906 """
905 """
907 Check whether the given path is on a case-sensitive filesystem
906 Check whether the given path is on a case-sensitive filesystem
908
907
909 Requires a path (like /foo/.hg) ending with a foldable final
908 Requires a path (like /foo/.hg) ending with a foldable final
910 directory component.
909 directory component.
911 """
910 """
912 s1 = os.stat(path)
911 s1 = os.stat(path)
913 d, b = os.path.split(path)
912 d, b = os.path.split(path)
914 p2 = os.path.join(d, b.upper())
913 p2 = os.path.join(d, b.upper())
915 if path == p2:
914 if path == p2:
916 p2 = os.path.join(d, b.lower())
915 p2 = os.path.join(d, b.lower())
917 try:
916 try:
918 s2 = os.stat(p2)
917 s2 = os.stat(p2)
919 if s2 == s1:
918 if s2 == s1:
920 return False
919 return False
921 return True
920 return True
922 except:
921 except:
923 return True
922 return True
924
923
925 _fspathcache = {}
924 _fspathcache = {}
926 def fspath(name, root):
925 def fspath(name, root):
927 '''Get name in the case stored in the filesystem
926 '''Get name in the case stored in the filesystem
928
927
929 The name is either relative to root, or it is an absolute path starting
928 The name is either relative to root, or it is an absolute path starting
930 with root. Note that this function is unnecessary, and should not be
929 with root. Note that this function is unnecessary, and should not be
931 called, for case-sensitive filesystems (simply because it's expensive).
930 called, for case-sensitive filesystems (simply because it's expensive).
932 '''
931 '''
933 # If name is absolute, make it relative
932 # If name is absolute, make it relative
934 if name.lower().startswith(root.lower()):
933 if name.lower().startswith(root.lower()):
935 l = len(root)
934 l = len(root)
936 if name[l] == os.sep or name[l] == os.altsep:
935 if name[l] == os.sep or name[l] == os.altsep:
937 l = l + 1
936 l = l + 1
938 name = name[l:]
937 name = name[l:]
939
938
940 if not os.path.exists(os.path.join(root, name)):
939 if not os.path.exists(os.path.join(root, name)):
941 return None
940 return None
942
941
943 seps = os.sep
942 seps = os.sep
944 if os.altsep:
943 if os.altsep:
945 seps = seps + os.altsep
944 seps = seps + os.altsep
946 # Protect backslashes. This gets silly very quickly.
945 # Protect backslashes. This gets silly very quickly.
947 seps.replace('\\','\\\\')
946 seps.replace('\\','\\\\')
948 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
947 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
949 dir = os.path.normcase(os.path.normpath(root))
948 dir = os.path.normcase(os.path.normpath(root))
950 result = []
949 result = []
951 for part, sep in pattern.findall(name):
950 for part, sep in pattern.findall(name):
952 if sep:
951 if sep:
953 result.append(sep)
952 result.append(sep)
954 continue
953 continue
955
954
956 if dir not in _fspathcache:
955 if dir not in _fspathcache:
957 _fspathcache[dir] = os.listdir(dir)
956 _fspathcache[dir] = os.listdir(dir)
958 contents = _fspathcache[dir]
957 contents = _fspathcache[dir]
959
958
960 lpart = part.lower()
959 lpart = part.lower()
961 for n in contents:
960 for n in contents:
962 if n.lower() == lpart:
961 if n.lower() == lpart:
963 result.append(n)
962 result.append(n)
964 break
963 break
965 else:
964 else:
966 # Cannot happen, as the file exists!
965 # Cannot happen, as the file exists!
967 result.append(part)
966 result.append(part)
968 dir = os.path.join(dir, lpart)
967 dir = os.path.join(dir, lpart)
969
968
970 return ''.join(result)
969 return ''.join(result)
971
970
972 def checkexec(path):
971 def checkexec(path):
973 """
972 """
974 Check whether the given path is on a filesystem with UNIX-like exec flags
973 Check whether the given path is on a filesystem with UNIX-like exec flags
975
974
976 Requires a directory (like /foo/.hg)
975 Requires a directory (like /foo/.hg)
977 """
976 """
978
977
979 # VFAT on some Linux versions can flip mode but it doesn't persist
978 # VFAT on some Linux versions can flip mode but it doesn't persist
980 # a FS remount. Frequently we can detect it if files are created
979 # a FS remount. Frequently we can detect it if files are created
981 # with exec bit on.
980 # with exec bit on.
982
981
983 try:
982 try:
984 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
983 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
985 fh, fn = tempfile.mkstemp("", "", path)
984 fh, fn = tempfile.mkstemp("", "", path)
986 try:
985 try:
987 os.close(fh)
986 os.close(fh)
988 m = os.stat(fn).st_mode & 0777
987 m = os.stat(fn).st_mode & 0777
989 new_file_has_exec = m & EXECFLAGS
988 new_file_has_exec = m & EXECFLAGS
990 os.chmod(fn, m ^ EXECFLAGS)
989 os.chmod(fn, m ^ EXECFLAGS)
991 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
990 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
992 finally:
991 finally:
993 os.unlink(fn)
992 os.unlink(fn)
994 except (IOError, OSError):
993 except (IOError, OSError):
995 # we don't care, the user probably won't be able to commit anyway
994 # we don't care, the user probably won't be able to commit anyway
996 return False
995 return False
997 return not (new_file_has_exec or exec_flags_cannot_flip)
996 return not (new_file_has_exec or exec_flags_cannot_flip)
998
997
999 def checklink(path):
998 def checklink(path):
1000 """check whether the given path is on a symlink-capable filesystem"""
999 """check whether the given path is on a symlink-capable filesystem"""
1001 # mktemp is not racy because symlink creation will fail if the
1000 # mktemp is not racy because symlink creation will fail if the
1002 # file already exists
1001 # file already exists
1003 name = tempfile.mktemp(dir=path)
1002 name = tempfile.mktemp(dir=path)
1004 try:
1003 try:
1005 os.symlink(".", name)
1004 os.symlink(".", name)
1006 os.unlink(name)
1005 os.unlink(name)
1007 return True
1006 return True
1008 except (OSError, AttributeError):
1007 except (OSError, AttributeError):
1009 return False
1008 return False
1010
1009
1011 def needbinarypatch():
1010 def needbinarypatch():
1012 """return True if patches should be applied in binary mode by default."""
1011 """return True if patches should be applied in binary mode by default."""
1013 return os.name == 'nt'
1012 return os.name == 'nt'
1014
1013
1015 def endswithsep(path):
1014 def endswithsep(path):
1016 '''Check path ends with os.sep or os.altsep.'''
1015 '''Check path ends with os.sep or os.altsep.'''
1017 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1016 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1018
1017
1019 def splitpath(path):
1018 def splitpath(path):
1020 '''Split path by os.sep.
1019 '''Split path by os.sep.
1021 Note that this function does not use os.altsep because this is
1020 Note that this function does not use os.altsep because this is
1022 an alternative of simple "xxx.split(os.sep)".
1021 an alternative of simple "xxx.split(os.sep)".
1023 It is recommended to use os.path.normpath() before using this
1022 It is recommended to use os.path.normpath() before using this
1024 function if need.'''
1023 function if need.'''
1025 return path.split(os.sep)
1024 return path.split(os.sep)
1026
1025
1027 def gui():
1026 def gui():
1028 '''Are we running in a GUI?'''
1027 '''Are we running in a GUI?'''
1029 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1028 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1030
1029
1031 def lookup_reg(key, name=None, scope=None):
1030 def lookup_reg(key, name=None, scope=None):
1032 return None
1031 return None
1033
1032
1034 def mktempcopy(name, emptyok=False, createmode=None):
1033 def mktempcopy(name, emptyok=False, createmode=None):
1035 """Create a temporary file with the same contents from name
1034 """Create a temporary file with the same contents from name
1036
1035
1037 The permission bits are copied from the original file.
1036 The permission bits are copied from the original file.
1038
1037
1039 If the temporary file is going to be truncated immediately, you
1038 If the temporary file is going to be truncated immediately, you
1040 can use emptyok=True as an optimization.
1039 can use emptyok=True as an optimization.
1041
1040
1042 Returns the name of the temporary file.
1041 Returns the name of the temporary file.
1043 """
1042 """
1044 d, fn = os.path.split(name)
1043 d, fn = os.path.split(name)
1045 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1044 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1046 os.close(fd)
1045 os.close(fd)
1047 # Temporary files are created with mode 0600, which is usually not
1046 # Temporary files are created with mode 0600, which is usually not
1048 # what we want. If the original file already exists, just copy
1047 # what we want. If the original file already exists, just copy
1049 # its mode. Otherwise, manually obey umask.
1048 # its mode. Otherwise, manually obey umask.
1050 try:
1049 try:
1051 st_mode = os.lstat(name).st_mode & 0777
1050 st_mode = os.lstat(name).st_mode & 0777
1052 except OSError, inst:
1051 except OSError, inst:
1053 if inst.errno != errno.ENOENT:
1052 if inst.errno != errno.ENOENT:
1054 raise
1053 raise
1055 st_mode = createmode
1054 st_mode = createmode
1056 if st_mode is None:
1055 if st_mode is None:
1057 st_mode = ~umask
1056 st_mode = ~umask
1058 st_mode &= 0666
1057 st_mode &= 0666
1059 os.chmod(temp, st_mode)
1058 os.chmod(temp, st_mode)
1060 if emptyok:
1059 if emptyok:
1061 return temp
1060 return temp
1062 try:
1061 try:
1063 try:
1062 try:
1064 ifp = posixfile(name, "rb")
1063 ifp = posixfile(name, "rb")
1065 except IOError, inst:
1064 except IOError, inst:
1066 if inst.errno == errno.ENOENT:
1065 if inst.errno == errno.ENOENT:
1067 return temp
1066 return temp
1068 if not getattr(inst, 'filename', None):
1067 if not getattr(inst, 'filename', None):
1069 inst.filename = name
1068 inst.filename = name
1070 raise
1069 raise
1071 ofp = posixfile(temp, "wb")
1070 ofp = posixfile(temp, "wb")
1072 for chunk in filechunkiter(ifp):
1071 for chunk in filechunkiter(ifp):
1073 ofp.write(chunk)
1072 ofp.write(chunk)
1074 ifp.close()
1073 ifp.close()
1075 ofp.close()
1074 ofp.close()
1076 except:
1075 except:
1077 try: os.unlink(temp)
1076 try: os.unlink(temp)
1078 except: pass
1077 except: pass
1079 raise
1078 raise
1080 return temp
1079 return temp
1081
1080
1082 class atomictempfile(posixfile):
1081 class atomictempfile(posixfile):
1083 """file-like object that atomically updates a file
1082 """file-like object that atomically updates a file
1084
1083
1085 All writes will be redirected to a temporary copy of the original
1084 All writes will be redirected to a temporary copy of the original
1086 file. When rename is called, the copy is renamed to the original
1085 file. When rename is called, the copy is renamed to the original
1087 name, making the changes visible.
1086 name, making the changes visible.
1088 """
1087 """
1089 def __init__(self, name, mode, createmode):
1088 def __init__(self, name, mode, createmode):
1090 self.__name = name
1089 self.__name = name
1091 self.temp = mktempcopy(name, emptyok=('w' in mode),
1090 self.temp = mktempcopy(name, emptyok=('w' in mode),
1092 createmode=createmode)
1091 createmode=createmode)
1093 posixfile.__init__(self, self.temp, mode)
1092 posixfile.__init__(self, self.temp, mode)
1094
1093
1095 def rename(self):
1094 def rename(self):
1096 if not self.closed:
1095 if not self.closed:
1097 posixfile.close(self)
1096 posixfile.close(self)
1098 rename(self.temp, localpath(self.__name))
1097 rename(self.temp, localpath(self.__name))
1099
1098
1100 def __del__(self):
1099 def __del__(self):
1101 if not self.closed:
1100 if not self.closed:
1102 try:
1101 try:
1103 os.unlink(self.temp)
1102 os.unlink(self.temp)
1104 except: pass
1103 except: pass
1105 posixfile.close(self)
1104 posixfile.close(self)
1106
1105
1107 def makedirs(name, mode=None):
1106 def makedirs(name, mode=None):
1108 """recursive directory creation with parent mode inheritance"""
1107 """recursive directory creation with parent mode inheritance"""
1109 try:
1108 try:
1110 os.mkdir(name)
1109 os.mkdir(name)
1111 if mode is not None:
1110 if mode is not None:
1112 os.chmod(name, mode)
1111 os.chmod(name, mode)
1113 return
1112 return
1114 except OSError, err:
1113 except OSError, err:
1115 if err.errno == errno.EEXIST:
1114 if err.errno == errno.EEXIST:
1116 return
1115 return
1117 if err.errno != errno.ENOENT:
1116 if err.errno != errno.ENOENT:
1118 raise
1117 raise
1119 parent = os.path.abspath(os.path.dirname(name))
1118 parent = os.path.abspath(os.path.dirname(name))
1120 makedirs(parent, mode)
1119 makedirs(parent, mode)
1121 makedirs(name, mode)
1120 makedirs(name, mode)
1122
1121
1123 class opener(object):
1122 class opener(object):
1124 """Open files relative to a base directory
1123 """Open files relative to a base directory
1125
1124
1126 This class is used to hide the details of COW semantics and
1125 This class is used to hide the details of COW semantics and
1127 remote file access from higher level code.
1126 remote file access from higher level code.
1128 """
1127 """
1129 def __init__(self, base, audit=True):
1128 def __init__(self, base, audit=True):
1130 self.base = base
1129 self.base = base
1131 if audit:
1130 if audit:
1132 self.audit_path = path_auditor(base)
1131 self.audit_path = path_auditor(base)
1133 else:
1132 else:
1134 self.audit_path = always
1133 self.audit_path = always
1135 self.createmode = None
1134 self.createmode = None
1136
1135
1137 def __getattr__(self, name):
1136 def __getattr__(self, name):
1138 if name == '_can_symlink':
1137 if name == '_can_symlink':
1139 self._can_symlink = checklink(self.base)
1138 self._can_symlink = checklink(self.base)
1140 return self._can_symlink
1139 return self._can_symlink
1141 raise AttributeError(name)
1140 raise AttributeError(name)
1142
1141
1143 def _fixfilemode(self, name):
1142 def _fixfilemode(self, name):
1144 if self.createmode is None:
1143 if self.createmode is None:
1145 return
1144 return
1146 os.chmod(name, self.createmode & 0666)
1145 os.chmod(name, self.createmode & 0666)
1147
1146
1148 def __call__(self, path, mode="r", text=False, atomictemp=False):
1147 def __call__(self, path, mode="r", text=False, atomictemp=False):
1149 self.audit_path(path)
1148 self.audit_path(path)
1150 f = os.path.join(self.base, path)
1149 f = os.path.join(self.base, path)
1151
1150
1152 if not text and "b" not in mode:
1151 if not text and "b" not in mode:
1153 mode += "b" # for that other OS
1152 mode += "b" # for that other OS
1154
1153
1155 nlink = -1
1154 nlink = -1
1156 if mode not in ("r", "rb"):
1155 if mode not in ("r", "rb"):
1157 try:
1156 try:
1158 nlink = nlinks(f)
1157 nlink = nlinks(f)
1159 except OSError:
1158 except OSError:
1160 nlink = 0
1159 nlink = 0
1161 d = os.path.dirname(f)
1160 d = os.path.dirname(f)
1162 if not os.path.isdir(d):
1161 if not os.path.isdir(d):
1163 makedirs(d, self.createmode)
1162 makedirs(d, self.createmode)
1164 if atomictemp:
1163 if atomictemp:
1165 return atomictempfile(f, mode, self.createmode)
1164 return atomictempfile(f, mode, self.createmode)
1166 if nlink > 1:
1165 if nlink > 1:
1167 rename(mktempcopy(f), f)
1166 rename(mktempcopy(f), f)
1168 fp = posixfile(f, mode)
1167 fp = posixfile(f, mode)
1169 if nlink == 0:
1168 if nlink == 0:
1170 self._fixfilemode(f)
1169 self._fixfilemode(f)
1171 return fp
1170 return fp
1172
1171
1173 def symlink(self, src, dst):
1172 def symlink(self, src, dst):
1174 self.audit_path(dst)
1173 self.audit_path(dst)
1175 linkname = os.path.join(self.base, dst)
1174 linkname = os.path.join(self.base, dst)
1176 try:
1175 try:
1177 os.unlink(linkname)
1176 os.unlink(linkname)
1178 except OSError:
1177 except OSError:
1179 pass
1178 pass
1180
1179
1181 dirname = os.path.dirname(linkname)
1180 dirname = os.path.dirname(linkname)
1182 if not os.path.exists(dirname):
1181 if not os.path.exists(dirname):
1183 makedirs(dirname, self.createmode)
1182 makedirs(dirname, self.createmode)
1184
1183
1185 if self._can_symlink:
1184 if self._can_symlink:
1186 try:
1185 try:
1187 os.symlink(src, linkname)
1186 os.symlink(src, linkname)
1188 except OSError, err:
1187 except OSError, err:
1189 raise OSError(err.errno, _('could not symlink to %r: %s') %
1188 raise OSError(err.errno, _('could not symlink to %r: %s') %
1190 (src, err.strerror), linkname)
1189 (src, err.strerror), linkname)
1191 else:
1190 else:
1192 f = self(dst, "w")
1191 f = self(dst, "w")
1193 f.write(src)
1192 f.write(src)
1194 f.close()
1193 f.close()
1195 self._fixfilemode(dst)
1194 self._fixfilemode(dst)
1196
1195
1197 class chunkbuffer(object):
1196 class chunkbuffer(object):
1198 """Allow arbitrary sized chunks of data to be efficiently read from an
1197 """Allow arbitrary sized chunks of data to be efficiently read from an
1199 iterator over chunks of arbitrary size."""
1198 iterator over chunks of arbitrary size."""
1200
1199
1201 def __init__(self, in_iter):
1200 def __init__(self, in_iter):
1202 """in_iter is the iterator that's iterating over the input chunks.
1201 """in_iter is the iterator that's iterating over the input chunks.
1203 targetsize is how big a buffer to try to maintain."""
1202 targetsize is how big a buffer to try to maintain."""
1204 self.iter = iter(in_iter)
1203 self.iter = iter(in_iter)
1205 self.buf = ''
1204 self.buf = ''
1206 self.targetsize = 2**16
1205 self.targetsize = 2**16
1207
1206
1208 def read(self, l):
1207 def read(self, l):
1209 """Read L bytes of data from the iterator of chunks of data.
1208 """Read L bytes of data from the iterator of chunks of data.
1210 Returns less than L bytes if the iterator runs dry."""
1209 Returns less than L bytes if the iterator runs dry."""
1211 if l > len(self.buf) and self.iter:
1210 if l > len(self.buf) and self.iter:
1212 # Clamp to a multiple of self.targetsize
1211 # Clamp to a multiple of self.targetsize
1213 targetsize = max(l, self.targetsize)
1212 targetsize = max(l, self.targetsize)
1214 collector = cStringIO.StringIO()
1213 collector = cStringIO.StringIO()
1215 collector.write(self.buf)
1214 collector.write(self.buf)
1216 collected = len(self.buf)
1215 collected = len(self.buf)
1217 for chunk in self.iter:
1216 for chunk in self.iter:
1218 collector.write(chunk)
1217 collector.write(chunk)
1219 collected += len(chunk)
1218 collected += len(chunk)
1220 if collected >= targetsize:
1219 if collected >= targetsize:
1221 break
1220 break
1222 if collected < targetsize:
1221 if collected < targetsize:
1223 self.iter = False
1222 self.iter = False
1224 self.buf = collector.getvalue()
1223 self.buf = collector.getvalue()
1225 if len(self.buf) == l:
1224 if len(self.buf) == l:
1226 s, self.buf = str(self.buf), ''
1225 s, self.buf = str(self.buf), ''
1227 else:
1226 else:
1228 s, self.buf = self.buf[:l], buffer(self.buf, l)
1227 s, self.buf = self.buf[:l], buffer(self.buf, l)
1229 return s
1228 return s
1230
1229
1231 def filechunkiter(f, size=65536, limit=None):
1230 def filechunkiter(f, size=65536, limit=None):
1232 """Create a generator that produces the data in the file size
1231 """Create a generator that produces the data in the file size
1233 (default 65536) bytes at a time, up to optional limit (default is
1232 (default 65536) bytes at a time, up to optional limit (default is
1234 to read all data). Chunks may be less than size bytes if the
1233 to read all data). Chunks may be less than size bytes if the
1235 chunk is the last chunk in the file, or the file is a socket or
1234 chunk is the last chunk in the file, or the file is a socket or
1236 some other type of file that sometimes reads less data than is
1235 some other type of file that sometimes reads less data than is
1237 requested."""
1236 requested."""
1238 assert size >= 0
1237 assert size >= 0
1239 assert limit is None or limit >= 0
1238 assert limit is None or limit >= 0
1240 while True:
1239 while True:
1241 if limit is None: nbytes = size
1240 if limit is None: nbytes = size
1242 else: nbytes = min(limit, size)
1241 else: nbytes = min(limit, size)
1243 s = nbytes and f.read(nbytes)
1242 s = nbytes and f.read(nbytes)
1244 if not s: break
1243 if not s: break
1245 if limit: limit -= len(s)
1244 if limit: limit -= len(s)
1246 yield s
1245 yield s
1247
1246
1248 def makedate():
1247 def makedate():
1249 lt = time.localtime()
1248 lt = time.localtime()
1250 if lt[8] == 1 and time.daylight:
1249 if lt[8] == 1 and time.daylight:
1251 tz = time.altzone
1250 tz = time.altzone
1252 else:
1251 else:
1253 tz = time.timezone
1252 tz = time.timezone
1254 return time.mktime(lt), tz
1253 return time.mktime(lt), tz
1255
1254
1256 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1255 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1257 """represent a (unixtime, offset) tuple as a localized time.
1256 """represent a (unixtime, offset) tuple as a localized time.
1258 unixtime is seconds since the epoch, and offset is the time zone's
1257 unixtime is seconds since the epoch, and offset is the time zone's
1259 number of seconds away from UTC. if timezone is false, do not
1258 number of seconds away from UTC. if timezone is false, do not
1260 append time zone to string."""
1259 append time zone to string."""
1261 t, tz = date or makedate()
1260 t, tz = date or makedate()
1262 if "%1" in format or "%2" in format:
1261 if "%1" in format or "%2" in format:
1263 sign = (tz > 0) and "-" or "+"
1262 sign = (tz > 0) and "-" or "+"
1264 minutes = abs(tz) / 60
1263 minutes = abs(tz) / 60
1265 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1264 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1266 format = format.replace("%2", "%02d" % (minutes % 60))
1265 format = format.replace("%2", "%02d" % (minutes % 60))
1267 s = time.strftime(format, time.gmtime(float(t) - tz))
1266 s = time.strftime(format, time.gmtime(float(t) - tz))
1268 return s
1267 return s
1269
1268
1270 def shortdate(date=None):
1269 def shortdate(date=None):
1271 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1270 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1272 return datestr(date, format='%Y-%m-%d')
1271 return datestr(date, format='%Y-%m-%d')
1273
1272
1274 def strdate(string, format, defaults=[]):
1273 def strdate(string, format, defaults=[]):
1275 """parse a localized time string and return a (unixtime, offset) tuple.
1274 """parse a localized time string and return a (unixtime, offset) tuple.
1276 if the string cannot be parsed, ValueError is raised."""
1275 if the string cannot be parsed, ValueError is raised."""
1277 def timezone(string):
1276 def timezone(string):
1278 tz = string.split()[-1]
1277 tz = string.split()[-1]
1279 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1278 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1280 sign = (tz[0] == "+") and 1 or -1
1279 sign = (tz[0] == "+") and 1 or -1
1281 hours = int(tz[1:3])
1280 hours = int(tz[1:3])
1282 minutes = int(tz[3:5])
1281 minutes = int(tz[3:5])
1283 return -sign * (hours * 60 + minutes) * 60
1282 return -sign * (hours * 60 + minutes) * 60
1284 if tz == "GMT" or tz == "UTC":
1283 if tz == "GMT" or tz == "UTC":
1285 return 0
1284 return 0
1286 return None
1285 return None
1287
1286
1288 # NOTE: unixtime = localunixtime + offset
1287 # NOTE: unixtime = localunixtime + offset
1289 offset, date = timezone(string), string
1288 offset, date = timezone(string), string
1290 if offset != None:
1289 if offset != None:
1291 date = " ".join(string.split()[:-1])
1290 date = " ".join(string.split()[:-1])
1292
1291
1293 # add missing elements from defaults
1292 # add missing elements from defaults
1294 for part in defaults:
1293 for part in defaults:
1295 found = [True for p in part if ("%"+p) in format]
1294 found = [True for p in part if ("%"+p) in format]
1296 if not found:
1295 if not found:
1297 date += "@" + defaults[part]
1296 date += "@" + defaults[part]
1298 format += "@%" + part[0]
1297 format += "@%" + part[0]
1299
1298
1300 timetuple = time.strptime(date, format)
1299 timetuple = time.strptime(date, format)
1301 localunixtime = int(calendar.timegm(timetuple))
1300 localunixtime = int(calendar.timegm(timetuple))
1302 if offset is None:
1301 if offset is None:
1303 # local timezone
1302 # local timezone
1304 unixtime = int(time.mktime(timetuple))
1303 unixtime = int(time.mktime(timetuple))
1305 offset = unixtime - localunixtime
1304 offset = unixtime - localunixtime
1306 else:
1305 else:
1307 unixtime = localunixtime + offset
1306 unixtime = localunixtime + offset
1308 return unixtime, offset
1307 return unixtime, offset
1309
1308
1310 def parsedate(date, formats=None, defaults=None):
1309 def parsedate(date, formats=None, defaults=None):
1311 """parse a localized date/time string and return a (unixtime, offset) tuple.
1310 """parse a localized date/time string and return a (unixtime, offset) tuple.
1312
1311
1313 The date may be a "unixtime offset" string or in one of the specified
1312 The date may be a "unixtime offset" string or in one of the specified
1314 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1313 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1315 """
1314 """
1316 if not date:
1315 if not date:
1317 return 0, 0
1316 return 0, 0
1318 if isinstance(date, tuple) and len(date) == 2:
1317 if isinstance(date, tuple) and len(date) == 2:
1319 return date
1318 return date
1320 if not formats:
1319 if not formats:
1321 formats = defaultdateformats
1320 formats = defaultdateformats
1322 date = date.strip()
1321 date = date.strip()
1323 try:
1322 try:
1324 when, offset = map(int, date.split(' '))
1323 when, offset = map(int, date.split(' '))
1325 except ValueError:
1324 except ValueError:
1326 # fill out defaults
1325 # fill out defaults
1327 if not defaults:
1326 if not defaults:
1328 defaults = {}
1327 defaults = {}
1329 now = makedate()
1328 now = makedate()
1330 for part in "d mb yY HI M S".split():
1329 for part in "d mb yY HI M S".split():
1331 if part not in defaults:
1330 if part not in defaults:
1332 if part[0] in "HMS":
1331 if part[0] in "HMS":
1333 defaults[part] = "00"
1332 defaults[part] = "00"
1334 else:
1333 else:
1335 defaults[part] = datestr(now, "%" + part[0])
1334 defaults[part] = datestr(now, "%" + part[0])
1336
1335
1337 for format in formats:
1336 for format in formats:
1338 try:
1337 try:
1339 when, offset = strdate(date, format, defaults)
1338 when, offset = strdate(date, format, defaults)
1340 except (ValueError, OverflowError):
1339 except (ValueError, OverflowError):
1341 pass
1340 pass
1342 else:
1341 else:
1343 break
1342 break
1344 else:
1343 else:
1345 raise Abort(_('invalid date: %r ') % date)
1344 raise Abort(_('invalid date: %r ') % date)
1346 # validate explicit (probably user-specified) date and
1345 # validate explicit (probably user-specified) date and
1347 # time zone offset. values must fit in signed 32 bits for
1346 # time zone offset. values must fit in signed 32 bits for
1348 # current 32-bit linux runtimes. timezones go from UTC-12
1347 # current 32-bit linux runtimes. timezones go from UTC-12
1349 # to UTC+14
1348 # to UTC+14
1350 if abs(when) > 0x7fffffff:
1349 if abs(when) > 0x7fffffff:
1351 raise Abort(_('date exceeds 32 bits: %d') % when)
1350 raise Abort(_('date exceeds 32 bits: %d') % when)
1352 if offset < -50400 or offset > 43200:
1351 if offset < -50400 or offset > 43200:
1353 raise Abort(_('impossible time zone offset: %d') % offset)
1352 raise Abort(_('impossible time zone offset: %d') % offset)
1354 return when, offset
1353 return when, offset
1355
1354
1356 def matchdate(date):
1355 def matchdate(date):
1357 """Return a function that matches a given date match specifier
1356 """Return a function that matches a given date match specifier
1358
1357
1359 Formats include:
1358 Formats include:
1360
1359
1361 '{date}' match a given date to the accuracy provided
1360 '{date}' match a given date to the accuracy provided
1362
1361
1363 '<{date}' on or before a given date
1362 '<{date}' on or before a given date
1364
1363
1365 '>{date}' on or after a given date
1364 '>{date}' on or after a given date
1366
1365
1367 """
1366 """
1368
1367
1369 def lower(date):
1368 def lower(date):
1370 d = dict(mb="1", d="1")
1369 d = dict(mb="1", d="1")
1371 return parsedate(date, extendeddateformats, d)[0]
1370 return parsedate(date, extendeddateformats, d)[0]
1372
1371
1373 def upper(date):
1372 def upper(date):
1374 d = dict(mb="12", HI="23", M="59", S="59")
1373 d = dict(mb="12", HI="23", M="59", S="59")
1375 for days in "31 30 29".split():
1374 for days in "31 30 29".split():
1376 try:
1375 try:
1377 d["d"] = days
1376 d["d"] = days
1378 return parsedate(date, extendeddateformats, d)[0]
1377 return parsedate(date, extendeddateformats, d)[0]
1379 except:
1378 except:
1380 pass
1379 pass
1381 d["d"] = "28"
1380 d["d"] = "28"
1382 return parsedate(date, extendeddateformats, d)[0]
1381 return parsedate(date, extendeddateformats, d)[0]
1383
1382
1384 if date[0] == "<":
1383 if date[0] == "<":
1385 when = upper(date[1:])
1384 when = upper(date[1:])
1386 return lambda x: x <= when
1385 return lambda x: x <= when
1387 elif date[0] == ">":
1386 elif date[0] == ">":
1388 when = lower(date[1:])
1387 when = lower(date[1:])
1389 return lambda x: x >= when
1388 return lambda x: x >= when
1390 elif date[0] == "-":
1389 elif date[0] == "-":
1391 try:
1390 try:
1392 days = int(date[1:])
1391 days = int(date[1:])
1393 except ValueError:
1392 except ValueError:
1394 raise Abort(_("invalid day spec: %s") % date[1:])
1393 raise Abort(_("invalid day spec: %s") % date[1:])
1395 when = makedate()[0] - days * 3600 * 24
1394 when = makedate()[0] - days * 3600 * 24
1396 return lambda x: x >= when
1395 return lambda x: x >= when
1397 elif " to " in date:
1396 elif " to " in date:
1398 a, b = date.split(" to ")
1397 a, b = date.split(" to ")
1399 start, stop = lower(a), upper(b)
1398 start, stop = lower(a), upper(b)
1400 return lambda x: x >= start and x <= stop
1399 return lambda x: x >= start and x <= stop
1401 else:
1400 else:
1402 start, stop = lower(date), upper(date)
1401 start, stop = lower(date), upper(date)
1403 return lambda x: x >= start and x <= stop
1402 return lambda x: x >= start and x <= stop
1404
1403
1405 def shortuser(user):
1404 def shortuser(user):
1406 """Return a short representation of a user name or email address."""
1405 """Return a short representation of a user name or email address."""
1407 f = user.find('@')
1406 f = user.find('@')
1408 if f >= 0:
1407 if f >= 0:
1409 user = user[:f]
1408 user = user[:f]
1410 f = user.find('<')
1409 f = user.find('<')
1411 if f >= 0:
1410 if f >= 0:
1412 user = user[f+1:]
1411 user = user[f+1:]
1413 f = user.find(' ')
1412 f = user.find(' ')
1414 if f >= 0:
1413 if f >= 0:
1415 user = user[:f]
1414 user = user[:f]
1416 f = user.find('.')
1415 f = user.find('.')
1417 if f >= 0:
1416 if f >= 0:
1418 user = user[:f]
1417 user = user[:f]
1419 return user
1418 return user
1420
1419
1421 def email(author):
1420 def email(author):
1422 '''get email of author.'''
1421 '''get email of author.'''
1423 r = author.find('>')
1422 r = author.find('>')
1424 if r == -1: r = None
1423 if r == -1: r = None
1425 return author[author.find('<')+1:r]
1424 return author[author.find('<')+1:r]
1426
1425
1427 def ellipsis(text, maxlength=400):
1426 def ellipsis(text, maxlength=400):
1428 """Trim string to at most maxlength (default: 400) characters."""
1427 """Trim string to at most maxlength (default: 400) characters."""
1429 if len(text) <= maxlength:
1428 if len(text) <= maxlength:
1430 return text
1429 return text
1431 else:
1430 else:
1432 return "%s..." % (text[:maxlength-3])
1431 return "%s..." % (text[:maxlength-3])
1433
1432
1434 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1433 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1435 '''yield every hg repository under path, recursively.'''
1434 '''yield every hg repository under path, recursively.'''
1436 def errhandler(err):
1435 def errhandler(err):
1437 if err.filename == path:
1436 if err.filename == path:
1438 raise err
1437 raise err
1439 if followsym and hasattr(os.path, 'samestat'):
1438 if followsym and hasattr(os.path, 'samestat'):
1440 def _add_dir_if_not_there(dirlst, dirname):
1439 def _add_dir_if_not_there(dirlst, dirname):
1441 match = False
1440 match = False
1442 samestat = os.path.samestat
1441 samestat = os.path.samestat
1443 dirstat = os.stat(dirname)
1442 dirstat = os.stat(dirname)
1444 for lstdirstat in dirlst:
1443 for lstdirstat in dirlst:
1445 if samestat(dirstat, lstdirstat):
1444 if samestat(dirstat, lstdirstat):
1446 match = True
1445 match = True
1447 break
1446 break
1448 if not match:
1447 if not match:
1449 dirlst.append(dirstat)
1448 dirlst.append(dirstat)
1450 return not match
1449 return not match
1451 else:
1450 else:
1452 followsym = False
1451 followsym = False
1453
1452
1454 if (seen_dirs is None) and followsym:
1453 if (seen_dirs is None) and followsym:
1455 seen_dirs = []
1454 seen_dirs = []
1456 _add_dir_if_not_there(seen_dirs, path)
1455 _add_dir_if_not_there(seen_dirs, path)
1457 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1456 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1458 if '.hg' in dirs:
1457 if '.hg' in dirs:
1459 yield root # found a repository
1458 yield root # found a repository
1460 qroot = os.path.join(root, '.hg', 'patches')
1459 qroot = os.path.join(root, '.hg', 'patches')
1461 if os.path.isdir(os.path.join(qroot, '.hg')):
1460 if os.path.isdir(os.path.join(qroot, '.hg')):
1462 yield qroot # we have a patch queue repo here
1461 yield qroot # we have a patch queue repo here
1463 if recurse:
1462 if recurse:
1464 # avoid recursing inside the .hg directory
1463 # avoid recursing inside the .hg directory
1465 dirs.remove('.hg')
1464 dirs.remove('.hg')
1466 else:
1465 else:
1467 dirs[:] = [] # don't descend further
1466 dirs[:] = [] # don't descend further
1468 elif followsym:
1467 elif followsym:
1469 newdirs = []
1468 newdirs = []
1470 for d in dirs:
1469 for d in dirs:
1471 fname = os.path.join(root, d)
1470 fname = os.path.join(root, d)
1472 if _add_dir_if_not_there(seen_dirs, fname):
1471 if _add_dir_if_not_there(seen_dirs, fname):
1473 if os.path.islink(fname):
1472 if os.path.islink(fname):
1474 for hgname in walkrepos(fname, True, seen_dirs):
1473 for hgname in walkrepos(fname, True, seen_dirs):
1475 yield hgname
1474 yield hgname
1476 else:
1475 else:
1477 newdirs.append(d)
1476 newdirs.append(d)
1478 dirs[:] = newdirs
1477 dirs[:] = newdirs
1479
1478
1480 _rcpath = None
1479 _rcpath = None
1481
1480
1482 def os_rcpath():
1481 def os_rcpath():
1483 '''return default os-specific hgrc search path'''
1482 '''return default os-specific hgrc search path'''
1484 path = system_rcpath()
1483 path = system_rcpath()
1485 path.extend(user_rcpath())
1484 path.extend(user_rcpath())
1486 path = [os.path.normpath(f) for f in path]
1485 path = [os.path.normpath(f) for f in path]
1487 return path
1486 return path
1488
1487
1489 def rcpath():
1488 def rcpath():
1490 '''return hgrc search path. if env var HGRCPATH is set, use it.
1489 '''return hgrc search path. if env var HGRCPATH is set, use it.
1491 for each item in path, if directory, use files ending in .rc,
1490 for each item in path, if directory, use files ending in .rc,
1492 else use item.
1491 else use item.
1493 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1492 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1494 if no HGRCPATH, use default os-specific path.'''
1493 if no HGRCPATH, use default os-specific path.'''
1495 global _rcpath
1494 global _rcpath
1496 if _rcpath is None:
1495 if _rcpath is None:
1497 if 'HGRCPATH' in os.environ:
1496 if 'HGRCPATH' in os.environ:
1498 _rcpath = []
1497 _rcpath = []
1499 for p in os.environ['HGRCPATH'].split(os.pathsep):
1498 for p in os.environ['HGRCPATH'].split(os.pathsep):
1500 if not p: continue
1499 if not p: continue
1501 if os.path.isdir(p):
1500 if os.path.isdir(p):
1502 for f, kind in osutil.listdir(p):
1501 for f, kind in osutil.listdir(p):
1503 if f.endswith('.rc'):
1502 if f.endswith('.rc'):
1504 _rcpath.append(os.path.join(p, f))
1503 _rcpath.append(os.path.join(p, f))
1505 else:
1504 else:
1506 _rcpath.append(p)
1505 _rcpath.append(p)
1507 else:
1506 else:
1508 _rcpath = os_rcpath()
1507 _rcpath = os_rcpath()
1509 return _rcpath
1508 return _rcpath
1510
1509
1511 def bytecount(nbytes):
1510 def bytecount(nbytes):
1512 '''return byte count formatted as readable string, with units'''
1511 '''return byte count formatted as readable string, with units'''
1513
1512
1514 units = (
1513 units = (
1515 (100, 1<<30, _('%.0f GB')),
1514 (100, 1<<30, _('%.0f GB')),
1516 (10, 1<<30, _('%.1f GB')),
1515 (10, 1<<30, _('%.1f GB')),
1517 (1, 1<<30, _('%.2f GB')),
1516 (1, 1<<30, _('%.2f GB')),
1518 (100, 1<<20, _('%.0f MB')),
1517 (100, 1<<20, _('%.0f MB')),
1519 (10, 1<<20, _('%.1f MB')),
1518 (10, 1<<20, _('%.1f MB')),
1520 (1, 1<<20, _('%.2f MB')),
1519 (1, 1<<20, _('%.2f MB')),
1521 (100, 1<<10, _('%.0f KB')),
1520 (100, 1<<10, _('%.0f KB')),
1522 (10, 1<<10, _('%.1f KB')),
1521 (10, 1<<10, _('%.1f KB')),
1523 (1, 1<<10, _('%.2f KB')),
1522 (1, 1<<10, _('%.2f KB')),
1524 (1, 1, _('%.0f bytes')),
1523 (1, 1, _('%.0f bytes')),
1525 )
1524 )
1526
1525
1527 for multiplier, divisor, format in units:
1526 for multiplier, divisor, format in units:
1528 if nbytes >= divisor * multiplier:
1527 if nbytes >= divisor * multiplier:
1529 return format % (nbytes / float(divisor))
1528 return format % (nbytes / float(divisor))
1530 return units[-1][2] % nbytes
1529 return units[-1][2] % nbytes
1531
1530
1532 def drop_scheme(scheme, path):
1531 def drop_scheme(scheme, path):
1533 sc = scheme + ':'
1532 sc = scheme + ':'
1534 if path.startswith(sc):
1533 if path.startswith(sc):
1535 path = path[len(sc):]
1534 path = path[len(sc):]
1536 if path.startswith('//'):
1535 if path.startswith('//'):
1537 path = path[2:]
1536 path = path[2:]
1538 return path
1537 return path
1539
1538
1540 def uirepr(s):
1539 def uirepr(s):
1541 # Avoid double backslash in Windows path repr()
1540 # Avoid double backslash in Windows path repr()
1542 return repr(s).replace('\\\\', '\\')
1541 return repr(s).replace('\\\\', '\\')
1543
1542
1544 def termwidth():
1543 def termwidth():
1545 if 'COLUMNS' in os.environ:
1544 if 'COLUMNS' in os.environ:
1546 try:
1545 try:
1547 return int(os.environ['COLUMNS'])
1546 return int(os.environ['COLUMNS'])
1548 except ValueError:
1547 except ValueError:
1549 pass
1548 pass
1550 try:
1549 try:
1551 import termios, array, fcntl
1550 import termios, array, fcntl
1552 for dev in (sys.stdout, sys.stdin):
1551 for dev in (sys.stdout, sys.stdin):
1553 try:
1552 try:
1554 fd = dev.fileno()
1553 fd = dev.fileno()
1555 if not os.isatty(fd):
1554 if not os.isatty(fd):
1556 continue
1555 continue
1557 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
1556 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
1558 return array.array('h', arri)[1]
1557 return array.array('h', arri)[1]
1559 except ValueError:
1558 except ValueError:
1560 pass
1559 pass
1561 except ImportError:
1560 except ImportError:
1562 pass
1561 pass
1563 return 80
1562 return 80
1564
1563
1565 def iterlines(iterator):
1564 def iterlines(iterator):
1566 for chunk in iterator:
1565 for chunk in iterator:
1567 for line in chunk.splitlines():
1566 for line in chunk.splitlines():
1568 yield line
1567 yield line
General Comments 0
You need to be logged in to leave comments. Login now