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