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