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