##// END OF EJS Templates
Merge with crew
Lee Cantey -
r4627:d4e4d0f4 merge default
parent child Browse files
Show More
@@ -1,1531 +1,1531 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 _
15 from i18n import _
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
17 import os, threading, time, calendar, ConfigParser, locale, glob
17 import os, threading, time, calendar, ConfigParser, locale, glob
18
18
19 try:
19 try:
20 _encoding = os.environ.get("HGENCODING")
20 _encoding = os.environ.get("HGENCODING")
21 if sys.platform == 'darwin' and not _encoding:
21 if sys.platform == 'darwin' and not _encoding:
22 # On darwin, getpreferredencoding ignores the locale environment and
22 # On darwin, getpreferredencoding ignores the locale environment and
23 # always returns mac-roman. We override this if the environment is
23 # always returns mac-roman. We override this if the environment is
24 # not C (has been customized by the user).
24 # not C (has been customized by the user).
25 locale.setlocale(locale.LC_CTYPE, '')
25 locale.setlocale(locale.LC_CTYPE, '')
26 _encoding = locale.getlocale()[1]
26 _encoding = locale.getlocale()[1]
27 if not _encoding:
27 if not _encoding:
28 _encoding = locale.getpreferredencoding() or 'ascii'
28 _encoding = locale.getpreferredencoding() or 'ascii'
29 except locale.Error:
29 except locale.Error:
30 _encoding = 'ascii'
30 _encoding = 'ascii'
31 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
31 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
32 _fallbackencoding = 'ISO-8859-1'
32 _fallbackencoding = 'ISO-8859-1'
33
33
34 def tolocal(s):
34 def tolocal(s):
35 """
35 """
36 Convert a string from internal UTF-8 to local encoding
36 Convert a string from internal UTF-8 to local encoding
37
37
38 All internal strings should be UTF-8 but some repos before the
38 All internal strings should be UTF-8 but some repos before the
39 implementation of locale support may contain latin1 or possibly
39 implementation of locale support may contain latin1 or possibly
40 other character sets. We attempt to decode everything strictly
40 other character sets. We attempt to decode everything strictly
41 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
41 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
42 replace unknown characters.
42 replace unknown characters.
43 """
43 """
44 for e in ('UTF-8', _fallbackencoding):
44 for e in ('UTF-8', _fallbackencoding):
45 try:
45 try:
46 u = s.decode(e) # attempt strict decoding
46 u = s.decode(e) # attempt strict decoding
47 return u.encode(_encoding, "replace")
47 return u.encode(_encoding, "replace")
48 except LookupError, k:
48 except LookupError, k:
49 raise Abort(_("%s, please check your locale settings") % k)
49 raise Abort(_("%s, please check your locale settings") % k)
50 except UnicodeDecodeError:
50 except UnicodeDecodeError:
51 pass
51 pass
52 u = s.decode("utf-8", "replace") # last ditch
52 u = s.decode("utf-8", "replace") # last ditch
53 return u.encode(_encoding, "replace")
53 return u.encode(_encoding, "replace")
54
54
55 def fromlocal(s):
55 def fromlocal(s):
56 """
56 """
57 Convert a string from the local character encoding to UTF-8
57 Convert a string from the local character encoding to UTF-8
58
58
59 We attempt to decode strings using the encoding mode set by
59 We attempt to decode strings using the encoding mode set by
60 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
60 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
61 characters will cause an error message. Other modes include
61 characters will cause an error message. Other modes include
62 'replace', which replaces unknown characters with a special
62 'replace', which replaces unknown characters with a special
63 Unicode character, and 'ignore', which drops the character.
63 Unicode character, and 'ignore', which drops the character.
64 """
64 """
65 try:
65 try:
66 return s.decode(_encoding, _encodingmode).encode("utf-8")
66 return s.decode(_encoding, _encodingmode).encode("utf-8")
67 except UnicodeDecodeError, inst:
67 except UnicodeDecodeError, inst:
68 sub = s[max(0, inst.start-10):inst.start+10]
68 sub = s[max(0, inst.start-10):inst.start+10]
69 raise Abort("decoding near '%s': %s!" % (sub, inst))
69 raise Abort("decoding near '%s': %s!" % (sub, inst))
70 except LookupError, k:
70 except LookupError, k:
71 raise Abort(_("%s, please check your locale settings") % k)
71 raise Abort(_("%s, please check your locale settings") % k)
72
72
73 def locallen(s):
73 def locallen(s):
74 """Find the length in characters of a local string"""
74 """Find the length in characters of a local string"""
75 return len(s.decode(_encoding, "replace"))
75 return len(s.decode(_encoding, "replace"))
76
76
77 def localsub(s, a, b=None):
77 def localsub(s, a, b=None):
78 try:
78 try:
79 u = s.decode(_encoding, _encodingmode)
79 u = s.decode(_encoding, _encodingmode)
80 if b is not None:
80 if b is not None:
81 u = u[a:b]
81 u = u[a:b]
82 else:
82 else:
83 u = u[:a]
83 u = u[:a]
84 return u.encode(_encoding, _encodingmode)
84 return u.encode(_encoding, _encodingmode)
85 except UnicodeDecodeError, inst:
85 except UnicodeDecodeError, inst:
86 sub = s[max(0, inst.start-10), inst.start+10]
86 sub = s[max(0, inst.start-10), inst.start+10]
87 raise Abort(_("decoding near '%s': %s!") % (sub, inst))
87 raise Abort(_("decoding near '%s': %s!") % (sub, inst))
88
88
89 # used by parsedate
89 # used by parsedate
90 defaultdateformats = (
90 defaultdateformats = (
91 '%Y-%m-%d %H:%M:%S',
91 '%Y-%m-%d %H:%M:%S',
92 '%Y-%m-%d %I:%M:%S%p',
92 '%Y-%m-%d %I:%M:%S%p',
93 '%Y-%m-%d %H:%M',
93 '%Y-%m-%d %H:%M',
94 '%Y-%m-%d %I:%M%p',
94 '%Y-%m-%d %I:%M%p',
95 '%Y-%m-%d',
95 '%Y-%m-%d',
96 '%m-%d',
96 '%m-%d',
97 '%m/%d',
97 '%m/%d',
98 '%m/%d/%y',
98 '%m/%d/%y',
99 '%m/%d/%Y',
99 '%m/%d/%Y',
100 '%a %b %d %H:%M:%S %Y',
100 '%a %b %d %H:%M:%S %Y',
101 '%a %b %d %I:%M:%S%p %Y',
101 '%a %b %d %I:%M:%S%p %Y',
102 '%b %d %H:%M:%S %Y',
102 '%b %d %H:%M:%S %Y',
103 '%b %d %I:%M:%S%p %Y',
103 '%b %d %I:%M:%S%p %Y',
104 '%b %d %H:%M:%S',
104 '%b %d %H:%M:%S',
105 '%b %d %I:%M:%S%p',
105 '%b %d %I:%M:%S%p',
106 '%b %d %H:%M',
106 '%b %d %H:%M',
107 '%b %d %I:%M%p',
107 '%b %d %I:%M%p',
108 '%b %d %Y',
108 '%b %d %Y',
109 '%b %d',
109 '%b %d',
110 '%H:%M:%S',
110 '%H:%M:%S',
111 '%I:%M:%SP',
111 '%I:%M:%SP',
112 '%H:%M',
112 '%H:%M',
113 '%I:%M%p',
113 '%I:%M%p',
114 )
114 )
115
115
116 extendeddateformats = defaultdateformats + (
116 extendeddateformats = defaultdateformats + (
117 "%Y",
117 "%Y",
118 "%Y-%m",
118 "%Y-%m",
119 "%b",
119 "%b",
120 "%b %Y",
120 "%b %Y",
121 )
121 )
122
122
123 class SignalInterrupt(Exception):
123 class SignalInterrupt(Exception):
124 """Exception raised on SIGTERM and SIGHUP."""
124 """Exception raised on SIGTERM and SIGHUP."""
125
125
126 # differences from SafeConfigParser:
126 # differences from SafeConfigParser:
127 # - case-sensitive keys
127 # - case-sensitive keys
128 # - allows values that are not strings (this means that you may not
128 # - allows values that are not strings (this means that you may not
129 # be able to save the configuration to a file)
129 # be able to save the configuration to a file)
130 class configparser(ConfigParser.SafeConfigParser):
130 class configparser(ConfigParser.SafeConfigParser):
131 def optionxform(self, optionstr):
131 def optionxform(self, optionstr):
132 return optionstr
132 return optionstr
133
133
134 def set(self, section, option, value):
134 def set(self, section, option, value):
135 return ConfigParser.ConfigParser.set(self, section, option, value)
135 return ConfigParser.ConfigParser.set(self, section, option, value)
136
136
137 def _interpolate(self, section, option, rawval, vars):
137 def _interpolate(self, section, option, rawval, vars):
138 if not isinstance(rawval, basestring):
138 if not isinstance(rawval, basestring):
139 return rawval
139 return rawval
140 return ConfigParser.SafeConfigParser._interpolate(self, section,
140 return ConfigParser.SafeConfigParser._interpolate(self, section,
141 option, rawval, vars)
141 option, rawval, vars)
142
142
143 def cachefunc(func):
143 def cachefunc(func):
144 '''cache the result of function calls'''
144 '''cache the result of function calls'''
145 # XXX doesn't handle keywords args
145 # XXX doesn't handle keywords args
146 cache = {}
146 cache = {}
147 if func.func_code.co_argcount == 1:
147 if func.func_code.co_argcount == 1:
148 # we gain a small amount of time because
148 # we gain a small amount of time because
149 # we don't need to pack/unpack the list
149 # we don't need to pack/unpack the list
150 def f(arg):
150 def f(arg):
151 if arg not in cache:
151 if arg not in cache:
152 cache[arg] = func(arg)
152 cache[arg] = func(arg)
153 return cache[arg]
153 return cache[arg]
154 else:
154 else:
155 def f(*args):
155 def f(*args):
156 if args not in cache:
156 if args not in cache:
157 cache[args] = func(*args)
157 cache[args] = func(*args)
158 return cache[args]
158 return cache[args]
159
159
160 return f
160 return f
161
161
162 def pipefilter(s, cmd):
162 def pipefilter(s, cmd):
163 '''filter string S through command CMD, returning its output'''
163 '''filter string S through command CMD, returning its output'''
164 (pout, pin) = popen2.popen2(cmd, -1, 'b')
164 (pin, pout) = os.popen2(cmd, 'b')
165 def writer():
165 def writer():
166 try:
166 try:
167 pin.write(s)
167 pin.write(s)
168 pin.close()
168 pin.close()
169 except IOError, inst:
169 except IOError, inst:
170 if inst.errno != errno.EPIPE:
170 if inst.errno != errno.EPIPE:
171 raise
171 raise
172
172
173 # we should use select instead on UNIX, but this will work on most
173 # we should use select instead on UNIX, but this will work on most
174 # systems, including Windows
174 # systems, including Windows
175 w = threading.Thread(target=writer)
175 w = threading.Thread(target=writer)
176 w.start()
176 w.start()
177 f = pout.read()
177 f = pout.read()
178 pout.close()
178 pout.close()
179 w.join()
179 w.join()
180 return f
180 return f
181
181
182 def tempfilter(s, cmd):
182 def tempfilter(s, cmd):
183 '''filter string S through a pair of temporary files with CMD.
183 '''filter string S through a pair of temporary files with CMD.
184 CMD is used as a template to create the real command to be run,
184 CMD is used as a template to create the real command to be run,
185 with the strings INFILE and OUTFILE replaced by the real names of
185 with the strings INFILE and OUTFILE replaced by the real names of
186 the temporary files generated.'''
186 the temporary files generated.'''
187 inname, outname = None, None
187 inname, outname = None, None
188 try:
188 try:
189 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
189 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
190 fp = os.fdopen(infd, 'wb')
190 fp = os.fdopen(infd, 'wb')
191 fp.write(s)
191 fp.write(s)
192 fp.close()
192 fp.close()
193 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
193 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
194 os.close(outfd)
194 os.close(outfd)
195 cmd = cmd.replace('INFILE', inname)
195 cmd = cmd.replace('INFILE', inname)
196 cmd = cmd.replace('OUTFILE', outname)
196 cmd = cmd.replace('OUTFILE', outname)
197 code = os.system(cmd)
197 code = os.system(cmd)
198 if code: raise Abort(_("command '%s' failed: %s") %
198 if code: raise Abort(_("command '%s' failed: %s") %
199 (cmd, explain_exit(code)))
199 (cmd, explain_exit(code)))
200 return open(outname, 'rb').read()
200 return open(outname, 'rb').read()
201 finally:
201 finally:
202 try:
202 try:
203 if inname: os.unlink(inname)
203 if inname: os.unlink(inname)
204 except: pass
204 except: pass
205 try:
205 try:
206 if outname: os.unlink(outname)
206 if outname: os.unlink(outname)
207 except: pass
207 except: pass
208
208
209 filtertable = {
209 filtertable = {
210 'tempfile:': tempfilter,
210 'tempfile:': tempfilter,
211 'pipe:': pipefilter,
211 'pipe:': pipefilter,
212 }
212 }
213
213
214 def filter(s, cmd):
214 def filter(s, cmd):
215 "filter a string through a command that transforms its input to its output"
215 "filter a string through a command that transforms its input to its output"
216 for name, fn in filtertable.iteritems():
216 for name, fn in filtertable.iteritems():
217 if cmd.startswith(name):
217 if cmd.startswith(name):
218 return fn(s, cmd[len(name):].lstrip())
218 return fn(s, cmd[len(name):].lstrip())
219 return pipefilter(s, cmd)
219 return pipefilter(s, cmd)
220
220
221 def binary(s):
221 def binary(s):
222 """return true if a string is binary data using diff's heuristic"""
222 """return true if a string is binary data using diff's heuristic"""
223 if s and '\0' in s[:4096]:
223 if s and '\0' in s[:4096]:
224 return True
224 return True
225 return False
225 return False
226
226
227 def unique(g):
227 def unique(g):
228 """return the uniq elements of iterable g"""
228 """return the uniq elements of iterable g"""
229 seen = {}
229 seen = {}
230 l = []
230 l = []
231 for f in g:
231 for f in g:
232 if f not in seen:
232 if f not in seen:
233 seen[f] = 1
233 seen[f] = 1
234 l.append(f)
234 l.append(f)
235 return l
235 return l
236
236
237 class Abort(Exception):
237 class Abort(Exception):
238 """Raised if a command needs to print an error and exit."""
238 """Raised if a command needs to print an error and exit."""
239
239
240 class UnexpectedOutput(Abort):
240 class UnexpectedOutput(Abort):
241 """Raised to print an error with part of output and exit."""
241 """Raised to print an error with part of output and exit."""
242
242
243 def always(fn): return True
243 def always(fn): return True
244 def never(fn): return False
244 def never(fn): return False
245
245
246 def expand_glob(pats):
246 def expand_glob(pats):
247 '''On Windows, expand the implicit globs in a list of patterns'''
247 '''On Windows, expand the implicit globs in a list of patterns'''
248 if os.name != 'nt':
248 if os.name != 'nt':
249 return list(pats)
249 return list(pats)
250 ret = []
250 ret = []
251 for p in pats:
251 for p in pats:
252 kind, name = patkind(p, None)
252 kind, name = patkind(p, None)
253 if kind is None:
253 if kind is None:
254 globbed = glob.glob(name)
254 globbed = glob.glob(name)
255 if globbed:
255 if globbed:
256 ret.extend(globbed)
256 ret.extend(globbed)
257 continue
257 continue
258 # if we couldn't expand the glob, just keep it around
258 # if we couldn't expand the glob, just keep it around
259 ret.append(p)
259 ret.append(p)
260 return ret
260 return ret
261
261
262 def patkind(name, dflt_pat='glob'):
262 def patkind(name, dflt_pat='glob'):
263 """Split a string into an optional pattern kind prefix and the
263 """Split a string into an optional pattern kind prefix and the
264 actual pattern."""
264 actual pattern."""
265 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
265 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
266 if name.startswith(prefix + ':'): return name.split(':', 1)
266 if name.startswith(prefix + ':'): return name.split(':', 1)
267 return dflt_pat, name
267 return dflt_pat, name
268
268
269 def globre(pat, head='^', tail='$'):
269 def globre(pat, head='^', tail='$'):
270 "convert a glob pattern into a regexp"
270 "convert a glob pattern into a regexp"
271 i, n = 0, len(pat)
271 i, n = 0, len(pat)
272 res = ''
272 res = ''
273 group = False
273 group = False
274 def peek(): return i < n and pat[i]
274 def peek(): return i < n and pat[i]
275 while i < n:
275 while i < n:
276 c = pat[i]
276 c = pat[i]
277 i = i+1
277 i = i+1
278 if c == '*':
278 if c == '*':
279 if peek() == '*':
279 if peek() == '*':
280 i += 1
280 i += 1
281 res += '.*'
281 res += '.*'
282 else:
282 else:
283 res += '[^/]*'
283 res += '[^/]*'
284 elif c == '?':
284 elif c == '?':
285 res += '.'
285 res += '.'
286 elif c == '[':
286 elif c == '[':
287 j = i
287 j = i
288 if j < n and pat[j] in '!]':
288 if j < n and pat[j] in '!]':
289 j += 1
289 j += 1
290 while j < n and pat[j] != ']':
290 while j < n and pat[j] != ']':
291 j += 1
291 j += 1
292 if j >= n:
292 if j >= n:
293 res += '\\['
293 res += '\\['
294 else:
294 else:
295 stuff = pat[i:j].replace('\\','\\\\')
295 stuff = pat[i:j].replace('\\','\\\\')
296 i = j + 1
296 i = j + 1
297 if stuff[0] == '!':
297 if stuff[0] == '!':
298 stuff = '^' + stuff[1:]
298 stuff = '^' + stuff[1:]
299 elif stuff[0] == '^':
299 elif stuff[0] == '^':
300 stuff = '\\' + stuff
300 stuff = '\\' + stuff
301 res = '%s[%s]' % (res, stuff)
301 res = '%s[%s]' % (res, stuff)
302 elif c == '{':
302 elif c == '{':
303 group = True
303 group = True
304 res += '(?:'
304 res += '(?:'
305 elif c == '}' and group:
305 elif c == '}' and group:
306 res += ')'
306 res += ')'
307 group = False
307 group = False
308 elif c == ',' and group:
308 elif c == ',' and group:
309 res += '|'
309 res += '|'
310 elif c == '\\':
310 elif c == '\\':
311 p = peek()
311 p = peek()
312 if p:
312 if p:
313 i += 1
313 i += 1
314 res += re.escape(p)
314 res += re.escape(p)
315 else:
315 else:
316 res += re.escape(c)
316 res += re.escape(c)
317 else:
317 else:
318 res += re.escape(c)
318 res += re.escape(c)
319 return head + res + tail
319 return head + res + tail
320
320
321 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
321 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
322
322
323 def pathto(root, n1, n2):
323 def pathto(root, n1, n2):
324 '''return the relative path from one place to another.
324 '''return the relative path from one place to another.
325 root should use os.sep to separate directories
325 root should use os.sep to separate directories
326 n1 should use os.sep to separate directories
326 n1 should use os.sep to separate directories
327 n2 should use "/" to separate directories
327 n2 should use "/" to separate directories
328 returns an os.sep-separated path.
328 returns an os.sep-separated path.
329
329
330 If n1 is a relative path, it's assumed it's
330 If n1 is a relative path, it's assumed it's
331 relative to root.
331 relative to root.
332 n2 should always be relative to root.
332 n2 should always be relative to root.
333 '''
333 '''
334 if not n1: return localpath(n2)
334 if not n1: return localpath(n2)
335 if os.path.isabs(n1):
335 if os.path.isabs(n1):
336 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
336 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
337 return os.path.join(root, localpath(n2))
337 return os.path.join(root, localpath(n2))
338 n2 = '/'.join((pconvert(root), n2))
338 n2 = '/'.join((pconvert(root), n2))
339 a, b = n1.split(os.sep), n2.split('/')
339 a, b = n1.split(os.sep), n2.split('/')
340 a.reverse()
340 a.reverse()
341 b.reverse()
341 b.reverse()
342 while a and b and a[-1] == b[-1]:
342 while a and b and a[-1] == b[-1]:
343 a.pop()
343 a.pop()
344 b.pop()
344 b.pop()
345 b.reverse()
345 b.reverse()
346 return os.sep.join((['..'] * len(a)) + b)
346 return os.sep.join((['..'] * len(a)) + b)
347
347
348 def canonpath(root, cwd, myname):
348 def canonpath(root, cwd, myname):
349 """return the canonical path of myname, given cwd and root"""
349 """return the canonical path of myname, given cwd and root"""
350 if root == os.sep:
350 if root == os.sep:
351 rootsep = os.sep
351 rootsep = os.sep
352 elif root.endswith(os.sep):
352 elif root.endswith(os.sep):
353 rootsep = root
353 rootsep = root
354 else:
354 else:
355 rootsep = root + os.sep
355 rootsep = root + os.sep
356 name = myname
356 name = myname
357 if not os.path.isabs(name):
357 if not os.path.isabs(name):
358 name = os.path.join(root, cwd, name)
358 name = os.path.join(root, cwd, name)
359 name = os.path.normpath(name)
359 name = os.path.normpath(name)
360 if name != rootsep and name.startswith(rootsep):
360 if name != rootsep and name.startswith(rootsep):
361 name = name[len(rootsep):]
361 name = name[len(rootsep):]
362 audit_path(name)
362 audit_path(name)
363 return pconvert(name)
363 return pconvert(name)
364 elif name == root:
364 elif name == root:
365 return ''
365 return ''
366 else:
366 else:
367 # Determine whether `name' is in the hierarchy at or beneath `root',
367 # Determine whether `name' is in the hierarchy at or beneath `root',
368 # by iterating name=dirname(name) until that causes no change (can't
368 # by iterating name=dirname(name) until that causes no change (can't
369 # check name == '/', because that doesn't work on windows). For each
369 # check name == '/', because that doesn't work on windows). For each
370 # `name', compare dev/inode numbers. If they match, the list `rel'
370 # `name', compare dev/inode numbers. If they match, the list `rel'
371 # holds the reversed list of components making up the relative file
371 # holds the reversed list of components making up the relative file
372 # name we want.
372 # name we want.
373 root_st = os.stat(root)
373 root_st = os.stat(root)
374 rel = []
374 rel = []
375 while True:
375 while True:
376 try:
376 try:
377 name_st = os.stat(name)
377 name_st = os.stat(name)
378 except OSError:
378 except OSError:
379 break
379 break
380 if samestat(name_st, root_st):
380 if samestat(name_st, root_st):
381 if not rel:
381 if not rel:
382 # name was actually the same as root (maybe a symlink)
382 # name was actually the same as root (maybe a symlink)
383 return ''
383 return ''
384 rel.reverse()
384 rel.reverse()
385 name = os.path.join(*rel)
385 name = os.path.join(*rel)
386 audit_path(name)
386 audit_path(name)
387 return pconvert(name)
387 return pconvert(name)
388 dirname, basename = os.path.split(name)
388 dirname, basename = os.path.split(name)
389 rel.append(basename)
389 rel.append(basename)
390 if dirname == name:
390 if dirname == name:
391 break
391 break
392 name = dirname
392 name = dirname
393
393
394 raise Abort('%s not under root' % myname)
394 raise Abort('%s not under root' % myname)
395
395
396 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
396 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
397 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
397 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
398
398
399 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
399 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
400 globbed=False, default=None):
400 globbed=False, default=None):
401 default = default or 'relpath'
401 default = default or 'relpath'
402 if default == 'relpath' and not globbed:
402 if default == 'relpath' and not globbed:
403 names = expand_glob(names)
403 names = expand_glob(names)
404 return _matcher(canonroot, cwd, names, inc, exc, default, src)
404 return _matcher(canonroot, cwd, names, inc, exc, default, src)
405
405
406 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
406 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
407 """build a function to match a set of file patterns
407 """build a function to match a set of file patterns
408
408
409 arguments:
409 arguments:
410 canonroot - the canonical root of the tree you're matching against
410 canonroot - the canonical root of the tree you're matching against
411 cwd - the current working directory, if relevant
411 cwd - the current working directory, if relevant
412 names - patterns to find
412 names - patterns to find
413 inc - patterns to include
413 inc - patterns to include
414 exc - patterns to exclude
414 exc - patterns to exclude
415 dflt_pat - if a pattern in names has no explicit type, assume this one
415 dflt_pat - if a pattern in names has no explicit type, assume this one
416 src - where these patterns came from (e.g. .hgignore)
416 src - where these patterns came from (e.g. .hgignore)
417
417
418 a pattern is one of:
418 a pattern is one of:
419 'glob:<glob>' - a glob relative to cwd
419 'glob:<glob>' - a glob relative to cwd
420 're:<regexp>' - a regular expression
420 're:<regexp>' - a regular expression
421 'path:<path>' - a path relative to canonroot
421 'path:<path>' - a path relative to canonroot
422 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
422 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
423 'relpath:<path>' - a path relative to cwd
423 'relpath:<path>' - a path relative to cwd
424 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
424 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
425 '<something>' - one of the cases above, selected by the dflt_pat argument
425 '<something>' - one of the cases above, selected by the dflt_pat argument
426
426
427 returns:
427 returns:
428 a 3-tuple containing
428 a 3-tuple containing
429 - list of roots (places where one should start a recursive walk of the fs);
429 - list of roots (places where one should start a recursive walk of the fs);
430 this often matches the explicit non-pattern names passed in, but also
430 this often matches the explicit non-pattern names passed in, but also
431 includes the initial part of glob: patterns that has no glob characters
431 includes the initial part of glob: patterns that has no glob characters
432 - a bool match(filename) function
432 - a bool match(filename) function
433 - a bool indicating if any patterns were passed in
433 - a bool indicating if any patterns were passed in
434 """
434 """
435
435
436 # a common case: no patterns at all
436 # a common case: no patterns at all
437 if not names and not inc and not exc:
437 if not names and not inc and not exc:
438 return [], always, False
438 return [], always, False
439
439
440 def contains_glob(name):
440 def contains_glob(name):
441 for c in name:
441 for c in name:
442 if c in _globchars: return True
442 if c in _globchars: return True
443 return False
443 return False
444
444
445 def regex(kind, name, tail):
445 def regex(kind, name, tail):
446 '''convert a pattern into a regular expression'''
446 '''convert a pattern into a regular expression'''
447 if not name:
447 if not name:
448 return ''
448 return ''
449 if kind == 're':
449 if kind == 're':
450 return name
450 return name
451 elif kind == 'path':
451 elif kind == 'path':
452 return '^' + re.escape(name) + '(?:/|$)'
452 return '^' + re.escape(name) + '(?:/|$)'
453 elif kind == 'relglob':
453 elif kind == 'relglob':
454 return globre(name, '(?:|.*/)', tail)
454 return globre(name, '(?:|.*/)', tail)
455 elif kind == 'relpath':
455 elif kind == 'relpath':
456 return re.escape(name) + '(?:/|$)'
456 return re.escape(name) + '(?:/|$)'
457 elif kind == 'relre':
457 elif kind == 'relre':
458 if name.startswith('^'):
458 if name.startswith('^'):
459 return name
459 return name
460 return '.*' + name
460 return '.*' + name
461 return globre(name, '', tail)
461 return globre(name, '', tail)
462
462
463 def matchfn(pats, tail):
463 def matchfn(pats, tail):
464 """build a matching function from a set of patterns"""
464 """build a matching function from a set of patterns"""
465 if not pats:
465 if not pats:
466 return
466 return
467 try:
467 try:
468 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
468 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
469 return re.compile(pat).match
469 return re.compile(pat).match
470 except re.error:
470 except re.error:
471 for k, p in pats:
471 for k, p in pats:
472 try:
472 try:
473 re.compile('(?:%s)' % regex(k, p, tail))
473 re.compile('(?:%s)' % regex(k, p, tail))
474 except re.error:
474 except re.error:
475 if src:
475 if src:
476 raise Abort("%s: invalid pattern (%s): %s" %
476 raise Abort("%s: invalid pattern (%s): %s" %
477 (src, k, p))
477 (src, k, p))
478 else:
478 else:
479 raise Abort("invalid pattern (%s): %s" % (k, p))
479 raise Abort("invalid pattern (%s): %s" % (k, p))
480 raise Abort("invalid pattern")
480 raise Abort("invalid pattern")
481
481
482 def globprefix(pat):
482 def globprefix(pat):
483 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
483 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
484 root = []
484 root = []
485 for p in pat.split('/'):
485 for p in pat.split('/'):
486 if contains_glob(p): break
486 if contains_glob(p): break
487 root.append(p)
487 root.append(p)
488 return '/'.join(root) or '.'
488 return '/'.join(root) or '.'
489
489
490 def normalizepats(names, default):
490 def normalizepats(names, default):
491 pats = []
491 pats = []
492 roots = []
492 roots = []
493 anypats = False
493 anypats = False
494 for kind, name in [patkind(p, default) for p in names]:
494 for kind, name in [patkind(p, default) for p in names]:
495 if kind in ('glob', 'relpath'):
495 if kind in ('glob', 'relpath'):
496 name = canonpath(canonroot, cwd, name)
496 name = canonpath(canonroot, cwd, name)
497 elif kind in ('relglob', 'path'):
497 elif kind in ('relglob', 'path'):
498 name = normpath(name)
498 name = normpath(name)
499
499
500 pats.append((kind, name))
500 pats.append((kind, name))
501
501
502 if kind in ('glob', 're', 'relglob', 'relre'):
502 if kind in ('glob', 're', 'relglob', 'relre'):
503 anypats = True
503 anypats = True
504
504
505 if kind == 'glob':
505 if kind == 'glob':
506 root = globprefix(name)
506 root = globprefix(name)
507 roots.append(root)
507 roots.append(root)
508 elif kind in ('relpath', 'path'):
508 elif kind in ('relpath', 'path'):
509 roots.append(name or '.')
509 roots.append(name or '.')
510 elif kind == 'relglob':
510 elif kind == 'relglob':
511 roots.append('.')
511 roots.append('.')
512 return roots, pats, anypats
512 return roots, pats, anypats
513
513
514 roots, pats, anypats = normalizepats(names, dflt_pat)
514 roots, pats, anypats = normalizepats(names, dflt_pat)
515
515
516 patmatch = matchfn(pats, '$') or always
516 patmatch = matchfn(pats, '$') or always
517 incmatch = always
517 incmatch = always
518 if inc:
518 if inc:
519 dummy, inckinds, dummy = normalizepats(inc, 'glob')
519 dummy, inckinds, dummy = normalizepats(inc, 'glob')
520 incmatch = matchfn(inckinds, '(?:/|$)')
520 incmatch = matchfn(inckinds, '(?:/|$)')
521 excmatch = lambda fn: False
521 excmatch = lambda fn: False
522 if exc:
522 if exc:
523 dummy, exckinds, dummy = normalizepats(exc, 'glob')
523 dummy, exckinds, dummy = normalizepats(exc, 'glob')
524 excmatch = matchfn(exckinds, '(?:/|$)')
524 excmatch = matchfn(exckinds, '(?:/|$)')
525
525
526 if not names and inc and not exc:
526 if not names and inc and not exc:
527 # common case: hgignore patterns
527 # common case: hgignore patterns
528 match = incmatch
528 match = incmatch
529 else:
529 else:
530 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
530 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
531
531
532 return (roots, match, (inc or exc or anypats) and True)
532 return (roots, match, (inc or exc or anypats) and True)
533
533
534 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
534 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
535 '''enhanced shell command execution.
535 '''enhanced shell command execution.
536 run with environment maybe modified, maybe in different dir.
536 run with environment maybe modified, maybe in different dir.
537
537
538 if command fails and onerr is None, return status. if ui object,
538 if command fails and onerr is None, return status. if ui object,
539 print error message and return status, else raise onerr object as
539 print error message and return status, else raise onerr object as
540 exception.'''
540 exception.'''
541 def py2shell(val):
541 def py2shell(val):
542 'convert python object into string that is useful to shell'
542 'convert python object into string that is useful to shell'
543 if val in (None, False):
543 if val in (None, False):
544 return '0'
544 return '0'
545 if val == True:
545 if val == True:
546 return '1'
546 return '1'
547 return str(val)
547 return str(val)
548 oldenv = {}
548 oldenv = {}
549 for k in environ:
549 for k in environ:
550 oldenv[k] = os.environ.get(k)
550 oldenv[k] = os.environ.get(k)
551 if cwd is not None:
551 if cwd is not None:
552 oldcwd = os.getcwd()
552 oldcwd = os.getcwd()
553 origcmd = cmd
553 origcmd = cmd
554 if os.name == 'nt':
554 if os.name == 'nt':
555 cmd = '"%s"' % cmd
555 cmd = '"%s"' % cmd
556 try:
556 try:
557 for k, v in environ.iteritems():
557 for k, v in environ.iteritems():
558 os.environ[k] = py2shell(v)
558 os.environ[k] = py2shell(v)
559 if cwd is not None and oldcwd != cwd:
559 if cwd is not None and oldcwd != cwd:
560 os.chdir(cwd)
560 os.chdir(cwd)
561 rc = os.system(cmd)
561 rc = os.system(cmd)
562 if rc and onerr:
562 if rc and onerr:
563 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
563 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
564 explain_exit(rc)[0])
564 explain_exit(rc)[0])
565 if errprefix:
565 if errprefix:
566 errmsg = '%s: %s' % (errprefix, errmsg)
566 errmsg = '%s: %s' % (errprefix, errmsg)
567 try:
567 try:
568 onerr.warn(errmsg + '\n')
568 onerr.warn(errmsg + '\n')
569 except AttributeError:
569 except AttributeError:
570 raise onerr(errmsg)
570 raise onerr(errmsg)
571 return rc
571 return rc
572 finally:
572 finally:
573 for k, v in oldenv.iteritems():
573 for k, v in oldenv.iteritems():
574 if v is None:
574 if v is None:
575 del os.environ[k]
575 del os.environ[k]
576 else:
576 else:
577 os.environ[k] = v
577 os.environ[k] = v
578 if cwd is not None and oldcwd != cwd:
578 if cwd is not None and oldcwd != cwd:
579 os.chdir(oldcwd)
579 os.chdir(oldcwd)
580
580
581 # os.path.lexists is not available on python2.3
581 # os.path.lexists is not available on python2.3
582 def lexists(filename):
582 def lexists(filename):
583 "test whether a file with this name exists. does not follow symlinks"
583 "test whether a file with this name exists. does not follow symlinks"
584 try:
584 try:
585 os.lstat(filename)
585 os.lstat(filename)
586 except:
586 except:
587 return False
587 return False
588 return True
588 return True
589
589
590 def rename(src, dst):
590 def rename(src, dst):
591 """forcibly rename a file"""
591 """forcibly rename a file"""
592 try:
592 try:
593 os.rename(src, dst)
593 os.rename(src, dst)
594 except OSError, err:
594 except OSError, err:
595 # on windows, rename to existing file is not allowed, so we
595 # on windows, rename to existing file is not allowed, so we
596 # must delete destination first. but if file is open, unlink
596 # must delete destination first. but if file is open, unlink
597 # schedules it for delete but does not delete it. rename
597 # schedules it for delete but does not delete it. rename
598 # happens immediately even for open files, so we create
598 # happens immediately even for open files, so we create
599 # temporary file, delete it, rename destination to that name,
599 # temporary file, delete it, rename destination to that name,
600 # then delete that. then rename is safe to do.
600 # then delete that. then rename is safe to do.
601 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
601 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
602 os.close(fd)
602 os.close(fd)
603 os.unlink(temp)
603 os.unlink(temp)
604 os.rename(dst, temp)
604 os.rename(dst, temp)
605 os.unlink(temp)
605 os.unlink(temp)
606 os.rename(src, dst)
606 os.rename(src, dst)
607
607
608 def unlink(f):
608 def unlink(f):
609 """unlink and remove the directory if it is empty"""
609 """unlink and remove the directory if it is empty"""
610 os.unlink(f)
610 os.unlink(f)
611 # try removing directories that might now be empty
611 # try removing directories that might now be empty
612 try:
612 try:
613 os.removedirs(os.path.dirname(f))
613 os.removedirs(os.path.dirname(f))
614 except OSError:
614 except OSError:
615 pass
615 pass
616
616
617 def copyfile(src, dest):
617 def copyfile(src, dest):
618 "copy a file, preserving mode"
618 "copy a file, preserving mode"
619 if os.path.islink(src):
619 if os.path.islink(src):
620 try:
620 try:
621 os.unlink(dest)
621 os.unlink(dest)
622 except:
622 except:
623 pass
623 pass
624 os.symlink(os.readlink(src), dest)
624 os.symlink(os.readlink(src), dest)
625 else:
625 else:
626 try:
626 try:
627 shutil.copyfile(src, dest)
627 shutil.copyfile(src, dest)
628 shutil.copymode(src, dest)
628 shutil.copymode(src, dest)
629 except shutil.Error, inst:
629 except shutil.Error, inst:
630 raise Abort(str(inst))
630 raise Abort(str(inst))
631
631
632 def copyfiles(src, dst, hardlink=None):
632 def copyfiles(src, dst, hardlink=None):
633 """Copy a directory tree using hardlinks if possible"""
633 """Copy a directory tree using hardlinks if possible"""
634
634
635 if hardlink is None:
635 if hardlink is None:
636 hardlink = (os.stat(src).st_dev ==
636 hardlink = (os.stat(src).st_dev ==
637 os.stat(os.path.dirname(dst)).st_dev)
637 os.stat(os.path.dirname(dst)).st_dev)
638
638
639 if os.path.isdir(src):
639 if os.path.isdir(src):
640 os.mkdir(dst)
640 os.mkdir(dst)
641 for name in os.listdir(src):
641 for name in os.listdir(src):
642 srcname = os.path.join(src, name)
642 srcname = os.path.join(src, name)
643 dstname = os.path.join(dst, name)
643 dstname = os.path.join(dst, name)
644 copyfiles(srcname, dstname, hardlink)
644 copyfiles(srcname, dstname, hardlink)
645 else:
645 else:
646 if hardlink:
646 if hardlink:
647 try:
647 try:
648 os_link(src, dst)
648 os_link(src, dst)
649 except (IOError, OSError):
649 except (IOError, OSError):
650 hardlink = False
650 hardlink = False
651 shutil.copy(src, dst)
651 shutil.copy(src, dst)
652 else:
652 else:
653 shutil.copy(src, dst)
653 shutil.copy(src, dst)
654
654
655 def audit_path(path):
655 def audit_path(path):
656 """Abort if path contains dangerous components"""
656 """Abort if path contains dangerous components"""
657 parts = os.path.normcase(path).split(os.sep)
657 parts = os.path.normcase(path).split(os.sep)
658 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
658 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
659 or os.pardir in parts):
659 or os.pardir in parts):
660 raise Abort(_("path contains illegal component: %s") % path)
660 raise Abort(_("path contains illegal component: %s") % path)
661
661
662 def _makelock_file(info, pathname):
662 def _makelock_file(info, pathname):
663 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
663 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
664 os.write(ld, info)
664 os.write(ld, info)
665 os.close(ld)
665 os.close(ld)
666
666
667 def _readlock_file(pathname):
667 def _readlock_file(pathname):
668 return posixfile(pathname).read()
668 return posixfile(pathname).read()
669
669
670 def nlinks(pathname):
670 def nlinks(pathname):
671 """Return number of hardlinks for the given file."""
671 """Return number of hardlinks for the given file."""
672 return os.lstat(pathname).st_nlink
672 return os.lstat(pathname).st_nlink
673
673
674 if hasattr(os, 'link'):
674 if hasattr(os, 'link'):
675 os_link = os.link
675 os_link = os.link
676 else:
676 else:
677 def os_link(src, dst):
677 def os_link(src, dst):
678 raise OSError(0, _("Hardlinks not supported"))
678 raise OSError(0, _("Hardlinks not supported"))
679
679
680 def fstat(fp):
680 def fstat(fp):
681 '''stat file object that may not have fileno method.'''
681 '''stat file object that may not have fileno method.'''
682 try:
682 try:
683 return os.fstat(fp.fileno())
683 return os.fstat(fp.fileno())
684 except AttributeError:
684 except AttributeError:
685 return os.stat(fp.name)
685 return os.stat(fp.name)
686
686
687 posixfile = file
687 posixfile = file
688
688
689 def is_win_9x():
689 def is_win_9x():
690 '''return true if run on windows 95, 98 or me.'''
690 '''return true if run on windows 95, 98 or me.'''
691 try:
691 try:
692 return sys.getwindowsversion()[3] == 1
692 return sys.getwindowsversion()[3] == 1
693 except AttributeError:
693 except AttributeError:
694 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
694 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
695
695
696 getuser_fallback = None
696 getuser_fallback = None
697
697
698 def getuser():
698 def getuser():
699 '''return name of current user'''
699 '''return name of current user'''
700 try:
700 try:
701 return getpass.getuser()
701 return getpass.getuser()
702 except ImportError:
702 except ImportError:
703 # import of pwd will fail on windows - try fallback
703 # import of pwd will fail on windows - try fallback
704 if getuser_fallback:
704 if getuser_fallback:
705 return getuser_fallback()
705 return getuser_fallback()
706 # raised if win32api not available
706 # raised if win32api not available
707 raise Abort(_('user name not available - set USERNAME '
707 raise Abort(_('user name not available - set USERNAME '
708 'environment variable'))
708 'environment variable'))
709
709
710 def username(uid=None):
710 def username(uid=None):
711 """Return the name of the user with the given uid.
711 """Return the name of the user with the given uid.
712
712
713 If uid is None, return the name of the current user."""
713 If uid is None, return the name of the current user."""
714 try:
714 try:
715 import pwd
715 import pwd
716 if uid is None:
716 if uid is None:
717 uid = os.getuid()
717 uid = os.getuid()
718 try:
718 try:
719 return pwd.getpwuid(uid)[0]
719 return pwd.getpwuid(uid)[0]
720 except KeyError:
720 except KeyError:
721 return str(uid)
721 return str(uid)
722 except ImportError:
722 except ImportError:
723 return None
723 return None
724
724
725 def groupname(gid=None):
725 def groupname(gid=None):
726 """Return the name of the group with the given gid.
726 """Return the name of the group with the given gid.
727
727
728 If gid is None, return the name of the current group."""
728 If gid is None, return the name of the current group."""
729 try:
729 try:
730 import grp
730 import grp
731 if gid is None:
731 if gid is None:
732 gid = os.getgid()
732 gid = os.getgid()
733 try:
733 try:
734 return grp.getgrgid(gid)[0]
734 return grp.getgrgid(gid)[0]
735 except KeyError:
735 except KeyError:
736 return str(gid)
736 return str(gid)
737 except ImportError:
737 except ImportError:
738 return None
738 return None
739
739
740 # File system features
740 # File system features
741
741
742 def checkfolding(path):
742 def checkfolding(path):
743 """
743 """
744 Check whether the given path is on a case-sensitive filesystem
744 Check whether the given path is on a case-sensitive filesystem
745
745
746 Requires a path (like /foo/.hg) ending with a foldable final
746 Requires a path (like /foo/.hg) ending with a foldable final
747 directory component.
747 directory component.
748 """
748 """
749 s1 = os.stat(path)
749 s1 = os.stat(path)
750 d, b = os.path.split(path)
750 d, b = os.path.split(path)
751 p2 = os.path.join(d, b.upper())
751 p2 = os.path.join(d, b.upper())
752 if path == p2:
752 if path == p2:
753 p2 = os.path.join(d, b.lower())
753 p2 = os.path.join(d, b.lower())
754 try:
754 try:
755 s2 = os.stat(p2)
755 s2 = os.stat(p2)
756 if s2 == s1:
756 if s2 == s1:
757 return False
757 return False
758 return True
758 return True
759 except:
759 except:
760 return True
760 return True
761
761
762 def checkexec(path):
762 def checkexec(path):
763 """
763 """
764 Check whether the given path is on a filesystem with UNIX-like exec flags
764 Check whether the given path is on a filesystem with UNIX-like exec flags
765
765
766 Requires a directory (like /foo/.hg)
766 Requires a directory (like /foo/.hg)
767 """
767 """
768 fh, fn = tempfile.mkstemp("", "", path)
768 fh, fn = tempfile.mkstemp("", "", path)
769 os.close(fh)
769 os.close(fh)
770 m = os.stat(fn).st_mode
770 m = os.stat(fn).st_mode
771 os.chmod(fn, m ^ 0111)
771 os.chmod(fn, m ^ 0111)
772 r = (os.stat(fn).st_mode != m)
772 r = (os.stat(fn).st_mode != m)
773 os.unlink(fn)
773 os.unlink(fn)
774 return r
774 return r
775
775
776 def execfunc(path, fallback):
776 def execfunc(path, fallback):
777 '''return an is_exec() function with default to fallback'''
777 '''return an is_exec() function with default to fallback'''
778 if checkexec(path):
778 if checkexec(path):
779 return lambda x: is_exec(os.path.join(path, x))
779 return lambda x: is_exec(os.path.join(path, x))
780 return fallback
780 return fallback
781
781
782 def checklink(path):
782 def checklink(path):
783 """check whether the given path is on a symlink-capable filesystem"""
783 """check whether the given path is on a symlink-capable filesystem"""
784 # mktemp is not racy because symlink creation will fail if the
784 # mktemp is not racy because symlink creation will fail if the
785 # file already exists
785 # file already exists
786 name = tempfile.mktemp(dir=path)
786 name = tempfile.mktemp(dir=path)
787 try:
787 try:
788 os.symlink(".", name)
788 os.symlink(".", name)
789 os.unlink(name)
789 os.unlink(name)
790 return True
790 return True
791 except (OSError, AttributeError):
791 except (OSError, AttributeError):
792 return False
792 return False
793
793
794 def linkfunc(path, fallback):
794 def linkfunc(path, fallback):
795 '''return an is_link() function with default to fallback'''
795 '''return an is_link() function with default to fallback'''
796 if checklink(path):
796 if checklink(path):
797 return lambda x: os.path.islink(os.path.join(path, x))
797 return lambda x: os.path.islink(os.path.join(path, x))
798 return fallback
798 return fallback
799
799
800 _umask = os.umask(0)
800 _umask = os.umask(0)
801 os.umask(_umask)
801 os.umask(_umask)
802
802
803 def needbinarypatch():
803 def needbinarypatch():
804 """return True if patches should be applied in binary mode by default."""
804 """return True if patches should be applied in binary mode by default."""
805 return os.name == 'nt'
805 return os.name == 'nt'
806
806
807 # Platform specific variants
807 # Platform specific variants
808 if os.name == 'nt':
808 if os.name == 'nt':
809 import msvcrt
809 import msvcrt
810 nulldev = 'NUL:'
810 nulldev = 'NUL:'
811
811
812 class winstdout:
812 class winstdout:
813 '''stdout on windows misbehaves if sent through a pipe'''
813 '''stdout on windows misbehaves if sent through a pipe'''
814
814
815 def __init__(self, fp):
815 def __init__(self, fp):
816 self.fp = fp
816 self.fp = fp
817
817
818 def __getattr__(self, key):
818 def __getattr__(self, key):
819 return getattr(self.fp, key)
819 return getattr(self.fp, key)
820
820
821 def close(self):
821 def close(self):
822 try:
822 try:
823 self.fp.close()
823 self.fp.close()
824 except: pass
824 except: pass
825
825
826 def write(self, s):
826 def write(self, s):
827 try:
827 try:
828 return self.fp.write(s)
828 return self.fp.write(s)
829 except IOError, inst:
829 except IOError, inst:
830 if inst.errno != 0: raise
830 if inst.errno != 0: raise
831 self.close()
831 self.close()
832 raise IOError(errno.EPIPE, 'Broken pipe')
832 raise IOError(errno.EPIPE, 'Broken pipe')
833
833
834 def flush(self):
834 def flush(self):
835 try:
835 try:
836 return self.fp.flush()
836 return self.fp.flush()
837 except IOError, inst:
837 except IOError, inst:
838 if inst.errno != errno.EINVAL: raise
838 if inst.errno != errno.EINVAL: raise
839 self.close()
839 self.close()
840 raise IOError(errno.EPIPE, 'Broken pipe')
840 raise IOError(errno.EPIPE, 'Broken pipe')
841
841
842 sys.stdout = winstdout(sys.stdout)
842 sys.stdout = winstdout(sys.stdout)
843
843
844 def system_rcpath():
844 def system_rcpath():
845 try:
845 try:
846 return system_rcpath_win32()
846 return system_rcpath_win32()
847 except:
847 except:
848 return [r'c:\mercurial\mercurial.ini']
848 return [r'c:\mercurial\mercurial.ini']
849
849
850 def user_rcpath():
850 def user_rcpath():
851 '''return os-specific hgrc search path to the user dir'''
851 '''return os-specific hgrc search path to the user dir'''
852 try:
852 try:
853 userrc = user_rcpath_win32()
853 userrc = user_rcpath_win32()
854 except:
854 except:
855 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
855 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
856 path = [userrc]
856 path = [userrc]
857 userprofile = os.environ.get('USERPROFILE')
857 userprofile = os.environ.get('USERPROFILE')
858 if userprofile:
858 if userprofile:
859 path.append(os.path.join(userprofile, 'mercurial.ini'))
859 path.append(os.path.join(userprofile, 'mercurial.ini'))
860 return path
860 return path
861
861
862 def parse_patch_output(output_line):
862 def parse_patch_output(output_line):
863 """parses the output produced by patch and returns the file name"""
863 """parses the output produced by patch and returns the file name"""
864 pf = output_line[14:]
864 pf = output_line[14:]
865 if pf[0] == '`':
865 if pf[0] == '`':
866 pf = pf[1:-1] # Remove the quotes
866 pf = pf[1:-1] # Remove the quotes
867 return pf
867 return pf
868
868
869 def testpid(pid):
869 def testpid(pid):
870 '''return False if pid dead, True if running or not known'''
870 '''return False if pid dead, True if running or not known'''
871 return True
871 return True
872
872
873 def set_exec(f, mode):
873 def set_exec(f, mode):
874 pass
874 pass
875
875
876 def set_link(f, mode):
876 def set_link(f, mode):
877 pass
877 pass
878
878
879 def set_binary(fd):
879 def set_binary(fd):
880 msvcrt.setmode(fd.fileno(), os.O_BINARY)
880 msvcrt.setmode(fd.fileno(), os.O_BINARY)
881
881
882 def pconvert(path):
882 def pconvert(path):
883 return path.replace("\\", "/")
883 return path.replace("\\", "/")
884
884
885 def localpath(path):
885 def localpath(path):
886 return path.replace('/', '\\')
886 return path.replace('/', '\\')
887
887
888 def normpath(path):
888 def normpath(path):
889 return pconvert(os.path.normpath(path))
889 return pconvert(os.path.normpath(path))
890
890
891 makelock = _makelock_file
891 makelock = _makelock_file
892 readlock = _readlock_file
892 readlock = _readlock_file
893
893
894 def samestat(s1, s2):
894 def samestat(s1, s2):
895 return False
895 return False
896
896
897 # A sequence of backslashes is special iff it precedes a double quote:
897 # A sequence of backslashes is special iff it precedes a double quote:
898 # - if there's an even number of backslashes, the double quote is not
898 # - if there's an even number of backslashes, the double quote is not
899 # quoted (i.e. it ends the quoted region)
899 # quoted (i.e. it ends the quoted region)
900 # - if there's an odd number of backslashes, the double quote is quoted
900 # - if there's an odd number of backslashes, the double quote is quoted
901 # - in both cases, every pair of backslashes is unquoted into a single
901 # - in both cases, every pair of backslashes is unquoted into a single
902 # backslash
902 # backslash
903 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
903 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
904 # So, to quote a string, we must surround it in double quotes, double
904 # So, to quote a string, we must surround it in double quotes, double
905 # the number of backslashes that preceed double quotes and add another
905 # the number of backslashes that preceed double quotes and add another
906 # backslash before every double quote (being careful with the double
906 # backslash before every double quote (being careful with the double
907 # quote we've appended to the end)
907 # quote we've appended to the end)
908 _quotere = None
908 _quotere = None
909 def shellquote(s):
909 def shellquote(s):
910 global _quotere
910 global _quotere
911 if _quotere is None:
911 if _quotere is None:
912 _quotere = re.compile(r'(\\*)("|\\$)')
912 _quotere = re.compile(r'(\\*)("|\\$)')
913 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
913 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
914
914
915 def explain_exit(code):
915 def explain_exit(code):
916 return _("exited with status %d") % code, code
916 return _("exited with status %d") % code, code
917
917
918 # if you change this stub into a real check, please try to implement the
918 # if you change this stub into a real check, please try to implement the
919 # username and groupname functions above, too.
919 # username and groupname functions above, too.
920 def isowner(fp, st=None):
920 def isowner(fp, st=None):
921 return True
921 return True
922
922
923 def find_in_path(name, path, default=None):
923 def find_in_path(name, path, default=None):
924 '''find name in search path. path can be string (will be split
924 '''find name in search path. path can be string (will be split
925 with os.pathsep), or iterable thing that returns strings. if name
925 with os.pathsep), or iterable thing that returns strings. if name
926 found, return path to name. else return default. name is looked up
926 found, return path to name. else return default. name is looked up
927 using cmd.exe rules, using PATHEXT.'''
927 using cmd.exe rules, using PATHEXT.'''
928 if isinstance(path, str):
928 if isinstance(path, str):
929 path = path.split(os.pathsep)
929 path = path.split(os.pathsep)
930
930
931 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
931 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
932 pathext = pathext.lower().split(os.pathsep)
932 pathext = pathext.lower().split(os.pathsep)
933 isexec = os.path.splitext(name)[1].lower() in pathext
933 isexec = os.path.splitext(name)[1].lower() in pathext
934
934
935 for p in path:
935 for p in path:
936 p_name = os.path.join(p, name)
936 p_name = os.path.join(p, name)
937
937
938 if isexec and os.path.exists(p_name):
938 if isexec and os.path.exists(p_name):
939 return p_name
939 return p_name
940
940
941 for ext in pathext:
941 for ext in pathext:
942 p_name_ext = p_name + ext
942 p_name_ext = p_name + ext
943 if os.path.exists(p_name_ext):
943 if os.path.exists(p_name_ext):
944 return p_name_ext
944 return p_name_ext
945 return default
945 return default
946
946
947 try:
947 try:
948 # override functions with win32 versions if possible
948 # override functions with win32 versions if possible
949 from util_win32 import *
949 from util_win32 import *
950 if not is_win_9x():
950 if not is_win_9x():
951 posixfile = posixfile_nt
951 posixfile = posixfile_nt
952 except ImportError:
952 except ImportError:
953 pass
953 pass
954
954
955 else:
955 else:
956 nulldev = '/dev/null'
956 nulldev = '/dev/null'
957
957
958 def rcfiles(path):
958 def rcfiles(path):
959 rcs = [os.path.join(path, 'hgrc')]
959 rcs = [os.path.join(path, 'hgrc')]
960 rcdir = os.path.join(path, 'hgrc.d')
960 rcdir = os.path.join(path, 'hgrc.d')
961 try:
961 try:
962 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
962 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
963 if f.endswith(".rc")])
963 if f.endswith(".rc")])
964 except OSError:
964 except OSError:
965 pass
965 pass
966 return rcs
966 return rcs
967
967
968 def system_rcpath():
968 def system_rcpath():
969 path = []
969 path = []
970 # old mod_python does not set sys.argv
970 # old mod_python does not set sys.argv
971 if len(getattr(sys, 'argv', [])) > 0:
971 if len(getattr(sys, 'argv', [])) > 0:
972 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
972 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
973 '/../etc/mercurial'))
973 '/../etc/mercurial'))
974 path.extend(rcfiles('/etc/mercurial'))
974 path.extend(rcfiles('/etc/mercurial'))
975 return path
975 return path
976
976
977 def user_rcpath():
977 def user_rcpath():
978 return [os.path.expanduser('~/.hgrc')]
978 return [os.path.expanduser('~/.hgrc')]
979
979
980 def parse_patch_output(output_line):
980 def parse_patch_output(output_line):
981 """parses the output produced by patch and returns the file name"""
981 """parses the output produced by patch and returns the file name"""
982 pf = output_line[14:]
982 pf = output_line[14:]
983 if pf.startswith("'") and pf.endswith("'") and " " in pf:
983 if pf.startswith("'") and pf.endswith("'") and " " in pf:
984 pf = pf[1:-1] # Remove the quotes
984 pf = pf[1:-1] # Remove the quotes
985 return pf
985 return pf
986
986
987 def is_exec(f):
987 def is_exec(f):
988 """check whether a file is executable"""
988 """check whether a file is executable"""
989 return (os.lstat(f).st_mode & 0100 != 0)
989 return (os.lstat(f).st_mode & 0100 != 0)
990
990
991 def set_exec(f, mode):
991 def set_exec(f, mode):
992 s = os.lstat(f).st_mode
992 s = os.lstat(f).st_mode
993 if (s & 0100 != 0) == mode:
993 if (s & 0100 != 0) == mode:
994 return
994 return
995 if mode:
995 if mode:
996 # Turn on +x for every +r bit when making a file executable
996 # Turn on +x for every +r bit when making a file executable
997 # and obey umask.
997 # and obey umask.
998 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
998 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
999 else:
999 else:
1000 os.chmod(f, s & 0666)
1000 os.chmod(f, s & 0666)
1001
1001
1002 def set_link(f, mode):
1002 def set_link(f, mode):
1003 """make a file a symbolic link/regular file
1003 """make a file a symbolic link/regular file
1004
1004
1005 if a file is changed to a link, its contents become the link data
1005 if a file is changed to a link, its contents become the link data
1006 if a link is changed to a file, its link data become its contents
1006 if a link is changed to a file, its link data become its contents
1007 """
1007 """
1008
1008
1009 m = os.path.islink(f)
1009 m = os.path.islink(f)
1010 if m == bool(mode):
1010 if m == bool(mode):
1011 return
1011 return
1012
1012
1013 if mode: # switch file to link
1013 if mode: # switch file to link
1014 data = file(f).read()
1014 data = file(f).read()
1015 os.unlink(f)
1015 os.unlink(f)
1016 os.symlink(data, f)
1016 os.symlink(data, f)
1017 else:
1017 else:
1018 data = os.readlink(f)
1018 data = os.readlink(f)
1019 os.unlink(f)
1019 os.unlink(f)
1020 file(f, "w").write(data)
1020 file(f, "w").write(data)
1021
1021
1022 def set_binary(fd):
1022 def set_binary(fd):
1023 pass
1023 pass
1024
1024
1025 def pconvert(path):
1025 def pconvert(path):
1026 return path
1026 return path
1027
1027
1028 def localpath(path):
1028 def localpath(path):
1029 return path
1029 return path
1030
1030
1031 normpath = os.path.normpath
1031 normpath = os.path.normpath
1032 samestat = os.path.samestat
1032 samestat = os.path.samestat
1033
1033
1034 def makelock(info, pathname):
1034 def makelock(info, pathname):
1035 try:
1035 try:
1036 os.symlink(info, pathname)
1036 os.symlink(info, pathname)
1037 except OSError, why:
1037 except OSError, why:
1038 if why.errno == errno.EEXIST:
1038 if why.errno == errno.EEXIST:
1039 raise
1039 raise
1040 else:
1040 else:
1041 _makelock_file(info, pathname)
1041 _makelock_file(info, pathname)
1042
1042
1043 def readlock(pathname):
1043 def readlock(pathname):
1044 try:
1044 try:
1045 return os.readlink(pathname)
1045 return os.readlink(pathname)
1046 except OSError, why:
1046 except OSError, why:
1047 if why.errno == errno.EINVAL:
1047 if why.errno == errno.EINVAL:
1048 return _readlock_file(pathname)
1048 return _readlock_file(pathname)
1049 else:
1049 else:
1050 raise
1050 raise
1051
1051
1052 def shellquote(s):
1052 def shellquote(s):
1053 return "'%s'" % s.replace("'", "'\\''")
1053 return "'%s'" % s.replace("'", "'\\''")
1054
1054
1055 def testpid(pid):
1055 def testpid(pid):
1056 '''return False if pid dead, True if running or not sure'''
1056 '''return False if pid dead, True if running or not sure'''
1057 try:
1057 try:
1058 os.kill(pid, 0)
1058 os.kill(pid, 0)
1059 return True
1059 return True
1060 except OSError, inst:
1060 except OSError, inst:
1061 return inst.errno != errno.ESRCH
1061 return inst.errno != errno.ESRCH
1062
1062
1063 def explain_exit(code):
1063 def explain_exit(code):
1064 """return a 2-tuple (desc, code) describing a process's status"""
1064 """return a 2-tuple (desc, code) describing a process's status"""
1065 if os.WIFEXITED(code):
1065 if os.WIFEXITED(code):
1066 val = os.WEXITSTATUS(code)
1066 val = os.WEXITSTATUS(code)
1067 return _("exited with status %d") % val, val
1067 return _("exited with status %d") % val, val
1068 elif os.WIFSIGNALED(code):
1068 elif os.WIFSIGNALED(code):
1069 val = os.WTERMSIG(code)
1069 val = os.WTERMSIG(code)
1070 return _("killed by signal %d") % val, val
1070 return _("killed by signal %d") % val, val
1071 elif os.WIFSTOPPED(code):
1071 elif os.WIFSTOPPED(code):
1072 val = os.WSTOPSIG(code)
1072 val = os.WSTOPSIG(code)
1073 return _("stopped by signal %d") % val, val
1073 return _("stopped by signal %d") % val, val
1074 raise ValueError(_("invalid exit code"))
1074 raise ValueError(_("invalid exit code"))
1075
1075
1076 def isowner(fp, st=None):
1076 def isowner(fp, st=None):
1077 """Return True if the file object f belongs to the current user.
1077 """Return True if the file object f belongs to the current user.
1078
1078
1079 The return value of a util.fstat(f) may be passed as the st argument.
1079 The return value of a util.fstat(f) may be passed as the st argument.
1080 """
1080 """
1081 if st is None:
1081 if st is None:
1082 st = fstat(fp)
1082 st = fstat(fp)
1083 return st.st_uid == os.getuid()
1083 return st.st_uid == os.getuid()
1084
1084
1085 def find_in_path(name, path, default=None):
1085 def find_in_path(name, path, default=None):
1086 '''find name in search path. path can be string (will be split
1086 '''find name in search path. path can be string (will be split
1087 with os.pathsep), or iterable thing that returns strings. if name
1087 with os.pathsep), or iterable thing that returns strings. if name
1088 found, return path to name. else return default.'''
1088 found, return path to name. else return default.'''
1089 if isinstance(path, str):
1089 if isinstance(path, str):
1090 path = path.split(os.pathsep)
1090 path = path.split(os.pathsep)
1091 for p in path:
1091 for p in path:
1092 p_name = os.path.join(p, name)
1092 p_name = os.path.join(p, name)
1093 if os.path.exists(p_name):
1093 if os.path.exists(p_name):
1094 return p_name
1094 return p_name
1095 return default
1095 return default
1096
1096
1097 def find_exe(name, default=None):
1097 def find_exe(name, default=None):
1098 '''find path of an executable.
1098 '''find path of an executable.
1099 if name contains a path component, return it as is. otherwise,
1099 if name contains a path component, return it as is. otherwise,
1100 use normal executable search path.'''
1100 use normal executable search path.'''
1101
1101
1102 if os.sep in name:
1102 if os.sep in name:
1103 # don't check the executable bit. if the file isn't
1103 # don't check the executable bit. if the file isn't
1104 # executable, whoever tries to actually run it will give a
1104 # executable, whoever tries to actually run it will give a
1105 # much more useful error message.
1105 # much more useful error message.
1106 return name
1106 return name
1107 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1107 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1108
1108
1109 def _buildencodefun():
1109 def _buildencodefun():
1110 e = '_'
1110 e = '_'
1111 win_reserved = [ord(x) for x in '\\:*?"<>|']
1111 win_reserved = [ord(x) for x in '\\:*?"<>|']
1112 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1112 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1113 for x in (range(32) + range(126, 256) + win_reserved):
1113 for x in (range(32) + range(126, 256) + win_reserved):
1114 cmap[chr(x)] = "~%02x" % x
1114 cmap[chr(x)] = "~%02x" % x
1115 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1115 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1116 cmap[chr(x)] = e + chr(x).lower()
1116 cmap[chr(x)] = e + chr(x).lower()
1117 dmap = {}
1117 dmap = {}
1118 for k, v in cmap.iteritems():
1118 for k, v in cmap.iteritems():
1119 dmap[v] = k
1119 dmap[v] = k
1120 def decode(s):
1120 def decode(s):
1121 i = 0
1121 i = 0
1122 while i < len(s):
1122 while i < len(s):
1123 for l in xrange(1, 4):
1123 for l in xrange(1, 4):
1124 try:
1124 try:
1125 yield dmap[s[i:i+l]]
1125 yield dmap[s[i:i+l]]
1126 i += l
1126 i += l
1127 break
1127 break
1128 except KeyError:
1128 except KeyError:
1129 pass
1129 pass
1130 else:
1130 else:
1131 raise KeyError
1131 raise KeyError
1132 return (lambda s: "".join([cmap[c] for c in s]),
1132 return (lambda s: "".join([cmap[c] for c in s]),
1133 lambda s: "".join(list(decode(s))))
1133 lambda s: "".join(list(decode(s))))
1134
1134
1135 encodefilename, decodefilename = _buildencodefun()
1135 encodefilename, decodefilename = _buildencodefun()
1136
1136
1137 def encodedopener(openerfn, fn):
1137 def encodedopener(openerfn, fn):
1138 def o(path, *args, **kw):
1138 def o(path, *args, **kw):
1139 return openerfn(fn(path), *args, **kw)
1139 return openerfn(fn(path), *args, **kw)
1140 return o
1140 return o
1141
1141
1142 def opener(base, audit=True):
1142 def opener(base, audit=True):
1143 """
1143 """
1144 return a function that opens files relative to base
1144 return a function that opens files relative to base
1145
1145
1146 this function is used to hide the details of COW semantics and
1146 this function is used to hide the details of COW semantics and
1147 remote file access from higher level code.
1147 remote file access from higher level code.
1148 """
1148 """
1149 p = base
1149 p = base
1150 audit_p = audit
1150 audit_p = audit
1151
1151
1152 def mktempcopy(name, emptyok=False):
1152 def mktempcopy(name, emptyok=False):
1153 d, fn = os.path.split(name)
1153 d, fn = os.path.split(name)
1154 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1154 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1155 os.close(fd)
1155 os.close(fd)
1156 # Temporary files are created with mode 0600, which is usually not
1156 # Temporary files are created with mode 0600, which is usually not
1157 # what we want. If the original file already exists, just copy
1157 # what we want. If the original file already exists, just copy
1158 # its mode. Otherwise, manually obey umask.
1158 # its mode. Otherwise, manually obey umask.
1159 try:
1159 try:
1160 st_mode = os.lstat(name).st_mode
1160 st_mode = os.lstat(name).st_mode
1161 except OSError, inst:
1161 except OSError, inst:
1162 if inst.errno != errno.ENOENT:
1162 if inst.errno != errno.ENOENT:
1163 raise
1163 raise
1164 st_mode = 0666 & ~_umask
1164 st_mode = 0666 & ~_umask
1165 os.chmod(temp, st_mode)
1165 os.chmod(temp, st_mode)
1166 if emptyok:
1166 if emptyok:
1167 return temp
1167 return temp
1168 try:
1168 try:
1169 try:
1169 try:
1170 ifp = posixfile(name, "rb")
1170 ifp = posixfile(name, "rb")
1171 except IOError, inst:
1171 except IOError, inst:
1172 if inst.errno == errno.ENOENT:
1172 if inst.errno == errno.ENOENT:
1173 return temp
1173 return temp
1174 if not getattr(inst, 'filename', None):
1174 if not getattr(inst, 'filename', None):
1175 inst.filename = name
1175 inst.filename = name
1176 raise
1176 raise
1177 ofp = posixfile(temp, "wb")
1177 ofp = posixfile(temp, "wb")
1178 for chunk in filechunkiter(ifp):
1178 for chunk in filechunkiter(ifp):
1179 ofp.write(chunk)
1179 ofp.write(chunk)
1180 ifp.close()
1180 ifp.close()
1181 ofp.close()
1181 ofp.close()
1182 except:
1182 except:
1183 try: os.unlink(temp)
1183 try: os.unlink(temp)
1184 except: pass
1184 except: pass
1185 raise
1185 raise
1186 return temp
1186 return temp
1187
1187
1188 class atomictempfile(posixfile):
1188 class atomictempfile(posixfile):
1189 """the file will only be copied when rename is called"""
1189 """the file will only be copied when rename is called"""
1190 def __init__(self, name, mode):
1190 def __init__(self, name, mode):
1191 self.__name = name
1191 self.__name = name
1192 self.temp = mktempcopy(name, emptyok=('w' in mode))
1192 self.temp = mktempcopy(name, emptyok=('w' in mode))
1193 posixfile.__init__(self, self.temp, mode)
1193 posixfile.__init__(self, self.temp, mode)
1194 def rename(self):
1194 def rename(self):
1195 if not self.closed:
1195 if not self.closed:
1196 posixfile.close(self)
1196 posixfile.close(self)
1197 rename(self.temp, localpath(self.__name))
1197 rename(self.temp, localpath(self.__name))
1198 def __del__(self):
1198 def __del__(self):
1199 if not self.closed:
1199 if not self.closed:
1200 try:
1200 try:
1201 os.unlink(self.temp)
1201 os.unlink(self.temp)
1202 except: pass
1202 except: pass
1203 posixfile.close(self)
1203 posixfile.close(self)
1204
1204
1205 def o(path, mode="r", text=False, atomictemp=False):
1205 def o(path, mode="r", text=False, atomictemp=False):
1206 if audit_p:
1206 if audit_p:
1207 audit_path(path)
1207 audit_path(path)
1208 f = os.path.join(p, path)
1208 f = os.path.join(p, path)
1209
1209
1210 if not text:
1210 if not text:
1211 mode += "b" # for that other OS
1211 mode += "b" # for that other OS
1212
1212
1213 if mode[0] != "r":
1213 if mode[0] != "r":
1214 try:
1214 try:
1215 nlink = nlinks(f)
1215 nlink = nlinks(f)
1216 except OSError:
1216 except OSError:
1217 nlink = 0
1217 nlink = 0
1218 d = os.path.dirname(f)
1218 d = os.path.dirname(f)
1219 if not os.path.isdir(d):
1219 if not os.path.isdir(d):
1220 os.makedirs(d)
1220 os.makedirs(d)
1221 if atomictemp:
1221 if atomictemp:
1222 return atomictempfile(f, mode)
1222 return atomictempfile(f, mode)
1223 if nlink > 1:
1223 if nlink > 1:
1224 rename(mktempcopy(f), f)
1224 rename(mktempcopy(f), f)
1225 return posixfile(f, mode)
1225 return posixfile(f, mode)
1226
1226
1227 return o
1227 return o
1228
1228
1229 class chunkbuffer(object):
1229 class chunkbuffer(object):
1230 """Allow arbitrary sized chunks of data to be efficiently read from an
1230 """Allow arbitrary sized chunks of data to be efficiently read from an
1231 iterator over chunks of arbitrary size."""
1231 iterator over chunks of arbitrary size."""
1232
1232
1233 def __init__(self, in_iter, targetsize = 2**16):
1233 def __init__(self, in_iter, targetsize = 2**16):
1234 """in_iter is the iterator that's iterating over the input chunks.
1234 """in_iter is the iterator that's iterating over the input chunks.
1235 targetsize is how big a buffer to try to maintain."""
1235 targetsize is how big a buffer to try to maintain."""
1236 self.in_iter = iter(in_iter)
1236 self.in_iter = iter(in_iter)
1237 self.buf = ''
1237 self.buf = ''
1238 self.targetsize = int(targetsize)
1238 self.targetsize = int(targetsize)
1239 if self.targetsize <= 0:
1239 if self.targetsize <= 0:
1240 raise ValueError(_("targetsize must be greater than 0, was %d") %
1240 raise ValueError(_("targetsize must be greater than 0, was %d") %
1241 targetsize)
1241 targetsize)
1242 self.iterempty = False
1242 self.iterempty = False
1243
1243
1244 def fillbuf(self):
1244 def fillbuf(self):
1245 """Ignore target size; read every chunk from iterator until empty."""
1245 """Ignore target size; read every chunk from iterator until empty."""
1246 if not self.iterempty:
1246 if not self.iterempty:
1247 collector = cStringIO.StringIO()
1247 collector = cStringIO.StringIO()
1248 collector.write(self.buf)
1248 collector.write(self.buf)
1249 for ch in self.in_iter:
1249 for ch in self.in_iter:
1250 collector.write(ch)
1250 collector.write(ch)
1251 self.buf = collector.getvalue()
1251 self.buf = collector.getvalue()
1252 self.iterempty = True
1252 self.iterempty = True
1253
1253
1254 def read(self, l):
1254 def read(self, l):
1255 """Read L bytes of data from the iterator of chunks of data.
1255 """Read L bytes of data from the iterator of chunks of data.
1256 Returns less than L bytes if the iterator runs dry."""
1256 Returns less than L bytes if the iterator runs dry."""
1257 if l > len(self.buf) and not self.iterempty:
1257 if l > len(self.buf) and not self.iterempty:
1258 # Clamp to a multiple of self.targetsize
1258 # Clamp to a multiple of self.targetsize
1259 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1259 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1260 collector = cStringIO.StringIO()
1260 collector = cStringIO.StringIO()
1261 collector.write(self.buf)
1261 collector.write(self.buf)
1262 collected = len(self.buf)
1262 collected = len(self.buf)
1263 for chunk in self.in_iter:
1263 for chunk in self.in_iter:
1264 collector.write(chunk)
1264 collector.write(chunk)
1265 collected += len(chunk)
1265 collected += len(chunk)
1266 if collected >= targetsize:
1266 if collected >= targetsize:
1267 break
1267 break
1268 if collected < targetsize:
1268 if collected < targetsize:
1269 self.iterempty = True
1269 self.iterempty = True
1270 self.buf = collector.getvalue()
1270 self.buf = collector.getvalue()
1271 s, self.buf = self.buf[:l], buffer(self.buf, l)
1271 s, self.buf = self.buf[:l], buffer(self.buf, l)
1272 return s
1272 return s
1273
1273
1274 def filechunkiter(f, size=65536, limit=None):
1274 def filechunkiter(f, size=65536, limit=None):
1275 """Create a generator that produces the data in the file size
1275 """Create a generator that produces the data in the file size
1276 (default 65536) bytes at a time, up to optional limit (default is
1276 (default 65536) bytes at a time, up to optional limit (default is
1277 to read all data). Chunks may be less than size bytes if the
1277 to read all data). Chunks may be less than size bytes if the
1278 chunk is the last chunk in the file, or the file is a socket or
1278 chunk is the last chunk in the file, or the file is a socket or
1279 some other type of file that sometimes reads less data than is
1279 some other type of file that sometimes reads less data than is
1280 requested."""
1280 requested."""
1281 assert size >= 0
1281 assert size >= 0
1282 assert limit is None or limit >= 0
1282 assert limit is None or limit >= 0
1283 while True:
1283 while True:
1284 if limit is None: nbytes = size
1284 if limit is None: nbytes = size
1285 else: nbytes = min(limit, size)
1285 else: nbytes = min(limit, size)
1286 s = nbytes and f.read(nbytes)
1286 s = nbytes and f.read(nbytes)
1287 if not s: break
1287 if not s: break
1288 if limit: limit -= len(s)
1288 if limit: limit -= len(s)
1289 yield s
1289 yield s
1290
1290
1291 def makedate():
1291 def makedate():
1292 lt = time.localtime()
1292 lt = time.localtime()
1293 if lt[8] == 1 and time.daylight:
1293 if lt[8] == 1 and time.daylight:
1294 tz = time.altzone
1294 tz = time.altzone
1295 else:
1295 else:
1296 tz = time.timezone
1296 tz = time.timezone
1297 return time.mktime(lt), tz
1297 return time.mktime(lt), tz
1298
1298
1299 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1299 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1300 """represent a (unixtime, offset) tuple as a localized time.
1300 """represent a (unixtime, offset) tuple as a localized time.
1301 unixtime is seconds since the epoch, and offset is the time zone's
1301 unixtime is seconds since the epoch, and offset is the time zone's
1302 number of seconds away from UTC. if timezone is false, do not
1302 number of seconds away from UTC. if timezone is false, do not
1303 append time zone to string."""
1303 append time zone to string."""
1304 t, tz = date or makedate()
1304 t, tz = date or makedate()
1305 s = time.strftime(format, time.gmtime(float(t) - tz))
1305 s = time.strftime(format, time.gmtime(float(t) - tz))
1306 if timezone:
1306 if timezone:
1307 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1307 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1308 return s
1308 return s
1309
1309
1310 def strdate(string, format, defaults):
1310 def strdate(string, format, defaults):
1311 """parse a localized time string and return a (unixtime, offset) tuple.
1311 """parse a localized time string and return a (unixtime, offset) tuple.
1312 if the string cannot be parsed, ValueError is raised."""
1312 if the string cannot be parsed, ValueError is raised."""
1313 def timezone(string):
1313 def timezone(string):
1314 tz = string.split()[-1]
1314 tz = string.split()[-1]
1315 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1315 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1316 tz = int(tz)
1316 tz = int(tz)
1317 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1317 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1318 return offset
1318 return offset
1319 if tz == "GMT" or tz == "UTC":
1319 if tz == "GMT" or tz == "UTC":
1320 return 0
1320 return 0
1321 return None
1321 return None
1322
1322
1323 # NOTE: unixtime = localunixtime + offset
1323 # NOTE: unixtime = localunixtime + offset
1324 offset, date = timezone(string), string
1324 offset, date = timezone(string), string
1325 if offset != None:
1325 if offset != None:
1326 date = " ".join(string.split()[:-1])
1326 date = " ".join(string.split()[:-1])
1327
1327
1328 # add missing elements from defaults
1328 # add missing elements from defaults
1329 for part in defaults:
1329 for part in defaults:
1330 found = [True for p in part if ("%"+p) in format]
1330 found = [True for p in part if ("%"+p) in format]
1331 if not found:
1331 if not found:
1332 date += "@" + defaults[part]
1332 date += "@" + defaults[part]
1333 format += "@%" + part[0]
1333 format += "@%" + part[0]
1334
1334
1335 timetuple = time.strptime(date, format)
1335 timetuple = time.strptime(date, format)
1336 localunixtime = int(calendar.timegm(timetuple))
1336 localunixtime = int(calendar.timegm(timetuple))
1337 if offset is None:
1337 if offset is None:
1338 # local timezone
1338 # local timezone
1339 unixtime = int(time.mktime(timetuple))
1339 unixtime = int(time.mktime(timetuple))
1340 offset = unixtime - localunixtime
1340 offset = unixtime - localunixtime
1341 else:
1341 else:
1342 unixtime = localunixtime + offset
1342 unixtime = localunixtime + offset
1343 return unixtime, offset
1343 return unixtime, offset
1344
1344
1345 def parsedate(string, formats=None, defaults=None):
1345 def parsedate(string, formats=None, defaults=None):
1346 """parse a localized time string and return a (unixtime, offset) tuple.
1346 """parse a localized time string and return a (unixtime, offset) tuple.
1347 The date may be a "unixtime offset" string or in one of the specified
1347 The date may be a "unixtime offset" string or in one of the specified
1348 formats."""
1348 formats."""
1349 if not string:
1349 if not string:
1350 return 0, 0
1350 return 0, 0
1351 if not formats:
1351 if not formats:
1352 formats = defaultdateformats
1352 formats = defaultdateformats
1353 string = string.strip()
1353 string = string.strip()
1354 try:
1354 try:
1355 when, offset = map(int, string.split(' '))
1355 when, offset = map(int, string.split(' '))
1356 except ValueError:
1356 except ValueError:
1357 # fill out defaults
1357 # fill out defaults
1358 if not defaults:
1358 if not defaults:
1359 defaults = {}
1359 defaults = {}
1360 now = makedate()
1360 now = makedate()
1361 for part in "d mb yY HI M S".split():
1361 for part in "d mb yY HI M S".split():
1362 if part not in defaults:
1362 if part not in defaults:
1363 if part[0] in "HMS":
1363 if part[0] in "HMS":
1364 defaults[part] = "00"
1364 defaults[part] = "00"
1365 elif part[0] in "dm":
1365 elif part[0] in "dm":
1366 defaults[part] = "1"
1366 defaults[part] = "1"
1367 else:
1367 else:
1368 defaults[part] = datestr(now, "%" + part[0], False)
1368 defaults[part] = datestr(now, "%" + part[0], False)
1369
1369
1370 for format in formats:
1370 for format in formats:
1371 try:
1371 try:
1372 when, offset = strdate(string, format, defaults)
1372 when, offset = strdate(string, format, defaults)
1373 except ValueError:
1373 except ValueError:
1374 pass
1374 pass
1375 else:
1375 else:
1376 break
1376 break
1377 else:
1377 else:
1378 raise Abort(_('invalid date: %r ') % string)
1378 raise Abort(_('invalid date: %r ') % string)
1379 # validate explicit (probably user-specified) date and
1379 # validate explicit (probably user-specified) date and
1380 # time zone offset. values must fit in signed 32 bits for
1380 # time zone offset. values must fit in signed 32 bits for
1381 # current 32-bit linux runtimes. timezones go from UTC-12
1381 # current 32-bit linux runtimes. timezones go from UTC-12
1382 # to UTC+14
1382 # to UTC+14
1383 if abs(when) > 0x7fffffff:
1383 if abs(when) > 0x7fffffff:
1384 raise Abort(_('date exceeds 32 bits: %d') % when)
1384 raise Abort(_('date exceeds 32 bits: %d') % when)
1385 if offset < -50400 or offset > 43200:
1385 if offset < -50400 or offset > 43200:
1386 raise Abort(_('impossible time zone offset: %d') % offset)
1386 raise Abort(_('impossible time zone offset: %d') % offset)
1387 return when, offset
1387 return when, offset
1388
1388
1389 def matchdate(date):
1389 def matchdate(date):
1390 """Return a function that matches a given date match specifier
1390 """Return a function that matches a given date match specifier
1391
1391
1392 Formats include:
1392 Formats include:
1393
1393
1394 '{date}' match a given date to the accuracy provided
1394 '{date}' match a given date to the accuracy provided
1395
1395
1396 '<{date}' on or before a given date
1396 '<{date}' on or before a given date
1397
1397
1398 '>{date}' on or after a given date
1398 '>{date}' on or after a given date
1399
1399
1400 """
1400 """
1401
1401
1402 def lower(date):
1402 def lower(date):
1403 return parsedate(date, extendeddateformats)[0]
1403 return parsedate(date, extendeddateformats)[0]
1404
1404
1405 def upper(date):
1405 def upper(date):
1406 d = dict(mb="12", HI="23", M="59", S="59")
1406 d = dict(mb="12", HI="23", M="59", S="59")
1407 for days in "31 30 29".split():
1407 for days in "31 30 29".split():
1408 try:
1408 try:
1409 d["d"] = days
1409 d["d"] = days
1410 return parsedate(date, extendeddateformats, d)[0]
1410 return parsedate(date, extendeddateformats, d)[0]
1411 except:
1411 except:
1412 pass
1412 pass
1413 d["d"] = "28"
1413 d["d"] = "28"
1414 return parsedate(date, extendeddateformats, d)[0]
1414 return parsedate(date, extendeddateformats, d)[0]
1415
1415
1416 if date[0] == "<":
1416 if date[0] == "<":
1417 when = upper(date[1:])
1417 when = upper(date[1:])
1418 return lambda x: x <= when
1418 return lambda x: x <= when
1419 elif date[0] == ">":
1419 elif date[0] == ">":
1420 when = lower(date[1:])
1420 when = lower(date[1:])
1421 return lambda x: x >= when
1421 return lambda x: x >= when
1422 elif date[0] == "-":
1422 elif date[0] == "-":
1423 try:
1423 try:
1424 days = int(date[1:])
1424 days = int(date[1:])
1425 except ValueError:
1425 except ValueError:
1426 raise Abort(_("invalid day spec: %s") % date[1:])
1426 raise Abort(_("invalid day spec: %s") % date[1:])
1427 when = makedate()[0] - days * 3600 * 24
1427 when = makedate()[0] - days * 3600 * 24
1428 return lambda x: x >= when
1428 return lambda x: x >= when
1429 elif " to " in date:
1429 elif " to " in date:
1430 a, b = date.split(" to ")
1430 a, b = date.split(" to ")
1431 start, stop = lower(a), upper(b)
1431 start, stop = lower(a), upper(b)
1432 return lambda x: x >= start and x <= stop
1432 return lambda x: x >= start and x <= stop
1433 else:
1433 else:
1434 start, stop = lower(date), upper(date)
1434 start, stop = lower(date), upper(date)
1435 return lambda x: x >= start and x <= stop
1435 return lambda x: x >= start and x <= stop
1436
1436
1437 def shortuser(user):
1437 def shortuser(user):
1438 """Return a short representation of a user name or email address."""
1438 """Return a short representation of a user name or email address."""
1439 f = user.find('@')
1439 f = user.find('@')
1440 if f >= 0:
1440 if f >= 0:
1441 user = user[:f]
1441 user = user[:f]
1442 f = user.find('<')
1442 f = user.find('<')
1443 if f >= 0:
1443 if f >= 0:
1444 user = user[f+1:]
1444 user = user[f+1:]
1445 f = user.find(' ')
1445 f = user.find(' ')
1446 if f >= 0:
1446 if f >= 0:
1447 user = user[:f]
1447 user = user[:f]
1448 f = user.find('.')
1448 f = user.find('.')
1449 if f >= 0:
1449 if f >= 0:
1450 user = user[:f]
1450 user = user[:f]
1451 return user
1451 return user
1452
1452
1453 def ellipsis(text, maxlength=400):
1453 def ellipsis(text, maxlength=400):
1454 """Trim string to at most maxlength (default: 400) characters."""
1454 """Trim string to at most maxlength (default: 400) characters."""
1455 if len(text) <= maxlength:
1455 if len(text) <= maxlength:
1456 return text
1456 return text
1457 else:
1457 else:
1458 return "%s..." % (text[:maxlength-3])
1458 return "%s..." % (text[:maxlength-3])
1459
1459
1460 def walkrepos(path):
1460 def walkrepos(path):
1461 '''yield every hg repository under path, recursively.'''
1461 '''yield every hg repository under path, recursively.'''
1462 def errhandler(err):
1462 def errhandler(err):
1463 if err.filename == path:
1463 if err.filename == path:
1464 raise err
1464 raise err
1465
1465
1466 for root, dirs, files in os.walk(path, onerror=errhandler):
1466 for root, dirs, files in os.walk(path, onerror=errhandler):
1467 for d in dirs:
1467 for d in dirs:
1468 if d == '.hg':
1468 if d == '.hg':
1469 yield root
1469 yield root
1470 dirs[:] = []
1470 dirs[:] = []
1471 break
1471 break
1472
1472
1473 _rcpath = None
1473 _rcpath = None
1474
1474
1475 def os_rcpath():
1475 def os_rcpath():
1476 '''return default os-specific hgrc search path'''
1476 '''return default os-specific hgrc search path'''
1477 path = system_rcpath()
1477 path = system_rcpath()
1478 path.extend(user_rcpath())
1478 path.extend(user_rcpath())
1479 path = [os.path.normpath(f) for f in path]
1479 path = [os.path.normpath(f) for f in path]
1480 return path
1480 return path
1481
1481
1482 def rcpath():
1482 def rcpath():
1483 '''return hgrc search path. if env var HGRCPATH is set, use it.
1483 '''return hgrc search path. if env var HGRCPATH is set, use it.
1484 for each item in path, if directory, use files ending in .rc,
1484 for each item in path, if directory, use files ending in .rc,
1485 else use item.
1485 else use item.
1486 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1486 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1487 if no HGRCPATH, use default os-specific path.'''
1487 if no HGRCPATH, use default os-specific path.'''
1488 global _rcpath
1488 global _rcpath
1489 if _rcpath is None:
1489 if _rcpath is None:
1490 if 'HGRCPATH' in os.environ:
1490 if 'HGRCPATH' in os.environ:
1491 _rcpath = []
1491 _rcpath = []
1492 for p in os.environ['HGRCPATH'].split(os.pathsep):
1492 for p in os.environ['HGRCPATH'].split(os.pathsep):
1493 if not p: continue
1493 if not p: continue
1494 if os.path.isdir(p):
1494 if os.path.isdir(p):
1495 for f in os.listdir(p):
1495 for f in os.listdir(p):
1496 if f.endswith('.rc'):
1496 if f.endswith('.rc'):
1497 _rcpath.append(os.path.join(p, f))
1497 _rcpath.append(os.path.join(p, f))
1498 else:
1498 else:
1499 _rcpath.append(p)
1499 _rcpath.append(p)
1500 else:
1500 else:
1501 _rcpath = os_rcpath()
1501 _rcpath = os_rcpath()
1502 return _rcpath
1502 return _rcpath
1503
1503
1504 def bytecount(nbytes):
1504 def bytecount(nbytes):
1505 '''return byte count formatted as readable string, with units'''
1505 '''return byte count formatted as readable string, with units'''
1506
1506
1507 units = (
1507 units = (
1508 (100, 1<<30, _('%.0f GB')),
1508 (100, 1<<30, _('%.0f GB')),
1509 (10, 1<<30, _('%.1f GB')),
1509 (10, 1<<30, _('%.1f GB')),
1510 (1, 1<<30, _('%.2f GB')),
1510 (1, 1<<30, _('%.2f GB')),
1511 (100, 1<<20, _('%.0f MB')),
1511 (100, 1<<20, _('%.0f MB')),
1512 (10, 1<<20, _('%.1f MB')),
1512 (10, 1<<20, _('%.1f MB')),
1513 (1, 1<<20, _('%.2f MB')),
1513 (1, 1<<20, _('%.2f MB')),
1514 (100, 1<<10, _('%.0f KB')),
1514 (100, 1<<10, _('%.0f KB')),
1515 (10, 1<<10, _('%.1f KB')),
1515 (10, 1<<10, _('%.1f KB')),
1516 (1, 1<<10, _('%.2f KB')),
1516 (1, 1<<10, _('%.2f KB')),
1517 (1, 1, _('%.0f bytes')),
1517 (1, 1, _('%.0f bytes')),
1518 )
1518 )
1519
1519
1520 for multiplier, divisor, format in units:
1520 for multiplier, divisor, format in units:
1521 if nbytes >= divisor * multiplier:
1521 if nbytes >= divisor * multiplier:
1522 return format % (nbytes / float(divisor))
1522 return format % (nbytes / float(divisor))
1523 return units[-1][2] % nbytes
1523 return units[-1][2] % nbytes
1524
1524
1525 def drop_scheme(scheme, path):
1525 def drop_scheme(scheme, path):
1526 sc = scheme + ':'
1526 sc = scheme + ':'
1527 if path.startswith(sc):
1527 if path.startswith(sc):
1528 path = path[len(sc):]
1528 path = path[len(sc):]
1529 if path.startswith('//'):
1529 if path.startswith('//'):
1530 path = path[2:]
1530 path = path[2:]
1531 return path
1531 return path
General Comments 0
You need to be logged in to leave comments. Login now