##// END OF EJS Templates
path_auditor: cache names of audited directories...
Alexis S. L. Carvalho -
r5200:c7e8fe11 default
parent child Browse files
Show More
@@ -1,1660 +1,1670 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, strutil
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile, strutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob
17 import os, stat, 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 HGENCODINGMODE, 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 audit_path = path_auditor(root)
369 audit_path = path_auditor(root)
370 if name != rootsep and name.startswith(rootsep):
370 if name != rootsep and name.startswith(rootsep):
371 name = name[len(rootsep):]
371 name = name[len(rootsep):]
372 audit_path(name)
372 audit_path(name)
373 return pconvert(name)
373 return pconvert(name)
374 elif name == root:
374 elif name == root:
375 return ''
375 return ''
376 else:
376 else:
377 # Determine whether `name' is in the hierarchy at or beneath `root',
377 # Determine whether `name' is in the hierarchy at or beneath `root',
378 # by iterating name=dirname(name) until that causes no change (can't
378 # by iterating name=dirname(name) until that causes no change (can't
379 # check name == '/', because that doesn't work on windows). For each
379 # check name == '/', because that doesn't work on windows). For each
380 # `name', compare dev/inode numbers. If they match, the list `rel'
380 # `name', compare dev/inode numbers. If they match, the list `rel'
381 # holds the reversed list of components making up the relative file
381 # holds the reversed list of components making up the relative file
382 # name we want.
382 # name we want.
383 root_st = os.stat(root)
383 root_st = os.stat(root)
384 rel = []
384 rel = []
385 while True:
385 while True:
386 try:
386 try:
387 name_st = os.stat(name)
387 name_st = os.stat(name)
388 except OSError:
388 except OSError:
389 break
389 break
390 if samestat(name_st, root_st):
390 if samestat(name_st, root_st):
391 if not rel:
391 if not rel:
392 # name was actually the same as root (maybe a symlink)
392 # name was actually the same as root (maybe a symlink)
393 return ''
393 return ''
394 rel.reverse()
394 rel.reverse()
395 name = os.path.join(*rel)
395 name = os.path.join(*rel)
396 audit_path(name)
396 audit_path(name)
397 return pconvert(name)
397 return pconvert(name)
398 dirname, basename = os.path.split(name)
398 dirname, basename = os.path.split(name)
399 rel.append(basename)
399 rel.append(basename)
400 if dirname == name:
400 if dirname == name:
401 break
401 break
402 name = dirname
402 name = dirname
403
403
404 raise Abort('%s not under root' % myname)
404 raise Abort('%s not under root' % myname)
405
405
406 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
406 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
407 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
407 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
408
408
409 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
409 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
410 globbed=False, default=None):
410 globbed=False, default=None):
411 default = default or 'relpath'
411 default = default or 'relpath'
412 if default == 'relpath' and not globbed:
412 if default == 'relpath' and not globbed:
413 names = expand_glob(names)
413 names = expand_glob(names)
414 return _matcher(canonroot, cwd, names, inc, exc, default, src)
414 return _matcher(canonroot, cwd, names, inc, exc, default, src)
415
415
416 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
416 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
417 """build a function to match a set of file patterns
417 """build a function to match a set of file patterns
418
418
419 arguments:
419 arguments:
420 canonroot - the canonical root of the tree you're matching against
420 canonroot - the canonical root of the tree you're matching against
421 cwd - the current working directory, if relevant
421 cwd - the current working directory, if relevant
422 names - patterns to find
422 names - patterns to find
423 inc - patterns to include
423 inc - patterns to include
424 exc - patterns to exclude
424 exc - patterns to exclude
425 dflt_pat - if a pattern in names has no explicit type, assume this one
425 dflt_pat - if a pattern in names has no explicit type, assume this one
426 src - where these patterns came from (e.g. .hgignore)
426 src - where these patterns came from (e.g. .hgignore)
427
427
428 a pattern is one of:
428 a pattern is one of:
429 'glob:<glob>' - a glob relative to cwd
429 'glob:<glob>' - a glob relative to cwd
430 're:<regexp>' - a regular expression
430 're:<regexp>' - a regular expression
431 'path:<path>' - a path relative to canonroot
431 'path:<path>' - a path relative to canonroot
432 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
432 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
433 'relpath:<path>' - a path relative to cwd
433 'relpath:<path>' - a path relative to cwd
434 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
434 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
435 '<something>' - one of the cases above, selected by the dflt_pat argument
435 '<something>' - one of the cases above, selected by the dflt_pat argument
436
436
437 returns:
437 returns:
438 a 3-tuple containing
438 a 3-tuple containing
439 - list of roots (places where one should start a recursive walk of the fs);
439 - list of roots (places where one should start a recursive walk of the fs);
440 this often matches the explicit non-pattern names passed in, but also
440 this often matches the explicit non-pattern names passed in, but also
441 includes the initial part of glob: patterns that has no glob characters
441 includes the initial part of glob: patterns that has no glob characters
442 - a bool match(filename) function
442 - a bool match(filename) function
443 - a bool indicating if any patterns were passed in
443 - a bool indicating if any patterns were passed in
444 """
444 """
445
445
446 # a common case: no patterns at all
446 # a common case: no patterns at all
447 if not names and not inc and not exc:
447 if not names and not inc and not exc:
448 return [], always, False
448 return [], always, False
449
449
450 def contains_glob(name):
450 def contains_glob(name):
451 for c in name:
451 for c in name:
452 if c in _globchars: return True
452 if c in _globchars: return True
453 return False
453 return False
454
454
455 def regex(kind, name, tail):
455 def regex(kind, name, tail):
456 '''convert a pattern into a regular expression'''
456 '''convert a pattern into a regular expression'''
457 if not name:
457 if not name:
458 return ''
458 return ''
459 if kind == 're':
459 if kind == 're':
460 return name
460 return name
461 elif kind == 'path':
461 elif kind == 'path':
462 return '^' + re.escape(name) + '(?:/|$)'
462 return '^' + re.escape(name) + '(?:/|$)'
463 elif kind == 'relglob':
463 elif kind == 'relglob':
464 return globre(name, '(?:|.*/)', tail)
464 return globre(name, '(?:|.*/)', tail)
465 elif kind == 'relpath':
465 elif kind == 'relpath':
466 return re.escape(name) + '(?:/|$)'
466 return re.escape(name) + '(?:/|$)'
467 elif kind == 'relre':
467 elif kind == 'relre':
468 if name.startswith('^'):
468 if name.startswith('^'):
469 return name
469 return name
470 return '.*' + name
470 return '.*' + name
471 return globre(name, '', tail)
471 return globre(name, '', tail)
472
472
473 def matchfn(pats, tail):
473 def matchfn(pats, tail):
474 """build a matching function from a set of patterns"""
474 """build a matching function from a set of patterns"""
475 if not pats:
475 if not pats:
476 return
476 return
477 try:
477 try:
478 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
478 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
479 return re.compile(pat).match
479 return re.compile(pat).match
480 except re.error:
480 except re.error:
481 for k, p in pats:
481 for k, p in pats:
482 try:
482 try:
483 re.compile('(?:%s)' % regex(k, p, tail))
483 re.compile('(?:%s)' % regex(k, p, tail))
484 except re.error:
484 except re.error:
485 if src:
485 if src:
486 raise Abort("%s: invalid pattern (%s): %s" %
486 raise Abort("%s: invalid pattern (%s): %s" %
487 (src, k, p))
487 (src, k, p))
488 else:
488 else:
489 raise Abort("invalid pattern (%s): %s" % (k, p))
489 raise Abort("invalid pattern (%s): %s" % (k, p))
490 raise Abort("invalid pattern")
490 raise Abort("invalid pattern")
491
491
492 def globprefix(pat):
492 def globprefix(pat):
493 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
493 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
494 root = []
494 root = []
495 for p in pat.split('/'):
495 for p in pat.split('/'):
496 if contains_glob(p): break
496 if contains_glob(p): break
497 root.append(p)
497 root.append(p)
498 return '/'.join(root) or '.'
498 return '/'.join(root) or '.'
499
499
500 def normalizepats(names, default):
500 def normalizepats(names, default):
501 pats = []
501 pats = []
502 roots = []
502 roots = []
503 anypats = False
503 anypats = False
504 for kind, name in [patkind(p, default) for p in names]:
504 for kind, name in [patkind(p, default) for p in names]:
505 if kind in ('glob', 'relpath'):
505 if kind in ('glob', 'relpath'):
506 name = canonpath(canonroot, cwd, name)
506 name = canonpath(canonroot, cwd, name)
507 elif kind in ('relglob', 'path'):
507 elif kind in ('relglob', 'path'):
508 name = normpath(name)
508 name = normpath(name)
509
509
510 pats.append((kind, name))
510 pats.append((kind, name))
511
511
512 if kind in ('glob', 're', 'relglob', 'relre'):
512 if kind in ('glob', 're', 'relglob', 'relre'):
513 anypats = True
513 anypats = True
514
514
515 if kind == 'glob':
515 if kind == 'glob':
516 root = globprefix(name)
516 root = globprefix(name)
517 roots.append(root)
517 roots.append(root)
518 elif kind in ('relpath', 'path'):
518 elif kind in ('relpath', 'path'):
519 roots.append(name or '.')
519 roots.append(name or '.')
520 elif kind == 'relglob':
520 elif kind == 'relglob':
521 roots.append('.')
521 roots.append('.')
522 return roots, pats, anypats
522 return roots, pats, anypats
523
523
524 roots, pats, anypats = normalizepats(names, dflt_pat)
524 roots, pats, anypats = normalizepats(names, dflt_pat)
525
525
526 patmatch = matchfn(pats, '$') or always
526 patmatch = matchfn(pats, '$') or always
527 incmatch = always
527 incmatch = always
528 if inc:
528 if inc:
529 dummy, inckinds, dummy = normalizepats(inc, 'glob')
529 dummy, inckinds, dummy = normalizepats(inc, 'glob')
530 incmatch = matchfn(inckinds, '(?:/|$)')
530 incmatch = matchfn(inckinds, '(?:/|$)')
531 excmatch = lambda fn: False
531 excmatch = lambda fn: False
532 if exc:
532 if exc:
533 dummy, exckinds, dummy = normalizepats(exc, 'glob')
533 dummy, exckinds, dummy = normalizepats(exc, 'glob')
534 excmatch = matchfn(exckinds, '(?:/|$)')
534 excmatch = matchfn(exckinds, '(?:/|$)')
535
535
536 if not names and inc and not exc:
536 if not names and inc and not exc:
537 # common case: hgignore patterns
537 # common case: hgignore patterns
538 match = incmatch
538 match = incmatch
539 else:
539 else:
540 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
540 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
541
541
542 return (roots, match, (inc or exc or anypats) and True)
542 return (roots, match, (inc or exc or anypats) and True)
543
543
544 _hgexecutable = None
544 _hgexecutable = None
545
545
546 def hgexecutable():
546 def hgexecutable():
547 """return location of the 'hg' executable.
547 """return location of the 'hg' executable.
548
548
549 Defaults to $HG or 'hg' in the search path.
549 Defaults to $HG or 'hg' in the search path.
550 """
550 """
551 if _hgexecutable is None:
551 if _hgexecutable is None:
552 set_hgexecutable(os.environ.get('HG') or find_exe('hg', 'hg'))
552 set_hgexecutable(os.environ.get('HG') or find_exe('hg', 'hg'))
553 return _hgexecutable
553 return _hgexecutable
554
554
555 def set_hgexecutable(path):
555 def set_hgexecutable(path):
556 """set location of the 'hg' executable"""
556 """set location of the 'hg' executable"""
557 global _hgexecutable
557 global _hgexecutable
558 _hgexecutable = path
558 _hgexecutable = path
559
559
560 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
560 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
561 '''enhanced shell command execution.
561 '''enhanced shell command execution.
562 run with environment maybe modified, maybe in different dir.
562 run with environment maybe modified, maybe in different dir.
563
563
564 if command fails and onerr is None, return status. if ui object,
564 if command fails and onerr is None, return status. if ui object,
565 print error message and return status, else raise onerr object as
565 print error message and return status, else raise onerr object as
566 exception.'''
566 exception.'''
567 def py2shell(val):
567 def py2shell(val):
568 'convert python object into string that is useful to shell'
568 'convert python object into string that is useful to shell'
569 if val in (None, False):
569 if val in (None, False):
570 return '0'
570 return '0'
571 if val == True:
571 if val == True:
572 return '1'
572 return '1'
573 return str(val)
573 return str(val)
574 oldenv = {}
574 oldenv = {}
575 for k in environ:
575 for k in environ:
576 oldenv[k] = os.environ.get(k)
576 oldenv[k] = os.environ.get(k)
577 if cwd is not None:
577 if cwd is not None:
578 oldcwd = os.getcwd()
578 oldcwd = os.getcwd()
579 origcmd = cmd
579 origcmd = cmd
580 if os.name == 'nt':
580 if os.name == 'nt':
581 cmd = '"%s"' % cmd
581 cmd = '"%s"' % cmd
582 try:
582 try:
583 for k, v in environ.iteritems():
583 for k, v in environ.iteritems():
584 os.environ[k] = py2shell(v)
584 os.environ[k] = py2shell(v)
585 os.environ['HG'] = hgexecutable()
585 os.environ['HG'] = hgexecutable()
586 if cwd is not None and oldcwd != cwd:
586 if cwd is not None and oldcwd != cwd:
587 os.chdir(cwd)
587 os.chdir(cwd)
588 rc = os.system(cmd)
588 rc = os.system(cmd)
589 if sys.platform == 'OpenVMS' and rc & 1:
589 if sys.platform == 'OpenVMS' and rc & 1:
590 rc = 0
590 rc = 0
591 if rc and onerr:
591 if rc and onerr:
592 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
592 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
593 explain_exit(rc)[0])
593 explain_exit(rc)[0])
594 if errprefix:
594 if errprefix:
595 errmsg = '%s: %s' % (errprefix, errmsg)
595 errmsg = '%s: %s' % (errprefix, errmsg)
596 try:
596 try:
597 onerr.warn(errmsg + '\n')
597 onerr.warn(errmsg + '\n')
598 except AttributeError:
598 except AttributeError:
599 raise onerr(errmsg)
599 raise onerr(errmsg)
600 return rc
600 return rc
601 finally:
601 finally:
602 for k, v in oldenv.iteritems():
602 for k, v in oldenv.iteritems():
603 if v is None:
603 if v is None:
604 del os.environ[k]
604 del os.environ[k]
605 else:
605 else:
606 os.environ[k] = v
606 os.environ[k] = v
607 if cwd is not None and oldcwd != cwd:
607 if cwd is not None and oldcwd != cwd:
608 os.chdir(oldcwd)
608 os.chdir(oldcwd)
609
609
610 # os.path.lexists is not available on python2.3
610 # os.path.lexists is not available on python2.3
611 def lexists(filename):
611 def lexists(filename):
612 "test whether a file with this name exists. does not follow symlinks"
612 "test whether a file with this name exists. does not follow symlinks"
613 try:
613 try:
614 os.lstat(filename)
614 os.lstat(filename)
615 except:
615 except:
616 return False
616 return False
617 return True
617 return True
618
618
619 def rename(src, dst):
619 def rename(src, dst):
620 """forcibly rename a file"""
620 """forcibly rename a file"""
621 try:
621 try:
622 os.rename(src, dst)
622 os.rename(src, dst)
623 except OSError, err: # FIXME: check err (EEXIST ?)
623 except OSError, err: # FIXME: check err (EEXIST ?)
624 # on windows, rename to existing file is not allowed, so we
624 # on windows, rename to existing file is not allowed, so we
625 # must delete destination first. but if file is open, unlink
625 # must delete destination first. but if file is open, unlink
626 # schedules it for delete but does not delete it. rename
626 # schedules it for delete but does not delete it. rename
627 # happens immediately even for open files, so we create
627 # happens immediately even for open files, so we create
628 # temporary file, delete it, rename destination to that name,
628 # temporary file, delete it, rename destination to that name,
629 # then delete that. then rename is safe to do.
629 # then delete that. then rename is safe to do.
630 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
630 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
631 os.close(fd)
631 os.close(fd)
632 os.unlink(temp)
632 os.unlink(temp)
633 os.rename(dst, temp)
633 os.rename(dst, temp)
634 os.unlink(temp)
634 os.unlink(temp)
635 os.rename(src, dst)
635 os.rename(src, dst)
636
636
637 def unlink(f):
637 def unlink(f):
638 """unlink and remove the directory if it is empty"""
638 """unlink and remove the directory if it is empty"""
639 os.unlink(f)
639 os.unlink(f)
640 # try removing directories that might now be empty
640 # try removing directories that might now be empty
641 try:
641 try:
642 os.removedirs(os.path.dirname(f))
642 os.removedirs(os.path.dirname(f))
643 except OSError:
643 except OSError:
644 pass
644 pass
645
645
646 def copyfile(src, dest):
646 def copyfile(src, dest):
647 "copy a file, preserving mode"
647 "copy a file, preserving mode"
648 if os.path.islink(src):
648 if os.path.islink(src):
649 try:
649 try:
650 os.unlink(dest)
650 os.unlink(dest)
651 except:
651 except:
652 pass
652 pass
653 os.symlink(os.readlink(src), dest)
653 os.symlink(os.readlink(src), dest)
654 else:
654 else:
655 try:
655 try:
656 shutil.copyfile(src, dest)
656 shutil.copyfile(src, dest)
657 shutil.copymode(src, dest)
657 shutil.copymode(src, dest)
658 except shutil.Error, inst:
658 except shutil.Error, inst:
659 raise Abort(str(inst))
659 raise Abort(str(inst))
660
660
661 def copyfiles(src, dst, hardlink=None):
661 def copyfiles(src, dst, hardlink=None):
662 """Copy a directory tree using hardlinks if possible"""
662 """Copy a directory tree using hardlinks if possible"""
663
663
664 if hardlink is None:
664 if hardlink is None:
665 hardlink = (os.stat(src).st_dev ==
665 hardlink = (os.stat(src).st_dev ==
666 os.stat(os.path.dirname(dst)).st_dev)
666 os.stat(os.path.dirname(dst)).st_dev)
667
667
668 if os.path.isdir(src):
668 if os.path.isdir(src):
669 os.mkdir(dst)
669 os.mkdir(dst)
670 for name in os.listdir(src):
670 for name in os.listdir(src):
671 srcname = os.path.join(src, name)
671 srcname = os.path.join(src, name)
672 dstname = os.path.join(dst, name)
672 dstname = os.path.join(dst, name)
673 copyfiles(srcname, dstname, hardlink)
673 copyfiles(srcname, dstname, hardlink)
674 else:
674 else:
675 if hardlink:
675 if hardlink:
676 try:
676 try:
677 os_link(src, dst)
677 os_link(src, dst)
678 except (IOError, OSError):
678 except (IOError, OSError):
679 hardlink = False
679 hardlink = False
680 shutil.copy(src, dst)
680 shutil.copy(src, dst)
681 else:
681 else:
682 shutil.copy(src, dst)
682 shutil.copy(src, dst)
683
683
684 class path_auditor(object):
684 class path_auditor(object):
685 '''ensure that a filesystem path contains no banned components.
685 '''ensure that a filesystem path contains no banned components.
686 the following properties of a path are checked:
686 the following properties of a path are checked:
687
687
688 - under top-level .hg
688 - under top-level .hg
689 - starts at the root of a windows drive
689 - starts at the root of a windows drive
690 - contains ".."
690 - contains ".."
691 - traverses a symlink (e.g. a/symlink_here/b)
691 - traverses a symlink (e.g. a/symlink_here/b)
692 - inside a nested repository'''
692 - inside a nested repository'''
693
693
694 def __init__(self, root):
694 def __init__(self, root):
695 self.audited = {}
695 self.audited = set()
696 self.auditeddir = set()
696 self.root = root
697 self.root = root
697
698
698 def __call__(self, path):
699 def __call__(self, path):
699 if path in self.audited:
700 if path in self.audited:
700 return
701 return
701 normpath = os.path.normcase(path)
702 normpath = os.path.normcase(path)
702 parts = normpath.split(os.sep)
703 parts = normpath.split(os.sep)
703 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
704 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
704 or os.pardir in parts):
705 or os.pardir in parts):
705 raise Abort(_("path contains illegal component: %s") % path)
706 raise Abort(_("path contains illegal component: %s") % path)
706 def check(prefix):
707 def check(prefix):
707 curpath = os.path.join(self.root, prefix)
708 curpath = os.path.join(self.root, prefix)
708 try:
709 try:
709 st = os.lstat(curpath)
710 st = os.lstat(curpath)
710 except OSError, err:
711 except OSError, err:
711 # EINVAL can be raised as invalid path syntax under win32.
712 # EINVAL can be raised as invalid path syntax under win32.
712 # They must be ignored for patterns can be checked too.
713 # They must be ignored for patterns can be checked too.
713 if err.errno not in (errno.ENOENT, errno.EINVAL):
714 if err.errno not in (errno.ENOENT, errno.EINVAL):
714 raise
715 raise
715 else:
716 else:
716 if stat.S_ISLNK(st.st_mode):
717 if stat.S_ISLNK(st.st_mode):
717 raise Abort(_('path %r traverses symbolic link %r') %
718 raise Abort(_('path %r traverses symbolic link %r') %
718 (path, prefix))
719 (path, prefix))
719 elif (stat.S_ISDIR(st.st_mode) and
720 elif (stat.S_ISDIR(st.st_mode) and
720 os.path.isdir(os.path.join(curpath, '.hg'))):
721 os.path.isdir(os.path.join(curpath, '.hg'))):
721 raise Abort(_('path %r is inside repo %r') %
722 raise Abort(_('path %r is inside repo %r') %
722 (path, prefix))
723 (path, prefix))
723 self.audited[prefix] = True
724
725 prefixes = []
724 for c in strutil.rfindall(normpath, os.sep):
726 for c in strutil.rfindall(normpath, os.sep):
725 check(normpath[:c])
727 prefix = normpath[:c]
726 self.audited[path] = True
728 if prefix in self.auditeddir:
729 break
730 check(prefix)
731 prefixes.append(prefix)
732
733 self.audited.add(path)
734 # only add prefixes to the cache after checking everything: we don't
735 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
736 self.auditeddir.update(prefixes)
727
737
728 def _makelock_file(info, pathname):
738 def _makelock_file(info, pathname):
729 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
739 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
730 os.write(ld, info)
740 os.write(ld, info)
731 os.close(ld)
741 os.close(ld)
732
742
733 def _readlock_file(pathname):
743 def _readlock_file(pathname):
734 return posixfile(pathname).read()
744 return posixfile(pathname).read()
735
745
736 def nlinks(pathname):
746 def nlinks(pathname):
737 """Return number of hardlinks for the given file."""
747 """Return number of hardlinks for the given file."""
738 return os.lstat(pathname).st_nlink
748 return os.lstat(pathname).st_nlink
739
749
740 if hasattr(os, 'link'):
750 if hasattr(os, 'link'):
741 os_link = os.link
751 os_link = os.link
742 else:
752 else:
743 def os_link(src, dst):
753 def os_link(src, dst):
744 raise OSError(0, _("Hardlinks not supported"))
754 raise OSError(0, _("Hardlinks not supported"))
745
755
746 def fstat(fp):
756 def fstat(fp):
747 '''stat file object that may not have fileno method.'''
757 '''stat file object that may not have fileno method.'''
748 try:
758 try:
749 return os.fstat(fp.fileno())
759 return os.fstat(fp.fileno())
750 except AttributeError:
760 except AttributeError:
751 return os.stat(fp.name)
761 return os.stat(fp.name)
752
762
753 posixfile = file
763 posixfile = file
754
764
755 def is_win_9x():
765 def is_win_9x():
756 '''return true if run on windows 95, 98 or me.'''
766 '''return true if run on windows 95, 98 or me.'''
757 try:
767 try:
758 return sys.getwindowsversion()[3] == 1
768 return sys.getwindowsversion()[3] == 1
759 except AttributeError:
769 except AttributeError:
760 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
770 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
761
771
762 getuser_fallback = None
772 getuser_fallback = None
763
773
764 def getuser():
774 def getuser():
765 '''return name of current user'''
775 '''return name of current user'''
766 try:
776 try:
767 return getpass.getuser()
777 return getpass.getuser()
768 except ImportError:
778 except ImportError:
769 # import of pwd will fail on windows - try fallback
779 # import of pwd will fail on windows - try fallback
770 if getuser_fallback:
780 if getuser_fallback:
771 return getuser_fallback()
781 return getuser_fallback()
772 # raised if win32api not available
782 # raised if win32api not available
773 raise Abort(_('user name not available - set USERNAME '
783 raise Abort(_('user name not available - set USERNAME '
774 'environment variable'))
784 'environment variable'))
775
785
776 def username(uid=None):
786 def username(uid=None):
777 """Return the name of the user with the given uid.
787 """Return the name of the user with the given uid.
778
788
779 If uid is None, return the name of the current user."""
789 If uid is None, return the name of the current user."""
780 try:
790 try:
781 import pwd
791 import pwd
782 if uid is None:
792 if uid is None:
783 uid = os.getuid()
793 uid = os.getuid()
784 try:
794 try:
785 return pwd.getpwuid(uid)[0]
795 return pwd.getpwuid(uid)[0]
786 except KeyError:
796 except KeyError:
787 return str(uid)
797 return str(uid)
788 except ImportError:
798 except ImportError:
789 return None
799 return None
790
800
791 def groupname(gid=None):
801 def groupname(gid=None):
792 """Return the name of the group with the given gid.
802 """Return the name of the group with the given gid.
793
803
794 If gid is None, return the name of the current group."""
804 If gid is None, return the name of the current group."""
795 try:
805 try:
796 import grp
806 import grp
797 if gid is None:
807 if gid is None:
798 gid = os.getgid()
808 gid = os.getgid()
799 try:
809 try:
800 return grp.getgrgid(gid)[0]
810 return grp.getgrgid(gid)[0]
801 except KeyError:
811 except KeyError:
802 return str(gid)
812 return str(gid)
803 except ImportError:
813 except ImportError:
804 return None
814 return None
805
815
806 # File system features
816 # File system features
807
817
808 def checkfolding(path):
818 def checkfolding(path):
809 """
819 """
810 Check whether the given path is on a case-sensitive filesystem
820 Check whether the given path is on a case-sensitive filesystem
811
821
812 Requires a path (like /foo/.hg) ending with a foldable final
822 Requires a path (like /foo/.hg) ending with a foldable final
813 directory component.
823 directory component.
814 """
824 """
815 s1 = os.stat(path)
825 s1 = os.stat(path)
816 d, b = os.path.split(path)
826 d, b = os.path.split(path)
817 p2 = os.path.join(d, b.upper())
827 p2 = os.path.join(d, b.upper())
818 if path == p2:
828 if path == p2:
819 p2 = os.path.join(d, b.lower())
829 p2 = os.path.join(d, b.lower())
820 try:
830 try:
821 s2 = os.stat(p2)
831 s2 = os.stat(p2)
822 if s2 == s1:
832 if s2 == s1:
823 return False
833 return False
824 return True
834 return True
825 except:
835 except:
826 return True
836 return True
827
837
828 def checkexec(path):
838 def checkexec(path):
829 """
839 """
830 Check whether the given path is on a filesystem with UNIX-like exec flags
840 Check whether the given path is on a filesystem with UNIX-like exec flags
831
841
832 Requires a directory (like /foo/.hg)
842 Requires a directory (like /foo/.hg)
833 """
843 """
834 fh, fn = tempfile.mkstemp("", "", path)
844 fh, fn = tempfile.mkstemp("", "", path)
835 os.close(fh)
845 os.close(fh)
836 m = os.stat(fn).st_mode
846 m = os.stat(fn).st_mode
837 os.chmod(fn, m ^ 0111)
847 os.chmod(fn, m ^ 0111)
838 r = (os.stat(fn).st_mode != m)
848 r = (os.stat(fn).st_mode != m)
839 os.unlink(fn)
849 os.unlink(fn)
840 return r
850 return r
841
851
842 def execfunc(path, fallback):
852 def execfunc(path, fallback):
843 '''return an is_exec() function with default to fallback'''
853 '''return an is_exec() function with default to fallback'''
844 if checkexec(path):
854 if checkexec(path):
845 return lambda x: is_exec(os.path.join(path, x))
855 return lambda x: is_exec(os.path.join(path, x))
846 return fallback
856 return fallback
847
857
848 def checklink(path):
858 def checklink(path):
849 """check whether the given path is on a symlink-capable filesystem"""
859 """check whether the given path is on a symlink-capable filesystem"""
850 # mktemp is not racy because symlink creation will fail if the
860 # mktemp is not racy because symlink creation will fail if the
851 # file already exists
861 # file already exists
852 name = tempfile.mktemp(dir=path)
862 name = tempfile.mktemp(dir=path)
853 try:
863 try:
854 os.symlink(".", name)
864 os.symlink(".", name)
855 os.unlink(name)
865 os.unlink(name)
856 return True
866 return True
857 except (OSError, AttributeError):
867 except (OSError, AttributeError):
858 return False
868 return False
859
869
860 def linkfunc(path, fallback):
870 def linkfunc(path, fallback):
861 '''return an is_link() function with default to fallback'''
871 '''return an is_link() function with default to fallback'''
862 if checklink(path):
872 if checklink(path):
863 return lambda x: os.path.islink(os.path.join(path, x))
873 return lambda x: os.path.islink(os.path.join(path, x))
864 return fallback
874 return fallback
865
875
866 _umask = os.umask(0)
876 _umask = os.umask(0)
867 os.umask(_umask)
877 os.umask(_umask)
868
878
869 def needbinarypatch():
879 def needbinarypatch():
870 """return True if patches should be applied in binary mode by default."""
880 """return True if patches should be applied in binary mode by default."""
871 return os.name == 'nt'
881 return os.name == 'nt'
872
882
873 # Platform specific variants
883 # Platform specific variants
874 if os.name == 'nt':
884 if os.name == 'nt':
875 import msvcrt
885 import msvcrt
876 nulldev = 'NUL:'
886 nulldev = 'NUL:'
877
887
878 class winstdout:
888 class winstdout:
879 '''stdout on windows misbehaves if sent through a pipe'''
889 '''stdout on windows misbehaves if sent through a pipe'''
880
890
881 def __init__(self, fp):
891 def __init__(self, fp):
882 self.fp = fp
892 self.fp = fp
883
893
884 def __getattr__(self, key):
894 def __getattr__(self, key):
885 return getattr(self.fp, key)
895 return getattr(self.fp, key)
886
896
887 def close(self):
897 def close(self):
888 try:
898 try:
889 self.fp.close()
899 self.fp.close()
890 except: pass
900 except: pass
891
901
892 def write(self, s):
902 def write(self, s):
893 try:
903 try:
894 return self.fp.write(s)
904 return self.fp.write(s)
895 except IOError, inst:
905 except IOError, inst:
896 if inst.errno != 0: raise
906 if inst.errno != 0: raise
897 self.close()
907 self.close()
898 raise IOError(errno.EPIPE, 'Broken pipe')
908 raise IOError(errno.EPIPE, 'Broken pipe')
899
909
900 def flush(self):
910 def flush(self):
901 try:
911 try:
902 return self.fp.flush()
912 return self.fp.flush()
903 except IOError, inst:
913 except IOError, inst:
904 if inst.errno != errno.EINVAL: raise
914 if inst.errno != errno.EINVAL: raise
905 self.close()
915 self.close()
906 raise IOError(errno.EPIPE, 'Broken pipe')
916 raise IOError(errno.EPIPE, 'Broken pipe')
907
917
908 sys.stdout = winstdout(sys.stdout)
918 sys.stdout = winstdout(sys.stdout)
909
919
910 def system_rcpath():
920 def system_rcpath():
911 try:
921 try:
912 return system_rcpath_win32()
922 return system_rcpath_win32()
913 except:
923 except:
914 return [r'c:\mercurial\mercurial.ini']
924 return [r'c:\mercurial\mercurial.ini']
915
925
916 def user_rcpath():
926 def user_rcpath():
917 '''return os-specific hgrc search path to the user dir'''
927 '''return os-specific hgrc search path to the user dir'''
918 try:
928 try:
919 userrc = user_rcpath_win32()
929 userrc = user_rcpath_win32()
920 except:
930 except:
921 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
931 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
922 path = [userrc]
932 path = [userrc]
923 userprofile = os.environ.get('USERPROFILE')
933 userprofile = os.environ.get('USERPROFILE')
924 if userprofile:
934 if userprofile:
925 path.append(os.path.join(userprofile, 'mercurial.ini'))
935 path.append(os.path.join(userprofile, 'mercurial.ini'))
926 return path
936 return path
927
937
928 def parse_patch_output(output_line):
938 def parse_patch_output(output_line):
929 """parses the output produced by patch and returns the file name"""
939 """parses the output produced by patch and returns the file name"""
930 pf = output_line[14:]
940 pf = output_line[14:]
931 if pf[0] == '`':
941 if pf[0] == '`':
932 pf = pf[1:-1] # Remove the quotes
942 pf = pf[1:-1] # Remove the quotes
933 return pf
943 return pf
934
944
935 def testpid(pid):
945 def testpid(pid):
936 '''return False if pid dead, True if running or not known'''
946 '''return False if pid dead, True if running or not known'''
937 return True
947 return True
938
948
939 def set_exec(f, mode):
949 def set_exec(f, mode):
940 pass
950 pass
941
951
942 def set_link(f, mode):
952 def set_link(f, mode):
943 pass
953 pass
944
954
945 def set_binary(fd):
955 def set_binary(fd):
946 msvcrt.setmode(fd.fileno(), os.O_BINARY)
956 msvcrt.setmode(fd.fileno(), os.O_BINARY)
947
957
948 def pconvert(path):
958 def pconvert(path):
949 return path.replace("\\", "/")
959 return path.replace("\\", "/")
950
960
951 def localpath(path):
961 def localpath(path):
952 return path.replace('/', '\\')
962 return path.replace('/', '\\')
953
963
954 def normpath(path):
964 def normpath(path):
955 return pconvert(os.path.normpath(path))
965 return pconvert(os.path.normpath(path))
956
966
957 makelock = _makelock_file
967 makelock = _makelock_file
958 readlock = _readlock_file
968 readlock = _readlock_file
959
969
960 def samestat(s1, s2):
970 def samestat(s1, s2):
961 return False
971 return False
962
972
963 # A sequence of backslashes is special iff it precedes a double quote:
973 # A sequence of backslashes is special iff it precedes a double quote:
964 # - if there's an even number of backslashes, the double quote is not
974 # - if there's an even number of backslashes, the double quote is not
965 # quoted (i.e. it ends the quoted region)
975 # quoted (i.e. it ends the quoted region)
966 # - if there's an odd number of backslashes, the double quote is quoted
976 # - if there's an odd number of backslashes, the double quote is quoted
967 # - in both cases, every pair of backslashes is unquoted into a single
977 # - in both cases, every pair of backslashes is unquoted into a single
968 # backslash
978 # backslash
969 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
979 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
970 # So, to quote a string, we must surround it in double quotes, double
980 # So, to quote a string, we must surround it in double quotes, double
971 # the number of backslashes that preceed double quotes and add another
981 # the number of backslashes that preceed double quotes and add another
972 # backslash before every double quote (being careful with the double
982 # backslash before every double quote (being careful with the double
973 # quote we've appended to the end)
983 # quote we've appended to the end)
974 _quotere = None
984 _quotere = None
975 def shellquote(s):
985 def shellquote(s):
976 global _quotere
986 global _quotere
977 if _quotere is None:
987 if _quotere is None:
978 _quotere = re.compile(r'(\\*)("|\\$)')
988 _quotere = re.compile(r'(\\*)("|\\$)')
979 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
989 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
980
990
981 def explain_exit(code):
991 def explain_exit(code):
982 return _("exited with status %d") % code, code
992 return _("exited with status %d") % code, code
983
993
984 # if you change this stub into a real check, please try to implement the
994 # if you change this stub into a real check, please try to implement the
985 # username and groupname functions above, too.
995 # username and groupname functions above, too.
986 def isowner(fp, st=None):
996 def isowner(fp, st=None):
987 return True
997 return True
988
998
989 def find_in_path(name, path, default=None):
999 def find_in_path(name, path, default=None):
990 '''find name in search path. path can be string (will be split
1000 '''find name in search path. path can be string (will be split
991 with os.pathsep), or iterable thing that returns strings. if name
1001 with os.pathsep), or iterable thing that returns strings. if name
992 found, return path to name. else return default. name is looked up
1002 found, return path to name. else return default. name is looked up
993 using cmd.exe rules, using PATHEXT.'''
1003 using cmd.exe rules, using PATHEXT.'''
994 if isinstance(path, str):
1004 if isinstance(path, str):
995 path = path.split(os.pathsep)
1005 path = path.split(os.pathsep)
996
1006
997 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1007 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
998 pathext = pathext.lower().split(os.pathsep)
1008 pathext = pathext.lower().split(os.pathsep)
999 isexec = os.path.splitext(name)[1].lower() in pathext
1009 isexec = os.path.splitext(name)[1].lower() in pathext
1000
1010
1001 for p in path:
1011 for p in path:
1002 p_name = os.path.join(p, name)
1012 p_name = os.path.join(p, name)
1003
1013
1004 if isexec and os.path.exists(p_name):
1014 if isexec and os.path.exists(p_name):
1005 return p_name
1015 return p_name
1006
1016
1007 for ext in pathext:
1017 for ext in pathext:
1008 p_name_ext = p_name + ext
1018 p_name_ext = p_name + ext
1009 if os.path.exists(p_name_ext):
1019 if os.path.exists(p_name_ext):
1010 return p_name_ext
1020 return p_name_ext
1011 return default
1021 return default
1012
1022
1013 def set_signal_handler():
1023 def set_signal_handler():
1014 try:
1024 try:
1015 set_signal_handler_win32()
1025 set_signal_handler_win32()
1016 except NameError:
1026 except NameError:
1017 pass
1027 pass
1018
1028
1019 try:
1029 try:
1020 # override functions with win32 versions if possible
1030 # override functions with win32 versions if possible
1021 from util_win32 import *
1031 from util_win32 import *
1022 if not is_win_9x():
1032 if not is_win_9x():
1023 posixfile = posixfile_nt
1033 posixfile = posixfile_nt
1024 except ImportError:
1034 except ImportError:
1025 pass
1035 pass
1026
1036
1027 else:
1037 else:
1028 nulldev = '/dev/null'
1038 nulldev = '/dev/null'
1029
1039
1030 def rcfiles(path):
1040 def rcfiles(path):
1031 rcs = [os.path.join(path, 'hgrc')]
1041 rcs = [os.path.join(path, 'hgrc')]
1032 rcdir = os.path.join(path, 'hgrc.d')
1042 rcdir = os.path.join(path, 'hgrc.d')
1033 try:
1043 try:
1034 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
1044 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
1035 if f.endswith(".rc")])
1045 if f.endswith(".rc")])
1036 except OSError:
1046 except OSError:
1037 pass
1047 pass
1038 return rcs
1048 return rcs
1039
1049
1040 def system_rcpath():
1050 def system_rcpath():
1041 path = []
1051 path = []
1042 # old mod_python does not set sys.argv
1052 # old mod_python does not set sys.argv
1043 if len(getattr(sys, 'argv', [])) > 0:
1053 if len(getattr(sys, 'argv', [])) > 0:
1044 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1054 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1045 '/../etc/mercurial'))
1055 '/../etc/mercurial'))
1046 path.extend(rcfiles('/etc/mercurial'))
1056 path.extend(rcfiles('/etc/mercurial'))
1047 return path
1057 return path
1048
1058
1049 def user_rcpath():
1059 def user_rcpath():
1050 return [os.path.expanduser('~/.hgrc')]
1060 return [os.path.expanduser('~/.hgrc')]
1051
1061
1052 def parse_patch_output(output_line):
1062 def parse_patch_output(output_line):
1053 """parses the output produced by patch and returns the file name"""
1063 """parses the output produced by patch and returns the file name"""
1054 pf = output_line[14:]
1064 pf = output_line[14:]
1055 if os.sys.platform == 'OpenVMS':
1065 if os.sys.platform == 'OpenVMS':
1056 if pf[0] == '`':
1066 if pf[0] == '`':
1057 pf = pf[1:-1] # Remove the quotes
1067 pf = pf[1:-1] # Remove the quotes
1058 else:
1068 else:
1059 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1069 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1060 pf = pf[1:-1] # Remove the quotes
1070 pf = pf[1:-1] # Remove the quotes
1061 return pf
1071 return pf
1062
1072
1063 def is_exec(f):
1073 def is_exec(f):
1064 """check whether a file is executable"""
1074 """check whether a file is executable"""
1065 return (os.lstat(f).st_mode & 0100 != 0)
1075 return (os.lstat(f).st_mode & 0100 != 0)
1066
1076
1067 def set_exec(f, mode):
1077 def set_exec(f, mode):
1068 s = os.lstat(f).st_mode
1078 s = os.lstat(f).st_mode
1069 if (s & 0100 != 0) == mode:
1079 if (s & 0100 != 0) == mode:
1070 return
1080 return
1071 if mode:
1081 if mode:
1072 # Turn on +x for every +r bit when making a file executable
1082 # Turn on +x for every +r bit when making a file executable
1073 # and obey umask.
1083 # and obey umask.
1074 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1084 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1075 else:
1085 else:
1076 os.chmod(f, s & 0666)
1086 os.chmod(f, s & 0666)
1077
1087
1078 def set_link(f, mode):
1088 def set_link(f, mode):
1079 """make a file a symbolic link/regular file
1089 """make a file a symbolic link/regular file
1080
1090
1081 if a file is changed to a link, its contents become the link data
1091 if a file is changed to a link, its contents become the link data
1082 if a link is changed to a file, its link data become its contents
1092 if a link is changed to a file, its link data become its contents
1083 """
1093 """
1084
1094
1085 m = os.path.islink(f)
1095 m = os.path.islink(f)
1086 if m == bool(mode):
1096 if m == bool(mode):
1087 return
1097 return
1088
1098
1089 if mode: # switch file to link
1099 if mode: # switch file to link
1090 data = file(f).read()
1100 data = file(f).read()
1091 os.unlink(f)
1101 os.unlink(f)
1092 os.symlink(data, f)
1102 os.symlink(data, f)
1093 else:
1103 else:
1094 data = os.readlink(f)
1104 data = os.readlink(f)
1095 os.unlink(f)
1105 os.unlink(f)
1096 file(f, "w").write(data)
1106 file(f, "w").write(data)
1097
1107
1098 def set_binary(fd):
1108 def set_binary(fd):
1099 pass
1109 pass
1100
1110
1101 def pconvert(path):
1111 def pconvert(path):
1102 return path
1112 return path
1103
1113
1104 def localpath(path):
1114 def localpath(path):
1105 return path
1115 return path
1106
1116
1107 normpath = os.path.normpath
1117 normpath = os.path.normpath
1108 samestat = os.path.samestat
1118 samestat = os.path.samestat
1109
1119
1110 def makelock(info, pathname):
1120 def makelock(info, pathname):
1111 try:
1121 try:
1112 os.symlink(info, pathname)
1122 os.symlink(info, pathname)
1113 except OSError, why:
1123 except OSError, why:
1114 if why.errno == errno.EEXIST:
1124 if why.errno == errno.EEXIST:
1115 raise
1125 raise
1116 else:
1126 else:
1117 _makelock_file(info, pathname)
1127 _makelock_file(info, pathname)
1118
1128
1119 def readlock(pathname):
1129 def readlock(pathname):
1120 try:
1130 try:
1121 return os.readlink(pathname)
1131 return os.readlink(pathname)
1122 except OSError, why:
1132 except OSError, why:
1123 if why.errno in (errno.EINVAL, errno.ENOSYS):
1133 if why.errno in (errno.EINVAL, errno.ENOSYS):
1124 return _readlock_file(pathname)
1134 return _readlock_file(pathname)
1125 else:
1135 else:
1126 raise
1136 raise
1127
1137
1128 def shellquote(s):
1138 def shellquote(s):
1129 if os.sys.platform == 'OpenVMS':
1139 if os.sys.platform == 'OpenVMS':
1130 return '"%s"' % s
1140 return '"%s"' % s
1131 else:
1141 else:
1132 return "'%s'" % s.replace("'", "'\\''")
1142 return "'%s'" % s.replace("'", "'\\''")
1133
1143
1134 def testpid(pid):
1144 def testpid(pid):
1135 '''return False if pid dead, True if running or not sure'''
1145 '''return False if pid dead, True if running or not sure'''
1136 if os.sys.platform == 'OpenVMS':
1146 if os.sys.platform == 'OpenVMS':
1137 return True
1147 return True
1138 try:
1148 try:
1139 os.kill(pid, 0)
1149 os.kill(pid, 0)
1140 return True
1150 return True
1141 except OSError, inst:
1151 except OSError, inst:
1142 return inst.errno != errno.ESRCH
1152 return inst.errno != errno.ESRCH
1143
1153
1144 def explain_exit(code):
1154 def explain_exit(code):
1145 """return a 2-tuple (desc, code) describing a process's status"""
1155 """return a 2-tuple (desc, code) describing a process's status"""
1146 if os.WIFEXITED(code):
1156 if os.WIFEXITED(code):
1147 val = os.WEXITSTATUS(code)
1157 val = os.WEXITSTATUS(code)
1148 return _("exited with status %d") % val, val
1158 return _("exited with status %d") % val, val
1149 elif os.WIFSIGNALED(code):
1159 elif os.WIFSIGNALED(code):
1150 val = os.WTERMSIG(code)
1160 val = os.WTERMSIG(code)
1151 return _("killed by signal %d") % val, val
1161 return _("killed by signal %d") % val, val
1152 elif os.WIFSTOPPED(code):
1162 elif os.WIFSTOPPED(code):
1153 val = os.WSTOPSIG(code)
1163 val = os.WSTOPSIG(code)
1154 return _("stopped by signal %d") % val, val
1164 return _("stopped by signal %d") % val, val
1155 raise ValueError(_("invalid exit code"))
1165 raise ValueError(_("invalid exit code"))
1156
1166
1157 def isowner(fp, st=None):
1167 def isowner(fp, st=None):
1158 """Return True if the file object f belongs to the current user.
1168 """Return True if the file object f belongs to the current user.
1159
1169
1160 The return value of a util.fstat(f) may be passed as the st argument.
1170 The return value of a util.fstat(f) may be passed as the st argument.
1161 """
1171 """
1162 if st is None:
1172 if st is None:
1163 st = fstat(fp)
1173 st = fstat(fp)
1164 return st.st_uid == os.getuid()
1174 return st.st_uid == os.getuid()
1165
1175
1166 def find_in_path(name, path, default=None):
1176 def find_in_path(name, path, default=None):
1167 '''find name in search path. path can be string (will be split
1177 '''find name in search path. path can be string (will be split
1168 with os.pathsep), or iterable thing that returns strings. if name
1178 with os.pathsep), or iterable thing that returns strings. if name
1169 found, return path to name. else return default.'''
1179 found, return path to name. else return default.'''
1170 if isinstance(path, str):
1180 if isinstance(path, str):
1171 path = path.split(os.pathsep)
1181 path = path.split(os.pathsep)
1172 for p in path:
1182 for p in path:
1173 p_name = os.path.join(p, name)
1183 p_name = os.path.join(p, name)
1174 if os.path.exists(p_name):
1184 if os.path.exists(p_name):
1175 return p_name
1185 return p_name
1176 return default
1186 return default
1177
1187
1178 def set_signal_handler():
1188 def set_signal_handler():
1179 pass
1189 pass
1180
1190
1181 def find_exe(name, default=None):
1191 def find_exe(name, default=None):
1182 '''find path of an executable.
1192 '''find path of an executable.
1183 if name contains a path component, return it as is. otherwise,
1193 if name contains a path component, return it as is. otherwise,
1184 use normal executable search path.'''
1194 use normal executable search path.'''
1185
1195
1186 if os.sep in name or sys.platform == 'OpenVMS':
1196 if os.sep in name or sys.platform == 'OpenVMS':
1187 # don't check the executable bit. if the file isn't
1197 # don't check the executable bit. if the file isn't
1188 # executable, whoever tries to actually run it will give a
1198 # executable, whoever tries to actually run it will give a
1189 # much more useful error message.
1199 # much more useful error message.
1190 return name
1200 return name
1191 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1201 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1192
1202
1193 def _buildencodefun():
1203 def _buildencodefun():
1194 e = '_'
1204 e = '_'
1195 win_reserved = [ord(x) for x in '\\:*?"<>|']
1205 win_reserved = [ord(x) for x in '\\:*?"<>|']
1196 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1206 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1197 for x in (range(32) + range(126, 256) + win_reserved):
1207 for x in (range(32) + range(126, 256) + win_reserved):
1198 cmap[chr(x)] = "~%02x" % x
1208 cmap[chr(x)] = "~%02x" % x
1199 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1209 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1200 cmap[chr(x)] = e + chr(x).lower()
1210 cmap[chr(x)] = e + chr(x).lower()
1201 dmap = {}
1211 dmap = {}
1202 for k, v in cmap.iteritems():
1212 for k, v in cmap.iteritems():
1203 dmap[v] = k
1213 dmap[v] = k
1204 def decode(s):
1214 def decode(s):
1205 i = 0
1215 i = 0
1206 while i < len(s):
1216 while i < len(s):
1207 for l in xrange(1, 4):
1217 for l in xrange(1, 4):
1208 try:
1218 try:
1209 yield dmap[s[i:i+l]]
1219 yield dmap[s[i:i+l]]
1210 i += l
1220 i += l
1211 break
1221 break
1212 except KeyError:
1222 except KeyError:
1213 pass
1223 pass
1214 else:
1224 else:
1215 raise KeyError
1225 raise KeyError
1216 return (lambda s: "".join([cmap[c] for c in s]),
1226 return (lambda s: "".join([cmap[c] for c in s]),
1217 lambda s: "".join(list(decode(s))))
1227 lambda s: "".join(list(decode(s))))
1218
1228
1219 encodefilename, decodefilename = _buildencodefun()
1229 encodefilename, decodefilename = _buildencodefun()
1220
1230
1221 def encodedopener(openerfn, fn):
1231 def encodedopener(openerfn, fn):
1222 def o(path, *args, **kw):
1232 def o(path, *args, **kw):
1223 return openerfn(fn(path), *args, **kw)
1233 return openerfn(fn(path), *args, **kw)
1224 return o
1234 return o
1225
1235
1226 def mktempcopy(name, emptyok=False):
1236 def mktempcopy(name, emptyok=False):
1227 """Create a temporary file with the same contents from name
1237 """Create a temporary file with the same contents from name
1228
1238
1229 The permission bits are copied from the original file.
1239 The permission bits are copied from the original file.
1230
1240
1231 If the temporary file is going to be truncated immediately, you
1241 If the temporary file is going to be truncated immediately, you
1232 can use emptyok=True as an optimization.
1242 can use emptyok=True as an optimization.
1233
1243
1234 Returns the name of the temporary file.
1244 Returns the name of the temporary file.
1235 """
1245 """
1236 d, fn = os.path.split(name)
1246 d, fn = os.path.split(name)
1237 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1247 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1238 os.close(fd)
1248 os.close(fd)
1239 # Temporary files are created with mode 0600, which is usually not
1249 # Temporary files are created with mode 0600, which is usually not
1240 # what we want. If the original file already exists, just copy
1250 # what we want. If the original file already exists, just copy
1241 # its mode. Otherwise, manually obey umask.
1251 # its mode. Otherwise, manually obey umask.
1242 try:
1252 try:
1243 st_mode = os.lstat(name).st_mode
1253 st_mode = os.lstat(name).st_mode
1244 except OSError, inst:
1254 except OSError, inst:
1245 if inst.errno != errno.ENOENT:
1255 if inst.errno != errno.ENOENT:
1246 raise
1256 raise
1247 st_mode = 0666 & ~_umask
1257 st_mode = 0666 & ~_umask
1248 os.chmod(temp, st_mode)
1258 os.chmod(temp, st_mode)
1249 if emptyok:
1259 if emptyok:
1250 return temp
1260 return temp
1251 try:
1261 try:
1252 try:
1262 try:
1253 ifp = posixfile(name, "rb")
1263 ifp = posixfile(name, "rb")
1254 except IOError, inst:
1264 except IOError, inst:
1255 if inst.errno == errno.ENOENT:
1265 if inst.errno == errno.ENOENT:
1256 return temp
1266 return temp
1257 if not getattr(inst, 'filename', None):
1267 if not getattr(inst, 'filename', None):
1258 inst.filename = name
1268 inst.filename = name
1259 raise
1269 raise
1260 ofp = posixfile(temp, "wb")
1270 ofp = posixfile(temp, "wb")
1261 for chunk in filechunkiter(ifp):
1271 for chunk in filechunkiter(ifp):
1262 ofp.write(chunk)
1272 ofp.write(chunk)
1263 ifp.close()
1273 ifp.close()
1264 ofp.close()
1274 ofp.close()
1265 except:
1275 except:
1266 try: os.unlink(temp)
1276 try: os.unlink(temp)
1267 except: pass
1277 except: pass
1268 raise
1278 raise
1269 return temp
1279 return temp
1270
1280
1271 class atomictempfile(posixfile):
1281 class atomictempfile(posixfile):
1272 """file-like object that atomically updates a file
1282 """file-like object that atomically updates a file
1273
1283
1274 All writes will be redirected to a temporary copy of the original
1284 All writes will be redirected to a temporary copy of the original
1275 file. When rename is called, the copy is renamed to the original
1285 file. When rename is called, the copy is renamed to the original
1276 name, making the changes visible.
1286 name, making the changes visible.
1277 """
1287 """
1278 def __init__(self, name, mode):
1288 def __init__(self, name, mode):
1279 self.__name = name
1289 self.__name = name
1280 self.temp = mktempcopy(name, emptyok=('w' in mode))
1290 self.temp = mktempcopy(name, emptyok=('w' in mode))
1281 posixfile.__init__(self, self.temp, mode)
1291 posixfile.__init__(self, self.temp, mode)
1282
1292
1283 def rename(self):
1293 def rename(self):
1284 if not self.closed:
1294 if not self.closed:
1285 posixfile.close(self)
1295 posixfile.close(self)
1286 rename(self.temp, localpath(self.__name))
1296 rename(self.temp, localpath(self.__name))
1287
1297
1288 def __del__(self):
1298 def __del__(self):
1289 if not self.closed:
1299 if not self.closed:
1290 try:
1300 try:
1291 os.unlink(self.temp)
1301 os.unlink(self.temp)
1292 except: pass
1302 except: pass
1293 posixfile.close(self)
1303 posixfile.close(self)
1294
1304
1295 class opener(object):
1305 class opener(object):
1296 """Open files relative to a base directory
1306 """Open files relative to a base directory
1297
1307
1298 This class is used to hide the details of COW semantics and
1308 This class is used to hide the details of COW semantics and
1299 remote file access from higher level code.
1309 remote file access from higher level code.
1300 """
1310 """
1301 def __init__(self, base, audit=True):
1311 def __init__(self, base, audit=True):
1302 self.base = base
1312 self.base = base
1303 if audit:
1313 if audit:
1304 self.audit_path = path_auditor(base)
1314 self.audit_path = path_auditor(base)
1305 else:
1315 else:
1306 self.audit_path = always
1316 self.audit_path = always
1307
1317
1308 def __getattr__(self, name):
1318 def __getattr__(self, name):
1309 if name == '_can_symlink':
1319 if name == '_can_symlink':
1310 self._can_symlink = checklink(self.base)
1320 self._can_symlink = checklink(self.base)
1311 return self._can_symlink
1321 return self._can_symlink
1312 raise AttributeError(name)
1322 raise AttributeError(name)
1313
1323
1314 def __call__(self, path, mode="r", text=False, atomictemp=False):
1324 def __call__(self, path, mode="r", text=False, atomictemp=False):
1315 self.audit_path(path)
1325 self.audit_path(path)
1316 f = os.path.join(self.base, path)
1326 f = os.path.join(self.base, path)
1317
1327
1318 if not text and "b" not in mode:
1328 if not text and "b" not in mode:
1319 mode += "b" # for that other OS
1329 mode += "b" # for that other OS
1320
1330
1321 if mode[0] != "r":
1331 if mode[0] != "r":
1322 try:
1332 try:
1323 nlink = nlinks(f)
1333 nlink = nlinks(f)
1324 except OSError:
1334 except OSError:
1325 nlink = 0
1335 nlink = 0
1326 d = os.path.dirname(f)
1336 d = os.path.dirname(f)
1327 if not os.path.isdir(d):
1337 if not os.path.isdir(d):
1328 os.makedirs(d)
1338 os.makedirs(d)
1329 if atomictemp:
1339 if atomictemp:
1330 return atomictempfile(f, mode)
1340 return atomictempfile(f, mode)
1331 if nlink > 1:
1341 if nlink > 1:
1332 rename(mktempcopy(f), f)
1342 rename(mktempcopy(f), f)
1333 return posixfile(f, mode)
1343 return posixfile(f, mode)
1334
1344
1335 def symlink(self, src, dst):
1345 def symlink(self, src, dst):
1336 self.audit_path(dst)
1346 self.audit_path(dst)
1337 linkname = os.path.join(self.base, dst)
1347 linkname = os.path.join(self.base, dst)
1338 try:
1348 try:
1339 os.unlink(linkname)
1349 os.unlink(linkname)
1340 except OSError:
1350 except OSError:
1341 pass
1351 pass
1342
1352
1343 dirname = os.path.dirname(linkname)
1353 dirname = os.path.dirname(linkname)
1344 if not os.path.exists(dirname):
1354 if not os.path.exists(dirname):
1345 os.makedirs(dirname)
1355 os.makedirs(dirname)
1346
1356
1347 if self._can_symlink:
1357 if self._can_symlink:
1348 try:
1358 try:
1349 os.symlink(src, linkname)
1359 os.symlink(src, linkname)
1350 except OSError, err:
1360 except OSError, err:
1351 raise OSError(err.errno, _('could not symlink to %r: %s') %
1361 raise OSError(err.errno, _('could not symlink to %r: %s') %
1352 (src, err.strerror), linkname)
1362 (src, err.strerror), linkname)
1353 else:
1363 else:
1354 f = self(dst, "w")
1364 f = self(dst, "w")
1355 f.write(src)
1365 f.write(src)
1356 f.close()
1366 f.close()
1357
1367
1358 class chunkbuffer(object):
1368 class chunkbuffer(object):
1359 """Allow arbitrary sized chunks of data to be efficiently read from an
1369 """Allow arbitrary sized chunks of data to be efficiently read from an
1360 iterator over chunks of arbitrary size."""
1370 iterator over chunks of arbitrary size."""
1361
1371
1362 def __init__(self, in_iter, targetsize = 2**16):
1372 def __init__(self, in_iter, targetsize = 2**16):
1363 """in_iter is the iterator that's iterating over the input chunks.
1373 """in_iter is the iterator that's iterating over the input chunks.
1364 targetsize is how big a buffer to try to maintain."""
1374 targetsize is how big a buffer to try to maintain."""
1365 self.in_iter = iter(in_iter)
1375 self.in_iter = iter(in_iter)
1366 self.buf = ''
1376 self.buf = ''
1367 self.targetsize = int(targetsize)
1377 self.targetsize = int(targetsize)
1368 if self.targetsize <= 0:
1378 if self.targetsize <= 0:
1369 raise ValueError(_("targetsize must be greater than 0, was %d") %
1379 raise ValueError(_("targetsize must be greater than 0, was %d") %
1370 targetsize)
1380 targetsize)
1371 self.iterempty = False
1381 self.iterempty = False
1372
1382
1373 def fillbuf(self):
1383 def fillbuf(self):
1374 """Ignore target size; read every chunk from iterator until empty."""
1384 """Ignore target size; read every chunk from iterator until empty."""
1375 if not self.iterempty:
1385 if not self.iterempty:
1376 collector = cStringIO.StringIO()
1386 collector = cStringIO.StringIO()
1377 collector.write(self.buf)
1387 collector.write(self.buf)
1378 for ch in self.in_iter:
1388 for ch in self.in_iter:
1379 collector.write(ch)
1389 collector.write(ch)
1380 self.buf = collector.getvalue()
1390 self.buf = collector.getvalue()
1381 self.iterempty = True
1391 self.iterempty = True
1382
1392
1383 def read(self, l):
1393 def read(self, l):
1384 """Read L bytes of data from the iterator of chunks of data.
1394 """Read L bytes of data from the iterator of chunks of data.
1385 Returns less than L bytes if the iterator runs dry."""
1395 Returns less than L bytes if the iterator runs dry."""
1386 if l > len(self.buf) and not self.iterempty:
1396 if l > len(self.buf) and not self.iterempty:
1387 # Clamp to a multiple of self.targetsize
1397 # Clamp to a multiple of self.targetsize
1388 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1398 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1389 collector = cStringIO.StringIO()
1399 collector = cStringIO.StringIO()
1390 collector.write(self.buf)
1400 collector.write(self.buf)
1391 collected = len(self.buf)
1401 collected = len(self.buf)
1392 for chunk in self.in_iter:
1402 for chunk in self.in_iter:
1393 collector.write(chunk)
1403 collector.write(chunk)
1394 collected += len(chunk)
1404 collected += len(chunk)
1395 if collected >= targetsize:
1405 if collected >= targetsize:
1396 break
1406 break
1397 if collected < targetsize:
1407 if collected < targetsize:
1398 self.iterempty = True
1408 self.iterempty = True
1399 self.buf = collector.getvalue()
1409 self.buf = collector.getvalue()
1400 s, self.buf = self.buf[:l], buffer(self.buf, l)
1410 s, self.buf = self.buf[:l], buffer(self.buf, l)
1401 return s
1411 return s
1402
1412
1403 def filechunkiter(f, size=65536, limit=None):
1413 def filechunkiter(f, size=65536, limit=None):
1404 """Create a generator that produces the data in the file size
1414 """Create a generator that produces the data in the file size
1405 (default 65536) bytes at a time, up to optional limit (default is
1415 (default 65536) bytes at a time, up to optional limit (default is
1406 to read all data). Chunks may be less than size bytes if the
1416 to read all data). Chunks may be less than size bytes if the
1407 chunk is the last chunk in the file, or the file is a socket or
1417 chunk is the last chunk in the file, or the file is a socket or
1408 some other type of file that sometimes reads less data than is
1418 some other type of file that sometimes reads less data than is
1409 requested."""
1419 requested."""
1410 assert size >= 0
1420 assert size >= 0
1411 assert limit is None or limit >= 0
1421 assert limit is None or limit >= 0
1412 while True:
1422 while True:
1413 if limit is None: nbytes = size
1423 if limit is None: nbytes = size
1414 else: nbytes = min(limit, size)
1424 else: nbytes = min(limit, size)
1415 s = nbytes and f.read(nbytes)
1425 s = nbytes and f.read(nbytes)
1416 if not s: break
1426 if not s: break
1417 if limit: limit -= len(s)
1427 if limit: limit -= len(s)
1418 yield s
1428 yield s
1419
1429
1420 def makedate():
1430 def makedate():
1421 lt = time.localtime()
1431 lt = time.localtime()
1422 if lt[8] == 1 and time.daylight:
1432 if lt[8] == 1 and time.daylight:
1423 tz = time.altzone
1433 tz = time.altzone
1424 else:
1434 else:
1425 tz = time.timezone
1435 tz = time.timezone
1426 return time.mktime(lt), tz
1436 return time.mktime(lt), tz
1427
1437
1428 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1438 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1429 """represent a (unixtime, offset) tuple as a localized time.
1439 """represent a (unixtime, offset) tuple as a localized time.
1430 unixtime is seconds since the epoch, and offset is the time zone's
1440 unixtime is seconds since the epoch, and offset is the time zone's
1431 number of seconds away from UTC. if timezone is false, do not
1441 number of seconds away from UTC. if timezone is false, do not
1432 append time zone to string."""
1442 append time zone to string."""
1433 t, tz = date or makedate()
1443 t, tz = date or makedate()
1434 s = time.strftime(format, time.gmtime(float(t) - tz))
1444 s = time.strftime(format, time.gmtime(float(t) - tz))
1435 if timezone:
1445 if timezone:
1436 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1446 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1437 return s
1447 return s
1438
1448
1439 def strdate(string, format, defaults):
1449 def strdate(string, format, defaults):
1440 """parse a localized time string and return a (unixtime, offset) tuple.
1450 """parse a localized time string and return a (unixtime, offset) tuple.
1441 if the string cannot be parsed, ValueError is raised."""
1451 if the string cannot be parsed, ValueError is raised."""
1442 def timezone(string):
1452 def timezone(string):
1443 tz = string.split()[-1]
1453 tz = string.split()[-1]
1444 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1454 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1445 tz = int(tz)
1455 tz = int(tz)
1446 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1456 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1447 return offset
1457 return offset
1448 if tz == "GMT" or tz == "UTC":
1458 if tz == "GMT" or tz == "UTC":
1449 return 0
1459 return 0
1450 return None
1460 return None
1451
1461
1452 # NOTE: unixtime = localunixtime + offset
1462 # NOTE: unixtime = localunixtime + offset
1453 offset, date = timezone(string), string
1463 offset, date = timezone(string), string
1454 if offset != None:
1464 if offset != None:
1455 date = " ".join(string.split()[:-1])
1465 date = " ".join(string.split()[:-1])
1456
1466
1457 # add missing elements from defaults
1467 # add missing elements from defaults
1458 for part in defaults:
1468 for part in defaults:
1459 found = [True for p in part if ("%"+p) in format]
1469 found = [True for p in part if ("%"+p) in format]
1460 if not found:
1470 if not found:
1461 date += "@" + defaults[part]
1471 date += "@" + defaults[part]
1462 format += "@%" + part[0]
1472 format += "@%" + part[0]
1463
1473
1464 timetuple = time.strptime(date, format)
1474 timetuple = time.strptime(date, format)
1465 localunixtime = int(calendar.timegm(timetuple))
1475 localunixtime = int(calendar.timegm(timetuple))
1466 if offset is None:
1476 if offset is None:
1467 # local timezone
1477 # local timezone
1468 unixtime = int(time.mktime(timetuple))
1478 unixtime = int(time.mktime(timetuple))
1469 offset = unixtime - localunixtime
1479 offset = unixtime - localunixtime
1470 else:
1480 else:
1471 unixtime = localunixtime + offset
1481 unixtime = localunixtime + offset
1472 return unixtime, offset
1482 return unixtime, offset
1473
1483
1474 def parsedate(string, formats=None, defaults=None):
1484 def parsedate(string, formats=None, defaults=None):
1475 """parse a localized time string and return a (unixtime, offset) tuple.
1485 """parse a localized time string and return a (unixtime, offset) tuple.
1476 The date may be a "unixtime offset" string or in one of the specified
1486 The date may be a "unixtime offset" string or in one of the specified
1477 formats."""
1487 formats."""
1478 if not string:
1488 if not string:
1479 return 0, 0
1489 return 0, 0
1480 if not formats:
1490 if not formats:
1481 formats = defaultdateformats
1491 formats = defaultdateformats
1482 string = string.strip()
1492 string = string.strip()
1483 try:
1493 try:
1484 when, offset = map(int, string.split(' '))
1494 when, offset = map(int, string.split(' '))
1485 except ValueError:
1495 except ValueError:
1486 # fill out defaults
1496 # fill out defaults
1487 if not defaults:
1497 if not defaults:
1488 defaults = {}
1498 defaults = {}
1489 now = makedate()
1499 now = makedate()
1490 for part in "d mb yY HI M S".split():
1500 for part in "d mb yY HI M S".split():
1491 if part not in defaults:
1501 if part not in defaults:
1492 if part[0] in "HMS":
1502 if part[0] in "HMS":
1493 defaults[part] = "00"
1503 defaults[part] = "00"
1494 elif part[0] in "dm":
1504 elif part[0] in "dm":
1495 defaults[part] = "1"
1505 defaults[part] = "1"
1496 else:
1506 else:
1497 defaults[part] = datestr(now, "%" + part[0], False)
1507 defaults[part] = datestr(now, "%" + part[0], False)
1498
1508
1499 for format in formats:
1509 for format in formats:
1500 try:
1510 try:
1501 when, offset = strdate(string, format, defaults)
1511 when, offset = strdate(string, format, defaults)
1502 except ValueError:
1512 except ValueError:
1503 pass
1513 pass
1504 else:
1514 else:
1505 break
1515 break
1506 else:
1516 else:
1507 raise Abort(_('invalid date: %r ') % string)
1517 raise Abort(_('invalid date: %r ') % string)
1508 # validate explicit (probably user-specified) date and
1518 # validate explicit (probably user-specified) date and
1509 # time zone offset. values must fit in signed 32 bits for
1519 # time zone offset. values must fit in signed 32 bits for
1510 # current 32-bit linux runtimes. timezones go from UTC-12
1520 # current 32-bit linux runtimes. timezones go from UTC-12
1511 # to UTC+14
1521 # to UTC+14
1512 if abs(when) > 0x7fffffff:
1522 if abs(when) > 0x7fffffff:
1513 raise Abort(_('date exceeds 32 bits: %d') % when)
1523 raise Abort(_('date exceeds 32 bits: %d') % when)
1514 if offset < -50400 or offset > 43200:
1524 if offset < -50400 or offset > 43200:
1515 raise Abort(_('impossible time zone offset: %d') % offset)
1525 raise Abort(_('impossible time zone offset: %d') % offset)
1516 return when, offset
1526 return when, offset
1517
1527
1518 def matchdate(date):
1528 def matchdate(date):
1519 """Return a function that matches a given date match specifier
1529 """Return a function that matches a given date match specifier
1520
1530
1521 Formats include:
1531 Formats include:
1522
1532
1523 '{date}' match a given date to the accuracy provided
1533 '{date}' match a given date to the accuracy provided
1524
1534
1525 '<{date}' on or before a given date
1535 '<{date}' on or before a given date
1526
1536
1527 '>{date}' on or after a given date
1537 '>{date}' on or after a given date
1528
1538
1529 """
1539 """
1530
1540
1531 def lower(date):
1541 def lower(date):
1532 return parsedate(date, extendeddateformats)[0]
1542 return parsedate(date, extendeddateformats)[0]
1533
1543
1534 def upper(date):
1544 def upper(date):
1535 d = dict(mb="12", HI="23", M="59", S="59")
1545 d = dict(mb="12", HI="23", M="59", S="59")
1536 for days in "31 30 29".split():
1546 for days in "31 30 29".split():
1537 try:
1547 try:
1538 d["d"] = days
1548 d["d"] = days
1539 return parsedate(date, extendeddateformats, d)[0]
1549 return parsedate(date, extendeddateformats, d)[0]
1540 except:
1550 except:
1541 pass
1551 pass
1542 d["d"] = "28"
1552 d["d"] = "28"
1543 return parsedate(date, extendeddateformats, d)[0]
1553 return parsedate(date, extendeddateformats, d)[0]
1544
1554
1545 if date[0] == "<":
1555 if date[0] == "<":
1546 when = upper(date[1:])
1556 when = upper(date[1:])
1547 return lambda x: x <= when
1557 return lambda x: x <= when
1548 elif date[0] == ">":
1558 elif date[0] == ">":
1549 when = lower(date[1:])
1559 when = lower(date[1:])
1550 return lambda x: x >= when
1560 return lambda x: x >= when
1551 elif date[0] == "-":
1561 elif date[0] == "-":
1552 try:
1562 try:
1553 days = int(date[1:])
1563 days = int(date[1:])
1554 except ValueError:
1564 except ValueError:
1555 raise Abort(_("invalid day spec: %s") % date[1:])
1565 raise Abort(_("invalid day spec: %s") % date[1:])
1556 when = makedate()[0] - days * 3600 * 24
1566 when = makedate()[0] - days * 3600 * 24
1557 return lambda x: x >= when
1567 return lambda x: x >= when
1558 elif " to " in date:
1568 elif " to " in date:
1559 a, b = date.split(" to ")
1569 a, b = date.split(" to ")
1560 start, stop = lower(a), upper(b)
1570 start, stop = lower(a), upper(b)
1561 return lambda x: x >= start and x <= stop
1571 return lambda x: x >= start and x <= stop
1562 else:
1572 else:
1563 start, stop = lower(date), upper(date)
1573 start, stop = lower(date), upper(date)
1564 return lambda x: x >= start and x <= stop
1574 return lambda x: x >= start and x <= stop
1565
1575
1566 def shortuser(user):
1576 def shortuser(user):
1567 """Return a short representation of a user name or email address."""
1577 """Return a short representation of a user name or email address."""
1568 f = user.find('@')
1578 f = user.find('@')
1569 if f >= 0:
1579 if f >= 0:
1570 user = user[:f]
1580 user = user[:f]
1571 f = user.find('<')
1581 f = user.find('<')
1572 if f >= 0:
1582 if f >= 0:
1573 user = user[f+1:]
1583 user = user[f+1:]
1574 f = user.find(' ')
1584 f = user.find(' ')
1575 if f >= 0:
1585 if f >= 0:
1576 user = user[:f]
1586 user = user[:f]
1577 f = user.find('.')
1587 f = user.find('.')
1578 if f >= 0:
1588 if f >= 0:
1579 user = user[:f]
1589 user = user[:f]
1580 return user
1590 return user
1581
1591
1582 def ellipsis(text, maxlength=400):
1592 def ellipsis(text, maxlength=400):
1583 """Trim string to at most maxlength (default: 400) characters."""
1593 """Trim string to at most maxlength (default: 400) characters."""
1584 if len(text) <= maxlength:
1594 if len(text) <= maxlength:
1585 return text
1595 return text
1586 else:
1596 else:
1587 return "%s..." % (text[:maxlength-3])
1597 return "%s..." % (text[:maxlength-3])
1588
1598
1589 def walkrepos(path):
1599 def walkrepos(path):
1590 '''yield every hg repository under path, recursively.'''
1600 '''yield every hg repository under path, recursively.'''
1591 def errhandler(err):
1601 def errhandler(err):
1592 if err.filename == path:
1602 if err.filename == path:
1593 raise err
1603 raise err
1594
1604
1595 for root, dirs, files in os.walk(path, onerror=errhandler):
1605 for root, dirs, files in os.walk(path, onerror=errhandler):
1596 for d in dirs:
1606 for d in dirs:
1597 if d == '.hg':
1607 if d == '.hg':
1598 yield root
1608 yield root
1599 dirs[:] = []
1609 dirs[:] = []
1600 break
1610 break
1601
1611
1602 _rcpath = None
1612 _rcpath = None
1603
1613
1604 def os_rcpath():
1614 def os_rcpath():
1605 '''return default os-specific hgrc search path'''
1615 '''return default os-specific hgrc search path'''
1606 path = system_rcpath()
1616 path = system_rcpath()
1607 path.extend(user_rcpath())
1617 path.extend(user_rcpath())
1608 path = [os.path.normpath(f) for f in path]
1618 path = [os.path.normpath(f) for f in path]
1609 return path
1619 return path
1610
1620
1611 def rcpath():
1621 def rcpath():
1612 '''return hgrc search path. if env var HGRCPATH is set, use it.
1622 '''return hgrc search path. if env var HGRCPATH is set, use it.
1613 for each item in path, if directory, use files ending in .rc,
1623 for each item in path, if directory, use files ending in .rc,
1614 else use item.
1624 else use item.
1615 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1625 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1616 if no HGRCPATH, use default os-specific path.'''
1626 if no HGRCPATH, use default os-specific path.'''
1617 global _rcpath
1627 global _rcpath
1618 if _rcpath is None:
1628 if _rcpath is None:
1619 if 'HGRCPATH' in os.environ:
1629 if 'HGRCPATH' in os.environ:
1620 _rcpath = []
1630 _rcpath = []
1621 for p in os.environ['HGRCPATH'].split(os.pathsep):
1631 for p in os.environ['HGRCPATH'].split(os.pathsep):
1622 if not p: continue
1632 if not p: continue
1623 if os.path.isdir(p):
1633 if os.path.isdir(p):
1624 for f in os.listdir(p):
1634 for f in os.listdir(p):
1625 if f.endswith('.rc'):
1635 if f.endswith('.rc'):
1626 _rcpath.append(os.path.join(p, f))
1636 _rcpath.append(os.path.join(p, f))
1627 else:
1637 else:
1628 _rcpath.append(p)
1638 _rcpath.append(p)
1629 else:
1639 else:
1630 _rcpath = os_rcpath()
1640 _rcpath = os_rcpath()
1631 return _rcpath
1641 return _rcpath
1632
1642
1633 def bytecount(nbytes):
1643 def bytecount(nbytes):
1634 '''return byte count formatted as readable string, with units'''
1644 '''return byte count formatted as readable string, with units'''
1635
1645
1636 units = (
1646 units = (
1637 (100, 1<<30, _('%.0f GB')),
1647 (100, 1<<30, _('%.0f GB')),
1638 (10, 1<<30, _('%.1f GB')),
1648 (10, 1<<30, _('%.1f GB')),
1639 (1, 1<<30, _('%.2f GB')),
1649 (1, 1<<30, _('%.2f GB')),
1640 (100, 1<<20, _('%.0f MB')),
1650 (100, 1<<20, _('%.0f MB')),
1641 (10, 1<<20, _('%.1f MB')),
1651 (10, 1<<20, _('%.1f MB')),
1642 (1, 1<<20, _('%.2f MB')),
1652 (1, 1<<20, _('%.2f MB')),
1643 (100, 1<<10, _('%.0f KB')),
1653 (100, 1<<10, _('%.0f KB')),
1644 (10, 1<<10, _('%.1f KB')),
1654 (10, 1<<10, _('%.1f KB')),
1645 (1, 1<<10, _('%.2f KB')),
1655 (1, 1<<10, _('%.2f KB')),
1646 (1, 1, _('%.0f bytes')),
1656 (1, 1, _('%.0f bytes')),
1647 )
1657 )
1648
1658
1649 for multiplier, divisor, format in units:
1659 for multiplier, divisor, format in units:
1650 if nbytes >= divisor * multiplier:
1660 if nbytes >= divisor * multiplier:
1651 return format % (nbytes / float(divisor))
1661 return format % (nbytes / float(divisor))
1652 return units[-1][2] % nbytes
1662 return units[-1][2] % nbytes
1653
1663
1654 def drop_scheme(scheme, path):
1664 def drop_scheme(scheme, path):
1655 sc = scheme + ':'
1665 sc = scheme + ':'
1656 if path.startswith(sc):
1666 if path.startswith(sc):
1657 path = path[len(sc):]
1667 path = path[len(sc):]
1658 if path.startswith('//'):
1668 if path.startswith('//'):
1659 path = path[2:]
1669 path = path[2:]
1660 return path
1670 return path
@@ -1,24 +1,28 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init a
3 hg init a
4 cd a
4 cd a
5 hg init b
5 hg init b
6 echo x > b/x
6 echo x > b/x
7
7
8 echo '# should print nothing'
8 echo '# should print nothing'
9 hg add b
9 hg add b
10 hg st
10 hg st
11
11
12 echo '# should fail'
12 echo '# should fail'
13 hg st b/x
13 hg st b/x
14 hg add b/x
14 hg add b/x
15
15
16 echo '# should fail'
17 hg add b b/x
18 hg st
19
16 echo '# should arguably print nothing'
20 echo '# should arguably print nothing'
17 hg st b
21 hg st b
18
22
19 echo a > a
23 echo a > a
20 hg ci -Ama a
24 hg ci -Ama a
21
25
22 echo '# should fail'
26 echo '# should fail'
23 hg mv a b
27 hg mv a b
24 hg st
28 hg st
@@ -1,7 +1,9 b''
1 # should print nothing
1 # should print nothing
2 # should fail
2 # should fail
3 abort: path 'b/x' is inside repo 'b'
3 abort: path 'b/x' is inside repo 'b'
4 abort: path 'b/x' is inside repo 'b'
4 abort: path 'b/x' is inside repo 'b'
5 # should fail
6 abort: path 'b/x' is inside repo 'b'
5 # should arguably print nothing
7 # should arguably print nothing
6 # should fail
8 # should fail
7 abort: path 'b/a' is inside repo 'b'
9 abort: path 'b/a' is inside repo 'b'
General Comments 0
You need to be logged in to leave comments. Login now