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