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