##// END OF EJS Templates
util._matcher: fix handling of path: patterns
Alexis S. L. Carvalho -
r4189:b1716a1f default
parent child Browse files
Show More
@@ -1,1377 +1,1377
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, '(?:|.*/)', '(?:/|$)')
436 return head + globre(name, '(?:|.*/)', '(?:/|$)')
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) or '.'
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 elif kind == 'relglob':
482 elif kind in ('relglob', 'path'):
483 name = normpath(name)
483 name = normpath(name)
484 if kind in ('glob', 'path', 're', 'relglob'):
484 if kind in ('glob', 're', 'relglob'):
485 pats.append((kind, name))
485 pats.append((kind, name))
486 if kind == 'glob':
486 if kind == 'glob':
487 root = globprefix(name)
487 root = globprefix(name)
488 roots.append(root)
488 roots.append(root)
489 elif kind == 'relpath':
489 elif kind in ('relpath', 'path'):
490 files.append((kind, name))
490 files.append((kind, name))
491 roots.append(name)
491 roots.append(name)
492 elif kind == 'relglob':
492 elif kind == 'relglob':
493 roots.append('.')
493 roots.append('.')
494
494
495 patmatch = matchfn(pats, '$') or always
495 patmatch = matchfn(pats, '$') or always
496 filematch = matchfn(files, '(?:/|$)') or always
496 filematch = matchfn(files, '(?:/|$)') or always
497 incmatch = always
497 incmatch = always
498 if inc:
498 if inc:
499 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
499 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
500 incmatch = matchfn(inckinds, '(?:/|$)')
500 incmatch = matchfn(inckinds, '(?:/|$)')
501 excmatch = lambda fn: False
501 excmatch = lambda fn: False
502 if exc:
502 if exc:
503 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
503 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
504 excmatch = matchfn(exckinds, '(?:/|$)')
504 excmatch = matchfn(exckinds, '(?:/|$)')
505
505
506 return (roots,
506 return (roots,
507 lambda fn: (incmatch(fn) and not excmatch(fn) and
507 lambda fn: (incmatch(fn) and not excmatch(fn) and
508 (fn.endswith('/') or
508 (fn.endswith('/') or
509 (not pats and not files) or
509 (not pats and not files) or
510 (pats and patmatch(fn)) or
510 (pats and patmatch(fn)) or
511 (files and filematch(fn)))),
511 (files and filematch(fn)))),
512 (inc or exc or (pats and pats != [('glob', '**')])) and True)
512 (inc or exc or (pats and pats != [('glob', '**')])) and True)
513
513
514 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
514 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
515 '''enhanced shell command execution.
515 '''enhanced shell command execution.
516 run with environment maybe modified, maybe in different dir.
516 run with environment maybe modified, maybe in different dir.
517
517
518 if command fails and onerr is None, return status. if ui object,
518 if command fails and onerr is None, return status. if ui object,
519 print error message and return status, else raise onerr object as
519 print error message and return status, else raise onerr object as
520 exception.'''
520 exception.'''
521 def py2shell(val):
521 def py2shell(val):
522 'convert python object into string that is useful to shell'
522 'convert python object into string that is useful to shell'
523 if val in (None, False):
523 if val in (None, False):
524 return '0'
524 return '0'
525 if val == True:
525 if val == True:
526 return '1'
526 return '1'
527 return str(val)
527 return str(val)
528 oldenv = {}
528 oldenv = {}
529 for k in environ:
529 for k in environ:
530 oldenv[k] = os.environ.get(k)
530 oldenv[k] = os.environ.get(k)
531 if cwd is not None:
531 if cwd is not None:
532 oldcwd = os.getcwd()
532 oldcwd = os.getcwd()
533 origcmd = cmd
533 origcmd = cmd
534 if os.name == 'nt':
534 if os.name == 'nt':
535 cmd = '"%s"' % cmd
535 cmd = '"%s"' % cmd
536 try:
536 try:
537 for k, v in environ.iteritems():
537 for k, v in environ.iteritems():
538 os.environ[k] = py2shell(v)
538 os.environ[k] = py2shell(v)
539 if cwd is not None and oldcwd != cwd:
539 if cwd is not None and oldcwd != cwd:
540 os.chdir(cwd)
540 os.chdir(cwd)
541 rc = os.system(cmd)
541 rc = os.system(cmd)
542 if rc and onerr:
542 if rc and onerr:
543 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
543 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
544 explain_exit(rc)[0])
544 explain_exit(rc)[0])
545 if errprefix:
545 if errprefix:
546 errmsg = '%s: %s' % (errprefix, errmsg)
546 errmsg = '%s: %s' % (errprefix, errmsg)
547 try:
547 try:
548 onerr.warn(errmsg + '\n')
548 onerr.warn(errmsg + '\n')
549 except AttributeError:
549 except AttributeError:
550 raise onerr(errmsg)
550 raise onerr(errmsg)
551 return rc
551 return rc
552 finally:
552 finally:
553 for k, v in oldenv.iteritems():
553 for k, v in oldenv.iteritems():
554 if v is None:
554 if v is None:
555 del os.environ[k]
555 del os.environ[k]
556 else:
556 else:
557 os.environ[k] = v
557 os.environ[k] = v
558 if cwd is not None and oldcwd != cwd:
558 if cwd is not None and oldcwd != cwd:
559 os.chdir(oldcwd)
559 os.chdir(oldcwd)
560
560
561 def rename(src, dst):
561 def rename(src, dst):
562 """forcibly rename a file"""
562 """forcibly rename a file"""
563 try:
563 try:
564 os.rename(src, dst)
564 os.rename(src, dst)
565 except OSError, err:
565 except OSError, err:
566 # on windows, rename to existing file is not allowed, so we
566 # on windows, rename to existing file is not allowed, so we
567 # must delete destination first. but if file is open, unlink
567 # must delete destination first. but if file is open, unlink
568 # schedules it for delete but does not delete it. rename
568 # schedules it for delete but does not delete it. rename
569 # happens immediately even for open files, so we create
569 # happens immediately even for open files, so we create
570 # temporary file, delete it, rename destination to that name,
570 # temporary file, delete it, rename destination to that name,
571 # then delete that. then rename is safe to do.
571 # then delete that. then rename is safe to do.
572 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
572 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
573 os.close(fd)
573 os.close(fd)
574 os.unlink(temp)
574 os.unlink(temp)
575 os.rename(dst, temp)
575 os.rename(dst, temp)
576 os.unlink(temp)
576 os.unlink(temp)
577 os.rename(src, dst)
577 os.rename(src, dst)
578
578
579 def unlink(f):
579 def unlink(f):
580 """unlink and remove the directory if it is empty"""
580 """unlink and remove the directory if it is empty"""
581 os.unlink(f)
581 os.unlink(f)
582 # try removing directories that might now be empty
582 # try removing directories that might now be empty
583 try:
583 try:
584 os.removedirs(os.path.dirname(f))
584 os.removedirs(os.path.dirname(f))
585 except OSError:
585 except OSError:
586 pass
586 pass
587
587
588 def copyfile(src, dest):
588 def copyfile(src, dest):
589 "copy a file, preserving mode"
589 "copy a file, preserving mode"
590 try:
590 try:
591 shutil.copyfile(src, dest)
591 shutil.copyfile(src, dest)
592 shutil.copymode(src, dest)
592 shutil.copymode(src, dest)
593 except shutil.Error, inst:
593 except shutil.Error, inst:
594 raise Abort(str(inst))
594 raise Abort(str(inst))
595
595
596 def copyfiles(src, dst, hardlink=None):
596 def copyfiles(src, dst, hardlink=None):
597 """Copy a directory tree using hardlinks if possible"""
597 """Copy a directory tree using hardlinks if possible"""
598
598
599 if hardlink is None:
599 if hardlink is None:
600 hardlink = (os.stat(src).st_dev ==
600 hardlink = (os.stat(src).st_dev ==
601 os.stat(os.path.dirname(dst)).st_dev)
601 os.stat(os.path.dirname(dst)).st_dev)
602
602
603 if os.path.isdir(src):
603 if os.path.isdir(src):
604 os.mkdir(dst)
604 os.mkdir(dst)
605 for name in os.listdir(src):
605 for name in os.listdir(src):
606 srcname = os.path.join(src, name)
606 srcname = os.path.join(src, name)
607 dstname = os.path.join(dst, name)
607 dstname = os.path.join(dst, name)
608 copyfiles(srcname, dstname, hardlink)
608 copyfiles(srcname, dstname, hardlink)
609 else:
609 else:
610 if hardlink:
610 if hardlink:
611 try:
611 try:
612 os_link(src, dst)
612 os_link(src, dst)
613 except (IOError, OSError):
613 except (IOError, OSError):
614 hardlink = False
614 hardlink = False
615 shutil.copy(src, dst)
615 shutil.copy(src, dst)
616 else:
616 else:
617 shutil.copy(src, dst)
617 shutil.copy(src, dst)
618
618
619 def audit_path(path):
619 def audit_path(path):
620 """Abort if path contains dangerous components"""
620 """Abort if path contains dangerous components"""
621 parts = os.path.normcase(path).split(os.sep)
621 parts = os.path.normcase(path).split(os.sep)
622 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
622 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
623 or os.pardir in parts):
623 or os.pardir in parts):
624 raise Abort(_("path contains illegal component: %s\n") % path)
624 raise Abort(_("path contains illegal component: %s\n") % path)
625
625
626 def _makelock_file(info, pathname):
626 def _makelock_file(info, pathname):
627 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
627 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
628 os.write(ld, info)
628 os.write(ld, info)
629 os.close(ld)
629 os.close(ld)
630
630
631 def _readlock_file(pathname):
631 def _readlock_file(pathname):
632 return posixfile(pathname).read()
632 return posixfile(pathname).read()
633
633
634 def nlinks(pathname):
634 def nlinks(pathname):
635 """Return number of hardlinks for the given file."""
635 """Return number of hardlinks for the given file."""
636 return os.lstat(pathname).st_nlink
636 return os.lstat(pathname).st_nlink
637
637
638 if hasattr(os, 'link'):
638 if hasattr(os, 'link'):
639 os_link = os.link
639 os_link = os.link
640 else:
640 else:
641 def os_link(src, dst):
641 def os_link(src, dst):
642 raise OSError(0, _("Hardlinks not supported"))
642 raise OSError(0, _("Hardlinks not supported"))
643
643
644 def fstat(fp):
644 def fstat(fp):
645 '''stat file object that may not have fileno method.'''
645 '''stat file object that may not have fileno method.'''
646 try:
646 try:
647 return os.fstat(fp.fileno())
647 return os.fstat(fp.fileno())
648 except AttributeError:
648 except AttributeError:
649 return os.stat(fp.name)
649 return os.stat(fp.name)
650
650
651 posixfile = file
651 posixfile = file
652
652
653 def is_win_9x():
653 def is_win_9x():
654 '''return true if run on windows 95, 98 or me.'''
654 '''return true if run on windows 95, 98 or me.'''
655 try:
655 try:
656 return sys.getwindowsversion()[3] == 1
656 return sys.getwindowsversion()[3] == 1
657 except AttributeError:
657 except AttributeError:
658 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
658 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
659
659
660 getuser_fallback = None
660 getuser_fallback = None
661
661
662 def getuser():
662 def getuser():
663 '''return name of current user'''
663 '''return name of current user'''
664 try:
664 try:
665 return getpass.getuser()
665 return getpass.getuser()
666 except ImportError:
666 except ImportError:
667 # import of pwd will fail on windows - try fallback
667 # import of pwd will fail on windows - try fallback
668 if getuser_fallback:
668 if getuser_fallback:
669 return getuser_fallback()
669 return getuser_fallback()
670 # raised if win32api not available
670 # raised if win32api not available
671 raise Abort(_('user name not available - set USERNAME '
671 raise Abort(_('user name not available - set USERNAME '
672 'environment variable'))
672 'environment variable'))
673
673
674 def username(uid=None):
674 def username(uid=None):
675 """Return the name of the user with the given uid.
675 """Return the name of the user with the given uid.
676
676
677 If uid is None, return the name of the current user."""
677 If uid is None, return the name of the current user."""
678 try:
678 try:
679 import pwd
679 import pwd
680 if uid is None:
680 if uid is None:
681 uid = os.getuid()
681 uid = os.getuid()
682 try:
682 try:
683 return pwd.getpwuid(uid)[0]
683 return pwd.getpwuid(uid)[0]
684 except KeyError:
684 except KeyError:
685 return str(uid)
685 return str(uid)
686 except ImportError:
686 except ImportError:
687 return None
687 return None
688
688
689 def groupname(gid=None):
689 def groupname(gid=None):
690 """Return the name of the group with the given gid.
690 """Return the name of the group with the given gid.
691
691
692 If gid is None, return the name of the current group."""
692 If gid is None, return the name of the current group."""
693 try:
693 try:
694 import grp
694 import grp
695 if gid is None:
695 if gid is None:
696 gid = os.getgid()
696 gid = os.getgid()
697 try:
697 try:
698 return grp.getgrgid(gid)[0]
698 return grp.getgrgid(gid)[0]
699 except KeyError:
699 except KeyError:
700 return str(gid)
700 return str(gid)
701 except ImportError:
701 except ImportError:
702 return None
702 return None
703
703
704 # File system features
704 # File system features
705
705
706 def checkfolding(path):
706 def checkfolding(path):
707 """
707 """
708 Check whether the given path is on a case-sensitive filesystem
708 Check whether the given path is on a case-sensitive filesystem
709
709
710 Requires a path (like /foo/.hg) ending with a foldable final
710 Requires a path (like /foo/.hg) ending with a foldable final
711 directory component.
711 directory component.
712 """
712 """
713 s1 = os.stat(path)
713 s1 = os.stat(path)
714 d, b = os.path.split(path)
714 d, b = os.path.split(path)
715 p2 = os.path.join(d, b.upper())
715 p2 = os.path.join(d, b.upper())
716 if path == p2:
716 if path == p2:
717 p2 = os.path.join(d, b.lower())
717 p2 = os.path.join(d, b.lower())
718 try:
718 try:
719 s2 = os.stat(p2)
719 s2 = os.stat(p2)
720 if s2 == s1:
720 if s2 == s1:
721 return False
721 return False
722 return True
722 return True
723 except:
723 except:
724 return True
724 return True
725
725
726 # Platform specific variants
726 # Platform specific variants
727 if os.name == 'nt':
727 if os.name == 'nt':
728 demandload(globals(), "msvcrt")
728 demandload(globals(), "msvcrt")
729 nulldev = 'NUL:'
729 nulldev = 'NUL:'
730
730
731 class winstdout:
731 class winstdout:
732 '''stdout on windows misbehaves if sent through a pipe'''
732 '''stdout on windows misbehaves if sent through a pipe'''
733
733
734 def __init__(self, fp):
734 def __init__(self, fp):
735 self.fp = fp
735 self.fp = fp
736
736
737 def __getattr__(self, key):
737 def __getattr__(self, key):
738 return getattr(self.fp, key)
738 return getattr(self.fp, key)
739
739
740 def close(self):
740 def close(self):
741 try:
741 try:
742 self.fp.close()
742 self.fp.close()
743 except: pass
743 except: pass
744
744
745 def write(self, s):
745 def write(self, s):
746 try:
746 try:
747 return self.fp.write(s)
747 return self.fp.write(s)
748 except IOError, inst:
748 except IOError, inst:
749 if inst.errno != 0: raise
749 if inst.errno != 0: raise
750 self.close()
750 self.close()
751 raise IOError(errno.EPIPE, 'Broken pipe')
751 raise IOError(errno.EPIPE, 'Broken pipe')
752
752
753 def flush(self):
753 def flush(self):
754 try:
754 try:
755 return self.fp.flush()
755 return self.fp.flush()
756 except IOError, inst:
756 except IOError, inst:
757 if inst.errno != errno.EINVAL: raise
757 if inst.errno != errno.EINVAL: raise
758 self.close()
758 self.close()
759 raise IOError(errno.EPIPE, 'Broken pipe')
759 raise IOError(errno.EPIPE, 'Broken pipe')
760
760
761 sys.stdout = winstdout(sys.stdout)
761 sys.stdout = winstdout(sys.stdout)
762
762
763 def system_rcpath():
763 def system_rcpath():
764 try:
764 try:
765 return system_rcpath_win32()
765 return system_rcpath_win32()
766 except:
766 except:
767 return [r'c:\mercurial\mercurial.ini']
767 return [r'c:\mercurial\mercurial.ini']
768
768
769 def os_rcpath():
769 def os_rcpath():
770 '''return default os-specific hgrc search path'''
770 '''return default os-specific hgrc search path'''
771 path = system_rcpath()
771 path = system_rcpath()
772 path.append(user_rcpath())
772 path.append(user_rcpath())
773 userprofile = os.environ.get('USERPROFILE')
773 userprofile = os.environ.get('USERPROFILE')
774 if userprofile:
774 if userprofile:
775 path.append(os.path.join(userprofile, 'mercurial.ini'))
775 path.append(os.path.join(userprofile, 'mercurial.ini'))
776 return path
776 return path
777
777
778 def user_rcpath():
778 def user_rcpath():
779 '''return os-specific hgrc search path to the user dir'''
779 '''return os-specific hgrc search path to the user dir'''
780 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
780 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
781
781
782 def parse_patch_output(output_line):
782 def parse_patch_output(output_line):
783 """parses the output produced by patch and returns the file name"""
783 """parses the output produced by patch and returns the file name"""
784 pf = output_line[14:]
784 pf = output_line[14:]
785 if pf[0] == '`':
785 if pf[0] == '`':
786 pf = pf[1:-1] # Remove the quotes
786 pf = pf[1:-1] # Remove the quotes
787 return pf
787 return pf
788
788
789 def testpid(pid):
789 def testpid(pid):
790 '''return False if pid dead, True if running or not known'''
790 '''return False if pid dead, True if running or not known'''
791 return True
791 return True
792
792
793 def is_exec(f, last):
793 def is_exec(f, last):
794 return last
794 return last
795
795
796 def set_exec(f, mode):
796 def set_exec(f, mode):
797 pass
797 pass
798
798
799 def set_binary(fd):
799 def set_binary(fd):
800 msvcrt.setmode(fd.fileno(), os.O_BINARY)
800 msvcrt.setmode(fd.fileno(), os.O_BINARY)
801
801
802 def pconvert(path):
802 def pconvert(path):
803 return path.replace("\\", "/")
803 return path.replace("\\", "/")
804
804
805 def localpath(path):
805 def localpath(path):
806 return path.replace('/', '\\')
806 return path.replace('/', '\\')
807
807
808 def normpath(path):
808 def normpath(path):
809 return pconvert(os.path.normpath(path))
809 return pconvert(os.path.normpath(path))
810
810
811 makelock = _makelock_file
811 makelock = _makelock_file
812 readlock = _readlock_file
812 readlock = _readlock_file
813
813
814 def samestat(s1, s2):
814 def samestat(s1, s2):
815 return False
815 return False
816
816
817 # A sequence of backslashes is special iff it precedes a double quote:
817 # A sequence of backslashes is special iff it precedes a double quote:
818 # - if there's an even number of backslashes, the double quote is not
818 # - if there's an even number of backslashes, the double quote is not
819 # quoted (i.e. it ends the quoted region)
819 # quoted (i.e. it ends the quoted region)
820 # - if there's an odd number of backslashes, the double quote is quoted
820 # - if there's an odd number of backslashes, the double quote is quoted
821 # - in both cases, every pair of backslashes is unquoted into a single
821 # - in both cases, every pair of backslashes is unquoted into a single
822 # backslash
822 # backslash
823 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
823 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
824 # So, to quote a string, we must surround it in double quotes, double
824 # So, to quote a string, we must surround it in double quotes, double
825 # the number of backslashes that preceed double quotes and add another
825 # the number of backslashes that preceed double quotes and add another
826 # backslash before every double quote (being careful with the double
826 # backslash before every double quote (being careful with the double
827 # quote we've appended to the end)
827 # quote we've appended to the end)
828 _quotere = None
828 _quotere = None
829 def shellquote(s):
829 def shellquote(s):
830 global _quotere
830 global _quotere
831 if _quotere is None:
831 if _quotere is None:
832 _quotere = re.compile(r'(\\*)("|\\$)')
832 _quotere = re.compile(r'(\\*)("|\\$)')
833 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
833 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
834
834
835 def explain_exit(code):
835 def explain_exit(code):
836 return _("exited with status %d") % code, code
836 return _("exited with status %d") % code, code
837
837
838 # if you change this stub into a real check, please try to implement the
838 # if you change this stub into a real check, please try to implement the
839 # username and groupname functions above, too.
839 # username and groupname functions above, too.
840 def isowner(fp, st=None):
840 def isowner(fp, st=None):
841 return True
841 return True
842
842
843 try:
843 try:
844 # override functions with win32 versions if possible
844 # override functions with win32 versions if possible
845 from util_win32 import *
845 from util_win32 import *
846 if not is_win_9x():
846 if not is_win_9x():
847 posixfile = posixfile_nt
847 posixfile = posixfile_nt
848 except ImportError:
848 except ImportError:
849 pass
849 pass
850
850
851 else:
851 else:
852 nulldev = '/dev/null'
852 nulldev = '/dev/null'
853
853
854 def rcfiles(path):
854 def rcfiles(path):
855 rcs = [os.path.join(path, 'hgrc')]
855 rcs = [os.path.join(path, 'hgrc')]
856 rcdir = os.path.join(path, 'hgrc.d')
856 rcdir = os.path.join(path, 'hgrc.d')
857 try:
857 try:
858 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
858 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
859 if f.endswith(".rc")])
859 if f.endswith(".rc")])
860 except OSError:
860 except OSError:
861 pass
861 pass
862 return rcs
862 return rcs
863
863
864 def os_rcpath():
864 def os_rcpath():
865 '''return default os-specific hgrc search path'''
865 '''return default os-specific hgrc search path'''
866 path = []
866 path = []
867 # old mod_python does not set sys.argv
867 # old mod_python does not set sys.argv
868 if len(getattr(sys, 'argv', [])) > 0:
868 if len(getattr(sys, 'argv', [])) > 0:
869 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
869 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
870 '/../etc/mercurial'))
870 '/../etc/mercurial'))
871 path.extend(rcfiles('/etc/mercurial'))
871 path.extend(rcfiles('/etc/mercurial'))
872 path.append(os.path.expanduser('~/.hgrc'))
872 path.append(os.path.expanduser('~/.hgrc'))
873 path = [os.path.normpath(f) for f in path]
873 path = [os.path.normpath(f) for f in path]
874 return path
874 return path
875
875
876 def parse_patch_output(output_line):
876 def parse_patch_output(output_line):
877 """parses the output produced by patch and returns the file name"""
877 """parses the output produced by patch and returns the file name"""
878 pf = output_line[14:]
878 pf = output_line[14:]
879 if pf.startswith("'") and pf.endswith("'") and " " in pf:
879 if pf.startswith("'") and pf.endswith("'") and " " in pf:
880 pf = pf[1:-1] # Remove the quotes
880 pf = pf[1:-1] # Remove the quotes
881 return pf
881 return pf
882
882
883 def is_exec(f, last):
883 def is_exec(f, last):
884 """check whether a file is executable"""
884 """check whether a file is executable"""
885 return (os.lstat(f).st_mode & 0100 != 0)
885 return (os.lstat(f).st_mode & 0100 != 0)
886
886
887 def set_exec(f, mode):
887 def set_exec(f, mode):
888 s = os.lstat(f).st_mode
888 s = os.lstat(f).st_mode
889 if (s & 0100 != 0) == mode:
889 if (s & 0100 != 0) == mode:
890 return
890 return
891 if mode:
891 if mode:
892 # Turn on +x for every +r bit when making a file executable
892 # Turn on +x for every +r bit when making a file executable
893 # and obey umask.
893 # and obey umask.
894 umask = os.umask(0)
894 umask = os.umask(0)
895 os.umask(umask)
895 os.umask(umask)
896 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
896 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
897 else:
897 else:
898 os.chmod(f, s & 0666)
898 os.chmod(f, s & 0666)
899
899
900 def set_binary(fd):
900 def set_binary(fd):
901 pass
901 pass
902
902
903 def pconvert(path):
903 def pconvert(path):
904 return path
904 return path
905
905
906 def localpath(path):
906 def localpath(path):
907 return path
907 return path
908
908
909 normpath = os.path.normpath
909 normpath = os.path.normpath
910 samestat = os.path.samestat
910 samestat = os.path.samestat
911
911
912 def makelock(info, pathname):
912 def makelock(info, pathname):
913 try:
913 try:
914 os.symlink(info, pathname)
914 os.symlink(info, pathname)
915 except OSError, why:
915 except OSError, why:
916 if why.errno == errno.EEXIST:
916 if why.errno == errno.EEXIST:
917 raise
917 raise
918 else:
918 else:
919 _makelock_file(info, pathname)
919 _makelock_file(info, pathname)
920
920
921 def readlock(pathname):
921 def readlock(pathname):
922 try:
922 try:
923 return os.readlink(pathname)
923 return os.readlink(pathname)
924 except OSError, why:
924 except OSError, why:
925 if why.errno == errno.EINVAL:
925 if why.errno == errno.EINVAL:
926 return _readlock_file(pathname)
926 return _readlock_file(pathname)
927 else:
927 else:
928 raise
928 raise
929
929
930 def shellquote(s):
930 def shellquote(s):
931 return "'%s'" % s.replace("'", "'\\''")
931 return "'%s'" % s.replace("'", "'\\''")
932
932
933 def testpid(pid):
933 def testpid(pid):
934 '''return False if pid dead, True if running or not sure'''
934 '''return False if pid dead, True if running or not sure'''
935 try:
935 try:
936 os.kill(pid, 0)
936 os.kill(pid, 0)
937 return True
937 return True
938 except OSError, inst:
938 except OSError, inst:
939 return inst.errno != errno.ESRCH
939 return inst.errno != errno.ESRCH
940
940
941 def explain_exit(code):
941 def explain_exit(code):
942 """return a 2-tuple (desc, code) describing a process's status"""
942 """return a 2-tuple (desc, code) describing a process's status"""
943 if os.WIFEXITED(code):
943 if os.WIFEXITED(code):
944 val = os.WEXITSTATUS(code)
944 val = os.WEXITSTATUS(code)
945 return _("exited with status %d") % val, val
945 return _("exited with status %d") % val, val
946 elif os.WIFSIGNALED(code):
946 elif os.WIFSIGNALED(code):
947 val = os.WTERMSIG(code)
947 val = os.WTERMSIG(code)
948 return _("killed by signal %d") % val, val
948 return _("killed by signal %d") % val, val
949 elif os.WIFSTOPPED(code):
949 elif os.WIFSTOPPED(code):
950 val = os.WSTOPSIG(code)
950 val = os.WSTOPSIG(code)
951 return _("stopped by signal %d") % val, val
951 return _("stopped by signal %d") % val, val
952 raise ValueError(_("invalid exit code"))
952 raise ValueError(_("invalid exit code"))
953
953
954 def isowner(fp, st=None):
954 def isowner(fp, st=None):
955 """Return True if the file object f belongs to the current user.
955 """Return True if the file object f belongs to the current user.
956
956
957 The return value of a util.fstat(f) may be passed as the st argument.
957 The return value of a util.fstat(f) may be passed as the st argument.
958 """
958 """
959 if st is None:
959 if st is None:
960 st = fstat(fp)
960 st = fstat(fp)
961 return st.st_uid == os.getuid()
961 return st.st_uid == os.getuid()
962
962
963 def _buildencodefun():
963 def _buildencodefun():
964 e = '_'
964 e = '_'
965 win_reserved = [ord(x) for x in '\\:*?"<>|']
965 win_reserved = [ord(x) for x in '\\:*?"<>|']
966 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
966 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
967 for x in (range(32) + range(126, 256) + win_reserved):
967 for x in (range(32) + range(126, 256) + win_reserved):
968 cmap[chr(x)] = "~%02x" % x
968 cmap[chr(x)] = "~%02x" % x
969 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
969 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
970 cmap[chr(x)] = e + chr(x).lower()
970 cmap[chr(x)] = e + chr(x).lower()
971 dmap = {}
971 dmap = {}
972 for k, v in cmap.iteritems():
972 for k, v in cmap.iteritems():
973 dmap[v] = k
973 dmap[v] = k
974 def decode(s):
974 def decode(s):
975 i = 0
975 i = 0
976 while i < len(s):
976 while i < len(s):
977 for l in xrange(1, 4):
977 for l in xrange(1, 4):
978 try:
978 try:
979 yield dmap[s[i:i+l]]
979 yield dmap[s[i:i+l]]
980 i += l
980 i += l
981 break
981 break
982 except KeyError:
982 except KeyError:
983 pass
983 pass
984 else:
984 else:
985 raise KeyError
985 raise KeyError
986 return (lambda s: "".join([cmap[c] for c in s]),
986 return (lambda s: "".join([cmap[c] for c in s]),
987 lambda s: "".join(list(decode(s))))
987 lambda s: "".join(list(decode(s))))
988
988
989 encodefilename, decodefilename = _buildencodefun()
989 encodefilename, decodefilename = _buildencodefun()
990
990
991 def encodedopener(openerfn, fn):
991 def encodedopener(openerfn, fn):
992 def o(path, *args, **kw):
992 def o(path, *args, **kw):
993 return openerfn(fn(path), *args, **kw)
993 return openerfn(fn(path), *args, **kw)
994 return o
994 return o
995
995
996 def opener(base, audit=True):
996 def opener(base, audit=True):
997 """
997 """
998 return a function that opens files relative to base
998 return a function that opens files relative to base
999
999
1000 this function is used to hide the details of COW semantics and
1000 this function is used to hide the details of COW semantics and
1001 remote file access from higher level code.
1001 remote file access from higher level code.
1002 """
1002 """
1003 p = base
1003 p = base
1004 audit_p = audit
1004 audit_p = audit
1005
1005
1006 def mktempcopy(name):
1006 def mktempcopy(name):
1007 d, fn = os.path.split(name)
1007 d, fn = os.path.split(name)
1008 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1008 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1009 os.close(fd)
1009 os.close(fd)
1010 ofp = posixfile(temp, "wb")
1010 ofp = posixfile(temp, "wb")
1011 try:
1011 try:
1012 try:
1012 try:
1013 ifp = posixfile(name, "rb")
1013 ifp = posixfile(name, "rb")
1014 except IOError, inst:
1014 except IOError, inst:
1015 if not getattr(inst, 'filename', None):
1015 if not getattr(inst, 'filename', None):
1016 inst.filename = name
1016 inst.filename = name
1017 raise
1017 raise
1018 for chunk in filechunkiter(ifp):
1018 for chunk in filechunkiter(ifp):
1019 ofp.write(chunk)
1019 ofp.write(chunk)
1020 ifp.close()
1020 ifp.close()
1021 ofp.close()
1021 ofp.close()
1022 except:
1022 except:
1023 try: os.unlink(temp)
1023 try: os.unlink(temp)
1024 except: pass
1024 except: pass
1025 raise
1025 raise
1026 st = os.lstat(name)
1026 st = os.lstat(name)
1027 os.chmod(temp, st.st_mode)
1027 os.chmod(temp, st.st_mode)
1028 return temp
1028 return temp
1029
1029
1030 class atomictempfile(posixfile):
1030 class atomictempfile(posixfile):
1031 """the file will only be copied when rename is called"""
1031 """the file will only be copied when rename is called"""
1032 def __init__(self, name, mode):
1032 def __init__(self, name, mode):
1033 self.__name = name
1033 self.__name = name
1034 self.temp = mktempcopy(name)
1034 self.temp = mktempcopy(name)
1035 posixfile.__init__(self, self.temp, mode)
1035 posixfile.__init__(self, self.temp, mode)
1036 def rename(self):
1036 def rename(self):
1037 if not self.closed:
1037 if not self.closed:
1038 posixfile.close(self)
1038 posixfile.close(self)
1039 rename(self.temp, localpath(self.__name))
1039 rename(self.temp, localpath(self.__name))
1040 def __del__(self):
1040 def __del__(self):
1041 if not self.closed:
1041 if not self.closed:
1042 try:
1042 try:
1043 os.unlink(self.temp)
1043 os.unlink(self.temp)
1044 except: pass
1044 except: pass
1045 posixfile.close(self)
1045 posixfile.close(self)
1046
1046
1047 class atomicfile(atomictempfile):
1047 class atomicfile(atomictempfile):
1048 """the file will only be copied on close"""
1048 """the file will only be copied on close"""
1049 def __init__(self, name, mode):
1049 def __init__(self, name, mode):
1050 atomictempfile.__init__(self, name, mode)
1050 atomictempfile.__init__(self, name, mode)
1051 def close(self):
1051 def close(self):
1052 self.rename()
1052 self.rename()
1053 def __del__(self):
1053 def __del__(self):
1054 self.rename()
1054 self.rename()
1055
1055
1056 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1056 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1057 if audit_p:
1057 if audit_p:
1058 audit_path(path)
1058 audit_path(path)
1059 f = os.path.join(p, path)
1059 f = os.path.join(p, path)
1060
1060
1061 if not text:
1061 if not text:
1062 mode += "b" # for that other OS
1062 mode += "b" # for that other OS
1063
1063
1064 if mode[0] != "r":
1064 if mode[0] != "r":
1065 try:
1065 try:
1066 nlink = nlinks(f)
1066 nlink = nlinks(f)
1067 except OSError:
1067 except OSError:
1068 d = os.path.dirname(f)
1068 d = os.path.dirname(f)
1069 if not os.path.isdir(d):
1069 if not os.path.isdir(d):
1070 os.makedirs(d)
1070 os.makedirs(d)
1071 else:
1071 else:
1072 if atomic:
1072 if atomic:
1073 return atomicfile(f, mode)
1073 return atomicfile(f, mode)
1074 elif atomictemp:
1074 elif atomictemp:
1075 return atomictempfile(f, mode)
1075 return atomictempfile(f, mode)
1076 if nlink > 1:
1076 if nlink > 1:
1077 rename(mktempcopy(f), f)
1077 rename(mktempcopy(f), f)
1078 return posixfile(f, mode)
1078 return posixfile(f, mode)
1079
1079
1080 return o
1080 return o
1081
1081
1082 class chunkbuffer(object):
1082 class chunkbuffer(object):
1083 """Allow arbitrary sized chunks of data to be efficiently read from an
1083 """Allow arbitrary sized chunks of data to be efficiently read from an
1084 iterator over chunks of arbitrary size."""
1084 iterator over chunks of arbitrary size."""
1085
1085
1086 def __init__(self, in_iter, targetsize = 2**16):
1086 def __init__(self, in_iter, targetsize = 2**16):
1087 """in_iter is the iterator that's iterating over the input chunks.
1087 """in_iter is the iterator that's iterating over the input chunks.
1088 targetsize is how big a buffer to try to maintain."""
1088 targetsize is how big a buffer to try to maintain."""
1089 self.in_iter = iter(in_iter)
1089 self.in_iter = iter(in_iter)
1090 self.buf = ''
1090 self.buf = ''
1091 self.targetsize = int(targetsize)
1091 self.targetsize = int(targetsize)
1092 if self.targetsize <= 0:
1092 if self.targetsize <= 0:
1093 raise ValueError(_("targetsize must be greater than 0, was %d") %
1093 raise ValueError(_("targetsize must be greater than 0, was %d") %
1094 targetsize)
1094 targetsize)
1095 self.iterempty = False
1095 self.iterempty = False
1096
1096
1097 def fillbuf(self):
1097 def fillbuf(self):
1098 """Ignore target size; read every chunk from iterator until empty."""
1098 """Ignore target size; read every chunk from iterator until empty."""
1099 if not self.iterempty:
1099 if not self.iterempty:
1100 collector = cStringIO.StringIO()
1100 collector = cStringIO.StringIO()
1101 collector.write(self.buf)
1101 collector.write(self.buf)
1102 for ch in self.in_iter:
1102 for ch in self.in_iter:
1103 collector.write(ch)
1103 collector.write(ch)
1104 self.buf = collector.getvalue()
1104 self.buf = collector.getvalue()
1105 self.iterempty = True
1105 self.iterempty = True
1106
1106
1107 def read(self, l):
1107 def read(self, l):
1108 """Read L bytes of data from the iterator of chunks of data.
1108 """Read L bytes of data from the iterator of chunks of data.
1109 Returns less than L bytes if the iterator runs dry."""
1109 Returns less than L bytes if the iterator runs dry."""
1110 if l > len(self.buf) and not self.iterempty:
1110 if l > len(self.buf) and not self.iterempty:
1111 # Clamp to a multiple of self.targetsize
1111 # Clamp to a multiple of self.targetsize
1112 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1112 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1113 collector = cStringIO.StringIO()
1113 collector = cStringIO.StringIO()
1114 collector.write(self.buf)
1114 collector.write(self.buf)
1115 collected = len(self.buf)
1115 collected = len(self.buf)
1116 for chunk in self.in_iter:
1116 for chunk in self.in_iter:
1117 collector.write(chunk)
1117 collector.write(chunk)
1118 collected += len(chunk)
1118 collected += len(chunk)
1119 if collected >= targetsize:
1119 if collected >= targetsize:
1120 break
1120 break
1121 if collected < targetsize:
1121 if collected < targetsize:
1122 self.iterempty = True
1122 self.iterempty = True
1123 self.buf = collector.getvalue()
1123 self.buf = collector.getvalue()
1124 s, self.buf = self.buf[:l], buffer(self.buf, l)
1124 s, self.buf = self.buf[:l], buffer(self.buf, l)
1125 return s
1125 return s
1126
1126
1127 def filechunkiter(f, size=65536, limit=None):
1127 def filechunkiter(f, size=65536, limit=None):
1128 """Create a generator that produces the data in the file size
1128 """Create a generator that produces the data in the file size
1129 (default 65536) bytes at a time, up to optional limit (default is
1129 (default 65536) bytes at a time, up to optional limit (default is
1130 to read all data). Chunks may be less than size bytes if the
1130 to read all data). Chunks may be less than size bytes if the
1131 chunk is the last chunk in the file, or the file is a socket or
1131 chunk is the last chunk in the file, or the file is a socket or
1132 some other type of file that sometimes reads less data than is
1132 some other type of file that sometimes reads less data than is
1133 requested."""
1133 requested."""
1134 assert size >= 0
1134 assert size >= 0
1135 assert limit is None or limit >= 0
1135 assert limit is None or limit >= 0
1136 while True:
1136 while True:
1137 if limit is None: nbytes = size
1137 if limit is None: nbytes = size
1138 else: nbytes = min(limit, size)
1138 else: nbytes = min(limit, size)
1139 s = nbytes and f.read(nbytes)
1139 s = nbytes and f.read(nbytes)
1140 if not s: break
1140 if not s: break
1141 if limit: limit -= len(s)
1141 if limit: limit -= len(s)
1142 yield s
1142 yield s
1143
1143
1144 def makedate():
1144 def makedate():
1145 lt = time.localtime()
1145 lt = time.localtime()
1146 if lt[8] == 1 and time.daylight:
1146 if lt[8] == 1 and time.daylight:
1147 tz = time.altzone
1147 tz = time.altzone
1148 else:
1148 else:
1149 tz = time.timezone
1149 tz = time.timezone
1150 return time.mktime(lt), tz
1150 return time.mktime(lt), tz
1151
1151
1152 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1152 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1153 """represent a (unixtime, offset) tuple as a localized time.
1153 """represent a (unixtime, offset) tuple as a localized time.
1154 unixtime is seconds since the epoch, and offset is the time zone's
1154 unixtime is seconds since the epoch, and offset is the time zone's
1155 number of seconds away from UTC. if timezone is false, do not
1155 number of seconds away from UTC. if timezone is false, do not
1156 append time zone to string."""
1156 append time zone to string."""
1157 t, tz = date or makedate()
1157 t, tz = date or makedate()
1158 s = time.strftime(format, time.gmtime(float(t) - tz))
1158 s = time.strftime(format, time.gmtime(float(t) - tz))
1159 if timezone:
1159 if timezone:
1160 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1160 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1161 return s
1161 return s
1162
1162
1163 def strdate(string, format, defaults):
1163 def strdate(string, format, defaults):
1164 """parse a localized time string and return a (unixtime, offset) tuple.
1164 """parse a localized time string and return a (unixtime, offset) tuple.
1165 if the string cannot be parsed, ValueError is raised."""
1165 if the string cannot be parsed, ValueError is raised."""
1166 def timezone(string):
1166 def timezone(string):
1167 tz = string.split()[-1]
1167 tz = string.split()[-1]
1168 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1168 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1169 tz = int(tz)
1169 tz = int(tz)
1170 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1170 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1171 return offset
1171 return offset
1172 if tz == "GMT" or tz == "UTC":
1172 if tz == "GMT" or tz == "UTC":
1173 return 0
1173 return 0
1174 return None
1174 return None
1175
1175
1176 # NOTE: unixtime = localunixtime + offset
1176 # NOTE: unixtime = localunixtime + offset
1177 offset, date = timezone(string), string
1177 offset, date = timezone(string), string
1178 if offset != None:
1178 if offset != None:
1179 date = " ".join(string.split()[:-1])
1179 date = " ".join(string.split()[:-1])
1180
1180
1181 # add missing elements from defaults
1181 # add missing elements from defaults
1182 for part in defaults:
1182 for part in defaults:
1183 found = [True for p in part if ("%"+p) in format]
1183 found = [True for p in part if ("%"+p) in format]
1184 if not found:
1184 if not found:
1185 date += "@" + defaults[part]
1185 date += "@" + defaults[part]
1186 format += "@%" + part[0]
1186 format += "@%" + part[0]
1187
1187
1188 timetuple = time.strptime(date, format)
1188 timetuple = time.strptime(date, format)
1189 localunixtime = int(calendar.timegm(timetuple))
1189 localunixtime = int(calendar.timegm(timetuple))
1190 if offset is None:
1190 if offset is None:
1191 # local timezone
1191 # local timezone
1192 unixtime = int(time.mktime(timetuple))
1192 unixtime = int(time.mktime(timetuple))
1193 offset = unixtime - localunixtime
1193 offset = unixtime - localunixtime
1194 else:
1194 else:
1195 unixtime = localunixtime + offset
1195 unixtime = localunixtime + offset
1196 return unixtime, offset
1196 return unixtime, offset
1197
1197
1198 def parsedate(string, formats=None, defaults=None):
1198 def parsedate(string, formats=None, defaults=None):
1199 """parse a localized time string and return a (unixtime, offset) tuple.
1199 """parse a localized time string and return a (unixtime, offset) tuple.
1200 The date may be a "unixtime offset" string or in one of the specified
1200 The date may be a "unixtime offset" string or in one of the specified
1201 formats."""
1201 formats."""
1202 if not string:
1202 if not string:
1203 return 0, 0
1203 return 0, 0
1204 if not formats:
1204 if not formats:
1205 formats = defaultdateformats
1205 formats = defaultdateformats
1206 string = string.strip()
1206 string = string.strip()
1207 try:
1207 try:
1208 when, offset = map(int, string.split(' '))
1208 when, offset = map(int, string.split(' '))
1209 except ValueError:
1209 except ValueError:
1210 # fill out defaults
1210 # fill out defaults
1211 if not defaults:
1211 if not defaults:
1212 defaults = {}
1212 defaults = {}
1213 now = makedate()
1213 now = makedate()
1214 for part in "d mb yY HI M S".split():
1214 for part in "d mb yY HI M S".split():
1215 if part not in defaults:
1215 if part not in defaults:
1216 if part[0] in "HMS":
1216 if part[0] in "HMS":
1217 defaults[part] = "00"
1217 defaults[part] = "00"
1218 elif part[0] in "dm":
1218 elif part[0] in "dm":
1219 defaults[part] = "1"
1219 defaults[part] = "1"
1220 else:
1220 else:
1221 defaults[part] = datestr(now, "%" + part[0], False)
1221 defaults[part] = datestr(now, "%" + part[0], False)
1222
1222
1223 for format in formats:
1223 for format in formats:
1224 try:
1224 try:
1225 when, offset = strdate(string, format, defaults)
1225 when, offset = strdate(string, format, defaults)
1226 except ValueError:
1226 except ValueError:
1227 pass
1227 pass
1228 else:
1228 else:
1229 break
1229 break
1230 else:
1230 else:
1231 raise Abort(_('invalid date: %r ') % string)
1231 raise Abort(_('invalid date: %r ') % string)
1232 # validate explicit (probably user-specified) date and
1232 # validate explicit (probably user-specified) date and
1233 # time zone offset. values must fit in signed 32 bits for
1233 # time zone offset. values must fit in signed 32 bits for
1234 # current 32-bit linux runtimes. timezones go from UTC-12
1234 # current 32-bit linux runtimes. timezones go from UTC-12
1235 # to UTC+14
1235 # to UTC+14
1236 if abs(when) > 0x7fffffff:
1236 if abs(when) > 0x7fffffff:
1237 raise Abort(_('date exceeds 32 bits: %d') % when)
1237 raise Abort(_('date exceeds 32 bits: %d') % when)
1238 if offset < -50400 or offset > 43200:
1238 if offset < -50400 or offset > 43200:
1239 raise Abort(_('impossible time zone offset: %d') % offset)
1239 raise Abort(_('impossible time zone offset: %d') % offset)
1240 return when, offset
1240 return when, offset
1241
1241
1242 def matchdate(date):
1242 def matchdate(date):
1243 """Return a function that matches a given date match specifier
1243 """Return a function that matches a given date match specifier
1244
1244
1245 Formats include:
1245 Formats include:
1246
1246
1247 '{date}' match a given date to the accuracy provided
1247 '{date}' match a given date to the accuracy provided
1248
1248
1249 '<{date}' on or before a given date
1249 '<{date}' on or before a given date
1250
1250
1251 '>{date}' on or after a given date
1251 '>{date}' on or after a given date
1252
1252
1253 """
1253 """
1254
1254
1255 def lower(date):
1255 def lower(date):
1256 return parsedate(date, extendeddateformats)[0]
1256 return parsedate(date, extendeddateformats)[0]
1257
1257
1258 def upper(date):
1258 def upper(date):
1259 d = dict(mb="12", HI="23", M="59", S="59")
1259 d = dict(mb="12", HI="23", M="59", S="59")
1260 for days in "31 30 29".split():
1260 for days in "31 30 29".split():
1261 try:
1261 try:
1262 d["d"] = days
1262 d["d"] = days
1263 return parsedate(date, extendeddateformats, d)[0]
1263 return parsedate(date, extendeddateformats, d)[0]
1264 except:
1264 except:
1265 pass
1265 pass
1266 d["d"] = "28"
1266 d["d"] = "28"
1267 return parsedate(date, extendeddateformats, d)[0]
1267 return parsedate(date, extendeddateformats, d)[0]
1268
1268
1269 if date[0] == "<":
1269 if date[0] == "<":
1270 when = upper(date[1:])
1270 when = upper(date[1:])
1271 return lambda x: x <= when
1271 return lambda x: x <= when
1272 elif date[0] == ">":
1272 elif date[0] == ">":
1273 when = lower(date[1:])
1273 when = lower(date[1:])
1274 return lambda x: x >= when
1274 return lambda x: x >= when
1275 elif date[0] == "-":
1275 elif date[0] == "-":
1276 try:
1276 try:
1277 days = int(date[1:])
1277 days = int(date[1:])
1278 except ValueError:
1278 except ValueError:
1279 raise Abort(_("invalid day spec: %s") % date[1:])
1279 raise Abort(_("invalid day spec: %s") % date[1:])
1280 when = makedate()[0] - days * 3600 * 24
1280 when = makedate()[0] - days * 3600 * 24
1281 return lambda x: x >= when
1281 return lambda x: x >= when
1282 elif " to " in date:
1282 elif " to " in date:
1283 a, b = date.split(" to ")
1283 a, b = date.split(" to ")
1284 start, stop = lower(a), upper(b)
1284 start, stop = lower(a), upper(b)
1285 return lambda x: x >= start and x <= stop
1285 return lambda x: x >= start and x <= stop
1286 else:
1286 else:
1287 start, stop = lower(date), upper(date)
1287 start, stop = lower(date), upper(date)
1288 return lambda x: x >= start and x <= stop
1288 return lambda x: x >= start and x <= stop
1289
1289
1290 def shortuser(user):
1290 def shortuser(user):
1291 """Return a short representation of a user name or email address."""
1291 """Return a short representation of a user name or email address."""
1292 f = user.find('@')
1292 f = user.find('@')
1293 if f >= 0:
1293 if f >= 0:
1294 user = user[:f]
1294 user = user[:f]
1295 f = user.find('<')
1295 f = user.find('<')
1296 if f >= 0:
1296 if f >= 0:
1297 user = user[f+1:]
1297 user = user[f+1:]
1298 f = user.find(' ')
1298 f = user.find(' ')
1299 if f >= 0:
1299 if f >= 0:
1300 user = user[:f]
1300 user = user[:f]
1301 f = user.find('.')
1301 f = user.find('.')
1302 if f >= 0:
1302 if f >= 0:
1303 user = user[:f]
1303 user = user[:f]
1304 return user
1304 return user
1305
1305
1306 def ellipsis(text, maxlength=400):
1306 def ellipsis(text, maxlength=400):
1307 """Trim string to at most maxlength (default: 400) characters."""
1307 """Trim string to at most maxlength (default: 400) characters."""
1308 if len(text) <= maxlength:
1308 if len(text) <= maxlength:
1309 return text
1309 return text
1310 else:
1310 else:
1311 return "%s..." % (text[:maxlength-3])
1311 return "%s..." % (text[:maxlength-3])
1312
1312
1313 def walkrepos(path):
1313 def walkrepos(path):
1314 '''yield every hg repository under path, recursively.'''
1314 '''yield every hg repository under path, recursively.'''
1315 def errhandler(err):
1315 def errhandler(err):
1316 if err.filename == path:
1316 if err.filename == path:
1317 raise err
1317 raise err
1318
1318
1319 for root, dirs, files in os.walk(path, onerror=errhandler):
1319 for root, dirs, files in os.walk(path, onerror=errhandler):
1320 for d in dirs:
1320 for d in dirs:
1321 if d == '.hg':
1321 if d == '.hg':
1322 yield root
1322 yield root
1323 dirs[:] = []
1323 dirs[:] = []
1324 break
1324 break
1325
1325
1326 _rcpath = None
1326 _rcpath = None
1327
1327
1328 def rcpath():
1328 def rcpath():
1329 '''return hgrc search path. if env var HGRCPATH is set, use it.
1329 '''return hgrc search path. if env var HGRCPATH is set, use it.
1330 for each item in path, if directory, use files ending in .rc,
1330 for each item in path, if directory, use files ending in .rc,
1331 else use item.
1331 else use item.
1332 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1332 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1333 if no HGRCPATH, use default os-specific path.'''
1333 if no HGRCPATH, use default os-specific path.'''
1334 global _rcpath
1334 global _rcpath
1335 if _rcpath is None:
1335 if _rcpath is None:
1336 if 'HGRCPATH' in os.environ:
1336 if 'HGRCPATH' in os.environ:
1337 _rcpath = []
1337 _rcpath = []
1338 for p in os.environ['HGRCPATH'].split(os.pathsep):
1338 for p in os.environ['HGRCPATH'].split(os.pathsep):
1339 if not p: continue
1339 if not p: continue
1340 if os.path.isdir(p):
1340 if os.path.isdir(p):
1341 for f in os.listdir(p):
1341 for f in os.listdir(p):
1342 if f.endswith('.rc'):
1342 if f.endswith('.rc'):
1343 _rcpath.append(os.path.join(p, f))
1343 _rcpath.append(os.path.join(p, f))
1344 else:
1344 else:
1345 _rcpath.append(p)
1345 _rcpath.append(p)
1346 else:
1346 else:
1347 _rcpath = os_rcpath()
1347 _rcpath = os_rcpath()
1348 return _rcpath
1348 return _rcpath
1349
1349
1350 def bytecount(nbytes):
1350 def bytecount(nbytes):
1351 '''return byte count formatted as readable string, with units'''
1351 '''return byte count formatted as readable string, with units'''
1352
1352
1353 units = (
1353 units = (
1354 (100, 1<<30, _('%.0f GB')),
1354 (100, 1<<30, _('%.0f GB')),
1355 (10, 1<<30, _('%.1f GB')),
1355 (10, 1<<30, _('%.1f GB')),
1356 (1, 1<<30, _('%.2f GB')),
1356 (1, 1<<30, _('%.2f GB')),
1357 (100, 1<<20, _('%.0f MB')),
1357 (100, 1<<20, _('%.0f MB')),
1358 (10, 1<<20, _('%.1f MB')),
1358 (10, 1<<20, _('%.1f MB')),
1359 (1, 1<<20, _('%.2f MB')),
1359 (1, 1<<20, _('%.2f MB')),
1360 (100, 1<<10, _('%.0f KB')),
1360 (100, 1<<10, _('%.0f KB')),
1361 (10, 1<<10, _('%.1f KB')),
1361 (10, 1<<10, _('%.1f KB')),
1362 (1, 1<<10, _('%.2f KB')),
1362 (1, 1<<10, _('%.2f KB')),
1363 (1, 1, _('%.0f bytes')),
1363 (1, 1, _('%.0f bytes')),
1364 )
1364 )
1365
1365
1366 for multiplier, divisor, format in units:
1366 for multiplier, divisor, format in units:
1367 if nbytes >= divisor * multiplier:
1367 if nbytes >= divisor * multiplier:
1368 return format % (nbytes / float(divisor))
1368 return format % (nbytes / float(divisor))
1369 return units[-1][2] % nbytes
1369 return units[-1][2] % nbytes
1370
1370
1371 def drop_scheme(scheme, path):
1371 def drop_scheme(scheme, path):
1372 sc = scheme + ':'
1372 sc = scheme + ':'
1373 if path.startswith(sc):
1373 if path.startswith(sc):
1374 path = path[len(sc):]
1374 path = path[len(sc):]
1375 if path.startswith('//'):
1375 if path.startswith('//'):
1376 path = path[2:]
1376 path = path[2:]
1377 return path
1377 return path
@@ -1,65 +1,66
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 path:beans//black
51 hg debugwalk relglob:Procyonidae
52 hg debugwalk relglob:Procyonidae
52 hg debugwalk relglob:Procyonidae/ fennel
53 hg debugwalk relglob:Procyonidae/ fennel
53 hg debugwalk beans 'glob:beans/*'
54 hg debugwalk beans 'glob:beans/*'
54 hg debugwalk 'glob:mamm**'
55 hg debugwalk 'glob:mamm**'
55 hg debugwalk 'glob:mamm**' fennel
56 hg debugwalk 'glob:mamm**' fennel
56 hg debugwalk 'glob:j*'
57 hg debugwalk 'glob:j*'
57 hg debugwalk NOEXIST
58 hg debugwalk NOEXIST
58 mkfifo fifo
59 mkfifo fifo
59 hg debugwalk fifo
60 hg debugwalk fifo
60 rm fenugreek
61 rm fenugreek
61 hg debugwalk fenugreek
62 hg debugwalk fenugreek
62 hg rm fenugreek
63 hg rm fenugreek
63 hg debugwalk fenugreek
64 hg debugwalk fenugreek
64 touch new
65 touch new
65 hg debugwalk new
66 hg debugwalk new
@@ -1,113 +1,114
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 exact
87 f beans/black beans/black exact
87 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
88 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
88 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
89 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
89 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
90 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
90 f fennel fennel exact
91 f fennel fennel exact
91 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
92 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
92 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
93 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
93 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
94 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
94 f beans/black beans/black
95 f beans/black beans/black
95 f beans/borlotti beans/borlotti
96 f beans/borlotti beans/borlotti
96 f beans/kidney beans/kidney
97 f beans/kidney beans/kidney
97 f beans/navy beans/navy
98 f beans/navy beans/navy
98 f beans/pinto beans/pinto
99 f beans/pinto beans/pinto
99 f beans/turtle beans/turtle
100 f beans/turtle beans/turtle
100 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
101 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
101 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
102 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
102 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
103 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
103 f mammals/skunk mammals/skunk
104 f mammals/skunk mammals/skunk
104 f fennel fennel exact
105 f fennel fennel exact
105 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
106 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
106 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
107 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
107 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
108 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
108 f mammals/skunk mammals/skunk
109 f mammals/skunk mammals/skunk
109 NOEXIST: No such file or directory
110 NOEXIST: No such file or directory
110 fifo: unsupported file type (type is fifo)
111 fifo: unsupported file type (type is fifo)
111 m fenugreek fenugreek exact
112 m fenugreek fenugreek exact
112 m fenugreek fenugreek exact
113 m fenugreek fenugreek exact
113 f new new exact
114 f new new exact
General Comments 0
You need to be logged in to leave comments. Login now