##// END OF EJS Templates
typo fix
Jesse Glick -
r4876:001e8a74 default
parent child Browse files
Show More
@@ -1,1614 +1,1614 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
17 import os, threading, time, calendar, ConfigParser, locale, glob
17 import os, threading, time, calendar, ConfigParser, locale, glob
18
18
19 try:
19 try:
20 set = set
20 set = set
21 frozenset = frozenset
21 frozenset = frozenset
22 except NameError:
22 except NameError:
23 from sets import Set as set, ImmutableSet as frozenset
23 from sets import Set as set, ImmutableSet as frozenset
24
24
25 try:
25 try:
26 _encoding = os.environ.get("HGENCODING")
26 _encoding = os.environ.get("HGENCODING")
27 if sys.platform == 'darwin' and not _encoding:
27 if sys.platform == 'darwin' and not _encoding:
28 # On darwin, getpreferredencoding ignores the locale environment and
28 # On darwin, getpreferredencoding ignores the locale environment and
29 # always returns mac-roman. We override this if the environment is
29 # always returns mac-roman. We override this if the environment is
30 # not C (has been customized by the user).
30 # not C (has been customized by the user).
31 locale.setlocale(locale.LC_CTYPE, '')
31 locale.setlocale(locale.LC_CTYPE, '')
32 _encoding = locale.getlocale()[1]
32 _encoding = locale.getlocale()[1]
33 if not _encoding:
33 if not _encoding:
34 _encoding = locale.getpreferredencoding() or 'ascii'
34 _encoding = locale.getpreferredencoding() or 'ascii'
35 except locale.Error:
35 except locale.Error:
36 _encoding = 'ascii'
36 _encoding = 'ascii'
37 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
37 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
38 _fallbackencoding = 'ISO-8859-1'
38 _fallbackencoding = 'ISO-8859-1'
39
39
40 def tolocal(s):
40 def tolocal(s):
41 """
41 """
42 Convert a string from internal UTF-8 to local encoding
42 Convert a string from internal UTF-8 to local encoding
43
43
44 All internal strings should be UTF-8 but some repos before the
44 All internal strings should be UTF-8 but some repos before the
45 implementation of locale support may contain latin1 or possibly
45 implementation of locale support may contain latin1 or possibly
46 other character sets. We attempt to decode everything strictly
46 other character sets. We attempt to decode everything strictly
47 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
47 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
48 replace unknown characters.
48 replace unknown characters.
49 """
49 """
50 for e in ('UTF-8', _fallbackencoding):
50 for e in ('UTF-8', _fallbackencoding):
51 try:
51 try:
52 u = s.decode(e) # attempt strict decoding
52 u = s.decode(e) # attempt strict decoding
53 return u.encode(_encoding, "replace")
53 return u.encode(_encoding, "replace")
54 except LookupError, k:
54 except LookupError, k:
55 raise Abort(_("%s, please check your locale settings") % k)
55 raise Abort(_("%s, please check your locale settings") % k)
56 except UnicodeDecodeError:
56 except UnicodeDecodeError:
57 pass
57 pass
58 u = s.decode("utf-8", "replace") # last ditch
58 u = s.decode("utf-8", "replace") # last ditch
59 return u.encode(_encoding, "replace")
59 return u.encode(_encoding, "replace")
60
60
61 def fromlocal(s):
61 def fromlocal(s):
62 """
62 """
63 Convert a string from the local character encoding to UTF-8
63 Convert a string from the local character encoding to UTF-8
64
64
65 We attempt to decode strings using the encoding mode set by
65 We attempt to decode strings using the encoding mode set by
66 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
66 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
67 characters will cause an error message. Other modes include
67 characters will cause an error message. Other modes include
68 'replace', which replaces unknown characters with a special
68 'replace', which replaces unknown characters with a special
69 Unicode character, and 'ignore', which drops the character.
69 Unicode character, and 'ignore', which drops the character.
70 """
70 """
71 try:
71 try:
72 return s.decode(_encoding, _encodingmode).encode("utf-8")
72 return s.decode(_encoding, _encodingmode).encode("utf-8")
73 except UnicodeDecodeError, inst:
73 except UnicodeDecodeError, inst:
74 sub = s[max(0, inst.start-10):inst.start+10]
74 sub = s[max(0, inst.start-10):inst.start+10]
75 raise Abort("decoding near '%s': %s!" % (sub, inst))
75 raise Abort("decoding near '%s': %s!" % (sub, inst))
76 except LookupError, k:
76 except LookupError, k:
77 raise Abort(_("%s, please check your locale settings") % k)
77 raise Abort(_("%s, please check your locale settings") % k)
78
78
79 def locallen(s):
79 def locallen(s):
80 """Find the length in characters of a local string"""
80 """Find the length in characters of a local string"""
81 return len(s.decode(_encoding, "replace"))
81 return len(s.decode(_encoding, "replace"))
82
82
83 def localsub(s, a, b=None):
83 def localsub(s, a, b=None):
84 try:
84 try:
85 u = s.decode(_encoding, _encodingmode)
85 u = s.decode(_encoding, _encodingmode)
86 if b is not None:
86 if b is not None:
87 u = u[a:b]
87 u = u[a:b]
88 else:
88 else:
89 u = u[:a]
89 u = u[:a]
90 return u.encode(_encoding, _encodingmode)
90 return u.encode(_encoding, _encodingmode)
91 except UnicodeDecodeError, inst:
91 except UnicodeDecodeError, inst:
92 sub = s[max(0, inst.start-10), inst.start+10]
92 sub = s[max(0, inst.start-10), inst.start+10]
93 raise Abort(_("decoding near '%s': %s!") % (sub, inst))
93 raise Abort(_("decoding near '%s': %s!") % (sub, inst))
94
94
95 # used by parsedate
95 # used by parsedate
96 defaultdateformats = (
96 defaultdateformats = (
97 '%Y-%m-%d %H:%M:%S',
97 '%Y-%m-%d %H:%M:%S',
98 '%Y-%m-%d %I:%M:%S%p',
98 '%Y-%m-%d %I:%M:%S%p',
99 '%Y-%m-%d %H:%M',
99 '%Y-%m-%d %H:%M',
100 '%Y-%m-%d %I:%M%p',
100 '%Y-%m-%d %I:%M%p',
101 '%Y-%m-%d',
101 '%Y-%m-%d',
102 '%m-%d',
102 '%m-%d',
103 '%m/%d',
103 '%m/%d',
104 '%m/%d/%y',
104 '%m/%d/%y',
105 '%m/%d/%Y',
105 '%m/%d/%Y',
106 '%a %b %d %H:%M:%S %Y',
106 '%a %b %d %H:%M:%S %Y',
107 '%a %b %d %I:%M:%S%p %Y',
107 '%a %b %d %I:%M:%S%p %Y',
108 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
108 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
109 '%b %d %H:%M:%S %Y',
109 '%b %d %H:%M:%S %Y',
110 '%b %d %I:%M:%S%p %Y',
110 '%b %d %I:%M:%S%p %Y',
111 '%b %d %H:%M:%S',
111 '%b %d %H:%M:%S',
112 '%b %d %I:%M:%S%p',
112 '%b %d %I:%M:%S%p',
113 '%b %d %H:%M',
113 '%b %d %H:%M',
114 '%b %d %I:%M%p',
114 '%b %d %I:%M%p',
115 '%b %d %Y',
115 '%b %d %Y',
116 '%b %d',
116 '%b %d',
117 '%H:%M:%S',
117 '%H:%M:%S',
118 '%I:%M:%SP',
118 '%I:%M:%SP',
119 '%H:%M',
119 '%H:%M',
120 '%I:%M%p',
120 '%I:%M%p',
121 )
121 )
122
122
123 extendeddateformats = defaultdateformats + (
123 extendeddateformats = defaultdateformats + (
124 "%Y",
124 "%Y",
125 "%Y-%m",
125 "%Y-%m",
126 "%b",
126 "%b",
127 "%b %Y",
127 "%b %Y",
128 )
128 )
129
129
130 class SignalInterrupt(Exception):
130 class SignalInterrupt(Exception):
131 """Exception raised on SIGTERM and SIGHUP."""
131 """Exception raised on SIGTERM and SIGHUP."""
132
132
133 # differences from SafeConfigParser:
133 # differences from SafeConfigParser:
134 # - case-sensitive keys
134 # - case-sensitive keys
135 # - allows values that are not strings (this means that you may not
135 # - allows values that are not strings (this means that you may not
136 # be able to save the configuration to a file)
136 # be able to save the configuration to a file)
137 class configparser(ConfigParser.SafeConfigParser):
137 class configparser(ConfigParser.SafeConfigParser):
138 def optionxform(self, optionstr):
138 def optionxform(self, optionstr):
139 return optionstr
139 return optionstr
140
140
141 def set(self, section, option, value):
141 def set(self, section, option, value):
142 return ConfigParser.ConfigParser.set(self, section, option, value)
142 return ConfigParser.ConfigParser.set(self, section, option, value)
143
143
144 def _interpolate(self, section, option, rawval, vars):
144 def _interpolate(self, section, option, rawval, vars):
145 if not isinstance(rawval, basestring):
145 if not isinstance(rawval, basestring):
146 return rawval
146 return rawval
147 return ConfigParser.SafeConfigParser._interpolate(self, section,
147 return ConfigParser.SafeConfigParser._interpolate(self, section,
148 option, rawval, vars)
148 option, rawval, vars)
149
149
150 def cachefunc(func):
150 def cachefunc(func):
151 '''cache the result of function calls'''
151 '''cache the result of function calls'''
152 # XXX doesn't handle keywords args
152 # XXX doesn't handle keywords args
153 cache = {}
153 cache = {}
154 if func.func_code.co_argcount == 1:
154 if func.func_code.co_argcount == 1:
155 # we gain a small amount of time because
155 # we gain a small amount of time because
156 # we don't need to pack/unpack the list
156 # we don't need to pack/unpack the list
157 def f(arg):
157 def f(arg):
158 if arg not in cache:
158 if arg not in cache:
159 cache[arg] = func(arg)
159 cache[arg] = func(arg)
160 return cache[arg]
160 return cache[arg]
161 else:
161 else:
162 def f(*args):
162 def f(*args):
163 if args not in cache:
163 if args not in cache:
164 cache[args] = func(*args)
164 cache[args] = func(*args)
165 return cache[args]
165 return cache[args]
166
166
167 return f
167 return f
168
168
169 def pipefilter(s, cmd):
169 def pipefilter(s, cmd):
170 '''filter string S through command CMD, returning its output'''
170 '''filter string S through command CMD, returning its output'''
171 (pin, pout) = os.popen2(cmd, 'b')
171 (pin, pout) = os.popen2(cmd, 'b')
172 def writer():
172 def writer():
173 try:
173 try:
174 pin.write(s)
174 pin.write(s)
175 pin.close()
175 pin.close()
176 except IOError, inst:
176 except IOError, inst:
177 if inst.errno != errno.EPIPE:
177 if inst.errno != errno.EPIPE:
178 raise
178 raise
179
179
180 # we should use select instead on UNIX, but this will work on most
180 # we should use select instead on UNIX, but this will work on most
181 # systems, including Windows
181 # systems, including Windows
182 w = threading.Thread(target=writer)
182 w = threading.Thread(target=writer)
183 w.start()
183 w.start()
184 f = pout.read()
184 f = pout.read()
185 pout.close()
185 pout.close()
186 w.join()
186 w.join()
187 return f
187 return f
188
188
189 def tempfilter(s, cmd):
189 def tempfilter(s, cmd):
190 '''filter string S through a pair of temporary files with CMD.
190 '''filter string S through a pair of temporary files with CMD.
191 CMD is used as a template to create the real command to be run,
191 CMD is used as a template to create the real command to be run,
192 with the strings INFILE and OUTFILE replaced by the real names of
192 with the strings INFILE and OUTFILE replaced by the real names of
193 the temporary files generated.'''
193 the temporary files generated.'''
194 inname, outname = None, None
194 inname, outname = None, None
195 try:
195 try:
196 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
196 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
197 fp = os.fdopen(infd, 'wb')
197 fp = os.fdopen(infd, 'wb')
198 fp.write(s)
198 fp.write(s)
199 fp.close()
199 fp.close()
200 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
200 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
201 os.close(outfd)
201 os.close(outfd)
202 cmd = cmd.replace('INFILE', inname)
202 cmd = cmd.replace('INFILE', inname)
203 cmd = cmd.replace('OUTFILE', outname)
203 cmd = cmd.replace('OUTFILE', outname)
204 code = os.system(cmd)
204 code = os.system(cmd)
205 if sys.platform == 'OpenVMS' and code & 1:
205 if sys.platform == 'OpenVMS' and code & 1:
206 code = 0
206 code = 0
207 if code: raise Abort(_("command '%s' failed: %s") %
207 if code: raise Abort(_("command '%s' failed: %s") %
208 (cmd, explain_exit(code)))
208 (cmd, explain_exit(code)))
209 return open(outname, 'rb').read()
209 return open(outname, 'rb').read()
210 finally:
210 finally:
211 try:
211 try:
212 if inname: os.unlink(inname)
212 if inname: os.unlink(inname)
213 except: pass
213 except: pass
214 try:
214 try:
215 if outname: os.unlink(outname)
215 if outname: os.unlink(outname)
216 except: pass
216 except: pass
217
217
218 filtertable = {
218 filtertable = {
219 'tempfile:': tempfilter,
219 'tempfile:': tempfilter,
220 'pipe:': pipefilter,
220 'pipe:': pipefilter,
221 }
221 }
222
222
223 def filter(s, cmd):
223 def filter(s, cmd):
224 "filter a string through a command that transforms its input to its output"
224 "filter a string through a command that transforms its input to its output"
225 for name, fn in filtertable.iteritems():
225 for name, fn in filtertable.iteritems():
226 if cmd.startswith(name):
226 if cmd.startswith(name):
227 return fn(s, cmd[len(name):].lstrip())
227 return fn(s, cmd[len(name):].lstrip())
228 return pipefilter(s, cmd)
228 return pipefilter(s, cmd)
229
229
230 def binary(s):
230 def binary(s):
231 """return true if a string is binary data using diff's heuristic"""
231 """return true if a string is binary data using diff's heuristic"""
232 if s and '\0' in s[:4096]:
232 if s and '\0' in s[:4096]:
233 return True
233 return True
234 return False
234 return False
235
235
236 def unique(g):
236 def unique(g):
237 """return the uniq elements of iterable g"""
237 """return the uniq elements of iterable g"""
238 seen = {}
238 seen = {}
239 l = []
239 l = []
240 for f in g:
240 for f in g:
241 if f not in seen:
241 if f not in seen:
242 seen[f] = 1
242 seen[f] = 1
243 l.append(f)
243 l.append(f)
244 return l
244 return l
245
245
246 class Abort(Exception):
246 class Abort(Exception):
247 """Raised if a command needs to print an error and exit."""
247 """Raised if a command needs to print an error and exit."""
248
248
249 class UnexpectedOutput(Abort):
249 class UnexpectedOutput(Abort):
250 """Raised to print an error with part of output and exit."""
250 """Raised to print an error with part of output and exit."""
251
251
252 def always(fn): return True
252 def always(fn): return True
253 def never(fn): return False
253 def never(fn): return False
254
254
255 def expand_glob(pats):
255 def expand_glob(pats):
256 '''On Windows, expand the implicit globs in a list of patterns'''
256 '''On Windows, expand the implicit globs in a list of patterns'''
257 if os.name != 'nt':
257 if os.name != 'nt':
258 return list(pats)
258 return list(pats)
259 ret = []
259 ret = []
260 for p in pats:
260 for p in pats:
261 kind, name = patkind(p, None)
261 kind, name = patkind(p, None)
262 if kind is None:
262 if kind is None:
263 globbed = glob.glob(name)
263 globbed = glob.glob(name)
264 if globbed:
264 if globbed:
265 ret.extend(globbed)
265 ret.extend(globbed)
266 continue
266 continue
267 # if we couldn't expand the glob, just keep it around
267 # if we couldn't expand the glob, just keep it around
268 ret.append(p)
268 ret.append(p)
269 return ret
269 return ret
270
270
271 def patkind(name, dflt_pat='glob'):
271 def patkind(name, dflt_pat='glob'):
272 """Split a string into an optional pattern kind prefix and the
272 """Split a string into an optional pattern kind prefix and the
273 actual pattern."""
273 actual pattern."""
274 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
274 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
275 if name.startswith(prefix + ':'): return name.split(':', 1)
275 if name.startswith(prefix + ':'): return name.split(':', 1)
276 return dflt_pat, name
276 return dflt_pat, name
277
277
278 def globre(pat, head='^', tail='$'):
278 def globre(pat, head='^', tail='$'):
279 "convert a glob pattern into a regexp"
279 "convert a glob pattern into a regexp"
280 i, n = 0, len(pat)
280 i, n = 0, len(pat)
281 res = ''
281 res = ''
282 group = False
282 group = False
283 def peek(): return i < n and pat[i]
283 def peek(): return i < n and pat[i]
284 while i < n:
284 while i < n:
285 c = pat[i]
285 c = pat[i]
286 i = i+1
286 i = i+1
287 if c == '*':
287 if c == '*':
288 if peek() == '*':
288 if peek() == '*':
289 i += 1
289 i += 1
290 res += '.*'
290 res += '.*'
291 else:
291 else:
292 res += '[^/]*'
292 res += '[^/]*'
293 elif c == '?':
293 elif c == '?':
294 res += '.'
294 res += '.'
295 elif c == '[':
295 elif c == '[':
296 j = i
296 j = i
297 if j < n and pat[j] in '!]':
297 if j < n and pat[j] in '!]':
298 j += 1
298 j += 1
299 while j < n and pat[j] != ']':
299 while j < n and pat[j] != ']':
300 j += 1
300 j += 1
301 if j >= n:
301 if j >= n:
302 res += '\\['
302 res += '\\['
303 else:
303 else:
304 stuff = pat[i:j].replace('\\','\\\\')
304 stuff = pat[i:j].replace('\\','\\\\')
305 i = j + 1
305 i = j + 1
306 if stuff[0] == '!':
306 if stuff[0] == '!':
307 stuff = '^' + stuff[1:]
307 stuff = '^' + stuff[1:]
308 elif stuff[0] == '^':
308 elif stuff[0] == '^':
309 stuff = '\\' + stuff
309 stuff = '\\' + stuff
310 res = '%s[%s]' % (res, stuff)
310 res = '%s[%s]' % (res, stuff)
311 elif c == '{':
311 elif c == '{':
312 group = True
312 group = True
313 res += '(?:'
313 res += '(?:'
314 elif c == '}' and group:
314 elif c == '}' and group:
315 res += ')'
315 res += ')'
316 group = False
316 group = False
317 elif c == ',' and group:
317 elif c == ',' and group:
318 res += '|'
318 res += '|'
319 elif c == '\\':
319 elif c == '\\':
320 p = peek()
320 p = peek()
321 if p:
321 if p:
322 i += 1
322 i += 1
323 res += re.escape(p)
323 res += re.escape(p)
324 else:
324 else:
325 res += re.escape(c)
325 res += re.escape(c)
326 else:
326 else:
327 res += re.escape(c)
327 res += re.escape(c)
328 return head + res + tail
328 return head + res + tail
329
329
330 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
330 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
331
331
332 def pathto(root, n1, n2):
332 def pathto(root, n1, n2):
333 '''return the relative path from one place to another.
333 '''return the relative path from one place to another.
334 root should use os.sep to separate directories
334 root should use os.sep to separate directories
335 n1 should use os.sep to separate directories
335 n1 should use os.sep to separate directories
336 n2 should use "/" to separate directories
336 n2 should use "/" to separate directories
337 returns an os.sep-separated path.
337 returns an os.sep-separated path.
338
338
339 If n1 is a relative path, it's assumed it's
339 If n1 is a relative path, it's assumed it's
340 relative to root.
340 relative to root.
341 n2 should always be relative to root.
341 n2 should always be relative to root.
342 '''
342 '''
343 if not n1: return localpath(n2)
343 if not n1: return localpath(n2)
344 if os.path.isabs(n1):
344 if os.path.isabs(n1):
345 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
345 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
346 return os.path.join(root, localpath(n2))
346 return os.path.join(root, localpath(n2))
347 n2 = '/'.join((pconvert(root), n2))
347 n2 = '/'.join((pconvert(root), n2))
348 a, b = n1.split(os.sep), n2.split('/')
348 a, b = n1.split(os.sep), n2.split('/')
349 a.reverse()
349 a.reverse()
350 b.reverse()
350 b.reverse()
351 while a and b and a[-1] == b[-1]:
351 while a and b and a[-1] == b[-1]:
352 a.pop()
352 a.pop()
353 b.pop()
353 b.pop()
354 b.reverse()
354 b.reverse()
355 return os.sep.join((['..'] * len(a)) + b)
355 return os.sep.join((['..'] * len(a)) + b)
356
356
357 def canonpath(root, cwd, myname):
357 def canonpath(root, cwd, myname):
358 """return the canonical path of myname, given cwd and root"""
358 """return the canonical path of myname, given cwd and root"""
359 if root == os.sep:
359 if root == os.sep:
360 rootsep = os.sep
360 rootsep = os.sep
361 elif root.endswith(os.sep):
361 elif root.endswith(os.sep):
362 rootsep = root
362 rootsep = root
363 else:
363 else:
364 rootsep = root + os.sep
364 rootsep = root + os.sep
365 name = myname
365 name = myname
366 if not os.path.isabs(name):
366 if not os.path.isabs(name):
367 name = os.path.join(root, cwd, name)
367 name = os.path.join(root, cwd, name)
368 name = os.path.normpath(name)
368 name = os.path.normpath(name)
369 if name != rootsep and name.startswith(rootsep):
369 if name != rootsep and name.startswith(rootsep):
370 name = name[len(rootsep):]
370 name = name[len(rootsep):]
371 audit_path(name)
371 audit_path(name)
372 return pconvert(name)
372 return pconvert(name)
373 elif name == root:
373 elif name == root:
374 return ''
374 return ''
375 else:
375 else:
376 # Determine whether `name' is in the hierarchy at or beneath `root',
376 # Determine whether `name' is in the hierarchy at or beneath `root',
377 # by iterating name=dirname(name) until that causes no change (can't
377 # by iterating name=dirname(name) until that causes no change (can't
378 # check name == '/', because that doesn't work on windows). For each
378 # check name == '/', because that doesn't work on windows). For each
379 # `name', compare dev/inode numbers. If they match, the list `rel'
379 # `name', compare dev/inode numbers. If they match, the list `rel'
380 # holds the reversed list of components making up the relative file
380 # holds the reversed list of components making up the relative file
381 # name we want.
381 # name we want.
382 root_st = os.stat(root)
382 root_st = os.stat(root)
383 rel = []
383 rel = []
384 while True:
384 while True:
385 try:
385 try:
386 name_st = os.stat(name)
386 name_st = os.stat(name)
387 except OSError:
387 except OSError:
388 break
388 break
389 if samestat(name_st, root_st):
389 if samestat(name_st, root_st):
390 if not rel:
390 if not rel:
391 # name was actually the same as root (maybe a symlink)
391 # name was actually the same as root (maybe a symlink)
392 return ''
392 return ''
393 rel.reverse()
393 rel.reverse()
394 name = os.path.join(*rel)
394 name = os.path.join(*rel)
395 audit_path(name)
395 audit_path(name)
396 return pconvert(name)
396 return pconvert(name)
397 dirname, basename = os.path.split(name)
397 dirname, basename = os.path.split(name)
398 rel.append(basename)
398 rel.append(basename)
399 if dirname == name:
399 if dirname == name:
400 break
400 break
401 name = dirname
401 name = dirname
402
402
403 raise Abort('%s not under root' % myname)
403 raise Abort('%s not under root' % myname)
404
404
405 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
405 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
406 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
406 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
407
407
408 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
408 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
409 globbed=False, default=None):
409 globbed=False, default=None):
410 default = default or 'relpath'
410 default = default or 'relpath'
411 if default == 'relpath' and not globbed:
411 if default == 'relpath' and not globbed:
412 names = expand_glob(names)
412 names = expand_glob(names)
413 return _matcher(canonroot, cwd, names, inc, exc, default, src)
413 return _matcher(canonroot, cwd, names, inc, exc, default, src)
414
414
415 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
415 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
416 """build a function to match a set of file patterns
416 """build a function to match a set of file patterns
417
417
418 arguments:
418 arguments:
419 canonroot - the canonical root of the tree you're matching against
419 canonroot - the canonical root of the tree you're matching against
420 cwd - the current working directory, if relevant
420 cwd - the current working directory, if relevant
421 names - patterns to find
421 names - patterns to find
422 inc - patterns to include
422 inc - patterns to include
423 exc - patterns to exclude
423 exc - patterns to exclude
424 dflt_pat - if a pattern in names has no explicit type, assume this one
424 dflt_pat - if a pattern in names has no explicit type, assume this one
425 src - where these patterns came from (e.g. .hgignore)
425 src - where these patterns came from (e.g. .hgignore)
426
426
427 a pattern is one of:
427 a pattern is one of:
428 'glob:<glob>' - a glob relative to cwd
428 'glob:<glob>' - a glob relative to cwd
429 're:<regexp>' - a regular expression
429 're:<regexp>' - a regular expression
430 'path:<path>' - a path relative to canonroot
430 'path:<path>' - a path relative to canonroot
431 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
431 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
432 'relpath:<path>' - a path relative to cwd
432 'relpath:<path>' - a path relative to cwd
433 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
433 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
434 '<something>' - one of the cases above, selected by the dflt_pat argument
434 '<something>' - one of the cases above, selected by the dflt_pat argument
435
435
436 returns:
436 returns:
437 a 3-tuple containing
437 a 3-tuple containing
438 - list of roots (places where one should start a recursive walk of the fs);
438 - list of roots (places where one should start a recursive walk of the fs);
439 this often matches the explicit non-pattern names passed in, but also
439 this often matches the explicit non-pattern names passed in, but also
440 includes the initial part of glob: patterns that has no glob characters
440 includes the initial part of glob: patterns that has no glob characters
441 - a bool match(filename) function
441 - a bool match(filename) function
442 - a bool indicating if any patterns were passed in
442 - a bool indicating if any patterns were passed in
443 """
443 """
444
444
445 # a common case: no patterns at all
445 # a common case: no patterns at all
446 if not names and not inc and not exc:
446 if not names and not inc and not exc:
447 return [], always, False
447 return [], always, False
448
448
449 def contains_glob(name):
449 def contains_glob(name):
450 for c in name:
450 for c in name:
451 if c in _globchars: return True
451 if c in _globchars: return True
452 return False
452 return False
453
453
454 def regex(kind, name, tail):
454 def regex(kind, name, tail):
455 '''convert a pattern into a regular expression'''
455 '''convert a pattern into a regular expression'''
456 if not name:
456 if not name:
457 return ''
457 return ''
458 if kind == 're':
458 if kind == 're':
459 return name
459 return name
460 elif kind == 'path':
460 elif kind == 'path':
461 return '^' + re.escape(name) + '(?:/|$)'
461 return '^' + re.escape(name) + '(?:/|$)'
462 elif kind == 'relglob':
462 elif kind == 'relglob':
463 return globre(name, '(?:|.*/)', tail)
463 return globre(name, '(?:|.*/)', tail)
464 elif kind == 'relpath':
464 elif kind == 'relpath':
465 return re.escape(name) + '(?:/|$)'
465 return re.escape(name) + '(?:/|$)'
466 elif kind == 'relre':
466 elif kind == 'relre':
467 if name.startswith('^'):
467 if name.startswith('^'):
468 return name
468 return name
469 return '.*' + name
469 return '.*' + name
470 return globre(name, '', tail)
470 return globre(name, '', tail)
471
471
472 def matchfn(pats, tail):
472 def matchfn(pats, tail):
473 """build a matching function from a set of patterns"""
473 """build a matching function from a set of patterns"""
474 if not pats:
474 if not pats:
475 return
475 return
476 try:
476 try:
477 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
477 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
478 return re.compile(pat).match
478 return re.compile(pat).match
479 except re.error:
479 except re.error:
480 for k, p in pats:
480 for k, p in pats:
481 try:
481 try:
482 re.compile('(?:%s)' % regex(k, p, tail))
482 re.compile('(?:%s)' % regex(k, p, tail))
483 except re.error:
483 except re.error:
484 if src:
484 if src:
485 raise Abort("%s: invalid pattern (%s): %s" %
485 raise Abort("%s: invalid pattern (%s): %s" %
486 (src, k, p))
486 (src, k, p))
487 else:
487 else:
488 raise Abort("invalid pattern (%s): %s" % (k, p))
488 raise Abort("invalid pattern (%s): %s" % (k, p))
489 raise Abort("invalid pattern")
489 raise Abort("invalid pattern")
490
490
491 def globprefix(pat):
491 def globprefix(pat):
492 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
492 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
493 root = []
493 root = []
494 for p in pat.split('/'):
494 for p in pat.split('/'):
495 if contains_glob(p): break
495 if contains_glob(p): break
496 root.append(p)
496 root.append(p)
497 return '/'.join(root) or '.'
497 return '/'.join(root) or '.'
498
498
499 def normalizepats(names, default):
499 def normalizepats(names, default):
500 pats = []
500 pats = []
501 roots = []
501 roots = []
502 anypats = False
502 anypats = False
503 for kind, name in [patkind(p, default) for p in names]:
503 for kind, name in [patkind(p, default) for p in names]:
504 if kind in ('glob', 'relpath'):
504 if kind in ('glob', 'relpath'):
505 name = canonpath(canonroot, cwd, name)
505 name = canonpath(canonroot, cwd, name)
506 elif kind in ('relglob', 'path'):
506 elif kind in ('relglob', 'path'):
507 name = normpath(name)
507 name = normpath(name)
508
508
509 pats.append((kind, name))
509 pats.append((kind, name))
510
510
511 if kind in ('glob', 're', 'relglob', 'relre'):
511 if kind in ('glob', 're', 'relglob', 'relre'):
512 anypats = True
512 anypats = True
513
513
514 if kind == 'glob':
514 if kind == 'glob':
515 root = globprefix(name)
515 root = globprefix(name)
516 roots.append(root)
516 roots.append(root)
517 elif kind in ('relpath', 'path'):
517 elif kind in ('relpath', 'path'):
518 roots.append(name or '.')
518 roots.append(name or '.')
519 elif kind == 'relglob':
519 elif kind == 'relglob':
520 roots.append('.')
520 roots.append('.')
521 return roots, pats, anypats
521 return roots, pats, anypats
522
522
523 roots, pats, anypats = normalizepats(names, dflt_pat)
523 roots, pats, anypats = normalizepats(names, dflt_pat)
524
524
525 patmatch = matchfn(pats, '$') or always
525 patmatch = matchfn(pats, '$') or always
526 incmatch = always
526 incmatch = always
527 if inc:
527 if inc:
528 dummy, inckinds, dummy = normalizepats(inc, 'glob')
528 dummy, inckinds, dummy = normalizepats(inc, 'glob')
529 incmatch = matchfn(inckinds, '(?:/|$)')
529 incmatch = matchfn(inckinds, '(?:/|$)')
530 excmatch = lambda fn: False
530 excmatch = lambda fn: False
531 if exc:
531 if exc:
532 dummy, exckinds, dummy = normalizepats(exc, 'glob')
532 dummy, exckinds, dummy = normalizepats(exc, 'glob')
533 excmatch = matchfn(exckinds, '(?:/|$)')
533 excmatch = matchfn(exckinds, '(?:/|$)')
534
534
535 if not names and inc and not exc:
535 if not names and inc and not exc:
536 # common case: hgignore patterns
536 # common case: hgignore patterns
537 match = incmatch
537 match = incmatch
538 else:
538 else:
539 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
539 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
540
540
541 return (roots, match, (inc or exc or anypats) and True)
541 return (roots, match, (inc or exc or anypats) and True)
542
542
543 _hgexecutable = 'hg'
543 _hgexecutable = 'hg'
544
544
545 def set_hgexecutable(path):
545 def set_hgexecutable(path):
546 """remember location of the 'hg' executable if easily possible
546 """remember location of the 'hg' executable if easily possible
547
547
548 path might be None or empty if hg was loaded as a module,
548 path might be None or empty if hg was loaded as a module,
549 fall back to 'hg' in this case.
549 fall back to 'hg' in this case.
550 """
550 """
551 global _hgexecutable
551 global _hgexecutable
552 if path:
552 if path:
553 _hgexecutable = os.path.abspath(path)
553 _hgexecutable = os.path.abspath(path)
554
554
555 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
555 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
556 '''enhanced shell command execution.
556 '''enhanced shell command execution.
557 run with environment maybe modified, maybe in different dir.
557 run with environment maybe modified, maybe in different dir.
558
558
559 if command fails and onerr is None, return status. if ui object,
559 if command fails and onerr is None, return status. if ui object,
560 print error message and return status, else raise onerr object as
560 print error message and return status, else raise onerr object as
561 exception.'''
561 exception.'''
562 def py2shell(val):
562 def py2shell(val):
563 'convert python object into string that is useful to shell'
563 'convert python object into string that is useful to shell'
564 if val in (None, False):
564 if val in (None, False):
565 return '0'
565 return '0'
566 if val == True:
566 if val == True:
567 return '1'
567 return '1'
568 return str(val)
568 return str(val)
569 oldenv = {}
569 oldenv = {}
570 for k in environ:
570 for k in environ:
571 oldenv[k] = os.environ.get(k)
571 oldenv[k] = os.environ.get(k)
572 if cwd is not None:
572 if cwd is not None:
573 oldcwd = os.getcwd()
573 oldcwd = os.getcwd()
574 origcmd = cmd
574 origcmd = cmd
575 if os.name == 'nt':
575 if os.name == 'nt':
576 cmd = '"%s"' % cmd
576 cmd = '"%s"' % cmd
577 try:
577 try:
578 for k, v in environ.iteritems():
578 for k, v in environ.iteritems():
579 os.environ[k] = py2shell(v)
579 os.environ[k] = py2shell(v)
580 if 'HG' not in os.environ:
580 if 'HG' not in os.environ:
581 os.environ['HG'] = _hgexecutable
581 os.environ['HG'] = _hgexecutable
582 if cwd is not None and oldcwd != cwd:
582 if cwd is not None and oldcwd != cwd:
583 os.chdir(cwd)
583 os.chdir(cwd)
584 rc = os.system(cmd)
584 rc = os.system(cmd)
585 if sys.platform == 'OpenVMS' and rc & 1:
585 if sys.platform == 'OpenVMS' and rc & 1:
586 rc = 0
586 rc = 0
587 if rc and onerr:
587 if rc and onerr:
588 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
588 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
589 explain_exit(rc)[0])
589 explain_exit(rc)[0])
590 if errprefix:
590 if errprefix:
591 errmsg = '%s: %s' % (errprefix, errmsg)
591 errmsg = '%s: %s' % (errprefix, errmsg)
592 try:
592 try:
593 onerr.warn(errmsg + '\n')
593 onerr.warn(errmsg + '\n')
594 except AttributeError:
594 except AttributeError:
595 raise onerr(errmsg)
595 raise onerr(errmsg)
596 return rc
596 return rc
597 finally:
597 finally:
598 for k, v in oldenv.iteritems():
598 for k, v in oldenv.iteritems():
599 if v is None:
599 if v is None:
600 del os.environ[k]
600 del os.environ[k]
601 else:
601 else:
602 os.environ[k] = v
602 os.environ[k] = v
603 if cwd is not None and oldcwd != cwd:
603 if cwd is not None and oldcwd != cwd:
604 os.chdir(oldcwd)
604 os.chdir(oldcwd)
605
605
606 # os.path.lexists is not available on python2.3
606 # os.path.lexists is not available on python2.3
607 def lexists(filename):
607 def lexists(filename):
608 "test whether a file with this name exists. does not follow symlinks"
608 "test whether a file with this name exists. does not follow symlinks"
609 try:
609 try:
610 os.lstat(filename)
610 os.lstat(filename)
611 except:
611 except:
612 return False
612 return False
613 return True
613 return True
614
614
615 def rename(src, dst):
615 def rename(src, dst):
616 """forcibly rename a file"""
616 """forcibly rename a file"""
617 try:
617 try:
618 os.rename(src, dst)
618 os.rename(src, dst)
619 except OSError, err:
619 except OSError, err:
620 # on windows, rename to existing file is not allowed, so we
620 # on windows, rename to existing file is not allowed, so we
621 # must delete destination first. but if file is open, unlink
621 # must delete destination first. but if file is open, unlink
622 # schedules it for delete but does not delete it. rename
622 # schedules it for delete but does not delete it. rename
623 # happens immediately even for open files, so we create
623 # happens immediately even for open files, so we create
624 # temporary file, delete it, rename destination to that name,
624 # temporary file, delete it, rename destination to that name,
625 # then delete that. then rename is safe to do.
625 # then delete that. then rename is safe to do.
626 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
626 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
627 os.close(fd)
627 os.close(fd)
628 os.unlink(temp)
628 os.unlink(temp)
629 os.rename(dst, temp)
629 os.rename(dst, temp)
630 os.unlink(temp)
630 os.unlink(temp)
631 os.rename(src, dst)
631 os.rename(src, dst)
632
632
633 def unlink(f):
633 def unlink(f):
634 """unlink and remove the directory if it is empty"""
634 """unlink and remove the directory if it is empty"""
635 os.unlink(f)
635 os.unlink(f)
636 # try removing directories that might now be empty
636 # try removing directories that might now be empty
637 try:
637 try:
638 os.removedirs(os.path.dirname(f))
638 os.removedirs(os.path.dirname(f))
639 except OSError:
639 except OSError:
640 pass
640 pass
641
641
642 def copyfile(src, dest):
642 def copyfile(src, dest):
643 "copy a file, preserving mode"
643 "copy a file, preserving mode"
644 if os.path.islink(src):
644 if os.path.islink(src):
645 try:
645 try:
646 os.unlink(dest)
646 os.unlink(dest)
647 except:
647 except:
648 pass
648 pass
649 os.symlink(os.readlink(src), dest)
649 os.symlink(os.readlink(src), dest)
650 else:
650 else:
651 try:
651 try:
652 shutil.copyfile(src, dest)
652 shutil.copyfile(src, dest)
653 shutil.copymode(src, dest)
653 shutil.copymode(src, dest)
654 except shutil.Error, inst:
654 except shutil.Error, inst:
655 raise Abort(str(inst))
655 raise Abort(str(inst))
656
656
657 def copyfiles(src, dst, hardlink=None):
657 def copyfiles(src, dst, hardlink=None):
658 """Copy a directory tree using hardlinks if possible"""
658 """Copy a directory tree using hardlinks if possible"""
659
659
660 if hardlink is None:
660 if hardlink is None:
661 hardlink = (os.stat(src).st_dev ==
661 hardlink = (os.stat(src).st_dev ==
662 os.stat(os.path.dirname(dst)).st_dev)
662 os.stat(os.path.dirname(dst)).st_dev)
663
663
664 if os.path.isdir(src):
664 if os.path.isdir(src):
665 os.mkdir(dst)
665 os.mkdir(dst)
666 for name in os.listdir(src):
666 for name in os.listdir(src):
667 srcname = os.path.join(src, name)
667 srcname = os.path.join(src, name)
668 dstname = os.path.join(dst, name)
668 dstname = os.path.join(dst, name)
669 copyfiles(srcname, dstname, hardlink)
669 copyfiles(srcname, dstname, hardlink)
670 else:
670 else:
671 if hardlink:
671 if hardlink:
672 try:
672 try:
673 os_link(src, dst)
673 os_link(src, dst)
674 except (IOError, OSError):
674 except (IOError, OSError):
675 hardlink = False
675 hardlink = False
676 shutil.copy(src, dst)
676 shutil.copy(src, dst)
677 else:
677 else:
678 shutil.copy(src, dst)
678 shutil.copy(src, dst)
679
679
680 def audit_path(path):
680 def audit_path(path):
681 """Abort if path contains dangerous components"""
681 """Abort if path contains dangerous components"""
682 parts = os.path.normcase(path).split(os.sep)
682 parts = os.path.normcase(path).split(os.sep)
683 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
683 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
684 or os.pardir in parts):
684 or os.pardir in parts):
685 raise Abort(_("path contains illegal component: %s") % path)
685 raise Abort(_("path contains illegal component: %s") % path)
686
686
687 def _makelock_file(info, pathname):
687 def _makelock_file(info, pathname):
688 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
688 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
689 os.write(ld, info)
689 os.write(ld, info)
690 os.close(ld)
690 os.close(ld)
691
691
692 def _readlock_file(pathname):
692 def _readlock_file(pathname):
693 return posixfile(pathname).read()
693 return posixfile(pathname).read()
694
694
695 def nlinks(pathname):
695 def nlinks(pathname):
696 """Return number of hardlinks for the given file."""
696 """Return number of hardlinks for the given file."""
697 return os.lstat(pathname).st_nlink
697 return os.lstat(pathname).st_nlink
698
698
699 if hasattr(os, 'link'):
699 if hasattr(os, 'link'):
700 os_link = os.link
700 os_link = os.link
701 else:
701 else:
702 def os_link(src, dst):
702 def os_link(src, dst):
703 raise OSError(0, _("Hardlinks not supported"))
703 raise OSError(0, _("Hardlinks not supported"))
704
704
705 def fstat(fp):
705 def fstat(fp):
706 '''stat file object that may not have fileno method.'''
706 '''stat file object that may not have fileno method.'''
707 try:
707 try:
708 return os.fstat(fp.fileno())
708 return os.fstat(fp.fileno())
709 except AttributeError:
709 except AttributeError:
710 return os.stat(fp.name)
710 return os.stat(fp.name)
711
711
712 posixfile = file
712 posixfile = file
713
713
714 def is_win_9x():
714 def is_win_9x():
715 '''return true if run on windows 95, 98 or me.'''
715 '''return true if run on windows 95, 98 or me.'''
716 try:
716 try:
717 return sys.getwindowsversion()[3] == 1
717 return sys.getwindowsversion()[3] == 1
718 except AttributeError:
718 except AttributeError:
719 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
719 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
720
720
721 getuser_fallback = None
721 getuser_fallback = None
722
722
723 def getuser():
723 def getuser():
724 '''return name of current user'''
724 '''return name of current user'''
725 try:
725 try:
726 return getpass.getuser()
726 return getpass.getuser()
727 except ImportError:
727 except ImportError:
728 # import of pwd will fail on windows - try fallback
728 # import of pwd will fail on windows - try fallback
729 if getuser_fallback:
729 if getuser_fallback:
730 return getuser_fallback()
730 return getuser_fallback()
731 # raised if win32api not available
731 # raised if win32api not available
732 raise Abort(_('user name not available - set USERNAME '
732 raise Abort(_('user name not available - set USERNAME '
733 'environment variable'))
733 'environment variable'))
734
734
735 def username(uid=None):
735 def username(uid=None):
736 """Return the name of the user with the given uid.
736 """Return the name of the user with the given uid.
737
737
738 If uid is None, return the name of the current user."""
738 If uid is None, return the name of the current user."""
739 try:
739 try:
740 import pwd
740 import pwd
741 if uid is None:
741 if uid is None:
742 uid = os.getuid()
742 uid = os.getuid()
743 try:
743 try:
744 return pwd.getpwuid(uid)[0]
744 return pwd.getpwuid(uid)[0]
745 except KeyError:
745 except KeyError:
746 return str(uid)
746 return str(uid)
747 except ImportError:
747 except ImportError:
748 return None
748 return None
749
749
750 def groupname(gid=None):
750 def groupname(gid=None):
751 """Return the name of the group with the given gid.
751 """Return the name of the group with the given gid.
752
752
753 If gid is None, return the name of the current group."""
753 If gid is None, return the name of the current group."""
754 try:
754 try:
755 import grp
755 import grp
756 if gid is None:
756 if gid is None:
757 gid = os.getgid()
757 gid = os.getgid()
758 try:
758 try:
759 return grp.getgrgid(gid)[0]
759 return grp.getgrgid(gid)[0]
760 except KeyError:
760 except KeyError:
761 return str(gid)
761 return str(gid)
762 except ImportError:
762 except ImportError:
763 return None
763 return None
764
764
765 # File system features
765 # File system features
766
766
767 def checkfolding(path):
767 def checkfolding(path):
768 """
768 """
769 Check whether the given path is on a case-sensitive filesystem
769 Check whether the given path is on a case-sensitive filesystem
770
770
771 Requires a path (like /foo/.hg) ending with a foldable final
771 Requires a path (like /foo/.hg) ending with a foldable final
772 directory component.
772 directory component.
773 """
773 """
774 s1 = os.stat(path)
774 s1 = os.stat(path)
775 d, b = os.path.split(path)
775 d, b = os.path.split(path)
776 p2 = os.path.join(d, b.upper())
776 p2 = os.path.join(d, b.upper())
777 if path == p2:
777 if path == p2:
778 p2 = os.path.join(d, b.lower())
778 p2 = os.path.join(d, b.lower())
779 try:
779 try:
780 s2 = os.stat(p2)
780 s2 = os.stat(p2)
781 if s2 == s1:
781 if s2 == s1:
782 return False
782 return False
783 return True
783 return True
784 except:
784 except:
785 return True
785 return True
786
786
787 def checkexec(path):
787 def checkexec(path):
788 """
788 """
789 Check whether the given path is on a filesystem with UNIX-like exec flags
789 Check whether the given path is on a filesystem with UNIX-like exec flags
790
790
791 Requires a directory (like /foo/.hg)
791 Requires a directory (like /foo/.hg)
792 """
792 """
793 fh, fn = tempfile.mkstemp("", "", path)
793 fh, fn = tempfile.mkstemp("", "", path)
794 os.close(fh)
794 os.close(fh)
795 m = os.stat(fn).st_mode
795 m = os.stat(fn).st_mode
796 os.chmod(fn, m ^ 0111)
796 os.chmod(fn, m ^ 0111)
797 r = (os.stat(fn).st_mode != m)
797 r = (os.stat(fn).st_mode != m)
798 os.unlink(fn)
798 os.unlink(fn)
799 return r
799 return r
800
800
801 def execfunc(path, fallback):
801 def execfunc(path, fallback):
802 '''return an is_exec() function with default to fallback'''
802 '''return an is_exec() function with default to fallback'''
803 if checkexec(path):
803 if checkexec(path):
804 return lambda x: is_exec(os.path.join(path, x))
804 return lambda x: is_exec(os.path.join(path, x))
805 return fallback
805 return fallback
806
806
807 def checklink(path):
807 def checklink(path):
808 """check whether the given path is on a symlink-capable filesystem"""
808 """check whether the given path is on a symlink-capable filesystem"""
809 # mktemp is not racy because symlink creation will fail if the
809 # mktemp is not racy because symlink creation will fail if the
810 # file already exists
810 # file already exists
811 name = tempfile.mktemp(dir=path)
811 name = tempfile.mktemp(dir=path)
812 try:
812 try:
813 os.symlink(".", name)
813 os.symlink(".", name)
814 os.unlink(name)
814 os.unlink(name)
815 return True
815 return True
816 except (OSError, AttributeError):
816 except (OSError, AttributeError):
817 return False
817 return False
818
818
819 def linkfunc(path, fallback):
819 def linkfunc(path, fallback):
820 '''return an is_link() function with default to fallback'''
820 '''return an is_link() function with default to fallback'''
821 if checklink(path):
821 if checklink(path):
822 return lambda x: os.path.islink(os.path.join(path, x))
822 return lambda x: os.path.islink(os.path.join(path, x))
823 return fallback
823 return fallback
824
824
825 _umask = os.umask(0)
825 _umask = os.umask(0)
826 os.umask(_umask)
826 os.umask(_umask)
827
827
828 def needbinarypatch():
828 def needbinarypatch():
829 """return True if patches should be applied in binary mode by default."""
829 """return True if patches should be applied in binary mode by default."""
830 return os.name == 'nt'
830 return os.name == 'nt'
831
831
832 # Platform specific variants
832 # Platform specific variants
833 if os.name == 'nt':
833 if os.name == 'nt':
834 import msvcrt
834 import msvcrt
835 nulldev = 'NUL:'
835 nulldev = 'NUL:'
836
836
837 class winstdout:
837 class winstdout:
838 '''stdout on windows misbehaves if sent through a pipe'''
838 '''stdout on windows misbehaves if sent through a pipe'''
839
839
840 def __init__(self, fp):
840 def __init__(self, fp):
841 self.fp = fp
841 self.fp = fp
842
842
843 def __getattr__(self, key):
843 def __getattr__(self, key):
844 return getattr(self.fp, key)
844 return getattr(self.fp, key)
845
845
846 def close(self):
846 def close(self):
847 try:
847 try:
848 self.fp.close()
848 self.fp.close()
849 except: pass
849 except: pass
850
850
851 def write(self, s):
851 def write(self, s):
852 try:
852 try:
853 return self.fp.write(s)
853 return self.fp.write(s)
854 except IOError, inst:
854 except IOError, inst:
855 if inst.errno != 0: raise
855 if inst.errno != 0: raise
856 self.close()
856 self.close()
857 raise IOError(errno.EPIPE, 'Broken pipe')
857 raise IOError(errno.EPIPE, 'Broken pipe')
858
858
859 def flush(self):
859 def flush(self):
860 try:
860 try:
861 return self.fp.flush()
861 return self.fp.flush()
862 except IOError, inst:
862 except IOError, inst:
863 if inst.errno != errno.EINVAL: raise
863 if inst.errno != errno.EINVAL: raise
864 self.close()
864 self.close()
865 raise IOError(errno.EPIPE, 'Broken pipe')
865 raise IOError(errno.EPIPE, 'Broken pipe')
866
866
867 sys.stdout = winstdout(sys.stdout)
867 sys.stdout = winstdout(sys.stdout)
868
868
869 def system_rcpath():
869 def system_rcpath():
870 try:
870 try:
871 return system_rcpath_win32()
871 return system_rcpath_win32()
872 except:
872 except:
873 return [r'c:\mercurial\mercurial.ini']
873 return [r'c:\mercurial\mercurial.ini']
874
874
875 def user_rcpath():
875 def user_rcpath():
876 '''return os-specific hgrc search path to the user dir'''
876 '''return os-specific hgrc search path to the user dir'''
877 try:
877 try:
878 userrc = user_rcpath_win32()
878 userrc = user_rcpath_win32()
879 except:
879 except:
880 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
880 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
881 path = [userrc]
881 path = [userrc]
882 userprofile = os.environ.get('USERPROFILE')
882 userprofile = os.environ.get('USERPROFILE')
883 if userprofile:
883 if userprofile:
884 path.append(os.path.join(userprofile, 'mercurial.ini'))
884 path.append(os.path.join(userprofile, 'mercurial.ini'))
885 return path
885 return path
886
886
887 def parse_patch_output(output_line):
887 def parse_patch_output(output_line):
888 """parses the output produced by patch and returns the file name"""
888 """parses the output produced by patch and returns the file name"""
889 pf = output_line[14:]
889 pf = output_line[14:]
890 if pf[0] == '`':
890 if pf[0] == '`':
891 pf = pf[1:-1] # Remove the quotes
891 pf = pf[1:-1] # Remove the quotes
892 return pf
892 return pf
893
893
894 def testpid(pid):
894 def testpid(pid):
895 '''return False if pid dead, True if running or not known'''
895 '''return False if pid dead, True if running or not known'''
896 return True
896 return True
897
897
898 def set_exec(f, mode):
898 def set_exec(f, mode):
899 pass
899 pass
900
900
901 def set_link(f, mode):
901 def set_link(f, mode):
902 pass
902 pass
903
903
904 def set_binary(fd):
904 def set_binary(fd):
905 msvcrt.setmode(fd.fileno(), os.O_BINARY)
905 msvcrt.setmode(fd.fileno(), os.O_BINARY)
906
906
907 def pconvert(path):
907 def pconvert(path):
908 return path.replace("\\", "/")
908 return path.replace("\\", "/")
909
909
910 def localpath(path):
910 def localpath(path):
911 return path.replace('/', '\\')
911 return path.replace('/', '\\')
912
912
913 def normpath(path):
913 def normpath(path):
914 return pconvert(os.path.normpath(path))
914 return pconvert(os.path.normpath(path))
915
915
916 makelock = _makelock_file
916 makelock = _makelock_file
917 readlock = _readlock_file
917 readlock = _readlock_file
918
918
919 def samestat(s1, s2):
919 def samestat(s1, s2):
920 return False
920 return False
921
921
922 # A sequence of backslashes is special iff it precedes a double quote:
922 # A sequence of backslashes is special iff it precedes a double quote:
923 # - if there's an even number of backslashes, the double quote is not
923 # - if there's an even number of backslashes, the double quote is not
924 # quoted (i.e. it ends the quoted region)
924 # quoted (i.e. it ends the quoted region)
925 # - if there's an odd number of backslashes, the double quote is quoted
925 # - if there's an odd number of backslashes, the double quote is quoted
926 # - in both cases, every pair of backslashes is unquoted into a single
926 # - in both cases, every pair of backslashes is unquoted into a single
927 # backslash
927 # backslash
928 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
928 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
929 # So, to quote a string, we must surround it in double quotes, double
929 # So, to quote a string, we must surround it in double quotes, double
930 # the number of backslashes that preceed double quotes and add another
930 # the number of backslashes that preceed double quotes and add another
931 # backslash before every double quote (being careful with the double
931 # backslash before every double quote (being careful with the double
932 # quote we've appended to the end)
932 # quote we've appended to the end)
933 _quotere = None
933 _quotere = None
934 def shellquote(s):
934 def shellquote(s):
935 global _quotere
935 global _quotere
936 if _quotere is None:
936 if _quotere is None:
937 _quotere = re.compile(r'(\\*)("|\\$)')
937 _quotere = re.compile(r'(\\*)("|\\$)')
938 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
938 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
939
939
940 def explain_exit(code):
940 def explain_exit(code):
941 return _("exited with status %d") % code, code
941 return _("exited with status %d") % code, code
942
942
943 # if you change this stub into a real check, please try to implement the
943 # if you change this stub into a real check, please try to implement the
944 # username and groupname functions above, too.
944 # username and groupname functions above, too.
945 def isowner(fp, st=None):
945 def isowner(fp, st=None):
946 return True
946 return True
947
947
948 def find_in_path(name, path, default=None):
948 def find_in_path(name, path, default=None):
949 '''find name in search path. path can be string (will be split
949 '''find name in search path. path can be string (will be split
950 with os.pathsep), or iterable thing that returns strings. if name
950 with os.pathsep), or iterable thing that returns strings. if name
951 found, return path to name. else return default. name is looked up
951 found, return path to name. else return default. name is looked up
952 using cmd.exe rules, using PATHEXT.'''
952 using cmd.exe rules, using PATHEXT.'''
953 if isinstance(path, str):
953 if isinstance(path, str):
954 path = path.split(os.pathsep)
954 path = path.split(os.pathsep)
955
955
956 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
956 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
957 pathext = pathext.lower().split(os.pathsep)
957 pathext = pathext.lower().split(os.pathsep)
958 isexec = os.path.splitext(name)[1].lower() in pathext
958 isexec = os.path.splitext(name)[1].lower() in pathext
959
959
960 for p in path:
960 for p in path:
961 p_name = os.path.join(p, name)
961 p_name = os.path.join(p, name)
962
962
963 if isexec and os.path.exists(p_name):
963 if isexec and os.path.exists(p_name):
964 return p_name
964 return p_name
965
965
966 for ext in pathext:
966 for ext in pathext:
967 p_name_ext = p_name + ext
967 p_name_ext = p_name + ext
968 if os.path.exists(p_name_ext):
968 if os.path.exists(p_name_ext):
969 return p_name_ext
969 return p_name_ext
970 return default
970 return default
971
971
972 def set_signal_handler():
972 def set_signal_handler():
973 try:
973 try:
974 set_signal_handler_win32()
974 set_signal_handler_win32()
975 except NameError:
975 except NameError:
976 pass
976 pass
977
977
978 try:
978 try:
979 # override functions with win32 versions if possible
979 # override functions with win32 versions if possible
980 from util_win32 import *
980 from util_win32 import *
981 if not is_win_9x():
981 if not is_win_9x():
982 posixfile = posixfile_nt
982 posixfile = posixfile_nt
983 except ImportError:
983 except ImportError:
984 pass
984 pass
985
985
986 else:
986 else:
987 nulldev = '/dev/null'
987 nulldev = '/dev/null'
988
988
989 def rcfiles(path):
989 def rcfiles(path):
990 rcs = [os.path.join(path, 'hgrc')]
990 rcs = [os.path.join(path, 'hgrc')]
991 rcdir = os.path.join(path, 'hgrc.d')
991 rcdir = os.path.join(path, 'hgrc.d')
992 try:
992 try:
993 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
993 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
994 if f.endswith(".rc")])
994 if f.endswith(".rc")])
995 except OSError:
995 except OSError:
996 pass
996 pass
997 return rcs
997 return rcs
998
998
999 def system_rcpath():
999 def system_rcpath():
1000 path = []
1000 path = []
1001 # old mod_python does not set sys.argv
1001 # old mod_python does not set sys.argv
1002 if len(getattr(sys, 'argv', [])) > 0:
1002 if len(getattr(sys, 'argv', [])) > 0:
1003 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1003 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1004 '/../etc/mercurial'))
1004 '/../etc/mercurial'))
1005 path.extend(rcfiles('/etc/mercurial'))
1005 path.extend(rcfiles('/etc/mercurial'))
1006 return path
1006 return path
1007
1007
1008 def user_rcpath():
1008 def user_rcpath():
1009 return [os.path.expanduser('~/.hgrc')]
1009 return [os.path.expanduser('~/.hgrc')]
1010
1010
1011 def parse_patch_output(output_line):
1011 def parse_patch_output(output_line):
1012 """parses the output produced by patch and returns the file name"""
1012 """parses the output produced by patch and returns the file name"""
1013 pf = output_line[14:]
1013 pf = output_line[14:]
1014 if os.sys.platform == 'OpenVMS':
1014 if os.sys.platform == 'OpenVMS':
1015 if pf[0] == '`':
1015 if pf[0] == '`':
1016 pf = pf[1:-1] # Remove the quotes
1016 pf = pf[1:-1] # Remove the quotes
1017 else:
1017 else:
1018 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1018 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1019 pf = pf[1:-1] # Remove the quotes
1019 pf = pf[1:-1] # Remove the quotes
1020 return pf
1020 return pf
1021
1021
1022 def is_exec(f):
1022 def is_exec(f):
1023 """check whether a file is executable"""
1023 """check whether a file is executable"""
1024 return (os.lstat(f).st_mode & 0100 != 0)
1024 return (os.lstat(f).st_mode & 0100 != 0)
1025
1025
1026 def set_exec(f, mode):
1026 def set_exec(f, mode):
1027 s = os.lstat(f).st_mode
1027 s = os.lstat(f).st_mode
1028 if (s & 0100 != 0) == mode:
1028 if (s & 0100 != 0) == mode:
1029 return
1029 return
1030 if mode:
1030 if mode:
1031 # Turn on +x for every +r bit when making a file executable
1031 # Turn on +x for every +r bit when making a file executable
1032 # and obey umask.
1032 # and obey umask.
1033 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1033 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1034 else:
1034 else:
1035 os.chmod(f, s & 0666)
1035 os.chmod(f, s & 0666)
1036
1036
1037 def set_link(f, mode):
1037 def set_link(f, mode):
1038 """make a file a symbolic link/regular file
1038 """make a file a symbolic link/regular file
1039
1039
1040 if a file is changed to a link, its contents become the link data
1040 if a file is changed to a link, its contents become the link data
1041 if a link is changed to a file, its link data become its contents
1041 if a link is changed to a file, its link data become its contents
1042 """
1042 """
1043
1043
1044 m = os.path.islink(f)
1044 m = os.path.islink(f)
1045 if m == bool(mode):
1045 if m == bool(mode):
1046 return
1046 return
1047
1047
1048 if mode: # switch file to link
1048 if mode: # switch file to link
1049 data = file(f).read()
1049 data = file(f).read()
1050 os.unlink(f)
1050 os.unlink(f)
1051 os.symlink(data, f)
1051 os.symlink(data, f)
1052 else:
1052 else:
1053 data = os.readlink(f)
1053 data = os.readlink(f)
1054 os.unlink(f)
1054 os.unlink(f)
1055 file(f, "w").write(data)
1055 file(f, "w").write(data)
1056
1056
1057 def set_binary(fd):
1057 def set_binary(fd):
1058 pass
1058 pass
1059
1059
1060 def pconvert(path):
1060 def pconvert(path):
1061 return path
1061 return path
1062
1062
1063 def localpath(path):
1063 def localpath(path):
1064 return path
1064 return path
1065
1065
1066 normpath = os.path.normpath
1066 normpath = os.path.normpath
1067 samestat = os.path.samestat
1067 samestat = os.path.samestat
1068
1068
1069 def makelock(info, pathname):
1069 def makelock(info, pathname):
1070 try:
1070 try:
1071 os.symlink(info, pathname)
1071 os.symlink(info, pathname)
1072 except OSError, why:
1072 except OSError, why:
1073 if why.errno == errno.EEXIST:
1073 if why.errno == errno.EEXIST:
1074 raise
1074 raise
1075 else:
1075 else:
1076 _makelock_file(info, pathname)
1076 _makelock_file(info, pathname)
1077
1077
1078 def readlock(pathname):
1078 def readlock(pathname):
1079 try:
1079 try:
1080 return os.readlink(pathname)
1080 return os.readlink(pathname)
1081 except OSError, why:
1081 except OSError, why:
1082 if why.errno in (errno.EINVAL, errno.ENOSYS):
1082 if why.errno in (errno.EINVAL, errno.ENOSYS):
1083 return _readlock_file(pathname)
1083 return _readlock_file(pathname)
1084 else:
1084 else:
1085 raise
1085 raise
1086
1086
1087 def shellquote(s):
1087 def shellquote(s):
1088 if os.sys.platform == 'OpenVMS':
1088 if os.sys.platform == 'OpenVMS':
1089 return '"%s"' % s
1089 return '"%s"' % s
1090 else:
1090 else:
1091 return "'%s'" % s.replace("'", "'\\''")
1091 return "'%s'" % s.replace("'", "'\\''")
1092
1092
1093 def testpid(pid):
1093 def testpid(pid):
1094 '''return False if pid dead, True if running or not sure'''
1094 '''return False if pid dead, True if running or not sure'''
1095 if os.sys.platform == 'OpenVMS':
1095 if os.sys.platform == 'OpenVMS':
1096 return True
1096 return True
1097 try:
1097 try:
1098 os.kill(pid, 0)
1098 os.kill(pid, 0)
1099 return True
1099 return True
1100 except OSError, inst:
1100 except OSError, inst:
1101 return inst.errno != errno.ESRCH
1101 return inst.errno != errno.ESRCH
1102
1102
1103 def explain_exit(code):
1103 def explain_exit(code):
1104 """return a 2-tuple (desc, code) describing a process's status"""
1104 """return a 2-tuple (desc, code) describing a process's status"""
1105 if os.WIFEXITED(code):
1105 if os.WIFEXITED(code):
1106 val = os.WEXITSTATUS(code)
1106 val = os.WEXITSTATUS(code)
1107 return _("exited with status %d") % val, val
1107 return _("exited with status %d") % val, val
1108 elif os.WIFSIGNALED(code):
1108 elif os.WIFSIGNALED(code):
1109 val = os.WTERMSIG(code)
1109 val = os.WTERMSIG(code)
1110 return _("killed by signal %d") % val, val
1110 return _("killed by signal %d") % val, val
1111 elif os.WIFSTOPPED(code):
1111 elif os.WIFSTOPPED(code):
1112 val = os.WSTOPSIG(code)
1112 val = os.WSTOPSIG(code)
1113 return _("stopped by signal %d") % val, val
1113 return _("stopped by signal %d") % val, val
1114 raise ValueError(_("invalid exit code"))
1114 raise ValueError(_("invalid exit code"))
1115
1115
1116 def isowner(fp, st=None):
1116 def isowner(fp, st=None):
1117 """Return True if the file object f belongs to the current user.
1117 """Return True if the file object f belongs to the current user.
1118
1118
1119 The return value of a util.fstat(f) may be passed as the st argument.
1119 The return value of a util.fstat(f) may be passed as the st argument.
1120 """
1120 """
1121 if st is None:
1121 if st is None:
1122 st = fstat(fp)
1122 st = fstat(fp)
1123 return st.st_uid == os.getuid()
1123 return st.st_uid == os.getuid()
1124
1124
1125 def find_in_path(name, path, default=None):
1125 def find_in_path(name, path, default=None):
1126 '''find name in search path. path can be string (will be split
1126 '''find name in search path. path can be string (will be split
1127 with os.pathsep), or iterable thing that returns strings. if name
1127 with os.pathsep), or iterable thing that returns strings. if name
1128 found, return path to name. else return default.'''
1128 found, return path to name. else return default.'''
1129 if isinstance(path, str):
1129 if isinstance(path, str):
1130 path = path.split(os.pathsep)
1130 path = path.split(os.pathsep)
1131 for p in path:
1131 for p in path:
1132 p_name = os.path.join(p, name)
1132 p_name = os.path.join(p, name)
1133 if os.path.exists(p_name):
1133 if os.path.exists(p_name):
1134 return p_name
1134 return p_name
1135 return default
1135 return default
1136
1136
1137 def set_signal_handler():
1137 def set_signal_handler():
1138 pass
1138 pass
1139
1139
1140 def find_exe(name, default=None):
1140 def find_exe(name, default=None):
1141 '''find path of an executable.
1141 '''find path of an executable.
1142 if name contains a path component, return it as is. otherwise,
1142 if name contains a path component, return it as is. otherwise,
1143 use normal executable search path.'''
1143 use normal executable search path.'''
1144
1144
1145 if os.sep in name or sys.platform == 'OpenVMS':
1145 if os.sep in name or sys.platform == 'OpenVMS':
1146 # don't check the executable bit. if the file isn't
1146 # don't check the executable bit. if the file isn't
1147 # executable, whoever tries to actually run it will give a
1147 # executable, whoever tries to actually run it will give a
1148 # much more useful error message.
1148 # much more useful error message.
1149 return name
1149 return name
1150 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1150 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1151
1151
1152 def _buildencodefun():
1152 def _buildencodefun():
1153 e = '_'
1153 e = '_'
1154 win_reserved = [ord(x) for x in '\\:*?"<>|']
1154 win_reserved = [ord(x) for x in '\\:*?"<>|']
1155 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1155 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1156 for x in (range(32) + range(126, 256) + win_reserved):
1156 for x in (range(32) + range(126, 256) + win_reserved):
1157 cmap[chr(x)] = "~%02x" % x
1157 cmap[chr(x)] = "~%02x" % x
1158 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1158 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1159 cmap[chr(x)] = e + chr(x).lower()
1159 cmap[chr(x)] = e + chr(x).lower()
1160 dmap = {}
1160 dmap = {}
1161 for k, v in cmap.iteritems():
1161 for k, v in cmap.iteritems():
1162 dmap[v] = k
1162 dmap[v] = k
1163 def decode(s):
1163 def decode(s):
1164 i = 0
1164 i = 0
1165 while i < len(s):
1165 while i < len(s):
1166 for l in xrange(1, 4):
1166 for l in xrange(1, 4):
1167 try:
1167 try:
1168 yield dmap[s[i:i+l]]
1168 yield dmap[s[i:i+l]]
1169 i += l
1169 i += l
1170 break
1170 break
1171 except KeyError:
1171 except KeyError:
1172 pass
1172 pass
1173 else:
1173 else:
1174 raise KeyError
1174 raise KeyError
1175 return (lambda s: "".join([cmap[c] for c in s]),
1175 return (lambda s: "".join([cmap[c] for c in s]),
1176 lambda s: "".join(list(decode(s))))
1176 lambda s: "".join(list(decode(s))))
1177
1177
1178 encodefilename, decodefilename = _buildencodefun()
1178 encodefilename, decodefilename = _buildencodefun()
1179
1179
1180 def encodedopener(openerfn, fn):
1180 def encodedopener(openerfn, fn):
1181 def o(path, *args, **kw):
1181 def o(path, *args, **kw):
1182 return openerfn(fn(path), *args, **kw)
1182 return openerfn(fn(path), *args, **kw)
1183 return o
1183 return o
1184
1184
1185 def mktempcopy(name, emptyok=False):
1185 def mktempcopy(name, emptyok=False):
1186 """Create a temporary file with the same contents from name
1186 """Create a temporary file with the same contents from name
1187
1187
1188 The permission bits are copied from the original file.
1188 The permission bits are copied from the original file.
1189
1189
1190 If the temporary file is going to be truncated immediately, you
1190 If the temporary file is going to be truncated immediately, you
1191 can use emptyok=True as an optimization.
1191 can use emptyok=True as an optimization.
1192
1192
1193 Returns the name of the temporary file.
1193 Returns the name of the temporary file.
1194 """
1194 """
1195 d, fn = os.path.split(name)
1195 d, fn = os.path.split(name)
1196 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1196 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1197 os.close(fd)
1197 os.close(fd)
1198 # Temporary files are created with mode 0600, which is usually not
1198 # Temporary files are created with mode 0600, which is usually not
1199 # what we want. If the original file already exists, just copy
1199 # what we want. If the original file already exists, just copy
1200 # its mode. Otherwise, manually obey umask.
1200 # its mode. Otherwise, manually obey umask.
1201 try:
1201 try:
1202 st_mode = os.lstat(name).st_mode
1202 st_mode = os.lstat(name).st_mode
1203 except OSError, inst:
1203 except OSError, inst:
1204 if inst.errno != errno.ENOENT:
1204 if inst.errno != errno.ENOENT:
1205 raise
1205 raise
1206 st_mode = 0666 & ~_umask
1206 st_mode = 0666 & ~_umask
1207 os.chmod(temp, st_mode)
1207 os.chmod(temp, st_mode)
1208 if emptyok:
1208 if emptyok:
1209 return temp
1209 return temp
1210 try:
1210 try:
1211 try:
1211 try:
1212 ifp = posixfile(name, "rb")
1212 ifp = posixfile(name, "rb")
1213 except IOError, inst:
1213 except IOError, inst:
1214 if inst.errno == errno.ENOENT:
1214 if inst.errno == errno.ENOENT:
1215 return temp
1215 return temp
1216 if not getattr(inst, 'filename', None):
1216 if not getattr(inst, 'filename', None):
1217 inst.filename = name
1217 inst.filename = name
1218 raise
1218 raise
1219 ofp = posixfile(temp, "wb")
1219 ofp = posixfile(temp, "wb")
1220 for chunk in filechunkiter(ifp):
1220 for chunk in filechunkiter(ifp):
1221 ofp.write(chunk)
1221 ofp.write(chunk)
1222 ifp.close()
1222 ifp.close()
1223 ofp.close()
1223 ofp.close()
1224 except:
1224 except:
1225 try: os.unlink(temp)
1225 try: os.unlink(temp)
1226 except: pass
1226 except: pass
1227 raise
1227 raise
1228 return temp
1228 return temp
1229
1229
1230 class atomictempfile(posixfile):
1230 class atomictempfile(posixfile):
1231 """file-like object that atomically updates a file
1231 """file-like object that atomically updates a file
1232
1232
1233 All writes will be redirected to a temporary copy of the original
1233 All writes will be redirected to a temporary copy of the original
1234 file. When rename is called, the copy is renamed to the original
1234 file. When rename is called, the copy is renamed to the original
1235 name, making the changes visible.
1235 name, making the changes visible.
1236 """
1236 """
1237 def __init__(self, name, mode):
1237 def __init__(self, name, mode):
1238 self.__name = name
1238 self.__name = name
1239 self.temp = mktempcopy(name, emptyok=('w' in mode))
1239 self.temp = mktempcopy(name, emptyok=('w' in mode))
1240 posixfile.__init__(self, self.temp, mode)
1240 posixfile.__init__(self, self.temp, mode)
1241
1241
1242 def rename(self):
1242 def rename(self):
1243 if not self.closed:
1243 if not self.closed:
1244 posixfile.close(self)
1244 posixfile.close(self)
1245 rename(self.temp, localpath(self.__name))
1245 rename(self.temp, localpath(self.__name))
1246
1246
1247 def __del__(self):
1247 def __del__(self):
1248 if not self.closed:
1248 if not self.closed:
1249 try:
1249 try:
1250 os.unlink(self.temp)
1250 os.unlink(self.temp)
1251 except: pass
1251 except: pass
1252 posixfile.close(self)
1252 posixfile.close(self)
1253
1253
1254 class opener(object):
1254 class opener(object):
1255 """Open files relative to a base directory
1255 """Open files relative to a base directory
1256
1256
1257 This class is used to hide the details of COW semantics and
1257 This class is used to hide the details of COW semantics and
1258 remote file access from higher level code.
1258 remote file access from higher level code.
1259 """
1259 """
1260 def __init__(self, base, audit=True):
1260 def __init__(self, base, audit=True):
1261 self.base = base
1261 self.base = base
1262 self.audit = audit
1262 self.audit = audit
1263
1263
1264 def __getattr__(self, name):
1264 def __getattr__(self, name):
1265 if name == '_can_symlink':
1265 if name == '_can_symlink':
1266 self._can_symlink = checklink(self.base)
1266 self._can_symlink = checklink(self.base)
1267 return self._can_symlink
1267 return self._can_symlink
1268 raise AttributeError(name)
1268 raise AttributeError(name)
1269
1269
1270 def __call__(self, path, mode="r", text=False, atomictemp=False):
1270 def __call__(self, path, mode="r", text=False, atomictemp=False):
1271 if self.audit:
1271 if self.audit:
1272 audit_path(path)
1272 audit_path(path)
1273 f = os.path.join(self.base, path)
1273 f = os.path.join(self.base, path)
1274
1274
1275 if not text and "b" not in mode:
1275 if not text and "b" not in mode:
1276 mode += "b" # for that other OS
1276 mode += "b" # for that other OS
1277
1277
1278 if mode[0] != "r":
1278 if mode[0] != "r":
1279 try:
1279 try:
1280 nlink = nlinks(f)
1280 nlink = nlinks(f)
1281 except OSError:
1281 except OSError:
1282 nlink = 0
1282 nlink = 0
1283 d = os.path.dirname(f)
1283 d = os.path.dirname(f)
1284 if not os.path.isdir(d):
1284 if not os.path.isdir(d):
1285 os.makedirs(d)
1285 os.makedirs(d)
1286 if atomictemp:
1286 if atomictemp:
1287 return atomictempfile(f, mode)
1287 return atomictempfile(f, mode)
1288 if nlink > 1:
1288 if nlink > 1:
1289 rename(mktempcopy(f), f)
1289 rename(mktempcopy(f), f)
1290 return posixfile(f, mode)
1290 return posixfile(f, mode)
1291
1291
1292 def symlink(self, src, dst):
1292 def symlink(self, src, dst):
1293 if self.audit:
1293 if self.audit:
1294 audit_path(dst)
1294 audit_path(dst)
1295 linkname = os.path.join(self.base, dst)
1295 linkname = os.path.join(self.base, dst)
1296 try:
1296 try:
1297 os.unlink(linkname)
1297 os.unlink(linkname)
1298 except OSError:
1298 except OSError:
1299 pass
1299 pass
1300
1300
1301 dirname = os.path.dirname(linkname)
1301 dirname = os.path.dirname(linkname)
1302 if not os.path.exists(dirname):
1302 if not os.path.exists(dirname):
1303 os.makedirs(dirname)
1303 os.makedirs(dirname)
1304
1304
1305 if self._can_symlink:
1305 if self._can_symlink:
1306 os.symlink(src, linkname)
1306 os.symlink(src, linkname)
1307 else:
1307 else:
1308 f = self(self, dst, "w")
1308 f = self(self, dst, "w")
1309 f.write(src)
1309 f.write(src)
1310 f.close()
1310 f.close()
1311
1311
1312 class chunkbuffer(object):
1312 class chunkbuffer(object):
1313 """Allow arbitrary sized chunks of data to be efficiently read from an
1313 """Allow arbitrary sized chunks of data to be efficiently read from an
1314 iterator over chunks of arbitrary size."""
1314 iterator over chunks of arbitrary size."""
1315
1315
1316 def __init__(self, in_iter, targetsize = 2**16):
1316 def __init__(self, in_iter, targetsize = 2**16):
1317 """in_iter is the iterator that's iterating over the input chunks.
1317 """in_iter is the iterator that's iterating over the input chunks.
1318 targetsize is how big a buffer to try to maintain."""
1318 targetsize is how big a buffer to try to maintain."""
1319 self.in_iter = iter(in_iter)
1319 self.in_iter = iter(in_iter)
1320 self.buf = ''
1320 self.buf = ''
1321 self.targetsize = int(targetsize)
1321 self.targetsize = int(targetsize)
1322 if self.targetsize <= 0:
1322 if self.targetsize <= 0:
1323 raise ValueError(_("targetsize must be greater than 0, was %d") %
1323 raise ValueError(_("targetsize must be greater than 0, was %d") %
1324 targetsize)
1324 targetsize)
1325 self.iterempty = False
1325 self.iterempty = False
1326
1326
1327 def fillbuf(self):
1327 def fillbuf(self):
1328 """Ignore target size; read every chunk from iterator until empty."""
1328 """Ignore target size; read every chunk from iterator until empty."""
1329 if not self.iterempty:
1329 if not self.iterempty:
1330 collector = cStringIO.StringIO()
1330 collector = cStringIO.StringIO()
1331 collector.write(self.buf)
1331 collector.write(self.buf)
1332 for ch in self.in_iter:
1332 for ch in self.in_iter:
1333 collector.write(ch)
1333 collector.write(ch)
1334 self.buf = collector.getvalue()
1334 self.buf = collector.getvalue()
1335 self.iterempty = True
1335 self.iterempty = True
1336
1336
1337 def read(self, l):
1337 def read(self, l):
1338 """Read L bytes of data from the iterator of chunks of data.
1338 """Read L bytes of data from the iterator of chunks of data.
1339 Returns less than L bytes if the iterator runs dry."""
1339 Returns less than L bytes if the iterator runs dry."""
1340 if l > len(self.buf) and not self.iterempty:
1340 if l > len(self.buf) and not self.iterempty:
1341 # Clamp to a multiple of self.targetsize
1341 # Clamp to a multiple of self.targetsize
1342 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1342 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1343 collector = cStringIO.StringIO()
1343 collector = cStringIO.StringIO()
1344 collector.write(self.buf)
1344 collector.write(self.buf)
1345 collected = len(self.buf)
1345 collected = len(self.buf)
1346 for chunk in self.in_iter:
1346 for chunk in self.in_iter:
1347 collector.write(chunk)
1347 collector.write(chunk)
1348 collected += len(chunk)
1348 collected += len(chunk)
1349 if collected >= targetsize:
1349 if collected >= targetsize:
1350 break
1350 break
1351 if collected < targetsize:
1351 if collected < targetsize:
1352 self.iterempty = True
1352 self.iterempty = True
1353 self.buf = collector.getvalue()
1353 self.buf = collector.getvalue()
1354 s, self.buf = self.buf[:l], buffer(self.buf, l)
1354 s, self.buf = self.buf[:l], buffer(self.buf, l)
1355 return s
1355 return s
1356
1356
1357 def filechunkiter(f, size=65536, limit=None):
1357 def filechunkiter(f, size=65536, limit=None):
1358 """Create a generator that produces the data in the file size
1358 """Create a generator that produces the data in the file size
1359 (default 65536) bytes at a time, up to optional limit (default is
1359 (default 65536) bytes at a time, up to optional limit (default is
1360 to read all data). Chunks may be less than size bytes if the
1360 to read all data). Chunks may be less than size bytes if the
1361 chunk is the last chunk in the file, or the file is a socket or
1361 chunk is the last chunk in the file, or the file is a socket or
1362 some other type of file that sometimes reads less data than is
1362 some other type of file that sometimes reads less data than is
1363 requested."""
1363 requested."""
1364 assert size >= 0
1364 assert size >= 0
1365 assert limit is None or limit >= 0
1365 assert limit is None or limit >= 0
1366 while True:
1366 while True:
1367 if limit is None: nbytes = size
1367 if limit is None: nbytes = size
1368 else: nbytes = min(limit, size)
1368 else: nbytes = min(limit, size)
1369 s = nbytes and f.read(nbytes)
1369 s = nbytes and f.read(nbytes)
1370 if not s: break
1370 if not s: break
1371 if limit: limit -= len(s)
1371 if limit: limit -= len(s)
1372 yield s
1372 yield s
1373
1373
1374 def makedate():
1374 def makedate():
1375 lt = time.localtime()
1375 lt = time.localtime()
1376 if lt[8] == 1 and time.daylight:
1376 if lt[8] == 1 and time.daylight:
1377 tz = time.altzone
1377 tz = time.altzone
1378 else:
1378 else:
1379 tz = time.timezone
1379 tz = time.timezone
1380 return time.mktime(lt), tz
1380 return time.mktime(lt), tz
1381
1381
1382 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1382 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1383 """represent a (unixtime, offset) tuple as a localized time.
1383 """represent a (unixtime, offset) tuple as a localized time.
1384 unixtime is seconds since the epoch, and offset is the time zone's
1384 unixtime is seconds since the epoch, and offset is the time zone's
1385 number of seconds away from UTC. if timezone is false, do not
1385 number of seconds away from UTC. if timezone is false, do not
1386 append time zone to string."""
1386 append time zone to string."""
1387 t, tz = date or makedate()
1387 t, tz = date or makedate()
1388 s = time.strftime(format, time.gmtime(float(t) - tz))
1388 s = time.strftime(format, time.gmtime(float(t) - tz))
1389 if timezone:
1389 if timezone:
1390 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1390 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1391 return s
1391 return s
1392
1392
1393 def strdate(string, format, defaults):
1393 def strdate(string, format, defaults):
1394 """parse a localized time string and return a (unixtime, offset) tuple.
1394 """parse a localized time string and return a (unixtime, offset) tuple.
1395 if the string cannot be parsed, ValueError is raised."""
1395 if the string cannot be parsed, ValueError is raised."""
1396 def timezone(string):
1396 def timezone(string):
1397 tz = string.split()[-1]
1397 tz = string.split()[-1]
1398 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1398 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1399 tz = int(tz)
1399 tz = int(tz)
1400 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1400 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1401 return offset
1401 return offset
1402 if tz == "GMT" or tz == "UTC":
1402 if tz == "GMT" or tz == "UTC":
1403 return 0
1403 return 0
1404 return None
1404 return None
1405
1405
1406 # NOTE: unixtime = localunixtime + offset
1406 # NOTE: unixtime = localunixtime + offset
1407 offset, date = timezone(string), string
1407 offset, date = timezone(string), string
1408 if offset != None:
1408 if offset != None:
1409 date = " ".join(string.split()[:-1])
1409 date = " ".join(string.split()[:-1])
1410
1410
1411 # add missing elements from defaults
1411 # add missing elements from defaults
1412 for part in defaults:
1412 for part in defaults:
1413 found = [True for p in part if ("%"+p) in format]
1413 found = [True for p in part if ("%"+p) in format]
1414 if not found:
1414 if not found:
1415 date += "@" + defaults[part]
1415 date += "@" + defaults[part]
1416 format += "@%" + part[0]
1416 format += "@%" + part[0]
1417
1417
1418 timetuple = time.strptime(date, format)
1418 timetuple = time.strptime(date, format)
1419 localunixtime = int(calendar.timegm(timetuple))
1419 localunixtime = int(calendar.timegm(timetuple))
1420 if offset is None:
1420 if offset is None:
1421 # local timezone
1421 # local timezone
1422 unixtime = int(time.mktime(timetuple))
1422 unixtime = int(time.mktime(timetuple))
1423 offset = unixtime - localunixtime
1423 offset = unixtime - localunixtime
1424 else:
1424 else:
1425 unixtime = localunixtime + offset
1425 unixtime = localunixtime + offset
1426 return unixtime, offset
1426 return unixtime, offset
1427
1427
1428 def parsedate(string, formats=None, defaults=None):
1428 def parsedate(string, formats=None, defaults=None):
1429 """parse a localized time string and return a (unixtime, offset) tuple.
1429 """parse a localized time string and return a (unixtime, offset) tuple.
1430 The date may be a "unixtime offset" string or in one of the specified
1430 The date may be a "unixtime offset" string or in one of the specified
1431 formats."""
1431 formats."""
1432 if not string:
1432 if not string:
1433 return 0, 0
1433 return 0, 0
1434 if not formats:
1434 if not formats:
1435 formats = defaultdateformats
1435 formats = defaultdateformats
1436 string = string.strip()
1436 string = string.strip()
1437 try:
1437 try:
1438 when, offset = map(int, string.split(' '))
1438 when, offset = map(int, string.split(' '))
1439 except ValueError:
1439 except ValueError:
1440 # fill out defaults
1440 # fill out defaults
1441 if not defaults:
1441 if not defaults:
1442 defaults = {}
1442 defaults = {}
1443 now = makedate()
1443 now = makedate()
1444 for part in "d mb yY HI M S".split():
1444 for part in "d mb yY HI M S".split():
1445 if part not in defaults:
1445 if part not in defaults:
1446 if part[0] in "HMS":
1446 if part[0] in "HMS":
1447 defaults[part] = "00"
1447 defaults[part] = "00"
1448 elif part[0] in "dm":
1448 elif part[0] in "dm":
1449 defaults[part] = "1"
1449 defaults[part] = "1"
1450 else:
1450 else:
1451 defaults[part] = datestr(now, "%" + part[0], False)
1451 defaults[part] = datestr(now, "%" + part[0], False)
1452
1452
1453 for format in formats:
1453 for format in formats:
1454 try:
1454 try:
1455 when, offset = strdate(string, format, defaults)
1455 when, offset = strdate(string, format, defaults)
1456 except ValueError:
1456 except ValueError:
1457 pass
1457 pass
1458 else:
1458 else:
1459 break
1459 break
1460 else:
1460 else:
1461 raise Abort(_('invalid date: %r ') % string)
1461 raise Abort(_('invalid date: %r ') % string)
1462 # validate explicit (probably user-specified) date and
1462 # validate explicit (probably user-specified) date and
1463 # time zone offset. values must fit in signed 32 bits for
1463 # time zone offset. values must fit in signed 32 bits for
1464 # current 32-bit linux runtimes. timezones go from UTC-12
1464 # current 32-bit linux runtimes. timezones go from UTC-12
1465 # to UTC+14
1465 # to UTC+14
1466 if abs(when) > 0x7fffffff:
1466 if abs(when) > 0x7fffffff:
1467 raise Abort(_('date exceeds 32 bits: %d') % when)
1467 raise Abort(_('date exceeds 32 bits: %d') % when)
1468 if offset < -50400 or offset > 43200:
1468 if offset < -50400 or offset > 43200:
1469 raise Abort(_('impossible time zone offset: %d') % offset)
1469 raise Abort(_('impossible time zone offset: %d') % offset)
1470 return when, offset
1470 return when, offset
1471
1471
1472 def matchdate(date):
1472 def matchdate(date):
1473 """Return a function that matches a given date match specifier
1473 """Return a function that matches a given date match specifier
1474
1474
1475 Formats include:
1475 Formats include:
1476
1476
1477 '{date}' match a given date to the accuracy provided
1477 '{date}' match a given date to the accuracy provided
1478
1478
1479 '<{date}' on or before a given date
1479 '<{date}' on or before a given date
1480
1480
1481 '>{date}' on or after a given date
1481 '>{date}' on or after a given date
1482
1482
1483 """
1483 """
1484
1484
1485 def lower(date):
1485 def lower(date):
1486 return parsedate(date, extendeddateformats)[0]
1486 return parsedate(date, extendeddateformats)[0]
1487
1487
1488 def upper(date):
1488 def upper(date):
1489 d = dict(mb="12", HI="23", M="59", S="59")
1489 d = dict(mb="12", HI="23", M="59", S="59")
1490 for days in "31 30 29".split():
1490 for days in "31 30 29".split():
1491 try:
1491 try:
1492 d["d"] = days
1492 d["d"] = days
1493 return parsedate(date, extendeddateformats, d)[0]
1493 return parsedate(date, extendeddateformats, d)[0]
1494 except:
1494 except:
1495 pass
1495 pass
1496 d["d"] = "28"
1496 d["d"] = "28"
1497 return parsedate(date, extendeddateformats, d)[0]
1497 return parsedate(date, extendeddateformats, d)[0]
1498
1498
1499 if date[0] == "<":
1499 if date[0] == "<":
1500 when = upper(date[1:])
1500 when = upper(date[1:])
1501 return lambda x: x <= when
1501 return lambda x: x <= when
1502 elif date[0] == ">":
1502 elif date[0] == ">":
1503 when = lower(date[1:])
1503 when = lower(date[1:])
1504 return lambda x: x >= when
1504 return lambda x: x >= when
1505 elif date[0] == "-":
1505 elif date[0] == "-":
1506 try:
1506 try:
1507 days = int(date[1:])
1507 days = int(date[1:])
1508 except ValueError:
1508 except ValueError:
1509 raise Abort(_("invalid day spec: %s") % date[1:])
1509 raise Abort(_("invalid day spec: %s") % date[1:])
1510 when = makedate()[0] - days * 3600 * 24
1510 when = makedate()[0] - days * 3600 * 24
1511 return lambda x: x >= when
1511 return lambda x: x >= when
1512 elif " to " in date:
1512 elif " to " in date:
1513 a, b = date.split(" to ")
1513 a, b = date.split(" to ")
1514 start, stop = lower(a), upper(b)
1514 start, stop = lower(a), upper(b)
1515 return lambda x: x >= start and x <= stop
1515 return lambda x: x >= start and x <= stop
1516 else:
1516 else:
1517 start, stop = lower(date), upper(date)
1517 start, stop = lower(date), upper(date)
1518 return lambda x: x >= start and x <= stop
1518 return lambda x: x >= start and x <= stop
1519
1519
1520 def shortuser(user):
1520 def shortuser(user):
1521 """Return a short representation of a user name or email address."""
1521 """Return a short representation of a user name or email address."""
1522 f = user.find('@')
1522 f = user.find('@')
1523 if f >= 0:
1523 if f >= 0:
1524 user = user[:f]
1524 user = user[:f]
1525 f = user.find('<')
1525 f = user.find('<')
1526 if f >= 0:
1526 if f >= 0:
1527 user = user[f+1:]
1527 user = user[f+1:]
1528 f = user.find(' ')
1528 f = user.find(' ')
1529 if f >= 0:
1529 if f >= 0:
1530 user = user[:f]
1530 user = user[:f]
1531 f = user.find('.')
1531 f = user.find('.')
1532 if f >= 0:
1532 if f >= 0:
1533 user = user[:f]
1533 user = user[:f]
1534 return user
1534 return user
1535
1535
1536 def ellipsis(text, maxlength=400):
1536 def ellipsis(text, maxlength=400):
1537 """Trim string to at most maxlength (default: 400) characters."""
1537 """Trim string to at most maxlength (default: 400) characters."""
1538 if len(text) <= maxlength:
1538 if len(text) <= maxlength:
1539 return text
1539 return text
1540 else:
1540 else:
1541 return "%s..." % (text[:maxlength-3])
1541 return "%s..." % (text[:maxlength-3])
1542
1542
1543 def walkrepos(path):
1543 def walkrepos(path):
1544 '''yield every hg repository under path, recursively.'''
1544 '''yield every hg repository under path, recursively.'''
1545 def errhandler(err):
1545 def errhandler(err):
1546 if err.filename == path:
1546 if err.filename == path:
1547 raise err
1547 raise err
1548
1548
1549 for root, dirs, files in os.walk(path, onerror=errhandler):
1549 for root, dirs, files in os.walk(path, onerror=errhandler):
1550 for d in dirs:
1550 for d in dirs:
1551 if d == '.hg':
1551 if d == '.hg':
1552 yield root
1552 yield root
1553 dirs[:] = []
1553 dirs[:] = []
1554 break
1554 break
1555
1555
1556 _rcpath = None
1556 _rcpath = None
1557
1557
1558 def os_rcpath():
1558 def os_rcpath():
1559 '''return default os-specific hgrc search path'''
1559 '''return default os-specific hgrc search path'''
1560 path = system_rcpath()
1560 path = system_rcpath()
1561 path.extend(user_rcpath())
1561 path.extend(user_rcpath())
1562 path = [os.path.normpath(f) for f in path]
1562 path = [os.path.normpath(f) for f in path]
1563 return path
1563 return path
1564
1564
1565 def rcpath():
1565 def rcpath():
1566 '''return hgrc search path. if env var HGRCPATH is set, use it.
1566 '''return hgrc search path. if env var HGRCPATH is set, use it.
1567 for each item in path, if directory, use files ending in .rc,
1567 for each item in path, if directory, use files ending in .rc,
1568 else use item.
1568 else use item.
1569 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1569 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1570 if no HGRCPATH, use default os-specific path.'''
1570 if no HGRCPATH, use default os-specific path.'''
1571 global _rcpath
1571 global _rcpath
1572 if _rcpath is None:
1572 if _rcpath is None:
1573 if 'HGRCPATH' in os.environ:
1573 if 'HGRCPATH' in os.environ:
1574 _rcpath = []
1574 _rcpath = []
1575 for p in os.environ['HGRCPATH'].split(os.pathsep):
1575 for p in os.environ['HGRCPATH'].split(os.pathsep):
1576 if not p: continue
1576 if not p: continue
1577 if os.path.isdir(p):
1577 if os.path.isdir(p):
1578 for f in os.listdir(p):
1578 for f in os.listdir(p):
1579 if f.endswith('.rc'):
1579 if f.endswith('.rc'):
1580 _rcpath.append(os.path.join(p, f))
1580 _rcpath.append(os.path.join(p, f))
1581 else:
1581 else:
1582 _rcpath.append(p)
1582 _rcpath.append(p)
1583 else:
1583 else:
1584 _rcpath = os_rcpath()
1584 _rcpath = os_rcpath()
1585 return _rcpath
1585 return _rcpath
1586
1586
1587 def bytecount(nbytes):
1587 def bytecount(nbytes):
1588 '''return byte count formatted as readable string, with units'''
1588 '''return byte count formatted as readable string, with units'''
1589
1589
1590 units = (
1590 units = (
1591 (100, 1<<30, _('%.0f GB')),
1591 (100, 1<<30, _('%.0f GB')),
1592 (10, 1<<30, _('%.1f GB')),
1592 (10, 1<<30, _('%.1f GB')),
1593 (1, 1<<30, _('%.2f GB')),
1593 (1, 1<<30, _('%.2f GB')),
1594 (100, 1<<20, _('%.0f MB')),
1594 (100, 1<<20, _('%.0f MB')),
1595 (10, 1<<20, _('%.1f MB')),
1595 (10, 1<<20, _('%.1f MB')),
1596 (1, 1<<20, _('%.2f MB')),
1596 (1, 1<<20, _('%.2f MB')),
1597 (100, 1<<10, _('%.0f KB')),
1597 (100, 1<<10, _('%.0f KB')),
1598 (10, 1<<10, _('%.1f KB')),
1598 (10, 1<<10, _('%.1f KB')),
1599 (1, 1<<10, _('%.2f KB')),
1599 (1, 1<<10, _('%.2f KB')),
1600 (1, 1, _('%.0f bytes')),
1600 (1, 1, _('%.0f bytes')),
1601 )
1601 )
1602
1602
1603 for multiplier, divisor, format in units:
1603 for multiplier, divisor, format in units:
1604 if nbytes >= divisor * multiplier:
1604 if nbytes >= divisor * multiplier:
1605 return format % (nbytes / float(divisor))
1605 return format % (nbytes / float(divisor))
1606 return units[-1][2] % nbytes
1606 return units[-1][2] % nbytes
1607
1607
1608 def drop_scheme(scheme, path):
1608 def drop_scheme(scheme, path):
1609 sc = scheme + ':'
1609 sc = scheme + ':'
1610 if path.startswith(sc):
1610 if path.startswith(sc):
1611 path = path[len(sc):]
1611 path = path[len(sc):]
1612 if path.startswith('//'):
1612 if path.startswith('//'):
1613 path = path[2:]
1613 path = path[2:]
1614 return path
1614 return path
General Comments 0
You need to be logged in to leave comments. Login now