##// END OF EJS Templates
Add a new function, fspath...
Paul Moore -
r6676:33045179 default
parent child Browse files
Show More
@@ -1,1856 +1,1903 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-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 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, re, shutil, sys, tempfile
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18 import imp, urlparse
18 import imp, urlparse
19
19
20 # Python compatibility
20 # Python compatibility
21
21
22 try:
22 try:
23 set = set
23 set = set
24 frozenset = frozenset
24 frozenset = frozenset
25 except NameError:
25 except NameError:
26 from sets import Set as set, ImmutableSet as frozenset
26 from sets import Set as set, ImmutableSet as frozenset
27
27
28 _md5 = None
28 _md5 = None
29 def md5(s):
29 def md5(s):
30 global _md5
30 global _md5
31 if _md5 is None:
31 if _md5 is None:
32 try:
32 try:
33 import hashlib
33 import hashlib
34 _md5 = hashlib.md5
34 _md5 = hashlib.md5
35 except ImportError:
35 except ImportError:
36 import md5
36 import md5
37 _md5 = md5.md5
37 _md5 = md5.md5
38 return _md5(s)
38 return _md5(s)
39
39
40 _sha1 = None
40 _sha1 = None
41 def sha1(s):
41 def sha1(s):
42 global _sha1
42 global _sha1
43 if _sha1 is None:
43 if _sha1 is None:
44 try:
44 try:
45 import hashlib
45 import hashlib
46 _sha1 = hashlib.sha1
46 _sha1 = hashlib.sha1
47 except ImportError:
47 except ImportError:
48 import sha
48 import sha
49 _sha1 = sha.sha
49 _sha1 = sha.sha
50 return _sha1(s)
50 return _sha1(s)
51
51
52 try:
52 try:
53 _encoding = os.environ.get("HGENCODING")
53 _encoding = os.environ.get("HGENCODING")
54 if sys.platform == 'darwin' and not _encoding:
54 if sys.platform == 'darwin' and not _encoding:
55 # On darwin, getpreferredencoding ignores the locale environment and
55 # On darwin, getpreferredencoding ignores the locale environment and
56 # always returns mac-roman. We override this if the environment is
56 # always returns mac-roman. We override this if the environment is
57 # not C (has been customized by the user).
57 # not C (has been customized by the user).
58 locale.setlocale(locale.LC_CTYPE, '')
58 locale.setlocale(locale.LC_CTYPE, '')
59 _encoding = locale.getlocale()[1]
59 _encoding = locale.getlocale()[1]
60 if not _encoding:
60 if not _encoding:
61 _encoding = locale.getpreferredencoding() or 'ascii'
61 _encoding = locale.getpreferredencoding() or 'ascii'
62 except locale.Error:
62 except locale.Error:
63 _encoding = 'ascii'
63 _encoding = 'ascii'
64 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
64 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
65 _fallbackencoding = 'ISO-8859-1'
65 _fallbackencoding = 'ISO-8859-1'
66
66
67 def tolocal(s):
67 def tolocal(s):
68 """
68 """
69 Convert a string from internal UTF-8 to local encoding
69 Convert a string from internal UTF-8 to local encoding
70
70
71 All internal strings should be UTF-8 but some repos before the
71 All internal strings should be UTF-8 but some repos before the
72 implementation of locale support may contain latin1 or possibly
72 implementation of locale support may contain latin1 or possibly
73 other character sets. We attempt to decode everything strictly
73 other character sets. We attempt to decode everything strictly
74 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
74 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
75 replace unknown characters.
75 replace unknown characters.
76 """
76 """
77 for e in ('UTF-8', _fallbackencoding):
77 for e in ('UTF-8', _fallbackencoding):
78 try:
78 try:
79 u = s.decode(e) # attempt strict decoding
79 u = s.decode(e) # attempt strict decoding
80 return u.encode(_encoding, "replace")
80 return u.encode(_encoding, "replace")
81 except LookupError, k:
81 except LookupError, k:
82 raise Abort(_("%s, please check your locale settings") % k)
82 raise Abort(_("%s, please check your locale settings") % k)
83 except UnicodeDecodeError:
83 except UnicodeDecodeError:
84 pass
84 pass
85 u = s.decode("utf-8", "replace") # last ditch
85 u = s.decode("utf-8", "replace") # last ditch
86 return u.encode(_encoding, "replace")
86 return u.encode(_encoding, "replace")
87
87
88 def fromlocal(s):
88 def fromlocal(s):
89 """
89 """
90 Convert a string from the local character encoding to UTF-8
90 Convert a string from the local character encoding to UTF-8
91
91
92 We attempt to decode strings using the encoding mode set by
92 We attempt to decode strings using the encoding mode set by
93 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
93 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
94 characters will cause an error message. Other modes include
94 characters will cause an error message. Other modes include
95 'replace', which replaces unknown characters with a special
95 'replace', which replaces unknown characters with a special
96 Unicode character, and 'ignore', which drops the character.
96 Unicode character, and 'ignore', which drops the character.
97 """
97 """
98 try:
98 try:
99 return s.decode(_encoding, _encodingmode).encode("utf-8")
99 return s.decode(_encoding, _encodingmode).encode("utf-8")
100 except UnicodeDecodeError, inst:
100 except UnicodeDecodeError, inst:
101 sub = s[max(0, inst.start-10):inst.start+10]
101 sub = s[max(0, inst.start-10):inst.start+10]
102 raise Abort("decoding near '%s': %s!" % (sub, inst))
102 raise Abort("decoding near '%s': %s!" % (sub, inst))
103 except LookupError, k:
103 except LookupError, k:
104 raise Abort(_("%s, please check your locale settings") % k)
104 raise Abort(_("%s, please check your locale settings") % k)
105
105
106 def locallen(s):
106 def locallen(s):
107 """Find the length in characters of a local string"""
107 """Find the length in characters of a local string"""
108 return len(s.decode(_encoding, "replace"))
108 return len(s.decode(_encoding, "replace"))
109
109
110 # used by parsedate
110 # used by parsedate
111 defaultdateformats = (
111 defaultdateformats = (
112 '%Y-%m-%d %H:%M:%S',
112 '%Y-%m-%d %H:%M:%S',
113 '%Y-%m-%d %I:%M:%S%p',
113 '%Y-%m-%d %I:%M:%S%p',
114 '%Y-%m-%d %H:%M',
114 '%Y-%m-%d %H:%M',
115 '%Y-%m-%d %I:%M%p',
115 '%Y-%m-%d %I:%M%p',
116 '%Y-%m-%d',
116 '%Y-%m-%d',
117 '%m-%d',
117 '%m-%d',
118 '%m/%d',
118 '%m/%d',
119 '%m/%d/%y',
119 '%m/%d/%y',
120 '%m/%d/%Y',
120 '%m/%d/%Y',
121 '%a %b %d %H:%M:%S %Y',
121 '%a %b %d %H:%M:%S %Y',
122 '%a %b %d %I:%M:%S%p %Y',
122 '%a %b %d %I:%M:%S%p %Y',
123 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
123 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
124 '%b %d %H:%M:%S %Y',
124 '%b %d %H:%M:%S %Y',
125 '%b %d %I:%M:%S%p %Y',
125 '%b %d %I:%M:%S%p %Y',
126 '%b %d %H:%M:%S',
126 '%b %d %H:%M:%S',
127 '%b %d %I:%M:%S%p',
127 '%b %d %I:%M:%S%p',
128 '%b %d %H:%M',
128 '%b %d %H:%M',
129 '%b %d %I:%M%p',
129 '%b %d %I:%M%p',
130 '%b %d %Y',
130 '%b %d %Y',
131 '%b %d',
131 '%b %d',
132 '%H:%M:%S',
132 '%H:%M:%S',
133 '%I:%M:%SP',
133 '%I:%M:%SP',
134 '%H:%M',
134 '%H:%M',
135 '%I:%M%p',
135 '%I:%M%p',
136 )
136 )
137
137
138 extendeddateformats = defaultdateformats + (
138 extendeddateformats = defaultdateformats + (
139 "%Y",
139 "%Y",
140 "%Y-%m",
140 "%Y-%m",
141 "%b",
141 "%b",
142 "%b %Y",
142 "%b %Y",
143 )
143 )
144
144
145 class SignalInterrupt(Exception):
145 class SignalInterrupt(Exception):
146 """Exception raised on SIGTERM and SIGHUP."""
146 """Exception raised on SIGTERM and SIGHUP."""
147
147
148 # differences from SafeConfigParser:
148 # differences from SafeConfigParser:
149 # - case-sensitive keys
149 # - case-sensitive keys
150 # - allows values that are not strings (this means that you may not
150 # - allows values that are not strings (this means that you may not
151 # be able to save the configuration to a file)
151 # be able to save the configuration to a file)
152 class configparser(ConfigParser.SafeConfigParser):
152 class configparser(ConfigParser.SafeConfigParser):
153 def optionxform(self, optionstr):
153 def optionxform(self, optionstr):
154 return optionstr
154 return optionstr
155
155
156 def set(self, section, option, value):
156 def set(self, section, option, value):
157 return ConfigParser.ConfigParser.set(self, section, option, value)
157 return ConfigParser.ConfigParser.set(self, section, option, value)
158
158
159 def _interpolate(self, section, option, rawval, vars):
159 def _interpolate(self, section, option, rawval, vars):
160 if not isinstance(rawval, basestring):
160 if not isinstance(rawval, basestring):
161 return rawval
161 return rawval
162 return ConfigParser.SafeConfigParser._interpolate(self, section,
162 return ConfigParser.SafeConfigParser._interpolate(self, section,
163 option, rawval, vars)
163 option, rawval, vars)
164
164
165 def cachefunc(func):
165 def cachefunc(func):
166 '''cache the result of function calls'''
166 '''cache the result of function calls'''
167 # XXX doesn't handle keywords args
167 # XXX doesn't handle keywords args
168 cache = {}
168 cache = {}
169 if func.func_code.co_argcount == 1:
169 if func.func_code.co_argcount == 1:
170 # we gain a small amount of time because
170 # we gain a small amount of time because
171 # we don't need to pack/unpack the list
171 # we don't need to pack/unpack the list
172 def f(arg):
172 def f(arg):
173 if arg not in cache:
173 if arg not in cache:
174 cache[arg] = func(arg)
174 cache[arg] = func(arg)
175 return cache[arg]
175 return cache[arg]
176 else:
176 else:
177 def f(*args):
177 def f(*args):
178 if args not in cache:
178 if args not in cache:
179 cache[args] = func(*args)
179 cache[args] = func(*args)
180 return cache[args]
180 return cache[args]
181
181
182 return f
182 return f
183
183
184 def pipefilter(s, cmd):
184 def pipefilter(s, cmd):
185 '''filter string S through command CMD, returning its output'''
185 '''filter string S through command CMD, returning its output'''
186 (pin, pout) = os.popen2(cmd, 'b')
186 (pin, pout) = os.popen2(cmd, 'b')
187 def writer():
187 def writer():
188 try:
188 try:
189 pin.write(s)
189 pin.write(s)
190 pin.close()
190 pin.close()
191 except IOError, inst:
191 except IOError, inst:
192 if inst.errno != errno.EPIPE:
192 if inst.errno != errno.EPIPE:
193 raise
193 raise
194
194
195 # we should use select instead on UNIX, but this will work on most
195 # we should use select instead on UNIX, but this will work on most
196 # systems, including Windows
196 # systems, including Windows
197 w = threading.Thread(target=writer)
197 w = threading.Thread(target=writer)
198 w.start()
198 w.start()
199 f = pout.read()
199 f = pout.read()
200 pout.close()
200 pout.close()
201 w.join()
201 w.join()
202 return f
202 return f
203
203
204 def tempfilter(s, cmd):
204 def tempfilter(s, cmd):
205 '''filter string S through a pair of temporary files with CMD.
205 '''filter string S through a pair of temporary files with CMD.
206 CMD is used as a template to create the real command to be run,
206 CMD is used as a template to create the real command to be run,
207 with the strings INFILE and OUTFILE replaced by the real names of
207 with the strings INFILE and OUTFILE replaced by the real names of
208 the temporary files generated.'''
208 the temporary files generated.'''
209 inname, outname = None, None
209 inname, outname = None, None
210 try:
210 try:
211 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
211 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
212 fp = os.fdopen(infd, 'wb')
212 fp = os.fdopen(infd, 'wb')
213 fp.write(s)
213 fp.write(s)
214 fp.close()
214 fp.close()
215 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
215 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
216 os.close(outfd)
216 os.close(outfd)
217 cmd = cmd.replace('INFILE', inname)
217 cmd = cmd.replace('INFILE', inname)
218 cmd = cmd.replace('OUTFILE', outname)
218 cmd = cmd.replace('OUTFILE', outname)
219 code = os.system(cmd)
219 code = os.system(cmd)
220 if sys.platform == 'OpenVMS' and code & 1:
220 if sys.platform == 'OpenVMS' and code & 1:
221 code = 0
221 code = 0
222 if code: raise Abort(_("command '%s' failed: %s") %
222 if code: raise Abort(_("command '%s' failed: %s") %
223 (cmd, explain_exit(code)))
223 (cmd, explain_exit(code)))
224 return open(outname, 'rb').read()
224 return open(outname, 'rb').read()
225 finally:
225 finally:
226 try:
226 try:
227 if inname: os.unlink(inname)
227 if inname: os.unlink(inname)
228 except: pass
228 except: pass
229 try:
229 try:
230 if outname: os.unlink(outname)
230 if outname: os.unlink(outname)
231 except: pass
231 except: pass
232
232
233 filtertable = {
233 filtertable = {
234 'tempfile:': tempfilter,
234 'tempfile:': tempfilter,
235 'pipe:': pipefilter,
235 'pipe:': pipefilter,
236 }
236 }
237
237
238 def filter(s, cmd):
238 def filter(s, cmd):
239 "filter a string through a command that transforms its input to its output"
239 "filter a string through a command that transforms its input to its output"
240 for name, fn in filtertable.iteritems():
240 for name, fn in filtertable.iteritems():
241 if cmd.startswith(name):
241 if cmd.startswith(name):
242 return fn(s, cmd[len(name):].lstrip())
242 return fn(s, cmd[len(name):].lstrip())
243 return pipefilter(s, cmd)
243 return pipefilter(s, cmd)
244
244
245 def binary(s):
245 def binary(s):
246 """return true if a string is binary data"""
246 """return true if a string is binary data"""
247 if s and '\0' in s:
247 if s and '\0' in s:
248 return True
248 return True
249 return False
249 return False
250
250
251 def unique(g):
251 def unique(g):
252 """return the uniq elements of iterable g"""
252 """return the uniq elements of iterable g"""
253 return dict.fromkeys(g).keys()
253 return dict.fromkeys(g).keys()
254
254
255 class Abort(Exception):
255 class Abort(Exception):
256 """Raised if a command needs to print an error and exit."""
256 """Raised if a command needs to print an error and exit."""
257
257
258 class UnexpectedOutput(Abort):
258 class UnexpectedOutput(Abort):
259 """Raised to print an error with part of output and exit."""
259 """Raised to print an error with part of output and exit."""
260
260
261 def always(fn): return True
261 def always(fn): return True
262 def never(fn): return False
262 def never(fn): return False
263
263
264 def expand_glob(pats):
264 def expand_glob(pats):
265 '''On Windows, expand the implicit globs in a list of patterns'''
265 '''On Windows, expand the implicit globs in a list of patterns'''
266 if os.name != 'nt':
266 if os.name != 'nt':
267 return list(pats)
267 return list(pats)
268 ret = []
268 ret = []
269 for p in pats:
269 for p in pats:
270 kind, name = patkind(p, None)
270 kind, name = patkind(p, None)
271 if kind is None:
271 if kind is None:
272 globbed = glob.glob(name)
272 globbed = glob.glob(name)
273 if globbed:
273 if globbed:
274 ret.extend(globbed)
274 ret.extend(globbed)
275 continue
275 continue
276 # if we couldn't expand the glob, just keep it around
276 # if we couldn't expand the glob, just keep it around
277 ret.append(p)
277 ret.append(p)
278 return ret
278 return ret
279
279
280 def patkind(name, default):
280 def patkind(name, default):
281 """Split a string into an optional pattern kind prefix and the
281 """Split a string into an optional pattern kind prefix and the
282 actual pattern."""
282 actual pattern."""
283 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
283 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
284 if name.startswith(prefix + ':'): return name.split(':', 1)
284 if name.startswith(prefix + ':'): return name.split(':', 1)
285 return default, name
285 return default, name
286
286
287 def globre(pat, head='^', tail='$'):
287 def globre(pat, head='^', tail='$'):
288 "convert a glob pattern into a regexp"
288 "convert a glob pattern into a regexp"
289 i, n = 0, len(pat)
289 i, n = 0, len(pat)
290 res = ''
290 res = ''
291 group = 0
291 group = 0
292 def peek(): return i < n and pat[i]
292 def peek(): return i < n and pat[i]
293 while i < n:
293 while i < n:
294 c = pat[i]
294 c = pat[i]
295 i = i+1
295 i = i+1
296 if c == '*':
296 if c == '*':
297 if peek() == '*':
297 if peek() == '*':
298 i += 1
298 i += 1
299 res += '.*'
299 res += '.*'
300 else:
300 else:
301 res += '[^/]*'
301 res += '[^/]*'
302 elif c == '?':
302 elif c == '?':
303 res += '.'
303 res += '.'
304 elif c == '[':
304 elif c == '[':
305 j = i
305 j = i
306 if j < n and pat[j] in '!]':
306 if j < n and pat[j] in '!]':
307 j += 1
307 j += 1
308 while j < n and pat[j] != ']':
308 while j < n and pat[j] != ']':
309 j += 1
309 j += 1
310 if j >= n:
310 if j >= n:
311 res += '\\['
311 res += '\\['
312 else:
312 else:
313 stuff = pat[i:j].replace('\\','\\\\')
313 stuff = pat[i:j].replace('\\','\\\\')
314 i = j + 1
314 i = j + 1
315 if stuff[0] == '!':
315 if stuff[0] == '!':
316 stuff = '^' + stuff[1:]
316 stuff = '^' + stuff[1:]
317 elif stuff[0] == '^':
317 elif stuff[0] == '^':
318 stuff = '\\' + stuff
318 stuff = '\\' + stuff
319 res = '%s[%s]' % (res, stuff)
319 res = '%s[%s]' % (res, stuff)
320 elif c == '{':
320 elif c == '{':
321 group += 1
321 group += 1
322 res += '(?:'
322 res += '(?:'
323 elif c == '}' and group:
323 elif c == '}' and group:
324 res += ')'
324 res += ')'
325 group -= 1
325 group -= 1
326 elif c == ',' and group:
326 elif c == ',' and group:
327 res += '|'
327 res += '|'
328 elif c == '\\':
328 elif c == '\\':
329 p = peek()
329 p = peek()
330 if p:
330 if p:
331 i += 1
331 i += 1
332 res += re.escape(p)
332 res += re.escape(p)
333 else:
333 else:
334 res += re.escape(c)
334 res += re.escape(c)
335 else:
335 else:
336 res += re.escape(c)
336 res += re.escape(c)
337 return head + res + tail
337 return head + res + tail
338
338
339 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
339 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
340
340
341 def pathto(root, n1, n2):
341 def pathto(root, n1, n2):
342 '''return the relative path from one place to another.
342 '''return the relative path from one place to another.
343 root should use os.sep to separate directories
343 root should use os.sep to separate directories
344 n1 should use os.sep to separate directories
344 n1 should use os.sep to separate directories
345 n2 should use "/" to separate directories
345 n2 should use "/" to separate directories
346 returns an os.sep-separated path.
346 returns an os.sep-separated path.
347
347
348 If n1 is a relative path, it's assumed it's
348 If n1 is a relative path, it's assumed it's
349 relative to root.
349 relative to root.
350 n2 should always be relative to root.
350 n2 should always be relative to root.
351 '''
351 '''
352 if not n1: return localpath(n2)
352 if not n1: return localpath(n2)
353 if os.path.isabs(n1):
353 if os.path.isabs(n1):
354 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
354 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
355 return os.path.join(root, localpath(n2))
355 return os.path.join(root, localpath(n2))
356 n2 = '/'.join((pconvert(root), n2))
356 n2 = '/'.join((pconvert(root), n2))
357 a, b = splitpath(n1), n2.split('/')
357 a, b = splitpath(n1), n2.split('/')
358 a.reverse()
358 a.reverse()
359 b.reverse()
359 b.reverse()
360 while a and b and a[-1] == b[-1]:
360 while a and b and a[-1] == b[-1]:
361 a.pop()
361 a.pop()
362 b.pop()
362 b.pop()
363 b.reverse()
363 b.reverse()
364 return os.sep.join((['..'] * len(a)) + b) or '.'
364 return os.sep.join((['..'] * len(a)) + b) or '.'
365
365
366 def canonpath(root, cwd, myname):
366 def canonpath(root, cwd, myname):
367 """return the canonical path of myname, given cwd and root"""
367 """return the canonical path of myname, given cwd and root"""
368 if root == os.sep:
368 if root == os.sep:
369 rootsep = os.sep
369 rootsep = os.sep
370 elif endswithsep(root):
370 elif endswithsep(root):
371 rootsep = root
371 rootsep = root
372 else:
372 else:
373 rootsep = root + os.sep
373 rootsep = root + os.sep
374 name = myname
374 name = myname
375 if not os.path.isabs(name):
375 if not os.path.isabs(name):
376 name = os.path.join(root, cwd, name)
376 name = os.path.join(root, cwd, name)
377 name = os.path.normpath(name)
377 name = os.path.normpath(name)
378 audit_path = path_auditor(root)
378 audit_path = path_auditor(root)
379 if name != rootsep and name.startswith(rootsep):
379 if name != rootsep and name.startswith(rootsep):
380 name = name[len(rootsep):]
380 name = name[len(rootsep):]
381 audit_path(name)
381 audit_path(name)
382 return pconvert(name)
382 return pconvert(name)
383 elif name == root:
383 elif name == root:
384 return ''
384 return ''
385 else:
385 else:
386 # Determine whether `name' is in the hierarchy at or beneath `root',
386 # Determine whether `name' is in the hierarchy at or beneath `root',
387 # by iterating name=dirname(name) until that causes no change (can't
387 # by iterating name=dirname(name) until that causes no change (can't
388 # check name == '/', because that doesn't work on windows). For each
388 # check name == '/', because that doesn't work on windows). For each
389 # `name', compare dev/inode numbers. If they match, the list `rel'
389 # `name', compare dev/inode numbers. If they match, the list `rel'
390 # holds the reversed list of components making up the relative file
390 # holds the reversed list of components making up the relative file
391 # name we want.
391 # name we want.
392 root_st = os.stat(root)
392 root_st = os.stat(root)
393 rel = []
393 rel = []
394 while True:
394 while True:
395 try:
395 try:
396 name_st = os.stat(name)
396 name_st = os.stat(name)
397 except OSError:
397 except OSError:
398 break
398 break
399 if samestat(name_st, root_st):
399 if samestat(name_st, root_st):
400 if not rel:
400 if not rel:
401 # name was actually the same as root (maybe a symlink)
401 # name was actually the same as root (maybe a symlink)
402 return ''
402 return ''
403 rel.reverse()
403 rel.reverse()
404 name = os.path.join(*rel)
404 name = os.path.join(*rel)
405 audit_path(name)
405 audit_path(name)
406 return pconvert(name)
406 return pconvert(name)
407 dirname, basename = os.path.split(name)
407 dirname, basename = os.path.split(name)
408 rel.append(basename)
408 rel.append(basename)
409 if dirname == name:
409 if dirname == name:
410 break
410 break
411 name = dirname
411 name = dirname
412
412
413 raise Abort('%s not under root' % myname)
413 raise Abort('%s not under root' % myname)
414
414
415 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
415 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
416 """build a function to match a set of file patterns
416 """build a function to match a set of file patterns
417
417
418 arguments:
418 arguments:
419 canonroot - the canonical root of the tree you're matching against
419 canonroot - the canonical root of the tree you're matching against
420 cwd - the current working directory, if relevant
420 cwd - the current working directory, if relevant
421 names - patterns to find
421 names - patterns to find
422 inc - patterns to include
422 inc - patterns to include
423 exc - patterns to exclude
423 exc - patterns to exclude
424 dflt_pat - if a pattern in names has no explicit type, assume this one
424 dflt_pat - if a pattern in names has no explicit type, assume this one
425 src - where these patterns came from (e.g. .hgignore)
425 src - where these patterns came from (e.g. .hgignore)
426
426
427 a pattern is one of:
427 a pattern is one of:
428 'glob:<glob>' - a glob relative to cwd
428 'glob:<glob>' - a glob relative to cwd
429 're:<regexp>' - a regular expression
429 're:<regexp>' - a regular expression
430 'path:<path>' - a path relative to canonroot
430 'path:<path>' - a path relative to canonroot
431 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
431 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
432 'relpath:<path>' - a path relative to cwd
432 'relpath:<path>' - a path relative to cwd
433 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
433 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
434 '<something>' - one of the cases above, selected by the dflt_pat argument
434 '<something>' - one of the cases above, selected by the dflt_pat argument
435
435
436 returns:
436 returns:
437 a 3-tuple containing
437 a 3-tuple containing
438 - list of roots (places where one should start a recursive walk of the fs);
438 - list of roots (places where one should start a recursive walk of the fs);
439 this often matches the explicit non-pattern names passed in, but also
439 this often matches the explicit non-pattern names passed in, but also
440 includes the initial part of glob: patterns that has no glob characters
440 includes the initial part of glob: patterns that has no glob characters
441 - a bool match(filename) function
441 - a bool match(filename) function
442 - a bool indicating if any patterns were passed in
442 - a bool indicating if any patterns were passed in
443 """
443 """
444
444
445 # a common case: no patterns at all
445 # a common case: no patterns at all
446 if not names and not inc and not exc:
446 if not names and not inc and not exc:
447 return [], always, False
447 return [], always, False
448
448
449 def contains_glob(name):
449 def contains_glob(name):
450 for c in name:
450 for c in name:
451 if c in _globchars: return True
451 if c in _globchars: return True
452 return False
452 return False
453
453
454 def regex(kind, name, tail):
454 def regex(kind, name, tail):
455 '''convert a pattern into a regular expression'''
455 '''convert a pattern into a regular expression'''
456 if not name:
456 if not name:
457 return ''
457 return ''
458 if kind == 're':
458 if kind == 're':
459 return name
459 return name
460 elif kind == 'path':
460 elif kind == 'path':
461 return '^' + re.escape(name) + '(?:/|$)'
461 return '^' + re.escape(name) + '(?:/|$)'
462 elif kind == 'relglob':
462 elif kind == 'relglob':
463 return globre(name, '(?:|.*/)', tail)
463 return globre(name, '(?:|.*/)', tail)
464 elif kind == 'relpath':
464 elif kind == 'relpath':
465 return re.escape(name) + '(?:/|$)'
465 return re.escape(name) + '(?:/|$)'
466 elif kind == 'relre':
466 elif kind == 'relre':
467 if name.startswith('^'):
467 if name.startswith('^'):
468 return name
468 return name
469 return '.*' + name
469 return '.*' + name
470 return globre(name, '', tail)
470 return globre(name, '', tail)
471
471
472 def matchfn(pats, tail):
472 def matchfn(pats, tail):
473 """build a matching function from a set of patterns"""
473 """build a matching function from a set of patterns"""
474 if not pats:
474 if not pats:
475 return
475 return
476 try:
476 try:
477 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
477 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
478 if len(pat) > 20000:
478 if len(pat) > 20000:
479 raise OverflowError()
479 raise OverflowError()
480 return re.compile(pat).match
480 return re.compile(pat).match
481 except OverflowError:
481 except OverflowError:
482 # We're using a Python with a tiny regex engine and we
482 # We're using a Python with a tiny regex engine and we
483 # made it explode, so we'll divide the pattern list in two
483 # made it explode, so we'll divide the pattern list in two
484 # until it works
484 # until it works
485 l = len(pats)
485 l = len(pats)
486 if l < 2:
486 if l < 2:
487 raise
487 raise
488 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
488 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
489 return lambda s: a(s) or b(s)
489 return lambda s: a(s) or b(s)
490 except re.error:
490 except re.error:
491 for k, p in pats:
491 for k, p in pats:
492 try:
492 try:
493 re.compile('(?:%s)' % regex(k, p, tail))
493 re.compile('(?:%s)' % regex(k, p, tail))
494 except re.error:
494 except re.error:
495 if src:
495 if src:
496 raise Abort("%s: invalid pattern (%s): %s" %
496 raise Abort("%s: invalid pattern (%s): %s" %
497 (src, k, p))
497 (src, k, p))
498 else:
498 else:
499 raise Abort("invalid pattern (%s): %s" % (k, p))
499 raise Abort("invalid pattern (%s): %s" % (k, p))
500 raise Abort("invalid pattern")
500 raise Abort("invalid pattern")
501
501
502 def globprefix(pat):
502 def globprefix(pat):
503 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
503 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
504 root = []
504 root = []
505 for p in pat.split('/'):
505 for p in pat.split('/'):
506 if contains_glob(p): break
506 if contains_glob(p): break
507 root.append(p)
507 root.append(p)
508 return '/'.join(root) or '.'
508 return '/'.join(root) or '.'
509
509
510 def normalizepats(names, default):
510 def normalizepats(names, default):
511 pats = []
511 pats = []
512 roots = []
512 roots = []
513 anypats = False
513 anypats = False
514 for kind, name in [patkind(p, default) for p in names]:
514 for kind, name in [patkind(p, default) for p in names]:
515 if kind in ('glob', 'relpath'):
515 if kind in ('glob', 'relpath'):
516 name = canonpath(canonroot, cwd, name)
516 name = canonpath(canonroot, cwd, name)
517 elif kind in ('relglob', 'path'):
517 elif kind in ('relglob', 'path'):
518 name = normpath(name)
518 name = normpath(name)
519
519
520 pats.append((kind, name))
520 pats.append((kind, name))
521
521
522 if kind in ('glob', 're', 'relglob', 'relre'):
522 if kind in ('glob', 're', 'relglob', 'relre'):
523 anypats = True
523 anypats = True
524
524
525 if kind == 'glob':
525 if kind == 'glob':
526 root = globprefix(name)
526 root = globprefix(name)
527 roots.append(root)
527 roots.append(root)
528 elif kind in ('relpath', 'path'):
528 elif kind in ('relpath', 'path'):
529 roots.append(name or '.')
529 roots.append(name or '.')
530 elif kind == 'relglob':
530 elif kind == 'relglob':
531 roots.append('.')
531 roots.append('.')
532 return roots, pats, anypats
532 return roots, pats, anypats
533
533
534 roots, pats, anypats = normalizepats(names, dflt_pat)
534 roots, pats, anypats = normalizepats(names, dflt_pat)
535
535
536 patmatch = matchfn(pats, '$') or always
536 patmatch = matchfn(pats, '$') or always
537 incmatch = always
537 incmatch = always
538 if inc:
538 if inc:
539 dummy, inckinds, dummy = normalizepats(inc, 'glob')
539 dummy, inckinds, dummy = normalizepats(inc, 'glob')
540 incmatch = matchfn(inckinds, '(?:/|$)')
540 incmatch = matchfn(inckinds, '(?:/|$)')
541 excmatch = lambda fn: False
541 excmatch = lambda fn: False
542 if exc:
542 if exc:
543 dummy, exckinds, dummy = normalizepats(exc, 'glob')
543 dummy, exckinds, dummy = normalizepats(exc, 'glob')
544 excmatch = matchfn(exckinds, '(?:/|$)')
544 excmatch = matchfn(exckinds, '(?:/|$)')
545
545
546 if not names and inc and not exc:
546 if not names and inc and not exc:
547 # common case: hgignore patterns
547 # common case: hgignore patterns
548 match = incmatch
548 match = incmatch
549 else:
549 else:
550 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
550 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
551
551
552 return (roots, match, (inc or exc or anypats) and True)
552 return (roots, match, (inc or exc or anypats) and True)
553
553
554 _hgexecutable = None
554 _hgexecutable = None
555
555
556 def main_is_frozen():
556 def main_is_frozen():
557 """return True if we are a frozen executable.
557 """return True if we are a frozen executable.
558
558
559 The code supports py2exe (most common, Windows only) and tools/freeze
559 The code supports py2exe (most common, Windows only) and tools/freeze
560 (portable, not much used).
560 (portable, not much used).
561 """
561 """
562 return (hasattr(sys, "frozen") or # new py2exe
562 return (hasattr(sys, "frozen") or # new py2exe
563 hasattr(sys, "importers") or # old py2exe
563 hasattr(sys, "importers") or # old py2exe
564 imp.is_frozen("__main__")) # tools/freeze
564 imp.is_frozen("__main__")) # tools/freeze
565
565
566 def hgexecutable():
566 def hgexecutable():
567 """return location of the 'hg' executable.
567 """return location of the 'hg' executable.
568
568
569 Defaults to $HG or 'hg' in the search path.
569 Defaults to $HG or 'hg' in the search path.
570 """
570 """
571 if _hgexecutable is None:
571 if _hgexecutable is None:
572 hg = os.environ.get('HG')
572 hg = os.environ.get('HG')
573 if hg:
573 if hg:
574 set_hgexecutable(hg)
574 set_hgexecutable(hg)
575 elif main_is_frozen():
575 elif main_is_frozen():
576 set_hgexecutable(sys.executable)
576 set_hgexecutable(sys.executable)
577 else:
577 else:
578 set_hgexecutable(find_exe('hg', 'hg'))
578 set_hgexecutable(find_exe('hg', 'hg'))
579 return _hgexecutable
579 return _hgexecutable
580
580
581 def set_hgexecutable(path):
581 def set_hgexecutable(path):
582 """set location of the 'hg' executable"""
582 """set location of the 'hg' executable"""
583 global _hgexecutable
583 global _hgexecutable
584 _hgexecutable = path
584 _hgexecutable = path
585
585
586 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
586 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
587 '''enhanced shell command execution.
587 '''enhanced shell command execution.
588 run with environment maybe modified, maybe in different dir.
588 run with environment maybe modified, maybe in different dir.
589
589
590 if command fails and onerr is None, return status. if ui object,
590 if command fails and onerr is None, return status. if ui object,
591 print error message and return status, else raise onerr object as
591 print error message and return status, else raise onerr object as
592 exception.'''
592 exception.'''
593 def py2shell(val):
593 def py2shell(val):
594 'convert python object into string that is useful to shell'
594 'convert python object into string that is useful to shell'
595 if val in (None, False):
595 if val in (None, False):
596 return '0'
596 return '0'
597 if val == True:
597 if val == True:
598 return '1'
598 return '1'
599 return str(val)
599 return str(val)
600 oldenv = {}
600 oldenv = {}
601 for k in environ:
601 for k in environ:
602 oldenv[k] = os.environ.get(k)
602 oldenv[k] = os.environ.get(k)
603 if cwd is not None:
603 if cwd is not None:
604 oldcwd = os.getcwd()
604 oldcwd = os.getcwd()
605 origcmd = cmd
605 origcmd = cmd
606 if os.name == 'nt':
606 if os.name == 'nt':
607 cmd = '"%s"' % cmd
607 cmd = '"%s"' % cmd
608 try:
608 try:
609 for k, v in environ.iteritems():
609 for k, v in environ.iteritems():
610 os.environ[k] = py2shell(v)
610 os.environ[k] = py2shell(v)
611 os.environ['HG'] = hgexecutable()
611 os.environ['HG'] = hgexecutable()
612 if cwd is not None and oldcwd != cwd:
612 if cwd is not None and oldcwd != cwd:
613 os.chdir(cwd)
613 os.chdir(cwd)
614 rc = os.system(cmd)
614 rc = os.system(cmd)
615 if sys.platform == 'OpenVMS' and rc & 1:
615 if sys.platform == 'OpenVMS' and rc & 1:
616 rc = 0
616 rc = 0
617 if rc and onerr:
617 if rc and onerr:
618 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
618 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
619 explain_exit(rc)[0])
619 explain_exit(rc)[0])
620 if errprefix:
620 if errprefix:
621 errmsg = '%s: %s' % (errprefix, errmsg)
621 errmsg = '%s: %s' % (errprefix, errmsg)
622 try:
622 try:
623 onerr.warn(errmsg + '\n')
623 onerr.warn(errmsg + '\n')
624 except AttributeError:
624 except AttributeError:
625 raise onerr(errmsg)
625 raise onerr(errmsg)
626 return rc
626 return rc
627 finally:
627 finally:
628 for k, v in oldenv.iteritems():
628 for k, v in oldenv.iteritems():
629 if v is None:
629 if v is None:
630 del os.environ[k]
630 del os.environ[k]
631 else:
631 else:
632 os.environ[k] = v
632 os.environ[k] = v
633 if cwd is not None and oldcwd != cwd:
633 if cwd is not None and oldcwd != cwd:
634 os.chdir(oldcwd)
634 os.chdir(oldcwd)
635
635
636 # os.path.lexists is not available on python2.3
636 # os.path.lexists is not available on python2.3
637 def lexists(filename):
637 def lexists(filename):
638 "test whether a file with this name exists. does not follow symlinks"
638 "test whether a file with this name exists. does not follow symlinks"
639 try:
639 try:
640 os.lstat(filename)
640 os.lstat(filename)
641 except:
641 except:
642 return False
642 return False
643 return True
643 return True
644
644
645 def rename(src, dst):
645 def rename(src, dst):
646 """forcibly rename a file"""
646 """forcibly rename a file"""
647 try:
647 try:
648 os.rename(src, dst)
648 os.rename(src, dst)
649 except OSError, err: # FIXME: check err (EEXIST ?)
649 except OSError, err: # FIXME: check err (EEXIST ?)
650 # on windows, rename to existing file is not allowed, so we
650 # on windows, rename to existing file is not allowed, so we
651 # must delete destination first. but if file is open, unlink
651 # must delete destination first. but if file is open, unlink
652 # schedules it for delete but does not delete it. rename
652 # schedules it for delete but does not delete it. rename
653 # happens immediately even for open files, so we create
653 # happens immediately even for open files, so we create
654 # temporary file, delete it, rename destination to that name,
654 # temporary file, delete it, rename destination to that name,
655 # then delete that. then rename is safe to do.
655 # then delete that. then rename is safe to do.
656 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
656 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
657 os.close(fd)
657 os.close(fd)
658 os.unlink(temp)
658 os.unlink(temp)
659 os.rename(dst, temp)
659 os.rename(dst, temp)
660 os.unlink(temp)
660 os.unlink(temp)
661 os.rename(src, dst)
661 os.rename(src, dst)
662
662
663 def unlink(f):
663 def unlink(f):
664 """unlink and remove the directory if it is empty"""
664 """unlink and remove the directory if it is empty"""
665 os.unlink(f)
665 os.unlink(f)
666 # try removing directories that might now be empty
666 # try removing directories that might now be empty
667 try:
667 try:
668 os.removedirs(os.path.dirname(f))
668 os.removedirs(os.path.dirname(f))
669 except OSError:
669 except OSError:
670 pass
670 pass
671
671
672 def copyfile(src, dest):
672 def copyfile(src, dest):
673 "copy a file, preserving mode"
673 "copy a file, preserving mode"
674 if os.path.islink(src):
674 if os.path.islink(src):
675 try:
675 try:
676 os.unlink(dest)
676 os.unlink(dest)
677 except:
677 except:
678 pass
678 pass
679 os.symlink(os.readlink(src), dest)
679 os.symlink(os.readlink(src), dest)
680 else:
680 else:
681 try:
681 try:
682 shutil.copyfile(src, dest)
682 shutil.copyfile(src, dest)
683 shutil.copymode(src, dest)
683 shutil.copymode(src, dest)
684 except shutil.Error, inst:
684 except shutil.Error, inst:
685 raise Abort(str(inst))
685 raise Abort(str(inst))
686
686
687 def copyfiles(src, dst, hardlink=None):
687 def copyfiles(src, dst, hardlink=None):
688 """Copy a directory tree using hardlinks if possible"""
688 """Copy a directory tree using hardlinks if possible"""
689
689
690 if hardlink is None:
690 if hardlink is None:
691 hardlink = (os.stat(src).st_dev ==
691 hardlink = (os.stat(src).st_dev ==
692 os.stat(os.path.dirname(dst)).st_dev)
692 os.stat(os.path.dirname(dst)).st_dev)
693
693
694 if os.path.isdir(src):
694 if os.path.isdir(src):
695 os.mkdir(dst)
695 os.mkdir(dst)
696 for name, kind in osutil.listdir(src):
696 for name, kind in osutil.listdir(src):
697 srcname = os.path.join(src, name)
697 srcname = os.path.join(src, name)
698 dstname = os.path.join(dst, name)
698 dstname = os.path.join(dst, name)
699 copyfiles(srcname, dstname, hardlink)
699 copyfiles(srcname, dstname, hardlink)
700 else:
700 else:
701 if hardlink:
701 if hardlink:
702 try:
702 try:
703 os_link(src, dst)
703 os_link(src, dst)
704 except (IOError, OSError):
704 except (IOError, OSError):
705 hardlink = False
705 hardlink = False
706 shutil.copy(src, dst)
706 shutil.copy(src, dst)
707 else:
707 else:
708 shutil.copy(src, dst)
708 shutil.copy(src, dst)
709
709
710 class path_auditor(object):
710 class path_auditor(object):
711 '''ensure that a filesystem path contains no banned components.
711 '''ensure that a filesystem path contains no banned components.
712 the following properties of a path are checked:
712 the following properties of a path are checked:
713
713
714 - under top-level .hg
714 - under top-level .hg
715 - starts at the root of a windows drive
715 - starts at the root of a windows drive
716 - contains ".."
716 - contains ".."
717 - traverses a symlink (e.g. a/symlink_here/b)
717 - traverses a symlink (e.g. a/symlink_here/b)
718 - inside a nested repository'''
718 - inside a nested repository'''
719
719
720 def __init__(self, root):
720 def __init__(self, root):
721 self.audited = set()
721 self.audited = set()
722 self.auditeddir = set()
722 self.auditeddir = set()
723 self.root = root
723 self.root = root
724
724
725 def __call__(self, path):
725 def __call__(self, path):
726 if path in self.audited:
726 if path in self.audited:
727 return
727 return
728 normpath = os.path.normcase(path)
728 normpath = os.path.normcase(path)
729 parts = splitpath(normpath)
729 parts = splitpath(normpath)
730 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
730 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
731 or os.pardir in parts):
731 or os.pardir in parts):
732 raise Abort(_("path contains illegal component: %s") % path)
732 raise Abort(_("path contains illegal component: %s") % path)
733 def check(prefix):
733 def check(prefix):
734 curpath = os.path.join(self.root, prefix)
734 curpath = os.path.join(self.root, prefix)
735 try:
735 try:
736 st = os.lstat(curpath)
736 st = os.lstat(curpath)
737 except OSError, err:
737 except OSError, err:
738 # EINVAL can be raised as invalid path syntax under win32.
738 # EINVAL can be raised as invalid path syntax under win32.
739 # They must be ignored for patterns can be checked too.
739 # They must be ignored for patterns can be checked too.
740 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
740 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
741 raise
741 raise
742 else:
742 else:
743 if stat.S_ISLNK(st.st_mode):
743 if stat.S_ISLNK(st.st_mode):
744 raise Abort(_('path %r traverses symbolic link %r') %
744 raise Abort(_('path %r traverses symbolic link %r') %
745 (path, prefix))
745 (path, prefix))
746 elif (stat.S_ISDIR(st.st_mode) and
746 elif (stat.S_ISDIR(st.st_mode) and
747 os.path.isdir(os.path.join(curpath, '.hg'))):
747 os.path.isdir(os.path.join(curpath, '.hg'))):
748 raise Abort(_('path %r is inside repo %r') %
748 raise Abort(_('path %r is inside repo %r') %
749 (path, prefix))
749 (path, prefix))
750 parts.pop()
750 parts.pop()
751 prefixes = []
751 prefixes = []
752 for n in range(len(parts)):
752 for n in range(len(parts)):
753 prefix = os.sep.join(parts)
753 prefix = os.sep.join(parts)
754 if prefix in self.auditeddir:
754 if prefix in self.auditeddir:
755 break
755 break
756 check(prefix)
756 check(prefix)
757 prefixes.append(prefix)
757 prefixes.append(prefix)
758 parts.pop()
758 parts.pop()
759
759
760 self.audited.add(path)
760 self.audited.add(path)
761 # only add prefixes to the cache after checking everything: we don't
761 # only add prefixes to the cache after checking everything: we don't
762 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
762 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
763 self.auditeddir.update(prefixes)
763 self.auditeddir.update(prefixes)
764
764
765 def _makelock_file(info, pathname):
765 def _makelock_file(info, pathname):
766 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
766 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
767 os.write(ld, info)
767 os.write(ld, info)
768 os.close(ld)
768 os.close(ld)
769
769
770 def _readlock_file(pathname):
770 def _readlock_file(pathname):
771 return posixfile(pathname).read()
771 return posixfile(pathname).read()
772
772
773 def nlinks(pathname):
773 def nlinks(pathname):
774 """Return number of hardlinks for the given file."""
774 """Return number of hardlinks for the given file."""
775 return os.lstat(pathname).st_nlink
775 return os.lstat(pathname).st_nlink
776
776
777 if hasattr(os, 'link'):
777 if hasattr(os, 'link'):
778 os_link = os.link
778 os_link = os.link
779 else:
779 else:
780 def os_link(src, dst):
780 def os_link(src, dst):
781 raise OSError(0, _("Hardlinks not supported"))
781 raise OSError(0, _("Hardlinks not supported"))
782
782
783 def fstat(fp):
783 def fstat(fp):
784 '''stat file object that may not have fileno method.'''
784 '''stat file object that may not have fileno method.'''
785 try:
785 try:
786 return os.fstat(fp.fileno())
786 return os.fstat(fp.fileno())
787 except AttributeError:
787 except AttributeError:
788 return os.stat(fp.name)
788 return os.stat(fp.name)
789
789
790 posixfile = file
790 posixfile = file
791
791
792 def openhardlinks():
792 def openhardlinks():
793 '''return true if it is safe to hold open file handles to hardlinks'''
793 '''return true if it is safe to hold open file handles to hardlinks'''
794 return True
794 return True
795
795
796 getuser_fallback = None
796 getuser_fallback = None
797
797
798 def getuser():
798 def getuser():
799 '''return name of current user'''
799 '''return name of current user'''
800 try:
800 try:
801 return getpass.getuser()
801 return getpass.getuser()
802 except ImportError:
802 except ImportError:
803 # import of pwd will fail on windows - try fallback
803 # import of pwd will fail on windows - try fallback
804 if getuser_fallback:
804 if getuser_fallback:
805 return getuser_fallback()
805 return getuser_fallback()
806 # raised if win32api not available
806 # raised if win32api not available
807 raise Abort(_('user name not available - set USERNAME '
807 raise Abort(_('user name not available - set USERNAME '
808 'environment variable'))
808 'environment variable'))
809
809
810 def username(uid=None):
810 def username(uid=None):
811 """Return the name of the user with the given uid.
811 """Return the name of the user with the given uid.
812
812
813 If uid is None, return the name of the current user."""
813 If uid is None, return the name of the current user."""
814 try:
814 try:
815 import pwd
815 import pwd
816 if uid is None:
816 if uid is None:
817 uid = os.getuid()
817 uid = os.getuid()
818 try:
818 try:
819 return pwd.getpwuid(uid)[0]
819 return pwd.getpwuid(uid)[0]
820 except KeyError:
820 except KeyError:
821 return str(uid)
821 return str(uid)
822 except ImportError:
822 except ImportError:
823 return None
823 return None
824
824
825 def groupname(gid=None):
825 def groupname(gid=None):
826 """Return the name of the group with the given gid.
826 """Return the name of the group with the given gid.
827
827
828 If gid is None, return the name of the current group."""
828 If gid is None, return the name of the current group."""
829 try:
829 try:
830 import grp
830 import grp
831 if gid is None:
831 if gid is None:
832 gid = os.getgid()
832 gid = os.getgid()
833 try:
833 try:
834 return grp.getgrgid(gid)[0]
834 return grp.getgrgid(gid)[0]
835 except KeyError:
835 except KeyError:
836 return str(gid)
836 return str(gid)
837 except ImportError:
837 except ImportError:
838 return None
838 return None
839
839
840 # File system features
840 # File system features
841
841
842 def checkfolding(path):
842 def checkfolding(path):
843 """
843 """
844 Check whether the given path is on a case-sensitive filesystem
844 Check whether the given path is on a case-sensitive filesystem
845
845
846 Requires a path (like /foo/.hg) ending with a foldable final
846 Requires a path (like /foo/.hg) ending with a foldable final
847 directory component.
847 directory component.
848 """
848 """
849 s1 = os.stat(path)
849 s1 = os.stat(path)
850 d, b = os.path.split(path)
850 d, b = os.path.split(path)
851 p2 = os.path.join(d, b.upper())
851 p2 = os.path.join(d, b.upper())
852 if path == p2:
852 if path == p2:
853 p2 = os.path.join(d, b.lower())
853 p2 = os.path.join(d, b.lower())
854 try:
854 try:
855 s2 = os.stat(p2)
855 s2 = os.stat(p2)
856 if s2 == s1:
856 if s2 == s1:
857 return False
857 return False
858 return True
858 return True
859 except:
859 except:
860 return True
860 return True
861
861
862 _fspathcache = {}
863 def fspath(name, root):
864 '''Get name in the case stored in the filesystem
865
866 The name is either relative to root, or it is an absolute path starting
867 with root. Note that this function is unnecessary, and should not be
868 called, for case-sensitive filesystems (simply because it's expensive).
869 '''
870 # If name is absolute, make it relative
871 if name.lower().startswith(root.lower()):
872 l = len(root)
873 if name[l] == os.sep or name[l] == os.altsep:
874 l = l + 1
875 name = name[l:]
876
877 if not os.path.exists(os.path.join(root, name)):
878 return None
879
880 seps = os.sep
881 if os.altsep:
882 seps = seps + os.altsep
883 # Protect backslashes. This gets silly very quickly.
884 seps.replace('\\','\\\\')
885 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
886 dir = os.path.normcase(os.path.normpath(root))
887 result = []
888 for part, sep in pattern.findall(name):
889 if sep:
890 result.append(sep)
891 continue
892
893 if dir not in _fspathcache:
894 _fspathcache[dir] = os.listdir(dir)
895 contents = _fspathcache[dir]
896
897 lpart = part.lower()
898 for n in contents:
899 if n.lower() == lpart:
900 result.append(n)
901 break
902 else:
903 # Cannot happen, as the file exists!
904 result.append(part)
905 dir = os.path.join(dir, lpart)
906
907 return ''.join(result)
908
862 def checkexec(path):
909 def checkexec(path):
863 """
910 """
864 Check whether the given path is on a filesystem with UNIX-like exec flags
911 Check whether the given path is on a filesystem with UNIX-like exec flags
865
912
866 Requires a directory (like /foo/.hg)
913 Requires a directory (like /foo/.hg)
867 """
914 """
868
915
869 # VFAT on some Linux versions can flip mode but it doesn't persist
916 # VFAT on some Linux versions can flip mode but it doesn't persist
870 # a FS remount. Frequently we can detect it if files are created
917 # a FS remount. Frequently we can detect it if files are created
871 # with exec bit on.
918 # with exec bit on.
872
919
873 try:
920 try:
874 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
921 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
875 fh, fn = tempfile.mkstemp("", "", path)
922 fh, fn = tempfile.mkstemp("", "", path)
876 try:
923 try:
877 os.close(fh)
924 os.close(fh)
878 m = os.stat(fn).st_mode & 0777
925 m = os.stat(fn).st_mode & 0777
879 new_file_has_exec = m & EXECFLAGS
926 new_file_has_exec = m & EXECFLAGS
880 os.chmod(fn, m ^ EXECFLAGS)
927 os.chmod(fn, m ^ EXECFLAGS)
881 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
928 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
882 finally:
929 finally:
883 os.unlink(fn)
930 os.unlink(fn)
884 except (IOError, OSError):
931 except (IOError, OSError):
885 # we don't care, the user probably won't be able to commit anyway
932 # we don't care, the user probably won't be able to commit anyway
886 return False
933 return False
887 return not (new_file_has_exec or exec_flags_cannot_flip)
934 return not (new_file_has_exec or exec_flags_cannot_flip)
888
935
889 def execfunc(path, fallback):
936 def execfunc(path, fallback):
890 '''return an is_exec() function with default to fallback'''
937 '''return an is_exec() function with default to fallback'''
891 if checkexec(path):
938 if checkexec(path):
892 return lambda x: is_exec(os.path.join(path, x))
939 return lambda x: is_exec(os.path.join(path, x))
893 return fallback
940 return fallback
894
941
895 def checklink(path):
942 def checklink(path):
896 """check whether the given path is on a symlink-capable filesystem"""
943 """check whether the given path is on a symlink-capable filesystem"""
897 # mktemp is not racy because symlink creation will fail if the
944 # mktemp is not racy because symlink creation will fail if the
898 # file already exists
945 # file already exists
899 name = tempfile.mktemp(dir=path)
946 name = tempfile.mktemp(dir=path)
900 try:
947 try:
901 os.symlink(".", name)
948 os.symlink(".", name)
902 os.unlink(name)
949 os.unlink(name)
903 return True
950 return True
904 except (OSError, AttributeError):
951 except (OSError, AttributeError):
905 return False
952 return False
906
953
907 def linkfunc(path, fallback):
954 def linkfunc(path, fallback):
908 '''return an is_link() function with default to fallback'''
955 '''return an is_link() function with default to fallback'''
909 if checklink(path):
956 if checklink(path):
910 return lambda x: os.path.islink(os.path.join(path, x))
957 return lambda x: os.path.islink(os.path.join(path, x))
911 return fallback
958 return fallback
912
959
913 _umask = os.umask(0)
960 _umask = os.umask(0)
914 os.umask(_umask)
961 os.umask(_umask)
915
962
916 def needbinarypatch():
963 def needbinarypatch():
917 """return True if patches should be applied in binary mode by default."""
964 """return True if patches should be applied in binary mode by default."""
918 return os.name == 'nt'
965 return os.name == 'nt'
919
966
920 def endswithsep(path):
967 def endswithsep(path):
921 '''Check path ends with os.sep or os.altsep.'''
968 '''Check path ends with os.sep or os.altsep.'''
922 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
969 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
923
970
924 def splitpath(path):
971 def splitpath(path):
925 '''Split path by os.sep.
972 '''Split path by os.sep.
926 Note that this function does not use os.altsep because this is
973 Note that this function does not use os.altsep because this is
927 an alternative of simple "xxx.split(os.sep)".
974 an alternative of simple "xxx.split(os.sep)".
928 It is recommended to use os.path.normpath() before using this
975 It is recommended to use os.path.normpath() before using this
929 function if need.'''
976 function if need.'''
930 return path.split(os.sep)
977 return path.split(os.sep)
931
978
932 def gui():
979 def gui():
933 '''Are we running in a GUI?'''
980 '''Are we running in a GUI?'''
934 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
981 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
935
982
936 def lookup_reg(key, name=None, scope=None):
983 def lookup_reg(key, name=None, scope=None):
937 return None
984 return None
938
985
939 # Platform specific variants
986 # Platform specific variants
940 if os.name == 'nt':
987 if os.name == 'nt':
941 import msvcrt
988 import msvcrt
942 nulldev = 'NUL:'
989 nulldev = 'NUL:'
943
990
944 class winstdout:
991 class winstdout:
945 '''stdout on windows misbehaves if sent through a pipe'''
992 '''stdout on windows misbehaves if sent through a pipe'''
946
993
947 def __init__(self, fp):
994 def __init__(self, fp):
948 self.fp = fp
995 self.fp = fp
949
996
950 def __getattr__(self, key):
997 def __getattr__(self, key):
951 return getattr(self.fp, key)
998 return getattr(self.fp, key)
952
999
953 def close(self):
1000 def close(self):
954 try:
1001 try:
955 self.fp.close()
1002 self.fp.close()
956 except: pass
1003 except: pass
957
1004
958 def write(self, s):
1005 def write(self, s):
959 try:
1006 try:
960 # This is workaround for "Not enough space" error on
1007 # This is workaround for "Not enough space" error on
961 # writing large size of data to console.
1008 # writing large size of data to console.
962 limit = 16000
1009 limit = 16000
963 l = len(s)
1010 l = len(s)
964 start = 0
1011 start = 0
965 while start < l:
1012 while start < l:
966 end = start + limit
1013 end = start + limit
967 self.fp.write(s[start:end])
1014 self.fp.write(s[start:end])
968 start = end
1015 start = end
969 except IOError, inst:
1016 except IOError, inst:
970 if inst.errno != 0: raise
1017 if inst.errno != 0: raise
971 self.close()
1018 self.close()
972 raise IOError(errno.EPIPE, 'Broken pipe')
1019 raise IOError(errno.EPIPE, 'Broken pipe')
973
1020
974 def flush(self):
1021 def flush(self):
975 try:
1022 try:
976 return self.fp.flush()
1023 return self.fp.flush()
977 except IOError, inst:
1024 except IOError, inst:
978 if inst.errno != errno.EINVAL: raise
1025 if inst.errno != errno.EINVAL: raise
979 self.close()
1026 self.close()
980 raise IOError(errno.EPIPE, 'Broken pipe')
1027 raise IOError(errno.EPIPE, 'Broken pipe')
981
1028
982 sys.stdout = winstdout(sys.stdout)
1029 sys.stdout = winstdout(sys.stdout)
983
1030
984 def _is_win_9x():
1031 def _is_win_9x():
985 '''return true if run on windows 95, 98 or me.'''
1032 '''return true if run on windows 95, 98 or me.'''
986 try:
1033 try:
987 return sys.getwindowsversion()[3] == 1
1034 return sys.getwindowsversion()[3] == 1
988 except AttributeError:
1035 except AttributeError:
989 return 'command' in os.environ.get('comspec', '')
1036 return 'command' in os.environ.get('comspec', '')
990
1037
991 def openhardlinks():
1038 def openhardlinks():
992 return not _is_win_9x and "win32api" in locals()
1039 return not _is_win_9x and "win32api" in locals()
993
1040
994 def system_rcpath():
1041 def system_rcpath():
995 try:
1042 try:
996 return system_rcpath_win32()
1043 return system_rcpath_win32()
997 except:
1044 except:
998 return [r'c:\mercurial\mercurial.ini']
1045 return [r'c:\mercurial\mercurial.ini']
999
1046
1000 def user_rcpath():
1047 def user_rcpath():
1001 '''return os-specific hgrc search path to the user dir'''
1048 '''return os-specific hgrc search path to the user dir'''
1002 try:
1049 try:
1003 path = user_rcpath_win32()
1050 path = user_rcpath_win32()
1004 except:
1051 except:
1005 home = os.path.expanduser('~')
1052 home = os.path.expanduser('~')
1006 path = [os.path.join(home, 'mercurial.ini'),
1053 path = [os.path.join(home, 'mercurial.ini'),
1007 os.path.join(home, '.hgrc')]
1054 os.path.join(home, '.hgrc')]
1008 userprofile = os.environ.get('USERPROFILE')
1055 userprofile = os.environ.get('USERPROFILE')
1009 if userprofile:
1056 if userprofile:
1010 path.append(os.path.join(userprofile, 'mercurial.ini'))
1057 path.append(os.path.join(userprofile, 'mercurial.ini'))
1011 path.append(os.path.join(userprofile, '.hgrc'))
1058 path.append(os.path.join(userprofile, '.hgrc'))
1012 return path
1059 return path
1013
1060
1014 def parse_patch_output(output_line):
1061 def parse_patch_output(output_line):
1015 """parses the output produced by patch and returns the file name"""
1062 """parses the output produced by patch and returns the file name"""
1016 pf = output_line[14:]
1063 pf = output_line[14:]
1017 if pf[0] == '`':
1064 if pf[0] == '`':
1018 pf = pf[1:-1] # Remove the quotes
1065 pf = pf[1:-1] # Remove the quotes
1019 return pf
1066 return pf
1020
1067
1021 def sshargs(sshcmd, host, user, port):
1068 def sshargs(sshcmd, host, user, port):
1022 '''Build argument list for ssh or Plink'''
1069 '''Build argument list for ssh or Plink'''
1023 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1070 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1024 args = user and ("%s@%s" % (user, host)) or host
1071 args = user and ("%s@%s" % (user, host)) or host
1025 return port and ("%s %s %s" % (args, pflag, port)) or args
1072 return port and ("%s %s %s" % (args, pflag, port)) or args
1026
1073
1027 def testpid(pid):
1074 def testpid(pid):
1028 '''return False if pid dead, True if running or not known'''
1075 '''return False if pid dead, True if running or not known'''
1029 return True
1076 return True
1030
1077
1031 def set_flags(f, flags):
1078 def set_flags(f, flags):
1032 pass
1079 pass
1033
1080
1034 def set_binary(fd):
1081 def set_binary(fd):
1035 # When run without console, pipes may expose invalid
1082 # When run without console, pipes may expose invalid
1036 # fileno(), usually set to -1.
1083 # fileno(), usually set to -1.
1037 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1084 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1038 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1085 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1039
1086
1040 def pconvert(path):
1087 def pconvert(path):
1041 return '/'.join(splitpath(path))
1088 return '/'.join(splitpath(path))
1042
1089
1043 def localpath(path):
1090 def localpath(path):
1044 return path.replace('/', '\\')
1091 return path.replace('/', '\\')
1045
1092
1046 def normpath(path):
1093 def normpath(path):
1047 return pconvert(os.path.normpath(path))
1094 return pconvert(os.path.normpath(path))
1048
1095
1049 makelock = _makelock_file
1096 makelock = _makelock_file
1050 readlock = _readlock_file
1097 readlock = _readlock_file
1051
1098
1052 def samestat(s1, s2):
1099 def samestat(s1, s2):
1053 return False
1100 return False
1054
1101
1055 # A sequence of backslashes is special iff it precedes a double quote:
1102 # A sequence of backslashes is special iff it precedes a double quote:
1056 # - if there's an even number of backslashes, the double quote is not
1103 # - if there's an even number of backslashes, the double quote is not
1057 # quoted (i.e. it ends the quoted region)
1104 # quoted (i.e. it ends the quoted region)
1058 # - if there's an odd number of backslashes, the double quote is quoted
1105 # - if there's an odd number of backslashes, the double quote is quoted
1059 # - in both cases, every pair of backslashes is unquoted into a single
1106 # - in both cases, every pair of backslashes is unquoted into a single
1060 # backslash
1107 # backslash
1061 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1108 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1062 # So, to quote a string, we must surround it in double quotes, double
1109 # So, to quote a string, we must surround it in double quotes, double
1063 # the number of backslashes that preceed double quotes and add another
1110 # the number of backslashes that preceed double quotes and add another
1064 # backslash before every double quote (being careful with the double
1111 # backslash before every double quote (being careful with the double
1065 # quote we've appended to the end)
1112 # quote we've appended to the end)
1066 _quotere = None
1113 _quotere = None
1067 def shellquote(s):
1114 def shellquote(s):
1068 global _quotere
1115 global _quotere
1069 if _quotere is None:
1116 if _quotere is None:
1070 _quotere = re.compile(r'(\\*)("|\\$)')
1117 _quotere = re.compile(r'(\\*)("|\\$)')
1071 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1118 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1072
1119
1073 def quotecommand(cmd):
1120 def quotecommand(cmd):
1074 """Build a command string suitable for os.popen* calls."""
1121 """Build a command string suitable for os.popen* calls."""
1075 # The extra quotes are needed because popen* runs the command
1122 # The extra quotes are needed because popen* runs the command
1076 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1123 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1077 return '"' + cmd + '"'
1124 return '"' + cmd + '"'
1078
1125
1079 def popen(command, mode='r'):
1126 def popen(command, mode='r'):
1080 # Work around "popen spawned process may not write to stdout
1127 # Work around "popen spawned process may not write to stdout
1081 # under windows"
1128 # under windows"
1082 # http://bugs.python.org/issue1366
1129 # http://bugs.python.org/issue1366
1083 command += " 2> %s" % nulldev
1130 command += " 2> %s" % nulldev
1084 return os.popen(quotecommand(command), mode)
1131 return os.popen(quotecommand(command), mode)
1085
1132
1086 def explain_exit(code):
1133 def explain_exit(code):
1087 return _("exited with status %d") % code, code
1134 return _("exited with status %d") % code, code
1088
1135
1089 # if you change this stub into a real check, please try to implement the
1136 # if you change this stub into a real check, please try to implement the
1090 # username and groupname functions above, too.
1137 # username and groupname functions above, too.
1091 def isowner(fp, st=None):
1138 def isowner(fp, st=None):
1092 return True
1139 return True
1093
1140
1094 def find_in_path(name, path, default=None):
1141 def find_in_path(name, path, default=None):
1095 '''find name in search path. path can be string (will be split
1142 '''find name in search path. path can be string (will be split
1096 with os.pathsep), or iterable thing that returns strings. if name
1143 with os.pathsep), or iterable thing that returns strings. if name
1097 found, return path to name. else return default. name is looked up
1144 found, return path to name. else return default. name is looked up
1098 using cmd.exe rules, using PATHEXT.'''
1145 using cmd.exe rules, using PATHEXT.'''
1099 if isinstance(path, str):
1146 if isinstance(path, str):
1100 path = path.split(os.pathsep)
1147 path = path.split(os.pathsep)
1101
1148
1102 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1149 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1103 pathext = pathext.lower().split(os.pathsep)
1150 pathext = pathext.lower().split(os.pathsep)
1104 isexec = os.path.splitext(name)[1].lower() in pathext
1151 isexec = os.path.splitext(name)[1].lower() in pathext
1105
1152
1106 for p in path:
1153 for p in path:
1107 p_name = os.path.join(p, name)
1154 p_name = os.path.join(p, name)
1108
1155
1109 if isexec and os.path.exists(p_name):
1156 if isexec and os.path.exists(p_name):
1110 return p_name
1157 return p_name
1111
1158
1112 for ext in pathext:
1159 for ext in pathext:
1113 p_name_ext = p_name + ext
1160 p_name_ext = p_name + ext
1114 if os.path.exists(p_name_ext):
1161 if os.path.exists(p_name_ext):
1115 return p_name_ext
1162 return p_name_ext
1116 return default
1163 return default
1117
1164
1118 def set_signal_handler():
1165 def set_signal_handler():
1119 try:
1166 try:
1120 set_signal_handler_win32()
1167 set_signal_handler_win32()
1121 except NameError:
1168 except NameError:
1122 pass
1169 pass
1123
1170
1124 try:
1171 try:
1125 # override functions with win32 versions if possible
1172 # override functions with win32 versions if possible
1126 from util_win32 import *
1173 from util_win32 import *
1127 if not _is_win_9x():
1174 if not _is_win_9x():
1128 posixfile = posixfile_nt
1175 posixfile = posixfile_nt
1129 except ImportError:
1176 except ImportError:
1130 pass
1177 pass
1131
1178
1132 else:
1179 else:
1133 nulldev = '/dev/null'
1180 nulldev = '/dev/null'
1134
1181
1135 def rcfiles(path):
1182 def rcfiles(path):
1136 rcs = [os.path.join(path, 'hgrc')]
1183 rcs = [os.path.join(path, 'hgrc')]
1137 rcdir = os.path.join(path, 'hgrc.d')
1184 rcdir = os.path.join(path, 'hgrc.d')
1138 try:
1185 try:
1139 rcs.extend([os.path.join(rcdir, f)
1186 rcs.extend([os.path.join(rcdir, f)
1140 for f, kind in osutil.listdir(rcdir)
1187 for f, kind in osutil.listdir(rcdir)
1141 if f.endswith(".rc")])
1188 if f.endswith(".rc")])
1142 except OSError:
1189 except OSError:
1143 pass
1190 pass
1144 return rcs
1191 return rcs
1145
1192
1146 def system_rcpath():
1193 def system_rcpath():
1147 path = []
1194 path = []
1148 # old mod_python does not set sys.argv
1195 # old mod_python does not set sys.argv
1149 if len(getattr(sys, 'argv', [])) > 0:
1196 if len(getattr(sys, 'argv', [])) > 0:
1150 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1197 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1151 '/../etc/mercurial'))
1198 '/../etc/mercurial'))
1152 path.extend(rcfiles('/etc/mercurial'))
1199 path.extend(rcfiles('/etc/mercurial'))
1153 return path
1200 return path
1154
1201
1155 def user_rcpath():
1202 def user_rcpath():
1156 return [os.path.expanduser('~/.hgrc')]
1203 return [os.path.expanduser('~/.hgrc')]
1157
1204
1158 def parse_patch_output(output_line):
1205 def parse_patch_output(output_line):
1159 """parses the output produced by patch and returns the file name"""
1206 """parses the output produced by patch and returns the file name"""
1160 pf = output_line[14:]
1207 pf = output_line[14:]
1161 if os.sys.platform == 'OpenVMS':
1208 if os.sys.platform == 'OpenVMS':
1162 if pf[0] == '`':
1209 if pf[0] == '`':
1163 pf = pf[1:-1] # Remove the quotes
1210 pf = pf[1:-1] # Remove the quotes
1164 else:
1211 else:
1165 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1212 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1166 pf = pf[1:-1] # Remove the quotes
1213 pf = pf[1:-1] # Remove the quotes
1167 return pf
1214 return pf
1168
1215
1169 def sshargs(sshcmd, host, user, port):
1216 def sshargs(sshcmd, host, user, port):
1170 '''Build argument list for ssh'''
1217 '''Build argument list for ssh'''
1171 args = user and ("%s@%s" % (user, host)) or host
1218 args = user and ("%s@%s" % (user, host)) or host
1172 return port and ("%s -p %s" % (args, port)) or args
1219 return port and ("%s -p %s" % (args, port)) or args
1173
1220
1174 def is_exec(f):
1221 def is_exec(f):
1175 """check whether a file is executable"""
1222 """check whether a file is executable"""
1176 return (os.lstat(f).st_mode & 0100 != 0)
1223 return (os.lstat(f).st_mode & 0100 != 0)
1177
1224
1178 def set_flags(f, flags):
1225 def set_flags(f, flags):
1179 s = os.lstat(f).st_mode
1226 s = os.lstat(f).st_mode
1180 x = "x" in flags
1227 x = "x" in flags
1181 l = "l" in flags
1228 l = "l" in flags
1182 if l:
1229 if l:
1183 if not stat.S_ISLNK(s):
1230 if not stat.S_ISLNK(s):
1184 # switch file to link
1231 # switch file to link
1185 data = file(f).read()
1232 data = file(f).read()
1186 os.unlink(f)
1233 os.unlink(f)
1187 os.symlink(data, f)
1234 os.symlink(data, f)
1188 # no chmod needed at this point
1235 # no chmod needed at this point
1189 return
1236 return
1190 if stat.S_ISLNK(s):
1237 if stat.S_ISLNK(s):
1191 # switch link to file
1238 # switch link to file
1192 data = os.readlink(f)
1239 data = os.readlink(f)
1193 os.unlink(f)
1240 os.unlink(f)
1194 file(f, "w").write(data)
1241 file(f, "w").write(data)
1195 s = 0666 & ~_umask # avoid restatting for chmod
1242 s = 0666 & ~_umask # avoid restatting for chmod
1196
1243
1197 sx = s & 0100
1244 sx = s & 0100
1198 if x and not sx:
1245 if x and not sx:
1199 # Turn on +x for every +r bit when making a file executable
1246 # Turn on +x for every +r bit when making a file executable
1200 # and obey umask.
1247 # and obey umask.
1201 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1248 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1202 elif not x and sx:
1249 elif not x and sx:
1203 # Turn off all +x bits
1250 # Turn off all +x bits
1204 os.chmod(f, s & 0666)
1251 os.chmod(f, s & 0666)
1205
1252
1206 def set_binary(fd):
1253 def set_binary(fd):
1207 pass
1254 pass
1208
1255
1209 def pconvert(path):
1256 def pconvert(path):
1210 return path
1257 return path
1211
1258
1212 def localpath(path):
1259 def localpath(path):
1213 return path
1260 return path
1214
1261
1215 normpath = os.path.normpath
1262 normpath = os.path.normpath
1216 samestat = os.path.samestat
1263 samestat = os.path.samestat
1217
1264
1218 def makelock(info, pathname):
1265 def makelock(info, pathname):
1219 try:
1266 try:
1220 os.symlink(info, pathname)
1267 os.symlink(info, pathname)
1221 except OSError, why:
1268 except OSError, why:
1222 if why.errno == errno.EEXIST:
1269 if why.errno == errno.EEXIST:
1223 raise
1270 raise
1224 else:
1271 else:
1225 _makelock_file(info, pathname)
1272 _makelock_file(info, pathname)
1226
1273
1227 def readlock(pathname):
1274 def readlock(pathname):
1228 try:
1275 try:
1229 return os.readlink(pathname)
1276 return os.readlink(pathname)
1230 except OSError, why:
1277 except OSError, why:
1231 if why.errno in (errno.EINVAL, errno.ENOSYS):
1278 if why.errno in (errno.EINVAL, errno.ENOSYS):
1232 return _readlock_file(pathname)
1279 return _readlock_file(pathname)
1233 else:
1280 else:
1234 raise
1281 raise
1235
1282
1236 def shellquote(s):
1283 def shellquote(s):
1237 if os.sys.platform == 'OpenVMS':
1284 if os.sys.platform == 'OpenVMS':
1238 return '"%s"' % s
1285 return '"%s"' % s
1239 else:
1286 else:
1240 return "'%s'" % s.replace("'", "'\\''")
1287 return "'%s'" % s.replace("'", "'\\''")
1241
1288
1242 def quotecommand(cmd):
1289 def quotecommand(cmd):
1243 return cmd
1290 return cmd
1244
1291
1245 def popen(command, mode='r'):
1292 def popen(command, mode='r'):
1246 return os.popen(command, mode)
1293 return os.popen(command, mode)
1247
1294
1248 def testpid(pid):
1295 def testpid(pid):
1249 '''return False if pid dead, True if running or not sure'''
1296 '''return False if pid dead, True if running or not sure'''
1250 if os.sys.platform == 'OpenVMS':
1297 if os.sys.platform == 'OpenVMS':
1251 return True
1298 return True
1252 try:
1299 try:
1253 os.kill(pid, 0)
1300 os.kill(pid, 0)
1254 return True
1301 return True
1255 except OSError, inst:
1302 except OSError, inst:
1256 return inst.errno != errno.ESRCH
1303 return inst.errno != errno.ESRCH
1257
1304
1258 def explain_exit(code):
1305 def explain_exit(code):
1259 """return a 2-tuple (desc, code) describing a process's status"""
1306 """return a 2-tuple (desc, code) describing a process's status"""
1260 if os.WIFEXITED(code):
1307 if os.WIFEXITED(code):
1261 val = os.WEXITSTATUS(code)
1308 val = os.WEXITSTATUS(code)
1262 return _("exited with status %d") % val, val
1309 return _("exited with status %d") % val, val
1263 elif os.WIFSIGNALED(code):
1310 elif os.WIFSIGNALED(code):
1264 val = os.WTERMSIG(code)
1311 val = os.WTERMSIG(code)
1265 return _("killed by signal %d") % val, val
1312 return _("killed by signal %d") % val, val
1266 elif os.WIFSTOPPED(code):
1313 elif os.WIFSTOPPED(code):
1267 val = os.WSTOPSIG(code)
1314 val = os.WSTOPSIG(code)
1268 return _("stopped by signal %d") % val, val
1315 return _("stopped by signal %d") % val, val
1269 raise ValueError(_("invalid exit code"))
1316 raise ValueError(_("invalid exit code"))
1270
1317
1271 def isowner(fp, st=None):
1318 def isowner(fp, st=None):
1272 """Return True if the file object f belongs to the current user.
1319 """Return True if the file object f belongs to the current user.
1273
1320
1274 The return value of a util.fstat(f) may be passed as the st argument.
1321 The return value of a util.fstat(f) may be passed as the st argument.
1275 """
1322 """
1276 if st is None:
1323 if st is None:
1277 st = fstat(fp)
1324 st = fstat(fp)
1278 return st.st_uid == os.getuid()
1325 return st.st_uid == os.getuid()
1279
1326
1280 def find_in_path(name, path, default=None):
1327 def find_in_path(name, path, default=None):
1281 '''find name in search path. path can be string (will be split
1328 '''find name in search path. path can be string (will be split
1282 with os.pathsep), or iterable thing that returns strings. if name
1329 with os.pathsep), or iterable thing that returns strings. if name
1283 found, return path to name. else return default.'''
1330 found, return path to name. else return default.'''
1284 if isinstance(path, str):
1331 if isinstance(path, str):
1285 path = path.split(os.pathsep)
1332 path = path.split(os.pathsep)
1286 for p in path:
1333 for p in path:
1287 p_name = os.path.join(p, name)
1334 p_name = os.path.join(p, name)
1288 if os.path.exists(p_name):
1335 if os.path.exists(p_name):
1289 return p_name
1336 return p_name
1290 return default
1337 return default
1291
1338
1292 def set_signal_handler():
1339 def set_signal_handler():
1293 pass
1340 pass
1294
1341
1295 def find_exe(name, default=None):
1342 def find_exe(name, default=None):
1296 '''find path of an executable.
1343 '''find path of an executable.
1297 if name contains a path component, return it as is. otherwise,
1344 if name contains a path component, return it as is. otherwise,
1298 use normal executable search path.'''
1345 use normal executable search path.'''
1299
1346
1300 if os.sep in name or sys.platform == 'OpenVMS':
1347 if os.sep in name or sys.platform == 'OpenVMS':
1301 # don't check the executable bit. if the file isn't
1348 # don't check the executable bit. if the file isn't
1302 # executable, whoever tries to actually run it will give a
1349 # executable, whoever tries to actually run it will give a
1303 # much more useful error message.
1350 # much more useful error message.
1304 return name
1351 return name
1305 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1352 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1306
1353
1307 def _buildencodefun():
1354 def _buildencodefun():
1308 e = '_'
1355 e = '_'
1309 win_reserved = [ord(x) for x in '\\:*?"<>|']
1356 win_reserved = [ord(x) for x in '\\:*?"<>|']
1310 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1357 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1311 for x in (range(32) + range(126, 256) + win_reserved):
1358 for x in (range(32) + range(126, 256) + win_reserved):
1312 cmap[chr(x)] = "~%02x" % x
1359 cmap[chr(x)] = "~%02x" % x
1313 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1360 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1314 cmap[chr(x)] = e + chr(x).lower()
1361 cmap[chr(x)] = e + chr(x).lower()
1315 dmap = {}
1362 dmap = {}
1316 for k, v in cmap.iteritems():
1363 for k, v in cmap.iteritems():
1317 dmap[v] = k
1364 dmap[v] = k
1318 def decode(s):
1365 def decode(s):
1319 i = 0
1366 i = 0
1320 while i < len(s):
1367 while i < len(s):
1321 for l in xrange(1, 4):
1368 for l in xrange(1, 4):
1322 try:
1369 try:
1323 yield dmap[s[i:i+l]]
1370 yield dmap[s[i:i+l]]
1324 i += l
1371 i += l
1325 break
1372 break
1326 except KeyError:
1373 except KeyError:
1327 pass
1374 pass
1328 else:
1375 else:
1329 raise KeyError
1376 raise KeyError
1330 return (lambda s: "".join([cmap[c] for c in s]),
1377 return (lambda s: "".join([cmap[c] for c in s]),
1331 lambda s: "".join(list(decode(s))))
1378 lambda s: "".join(list(decode(s))))
1332
1379
1333 encodefilename, decodefilename = _buildencodefun()
1380 encodefilename, decodefilename = _buildencodefun()
1334
1381
1335 def encodedopener(openerfn, fn):
1382 def encodedopener(openerfn, fn):
1336 def o(path, *args, **kw):
1383 def o(path, *args, **kw):
1337 return openerfn(fn(path), *args, **kw)
1384 return openerfn(fn(path), *args, **kw)
1338 return o
1385 return o
1339
1386
1340 def mktempcopy(name, emptyok=False, createmode=None):
1387 def mktempcopy(name, emptyok=False, createmode=None):
1341 """Create a temporary file with the same contents from name
1388 """Create a temporary file with the same contents from name
1342
1389
1343 The permission bits are copied from the original file.
1390 The permission bits are copied from the original file.
1344
1391
1345 If the temporary file is going to be truncated immediately, you
1392 If the temporary file is going to be truncated immediately, you
1346 can use emptyok=True as an optimization.
1393 can use emptyok=True as an optimization.
1347
1394
1348 Returns the name of the temporary file.
1395 Returns the name of the temporary file.
1349 """
1396 """
1350 d, fn = os.path.split(name)
1397 d, fn = os.path.split(name)
1351 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1398 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1352 os.close(fd)
1399 os.close(fd)
1353 # Temporary files are created with mode 0600, which is usually not
1400 # Temporary files are created with mode 0600, which is usually not
1354 # what we want. If the original file already exists, just copy
1401 # what we want. If the original file already exists, just copy
1355 # its mode. Otherwise, manually obey umask.
1402 # its mode. Otherwise, manually obey umask.
1356 try:
1403 try:
1357 st_mode = os.lstat(name).st_mode & 0777
1404 st_mode = os.lstat(name).st_mode & 0777
1358 except OSError, inst:
1405 except OSError, inst:
1359 if inst.errno != errno.ENOENT:
1406 if inst.errno != errno.ENOENT:
1360 raise
1407 raise
1361 st_mode = createmode
1408 st_mode = createmode
1362 if st_mode is None:
1409 if st_mode is None:
1363 st_mode = ~_umask
1410 st_mode = ~_umask
1364 st_mode &= 0666
1411 st_mode &= 0666
1365 os.chmod(temp, st_mode)
1412 os.chmod(temp, st_mode)
1366 if emptyok:
1413 if emptyok:
1367 return temp
1414 return temp
1368 try:
1415 try:
1369 try:
1416 try:
1370 ifp = posixfile(name, "rb")
1417 ifp = posixfile(name, "rb")
1371 except IOError, inst:
1418 except IOError, inst:
1372 if inst.errno == errno.ENOENT:
1419 if inst.errno == errno.ENOENT:
1373 return temp
1420 return temp
1374 if not getattr(inst, 'filename', None):
1421 if not getattr(inst, 'filename', None):
1375 inst.filename = name
1422 inst.filename = name
1376 raise
1423 raise
1377 ofp = posixfile(temp, "wb")
1424 ofp = posixfile(temp, "wb")
1378 for chunk in filechunkiter(ifp):
1425 for chunk in filechunkiter(ifp):
1379 ofp.write(chunk)
1426 ofp.write(chunk)
1380 ifp.close()
1427 ifp.close()
1381 ofp.close()
1428 ofp.close()
1382 except:
1429 except:
1383 try: os.unlink(temp)
1430 try: os.unlink(temp)
1384 except: pass
1431 except: pass
1385 raise
1432 raise
1386 return temp
1433 return temp
1387
1434
1388 class atomictempfile(posixfile):
1435 class atomictempfile(posixfile):
1389 """file-like object that atomically updates a file
1436 """file-like object that atomically updates a file
1390
1437
1391 All writes will be redirected to a temporary copy of the original
1438 All writes will be redirected to a temporary copy of the original
1392 file. When rename is called, the copy is renamed to the original
1439 file. When rename is called, the copy is renamed to the original
1393 name, making the changes visible.
1440 name, making the changes visible.
1394 """
1441 """
1395 def __init__(self, name, mode, createmode):
1442 def __init__(self, name, mode, createmode):
1396 self.__name = name
1443 self.__name = name
1397 self.temp = mktempcopy(name, emptyok=('w' in mode),
1444 self.temp = mktempcopy(name, emptyok=('w' in mode),
1398 createmode=createmode)
1445 createmode=createmode)
1399 posixfile.__init__(self, self.temp, mode)
1446 posixfile.__init__(self, self.temp, mode)
1400
1447
1401 def rename(self):
1448 def rename(self):
1402 if not self.closed:
1449 if not self.closed:
1403 posixfile.close(self)
1450 posixfile.close(self)
1404 rename(self.temp, localpath(self.__name))
1451 rename(self.temp, localpath(self.__name))
1405
1452
1406 def __del__(self):
1453 def __del__(self):
1407 if not self.closed:
1454 if not self.closed:
1408 try:
1455 try:
1409 os.unlink(self.temp)
1456 os.unlink(self.temp)
1410 except: pass
1457 except: pass
1411 posixfile.close(self)
1458 posixfile.close(self)
1412
1459
1413 def makedirs(name, mode=None):
1460 def makedirs(name, mode=None):
1414 """recursive directory creation with parent mode inheritance"""
1461 """recursive directory creation with parent mode inheritance"""
1415 try:
1462 try:
1416 os.mkdir(name)
1463 os.mkdir(name)
1417 if mode is not None:
1464 if mode is not None:
1418 os.chmod(name, mode)
1465 os.chmod(name, mode)
1419 return
1466 return
1420 except OSError, err:
1467 except OSError, err:
1421 if err.errno == errno.EEXIST:
1468 if err.errno == errno.EEXIST:
1422 return
1469 return
1423 if err.errno != errno.ENOENT:
1470 if err.errno != errno.ENOENT:
1424 raise
1471 raise
1425 parent = os.path.abspath(os.path.dirname(name))
1472 parent = os.path.abspath(os.path.dirname(name))
1426 makedirs(parent, mode)
1473 makedirs(parent, mode)
1427 makedirs(name, mode)
1474 makedirs(name, mode)
1428
1475
1429 class opener(object):
1476 class opener(object):
1430 """Open files relative to a base directory
1477 """Open files relative to a base directory
1431
1478
1432 This class is used to hide the details of COW semantics and
1479 This class is used to hide the details of COW semantics and
1433 remote file access from higher level code.
1480 remote file access from higher level code.
1434 """
1481 """
1435 def __init__(self, base, audit=True):
1482 def __init__(self, base, audit=True):
1436 self.base = base
1483 self.base = base
1437 if audit:
1484 if audit:
1438 self.audit_path = path_auditor(base)
1485 self.audit_path = path_auditor(base)
1439 else:
1486 else:
1440 self.audit_path = always
1487 self.audit_path = always
1441 self.createmode = None
1488 self.createmode = None
1442
1489
1443 def __getattr__(self, name):
1490 def __getattr__(self, name):
1444 if name == '_can_symlink':
1491 if name == '_can_symlink':
1445 self._can_symlink = checklink(self.base)
1492 self._can_symlink = checklink(self.base)
1446 return self._can_symlink
1493 return self._can_symlink
1447 raise AttributeError(name)
1494 raise AttributeError(name)
1448
1495
1449 def _fixfilemode(self, name):
1496 def _fixfilemode(self, name):
1450 if self.createmode is None:
1497 if self.createmode is None:
1451 return
1498 return
1452 os.chmod(name, self.createmode & 0666)
1499 os.chmod(name, self.createmode & 0666)
1453
1500
1454 def __call__(self, path, mode="r", text=False, atomictemp=False):
1501 def __call__(self, path, mode="r", text=False, atomictemp=False):
1455 self.audit_path(path)
1502 self.audit_path(path)
1456 f = os.path.join(self.base, path)
1503 f = os.path.join(self.base, path)
1457
1504
1458 if not text and "b" not in mode:
1505 if not text and "b" not in mode:
1459 mode += "b" # for that other OS
1506 mode += "b" # for that other OS
1460
1507
1461 nlink = -1
1508 nlink = -1
1462 if mode[0] != "r":
1509 if mode[0] != "r":
1463 try:
1510 try:
1464 nlink = nlinks(f)
1511 nlink = nlinks(f)
1465 except OSError:
1512 except OSError:
1466 nlink = 0
1513 nlink = 0
1467 d = os.path.dirname(f)
1514 d = os.path.dirname(f)
1468 if not os.path.isdir(d):
1515 if not os.path.isdir(d):
1469 makedirs(d, self.createmode)
1516 makedirs(d, self.createmode)
1470 if atomictemp:
1517 if atomictemp:
1471 return atomictempfile(f, mode, self.createmode)
1518 return atomictempfile(f, mode, self.createmode)
1472 if nlink > 1:
1519 if nlink > 1:
1473 rename(mktempcopy(f), f)
1520 rename(mktempcopy(f), f)
1474 fp = posixfile(f, mode)
1521 fp = posixfile(f, mode)
1475 if nlink == 0:
1522 if nlink == 0:
1476 self._fixfilemode(f)
1523 self._fixfilemode(f)
1477 return fp
1524 return fp
1478
1525
1479 def symlink(self, src, dst):
1526 def symlink(self, src, dst):
1480 self.audit_path(dst)
1527 self.audit_path(dst)
1481 linkname = os.path.join(self.base, dst)
1528 linkname = os.path.join(self.base, dst)
1482 try:
1529 try:
1483 os.unlink(linkname)
1530 os.unlink(linkname)
1484 except OSError:
1531 except OSError:
1485 pass
1532 pass
1486
1533
1487 dirname = os.path.dirname(linkname)
1534 dirname = os.path.dirname(linkname)
1488 if not os.path.exists(dirname):
1535 if not os.path.exists(dirname):
1489 makedirs(dirname, self.createmode)
1536 makedirs(dirname, self.createmode)
1490
1537
1491 if self._can_symlink:
1538 if self._can_symlink:
1492 try:
1539 try:
1493 os.symlink(src, linkname)
1540 os.symlink(src, linkname)
1494 except OSError, err:
1541 except OSError, err:
1495 raise OSError(err.errno, _('could not symlink to %r: %s') %
1542 raise OSError(err.errno, _('could not symlink to %r: %s') %
1496 (src, err.strerror), linkname)
1543 (src, err.strerror), linkname)
1497 else:
1544 else:
1498 f = self(dst, "w")
1545 f = self(dst, "w")
1499 f.write(src)
1546 f.write(src)
1500 f.close()
1547 f.close()
1501 self._fixfilemode(dst)
1548 self._fixfilemode(dst)
1502
1549
1503 class chunkbuffer(object):
1550 class chunkbuffer(object):
1504 """Allow arbitrary sized chunks of data to be efficiently read from an
1551 """Allow arbitrary sized chunks of data to be efficiently read from an
1505 iterator over chunks of arbitrary size."""
1552 iterator over chunks of arbitrary size."""
1506
1553
1507 def __init__(self, in_iter):
1554 def __init__(self, in_iter):
1508 """in_iter is the iterator that's iterating over the input chunks.
1555 """in_iter is the iterator that's iterating over the input chunks.
1509 targetsize is how big a buffer to try to maintain."""
1556 targetsize is how big a buffer to try to maintain."""
1510 self.iter = iter(in_iter)
1557 self.iter = iter(in_iter)
1511 self.buf = ''
1558 self.buf = ''
1512 self.targetsize = 2**16
1559 self.targetsize = 2**16
1513
1560
1514 def read(self, l):
1561 def read(self, l):
1515 """Read L bytes of data from the iterator of chunks of data.
1562 """Read L bytes of data from the iterator of chunks of data.
1516 Returns less than L bytes if the iterator runs dry."""
1563 Returns less than L bytes if the iterator runs dry."""
1517 if l > len(self.buf) and self.iter:
1564 if l > len(self.buf) and self.iter:
1518 # Clamp to a multiple of self.targetsize
1565 # Clamp to a multiple of self.targetsize
1519 targetsize = max(l, self.targetsize)
1566 targetsize = max(l, self.targetsize)
1520 collector = cStringIO.StringIO()
1567 collector = cStringIO.StringIO()
1521 collector.write(self.buf)
1568 collector.write(self.buf)
1522 collected = len(self.buf)
1569 collected = len(self.buf)
1523 for chunk in self.iter:
1570 for chunk in self.iter:
1524 collector.write(chunk)
1571 collector.write(chunk)
1525 collected += len(chunk)
1572 collected += len(chunk)
1526 if collected >= targetsize:
1573 if collected >= targetsize:
1527 break
1574 break
1528 if collected < targetsize:
1575 if collected < targetsize:
1529 self.iter = False
1576 self.iter = False
1530 self.buf = collector.getvalue()
1577 self.buf = collector.getvalue()
1531 if len(self.buf) == l:
1578 if len(self.buf) == l:
1532 s, self.buf = str(self.buf), ''
1579 s, self.buf = str(self.buf), ''
1533 else:
1580 else:
1534 s, self.buf = self.buf[:l], buffer(self.buf, l)
1581 s, self.buf = self.buf[:l], buffer(self.buf, l)
1535 return s
1582 return s
1536
1583
1537 def filechunkiter(f, size=65536, limit=None):
1584 def filechunkiter(f, size=65536, limit=None):
1538 """Create a generator that produces the data in the file size
1585 """Create a generator that produces the data in the file size
1539 (default 65536) bytes at a time, up to optional limit (default is
1586 (default 65536) bytes at a time, up to optional limit (default is
1540 to read all data). Chunks may be less than size bytes if the
1587 to read all data). Chunks may be less than size bytes if the
1541 chunk is the last chunk in the file, or the file is a socket or
1588 chunk is the last chunk in the file, or the file is a socket or
1542 some other type of file that sometimes reads less data than is
1589 some other type of file that sometimes reads less data than is
1543 requested."""
1590 requested."""
1544 assert size >= 0
1591 assert size >= 0
1545 assert limit is None or limit >= 0
1592 assert limit is None or limit >= 0
1546 while True:
1593 while True:
1547 if limit is None: nbytes = size
1594 if limit is None: nbytes = size
1548 else: nbytes = min(limit, size)
1595 else: nbytes = min(limit, size)
1549 s = nbytes and f.read(nbytes)
1596 s = nbytes and f.read(nbytes)
1550 if not s: break
1597 if not s: break
1551 if limit: limit -= len(s)
1598 if limit: limit -= len(s)
1552 yield s
1599 yield s
1553
1600
1554 def makedate():
1601 def makedate():
1555 lt = time.localtime()
1602 lt = time.localtime()
1556 if lt[8] == 1 and time.daylight:
1603 if lt[8] == 1 and time.daylight:
1557 tz = time.altzone
1604 tz = time.altzone
1558 else:
1605 else:
1559 tz = time.timezone
1606 tz = time.timezone
1560 return time.mktime(lt), tz
1607 return time.mktime(lt), tz
1561
1608
1562 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1609 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1563 """represent a (unixtime, offset) tuple as a localized time.
1610 """represent a (unixtime, offset) tuple as a localized time.
1564 unixtime is seconds since the epoch, and offset is the time zone's
1611 unixtime is seconds since the epoch, and offset is the time zone's
1565 number of seconds away from UTC. if timezone is false, do not
1612 number of seconds away from UTC. if timezone is false, do not
1566 append time zone to string."""
1613 append time zone to string."""
1567 t, tz = date or makedate()
1614 t, tz = date or makedate()
1568 if "%1" in format or "%2" in format:
1615 if "%1" in format or "%2" in format:
1569 sign = (tz > 0) and "-" or "+"
1616 sign = (tz > 0) and "-" or "+"
1570 minutes = abs(tz) / 60
1617 minutes = abs(tz) / 60
1571 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1618 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1572 format = format.replace("%2", "%02d" % (minutes % 60))
1619 format = format.replace("%2", "%02d" % (minutes % 60))
1573 s = time.strftime(format, time.gmtime(float(t) - tz))
1620 s = time.strftime(format, time.gmtime(float(t) - tz))
1574 return s
1621 return s
1575
1622
1576 def shortdate(date=None):
1623 def shortdate(date=None):
1577 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1624 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1578 return datestr(date, format='%Y-%m-%d')
1625 return datestr(date, format='%Y-%m-%d')
1579
1626
1580 def strdate(string, format, defaults=[]):
1627 def strdate(string, format, defaults=[]):
1581 """parse a localized time string and return a (unixtime, offset) tuple.
1628 """parse a localized time string and return a (unixtime, offset) tuple.
1582 if the string cannot be parsed, ValueError is raised."""
1629 if the string cannot be parsed, ValueError is raised."""
1583 def timezone(string):
1630 def timezone(string):
1584 tz = string.split()[-1]
1631 tz = string.split()[-1]
1585 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1632 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1586 sign = (tz[0] == "+") and 1 or -1
1633 sign = (tz[0] == "+") and 1 or -1
1587 hours = int(tz[1:3])
1634 hours = int(tz[1:3])
1588 minutes = int(tz[3:5])
1635 minutes = int(tz[3:5])
1589 return -sign * (hours * 60 + minutes) * 60
1636 return -sign * (hours * 60 + minutes) * 60
1590 if tz == "GMT" or tz == "UTC":
1637 if tz == "GMT" or tz == "UTC":
1591 return 0
1638 return 0
1592 return None
1639 return None
1593
1640
1594 # NOTE: unixtime = localunixtime + offset
1641 # NOTE: unixtime = localunixtime + offset
1595 offset, date = timezone(string), string
1642 offset, date = timezone(string), string
1596 if offset != None:
1643 if offset != None:
1597 date = " ".join(string.split()[:-1])
1644 date = " ".join(string.split()[:-1])
1598
1645
1599 # add missing elements from defaults
1646 # add missing elements from defaults
1600 for part in defaults:
1647 for part in defaults:
1601 found = [True for p in part if ("%"+p) in format]
1648 found = [True for p in part if ("%"+p) in format]
1602 if not found:
1649 if not found:
1603 date += "@" + defaults[part]
1650 date += "@" + defaults[part]
1604 format += "@%" + part[0]
1651 format += "@%" + part[0]
1605
1652
1606 timetuple = time.strptime(date, format)
1653 timetuple = time.strptime(date, format)
1607 localunixtime = int(calendar.timegm(timetuple))
1654 localunixtime = int(calendar.timegm(timetuple))
1608 if offset is None:
1655 if offset is None:
1609 # local timezone
1656 # local timezone
1610 unixtime = int(time.mktime(timetuple))
1657 unixtime = int(time.mktime(timetuple))
1611 offset = unixtime - localunixtime
1658 offset = unixtime - localunixtime
1612 else:
1659 else:
1613 unixtime = localunixtime + offset
1660 unixtime = localunixtime + offset
1614 return unixtime, offset
1661 return unixtime, offset
1615
1662
1616 def parsedate(date, formats=None, defaults=None):
1663 def parsedate(date, formats=None, defaults=None):
1617 """parse a localized date/time string and return a (unixtime, offset) tuple.
1664 """parse a localized date/time string and return a (unixtime, offset) tuple.
1618
1665
1619 The date may be a "unixtime offset" string or in one of the specified
1666 The date may be a "unixtime offset" string or in one of the specified
1620 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1667 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1621 """
1668 """
1622 if not date:
1669 if not date:
1623 return 0, 0
1670 return 0, 0
1624 if isinstance(date, tuple) and len(date) == 2:
1671 if isinstance(date, tuple) and len(date) == 2:
1625 return date
1672 return date
1626 if not formats:
1673 if not formats:
1627 formats = defaultdateformats
1674 formats = defaultdateformats
1628 date = date.strip()
1675 date = date.strip()
1629 try:
1676 try:
1630 when, offset = map(int, date.split(' '))
1677 when, offset = map(int, date.split(' '))
1631 except ValueError:
1678 except ValueError:
1632 # fill out defaults
1679 # fill out defaults
1633 if not defaults:
1680 if not defaults:
1634 defaults = {}
1681 defaults = {}
1635 now = makedate()
1682 now = makedate()
1636 for part in "d mb yY HI M S".split():
1683 for part in "d mb yY HI M S".split():
1637 if part not in defaults:
1684 if part not in defaults:
1638 if part[0] in "HMS":
1685 if part[0] in "HMS":
1639 defaults[part] = "00"
1686 defaults[part] = "00"
1640 else:
1687 else:
1641 defaults[part] = datestr(now, "%" + part[0])
1688 defaults[part] = datestr(now, "%" + part[0])
1642
1689
1643 for format in formats:
1690 for format in formats:
1644 try:
1691 try:
1645 when, offset = strdate(date, format, defaults)
1692 when, offset = strdate(date, format, defaults)
1646 except (ValueError, OverflowError):
1693 except (ValueError, OverflowError):
1647 pass
1694 pass
1648 else:
1695 else:
1649 break
1696 break
1650 else:
1697 else:
1651 raise Abort(_('invalid date: %r ') % date)
1698 raise Abort(_('invalid date: %r ') % date)
1652 # validate explicit (probably user-specified) date and
1699 # validate explicit (probably user-specified) date and
1653 # time zone offset. values must fit in signed 32 bits for
1700 # time zone offset. values must fit in signed 32 bits for
1654 # current 32-bit linux runtimes. timezones go from UTC-12
1701 # current 32-bit linux runtimes. timezones go from UTC-12
1655 # to UTC+14
1702 # to UTC+14
1656 if abs(when) > 0x7fffffff:
1703 if abs(when) > 0x7fffffff:
1657 raise Abort(_('date exceeds 32 bits: %d') % when)
1704 raise Abort(_('date exceeds 32 bits: %d') % when)
1658 if offset < -50400 or offset > 43200:
1705 if offset < -50400 or offset > 43200:
1659 raise Abort(_('impossible time zone offset: %d') % offset)
1706 raise Abort(_('impossible time zone offset: %d') % offset)
1660 return when, offset
1707 return when, offset
1661
1708
1662 def matchdate(date):
1709 def matchdate(date):
1663 """Return a function that matches a given date match specifier
1710 """Return a function that matches a given date match specifier
1664
1711
1665 Formats include:
1712 Formats include:
1666
1713
1667 '{date}' match a given date to the accuracy provided
1714 '{date}' match a given date to the accuracy provided
1668
1715
1669 '<{date}' on or before a given date
1716 '<{date}' on or before a given date
1670
1717
1671 '>{date}' on or after a given date
1718 '>{date}' on or after a given date
1672
1719
1673 """
1720 """
1674
1721
1675 def lower(date):
1722 def lower(date):
1676 d = dict(mb="1", d="1")
1723 d = dict(mb="1", d="1")
1677 return parsedate(date, extendeddateformats, d)[0]
1724 return parsedate(date, extendeddateformats, d)[0]
1678
1725
1679 def upper(date):
1726 def upper(date):
1680 d = dict(mb="12", HI="23", M="59", S="59")
1727 d = dict(mb="12", HI="23", M="59", S="59")
1681 for days in "31 30 29".split():
1728 for days in "31 30 29".split():
1682 try:
1729 try:
1683 d["d"] = days
1730 d["d"] = days
1684 return parsedate(date, extendeddateformats, d)[0]
1731 return parsedate(date, extendeddateformats, d)[0]
1685 except:
1732 except:
1686 pass
1733 pass
1687 d["d"] = "28"
1734 d["d"] = "28"
1688 return parsedate(date, extendeddateformats, d)[0]
1735 return parsedate(date, extendeddateformats, d)[0]
1689
1736
1690 if date[0] == "<":
1737 if date[0] == "<":
1691 when = upper(date[1:])
1738 when = upper(date[1:])
1692 return lambda x: x <= when
1739 return lambda x: x <= when
1693 elif date[0] == ">":
1740 elif date[0] == ">":
1694 when = lower(date[1:])
1741 when = lower(date[1:])
1695 return lambda x: x >= when
1742 return lambda x: x >= when
1696 elif date[0] == "-":
1743 elif date[0] == "-":
1697 try:
1744 try:
1698 days = int(date[1:])
1745 days = int(date[1:])
1699 except ValueError:
1746 except ValueError:
1700 raise Abort(_("invalid day spec: %s") % date[1:])
1747 raise Abort(_("invalid day spec: %s") % date[1:])
1701 when = makedate()[0] - days * 3600 * 24
1748 when = makedate()[0] - days * 3600 * 24
1702 return lambda x: x >= when
1749 return lambda x: x >= when
1703 elif " to " in date:
1750 elif " to " in date:
1704 a, b = date.split(" to ")
1751 a, b = date.split(" to ")
1705 start, stop = lower(a), upper(b)
1752 start, stop = lower(a), upper(b)
1706 return lambda x: x >= start and x <= stop
1753 return lambda x: x >= start and x <= stop
1707 else:
1754 else:
1708 start, stop = lower(date), upper(date)
1755 start, stop = lower(date), upper(date)
1709 return lambda x: x >= start and x <= stop
1756 return lambda x: x >= start and x <= stop
1710
1757
1711 def shortuser(user):
1758 def shortuser(user):
1712 """Return a short representation of a user name or email address."""
1759 """Return a short representation of a user name or email address."""
1713 f = user.find('@')
1760 f = user.find('@')
1714 if f >= 0:
1761 if f >= 0:
1715 user = user[:f]
1762 user = user[:f]
1716 f = user.find('<')
1763 f = user.find('<')
1717 if f >= 0:
1764 if f >= 0:
1718 user = user[f+1:]
1765 user = user[f+1:]
1719 f = user.find(' ')
1766 f = user.find(' ')
1720 if f >= 0:
1767 if f >= 0:
1721 user = user[:f]
1768 user = user[:f]
1722 f = user.find('.')
1769 f = user.find('.')
1723 if f >= 0:
1770 if f >= 0:
1724 user = user[:f]
1771 user = user[:f]
1725 return user
1772 return user
1726
1773
1727 def email(author):
1774 def email(author):
1728 '''get email of author.'''
1775 '''get email of author.'''
1729 r = author.find('>')
1776 r = author.find('>')
1730 if r == -1: r = None
1777 if r == -1: r = None
1731 return author[author.find('<')+1:r]
1778 return author[author.find('<')+1:r]
1732
1779
1733 def ellipsis(text, maxlength=400):
1780 def ellipsis(text, maxlength=400):
1734 """Trim string to at most maxlength (default: 400) characters."""
1781 """Trim string to at most maxlength (default: 400) characters."""
1735 if len(text) <= maxlength:
1782 if len(text) <= maxlength:
1736 return text
1783 return text
1737 else:
1784 else:
1738 return "%s..." % (text[:maxlength-3])
1785 return "%s..." % (text[:maxlength-3])
1739
1786
1740 def walkrepos(path, followsym=False, seen_dirs=None):
1787 def walkrepos(path, followsym=False, seen_dirs=None):
1741 '''yield every hg repository under path, recursively.'''
1788 '''yield every hg repository under path, recursively.'''
1742 def errhandler(err):
1789 def errhandler(err):
1743 if err.filename == path:
1790 if err.filename == path:
1744 raise err
1791 raise err
1745 if followsym and hasattr(os.path, 'samestat'):
1792 if followsym and hasattr(os.path, 'samestat'):
1746 def _add_dir_if_not_there(dirlst, dirname):
1793 def _add_dir_if_not_there(dirlst, dirname):
1747 match = False
1794 match = False
1748 samestat = os.path.samestat
1795 samestat = os.path.samestat
1749 dirstat = os.stat(dirname)
1796 dirstat = os.stat(dirname)
1750 for lstdirstat in dirlst:
1797 for lstdirstat in dirlst:
1751 if samestat(dirstat, lstdirstat):
1798 if samestat(dirstat, lstdirstat):
1752 match = True
1799 match = True
1753 break
1800 break
1754 if not match:
1801 if not match:
1755 dirlst.append(dirstat)
1802 dirlst.append(dirstat)
1756 return not match
1803 return not match
1757 else:
1804 else:
1758 followsym = False
1805 followsym = False
1759
1806
1760 if (seen_dirs is None) and followsym:
1807 if (seen_dirs is None) and followsym:
1761 seen_dirs = []
1808 seen_dirs = []
1762 _add_dir_if_not_there(seen_dirs, path)
1809 _add_dir_if_not_there(seen_dirs, path)
1763 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1810 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1764 if '.hg' in dirs:
1811 if '.hg' in dirs:
1765 dirs[:] = [] # don't descend further
1812 dirs[:] = [] # don't descend further
1766 yield root # found a repository
1813 yield root # found a repository
1767 qroot = os.path.join(root, '.hg', 'patches')
1814 qroot = os.path.join(root, '.hg', 'patches')
1768 if os.path.isdir(os.path.join(qroot, '.hg')):
1815 if os.path.isdir(os.path.join(qroot, '.hg')):
1769 yield qroot # we have a patch queue repo here
1816 yield qroot # we have a patch queue repo here
1770 elif followsym:
1817 elif followsym:
1771 newdirs = []
1818 newdirs = []
1772 for d in dirs:
1819 for d in dirs:
1773 fname = os.path.join(root, d)
1820 fname = os.path.join(root, d)
1774 if _add_dir_if_not_there(seen_dirs, fname):
1821 if _add_dir_if_not_there(seen_dirs, fname):
1775 if os.path.islink(fname):
1822 if os.path.islink(fname):
1776 for hgname in walkrepos(fname, True, seen_dirs):
1823 for hgname in walkrepos(fname, True, seen_dirs):
1777 yield hgname
1824 yield hgname
1778 else:
1825 else:
1779 newdirs.append(d)
1826 newdirs.append(d)
1780 dirs[:] = newdirs
1827 dirs[:] = newdirs
1781
1828
1782 _rcpath = None
1829 _rcpath = None
1783
1830
1784 def os_rcpath():
1831 def os_rcpath():
1785 '''return default os-specific hgrc search path'''
1832 '''return default os-specific hgrc search path'''
1786 path = system_rcpath()
1833 path = system_rcpath()
1787 path.extend(user_rcpath())
1834 path.extend(user_rcpath())
1788 path = [os.path.normpath(f) for f in path]
1835 path = [os.path.normpath(f) for f in path]
1789 return path
1836 return path
1790
1837
1791 def rcpath():
1838 def rcpath():
1792 '''return hgrc search path. if env var HGRCPATH is set, use it.
1839 '''return hgrc search path. if env var HGRCPATH is set, use it.
1793 for each item in path, if directory, use files ending in .rc,
1840 for each item in path, if directory, use files ending in .rc,
1794 else use item.
1841 else use item.
1795 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1842 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1796 if no HGRCPATH, use default os-specific path.'''
1843 if no HGRCPATH, use default os-specific path.'''
1797 global _rcpath
1844 global _rcpath
1798 if _rcpath is None:
1845 if _rcpath is None:
1799 if 'HGRCPATH' in os.environ:
1846 if 'HGRCPATH' in os.environ:
1800 _rcpath = []
1847 _rcpath = []
1801 for p in os.environ['HGRCPATH'].split(os.pathsep):
1848 for p in os.environ['HGRCPATH'].split(os.pathsep):
1802 if not p: continue
1849 if not p: continue
1803 if os.path.isdir(p):
1850 if os.path.isdir(p):
1804 for f, kind in osutil.listdir(p):
1851 for f, kind in osutil.listdir(p):
1805 if f.endswith('.rc'):
1852 if f.endswith('.rc'):
1806 _rcpath.append(os.path.join(p, f))
1853 _rcpath.append(os.path.join(p, f))
1807 else:
1854 else:
1808 _rcpath.append(p)
1855 _rcpath.append(p)
1809 else:
1856 else:
1810 _rcpath = os_rcpath()
1857 _rcpath = os_rcpath()
1811 return _rcpath
1858 return _rcpath
1812
1859
1813 def bytecount(nbytes):
1860 def bytecount(nbytes):
1814 '''return byte count formatted as readable string, with units'''
1861 '''return byte count formatted as readable string, with units'''
1815
1862
1816 units = (
1863 units = (
1817 (100, 1<<30, _('%.0f GB')),
1864 (100, 1<<30, _('%.0f GB')),
1818 (10, 1<<30, _('%.1f GB')),
1865 (10, 1<<30, _('%.1f GB')),
1819 (1, 1<<30, _('%.2f GB')),
1866 (1, 1<<30, _('%.2f GB')),
1820 (100, 1<<20, _('%.0f MB')),
1867 (100, 1<<20, _('%.0f MB')),
1821 (10, 1<<20, _('%.1f MB')),
1868 (10, 1<<20, _('%.1f MB')),
1822 (1, 1<<20, _('%.2f MB')),
1869 (1, 1<<20, _('%.2f MB')),
1823 (100, 1<<10, _('%.0f KB')),
1870 (100, 1<<10, _('%.0f KB')),
1824 (10, 1<<10, _('%.1f KB')),
1871 (10, 1<<10, _('%.1f KB')),
1825 (1, 1<<10, _('%.2f KB')),
1872 (1, 1<<10, _('%.2f KB')),
1826 (1, 1, _('%.0f bytes')),
1873 (1, 1, _('%.0f bytes')),
1827 )
1874 )
1828
1875
1829 for multiplier, divisor, format in units:
1876 for multiplier, divisor, format in units:
1830 if nbytes >= divisor * multiplier:
1877 if nbytes >= divisor * multiplier:
1831 return format % (nbytes / float(divisor))
1878 return format % (nbytes / float(divisor))
1832 return units[-1][2] % nbytes
1879 return units[-1][2] % nbytes
1833
1880
1834 def drop_scheme(scheme, path):
1881 def drop_scheme(scheme, path):
1835 sc = scheme + ':'
1882 sc = scheme + ':'
1836 if path.startswith(sc):
1883 if path.startswith(sc):
1837 path = path[len(sc):]
1884 path = path[len(sc):]
1838 if path.startswith('//'):
1885 if path.startswith('//'):
1839 path = path[2:]
1886 path = path[2:]
1840 return path
1887 return path
1841
1888
1842 def uirepr(s):
1889 def uirepr(s):
1843 # Avoid double backslash in Windows path repr()
1890 # Avoid double backslash in Windows path repr()
1844 return repr(s).replace('\\\\', '\\')
1891 return repr(s).replace('\\\\', '\\')
1845
1892
1846 def hidepassword(url):
1893 def hidepassword(url):
1847 '''hide user credential in a url string'''
1894 '''hide user credential in a url string'''
1848 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1895 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1849 netloc = re.sub('([^:]*):([^@]*)@(.*)', r'\1:***@\3', netloc)
1896 netloc = re.sub('([^:]*):([^@]*)@(.*)', r'\1:***@\3', netloc)
1850 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
1897 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
1851
1898
1852 def removeauth(url):
1899 def removeauth(url):
1853 '''remove all authentication information from a url string'''
1900 '''remove all authentication information from a url string'''
1854 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1901 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
1855 netloc = netloc[netloc.find('@')+1:]
1902 netloc = netloc[netloc.find('@')+1:]
1856 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
1903 return urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
General Comments 0
You need to be logged in to leave comments. Login now