##// END OF EJS Templates
Add encoding detection
Matt Mackall -
r3769:96095d9f default
parent child Browse files
Show More
@@ -1,1106 +1,1108
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import gettext as _
15 from i18n import gettext as _
16 from demandload import *
16 from demandload import *
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 demandload(globals(), "os threading time calendar ConfigParser")
18 demandload(globals(), "os threading time calendar ConfigParser locale")
19
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding()
19
21
20 # used by parsedate
22 # used by parsedate
21 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
23 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
22 '%a %b %d %H:%M:%S %Y')
24 '%a %b %d %H:%M:%S %Y')
23
25
24 class SignalInterrupt(Exception):
26 class SignalInterrupt(Exception):
25 """Exception raised on SIGTERM and SIGHUP."""
27 """Exception raised on SIGTERM and SIGHUP."""
26
28
27 # like SafeConfigParser but with case-sensitive keys
29 # like SafeConfigParser but with case-sensitive keys
28 class configparser(ConfigParser.SafeConfigParser):
30 class configparser(ConfigParser.SafeConfigParser):
29 def optionxform(self, optionstr):
31 def optionxform(self, optionstr):
30 return optionstr
32 return optionstr
31
33
32 def cachefunc(func):
34 def cachefunc(func):
33 '''cache the result of function calls'''
35 '''cache the result of function calls'''
34 # XXX doesn't handle keywords args
36 # XXX doesn't handle keywords args
35 cache = {}
37 cache = {}
36 if func.func_code.co_argcount == 1:
38 if func.func_code.co_argcount == 1:
37 # we gain a small amount of time because
39 # we gain a small amount of time because
38 # we don't need to pack/unpack the list
40 # we don't need to pack/unpack the list
39 def f(arg):
41 def f(arg):
40 if arg not in cache:
42 if arg not in cache:
41 cache[arg] = func(arg)
43 cache[arg] = func(arg)
42 return cache[arg]
44 return cache[arg]
43 else:
45 else:
44 def f(*args):
46 def f(*args):
45 if args not in cache:
47 if args not in cache:
46 cache[args] = func(*args)
48 cache[args] = func(*args)
47 return cache[args]
49 return cache[args]
48
50
49 return f
51 return f
50
52
51 def pipefilter(s, cmd):
53 def pipefilter(s, cmd):
52 '''filter string S through command CMD, returning its output'''
54 '''filter string S through command CMD, returning its output'''
53 (pout, pin) = popen2.popen2(cmd, -1, 'b')
55 (pout, pin) = popen2.popen2(cmd, -1, 'b')
54 def writer():
56 def writer():
55 try:
57 try:
56 pin.write(s)
58 pin.write(s)
57 pin.close()
59 pin.close()
58 except IOError, inst:
60 except IOError, inst:
59 if inst.errno != errno.EPIPE:
61 if inst.errno != errno.EPIPE:
60 raise
62 raise
61
63
62 # we should use select instead on UNIX, but this will work on most
64 # we should use select instead on UNIX, but this will work on most
63 # systems, including Windows
65 # systems, including Windows
64 w = threading.Thread(target=writer)
66 w = threading.Thread(target=writer)
65 w.start()
67 w.start()
66 f = pout.read()
68 f = pout.read()
67 pout.close()
69 pout.close()
68 w.join()
70 w.join()
69 return f
71 return f
70
72
71 def tempfilter(s, cmd):
73 def tempfilter(s, cmd):
72 '''filter string S through a pair of temporary files with CMD.
74 '''filter string S through a pair of temporary files with CMD.
73 CMD is used as a template to create the real command to be run,
75 CMD is used as a template to create the real command to be run,
74 with the strings INFILE and OUTFILE replaced by the real names of
76 with the strings INFILE and OUTFILE replaced by the real names of
75 the temporary files generated.'''
77 the temporary files generated.'''
76 inname, outname = None, None
78 inname, outname = None, None
77 try:
79 try:
78 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
80 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
79 fp = os.fdopen(infd, 'wb')
81 fp = os.fdopen(infd, 'wb')
80 fp.write(s)
82 fp.write(s)
81 fp.close()
83 fp.close()
82 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
84 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
83 os.close(outfd)
85 os.close(outfd)
84 cmd = cmd.replace('INFILE', inname)
86 cmd = cmd.replace('INFILE', inname)
85 cmd = cmd.replace('OUTFILE', outname)
87 cmd = cmd.replace('OUTFILE', outname)
86 code = os.system(cmd)
88 code = os.system(cmd)
87 if code: raise Abort(_("command '%s' failed: %s") %
89 if code: raise Abort(_("command '%s' failed: %s") %
88 (cmd, explain_exit(code)))
90 (cmd, explain_exit(code)))
89 return open(outname, 'rb').read()
91 return open(outname, 'rb').read()
90 finally:
92 finally:
91 try:
93 try:
92 if inname: os.unlink(inname)
94 if inname: os.unlink(inname)
93 except: pass
95 except: pass
94 try:
96 try:
95 if outname: os.unlink(outname)
97 if outname: os.unlink(outname)
96 except: pass
98 except: pass
97
99
98 filtertable = {
100 filtertable = {
99 'tempfile:': tempfilter,
101 'tempfile:': tempfilter,
100 'pipe:': pipefilter,
102 'pipe:': pipefilter,
101 }
103 }
102
104
103 def filter(s, cmd):
105 def filter(s, cmd):
104 "filter a string through a command that transforms its input to its output"
106 "filter a string through a command that transforms its input to its output"
105 for name, fn in filtertable.iteritems():
107 for name, fn in filtertable.iteritems():
106 if cmd.startswith(name):
108 if cmd.startswith(name):
107 return fn(s, cmd[len(name):].lstrip())
109 return fn(s, cmd[len(name):].lstrip())
108 return pipefilter(s, cmd)
110 return pipefilter(s, cmd)
109
111
110 def find_in_path(name, path, default=None):
112 def find_in_path(name, path, default=None):
111 '''find name in search path. path can be string (will be split
113 '''find name in search path. path can be string (will be split
112 with os.pathsep), or iterable thing that returns strings. if name
114 with os.pathsep), or iterable thing that returns strings. if name
113 found, return path to name. else return default.'''
115 found, return path to name. else return default.'''
114 if isinstance(path, str):
116 if isinstance(path, str):
115 path = path.split(os.pathsep)
117 path = path.split(os.pathsep)
116 for p in path:
118 for p in path:
117 p_name = os.path.join(p, name)
119 p_name = os.path.join(p, name)
118 if os.path.exists(p_name):
120 if os.path.exists(p_name):
119 return p_name
121 return p_name
120 return default
122 return default
121
123
122 def binary(s):
124 def binary(s):
123 """return true if a string is binary data using diff's heuristic"""
125 """return true if a string is binary data using diff's heuristic"""
124 if s and '\0' in s[:4096]:
126 if s and '\0' in s[:4096]:
125 return True
127 return True
126 return False
128 return False
127
129
128 def unique(g):
130 def unique(g):
129 """return the uniq elements of iterable g"""
131 """return the uniq elements of iterable g"""
130 seen = {}
132 seen = {}
131 l = []
133 l = []
132 for f in g:
134 for f in g:
133 if f not in seen:
135 if f not in seen:
134 seen[f] = 1
136 seen[f] = 1
135 l.append(f)
137 l.append(f)
136 return l
138 return l
137
139
138 class Abort(Exception):
140 class Abort(Exception):
139 """Raised if a command needs to print an error and exit."""
141 """Raised if a command needs to print an error and exit."""
140
142
141 class UnexpectedOutput(Abort):
143 class UnexpectedOutput(Abort):
142 """Raised to print an error with part of output and exit."""
144 """Raised to print an error with part of output and exit."""
143
145
144 def always(fn): return True
146 def always(fn): return True
145 def never(fn): return False
147 def never(fn): return False
146
148
147 def patkind(name, dflt_pat='glob'):
149 def patkind(name, dflt_pat='glob'):
148 """Split a string into an optional pattern kind prefix and the
150 """Split a string into an optional pattern kind prefix and the
149 actual pattern."""
151 actual pattern."""
150 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
152 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
151 if name.startswith(prefix + ':'): return name.split(':', 1)
153 if name.startswith(prefix + ':'): return name.split(':', 1)
152 return dflt_pat, name
154 return dflt_pat, name
153
155
154 def globre(pat, head='^', tail='$'):
156 def globre(pat, head='^', tail='$'):
155 "convert a glob pattern into a regexp"
157 "convert a glob pattern into a regexp"
156 i, n = 0, len(pat)
158 i, n = 0, len(pat)
157 res = ''
159 res = ''
158 group = False
160 group = False
159 def peek(): return i < n and pat[i]
161 def peek(): return i < n and pat[i]
160 while i < n:
162 while i < n:
161 c = pat[i]
163 c = pat[i]
162 i = i+1
164 i = i+1
163 if c == '*':
165 if c == '*':
164 if peek() == '*':
166 if peek() == '*':
165 i += 1
167 i += 1
166 res += '.*'
168 res += '.*'
167 else:
169 else:
168 res += '[^/]*'
170 res += '[^/]*'
169 elif c == '?':
171 elif c == '?':
170 res += '.'
172 res += '.'
171 elif c == '[':
173 elif c == '[':
172 j = i
174 j = i
173 if j < n and pat[j] in '!]':
175 if j < n and pat[j] in '!]':
174 j += 1
176 j += 1
175 while j < n and pat[j] != ']':
177 while j < n and pat[j] != ']':
176 j += 1
178 j += 1
177 if j >= n:
179 if j >= n:
178 res += '\\['
180 res += '\\['
179 else:
181 else:
180 stuff = pat[i:j].replace('\\','\\\\')
182 stuff = pat[i:j].replace('\\','\\\\')
181 i = j + 1
183 i = j + 1
182 if stuff[0] == '!':
184 if stuff[0] == '!':
183 stuff = '^' + stuff[1:]
185 stuff = '^' + stuff[1:]
184 elif stuff[0] == '^':
186 elif stuff[0] == '^':
185 stuff = '\\' + stuff
187 stuff = '\\' + stuff
186 res = '%s[%s]' % (res, stuff)
188 res = '%s[%s]' % (res, stuff)
187 elif c == '{':
189 elif c == '{':
188 group = True
190 group = True
189 res += '(?:'
191 res += '(?:'
190 elif c == '}' and group:
192 elif c == '}' and group:
191 res += ')'
193 res += ')'
192 group = False
194 group = False
193 elif c == ',' and group:
195 elif c == ',' and group:
194 res += '|'
196 res += '|'
195 elif c == '\\':
197 elif c == '\\':
196 p = peek()
198 p = peek()
197 if p:
199 if p:
198 i += 1
200 i += 1
199 res += re.escape(p)
201 res += re.escape(p)
200 else:
202 else:
201 res += re.escape(c)
203 res += re.escape(c)
202 else:
204 else:
203 res += re.escape(c)
205 res += re.escape(c)
204 return head + res + tail
206 return head + res + tail
205
207
206 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
208 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
207
209
208 def pathto(n1, n2):
210 def pathto(n1, n2):
209 '''return the relative path from one place to another.
211 '''return the relative path from one place to another.
210 n1 should use os.sep to separate directories
212 n1 should use os.sep to separate directories
211 n2 should use "/" to separate directories
213 n2 should use "/" to separate directories
212 returns an os.sep-separated path.
214 returns an os.sep-separated path.
213 '''
215 '''
214 if not n1: return localpath(n2)
216 if not n1: return localpath(n2)
215 a, b = n1.split(os.sep), n2.split('/')
217 a, b = n1.split(os.sep), n2.split('/')
216 a.reverse()
218 a.reverse()
217 b.reverse()
219 b.reverse()
218 while a and b and a[-1] == b[-1]:
220 while a and b and a[-1] == b[-1]:
219 a.pop()
221 a.pop()
220 b.pop()
222 b.pop()
221 b.reverse()
223 b.reverse()
222 return os.sep.join((['..'] * len(a)) + b)
224 return os.sep.join((['..'] * len(a)) + b)
223
225
224 def canonpath(root, cwd, myname):
226 def canonpath(root, cwd, myname):
225 """return the canonical path of myname, given cwd and root"""
227 """return the canonical path of myname, given cwd and root"""
226 if root == os.sep:
228 if root == os.sep:
227 rootsep = os.sep
229 rootsep = os.sep
228 elif root.endswith(os.sep):
230 elif root.endswith(os.sep):
229 rootsep = root
231 rootsep = root
230 else:
232 else:
231 rootsep = root + os.sep
233 rootsep = root + os.sep
232 name = myname
234 name = myname
233 if not os.path.isabs(name):
235 if not os.path.isabs(name):
234 name = os.path.join(root, cwd, name)
236 name = os.path.join(root, cwd, name)
235 name = os.path.normpath(name)
237 name = os.path.normpath(name)
236 if name != rootsep and name.startswith(rootsep):
238 if name != rootsep and name.startswith(rootsep):
237 name = name[len(rootsep):]
239 name = name[len(rootsep):]
238 audit_path(name)
240 audit_path(name)
239 return pconvert(name)
241 return pconvert(name)
240 elif name == root:
242 elif name == root:
241 return ''
243 return ''
242 else:
244 else:
243 # Determine whether `name' is in the hierarchy at or beneath `root',
245 # Determine whether `name' is in the hierarchy at or beneath `root',
244 # by iterating name=dirname(name) until that causes no change (can't
246 # by iterating name=dirname(name) until that causes no change (can't
245 # check name == '/', because that doesn't work on windows). For each
247 # check name == '/', because that doesn't work on windows). For each
246 # `name', compare dev/inode numbers. If they match, the list `rel'
248 # `name', compare dev/inode numbers. If they match, the list `rel'
247 # holds the reversed list of components making up the relative file
249 # holds the reversed list of components making up the relative file
248 # name we want.
250 # name we want.
249 root_st = os.stat(root)
251 root_st = os.stat(root)
250 rel = []
252 rel = []
251 while True:
253 while True:
252 try:
254 try:
253 name_st = os.stat(name)
255 name_st = os.stat(name)
254 except OSError:
256 except OSError:
255 break
257 break
256 if samestat(name_st, root_st):
258 if samestat(name_st, root_st):
257 rel.reverse()
259 rel.reverse()
258 name = os.path.join(*rel)
260 name = os.path.join(*rel)
259 audit_path(name)
261 audit_path(name)
260 return pconvert(name)
262 return pconvert(name)
261 dirname, basename = os.path.split(name)
263 dirname, basename = os.path.split(name)
262 rel.append(basename)
264 rel.append(basename)
263 if dirname == name:
265 if dirname == name:
264 break
266 break
265 name = dirname
267 name = dirname
266
268
267 raise Abort('%s not under root' % myname)
269 raise Abort('%s not under root' % myname)
268
270
269 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
271 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
270 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
272 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
271
273
272 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
274 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
273 if os.name == 'nt':
275 if os.name == 'nt':
274 dflt_pat = 'glob'
276 dflt_pat = 'glob'
275 else:
277 else:
276 dflt_pat = 'relpath'
278 dflt_pat = 'relpath'
277 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
279 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
278
280
279 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
281 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
280 """build a function to match a set of file patterns
282 """build a function to match a set of file patterns
281
283
282 arguments:
284 arguments:
283 canonroot - the canonical root of the tree you're matching against
285 canonroot - the canonical root of the tree you're matching against
284 cwd - the current working directory, if relevant
286 cwd - the current working directory, if relevant
285 names - patterns to find
287 names - patterns to find
286 inc - patterns to include
288 inc - patterns to include
287 exc - patterns to exclude
289 exc - patterns to exclude
288 head - a regex to prepend to patterns to control whether a match is rooted
290 head - a regex to prepend to patterns to control whether a match is rooted
289
291
290 a pattern is one of:
292 a pattern is one of:
291 'glob:<rooted glob>'
293 'glob:<rooted glob>'
292 're:<rooted regexp>'
294 're:<rooted regexp>'
293 'path:<rooted path>'
295 'path:<rooted path>'
294 'relglob:<relative glob>'
296 'relglob:<relative glob>'
295 'relpath:<relative path>'
297 'relpath:<relative path>'
296 'relre:<relative regexp>'
298 'relre:<relative regexp>'
297 '<rooted path or regexp>'
299 '<rooted path or regexp>'
298
300
299 returns:
301 returns:
300 a 3-tuple containing
302 a 3-tuple containing
301 - list of explicit non-pattern names passed in
303 - list of explicit non-pattern names passed in
302 - a bool match(filename) function
304 - a bool match(filename) function
303 - a bool indicating if any patterns were passed in
305 - a bool indicating if any patterns were passed in
304
306
305 todo:
307 todo:
306 make head regex a rooted bool
308 make head regex a rooted bool
307 """
309 """
308
310
309 def contains_glob(name):
311 def contains_glob(name):
310 for c in name:
312 for c in name:
311 if c in _globchars: return True
313 if c in _globchars: return True
312 return False
314 return False
313
315
314 def regex(kind, name, tail):
316 def regex(kind, name, tail):
315 '''convert a pattern into a regular expression'''
317 '''convert a pattern into a regular expression'''
316 if kind == 're':
318 if kind == 're':
317 return name
319 return name
318 elif kind == 'path':
320 elif kind == 'path':
319 return '^' + re.escape(name) + '(?:/|$)'
321 return '^' + re.escape(name) + '(?:/|$)'
320 elif kind == 'relglob':
322 elif kind == 'relglob':
321 return head + globre(name, '(?:|.*/)', tail)
323 return head + globre(name, '(?:|.*/)', tail)
322 elif kind == 'relpath':
324 elif kind == 'relpath':
323 return head + re.escape(name) + tail
325 return head + re.escape(name) + tail
324 elif kind == 'relre':
326 elif kind == 'relre':
325 if name.startswith('^'):
327 if name.startswith('^'):
326 return name
328 return name
327 return '.*' + name
329 return '.*' + name
328 return head + globre(name, '', tail)
330 return head + globre(name, '', tail)
329
331
330 def matchfn(pats, tail):
332 def matchfn(pats, tail):
331 """build a matching function from a set of patterns"""
333 """build a matching function from a set of patterns"""
332 if not pats:
334 if not pats:
333 return
335 return
334 matches = []
336 matches = []
335 for k, p in pats:
337 for k, p in pats:
336 try:
338 try:
337 pat = '(?:%s)' % regex(k, p, tail)
339 pat = '(?:%s)' % regex(k, p, tail)
338 matches.append(re.compile(pat).match)
340 matches.append(re.compile(pat).match)
339 except re.error:
341 except re.error:
340 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
342 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
341 else: raise Abort("invalid pattern (%s): %s" % (k, p))
343 else: raise Abort("invalid pattern (%s): %s" % (k, p))
342
344
343 def buildfn(text):
345 def buildfn(text):
344 for m in matches:
346 for m in matches:
345 r = m(text)
347 r = m(text)
346 if r:
348 if r:
347 return r
349 return r
348
350
349 return buildfn
351 return buildfn
350
352
351 def globprefix(pat):
353 def globprefix(pat):
352 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
354 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
353 root = []
355 root = []
354 for p in pat.split(os.sep):
356 for p in pat.split(os.sep):
355 if contains_glob(p): break
357 if contains_glob(p): break
356 root.append(p)
358 root.append(p)
357 return '/'.join(root)
359 return '/'.join(root)
358
360
359 pats = []
361 pats = []
360 files = []
362 files = []
361 roots = []
363 roots = []
362 for kind, name in [patkind(p, dflt_pat) for p in names]:
364 for kind, name in [patkind(p, dflt_pat) for p in names]:
363 if kind in ('glob', 'relpath'):
365 if kind in ('glob', 'relpath'):
364 name = canonpath(canonroot, cwd, name)
366 name = canonpath(canonroot, cwd, name)
365 if name == '':
367 if name == '':
366 kind, name = 'glob', '**'
368 kind, name = 'glob', '**'
367 if kind in ('glob', 'path', 're'):
369 if kind in ('glob', 'path', 're'):
368 pats.append((kind, name))
370 pats.append((kind, name))
369 if kind == 'glob':
371 if kind == 'glob':
370 root = globprefix(name)
372 root = globprefix(name)
371 if root: roots.append(root)
373 if root: roots.append(root)
372 elif kind == 'relpath':
374 elif kind == 'relpath':
373 files.append((kind, name))
375 files.append((kind, name))
374 roots.append(name)
376 roots.append(name)
375
377
376 patmatch = matchfn(pats, '$') or always
378 patmatch = matchfn(pats, '$') or always
377 filematch = matchfn(files, '(?:/|$)') or always
379 filematch = matchfn(files, '(?:/|$)') or always
378 incmatch = always
380 incmatch = always
379 if inc:
381 if inc:
380 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
382 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
381 incmatch = matchfn(inckinds, '(?:/|$)')
383 incmatch = matchfn(inckinds, '(?:/|$)')
382 excmatch = lambda fn: False
384 excmatch = lambda fn: False
383 if exc:
385 if exc:
384 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
386 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
385 excmatch = matchfn(exckinds, '(?:/|$)')
387 excmatch = matchfn(exckinds, '(?:/|$)')
386
388
387 return (roots,
389 return (roots,
388 lambda fn: (incmatch(fn) and not excmatch(fn) and
390 lambda fn: (incmatch(fn) and not excmatch(fn) and
389 (fn.endswith('/') or
391 (fn.endswith('/') or
390 (not pats and not files) or
392 (not pats and not files) or
391 (pats and patmatch(fn)) or
393 (pats and patmatch(fn)) or
392 (files and filematch(fn)))),
394 (files and filematch(fn)))),
393 (inc or exc or (pats and pats != [('glob', '**')])) and True)
395 (inc or exc or (pats and pats != [('glob', '**')])) and True)
394
396
395 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
397 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
396 '''enhanced shell command execution.
398 '''enhanced shell command execution.
397 run with environment maybe modified, maybe in different dir.
399 run with environment maybe modified, maybe in different dir.
398
400
399 if command fails and onerr is None, return status. if ui object,
401 if command fails and onerr is None, return status. if ui object,
400 print error message and return status, else raise onerr object as
402 print error message and return status, else raise onerr object as
401 exception.'''
403 exception.'''
402 def py2shell(val):
404 def py2shell(val):
403 'convert python object into string that is useful to shell'
405 'convert python object into string that is useful to shell'
404 if val in (None, False):
406 if val in (None, False):
405 return '0'
407 return '0'
406 if val == True:
408 if val == True:
407 return '1'
409 return '1'
408 return str(val)
410 return str(val)
409 oldenv = {}
411 oldenv = {}
410 for k in environ:
412 for k in environ:
411 oldenv[k] = os.environ.get(k)
413 oldenv[k] = os.environ.get(k)
412 if cwd is not None:
414 if cwd is not None:
413 oldcwd = os.getcwd()
415 oldcwd = os.getcwd()
414 try:
416 try:
415 for k, v in environ.iteritems():
417 for k, v in environ.iteritems():
416 os.environ[k] = py2shell(v)
418 os.environ[k] = py2shell(v)
417 if cwd is not None and oldcwd != cwd:
419 if cwd is not None and oldcwd != cwd:
418 os.chdir(cwd)
420 os.chdir(cwd)
419 rc = os.system(cmd)
421 rc = os.system(cmd)
420 if rc and onerr:
422 if rc and onerr:
421 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
423 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
422 explain_exit(rc)[0])
424 explain_exit(rc)[0])
423 if errprefix:
425 if errprefix:
424 errmsg = '%s: %s' % (errprefix, errmsg)
426 errmsg = '%s: %s' % (errprefix, errmsg)
425 try:
427 try:
426 onerr.warn(errmsg + '\n')
428 onerr.warn(errmsg + '\n')
427 except AttributeError:
429 except AttributeError:
428 raise onerr(errmsg)
430 raise onerr(errmsg)
429 return rc
431 return rc
430 finally:
432 finally:
431 for k, v in oldenv.iteritems():
433 for k, v in oldenv.iteritems():
432 if v is None:
434 if v is None:
433 del os.environ[k]
435 del os.environ[k]
434 else:
436 else:
435 os.environ[k] = v
437 os.environ[k] = v
436 if cwd is not None and oldcwd != cwd:
438 if cwd is not None and oldcwd != cwd:
437 os.chdir(oldcwd)
439 os.chdir(oldcwd)
438
440
439 def rename(src, dst):
441 def rename(src, dst):
440 """forcibly rename a file"""
442 """forcibly rename a file"""
441 try:
443 try:
442 os.rename(src, dst)
444 os.rename(src, dst)
443 except OSError, err:
445 except OSError, err:
444 # on windows, rename to existing file is not allowed, so we
446 # on windows, rename to existing file is not allowed, so we
445 # must delete destination first. but if file is open, unlink
447 # must delete destination first. but if file is open, unlink
446 # schedules it for delete but does not delete it. rename
448 # schedules it for delete but does not delete it. rename
447 # happens immediately even for open files, so we create
449 # happens immediately even for open files, so we create
448 # temporary file, delete it, rename destination to that name,
450 # temporary file, delete it, rename destination to that name,
449 # then delete that. then rename is safe to do.
451 # then delete that. then rename is safe to do.
450 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
452 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
451 os.close(fd)
453 os.close(fd)
452 os.unlink(temp)
454 os.unlink(temp)
453 os.rename(dst, temp)
455 os.rename(dst, temp)
454 os.unlink(temp)
456 os.unlink(temp)
455 os.rename(src, dst)
457 os.rename(src, dst)
456
458
457 def unlink(f):
459 def unlink(f):
458 """unlink and remove the directory if it is empty"""
460 """unlink and remove the directory if it is empty"""
459 os.unlink(f)
461 os.unlink(f)
460 # try removing directories that might now be empty
462 # try removing directories that might now be empty
461 try:
463 try:
462 os.removedirs(os.path.dirname(f))
464 os.removedirs(os.path.dirname(f))
463 except OSError:
465 except OSError:
464 pass
466 pass
465
467
466 def copyfile(src, dest):
468 def copyfile(src, dest):
467 "copy a file, preserving mode"
469 "copy a file, preserving mode"
468 try:
470 try:
469 shutil.copyfile(src, dest)
471 shutil.copyfile(src, dest)
470 shutil.copymode(src, dest)
472 shutil.copymode(src, dest)
471 except shutil.Error, inst:
473 except shutil.Error, inst:
472 raise util.Abort(str(inst))
474 raise util.Abort(str(inst))
473
475
474 def copyfiles(src, dst, hardlink=None):
476 def copyfiles(src, dst, hardlink=None):
475 """Copy a directory tree using hardlinks if possible"""
477 """Copy a directory tree using hardlinks if possible"""
476
478
477 if hardlink is None:
479 if hardlink is None:
478 hardlink = (os.stat(src).st_dev ==
480 hardlink = (os.stat(src).st_dev ==
479 os.stat(os.path.dirname(dst)).st_dev)
481 os.stat(os.path.dirname(dst)).st_dev)
480
482
481 if os.path.isdir(src):
483 if os.path.isdir(src):
482 os.mkdir(dst)
484 os.mkdir(dst)
483 for name in os.listdir(src):
485 for name in os.listdir(src):
484 srcname = os.path.join(src, name)
486 srcname = os.path.join(src, name)
485 dstname = os.path.join(dst, name)
487 dstname = os.path.join(dst, name)
486 copyfiles(srcname, dstname, hardlink)
488 copyfiles(srcname, dstname, hardlink)
487 else:
489 else:
488 if hardlink:
490 if hardlink:
489 try:
491 try:
490 os_link(src, dst)
492 os_link(src, dst)
491 except (IOError, OSError):
493 except (IOError, OSError):
492 hardlink = False
494 hardlink = False
493 shutil.copy(src, dst)
495 shutil.copy(src, dst)
494 else:
496 else:
495 shutil.copy(src, dst)
497 shutil.copy(src, dst)
496
498
497 def audit_path(path):
499 def audit_path(path):
498 """Abort if path contains dangerous components"""
500 """Abort if path contains dangerous components"""
499 parts = os.path.normcase(path).split(os.sep)
501 parts = os.path.normcase(path).split(os.sep)
500 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
502 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
501 or os.pardir in parts):
503 or os.pardir in parts):
502 raise Abort(_("path contains illegal component: %s\n") % path)
504 raise Abort(_("path contains illegal component: %s\n") % path)
503
505
504 def _makelock_file(info, pathname):
506 def _makelock_file(info, pathname):
505 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
507 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
506 os.write(ld, info)
508 os.write(ld, info)
507 os.close(ld)
509 os.close(ld)
508
510
509 def _readlock_file(pathname):
511 def _readlock_file(pathname):
510 return posixfile(pathname).read()
512 return posixfile(pathname).read()
511
513
512 def nlinks(pathname):
514 def nlinks(pathname):
513 """Return number of hardlinks for the given file."""
515 """Return number of hardlinks for the given file."""
514 return os.lstat(pathname).st_nlink
516 return os.lstat(pathname).st_nlink
515
517
516 if hasattr(os, 'link'):
518 if hasattr(os, 'link'):
517 os_link = os.link
519 os_link = os.link
518 else:
520 else:
519 def os_link(src, dst):
521 def os_link(src, dst):
520 raise OSError(0, _("Hardlinks not supported"))
522 raise OSError(0, _("Hardlinks not supported"))
521
523
522 def fstat(fp):
524 def fstat(fp):
523 '''stat file object that may not have fileno method.'''
525 '''stat file object that may not have fileno method.'''
524 try:
526 try:
525 return os.fstat(fp.fileno())
527 return os.fstat(fp.fileno())
526 except AttributeError:
528 except AttributeError:
527 return os.stat(fp.name)
529 return os.stat(fp.name)
528
530
529 posixfile = file
531 posixfile = file
530
532
531 def is_win_9x():
533 def is_win_9x():
532 '''return true if run on windows 95, 98 or me.'''
534 '''return true if run on windows 95, 98 or me.'''
533 try:
535 try:
534 return sys.getwindowsversion()[3] == 1
536 return sys.getwindowsversion()[3] == 1
535 except AttributeError:
537 except AttributeError:
536 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
538 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
537
539
538 getuser_fallback = None
540 getuser_fallback = None
539
541
540 def getuser():
542 def getuser():
541 '''return name of current user'''
543 '''return name of current user'''
542 try:
544 try:
543 return getpass.getuser()
545 return getpass.getuser()
544 except ImportError:
546 except ImportError:
545 # import of pwd will fail on windows - try fallback
547 # import of pwd will fail on windows - try fallback
546 if getuser_fallback:
548 if getuser_fallback:
547 return getuser_fallback()
549 return getuser_fallback()
548 # raised if win32api not available
550 # raised if win32api not available
549 raise Abort(_('user name not available - set USERNAME '
551 raise Abort(_('user name not available - set USERNAME '
550 'environment variable'))
552 'environment variable'))
551
553
552 def username(uid=None):
554 def username(uid=None):
553 """Return the name of the user with the given uid.
555 """Return the name of the user with the given uid.
554
556
555 If uid is None, return the name of the current user."""
557 If uid is None, return the name of the current user."""
556 try:
558 try:
557 import pwd
559 import pwd
558 if uid is None:
560 if uid is None:
559 uid = os.getuid()
561 uid = os.getuid()
560 try:
562 try:
561 return pwd.getpwuid(uid)[0]
563 return pwd.getpwuid(uid)[0]
562 except KeyError:
564 except KeyError:
563 return str(uid)
565 return str(uid)
564 except ImportError:
566 except ImportError:
565 return None
567 return None
566
568
567 def groupname(gid=None):
569 def groupname(gid=None):
568 """Return the name of the group with the given gid.
570 """Return the name of the group with the given gid.
569
571
570 If gid is None, return the name of the current group."""
572 If gid is None, return the name of the current group."""
571 try:
573 try:
572 import grp
574 import grp
573 if gid is None:
575 if gid is None:
574 gid = os.getgid()
576 gid = os.getgid()
575 try:
577 try:
576 return grp.getgrgid(gid)[0]
578 return grp.getgrgid(gid)[0]
577 except KeyError:
579 except KeyError:
578 return str(gid)
580 return str(gid)
579 except ImportError:
581 except ImportError:
580 return None
582 return None
581
583
582 # Platform specific variants
584 # Platform specific variants
583 if os.name == 'nt':
585 if os.name == 'nt':
584 demandload(globals(), "msvcrt")
586 demandload(globals(), "msvcrt")
585 nulldev = 'NUL:'
587 nulldev = 'NUL:'
586
588
587 class winstdout:
589 class winstdout:
588 '''stdout on windows misbehaves if sent through a pipe'''
590 '''stdout on windows misbehaves if sent through a pipe'''
589
591
590 def __init__(self, fp):
592 def __init__(self, fp):
591 self.fp = fp
593 self.fp = fp
592
594
593 def __getattr__(self, key):
595 def __getattr__(self, key):
594 return getattr(self.fp, key)
596 return getattr(self.fp, key)
595
597
596 def close(self):
598 def close(self):
597 try:
599 try:
598 self.fp.close()
600 self.fp.close()
599 except: pass
601 except: pass
600
602
601 def write(self, s):
603 def write(self, s):
602 try:
604 try:
603 return self.fp.write(s)
605 return self.fp.write(s)
604 except IOError, inst:
606 except IOError, inst:
605 if inst.errno != 0: raise
607 if inst.errno != 0: raise
606 self.close()
608 self.close()
607 raise IOError(errno.EPIPE, 'Broken pipe')
609 raise IOError(errno.EPIPE, 'Broken pipe')
608
610
609 sys.stdout = winstdout(sys.stdout)
611 sys.stdout = winstdout(sys.stdout)
610
612
611 def system_rcpath():
613 def system_rcpath():
612 try:
614 try:
613 return system_rcpath_win32()
615 return system_rcpath_win32()
614 except:
616 except:
615 return [r'c:\mercurial\mercurial.ini']
617 return [r'c:\mercurial\mercurial.ini']
616
618
617 def os_rcpath():
619 def os_rcpath():
618 '''return default os-specific hgrc search path'''
620 '''return default os-specific hgrc search path'''
619 path = system_rcpath()
621 path = system_rcpath()
620 path.append(user_rcpath())
622 path.append(user_rcpath())
621 userprofile = os.environ.get('USERPROFILE')
623 userprofile = os.environ.get('USERPROFILE')
622 if userprofile:
624 if userprofile:
623 path.append(os.path.join(userprofile, 'mercurial.ini'))
625 path.append(os.path.join(userprofile, 'mercurial.ini'))
624 return path
626 return path
625
627
626 def user_rcpath():
628 def user_rcpath():
627 '''return os-specific hgrc search path to the user dir'''
629 '''return os-specific hgrc search path to the user dir'''
628 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
630 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
629
631
630 def parse_patch_output(output_line):
632 def parse_patch_output(output_line):
631 """parses the output produced by patch and returns the file name"""
633 """parses the output produced by patch and returns the file name"""
632 pf = output_line[14:]
634 pf = output_line[14:]
633 if pf[0] == '`':
635 if pf[0] == '`':
634 pf = pf[1:-1] # Remove the quotes
636 pf = pf[1:-1] # Remove the quotes
635 return pf
637 return pf
636
638
637 def testpid(pid):
639 def testpid(pid):
638 '''return False if pid dead, True if running or not known'''
640 '''return False if pid dead, True if running or not known'''
639 return True
641 return True
640
642
641 def is_exec(f, last):
643 def is_exec(f, last):
642 return last
644 return last
643
645
644 def set_exec(f, mode):
646 def set_exec(f, mode):
645 pass
647 pass
646
648
647 def set_binary(fd):
649 def set_binary(fd):
648 msvcrt.setmode(fd.fileno(), os.O_BINARY)
650 msvcrt.setmode(fd.fileno(), os.O_BINARY)
649
651
650 def pconvert(path):
652 def pconvert(path):
651 return path.replace("\\", "/")
653 return path.replace("\\", "/")
652
654
653 def localpath(path):
655 def localpath(path):
654 return path.replace('/', '\\')
656 return path.replace('/', '\\')
655
657
656 def normpath(path):
658 def normpath(path):
657 return pconvert(os.path.normpath(path))
659 return pconvert(os.path.normpath(path))
658
660
659 makelock = _makelock_file
661 makelock = _makelock_file
660 readlock = _readlock_file
662 readlock = _readlock_file
661
663
662 def samestat(s1, s2):
664 def samestat(s1, s2):
663 return False
665 return False
664
666
665 def shellquote(s):
667 def shellquote(s):
666 return '"%s"' % s.replace('"', '\\"')
668 return '"%s"' % s.replace('"', '\\"')
667
669
668 def explain_exit(code):
670 def explain_exit(code):
669 return _("exited with status %d") % code, code
671 return _("exited with status %d") % code, code
670
672
671 # if you change this stub into a real check, please try to implement the
673 # if you change this stub into a real check, please try to implement the
672 # username and groupname functions above, too.
674 # username and groupname functions above, too.
673 def isowner(fp, st=None):
675 def isowner(fp, st=None):
674 return True
676 return True
675
677
676 try:
678 try:
677 # override functions with win32 versions if possible
679 # override functions with win32 versions if possible
678 from util_win32 import *
680 from util_win32 import *
679 if not is_win_9x():
681 if not is_win_9x():
680 posixfile = posixfile_nt
682 posixfile = posixfile_nt
681 except ImportError:
683 except ImportError:
682 pass
684 pass
683
685
684 else:
686 else:
685 nulldev = '/dev/null'
687 nulldev = '/dev/null'
686
688
687 def rcfiles(path):
689 def rcfiles(path):
688 rcs = [os.path.join(path, 'hgrc')]
690 rcs = [os.path.join(path, 'hgrc')]
689 rcdir = os.path.join(path, 'hgrc.d')
691 rcdir = os.path.join(path, 'hgrc.d')
690 try:
692 try:
691 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
693 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
692 if f.endswith(".rc")])
694 if f.endswith(".rc")])
693 except OSError:
695 except OSError:
694 pass
696 pass
695 return rcs
697 return rcs
696
698
697 def os_rcpath():
699 def os_rcpath():
698 '''return default os-specific hgrc search path'''
700 '''return default os-specific hgrc search path'''
699 path = []
701 path = []
700 # old mod_python does not set sys.argv
702 # old mod_python does not set sys.argv
701 if len(getattr(sys, 'argv', [])) > 0:
703 if len(getattr(sys, 'argv', [])) > 0:
702 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
704 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
703 '/../etc/mercurial'))
705 '/../etc/mercurial'))
704 path.extend(rcfiles('/etc/mercurial'))
706 path.extend(rcfiles('/etc/mercurial'))
705 path.append(os.path.expanduser('~/.hgrc'))
707 path.append(os.path.expanduser('~/.hgrc'))
706 path = [os.path.normpath(f) for f in path]
708 path = [os.path.normpath(f) for f in path]
707 return path
709 return path
708
710
709 def parse_patch_output(output_line):
711 def parse_patch_output(output_line):
710 """parses the output produced by patch and returns the file name"""
712 """parses the output produced by patch and returns the file name"""
711 pf = output_line[14:]
713 pf = output_line[14:]
712 if pf.startswith("'") and pf.endswith("'") and " " in pf:
714 if pf.startswith("'") and pf.endswith("'") and " " in pf:
713 pf = pf[1:-1] # Remove the quotes
715 pf = pf[1:-1] # Remove the quotes
714 return pf
716 return pf
715
717
716 def is_exec(f, last):
718 def is_exec(f, last):
717 """check whether a file is executable"""
719 """check whether a file is executable"""
718 return (os.lstat(f).st_mode & 0100 != 0)
720 return (os.lstat(f).st_mode & 0100 != 0)
719
721
720 def set_exec(f, mode):
722 def set_exec(f, mode):
721 s = os.lstat(f).st_mode
723 s = os.lstat(f).st_mode
722 if (s & 0100 != 0) == mode:
724 if (s & 0100 != 0) == mode:
723 return
725 return
724 if mode:
726 if mode:
725 # Turn on +x for every +r bit when making a file executable
727 # Turn on +x for every +r bit when making a file executable
726 # and obey umask.
728 # and obey umask.
727 umask = os.umask(0)
729 umask = os.umask(0)
728 os.umask(umask)
730 os.umask(umask)
729 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
731 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
730 else:
732 else:
731 os.chmod(f, s & 0666)
733 os.chmod(f, s & 0666)
732
734
733 def set_binary(fd):
735 def set_binary(fd):
734 pass
736 pass
735
737
736 def pconvert(path):
738 def pconvert(path):
737 return path
739 return path
738
740
739 def localpath(path):
741 def localpath(path):
740 return path
742 return path
741
743
742 normpath = os.path.normpath
744 normpath = os.path.normpath
743 samestat = os.path.samestat
745 samestat = os.path.samestat
744
746
745 def makelock(info, pathname):
747 def makelock(info, pathname):
746 try:
748 try:
747 os.symlink(info, pathname)
749 os.symlink(info, pathname)
748 except OSError, why:
750 except OSError, why:
749 if why.errno == errno.EEXIST:
751 if why.errno == errno.EEXIST:
750 raise
752 raise
751 else:
753 else:
752 _makelock_file(info, pathname)
754 _makelock_file(info, pathname)
753
755
754 def readlock(pathname):
756 def readlock(pathname):
755 try:
757 try:
756 return os.readlink(pathname)
758 return os.readlink(pathname)
757 except OSError, why:
759 except OSError, why:
758 if why.errno == errno.EINVAL:
760 if why.errno == errno.EINVAL:
759 return _readlock_file(pathname)
761 return _readlock_file(pathname)
760 else:
762 else:
761 raise
763 raise
762
764
763 def shellquote(s):
765 def shellquote(s):
764 return "'%s'" % s.replace("'", "'\\''")
766 return "'%s'" % s.replace("'", "'\\''")
765
767
766 def testpid(pid):
768 def testpid(pid):
767 '''return False if pid dead, True if running or not sure'''
769 '''return False if pid dead, True if running or not sure'''
768 try:
770 try:
769 os.kill(pid, 0)
771 os.kill(pid, 0)
770 return True
772 return True
771 except OSError, inst:
773 except OSError, inst:
772 return inst.errno != errno.ESRCH
774 return inst.errno != errno.ESRCH
773
775
774 def explain_exit(code):
776 def explain_exit(code):
775 """return a 2-tuple (desc, code) describing a process's status"""
777 """return a 2-tuple (desc, code) describing a process's status"""
776 if os.WIFEXITED(code):
778 if os.WIFEXITED(code):
777 val = os.WEXITSTATUS(code)
779 val = os.WEXITSTATUS(code)
778 return _("exited with status %d") % val, val
780 return _("exited with status %d") % val, val
779 elif os.WIFSIGNALED(code):
781 elif os.WIFSIGNALED(code):
780 val = os.WTERMSIG(code)
782 val = os.WTERMSIG(code)
781 return _("killed by signal %d") % val, val
783 return _("killed by signal %d") % val, val
782 elif os.WIFSTOPPED(code):
784 elif os.WIFSTOPPED(code):
783 val = os.WSTOPSIG(code)
785 val = os.WSTOPSIG(code)
784 return _("stopped by signal %d") % val, val
786 return _("stopped by signal %d") % val, val
785 raise ValueError(_("invalid exit code"))
787 raise ValueError(_("invalid exit code"))
786
788
787 def isowner(fp, st=None):
789 def isowner(fp, st=None):
788 """Return True if the file object f belongs to the current user.
790 """Return True if the file object f belongs to the current user.
789
791
790 The return value of a util.fstat(f) may be passed as the st argument.
792 The return value of a util.fstat(f) may be passed as the st argument.
791 """
793 """
792 if st is None:
794 if st is None:
793 st = fstat(f)
795 st = fstat(f)
794 return st.st_uid == os.getuid()
796 return st.st_uid == os.getuid()
795
797
796
798
797 def opener(base, audit=True):
799 def opener(base, audit=True):
798 """
800 """
799 return a function that opens files relative to base
801 return a function that opens files relative to base
800
802
801 this function is used to hide the details of COW semantics and
803 this function is used to hide the details of COW semantics and
802 remote file access from higher level code.
804 remote file access from higher level code.
803 """
805 """
804 p = base
806 p = base
805 audit_p = audit
807 audit_p = audit
806
808
807 def mktempcopy(name):
809 def mktempcopy(name):
808 d, fn = os.path.split(name)
810 d, fn = os.path.split(name)
809 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
811 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
810 os.close(fd)
812 os.close(fd)
811 ofp = posixfile(temp, "wb")
813 ofp = posixfile(temp, "wb")
812 try:
814 try:
813 try:
815 try:
814 ifp = posixfile(name, "rb")
816 ifp = posixfile(name, "rb")
815 except IOError, inst:
817 except IOError, inst:
816 if not getattr(inst, 'filename', None):
818 if not getattr(inst, 'filename', None):
817 inst.filename = name
819 inst.filename = name
818 raise
820 raise
819 for chunk in filechunkiter(ifp):
821 for chunk in filechunkiter(ifp):
820 ofp.write(chunk)
822 ofp.write(chunk)
821 ifp.close()
823 ifp.close()
822 ofp.close()
824 ofp.close()
823 except:
825 except:
824 try: os.unlink(temp)
826 try: os.unlink(temp)
825 except: pass
827 except: pass
826 raise
828 raise
827 st = os.lstat(name)
829 st = os.lstat(name)
828 os.chmod(temp, st.st_mode)
830 os.chmod(temp, st.st_mode)
829 return temp
831 return temp
830
832
831 class atomictempfile(posixfile):
833 class atomictempfile(posixfile):
832 """the file will only be copied when rename is called"""
834 """the file will only be copied when rename is called"""
833 def __init__(self, name, mode):
835 def __init__(self, name, mode):
834 self.__name = name
836 self.__name = name
835 self.temp = mktempcopy(name)
837 self.temp = mktempcopy(name)
836 posixfile.__init__(self, self.temp, mode)
838 posixfile.__init__(self, self.temp, mode)
837 def rename(self):
839 def rename(self):
838 if not self.closed:
840 if not self.closed:
839 posixfile.close(self)
841 posixfile.close(self)
840 rename(self.temp, localpath(self.__name))
842 rename(self.temp, localpath(self.__name))
841 def __del__(self):
843 def __del__(self):
842 if not self.closed:
844 if not self.closed:
843 try:
845 try:
844 os.unlink(self.temp)
846 os.unlink(self.temp)
845 except: pass
847 except: pass
846 posixfile.close(self)
848 posixfile.close(self)
847
849
848 class atomicfile(atomictempfile):
850 class atomicfile(atomictempfile):
849 """the file will only be copied on close"""
851 """the file will only be copied on close"""
850 def __init__(self, name, mode):
852 def __init__(self, name, mode):
851 atomictempfile.__init__(self, name, mode)
853 atomictempfile.__init__(self, name, mode)
852 def close(self):
854 def close(self):
853 self.rename()
855 self.rename()
854 def __del__(self):
856 def __del__(self):
855 self.rename()
857 self.rename()
856
858
857 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
859 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
858 if audit_p:
860 if audit_p:
859 audit_path(path)
861 audit_path(path)
860 f = os.path.join(p, path)
862 f = os.path.join(p, path)
861
863
862 if not text:
864 if not text:
863 mode += "b" # for that other OS
865 mode += "b" # for that other OS
864
866
865 if mode[0] != "r":
867 if mode[0] != "r":
866 try:
868 try:
867 nlink = nlinks(f)
869 nlink = nlinks(f)
868 except OSError:
870 except OSError:
869 d = os.path.dirname(f)
871 d = os.path.dirname(f)
870 if not os.path.isdir(d):
872 if not os.path.isdir(d):
871 os.makedirs(d)
873 os.makedirs(d)
872 else:
874 else:
873 if atomic:
875 if atomic:
874 return atomicfile(f, mode)
876 return atomicfile(f, mode)
875 elif atomictemp:
877 elif atomictemp:
876 return atomictempfile(f, mode)
878 return atomictempfile(f, mode)
877 if nlink > 1:
879 if nlink > 1:
878 rename(mktempcopy(f), f)
880 rename(mktempcopy(f), f)
879 return posixfile(f, mode)
881 return posixfile(f, mode)
880
882
881 return o
883 return o
882
884
883 class chunkbuffer(object):
885 class chunkbuffer(object):
884 """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
885 iterator over chunks of arbitrary size."""
887 iterator over chunks of arbitrary size."""
886
888
887 def __init__(self, in_iter, targetsize = 2**16):
889 def __init__(self, in_iter, targetsize = 2**16):
888 """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.
889 targetsize is how big a buffer to try to maintain."""
891 targetsize is how big a buffer to try to maintain."""
890 self.in_iter = iter(in_iter)
892 self.in_iter = iter(in_iter)
891 self.buf = ''
893 self.buf = ''
892 self.targetsize = int(targetsize)
894 self.targetsize = int(targetsize)
893 if self.targetsize <= 0:
895 if self.targetsize <= 0:
894 raise ValueError(_("targetsize must be greater than 0, was %d") %
896 raise ValueError(_("targetsize must be greater than 0, was %d") %
895 targetsize)
897 targetsize)
896 self.iterempty = False
898 self.iterempty = False
897
899
898 def fillbuf(self):
900 def fillbuf(self):
899 """Ignore target size; read every chunk from iterator until empty."""
901 """Ignore target size; read every chunk from iterator until empty."""
900 if not self.iterempty:
902 if not self.iterempty:
901 collector = cStringIO.StringIO()
903 collector = cStringIO.StringIO()
902 collector.write(self.buf)
904 collector.write(self.buf)
903 for ch in self.in_iter:
905 for ch in self.in_iter:
904 collector.write(ch)
906 collector.write(ch)
905 self.buf = collector.getvalue()
907 self.buf = collector.getvalue()
906 self.iterempty = True
908 self.iterempty = True
907
909
908 def read(self, l):
910 def read(self, l):
909 """Read L bytes of data from the iterator of chunks of data.
911 """Read L bytes of data from the iterator of chunks of data.
910 Returns less than L bytes if the iterator runs dry."""
912 Returns less than L bytes if the iterator runs dry."""
911 if l > len(self.buf) and not self.iterempty:
913 if l > len(self.buf) and not self.iterempty:
912 # Clamp to a multiple of self.targetsize
914 # Clamp to a multiple of self.targetsize
913 targetsize = self.targetsize * ((l // self.targetsize) + 1)
915 targetsize = self.targetsize * ((l // self.targetsize) + 1)
914 collector = cStringIO.StringIO()
916 collector = cStringIO.StringIO()
915 collector.write(self.buf)
917 collector.write(self.buf)
916 collected = len(self.buf)
918 collected = len(self.buf)
917 for chunk in self.in_iter:
919 for chunk in self.in_iter:
918 collector.write(chunk)
920 collector.write(chunk)
919 collected += len(chunk)
921 collected += len(chunk)
920 if collected >= targetsize:
922 if collected >= targetsize:
921 break
923 break
922 if collected < targetsize:
924 if collected < targetsize:
923 self.iterempty = True
925 self.iterempty = True
924 self.buf = collector.getvalue()
926 self.buf = collector.getvalue()
925 s, self.buf = self.buf[:l], buffer(self.buf, l)
927 s, self.buf = self.buf[:l], buffer(self.buf, l)
926 return s
928 return s
927
929
928 def filechunkiter(f, size=65536, limit=None):
930 def filechunkiter(f, size=65536, limit=None):
929 """Create a generator that produces the data in the file size
931 """Create a generator that produces the data in the file size
930 (default 65536) bytes at a time, up to optional limit (default is
932 (default 65536) bytes at a time, up to optional limit (default is
931 to read all data). Chunks may be less than size bytes if the
933 to read all data). Chunks may be less than size bytes if the
932 chunk is the last chunk in the file, or the file is a socket or
934 chunk is the last chunk in the file, or the file is a socket or
933 some other type of file that sometimes reads less data than is
935 some other type of file that sometimes reads less data than is
934 requested."""
936 requested."""
935 assert size >= 0
937 assert size >= 0
936 assert limit is None or limit >= 0
938 assert limit is None or limit >= 0
937 while True:
939 while True:
938 if limit is None: nbytes = size
940 if limit is None: nbytes = size
939 else: nbytes = min(limit, size)
941 else: nbytes = min(limit, size)
940 s = nbytes and f.read(nbytes)
942 s = nbytes and f.read(nbytes)
941 if not s: break
943 if not s: break
942 if limit: limit -= len(s)
944 if limit: limit -= len(s)
943 yield s
945 yield s
944
946
945 def makedate():
947 def makedate():
946 lt = time.localtime()
948 lt = time.localtime()
947 if lt[8] == 1 and time.daylight:
949 if lt[8] == 1 and time.daylight:
948 tz = time.altzone
950 tz = time.altzone
949 else:
951 else:
950 tz = time.timezone
952 tz = time.timezone
951 return time.mktime(lt), tz
953 return time.mktime(lt), tz
952
954
953 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
955 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
954 """represent a (unixtime, offset) tuple as a localized time.
956 """represent a (unixtime, offset) tuple as a localized time.
955 unixtime is seconds since the epoch, and offset is the time zone's
957 unixtime is seconds since the epoch, and offset is the time zone's
956 number of seconds away from UTC. if timezone is false, do not
958 number of seconds away from UTC. if timezone is false, do not
957 append time zone to string."""
959 append time zone to string."""
958 t, tz = date or makedate()
960 t, tz = date or makedate()
959 s = time.strftime(format, time.gmtime(float(t) - tz))
961 s = time.strftime(format, time.gmtime(float(t) - tz))
960 if timezone:
962 if timezone:
961 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
963 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
962 return s
964 return s
963
965
964 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
966 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
965 """parse a localized time string and return a (unixtime, offset) tuple.
967 """parse a localized time string and return a (unixtime, offset) tuple.
966 if the string cannot be parsed, ValueError is raised."""
968 if the string cannot be parsed, ValueError is raised."""
967 def hastimezone(string):
969 def hastimezone(string):
968 return (string[-4:].isdigit() and
970 return (string[-4:].isdigit() and
969 (string[-5] == '+' or string[-5] == '-') and
971 (string[-5] == '+' or string[-5] == '-') and
970 string[-6].isspace())
972 string[-6].isspace())
971
973
972 # NOTE: unixtime = localunixtime + offset
974 # NOTE: unixtime = localunixtime + offset
973 if hastimezone(string):
975 if hastimezone(string):
974 date, tz = string[:-6], string[-5:]
976 date, tz = string[:-6], string[-5:]
975 tz = int(tz)
977 tz = int(tz)
976 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
978 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
977 else:
979 else:
978 date, offset = string, None
980 date, offset = string, None
979 timetuple = time.strptime(date, format)
981 timetuple = time.strptime(date, format)
980 localunixtime = int(calendar.timegm(timetuple))
982 localunixtime = int(calendar.timegm(timetuple))
981 if offset is None:
983 if offset is None:
982 # local timezone
984 # local timezone
983 unixtime = int(time.mktime(timetuple))
985 unixtime = int(time.mktime(timetuple))
984 offset = unixtime - localunixtime
986 offset = unixtime - localunixtime
985 else:
987 else:
986 unixtime = localunixtime + offset
988 unixtime = localunixtime + offset
987 return unixtime, offset
989 return unixtime, offset
988
990
989 def parsedate(string, formats=None):
991 def parsedate(string, formats=None):
990 """parse a localized time string and return a (unixtime, offset) tuple.
992 """parse a localized time string and return a (unixtime, offset) tuple.
991 The date may be a "unixtime offset" string or in one of the specified
993 The date may be a "unixtime offset" string or in one of the specified
992 formats."""
994 formats."""
993 if not formats:
995 if not formats:
994 formats = defaultdateformats
996 formats = defaultdateformats
995 try:
997 try:
996 when, offset = map(int, string.split(' '))
998 when, offset = map(int, string.split(' '))
997 except ValueError:
999 except ValueError:
998 for format in formats:
1000 for format in formats:
999 try:
1001 try:
1000 when, offset = strdate(string, format)
1002 when, offset = strdate(string, format)
1001 except ValueError:
1003 except ValueError:
1002 pass
1004 pass
1003 else:
1005 else:
1004 break
1006 break
1005 else:
1007 else:
1006 raise ValueError(_('invalid date: %r '
1008 raise ValueError(_('invalid date: %r '
1007 'see hg(1) manual page for details')
1009 'see hg(1) manual page for details')
1008 % string)
1010 % string)
1009 # validate explicit (probably user-specified) date and
1011 # validate explicit (probably user-specified) date and
1010 # time zone offset. values must fit in signed 32 bits for
1012 # time zone offset. values must fit in signed 32 bits for
1011 # current 32-bit linux runtimes. timezones go from UTC-12
1013 # current 32-bit linux runtimes. timezones go from UTC-12
1012 # to UTC+14
1014 # to UTC+14
1013 if abs(when) > 0x7fffffff:
1015 if abs(when) > 0x7fffffff:
1014 raise ValueError(_('date exceeds 32 bits: %d') % when)
1016 raise ValueError(_('date exceeds 32 bits: %d') % when)
1015 if offset < -50400 or offset > 43200:
1017 if offset < -50400 or offset > 43200:
1016 raise ValueError(_('impossible time zone offset: %d') % offset)
1018 raise ValueError(_('impossible time zone offset: %d') % offset)
1017 return when, offset
1019 return when, offset
1018
1020
1019 def shortuser(user):
1021 def shortuser(user):
1020 """Return a short representation of a user name or email address."""
1022 """Return a short representation of a user name or email address."""
1021 f = user.find('@')
1023 f = user.find('@')
1022 if f >= 0:
1024 if f >= 0:
1023 user = user[:f]
1025 user = user[:f]
1024 f = user.find('<')
1026 f = user.find('<')
1025 if f >= 0:
1027 if f >= 0:
1026 user = user[f+1:]
1028 user = user[f+1:]
1027 f = user.find(' ')
1029 f = user.find(' ')
1028 if f >= 0:
1030 if f >= 0:
1029 user = user[:f]
1031 user = user[:f]
1030 f = user.find('.')
1032 f = user.find('.')
1031 if f >= 0:
1033 if f >= 0:
1032 user = user[:f]
1034 user = user[:f]
1033 return user
1035 return user
1034
1036
1035 def ellipsis(text, maxlength=400):
1037 def ellipsis(text, maxlength=400):
1036 """Trim string to at most maxlength (default: 400) characters."""
1038 """Trim string to at most maxlength (default: 400) characters."""
1037 if len(text) <= maxlength:
1039 if len(text) <= maxlength:
1038 return text
1040 return text
1039 else:
1041 else:
1040 return "%s..." % (text[:maxlength-3])
1042 return "%s..." % (text[:maxlength-3])
1041
1043
1042 def walkrepos(path):
1044 def walkrepos(path):
1043 '''yield every hg repository under path, recursively.'''
1045 '''yield every hg repository under path, recursively.'''
1044 def errhandler(err):
1046 def errhandler(err):
1045 if err.filename == path:
1047 if err.filename == path:
1046 raise err
1048 raise err
1047
1049
1048 for root, dirs, files in os.walk(path, onerror=errhandler):
1050 for root, dirs, files in os.walk(path, onerror=errhandler):
1049 for d in dirs:
1051 for d in dirs:
1050 if d == '.hg':
1052 if d == '.hg':
1051 yield root
1053 yield root
1052 dirs[:] = []
1054 dirs[:] = []
1053 break
1055 break
1054
1056
1055 _rcpath = None
1057 _rcpath = None
1056
1058
1057 def rcpath():
1059 def rcpath():
1058 '''return hgrc search path. if env var HGRCPATH is set, use it.
1060 '''return hgrc search path. if env var HGRCPATH is set, use it.
1059 for each item in path, if directory, use files ending in .rc,
1061 for each item in path, if directory, use files ending in .rc,
1060 else use item.
1062 else use item.
1061 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1063 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1062 if no HGRCPATH, use default os-specific path.'''
1064 if no HGRCPATH, use default os-specific path.'''
1063 global _rcpath
1065 global _rcpath
1064 if _rcpath is None:
1066 if _rcpath is None:
1065 if 'HGRCPATH' in os.environ:
1067 if 'HGRCPATH' in os.environ:
1066 _rcpath = []
1068 _rcpath = []
1067 for p in os.environ['HGRCPATH'].split(os.pathsep):
1069 for p in os.environ['HGRCPATH'].split(os.pathsep):
1068 if not p: continue
1070 if not p: continue
1069 if os.path.isdir(p):
1071 if os.path.isdir(p):
1070 for f in os.listdir(p):
1072 for f in os.listdir(p):
1071 if f.endswith('.rc'):
1073 if f.endswith('.rc'):
1072 _rcpath.append(os.path.join(p, f))
1074 _rcpath.append(os.path.join(p, f))
1073 else:
1075 else:
1074 _rcpath.append(p)
1076 _rcpath.append(p)
1075 else:
1077 else:
1076 _rcpath = os_rcpath()
1078 _rcpath = os_rcpath()
1077 return _rcpath
1079 return _rcpath
1078
1080
1079 def bytecount(nbytes):
1081 def bytecount(nbytes):
1080 '''return byte count formatted as readable string, with units'''
1082 '''return byte count formatted as readable string, with units'''
1081
1083
1082 units = (
1084 units = (
1083 (100, 1<<30, _('%.0f GB')),
1085 (100, 1<<30, _('%.0f GB')),
1084 (10, 1<<30, _('%.1f GB')),
1086 (10, 1<<30, _('%.1f GB')),
1085 (1, 1<<30, _('%.2f GB')),
1087 (1, 1<<30, _('%.2f GB')),
1086 (100, 1<<20, _('%.0f MB')),
1088 (100, 1<<20, _('%.0f MB')),
1087 (10, 1<<20, _('%.1f MB')),
1089 (10, 1<<20, _('%.1f MB')),
1088 (1, 1<<20, _('%.2f MB')),
1090 (1, 1<<20, _('%.2f MB')),
1089 (100, 1<<10, _('%.0f KB')),
1091 (100, 1<<10, _('%.0f KB')),
1090 (10, 1<<10, _('%.1f KB')),
1092 (10, 1<<10, _('%.1f KB')),
1091 (1, 1<<10, _('%.2f KB')),
1093 (1, 1<<10, _('%.2f KB')),
1092 (1, 1, _('%.0f bytes')),
1094 (1, 1, _('%.0f bytes')),
1093 )
1095 )
1094
1096
1095 for multiplier, divisor, format in units:
1097 for multiplier, divisor, format in units:
1096 if nbytes >= divisor * multiplier:
1098 if nbytes >= divisor * multiplier:
1097 return format % (nbytes / float(divisor))
1099 return format % (nbytes / float(divisor))
1098 return units[-1][2] % nbytes
1100 return units[-1][2] % nbytes
1099
1101
1100 def drop_scheme(scheme, path):
1102 def drop_scheme(scheme, path):
1101 sc = scheme + ':'
1103 sc = scheme + ':'
1102 if path.startswith(sc):
1104 if path.startswith(sc):
1103 path = path[len(sc):]
1105 path = path[len(sc):]
1104 if path.startswith('//'):
1106 if path.startswith('//'):
1105 path = path[2:]
1107 path = path[2:]
1106 return path
1108 return path
General Comments 0
You need to be logged in to leave comments. Login now