##// END OF EJS Templates
match: move util match functions over
Matt Mackall -
r8570:7fe2012b default
parent child Browse files
Show More
@@ -1,59 +1,252
1 1 # match.py - file name matching
2 2 #
3 3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 import util
8 import util, re
9 9
10 10 class _match(object):
11 11 def __init__(self, root, cwd, files, mf, ap):
12 12 self._root = root
13 13 self._cwd = cwd
14 14 self._files = files
15 15 self._fmap = set(files)
16 16 self.matchfn = mf
17 17 self._anypats = ap
18 18 def __call__(self, fn):
19 19 return self.matchfn(fn)
20 20 def __iter__(self):
21 21 for f in self._files:
22 22 yield f
23 23 def bad(self, f, msg):
24 24 return True
25 25 def dir(self, f):
26 26 pass
27 27 def missing(self, f):
28 28 pass
29 29 def exact(self, f):
30 30 return f in self._fmap
31 31 def rel(self, f):
32 32 return util.pathto(self._root, self._cwd, f)
33 33 def files(self):
34 34 return self._files
35 35 def anypats(self):
36 36 return self._anypats
37 37
38 38 class always(_match):
39 39 def __init__(self, root, cwd):
40 40 _match.__init__(self, root, cwd, [], lambda f: True, False)
41 41
42 42 class never(_match):
43 43 def __init__(self, root, cwd):
44 44 _match.__init__(self, root, cwd, [], lambda f: False, False)
45 45
46 46 class exact(_match):
47 47 def __init__(self, root, cwd, files):
48 48 _match.__init__(self, root, cwd, files, self.exact, False)
49 49
50 50 class match(_match):
51 51 def __init__(self, root, cwd, patterns, include=[], exclude=[],
52 52 default='glob'):
53 f, mf, ap = util.matcher(root, cwd, patterns, include, exclude,
54 default)
53 f, mf, ap = _matcher(root, cwd, patterns, include, exclude, default)
55 54 _match.__init__(self, root, cwd, f, mf, ap)
56 55
57 56 def patkind(pat):
58 return util._patsplit(pat, None)[0]
57 return _patsplit(pat, None)[0]
58
59 def _patsplit(pat, default):
60 """Split a string into an optional pattern kind prefix and the
61 actual pattern."""
62 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
63 if pat.startswith(prefix + ':'): return pat.split(':', 1)
64 return default, pat
65
66 _globchars = set('[{*?')
67
68 def _globre(pat, head='^', tail='$'):
69 "convert a glob pattern into a regexp"
70 i, n = 0, len(pat)
71 res = ''
72 group = 0
73 def peek(): return i < n and pat[i]
74 while i < n:
75 c = pat[i]
76 i = i+1
77 if c == '*':
78 if peek() == '*':
79 i += 1
80 res += '.*'
81 else:
82 res += '[^/]*'
83 elif c == '?':
84 res += '.'
85 elif c == '[':
86 j = i
87 if j < n and pat[j] in '!]':
88 j += 1
89 while j < n and pat[j] != ']':
90 j += 1
91 if j >= n:
92 res += '\\['
93 else:
94 stuff = pat[i:j].replace('\\','\\\\')
95 i = j + 1
96 if stuff[0] == '!':
97 stuff = '^' + stuff[1:]
98 elif stuff[0] == '^':
99 stuff = '\\' + stuff
100 res = '%s[%s]' % (res, stuff)
101 elif c == '{':
102 group += 1
103 res += '(?:'
104 elif c == '}' and group:
105 res += ')'
106 group -= 1
107 elif c == ',' and group:
108 res += '|'
109 elif c == '\\':
110 p = peek()
111 if p:
112 i += 1
113 res += re.escape(p)
114 else:
115 res += re.escape(c)
116 else:
117 res += re.escape(c)
118 return head + res + tail
119
120 def _matcher(canonroot, cwd='', names=[], inc=[], exc=[], dflt_pat='glob'):
121 """build a function to match a set of file patterns
122
123 arguments:
124 canonroot - the canonical root of the tree you're matching against
125 cwd - the current working directory, if relevant
126 names - patterns to find
127 inc - patterns to include
128 exc - patterns to exclude
129 dflt_pat - if a pattern in names has no explicit type, assume this one
130
131 a pattern is one of:
132 'glob:<glob>' - a glob relative to cwd
133 're:<regexp>' - a regular expression
134 'path:<path>' - a path relative to canonroot
135 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
136 'relpath:<path>' - a path relative to cwd
137 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
138 '<something>' - one of the cases above, selected by the dflt_pat argument
139
140 returns:
141 a 3-tuple containing
142 - list of roots (places where one should start a recursive walk of the fs);
143 this often matches the explicit non-pattern names passed in, but also
144 includes the initial part of glob: patterns that has no glob characters
145 - a bool match(filename) function
146 - a bool indicating if any patterns were passed in
147 """
148
149 # a common case: no patterns at all
150 if not names and not inc and not exc:
151 return [], util.always, False
59 152
153 def contains_glob(name):
154 for c in name:
155 if c in _globchars: return True
156 return False
157
158 def regex(kind, name, tail):
159 '''convert a pattern into a regular expression'''
160 if not name:
161 return ''
162 if kind == 're':
163 return name
164 elif kind == 'path':
165 return '^' + re.escape(name) + '(?:/|$)'
166 elif kind == 'relglob':
167 return _globre(name, '(?:|.*/)', tail)
168 elif kind == 'relpath':
169 return re.escape(name) + '(?:/|$)'
170 elif kind == 'relre':
171 if name.startswith('^'):
172 return name
173 return '.*' + name
174 return _globre(name, '', tail)
175
176 def matchfn(pats, tail):
177 """build a matching function from a set of patterns"""
178 if not pats:
179 return
180 try:
181 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
182 if len(pat) > 20000:
183 raise OverflowError()
184 return re.compile(pat).match
185 except OverflowError:
186 # We're using a Python with a tiny regex engine and we
187 # made it explode, so we'll divide the pattern list in two
188 # until it works
189 l = len(pats)
190 if l < 2:
191 raise
192 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
193 return lambda s: a(s) or b(s)
194 except re.error:
195 for k, p in pats:
196 try:
197 re.compile('(?:%s)' % regex(k, p, tail))
198 except re.error:
199 raise util.Abort("invalid pattern (%s): %s" % (k, p))
200 raise util.Abort("invalid pattern")
201
202 def globprefix(pat):
203 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
204 root = []
205 for p in pat.split('/'):
206 if contains_glob(p): break
207 root.append(p)
208 return '/'.join(root) or '.'
209
210 def normalizepats(names, default):
211 pats = []
212 roots = []
213 anypats = False
214 for kind, name in [_patsplit(p, default) for p in names]:
215 if kind in ('glob', 'relpath'):
216 name = util.canonpath(canonroot, cwd, name)
217 elif kind in ('relglob', 'path'):
218 name = util.normpath(name)
219
220 pats.append((kind, name))
221
222 if kind in ('glob', 're', 'relglob', 'relre'):
223 anypats = True
224
225 if kind == 'glob':
226 root = globprefix(name)
227 roots.append(root)
228 elif kind in ('relpath', 'path'):
229 roots.append(name or '.')
230 elif kind == 'relglob':
231 roots.append('.')
232 return roots, pats, anypats
233
234 roots, pats, anypats = normalizepats(names, dflt_pat)
235
236 patmatch = matchfn(pats, '$') or util.always
237 incmatch = util.always
238 if inc:
239 dummy, inckinds, dummy = normalizepats(inc, 'glob')
240 incmatch = matchfn(inckinds, '(?:/|$)')
241 excmatch = util.never
242 if exc:
243 dummy, exckinds, dummy = normalizepats(exc, 'glob')
244 excmatch = matchfn(exckinds, '(?:/|$)')
245
246 if not names and inc and not exc:
247 # common case: hgignore patterns
248 matcher = incmatch
249 else:
250 matcher = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
251
252 return (roots, matcher, (inc or exc or anypats) and True)
@@ -1,1455 +1,1260
1 1 # util.py - Mercurial utility functions and platform specfic implementations
2 2 #
3 3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2, incorporated herein by reference.
9 9
10 10 """Mercurial utility functions and platform specfic implementations.
11 11
12 12 This contains helper routines that are independent of the SCM core and
13 13 hide platform-specific details from the core.
14 14 """
15 15
16 16 from i18n import _
17 17 import error, osutil
18 18 import cStringIO, errno, re, shutil, sys, tempfile, traceback
19 19 import os, stat, time, calendar, glob, random
20 20 import imp
21 21
22 22 # Python compatibility
23 23
24 24 def sha1(s):
25 25 return _fastsha1(s)
26 26
27 27 def _fastsha1(s):
28 28 # This function will import sha1 from hashlib or sha (whichever is
29 29 # available) and overwrite itself with it on the first call.
30 30 # Subsequent calls will go directly to the imported function.
31 31 try:
32 32 from hashlib import sha1 as _sha1
33 33 except ImportError:
34 34 from sha import sha as _sha1
35 35 global _fastsha1, sha1
36 36 _fastsha1 = sha1 = _sha1
37 37 return _sha1(s)
38 38
39 39 import subprocess
40 40 closefds = os.name == 'posix'
41 41 def popen2(cmd):
42 42 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
43 43 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
44 44 return p.stdin, p.stdout
45 45 def popen3(cmd):
46 46 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
47 47 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
48 48 stderr=subprocess.PIPE)
49 49 return p.stdin, p.stdout, p.stderr
50 50
51 51 def version():
52 52 """Return version information if available."""
53 53 try:
54 54 import __version__
55 55 return __version__.version
56 56 except ImportError:
57 57 return 'unknown'
58 58
59 59 # used by parsedate
60 60 defaultdateformats = (
61 61 '%Y-%m-%d %H:%M:%S',
62 62 '%Y-%m-%d %I:%M:%S%p',
63 63 '%Y-%m-%d %H:%M',
64 64 '%Y-%m-%d %I:%M%p',
65 65 '%Y-%m-%d',
66 66 '%m-%d',
67 67 '%m/%d',
68 68 '%m/%d/%y',
69 69 '%m/%d/%Y',
70 70 '%a %b %d %H:%M:%S %Y',
71 71 '%a %b %d %I:%M:%S%p %Y',
72 72 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
73 73 '%b %d %H:%M:%S %Y',
74 74 '%b %d %I:%M:%S%p %Y',
75 75 '%b %d %H:%M:%S',
76 76 '%b %d %I:%M:%S%p',
77 77 '%b %d %H:%M',
78 78 '%b %d %I:%M%p',
79 79 '%b %d %Y',
80 80 '%b %d',
81 81 '%H:%M:%S',
82 82 '%I:%M:%SP',
83 83 '%H:%M',
84 84 '%I:%M%p',
85 85 )
86 86
87 87 extendeddateformats = defaultdateformats + (
88 88 "%Y",
89 89 "%Y-%m",
90 90 "%b",
91 91 "%b %Y",
92 92 )
93 93
94 94 def cachefunc(func):
95 95 '''cache the result of function calls'''
96 96 # XXX doesn't handle keywords args
97 97 cache = {}
98 98 if func.func_code.co_argcount == 1:
99 99 # we gain a small amount of time because
100 100 # we don't need to pack/unpack the list
101 101 def f(arg):
102 102 if arg not in cache:
103 103 cache[arg] = func(arg)
104 104 return cache[arg]
105 105 else:
106 106 def f(*args):
107 107 if args not in cache:
108 108 cache[args] = func(*args)
109 109 return cache[args]
110 110
111 111 return f
112 112
113 113 class propertycache(object):
114 114 def __init__(self, func):
115 115 self.func = func
116 116 self.name = func.__name__
117 117 def __get__(self, obj, type=None):
118 118 result = self.func(obj)
119 119 setattr(obj, self.name, result)
120 120 return result
121 121
122 122 def pipefilter(s, cmd):
123 123 '''filter string S through command CMD, returning its output'''
124 124 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
125 125 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
126 126 pout, perr = p.communicate(s)
127 127 return pout
128 128
129 129 def tempfilter(s, cmd):
130 130 '''filter string S through a pair of temporary files with CMD.
131 131 CMD is used as a template to create the real command to be run,
132 132 with the strings INFILE and OUTFILE replaced by the real names of
133 133 the temporary files generated.'''
134 134 inname, outname = None, None
135 135 try:
136 136 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
137 137 fp = os.fdopen(infd, 'wb')
138 138 fp.write(s)
139 139 fp.close()
140 140 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
141 141 os.close(outfd)
142 142 cmd = cmd.replace('INFILE', inname)
143 143 cmd = cmd.replace('OUTFILE', outname)
144 144 code = os.system(cmd)
145 145 if sys.platform == 'OpenVMS' and code & 1:
146 146 code = 0
147 147 if code: raise Abort(_("command '%s' failed: %s") %
148 148 (cmd, explain_exit(code)))
149 149 return open(outname, 'rb').read()
150 150 finally:
151 151 try:
152 152 if inname: os.unlink(inname)
153 153 except: pass
154 154 try:
155 155 if outname: os.unlink(outname)
156 156 except: pass
157 157
158 158 filtertable = {
159 159 'tempfile:': tempfilter,
160 160 'pipe:': pipefilter,
161 161 }
162 162
163 163 def filter(s, cmd):
164 164 "filter a string through a command that transforms its input to its output"
165 165 for name, fn in filtertable.iteritems():
166 166 if cmd.startswith(name):
167 167 return fn(s, cmd[len(name):].lstrip())
168 168 return pipefilter(s, cmd)
169 169
170 170 def binary(s):
171 171 """return true if a string is binary data"""
172 172 return bool(s and '\0' in s)
173 173
174 174 def increasingchunks(source, min=1024, max=65536):
175 175 '''return no less than min bytes per chunk while data remains,
176 176 doubling min after each chunk until it reaches max'''
177 177 def log2(x):
178 178 if not x:
179 179 return 0
180 180 i = 0
181 181 while x:
182 182 x >>= 1
183 183 i += 1
184 184 return i - 1
185 185
186 186 buf = []
187 187 blen = 0
188 188 for chunk in source:
189 189 buf.append(chunk)
190 190 blen += len(chunk)
191 191 if blen >= min:
192 192 if min < max:
193 193 min = min << 1
194 194 nmin = 1 << log2(blen)
195 195 if nmin > min:
196 196 min = nmin
197 197 if min > max:
198 198 min = max
199 199 yield ''.join(buf)
200 200 blen = 0
201 201 buf = []
202 202 if buf:
203 203 yield ''.join(buf)
204 204
205 205 Abort = error.Abort
206 206
207 207 def always(fn): return True
208 208 def never(fn): return False
209 209
210 def _patsplit(pat, default):
211 """Split a string into an optional pattern kind prefix and the
212 actual pattern."""
213 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
214 if pat.startswith(prefix + ':'): return pat.split(':', 1)
215 return default, pat
216
217 def _globre(pat, head='^', tail='$'):
218 "convert a glob pattern into a regexp"
219 i, n = 0, len(pat)
220 res = ''
221 group = 0
222 def peek(): return i < n and pat[i]
223 while i < n:
224 c = pat[i]
225 i = i+1
226 if c == '*':
227 if peek() == '*':
228 i += 1
229 res += '.*'
230 else:
231 res += '[^/]*'
232 elif c == '?':
233 res += '.'
234 elif c == '[':
235 j = i
236 if j < n and pat[j] in '!]':
237 j += 1
238 while j < n and pat[j] != ']':
239 j += 1
240 if j >= n:
241 res += '\\['
242 else:
243 stuff = pat[i:j].replace('\\','\\\\')
244 i = j + 1
245 if stuff[0] == '!':
246 stuff = '^' + stuff[1:]
247 elif stuff[0] == '^':
248 stuff = '\\' + stuff
249 res = '%s[%s]' % (res, stuff)
250 elif c == '{':
251 group += 1
252 res += '(?:'
253 elif c == '}' and group:
254 res += ')'
255 group -= 1
256 elif c == ',' and group:
257 res += '|'
258 elif c == '\\':
259 p = peek()
260 if p:
261 i += 1
262 res += re.escape(p)
263 else:
264 res += re.escape(c)
265 else:
266 res += re.escape(c)
267 return head + res + tail
268
269 _globchars = set('[{*?')
270
271 210 def pathto(root, n1, n2):
272 211 '''return the relative path from one place to another.
273 212 root should use os.sep to separate directories
274 213 n1 should use os.sep to separate directories
275 214 n2 should use "/" to separate directories
276 215 returns an os.sep-separated path.
277 216
278 217 If n1 is a relative path, it's assumed it's
279 218 relative to root.
280 219 n2 should always be relative to root.
281 220 '''
282 221 if not n1: return localpath(n2)
283 222 if os.path.isabs(n1):
284 223 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
285 224 return os.path.join(root, localpath(n2))
286 225 n2 = '/'.join((pconvert(root), n2))
287 226 a, b = splitpath(n1), n2.split('/')
288 227 a.reverse()
289 228 b.reverse()
290 229 while a and b and a[-1] == b[-1]:
291 230 a.pop()
292 231 b.pop()
293 232 b.reverse()
294 233 return os.sep.join((['..'] * len(a)) + b) or '.'
295 234
296 235 def canonpath(root, cwd, myname):
297 236 """return the canonical path of myname, given cwd and root"""
298 237 if root == os.sep:
299 238 rootsep = os.sep
300 239 elif endswithsep(root):
301 240 rootsep = root
302 241 else:
303 242 rootsep = root + os.sep
304 243 name = myname
305 244 if not os.path.isabs(name):
306 245 name = os.path.join(root, cwd, name)
307 246 name = os.path.normpath(name)
308 247 audit_path = path_auditor(root)
309 248 if name != rootsep and name.startswith(rootsep):
310 249 name = name[len(rootsep):]
311 250 audit_path(name)
312 251 return pconvert(name)
313 252 elif name == root:
314 253 return ''
315 254 else:
316 255 # Determine whether `name' is in the hierarchy at or beneath `root',
317 256 # by iterating name=dirname(name) until that causes no change (can't
318 257 # check name == '/', because that doesn't work on windows). For each
319 258 # `name', compare dev/inode numbers. If they match, the list `rel'
320 259 # holds the reversed list of components making up the relative file
321 260 # name we want.
322 261 root_st = os.stat(root)
323 262 rel = []
324 263 while True:
325 264 try:
326 265 name_st = os.stat(name)
327 266 except OSError:
328 267 break
329 268 if samestat(name_st, root_st):
330 269 if not rel:
331 270 # name was actually the same as root (maybe a symlink)
332 271 return ''
333 272 rel.reverse()
334 273 name = os.path.join(*rel)
335 274 audit_path(name)
336 275 return pconvert(name)
337 276 dirname, basename = os.path.split(name)
338 277 rel.append(basename)
339 278 if dirname == name:
340 279 break
341 280 name = dirname
342 281
343 282 raise Abort('%s not under root' % myname)
344 283
345 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], dflt_pat='glob'):
346 """build a function to match a set of file patterns
347
348 arguments:
349 canonroot - the canonical root of the tree you're matching against
350 cwd - the current working directory, if relevant
351 names - patterns to find
352 inc - patterns to include
353 exc - patterns to exclude
354 dflt_pat - if a pattern in names has no explicit type, assume this one
355
356 a pattern is one of:
357 'glob:<glob>' - a glob relative to cwd
358 're:<regexp>' - a regular expression
359 'path:<path>' - a path relative to canonroot
360 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
361 'relpath:<path>' - a path relative to cwd
362 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
363 '<something>' - one of the cases above, selected by the dflt_pat argument
364
365 returns:
366 a 3-tuple containing
367 - list of roots (places where one should start a recursive walk of the fs);
368 this often matches the explicit non-pattern names passed in, but also
369 includes the initial part of glob: patterns that has no glob characters
370 - a bool match(filename) function
371 - a bool indicating if any patterns were passed in
372 """
373
374 # a common case: no patterns at all
375 if not names and not inc and not exc:
376 return [], always, False
377
378 def contains_glob(name):
379 for c in name:
380 if c in _globchars: return True
381 return False
382
383 def regex(kind, name, tail):
384 '''convert a pattern into a regular expression'''
385 if not name:
386 return ''
387 if kind == 're':
388 return name
389 elif kind == 'path':
390 return '^' + re.escape(name) + '(?:/|$)'
391 elif kind == 'relglob':
392 return _globre(name, '(?:|.*/)', tail)
393 elif kind == 'relpath':
394 return re.escape(name) + '(?:/|$)'
395 elif kind == 'relre':
396 if name.startswith('^'):
397 return name
398 return '.*' + name
399 return _globre(name, '', tail)
400
401 def matchfn(pats, tail):
402 """build a matching function from a set of patterns"""
403 if not pats:
404 return
405 try:
406 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
407 if len(pat) > 20000:
408 raise OverflowError()
409 return re.compile(pat).match
410 except OverflowError:
411 # We're using a Python with a tiny regex engine and we
412 # made it explode, so we'll divide the pattern list in two
413 # until it works
414 l = len(pats)
415 if l < 2:
416 raise
417 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
418 return lambda s: a(s) or b(s)
419 except re.error:
420 for k, p in pats:
421 try:
422 re.compile('(?:%s)' % regex(k, p, tail))
423 except re.error:
424 raise Abort("invalid pattern (%s): %s" % (k, p))
425 raise Abort("invalid pattern")
426
427 def globprefix(pat):
428 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
429 root = []
430 for p in pat.split('/'):
431 if contains_glob(p): break
432 root.append(p)
433 return '/'.join(root) or '.'
434
435 def normalizepats(names, default):
436 pats = []
437 roots = []
438 anypats = False
439 for kind, name in [_patsplit(p, default) for p in names]:
440 if kind in ('glob', 'relpath'):
441 name = canonpath(canonroot, cwd, name)
442 elif kind in ('relglob', 'path'):
443 name = normpath(name)
444
445 pats.append((kind, name))
446
447 if kind in ('glob', 're', 'relglob', 'relre'):
448 anypats = True
449
450 if kind == 'glob':
451 root = globprefix(name)
452 roots.append(root)
453 elif kind in ('relpath', 'path'):
454 roots.append(name or '.')
455 elif kind == 'relglob':
456 roots.append('.')
457 return roots, pats, anypats
458
459 roots, pats, anypats = normalizepats(names, dflt_pat)
460
461 patmatch = matchfn(pats, '$') or always
462 incmatch = always
463 if inc:
464 dummy, inckinds, dummy = normalizepats(inc, 'glob')
465 incmatch = matchfn(inckinds, '(?:/|$)')
466 excmatch = never
467 if exc:
468 dummy, exckinds, dummy = normalizepats(exc, 'glob')
469 excmatch = matchfn(exckinds, '(?:/|$)')
470
471 if not names and inc and not exc:
472 # common case: hgignore patterns
473 match = incmatch
474 else:
475 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
476
477 return (roots, match, (inc or exc or anypats) and True)
478
479 284 _hgexecutable = None
480 285
481 286 def main_is_frozen():
482 287 """return True if we are a frozen executable.
483 288
484 289 The code supports py2exe (most common, Windows only) and tools/freeze
485 290 (portable, not much used).
486 291 """
487 292 return (hasattr(sys, "frozen") or # new py2exe
488 293 hasattr(sys, "importers") or # old py2exe
489 294 imp.is_frozen("__main__")) # tools/freeze
490 295
491 296 def hgexecutable():
492 297 """return location of the 'hg' executable.
493 298
494 299 Defaults to $HG or 'hg' in the search path.
495 300 """
496 301 if _hgexecutable is None:
497 302 hg = os.environ.get('HG')
498 303 if hg:
499 304 set_hgexecutable(hg)
500 305 elif main_is_frozen():
501 306 set_hgexecutable(sys.executable)
502 307 else:
503 308 set_hgexecutable(find_exe('hg') or 'hg')
504 309 return _hgexecutable
505 310
506 311 def set_hgexecutable(path):
507 312 """set location of the 'hg' executable"""
508 313 global _hgexecutable
509 314 _hgexecutable = path
510 315
511 316 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
512 317 '''enhanced shell command execution.
513 318 run with environment maybe modified, maybe in different dir.
514 319
515 320 if command fails and onerr is None, return status. if ui object,
516 321 print error message and return status, else raise onerr object as
517 322 exception.'''
518 323 def py2shell(val):
519 324 'convert python object into string that is useful to shell'
520 325 if val is None or val is False:
521 326 return '0'
522 327 if val is True:
523 328 return '1'
524 329 return str(val)
525 330 oldenv = {}
526 331 for k in environ:
527 332 oldenv[k] = os.environ.get(k)
528 333 if cwd is not None:
529 334 oldcwd = os.getcwd()
530 335 origcmd = cmd
531 336 if os.name == 'nt':
532 337 cmd = '"%s"' % cmd
533 338 try:
534 339 for k, v in environ.iteritems():
535 340 os.environ[k] = py2shell(v)
536 341 os.environ['HG'] = hgexecutable()
537 342 if cwd is not None and oldcwd != cwd:
538 343 os.chdir(cwd)
539 344 rc = os.system(cmd)
540 345 if sys.platform == 'OpenVMS' and rc & 1:
541 346 rc = 0
542 347 if rc and onerr:
543 348 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
544 349 explain_exit(rc)[0])
545 350 if errprefix:
546 351 errmsg = '%s: %s' % (errprefix, errmsg)
547 352 try:
548 353 onerr.warn(errmsg + '\n')
549 354 except AttributeError:
550 355 raise onerr(errmsg)
551 356 return rc
552 357 finally:
553 358 for k, v in oldenv.iteritems():
554 359 if v is None:
555 360 del os.environ[k]
556 361 else:
557 362 os.environ[k] = v
558 363 if cwd is not None and oldcwd != cwd:
559 364 os.chdir(oldcwd)
560 365
561 366 def checksignature(func):
562 367 '''wrap a function with code to check for calling errors'''
563 368 def check(*args, **kwargs):
564 369 try:
565 370 return func(*args, **kwargs)
566 371 except TypeError:
567 372 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
568 373 raise error.SignatureError
569 374 raise
570 375
571 376 return check
572 377
573 378 # os.path.lexists is not available on python2.3
574 379 def lexists(filename):
575 380 "test whether a file with this name exists. does not follow symlinks"
576 381 try:
577 382 os.lstat(filename)
578 383 except:
579 384 return False
580 385 return True
581 386
582 387 def rename(src, dst):
583 388 """forcibly rename a file"""
584 389 try:
585 390 os.rename(src, dst)
586 391 except OSError, err: # FIXME: check err (EEXIST ?)
587 392
588 393 # On windows, rename to existing file is not allowed, so we
589 394 # must delete destination first. But if a file is open, unlink
590 395 # schedules it for delete but does not delete it. Rename
591 396 # happens immediately even for open files, so we rename
592 397 # destination to a temporary name, then delete that. Then
593 398 # rename is safe to do.
594 399 # The temporary name is chosen at random to avoid the situation
595 400 # where a file is left lying around from a previous aborted run.
596 401 # The usual race condition this introduces can't be avoided as
597 402 # we need the name to rename into, and not the file itself. Due
598 403 # to the nature of the operation however, any races will at worst
599 404 # lead to the rename failing and the current operation aborting.
600 405
601 406 def tempname(prefix):
602 407 for tries in xrange(10):
603 408 temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
604 409 if not os.path.exists(temp):
605 410 return temp
606 411 raise IOError, (errno.EEXIST, "No usable temporary filename found")
607 412
608 413 temp = tempname(dst)
609 414 os.rename(dst, temp)
610 415 os.unlink(temp)
611 416 os.rename(src, dst)
612 417
613 418 def unlink(f):
614 419 """unlink and remove the directory if it is empty"""
615 420 os.unlink(f)
616 421 # try removing directories that might now be empty
617 422 try:
618 423 os.removedirs(os.path.dirname(f))
619 424 except OSError:
620 425 pass
621 426
622 427 def copyfile(src, dest):
623 428 "copy a file, preserving mode and atime/mtime"
624 429 if os.path.islink(src):
625 430 try:
626 431 os.unlink(dest)
627 432 except:
628 433 pass
629 434 os.symlink(os.readlink(src), dest)
630 435 else:
631 436 try:
632 437 shutil.copyfile(src, dest)
633 438 shutil.copystat(src, dest)
634 439 except shutil.Error, inst:
635 440 raise Abort(str(inst))
636 441
637 442 def copyfiles(src, dst, hardlink=None):
638 443 """Copy a directory tree using hardlinks if possible"""
639 444
640 445 if hardlink is None:
641 446 hardlink = (os.stat(src).st_dev ==
642 447 os.stat(os.path.dirname(dst)).st_dev)
643 448
644 449 if os.path.isdir(src):
645 450 os.mkdir(dst)
646 451 for name, kind in osutil.listdir(src):
647 452 srcname = os.path.join(src, name)
648 453 dstname = os.path.join(dst, name)
649 454 copyfiles(srcname, dstname, hardlink)
650 455 else:
651 456 if hardlink:
652 457 try:
653 458 os_link(src, dst)
654 459 except (IOError, OSError):
655 460 hardlink = False
656 461 shutil.copy(src, dst)
657 462 else:
658 463 shutil.copy(src, dst)
659 464
660 465 class path_auditor(object):
661 466 '''ensure that a filesystem path contains no banned components.
662 467 the following properties of a path are checked:
663 468
664 469 - under top-level .hg
665 470 - starts at the root of a windows drive
666 471 - contains ".."
667 472 - traverses a symlink (e.g. a/symlink_here/b)
668 473 - inside a nested repository'''
669 474
670 475 def __init__(self, root):
671 476 self.audited = set()
672 477 self.auditeddir = set()
673 478 self.root = root
674 479
675 480 def __call__(self, path):
676 481 if path in self.audited:
677 482 return
678 483 normpath = os.path.normcase(path)
679 484 parts = splitpath(normpath)
680 485 if (os.path.splitdrive(path)[0]
681 486 or parts[0].lower() in ('.hg', '.hg.', '')
682 487 or os.pardir in parts):
683 488 raise Abort(_("path contains illegal component: %s") % path)
684 489 if '.hg' in path.lower():
685 490 lparts = [p.lower() for p in parts]
686 491 for p in '.hg', '.hg.':
687 492 if p in lparts[1:]:
688 493 pos = lparts.index(p)
689 494 base = os.path.join(*parts[:pos])
690 495 raise Abort(_('path %r is inside repo %r') % (path, base))
691 496 def check(prefix):
692 497 curpath = os.path.join(self.root, prefix)
693 498 try:
694 499 st = os.lstat(curpath)
695 500 except OSError, err:
696 501 # EINVAL can be raised as invalid path syntax under win32.
697 502 # They must be ignored for patterns can be checked too.
698 503 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
699 504 raise
700 505 else:
701 506 if stat.S_ISLNK(st.st_mode):
702 507 raise Abort(_('path %r traverses symbolic link %r') %
703 508 (path, prefix))
704 509 elif (stat.S_ISDIR(st.st_mode) and
705 510 os.path.isdir(os.path.join(curpath, '.hg'))):
706 511 raise Abort(_('path %r is inside repo %r') %
707 512 (path, prefix))
708 513 parts.pop()
709 514 prefixes = []
710 515 for n in range(len(parts)):
711 516 prefix = os.sep.join(parts)
712 517 if prefix in self.auditeddir:
713 518 break
714 519 check(prefix)
715 520 prefixes.append(prefix)
716 521 parts.pop()
717 522
718 523 self.audited.add(path)
719 524 # only add prefixes to the cache after checking everything: we don't
720 525 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
721 526 self.auditeddir.update(prefixes)
722 527
723 528 def nlinks(pathname):
724 529 """Return number of hardlinks for the given file."""
725 530 return os.lstat(pathname).st_nlink
726 531
727 532 if hasattr(os, 'link'):
728 533 os_link = os.link
729 534 else:
730 535 def os_link(src, dst):
731 536 raise OSError(0, _("Hardlinks not supported"))
732 537
733 538 def lookup_reg(key, name=None, scope=None):
734 539 return None
735 540
736 541 if os.name == 'nt':
737 542 from windows import *
738 543 def expand_glob(pats):
739 544 '''On Windows, expand the implicit globs in a list of patterns'''
740 545 ret = []
741 546 for p in pats:
742 547 kind, name = _patsplit(p, None)
743 548 if kind is None:
744 549 globbed = glob.glob(name)
745 550 if globbed:
746 551 ret.extend(globbed)
747 552 continue
748 553 # if we couldn't expand the glob, just keep it around
749 554 ret.append(p)
750 555 return ret
751 556 else:
752 557 from posix import *
753 558
754 559 def makelock(info, pathname):
755 560 try:
756 561 return os.symlink(info, pathname)
757 562 except OSError, why:
758 563 if why.errno == errno.EEXIST:
759 564 raise
760 565 except AttributeError: # no symlink in os
761 566 pass
762 567
763 568 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
764 569 os.write(ld, info)
765 570 os.close(ld)
766 571
767 572 def readlock(pathname):
768 573 try:
769 574 return os.readlink(pathname)
770 575 except OSError, why:
771 576 if why.errno not in (errno.EINVAL, errno.ENOSYS):
772 577 raise
773 578 except AttributeError: # no symlink in os
774 579 pass
775 580 return posixfile(pathname).read()
776 581
777 582 def fstat(fp):
778 583 '''stat file object that may not have fileno method.'''
779 584 try:
780 585 return os.fstat(fp.fileno())
781 586 except AttributeError:
782 587 return os.stat(fp.name)
783 588
784 589 # File system features
785 590
786 591 def checkcase(path):
787 592 """
788 593 Check whether the given path is on a case-sensitive filesystem
789 594
790 595 Requires a path (like /foo/.hg) ending with a foldable final
791 596 directory component.
792 597 """
793 598 s1 = os.stat(path)
794 599 d, b = os.path.split(path)
795 600 p2 = os.path.join(d, b.upper())
796 601 if path == p2:
797 602 p2 = os.path.join(d, b.lower())
798 603 try:
799 604 s2 = os.stat(p2)
800 605 if s2 == s1:
801 606 return False
802 607 return True
803 608 except:
804 609 return True
805 610
806 611 _fspathcache = {}
807 612 def fspath(name, root):
808 613 '''Get name in the case stored in the filesystem
809 614
810 615 The name is either relative to root, or it is an absolute path starting
811 616 with root. Note that this function is unnecessary, and should not be
812 617 called, for case-sensitive filesystems (simply because it's expensive).
813 618 '''
814 619 # If name is absolute, make it relative
815 620 if name.lower().startswith(root.lower()):
816 621 l = len(root)
817 622 if name[l] == os.sep or name[l] == os.altsep:
818 623 l = l + 1
819 624 name = name[l:]
820 625
821 626 if not os.path.exists(os.path.join(root, name)):
822 627 return None
823 628
824 629 seps = os.sep
825 630 if os.altsep:
826 631 seps = seps + os.altsep
827 632 # Protect backslashes. This gets silly very quickly.
828 633 seps.replace('\\','\\\\')
829 634 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
830 635 dir = os.path.normcase(os.path.normpath(root))
831 636 result = []
832 637 for part, sep in pattern.findall(name):
833 638 if sep:
834 639 result.append(sep)
835 640 continue
836 641
837 642 if dir not in _fspathcache:
838 643 _fspathcache[dir] = os.listdir(dir)
839 644 contents = _fspathcache[dir]
840 645
841 646 lpart = part.lower()
842 647 for n in contents:
843 648 if n.lower() == lpart:
844 649 result.append(n)
845 650 break
846 651 else:
847 652 # Cannot happen, as the file exists!
848 653 result.append(part)
849 654 dir = os.path.join(dir, lpart)
850 655
851 656 return ''.join(result)
852 657
853 658 def checkexec(path):
854 659 """
855 660 Check whether the given path is on a filesystem with UNIX-like exec flags
856 661
857 662 Requires a directory (like /foo/.hg)
858 663 """
859 664
860 665 # VFAT on some Linux versions can flip mode but it doesn't persist
861 666 # a FS remount. Frequently we can detect it if files are created
862 667 # with exec bit on.
863 668
864 669 try:
865 670 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
866 671 fh, fn = tempfile.mkstemp("", "", path)
867 672 try:
868 673 os.close(fh)
869 674 m = os.stat(fn).st_mode & 0777
870 675 new_file_has_exec = m & EXECFLAGS
871 676 os.chmod(fn, m ^ EXECFLAGS)
872 677 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
873 678 finally:
874 679 os.unlink(fn)
875 680 except (IOError, OSError):
876 681 # we don't care, the user probably won't be able to commit anyway
877 682 return False
878 683 return not (new_file_has_exec or exec_flags_cannot_flip)
879 684
880 685 def checklink(path):
881 686 """check whether the given path is on a symlink-capable filesystem"""
882 687 # mktemp is not racy because symlink creation will fail if the
883 688 # file already exists
884 689 name = tempfile.mktemp(dir=path)
885 690 try:
886 691 os.symlink(".", name)
887 692 os.unlink(name)
888 693 return True
889 694 except (OSError, AttributeError):
890 695 return False
891 696
892 697 def needbinarypatch():
893 698 """return True if patches should be applied in binary mode by default."""
894 699 return os.name == 'nt'
895 700
896 701 def endswithsep(path):
897 702 '''Check path ends with os.sep or os.altsep.'''
898 703 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
899 704
900 705 def splitpath(path):
901 706 '''Split path by os.sep.
902 707 Note that this function does not use os.altsep because this is
903 708 an alternative of simple "xxx.split(os.sep)".
904 709 It is recommended to use os.path.normpath() before using this
905 710 function if need.'''
906 711 return path.split(os.sep)
907 712
908 713 def gui():
909 714 '''Are we running in a GUI?'''
910 715 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
911 716
912 717 def mktempcopy(name, emptyok=False, createmode=None):
913 718 """Create a temporary file with the same contents from name
914 719
915 720 The permission bits are copied from the original file.
916 721
917 722 If the temporary file is going to be truncated immediately, you
918 723 can use emptyok=True as an optimization.
919 724
920 725 Returns the name of the temporary file.
921 726 """
922 727 d, fn = os.path.split(name)
923 728 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
924 729 os.close(fd)
925 730 # Temporary files are created with mode 0600, which is usually not
926 731 # what we want. If the original file already exists, just copy
927 732 # its mode. Otherwise, manually obey umask.
928 733 try:
929 734 st_mode = os.lstat(name).st_mode & 0777
930 735 except OSError, inst:
931 736 if inst.errno != errno.ENOENT:
932 737 raise
933 738 st_mode = createmode
934 739 if st_mode is None:
935 740 st_mode = ~umask
936 741 st_mode &= 0666
937 742 os.chmod(temp, st_mode)
938 743 if emptyok:
939 744 return temp
940 745 try:
941 746 try:
942 747 ifp = posixfile(name, "rb")
943 748 except IOError, inst:
944 749 if inst.errno == errno.ENOENT:
945 750 return temp
946 751 if not getattr(inst, 'filename', None):
947 752 inst.filename = name
948 753 raise
949 754 ofp = posixfile(temp, "wb")
950 755 for chunk in filechunkiter(ifp):
951 756 ofp.write(chunk)
952 757 ifp.close()
953 758 ofp.close()
954 759 except:
955 760 try: os.unlink(temp)
956 761 except: pass
957 762 raise
958 763 return temp
959 764
960 765 class atomictempfile:
961 766 """file-like object that atomically updates a file
962 767
963 768 All writes will be redirected to a temporary copy of the original
964 769 file. When rename is called, the copy is renamed to the original
965 770 name, making the changes visible.
966 771 """
967 772 def __init__(self, name, mode, createmode):
968 773 self.__name = name
969 774 self._fp = None
970 775 self.temp = mktempcopy(name, emptyok=('w' in mode),
971 776 createmode=createmode)
972 777 self._fp = posixfile(self.temp, mode)
973 778
974 779 def __getattr__(self, name):
975 780 return getattr(self._fp, name)
976 781
977 782 def rename(self):
978 783 if not self.closed:
979 784 self._fp.close()
980 785 rename(self.temp, localpath(self.__name))
981 786
982 787 def __del__(self):
983 788 if not self.closed:
984 789 try:
985 790 os.unlink(self.temp)
986 791 except: pass
987 792 if self._fp:
988 793 self._fp.close()
989 794
990 795 def makedirs(name, mode=None):
991 796 """recursive directory creation with parent mode inheritance"""
992 797 try:
993 798 os.mkdir(name)
994 799 if mode is not None:
995 800 os.chmod(name, mode)
996 801 return
997 802 except OSError, err:
998 803 if err.errno == errno.EEXIST:
999 804 return
1000 805 if err.errno != errno.ENOENT:
1001 806 raise
1002 807 parent = os.path.abspath(os.path.dirname(name))
1003 808 makedirs(parent, mode)
1004 809 makedirs(name, mode)
1005 810
1006 811 class opener(object):
1007 812 """Open files relative to a base directory
1008 813
1009 814 This class is used to hide the details of COW semantics and
1010 815 remote file access from higher level code.
1011 816 """
1012 817 def __init__(self, base, audit=True):
1013 818 self.base = base
1014 819 if audit:
1015 820 self.audit_path = path_auditor(base)
1016 821 else:
1017 822 self.audit_path = always
1018 823 self.createmode = None
1019 824
1020 825 def __getattr__(self, name):
1021 826 if name == '_can_symlink':
1022 827 self._can_symlink = checklink(self.base)
1023 828 return self._can_symlink
1024 829 raise AttributeError(name)
1025 830
1026 831 def _fixfilemode(self, name):
1027 832 if self.createmode is None:
1028 833 return
1029 834 os.chmod(name, self.createmode & 0666)
1030 835
1031 836 def __call__(self, path, mode="r", text=False, atomictemp=False):
1032 837 self.audit_path(path)
1033 838 f = os.path.join(self.base, path)
1034 839
1035 840 if not text and "b" not in mode:
1036 841 mode += "b" # for that other OS
1037 842
1038 843 nlink = -1
1039 844 if mode not in ("r", "rb"):
1040 845 try:
1041 846 nlink = nlinks(f)
1042 847 except OSError:
1043 848 nlink = 0
1044 849 d = os.path.dirname(f)
1045 850 if not os.path.isdir(d):
1046 851 makedirs(d, self.createmode)
1047 852 if atomictemp:
1048 853 return atomictempfile(f, mode, self.createmode)
1049 854 if nlink > 1:
1050 855 rename(mktempcopy(f), f)
1051 856 fp = posixfile(f, mode)
1052 857 if nlink == 0:
1053 858 self._fixfilemode(f)
1054 859 return fp
1055 860
1056 861 def symlink(self, src, dst):
1057 862 self.audit_path(dst)
1058 863 linkname = os.path.join(self.base, dst)
1059 864 try:
1060 865 os.unlink(linkname)
1061 866 except OSError:
1062 867 pass
1063 868
1064 869 dirname = os.path.dirname(linkname)
1065 870 if not os.path.exists(dirname):
1066 871 makedirs(dirname, self.createmode)
1067 872
1068 873 if self._can_symlink:
1069 874 try:
1070 875 os.symlink(src, linkname)
1071 876 except OSError, err:
1072 877 raise OSError(err.errno, _('could not symlink to %r: %s') %
1073 878 (src, err.strerror), linkname)
1074 879 else:
1075 880 f = self(dst, "w")
1076 881 f.write(src)
1077 882 f.close()
1078 883 self._fixfilemode(dst)
1079 884
1080 885 class chunkbuffer(object):
1081 886 """Allow arbitrary sized chunks of data to be efficiently read from an
1082 887 iterator over chunks of arbitrary size."""
1083 888
1084 889 def __init__(self, in_iter):
1085 890 """in_iter is the iterator that's iterating over the input chunks.
1086 891 targetsize is how big a buffer to try to maintain."""
1087 892 self.iter = iter(in_iter)
1088 893 self.buf = ''
1089 894 self.targetsize = 2**16
1090 895
1091 896 def read(self, l):
1092 897 """Read L bytes of data from the iterator of chunks of data.
1093 898 Returns less than L bytes if the iterator runs dry."""
1094 899 if l > len(self.buf) and self.iter:
1095 900 # Clamp to a multiple of self.targetsize
1096 901 targetsize = max(l, self.targetsize)
1097 902 collector = cStringIO.StringIO()
1098 903 collector.write(self.buf)
1099 904 collected = len(self.buf)
1100 905 for chunk in self.iter:
1101 906 collector.write(chunk)
1102 907 collected += len(chunk)
1103 908 if collected >= targetsize:
1104 909 break
1105 910 if collected < targetsize:
1106 911 self.iter = False
1107 912 self.buf = collector.getvalue()
1108 913 if len(self.buf) == l:
1109 914 s, self.buf = str(self.buf), ''
1110 915 else:
1111 916 s, self.buf = self.buf[:l], buffer(self.buf, l)
1112 917 return s
1113 918
1114 919 def filechunkiter(f, size=65536, limit=None):
1115 920 """Create a generator that produces the data in the file size
1116 921 (default 65536) bytes at a time, up to optional limit (default is
1117 922 to read all data). Chunks may be less than size bytes if the
1118 923 chunk is the last chunk in the file, or the file is a socket or
1119 924 some other type of file that sometimes reads less data than is
1120 925 requested."""
1121 926 assert size >= 0
1122 927 assert limit is None or limit >= 0
1123 928 while True:
1124 929 if limit is None: nbytes = size
1125 930 else: nbytes = min(limit, size)
1126 931 s = nbytes and f.read(nbytes)
1127 932 if not s: break
1128 933 if limit: limit -= len(s)
1129 934 yield s
1130 935
1131 936 def makedate():
1132 937 lt = time.localtime()
1133 938 if lt[8] == 1 and time.daylight:
1134 939 tz = time.altzone
1135 940 else:
1136 941 tz = time.timezone
1137 942 return time.mktime(lt), tz
1138 943
1139 944 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1140 945 """represent a (unixtime, offset) tuple as a localized time.
1141 946 unixtime is seconds since the epoch, and offset is the time zone's
1142 947 number of seconds away from UTC. if timezone is false, do not
1143 948 append time zone to string."""
1144 949 t, tz = date or makedate()
1145 950 if "%1" in format or "%2" in format:
1146 951 sign = (tz > 0) and "-" or "+"
1147 952 minutes = abs(tz) / 60
1148 953 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1149 954 format = format.replace("%2", "%02d" % (minutes % 60))
1150 955 s = time.strftime(format, time.gmtime(float(t) - tz))
1151 956 return s
1152 957
1153 958 def shortdate(date=None):
1154 959 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1155 960 return datestr(date, format='%Y-%m-%d')
1156 961
1157 962 def strdate(string, format, defaults=[]):
1158 963 """parse a localized time string and return a (unixtime, offset) tuple.
1159 964 if the string cannot be parsed, ValueError is raised."""
1160 965 def timezone(string):
1161 966 tz = string.split()[-1]
1162 967 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1163 968 sign = (tz[0] == "+") and 1 or -1
1164 969 hours = int(tz[1:3])
1165 970 minutes = int(tz[3:5])
1166 971 return -sign * (hours * 60 + minutes) * 60
1167 972 if tz == "GMT" or tz == "UTC":
1168 973 return 0
1169 974 return None
1170 975
1171 976 # NOTE: unixtime = localunixtime + offset
1172 977 offset, date = timezone(string), string
1173 978 if offset != None:
1174 979 date = " ".join(string.split()[:-1])
1175 980
1176 981 # add missing elements from defaults
1177 982 for part in defaults:
1178 983 found = [True for p in part if ("%"+p) in format]
1179 984 if not found:
1180 985 date += "@" + defaults[part]
1181 986 format += "@%" + part[0]
1182 987
1183 988 timetuple = time.strptime(date, format)
1184 989 localunixtime = int(calendar.timegm(timetuple))
1185 990 if offset is None:
1186 991 # local timezone
1187 992 unixtime = int(time.mktime(timetuple))
1188 993 offset = unixtime - localunixtime
1189 994 else:
1190 995 unixtime = localunixtime + offset
1191 996 return unixtime, offset
1192 997
1193 998 def parsedate(date, formats=None, defaults=None):
1194 999 """parse a localized date/time string and return a (unixtime, offset) tuple.
1195 1000
1196 1001 The date may be a "unixtime offset" string or in one of the specified
1197 1002 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1198 1003 """
1199 1004 if not date:
1200 1005 return 0, 0
1201 1006 if isinstance(date, tuple) and len(date) == 2:
1202 1007 return date
1203 1008 if not formats:
1204 1009 formats = defaultdateformats
1205 1010 date = date.strip()
1206 1011 try:
1207 1012 when, offset = map(int, date.split(' '))
1208 1013 except ValueError:
1209 1014 # fill out defaults
1210 1015 if not defaults:
1211 1016 defaults = {}
1212 1017 now = makedate()
1213 1018 for part in "d mb yY HI M S".split():
1214 1019 if part not in defaults:
1215 1020 if part[0] in "HMS":
1216 1021 defaults[part] = "00"
1217 1022 else:
1218 1023 defaults[part] = datestr(now, "%" + part[0])
1219 1024
1220 1025 for format in formats:
1221 1026 try:
1222 1027 when, offset = strdate(date, format, defaults)
1223 1028 except (ValueError, OverflowError):
1224 1029 pass
1225 1030 else:
1226 1031 break
1227 1032 else:
1228 1033 raise Abort(_('invalid date: %r ') % date)
1229 1034 # validate explicit (probably user-specified) date and
1230 1035 # time zone offset. values must fit in signed 32 bits for
1231 1036 # current 32-bit linux runtimes. timezones go from UTC-12
1232 1037 # to UTC+14
1233 1038 if abs(when) > 0x7fffffff:
1234 1039 raise Abort(_('date exceeds 32 bits: %d') % when)
1235 1040 if offset < -50400 or offset > 43200:
1236 1041 raise Abort(_('impossible time zone offset: %d') % offset)
1237 1042 return when, offset
1238 1043
1239 1044 def matchdate(date):
1240 1045 """Return a function that matches a given date match specifier
1241 1046
1242 1047 Formats include:
1243 1048
1244 1049 '{date}' match a given date to the accuracy provided
1245 1050
1246 1051 '<{date}' on or before a given date
1247 1052
1248 1053 '>{date}' on or after a given date
1249 1054
1250 1055 """
1251 1056
1252 1057 def lower(date):
1253 1058 d = dict(mb="1", d="1")
1254 1059 return parsedate(date, extendeddateformats, d)[0]
1255 1060
1256 1061 def upper(date):
1257 1062 d = dict(mb="12", HI="23", M="59", S="59")
1258 1063 for days in "31 30 29".split():
1259 1064 try:
1260 1065 d["d"] = days
1261 1066 return parsedate(date, extendeddateformats, d)[0]
1262 1067 except:
1263 1068 pass
1264 1069 d["d"] = "28"
1265 1070 return parsedate(date, extendeddateformats, d)[0]
1266 1071
1267 1072 date = date.strip()
1268 1073 if date[0] == "<":
1269 1074 when = upper(date[1:])
1270 1075 return lambda x: x <= when
1271 1076 elif date[0] == ">":
1272 1077 when = lower(date[1:])
1273 1078 return lambda x: x >= when
1274 1079 elif date[0] == "-":
1275 1080 try:
1276 1081 days = int(date[1:])
1277 1082 except ValueError:
1278 1083 raise Abort(_("invalid day spec: %s") % date[1:])
1279 1084 when = makedate()[0] - days * 3600 * 24
1280 1085 return lambda x: x >= when
1281 1086 elif " to " in date:
1282 1087 a, b = date.split(" to ")
1283 1088 start, stop = lower(a), upper(b)
1284 1089 return lambda x: x >= start and x <= stop
1285 1090 else:
1286 1091 start, stop = lower(date), upper(date)
1287 1092 return lambda x: x >= start and x <= stop
1288 1093
1289 1094 def shortuser(user):
1290 1095 """Return a short representation of a user name or email address."""
1291 1096 f = user.find('@')
1292 1097 if f >= 0:
1293 1098 user = user[:f]
1294 1099 f = user.find('<')
1295 1100 if f >= 0:
1296 1101 user = user[f+1:]
1297 1102 f = user.find(' ')
1298 1103 if f >= 0:
1299 1104 user = user[:f]
1300 1105 f = user.find('.')
1301 1106 if f >= 0:
1302 1107 user = user[:f]
1303 1108 return user
1304 1109
1305 1110 def email(author):
1306 1111 '''get email of author.'''
1307 1112 r = author.find('>')
1308 1113 if r == -1: r = None
1309 1114 return author[author.find('<')+1:r]
1310 1115
1311 1116 def ellipsis(text, maxlength=400):
1312 1117 """Trim string to at most maxlength (default: 400) characters."""
1313 1118 if len(text) <= maxlength:
1314 1119 return text
1315 1120 else:
1316 1121 return "%s..." % (text[:maxlength-3])
1317 1122
1318 1123 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1319 1124 '''yield every hg repository under path, recursively.'''
1320 1125 def errhandler(err):
1321 1126 if err.filename == path:
1322 1127 raise err
1323 1128 if followsym and hasattr(os.path, 'samestat'):
1324 1129 def _add_dir_if_not_there(dirlst, dirname):
1325 1130 match = False
1326 1131 samestat = os.path.samestat
1327 1132 dirstat = os.stat(dirname)
1328 1133 for lstdirstat in dirlst:
1329 1134 if samestat(dirstat, lstdirstat):
1330 1135 match = True
1331 1136 break
1332 1137 if not match:
1333 1138 dirlst.append(dirstat)
1334 1139 return not match
1335 1140 else:
1336 1141 followsym = False
1337 1142
1338 1143 if (seen_dirs is None) and followsym:
1339 1144 seen_dirs = []
1340 1145 _add_dir_if_not_there(seen_dirs, path)
1341 1146 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1342 1147 if '.hg' in dirs:
1343 1148 yield root # found a repository
1344 1149 qroot = os.path.join(root, '.hg', 'patches')
1345 1150 if os.path.isdir(os.path.join(qroot, '.hg')):
1346 1151 yield qroot # we have a patch queue repo here
1347 1152 if recurse:
1348 1153 # avoid recursing inside the .hg directory
1349 1154 dirs.remove('.hg')
1350 1155 else:
1351 1156 dirs[:] = [] # don't descend further
1352 1157 elif followsym:
1353 1158 newdirs = []
1354 1159 for d in dirs:
1355 1160 fname = os.path.join(root, d)
1356 1161 if _add_dir_if_not_there(seen_dirs, fname):
1357 1162 if os.path.islink(fname):
1358 1163 for hgname in walkrepos(fname, True, seen_dirs):
1359 1164 yield hgname
1360 1165 else:
1361 1166 newdirs.append(d)
1362 1167 dirs[:] = newdirs
1363 1168
1364 1169 _rcpath = None
1365 1170
1366 1171 def os_rcpath():
1367 1172 '''return default os-specific hgrc search path'''
1368 1173 path = system_rcpath()
1369 1174 path.extend(user_rcpath())
1370 1175 path = [os.path.normpath(f) for f in path]
1371 1176 return path
1372 1177
1373 1178 def rcpath():
1374 1179 '''return hgrc search path. if env var HGRCPATH is set, use it.
1375 1180 for each item in path, if directory, use files ending in .rc,
1376 1181 else use item.
1377 1182 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1378 1183 if no HGRCPATH, use default os-specific path.'''
1379 1184 global _rcpath
1380 1185 if _rcpath is None:
1381 1186 if 'HGRCPATH' in os.environ:
1382 1187 _rcpath = []
1383 1188 for p in os.environ['HGRCPATH'].split(os.pathsep):
1384 1189 if not p: continue
1385 1190 if os.path.isdir(p):
1386 1191 for f, kind in osutil.listdir(p):
1387 1192 if f.endswith('.rc'):
1388 1193 _rcpath.append(os.path.join(p, f))
1389 1194 else:
1390 1195 _rcpath.append(p)
1391 1196 else:
1392 1197 _rcpath = os_rcpath()
1393 1198 return _rcpath
1394 1199
1395 1200 def bytecount(nbytes):
1396 1201 '''return byte count formatted as readable string, with units'''
1397 1202
1398 1203 units = (
1399 1204 (100, 1<<30, _('%.0f GB')),
1400 1205 (10, 1<<30, _('%.1f GB')),
1401 1206 (1, 1<<30, _('%.2f GB')),
1402 1207 (100, 1<<20, _('%.0f MB')),
1403 1208 (10, 1<<20, _('%.1f MB')),
1404 1209 (1, 1<<20, _('%.2f MB')),
1405 1210 (100, 1<<10, _('%.0f KB')),
1406 1211 (10, 1<<10, _('%.1f KB')),
1407 1212 (1, 1<<10, _('%.2f KB')),
1408 1213 (1, 1, _('%.0f bytes')),
1409 1214 )
1410 1215
1411 1216 for multiplier, divisor, format in units:
1412 1217 if nbytes >= divisor * multiplier:
1413 1218 return format % (nbytes / float(divisor))
1414 1219 return units[-1][2] % nbytes
1415 1220
1416 1221 def drop_scheme(scheme, path):
1417 1222 sc = scheme + ':'
1418 1223 if path.startswith(sc):
1419 1224 path = path[len(sc):]
1420 1225 if path.startswith('//'):
1421 1226 path = path[2:]
1422 1227 return path
1423 1228
1424 1229 def uirepr(s):
1425 1230 # Avoid double backslash in Windows path repr()
1426 1231 return repr(s).replace('\\\\', '\\')
1427 1232
1428 1233 def termwidth():
1429 1234 if 'COLUMNS' in os.environ:
1430 1235 try:
1431 1236 return int(os.environ['COLUMNS'])
1432 1237 except ValueError:
1433 1238 pass
1434 1239 try:
1435 1240 import termios, array, fcntl
1436 1241 for dev in (sys.stdout, sys.stdin):
1437 1242 try:
1438 1243 try:
1439 1244 fd = dev.fileno()
1440 1245 except AttributeError:
1441 1246 continue
1442 1247 if not os.isatty(fd):
1443 1248 continue
1444 1249 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
1445 1250 return array.array('h', arri)[1]
1446 1251 except ValueError:
1447 1252 pass
1448 1253 except ImportError:
1449 1254 pass
1450 1255 return 80
1451 1256
1452 1257 def iterlines(iterator):
1453 1258 for chunk in iterator:
1454 1259 for line in chunk.splitlines():
1455 1260 yield line
General Comments 0
You need to be logged in to leave comments. Login now