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