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