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