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