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