##// END OF EJS Templates
Added hgexecutable support for py2exe/frozen scripts
"Paul Moore " -
r6499:479847cc default
parent child Browse files
Show More
@@ -1,1757 +1,1772 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
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():
557 """return True if we are a frozen executable.
558
559 The code supports py2exe (most common, Windows only) and tools/freeze
560 (portable, not much used).
561 """
562 return (hasattr(sys, "frozen") or # new py2exe
563 hasattr(sys, "importers") or # old py2exe
564 imp.is_frozen("__main__")) # tools/freeze
565
556 def hgexecutable():
566 def hgexecutable():
557 """return location of the 'hg' executable.
567 """return location of the 'hg' executable.
558
568
559 Defaults to $HG or 'hg' in the search path.
569 Defaults to $HG or 'hg' in the search path.
560 """
570 """
561 if _hgexecutable is None:
571 if _hgexecutable is None:
562 set_hgexecutable(os.environ.get('HG') or find_exe('hg', 'hg'))
572 if os.environ.has_key('HG'):
573 set_hgexecutable(os.environ.get('HG'))
574 elif main_is_frozen():
575 set_hgexecutable(sys.executable)
576 else:
577 sel_hgexecutable(find_exe('hg', 'hg'))
563 return _hgexecutable
578 return _hgexecutable
564
579
565 def set_hgexecutable(path):
580 def set_hgexecutable(path):
566 """set location of the 'hg' executable"""
581 """set location of the 'hg' executable"""
567 global _hgexecutable
582 global _hgexecutable
568 _hgexecutable = path
583 _hgexecutable = path
569
584
570 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
585 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
571 '''enhanced shell command execution.
586 '''enhanced shell command execution.
572 run with environment maybe modified, maybe in different dir.
587 run with environment maybe modified, maybe in different dir.
573
588
574 if command fails and onerr is None, return status. if ui object,
589 if command fails and onerr is None, return status. if ui object,
575 print error message and return status, else raise onerr object as
590 print error message and return status, else raise onerr object as
576 exception.'''
591 exception.'''
577 def py2shell(val):
592 def py2shell(val):
578 'convert python object into string that is useful to shell'
593 'convert python object into string that is useful to shell'
579 if val in (None, False):
594 if val in (None, False):
580 return '0'
595 return '0'
581 if val == True:
596 if val == True:
582 return '1'
597 return '1'
583 return str(val)
598 return str(val)
584 oldenv = {}
599 oldenv = {}
585 for k in environ:
600 for k in environ:
586 oldenv[k] = os.environ.get(k)
601 oldenv[k] = os.environ.get(k)
587 if cwd is not None:
602 if cwd is not None:
588 oldcwd = os.getcwd()
603 oldcwd = os.getcwd()
589 origcmd = cmd
604 origcmd = cmd
590 if os.name == 'nt':
605 if os.name == 'nt':
591 cmd = '"%s"' % cmd
606 cmd = '"%s"' % cmd
592 try:
607 try:
593 for k, v in environ.iteritems():
608 for k, v in environ.iteritems():
594 os.environ[k] = py2shell(v)
609 os.environ[k] = py2shell(v)
595 os.environ['HG'] = hgexecutable()
610 os.environ['HG'] = hgexecutable()
596 if cwd is not None and oldcwd != cwd:
611 if cwd is not None and oldcwd != cwd:
597 os.chdir(cwd)
612 os.chdir(cwd)
598 rc = os.system(cmd)
613 rc = os.system(cmd)
599 if sys.platform == 'OpenVMS' and rc & 1:
614 if sys.platform == 'OpenVMS' and rc & 1:
600 rc = 0
615 rc = 0
601 if rc and onerr:
616 if rc and onerr:
602 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
617 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
603 explain_exit(rc)[0])
618 explain_exit(rc)[0])
604 if errprefix:
619 if errprefix:
605 errmsg = '%s: %s' % (errprefix, errmsg)
620 errmsg = '%s: %s' % (errprefix, errmsg)
606 try:
621 try:
607 onerr.warn(errmsg + '\n')
622 onerr.warn(errmsg + '\n')
608 except AttributeError:
623 except AttributeError:
609 raise onerr(errmsg)
624 raise onerr(errmsg)
610 return rc
625 return rc
611 finally:
626 finally:
612 for k, v in oldenv.iteritems():
627 for k, v in oldenv.iteritems():
613 if v is None:
628 if v is None:
614 del os.environ[k]
629 del os.environ[k]
615 else:
630 else:
616 os.environ[k] = v
631 os.environ[k] = v
617 if cwd is not None and oldcwd != cwd:
632 if cwd is not None and oldcwd != cwd:
618 os.chdir(oldcwd)
633 os.chdir(oldcwd)
619
634
620 # os.path.lexists is not available on python2.3
635 # os.path.lexists is not available on python2.3
621 def lexists(filename):
636 def lexists(filename):
622 "test whether a file with this name exists. does not follow symlinks"
637 "test whether a file with this name exists. does not follow symlinks"
623 try:
638 try:
624 os.lstat(filename)
639 os.lstat(filename)
625 except:
640 except:
626 return False
641 return False
627 return True
642 return True
628
643
629 def rename(src, dst):
644 def rename(src, dst):
630 """forcibly rename a file"""
645 """forcibly rename a file"""
631 try:
646 try:
632 os.rename(src, dst)
647 os.rename(src, dst)
633 except OSError, err: # FIXME: check err (EEXIST ?)
648 except OSError, err: # FIXME: check err (EEXIST ?)
634 # on windows, rename to existing file is not allowed, so we
649 # on windows, rename to existing file is not allowed, so we
635 # must delete destination first. but if file is open, unlink
650 # must delete destination first. but if file is open, unlink
636 # schedules it for delete but does not delete it. rename
651 # schedules it for delete but does not delete it. rename
637 # happens immediately even for open files, so we create
652 # happens immediately even for open files, so we create
638 # temporary file, delete it, rename destination to that name,
653 # temporary file, delete it, rename destination to that name,
639 # then delete that. then rename is safe to do.
654 # then delete that. then rename is safe to do.
640 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
655 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
641 os.close(fd)
656 os.close(fd)
642 os.unlink(temp)
657 os.unlink(temp)
643 os.rename(dst, temp)
658 os.rename(dst, temp)
644 os.unlink(temp)
659 os.unlink(temp)
645 os.rename(src, dst)
660 os.rename(src, dst)
646
661
647 def unlink(f):
662 def unlink(f):
648 """unlink and remove the directory if it is empty"""
663 """unlink and remove the directory if it is empty"""
649 os.unlink(f)
664 os.unlink(f)
650 # try removing directories that might now be empty
665 # try removing directories that might now be empty
651 try:
666 try:
652 os.removedirs(os.path.dirname(f))
667 os.removedirs(os.path.dirname(f))
653 except OSError:
668 except OSError:
654 pass
669 pass
655
670
656 def copyfile(src, dest):
671 def copyfile(src, dest):
657 "copy a file, preserving mode"
672 "copy a file, preserving mode"
658 if os.path.islink(src):
673 if os.path.islink(src):
659 try:
674 try:
660 os.unlink(dest)
675 os.unlink(dest)
661 except:
676 except:
662 pass
677 pass
663 os.symlink(os.readlink(src), dest)
678 os.symlink(os.readlink(src), dest)
664 else:
679 else:
665 try:
680 try:
666 shutil.copyfile(src, dest)
681 shutil.copyfile(src, dest)
667 shutil.copymode(src, dest)
682 shutil.copymode(src, dest)
668 except shutil.Error, inst:
683 except shutil.Error, inst:
669 raise Abort(str(inst))
684 raise Abort(str(inst))
670
685
671 def copyfiles(src, dst, hardlink=None):
686 def copyfiles(src, dst, hardlink=None):
672 """Copy a directory tree using hardlinks if possible"""
687 """Copy a directory tree using hardlinks if possible"""
673
688
674 if hardlink is None:
689 if hardlink is None:
675 hardlink = (os.stat(src).st_dev ==
690 hardlink = (os.stat(src).st_dev ==
676 os.stat(os.path.dirname(dst)).st_dev)
691 os.stat(os.path.dirname(dst)).st_dev)
677
692
678 if os.path.isdir(src):
693 if os.path.isdir(src):
679 os.mkdir(dst)
694 os.mkdir(dst)
680 for name, kind in osutil.listdir(src):
695 for name, kind in osutil.listdir(src):
681 srcname = os.path.join(src, name)
696 srcname = os.path.join(src, name)
682 dstname = os.path.join(dst, name)
697 dstname = os.path.join(dst, name)
683 copyfiles(srcname, dstname, hardlink)
698 copyfiles(srcname, dstname, hardlink)
684 else:
699 else:
685 if hardlink:
700 if hardlink:
686 try:
701 try:
687 os_link(src, dst)
702 os_link(src, dst)
688 except (IOError, OSError):
703 except (IOError, OSError):
689 hardlink = False
704 hardlink = False
690 shutil.copy(src, dst)
705 shutil.copy(src, dst)
691 else:
706 else:
692 shutil.copy(src, dst)
707 shutil.copy(src, dst)
693
708
694 class path_auditor(object):
709 class path_auditor(object):
695 '''ensure that a filesystem path contains no banned components.
710 '''ensure that a filesystem path contains no banned components.
696 the following properties of a path are checked:
711 the following properties of a path are checked:
697
712
698 - under top-level .hg
713 - under top-level .hg
699 - starts at the root of a windows drive
714 - starts at the root of a windows drive
700 - contains ".."
715 - contains ".."
701 - traverses a symlink (e.g. a/symlink_here/b)
716 - traverses a symlink (e.g. a/symlink_here/b)
702 - inside a nested repository'''
717 - inside a nested repository'''
703
718
704 def __init__(self, root):
719 def __init__(self, root):
705 self.audited = set()
720 self.audited = set()
706 self.auditeddir = set()
721 self.auditeddir = set()
707 self.root = root
722 self.root = root
708
723
709 def __call__(self, path):
724 def __call__(self, path):
710 if path in self.audited:
725 if path in self.audited:
711 return
726 return
712 normpath = os.path.normcase(path)
727 normpath = os.path.normcase(path)
713 parts = normpath.split(os.sep)
728 parts = normpath.split(os.sep)
714 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
729 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
715 or os.pardir in parts):
730 or os.pardir in parts):
716 raise Abort(_("path contains illegal component: %s") % path)
731 raise Abort(_("path contains illegal component: %s") % path)
717 def check(prefix):
732 def check(prefix):
718 curpath = os.path.join(self.root, prefix)
733 curpath = os.path.join(self.root, prefix)
719 try:
734 try:
720 st = os.lstat(curpath)
735 st = os.lstat(curpath)
721 except OSError, err:
736 except OSError, err:
722 # EINVAL can be raised as invalid path syntax under win32.
737 # EINVAL can be raised as invalid path syntax under win32.
723 # They must be ignored for patterns can be checked too.
738 # They must be ignored for patterns can be checked too.
724 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
739 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
725 raise
740 raise
726 else:
741 else:
727 if stat.S_ISLNK(st.st_mode):
742 if stat.S_ISLNK(st.st_mode):
728 raise Abort(_('path %r traverses symbolic link %r') %
743 raise Abort(_('path %r traverses symbolic link %r') %
729 (path, prefix))
744 (path, prefix))
730 elif (stat.S_ISDIR(st.st_mode) and
745 elif (stat.S_ISDIR(st.st_mode) and
731 os.path.isdir(os.path.join(curpath, '.hg'))):
746 os.path.isdir(os.path.join(curpath, '.hg'))):
732 raise Abort(_('path %r is inside repo %r') %
747 raise Abort(_('path %r is inside repo %r') %
733 (path, prefix))
748 (path, prefix))
734
749
735 prefixes = []
750 prefixes = []
736 for c in strutil.rfindall(normpath, os.sep):
751 for c in strutil.rfindall(normpath, os.sep):
737 prefix = normpath[:c]
752 prefix = normpath[:c]
738 if prefix in self.auditeddir:
753 if prefix in self.auditeddir:
739 break
754 break
740 check(prefix)
755 check(prefix)
741 prefixes.append(prefix)
756 prefixes.append(prefix)
742
757
743 self.audited.add(path)
758 self.audited.add(path)
744 # only add prefixes to the cache after checking everything: we don't
759 # only add prefixes to the cache after checking everything: we don't
745 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
760 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
746 self.auditeddir.update(prefixes)
761 self.auditeddir.update(prefixes)
747
762
748 def _makelock_file(info, pathname):
763 def _makelock_file(info, pathname):
749 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
764 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
750 os.write(ld, info)
765 os.write(ld, info)
751 os.close(ld)
766 os.close(ld)
752
767
753 def _readlock_file(pathname):
768 def _readlock_file(pathname):
754 return posixfile(pathname).read()
769 return posixfile(pathname).read()
755
770
756 def nlinks(pathname):
771 def nlinks(pathname):
757 """Return number of hardlinks for the given file."""
772 """Return number of hardlinks for the given file."""
758 return os.lstat(pathname).st_nlink
773 return os.lstat(pathname).st_nlink
759
774
760 if hasattr(os, 'link'):
775 if hasattr(os, 'link'):
761 os_link = os.link
776 os_link = os.link
762 else:
777 else:
763 def os_link(src, dst):
778 def os_link(src, dst):
764 raise OSError(0, _("Hardlinks not supported"))
779 raise OSError(0, _("Hardlinks not supported"))
765
780
766 def fstat(fp):
781 def fstat(fp):
767 '''stat file object that may not have fileno method.'''
782 '''stat file object that may not have fileno method.'''
768 try:
783 try:
769 return os.fstat(fp.fileno())
784 return os.fstat(fp.fileno())
770 except AttributeError:
785 except AttributeError:
771 return os.stat(fp.name)
786 return os.stat(fp.name)
772
787
773 posixfile = file
788 posixfile = file
774
789
775 def openhardlinks():
790 def openhardlinks():
776 '''return true if it is safe to hold open file handles to hardlinks'''
791 '''return true if it is safe to hold open file handles to hardlinks'''
777 return True
792 return True
778
793
779 getuser_fallback = None
794 getuser_fallback = None
780
795
781 def getuser():
796 def getuser():
782 '''return name of current user'''
797 '''return name of current user'''
783 try:
798 try:
784 return getpass.getuser()
799 return getpass.getuser()
785 except ImportError:
800 except ImportError:
786 # import of pwd will fail on windows - try fallback
801 # import of pwd will fail on windows - try fallback
787 if getuser_fallback:
802 if getuser_fallback:
788 return getuser_fallback()
803 return getuser_fallback()
789 # raised if win32api not available
804 # raised if win32api not available
790 raise Abort(_('user name not available - set USERNAME '
805 raise Abort(_('user name not available - set USERNAME '
791 'environment variable'))
806 'environment variable'))
792
807
793 def username(uid=None):
808 def username(uid=None):
794 """Return the name of the user with the given uid.
809 """Return the name of the user with the given uid.
795
810
796 If uid is None, return the name of the current user."""
811 If uid is None, return the name of the current user."""
797 try:
812 try:
798 import pwd
813 import pwd
799 if uid is None:
814 if uid is None:
800 uid = os.getuid()
815 uid = os.getuid()
801 try:
816 try:
802 return pwd.getpwuid(uid)[0]
817 return pwd.getpwuid(uid)[0]
803 except KeyError:
818 except KeyError:
804 return str(uid)
819 return str(uid)
805 except ImportError:
820 except ImportError:
806 return None
821 return None
807
822
808 def groupname(gid=None):
823 def groupname(gid=None):
809 """Return the name of the group with the given gid.
824 """Return the name of the group with the given gid.
810
825
811 If gid is None, return the name of the current group."""
826 If gid is None, return the name of the current group."""
812 try:
827 try:
813 import grp
828 import grp
814 if gid is None:
829 if gid is None:
815 gid = os.getgid()
830 gid = os.getgid()
816 try:
831 try:
817 return grp.getgrgid(gid)[0]
832 return grp.getgrgid(gid)[0]
818 except KeyError:
833 except KeyError:
819 return str(gid)
834 return str(gid)
820 except ImportError:
835 except ImportError:
821 return None
836 return None
822
837
823 # File system features
838 # File system features
824
839
825 def checkfolding(path):
840 def checkfolding(path):
826 """
841 """
827 Check whether the given path is on a case-sensitive filesystem
842 Check whether the given path is on a case-sensitive filesystem
828
843
829 Requires a path (like /foo/.hg) ending with a foldable final
844 Requires a path (like /foo/.hg) ending with a foldable final
830 directory component.
845 directory component.
831 """
846 """
832 s1 = os.stat(path)
847 s1 = os.stat(path)
833 d, b = os.path.split(path)
848 d, b = os.path.split(path)
834 p2 = os.path.join(d, b.upper())
849 p2 = os.path.join(d, b.upper())
835 if path == p2:
850 if path == p2:
836 p2 = os.path.join(d, b.lower())
851 p2 = os.path.join(d, b.lower())
837 try:
852 try:
838 s2 = os.stat(p2)
853 s2 = os.stat(p2)
839 if s2 == s1:
854 if s2 == s1:
840 return False
855 return False
841 return True
856 return True
842 except:
857 except:
843 return True
858 return True
844
859
845 def checkexec(path):
860 def checkexec(path):
846 """
861 """
847 Check whether the given path is on a filesystem with UNIX-like exec flags
862 Check whether the given path is on a filesystem with UNIX-like exec flags
848
863
849 Requires a directory (like /foo/.hg)
864 Requires a directory (like /foo/.hg)
850 """
865 """
851 try:
866 try:
852 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
867 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
853 fh, fn = tempfile.mkstemp("", "", path)
868 fh, fn = tempfile.mkstemp("", "", path)
854 os.close(fh)
869 os.close(fh)
855 m = os.stat(fn).st_mode
870 m = os.stat(fn).st_mode
856 # VFAT on Linux can flip mode but it doesn't persist a FS remount.
871 # VFAT on Linux can flip mode but it doesn't persist a FS remount.
857 # frequently we can detect it if files are created with exec bit on.
872 # frequently we can detect it if files are created with exec bit on.
858 new_file_has_exec = m & EXECFLAGS
873 new_file_has_exec = m & EXECFLAGS
859 os.chmod(fn, m ^ EXECFLAGS)
874 os.chmod(fn, m ^ EXECFLAGS)
860 exec_flags_cannot_flip = (os.stat(fn).st_mode == m)
875 exec_flags_cannot_flip = (os.stat(fn).st_mode == m)
861 os.unlink(fn)
876 os.unlink(fn)
862 except (IOError,OSError):
877 except (IOError,OSError):
863 # we don't care, the user probably won't be able to commit anyway
878 # we don't care, the user probably won't be able to commit anyway
864 return False
879 return False
865 return not (new_file_has_exec or exec_flags_cannot_flip)
880 return not (new_file_has_exec or exec_flags_cannot_flip)
866
881
867 def execfunc(path, fallback):
882 def execfunc(path, fallback):
868 '''return an is_exec() function with default to fallback'''
883 '''return an is_exec() function with default to fallback'''
869 if checkexec(path):
884 if checkexec(path):
870 return lambda x: is_exec(os.path.join(path, x))
885 return lambda x: is_exec(os.path.join(path, x))
871 return fallback
886 return fallback
872
887
873 def checklink(path):
888 def checklink(path):
874 """check whether the given path is on a symlink-capable filesystem"""
889 """check whether the given path is on a symlink-capable filesystem"""
875 # mktemp is not racy because symlink creation will fail if the
890 # mktemp is not racy because symlink creation will fail if the
876 # file already exists
891 # file already exists
877 name = tempfile.mktemp(dir=path)
892 name = tempfile.mktemp(dir=path)
878 try:
893 try:
879 os.symlink(".", name)
894 os.symlink(".", name)
880 os.unlink(name)
895 os.unlink(name)
881 return True
896 return True
882 except (OSError, AttributeError):
897 except (OSError, AttributeError):
883 return False
898 return False
884
899
885 def linkfunc(path, fallback):
900 def linkfunc(path, fallback):
886 '''return an is_link() function with default to fallback'''
901 '''return an is_link() function with default to fallback'''
887 if checklink(path):
902 if checklink(path):
888 return lambda x: os.path.islink(os.path.join(path, x))
903 return lambda x: os.path.islink(os.path.join(path, x))
889 return fallback
904 return fallback
890
905
891 _umask = os.umask(0)
906 _umask = os.umask(0)
892 os.umask(_umask)
907 os.umask(_umask)
893
908
894 def needbinarypatch():
909 def needbinarypatch():
895 """return True if patches should be applied in binary mode by default."""
910 """return True if patches should be applied in binary mode by default."""
896 return os.name == 'nt'
911 return os.name == 'nt'
897
912
898 # Platform specific variants
913 # Platform specific variants
899 if os.name == 'nt':
914 if os.name == 'nt':
900 import msvcrt
915 import msvcrt
901 nulldev = 'NUL:'
916 nulldev = 'NUL:'
902
917
903 class winstdout:
918 class winstdout:
904 '''stdout on windows misbehaves if sent through a pipe'''
919 '''stdout on windows misbehaves if sent through a pipe'''
905
920
906 def __init__(self, fp):
921 def __init__(self, fp):
907 self.fp = fp
922 self.fp = fp
908
923
909 def __getattr__(self, key):
924 def __getattr__(self, key):
910 return getattr(self.fp, key)
925 return getattr(self.fp, key)
911
926
912 def close(self):
927 def close(self):
913 try:
928 try:
914 self.fp.close()
929 self.fp.close()
915 except: pass
930 except: pass
916
931
917 def write(self, s):
932 def write(self, s):
918 try:
933 try:
919 # This is workaround for "Not enough space" error on
934 # This is workaround for "Not enough space" error on
920 # writing large size of data to console.
935 # writing large size of data to console.
921 limit = 16000
936 limit = 16000
922 l = len(s)
937 l = len(s)
923 start = 0
938 start = 0
924 while start < l:
939 while start < l:
925 end = start + limit
940 end = start + limit
926 self.fp.write(s[start:end])
941 self.fp.write(s[start:end])
927 start = end
942 start = end
928 except IOError, inst:
943 except IOError, inst:
929 if inst.errno != 0: raise
944 if inst.errno != 0: raise
930 self.close()
945 self.close()
931 raise IOError(errno.EPIPE, 'Broken pipe')
946 raise IOError(errno.EPIPE, 'Broken pipe')
932
947
933 def flush(self):
948 def flush(self):
934 try:
949 try:
935 return self.fp.flush()
950 return self.fp.flush()
936 except IOError, inst:
951 except IOError, inst:
937 if inst.errno != errno.EINVAL: raise
952 if inst.errno != errno.EINVAL: raise
938 self.close()
953 self.close()
939 raise IOError(errno.EPIPE, 'Broken pipe')
954 raise IOError(errno.EPIPE, 'Broken pipe')
940
955
941 sys.stdout = winstdout(sys.stdout)
956 sys.stdout = winstdout(sys.stdout)
942
957
943 def _is_win_9x():
958 def _is_win_9x():
944 '''return true if run on windows 95, 98 or me.'''
959 '''return true if run on windows 95, 98 or me.'''
945 try:
960 try:
946 return sys.getwindowsversion()[3] == 1
961 return sys.getwindowsversion()[3] == 1
947 except AttributeError:
962 except AttributeError:
948 return 'command' in os.environ.get('comspec', '')
963 return 'command' in os.environ.get('comspec', '')
949
964
950 def openhardlinks():
965 def openhardlinks():
951 return not _is_win_9x and "win32api" in locals()
966 return not _is_win_9x and "win32api" in locals()
952
967
953 def system_rcpath():
968 def system_rcpath():
954 try:
969 try:
955 return system_rcpath_win32()
970 return system_rcpath_win32()
956 except:
971 except:
957 return [r'c:\mercurial\mercurial.ini']
972 return [r'c:\mercurial\mercurial.ini']
958
973
959 def user_rcpath():
974 def user_rcpath():
960 '''return os-specific hgrc search path to the user dir'''
975 '''return os-specific hgrc search path to the user dir'''
961 try:
976 try:
962 userrc = user_rcpath_win32()
977 userrc = user_rcpath_win32()
963 except:
978 except:
964 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
979 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
965 path = [userrc]
980 path = [userrc]
966 userprofile = os.environ.get('USERPROFILE')
981 userprofile = os.environ.get('USERPROFILE')
967 if userprofile:
982 if userprofile:
968 path.append(os.path.join(userprofile, 'mercurial.ini'))
983 path.append(os.path.join(userprofile, 'mercurial.ini'))
969 return path
984 return path
970
985
971 def parse_patch_output(output_line):
986 def parse_patch_output(output_line):
972 """parses the output produced by patch and returns the file name"""
987 """parses the output produced by patch and returns the file name"""
973 pf = output_line[14:]
988 pf = output_line[14:]
974 if pf[0] == '`':
989 if pf[0] == '`':
975 pf = pf[1:-1] # Remove the quotes
990 pf = pf[1:-1] # Remove the quotes
976 return pf
991 return pf
977
992
978 def sshargs(sshcmd, host, user, port):
993 def sshargs(sshcmd, host, user, port):
979 '''Build argument list for ssh or Plink'''
994 '''Build argument list for ssh or Plink'''
980 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
995 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
981 args = user and ("%s@%s" % (user, host)) or host
996 args = user and ("%s@%s" % (user, host)) or host
982 return port and ("%s %s %s" % (args, pflag, port)) or args
997 return port and ("%s %s %s" % (args, pflag, port)) or args
983
998
984 def testpid(pid):
999 def testpid(pid):
985 '''return False if pid dead, True if running or not known'''
1000 '''return False if pid dead, True if running or not known'''
986 return True
1001 return True
987
1002
988 def set_exec(f, mode):
1003 def set_exec(f, mode):
989 pass
1004 pass
990
1005
991 def set_link(f, mode):
1006 def set_link(f, mode):
992 pass
1007 pass
993
1008
994 def set_binary(fd):
1009 def set_binary(fd):
995 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1010 msvcrt.setmode(fd.fileno(), os.O_BINARY)
996
1011
997 def pconvert(path):
1012 def pconvert(path):
998 return path.replace("\\", "/")
1013 return path.replace("\\", "/")
999
1014
1000 def localpath(path):
1015 def localpath(path):
1001 return path.replace('/', '\\')
1016 return path.replace('/', '\\')
1002
1017
1003 def normpath(path):
1018 def normpath(path):
1004 return pconvert(os.path.normpath(path))
1019 return pconvert(os.path.normpath(path))
1005
1020
1006 makelock = _makelock_file
1021 makelock = _makelock_file
1007 readlock = _readlock_file
1022 readlock = _readlock_file
1008
1023
1009 def samestat(s1, s2):
1024 def samestat(s1, s2):
1010 return False
1025 return False
1011
1026
1012 # A sequence of backslashes is special iff it precedes a double quote:
1027 # A sequence of backslashes is special iff it precedes a double quote:
1013 # - if there's an even number of backslashes, the double quote is not
1028 # - if there's an even number of backslashes, the double quote is not
1014 # quoted (i.e. it ends the quoted region)
1029 # quoted (i.e. it ends the quoted region)
1015 # - if there's an odd number of backslashes, the double quote is quoted
1030 # - if there's an odd number of backslashes, the double quote is quoted
1016 # - in both cases, every pair of backslashes is unquoted into a single
1031 # - in both cases, every pair of backslashes is unquoted into a single
1017 # backslash
1032 # backslash
1018 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1033 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1019 # So, to quote a string, we must surround it in double quotes, double
1034 # So, to quote a string, we must surround it in double quotes, double
1020 # the number of backslashes that preceed double quotes and add another
1035 # the number of backslashes that preceed double quotes and add another
1021 # backslash before every double quote (being careful with the double
1036 # backslash before every double quote (being careful with the double
1022 # quote we've appended to the end)
1037 # quote we've appended to the end)
1023 _quotere = None
1038 _quotere = None
1024 def shellquote(s):
1039 def shellquote(s):
1025 global _quotere
1040 global _quotere
1026 if _quotere is None:
1041 if _quotere is None:
1027 _quotere = re.compile(r'(\\*)("|\\$)')
1042 _quotere = re.compile(r'(\\*)("|\\$)')
1028 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1043 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1029
1044
1030 def quotecommand(cmd):
1045 def quotecommand(cmd):
1031 """Build a command string suitable for os.popen* calls."""
1046 """Build a command string suitable for os.popen* calls."""
1032 # The extra quotes are needed because popen* runs the command
1047 # The extra quotes are needed because popen* runs the command
1033 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1048 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1034 return '"' + cmd + '"'
1049 return '"' + cmd + '"'
1035
1050
1036 def popen(command):
1051 def popen(command):
1037 # Work around "popen spawned process may not write to stdout
1052 # Work around "popen spawned process may not write to stdout
1038 # under windows"
1053 # under windows"
1039 # http://bugs.python.org/issue1366
1054 # http://bugs.python.org/issue1366
1040 command += " 2> %s" % nulldev
1055 command += " 2> %s" % nulldev
1041 return os.popen(quotecommand(command))
1056 return os.popen(quotecommand(command))
1042
1057
1043 def explain_exit(code):
1058 def explain_exit(code):
1044 return _("exited with status %d") % code, code
1059 return _("exited with status %d") % code, code
1045
1060
1046 # if you change this stub into a real check, please try to implement the
1061 # if you change this stub into a real check, please try to implement the
1047 # username and groupname functions above, too.
1062 # username and groupname functions above, too.
1048 def isowner(fp, st=None):
1063 def isowner(fp, st=None):
1049 return True
1064 return True
1050
1065
1051 def find_in_path(name, path, default=None):
1066 def find_in_path(name, path, default=None):
1052 '''find name in search path. path can be string (will be split
1067 '''find name in search path. path can be string (will be split
1053 with os.pathsep), or iterable thing that returns strings. if name
1068 with os.pathsep), or iterable thing that returns strings. if name
1054 found, return path to name. else return default. name is looked up
1069 found, return path to name. else return default. name is looked up
1055 using cmd.exe rules, using PATHEXT.'''
1070 using cmd.exe rules, using PATHEXT.'''
1056 if isinstance(path, str):
1071 if isinstance(path, str):
1057 path = path.split(os.pathsep)
1072 path = path.split(os.pathsep)
1058
1073
1059 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1074 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1060 pathext = pathext.lower().split(os.pathsep)
1075 pathext = pathext.lower().split(os.pathsep)
1061 isexec = os.path.splitext(name)[1].lower() in pathext
1076 isexec = os.path.splitext(name)[1].lower() in pathext
1062
1077
1063 for p in path:
1078 for p in path:
1064 p_name = os.path.join(p, name)
1079 p_name = os.path.join(p, name)
1065
1080
1066 if isexec and os.path.exists(p_name):
1081 if isexec and os.path.exists(p_name):
1067 return p_name
1082 return p_name
1068
1083
1069 for ext in pathext:
1084 for ext in pathext:
1070 p_name_ext = p_name + ext
1085 p_name_ext = p_name + ext
1071 if os.path.exists(p_name_ext):
1086 if os.path.exists(p_name_ext):
1072 return p_name_ext
1087 return p_name_ext
1073 return default
1088 return default
1074
1089
1075 def set_signal_handler():
1090 def set_signal_handler():
1076 try:
1091 try:
1077 set_signal_handler_win32()
1092 set_signal_handler_win32()
1078 except NameError:
1093 except NameError:
1079 pass
1094 pass
1080
1095
1081 try:
1096 try:
1082 # override functions with win32 versions if possible
1097 # override functions with win32 versions if possible
1083 from util_win32 import *
1098 from util_win32 import *
1084 if not _is_win_9x():
1099 if not _is_win_9x():
1085 posixfile = posixfile_nt
1100 posixfile = posixfile_nt
1086 except ImportError:
1101 except ImportError:
1087 pass
1102 pass
1088
1103
1089 else:
1104 else:
1090 nulldev = '/dev/null'
1105 nulldev = '/dev/null'
1091
1106
1092 def rcfiles(path):
1107 def rcfiles(path):
1093 rcs = [os.path.join(path, 'hgrc')]
1108 rcs = [os.path.join(path, 'hgrc')]
1094 rcdir = os.path.join(path, 'hgrc.d')
1109 rcdir = os.path.join(path, 'hgrc.d')
1095 try:
1110 try:
1096 rcs.extend([os.path.join(rcdir, f)
1111 rcs.extend([os.path.join(rcdir, f)
1097 for f, kind in osutil.listdir(rcdir)
1112 for f, kind in osutil.listdir(rcdir)
1098 if f.endswith(".rc")])
1113 if f.endswith(".rc")])
1099 except OSError:
1114 except OSError:
1100 pass
1115 pass
1101 return rcs
1116 return rcs
1102
1117
1103 def system_rcpath():
1118 def system_rcpath():
1104 path = []
1119 path = []
1105 # old mod_python does not set sys.argv
1120 # old mod_python does not set sys.argv
1106 if len(getattr(sys, 'argv', [])) > 0:
1121 if len(getattr(sys, 'argv', [])) > 0:
1107 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1122 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1108 '/../etc/mercurial'))
1123 '/../etc/mercurial'))
1109 path.extend(rcfiles('/etc/mercurial'))
1124 path.extend(rcfiles('/etc/mercurial'))
1110 return path
1125 return path
1111
1126
1112 def user_rcpath():
1127 def user_rcpath():
1113 return [os.path.expanduser('~/.hgrc')]
1128 return [os.path.expanduser('~/.hgrc')]
1114
1129
1115 def parse_patch_output(output_line):
1130 def parse_patch_output(output_line):
1116 """parses the output produced by patch and returns the file name"""
1131 """parses the output produced by patch and returns the file name"""
1117 pf = output_line[14:]
1132 pf = output_line[14:]
1118 if os.sys.platform == 'OpenVMS':
1133 if os.sys.platform == 'OpenVMS':
1119 if pf[0] == '`':
1134 if pf[0] == '`':
1120 pf = pf[1:-1] # Remove the quotes
1135 pf = pf[1:-1] # Remove the quotes
1121 else:
1136 else:
1122 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1137 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1123 pf = pf[1:-1] # Remove the quotes
1138 pf = pf[1:-1] # Remove the quotes
1124 return pf
1139 return pf
1125
1140
1126 def sshargs(sshcmd, host, user, port):
1141 def sshargs(sshcmd, host, user, port):
1127 '''Build argument list for ssh'''
1142 '''Build argument list for ssh'''
1128 args = user and ("%s@%s" % (user, host)) or host
1143 args = user and ("%s@%s" % (user, host)) or host
1129 return port and ("%s -p %s" % (args, port)) or args
1144 return port and ("%s -p %s" % (args, port)) or args
1130
1145
1131 def is_exec(f):
1146 def is_exec(f):
1132 """check whether a file is executable"""
1147 """check whether a file is executable"""
1133 return (os.lstat(f).st_mode & 0100 != 0)
1148 return (os.lstat(f).st_mode & 0100 != 0)
1134
1149
1135 def set_exec(f, mode):
1150 def set_exec(f, mode):
1136 s = os.lstat(f).st_mode
1151 s = os.lstat(f).st_mode
1137 if stat.S_ISLNK(s) or (s & 0100 != 0) == mode:
1152 if stat.S_ISLNK(s) or (s & 0100 != 0) == mode:
1138 return
1153 return
1139 if mode:
1154 if mode:
1140 # Turn on +x for every +r bit when making a file executable
1155 # Turn on +x for every +r bit when making a file executable
1141 # and obey umask.
1156 # and obey umask.
1142 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1157 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1143 else:
1158 else:
1144 os.chmod(f, s & 0666)
1159 os.chmod(f, s & 0666)
1145
1160
1146 def set_link(f, mode):
1161 def set_link(f, mode):
1147 """make a file a symbolic link/regular file
1162 """make a file a symbolic link/regular file
1148
1163
1149 if a file is changed to a link, its contents become the link data
1164 if a file is changed to a link, its contents become the link data
1150 if a link is changed to a file, its link data become its contents
1165 if a link is changed to a file, its link data become its contents
1151 """
1166 """
1152
1167
1153 m = os.path.islink(f)
1168 m = os.path.islink(f)
1154 if m == bool(mode):
1169 if m == bool(mode):
1155 return
1170 return
1156
1171
1157 if mode: # switch file to link
1172 if mode: # switch file to link
1158 data = file(f).read()
1173 data = file(f).read()
1159 os.unlink(f)
1174 os.unlink(f)
1160 os.symlink(data, f)
1175 os.symlink(data, f)
1161 else:
1176 else:
1162 data = os.readlink(f)
1177 data = os.readlink(f)
1163 os.unlink(f)
1178 os.unlink(f)
1164 file(f, "w").write(data)
1179 file(f, "w").write(data)
1165
1180
1166 def set_binary(fd):
1181 def set_binary(fd):
1167 pass
1182 pass
1168
1183
1169 def pconvert(path):
1184 def pconvert(path):
1170 return path
1185 return path
1171
1186
1172 def localpath(path):
1187 def localpath(path):
1173 return path
1188 return path
1174
1189
1175 normpath = os.path.normpath
1190 normpath = os.path.normpath
1176 samestat = os.path.samestat
1191 samestat = os.path.samestat
1177
1192
1178 def makelock(info, pathname):
1193 def makelock(info, pathname):
1179 try:
1194 try:
1180 os.symlink(info, pathname)
1195 os.symlink(info, pathname)
1181 except OSError, why:
1196 except OSError, why:
1182 if why.errno == errno.EEXIST:
1197 if why.errno == errno.EEXIST:
1183 raise
1198 raise
1184 else:
1199 else:
1185 _makelock_file(info, pathname)
1200 _makelock_file(info, pathname)
1186
1201
1187 def readlock(pathname):
1202 def readlock(pathname):
1188 try:
1203 try:
1189 return os.readlink(pathname)
1204 return os.readlink(pathname)
1190 except OSError, why:
1205 except OSError, why:
1191 if why.errno in (errno.EINVAL, errno.ENOSYS):
1206 if why.errno in (errno.EINVAL, errno.ENOSYS):
1192 return _readlock_file(pathname)
1207 return _readlock_file(pathname)
1193 else:
1208 else:
1194 raise
1209 raise
1195
1210
1196 def shellquote(s):
1211 def shellquote(s):
1197 if os.sys.platform == 'OpenVMS':
1212 if os.sys.platform == 'OpenVMS':
1198 return '"%s"' % s
1213 return '"%s"' % s
1199 else:
1214 else:
1200 return "'%s'" % s.replace("'", "'\\''")
1215 return "'%s'" % s.replace("'", "'\\''")
1201
1216
1202 def quotecommand(cmd):
1217 def quotecommand(cmd):
1203 return cmd
1218 return cmd
1204
1219
1205 def popen(command):
1220 def popen(command):
1206 return os.popen(command)
1221 return os.popen(command)
1207
1222
1208 def testpid(pid):
1223 def testpid(pid):
1209 '''return False if pid dead, True if running or not sure'''
1224 '''return False if pid dead, True if running or not sure'''
1210 if os.sys.platform == 'OpenVMS':
1225 if os.sys.platform == 'OpenVMS':
1211 return True
1226 return True
1212 try:
1227 try:
1213 os.kill(pid, 0)
1228 os.kill(pid, 0)
1214 return True
1229 return True
1215 except OSError, inst:
1230 except OSError, inst:
1216 return inst.errno != errno.ESRCH
1231 return inst.errno != errno.ESRCH
1217
1232
1218 def explain_exit(code):
1233 def explain_exit(code):
1219 """return a 2-tuple (desc, code) describing a process's status"""
1234 """return a 2-tuple (desc, code) describing a process's status"""
1220 if os.WIFEXITED(code):
1235 if os.WIFEXITED(code):
1221 val = os.WEXITSTATUS(code)
1236 val = os.WEXITSTATUS(code)
1222 return _("exited with status %d") % val, val
1237 return _("exited with status %d") % val, val
1223 elif os.WIFSIGNALED(code):
1238 elif os.WIFSIGNALED(code):
1224 val = os.WTERMSIG(code)
1239 val = os.WTERMSIG(code)
1225 return _("killed by signal %d") % val, val
1240 return _("killed by signal %d") % val, val
1226 elif os.WIFSTOPPED(code):
1241 elif os.WIFSTOPPED(code):
1227 val = os.WSTOPSIG(code)
1242 val = os.WSTOPSIG(code)
1228 return _("stopped by signal %d") % val, val
1243 return _("stopped by signal %d") % val, val
1229 raise ValueError(_("invalid exit code"))
1244 raise ValueError(_("invalid exit code"))
1230
1245
1231 def isowner(fp, st=None):
1246 def isowner(fp, st=None):
1232 """Return True if the file object f belongs to the current user.
1247 """Return True if the file object f belongs to the current user.
1233
1248
1234 The return value of a util.fstat(f) may be passed as the st argument.
1249 The return value of a util.fstat(f) may be passed as the st argument.
1235 """
1250 """
1236 if st is None:
1251 if st is None:
1237 st = fstat(fp)
1252 st = fstat(fp)
1238 return st.st_uid == os.getuid()
1253 return st.st_uid == os.getuid()
1239
1254
1240 def find_in_path(name, path, default=None):
1255 def find_in_path(name, path, default=None):
1241 '''find name in search path. path can be string (will be split
1256 '''find name in search path. path can be string (will be split
1242 with os.pathsep), or iterable thing that returns strings. if name
1257 with os.pathsep), or iterable thing that returns strings. if name
1243 found, return path to name. else return default.'''
1258 found, return path to name. else return default.'''
1244 if isinstance(path, str):
1259 if isinstance(path, str):
1245 path = path.split(os.pathsep)
1260 path = path.split(os.pathsep)
1246 for p in path:
1261 for p in path:
1247 p_name = os.path.join(p, name)
1262 p_name = os.path.join(p, name)
1248 if os.path.exists(p_name):
1263 if os.path.exists(p_name):
1249 return p_name
1264 return p_name
1250 return default
1265 return default
1251
1266
1252 def set_signal_handler():
1267 def set_signal_handler():
1253 pass
1268 pass
1254
1269
1255 def find_exe(name, default=None):
1270 def find_exe(name, default=None):
1256 '''find path of an executable.
1271 '''find path of an executable.
1257 if name contains a path component, return it as is. otherwise,
1272 if name contains a path component, return it as is. otherwise,
1258 use normal executable search path.'''
1273 use normal executable search path.'''
1259
1274
1260 if os.sep in name or sys.platform == 'OpenVMS':
1275 if os.sep in name or sys.platform == 'OpenVMS':
1261 # don't check the executable bit. if the file isn't
1276 # don't check the executable bit. if the file isn't
1262 # executable, whoever tries to actually run it will give a
1277 # executable, whoever tries to actually run it will give a
1263 # much more useful error message.
1278 # much more useful error message.
1264 return name
1279 return name
1265 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1280 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1266
1281
1267 def _buildencodefun():
1282 def _buildencodefun():
1268 e = '_'
1283 e = '_'
1269 win_reserved = [ord(x) for x in '\\:*?"<>|']
1284 win_reserved = [ord(x) for x in '\\:*?"<>|']
1270 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1285 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1271 for x in (range(32) + range(126, 256) + win_reserved):
1286 for x in (range(32) + range(126, 256) + win_reserved):
1272 cmap[chr(x)] = "~%02x" % x
1287 cmap[chr(x)] = "~%02x" % x
1273 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1288 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1274 cmap[chr(x)] = e + chr(x).lower()
1289 cmap[chr(x)] = e + chr(x).lower()
1275 dmap = {}
1290 dmap = {}
1276 for k, v in cmap.iteritems():
1291 for k, v in cmap.iteritems():
1277 dmap[v] = k
1292 dmap[v] = k
1278 def decode(s):
1293 def decode(s):
1279 i = 0
1294 i = 0
1280 while i < len(s):
1295 while i < len(s):
1281 for l in xrange(1, 4):
1296 for l in xrange(1, 4):
1282 try:
1297 try:
1283 yield dmap[s[i:i+l]]
1298 yield dmap[s[i:i+l]]
1284 i += l
1299 i += l
1285 break
1300 break
1286 except KeyError:
1301 except KeyError:
1287 pass
1302 pass
1288 else:
1303 else:
1289 raise KeyError
1304 raise KeyError
1290 return (lambda s: "".join([cmap[c] for c in s]),
1305 return (lambda s: "".join([cmap[c] for c in s]),
1291 lambda s: "".join(list(decode(s))))
1306 lambda s: "".join(list(decode(s))))
1292
1307
1293 encodefilename, decodefilename = _buildencodefun()
1308 encodefilename, decodefilename = _buildencodefun()
1294
1309
1295 def encodedopener(openerfn, fn):
1310 def encodedopener(openerfn, fn):
1296 def o(path, *args, **kw):
1311 def o(path, *args, **kw):
1297 return openerfn(fn(path), *args, **kw)
1312 return openerfn(fn(path), *args, **kw)
1298 return o
1313 return o
1299
1314
1300 def mktempcopy(name, emptyok=False):
1315 def mktempcopy(name, emptyok=False):
1301 """Create a temporary file with the same contents from name
1316 """Create a temporary file with the same contents from name
1302
1317
1303 The permission bits are copied from the original file.
1318 The permission bits are copied from the original file.
1304
1319
1305 If the temporary file is going to be truncated immediately, you
1320 If the temporary file is going to be truncated immediately, you
1306 can use emptyok=True as an optimization.
1321 can use emptyok=True as an optimization.
1307
1322
1308 Returns the name of the temporary file.
1323 Returns the name of the temporary file.
1309 """
1324 """
1310 d, fn = os.path.split(name)
1325 d, fn = os.path.split(name)
1311 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1326 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1312 os.close(fd)
1327 os.close(fd)
1313 # Temporary files are created with mode 0600, which is usually not
1328 # Temporary files are created with mode 0600, which is usually not
1314 # what we want. If the original file already exists, just copy
1329 # what we want. If the original file already exists, just copy
1315 # its mode. Otherwise, manually obey umask.
1330 # its mode. Otherwise, manually obey umask.
1316 try:
1331 try:
1317 st_mode = os.lstat(name).st_mode
1332 st_mode = os.lstat(name).st_mode
1318 except OSError, inst:
1333 except OSError, inst:
1319 if inst.errno != errno.ENOENT:
1334 if inst.errno != errno.ENOENT:
1320 raise
1335 raise
1321 st_mode = 0666 & ~_umask
1336 st_mode = 0666 & ~_umask
1322 os.chmod(temp, st_mode)
1337 os.chmod(temp, st_mode)
1323 if emptyok:
1338 if emptyok:
1324 return temp
1339 return temp
1325 try:
1340 try:
1326 try:
1341 try:
1327 ifp = posixfile(name, "rb")
1342 ifp = posixfile(name, "rb")
1328 except IOError, inst:
1343 except IOError, inst:
1329 if inst.errno == errno.ENOENT:
1344 if inst.errno == errno.ENOENT:
1330 return temp
1345 return temp
1331 if not getattr(inst, 'filename', None):
1346 if not getattr(inst, 'filename', None):
1332 inst.filename = name
1347 inst.filename = name
1333 raise
1348 raise
1334 ofp = posixfile(temp, "wb")
1349 ofp = posixfile(temp, "wb")
1335 for chunk in filechunkiter(ifp):
1350 for chunk in filechunkiter(ifp):
1336 ofp.write(chunk)
1351 ofp.write(chunk)
1337 ifp.close()
1352 ifp.close()
1338 ofp.close()
1353 ofp.close()
1339 except:
1354 except:
1340 try: os.unlink(temp)
1355 try: os.unlink(temp)
1341 except: pass
1356 except: pass
1342 raise
1357 raise
1343 return temp
1358 return temp
1344
1359
1345 class atomictempfile(posixfile):
1360 class atomictempfile(posixfile):
1346 """file-like object that atomically updates a file
1361 """file-like object that atomically updates a file
1347
1362
1348 All writes will be redirected to a temporary copy of the original
1363 All writes will be redirected to a temporary copy of the original
1349 file. When rename is called, the copy is renamed to the original
1364 file. When rename is called, the copy is renamed to the original
1350 name, making the changes visible.
1365 name, making the changes visible.
1351 """
1366 """
1352 def __init__(self, name, mode):
1367 def __init__(self, name, mode):
1353 self.__name = name
1368 self.__name = name
1354 self.temp = mktempcopy(name, emptyok=('w' in mode))
1369 self.temp = mktempcopy(name, emptyok=('w' in mode))
1355 posixfile.__init__(self, self.temp, mode)
1370 posixfile.__init__(self, self.temp, mode)
1356
1371
1357 def rename(self):
1372 def rename(self):
1358 if not self.closed:
1373 if not self.closed:
1359 posixfile.close(self)
1374 posixfile.close(self)
1360 rename(self.temp, localpath(self.__name))
1375 rename(self.temp, localpath(self.__name))
1361
1376
1362 def __del__(self):
1377 def __del__(self):
1363 if not self.closed:
1378 if not self.closed:
1364 try:
1379 try:
1365 os.unlink(self.temp)
1380 os.unlink(self.temp)
1366 except: pass
1381 except: pass
1367 posixfile.close(self)
1382 posixfile.close(self)
1368
1383
1369 class opener(object):
1384 class opener(object):
1370 """Open files relative to a base directory
1385 """Open files relative to a base directory
1371
1386
1372 This class is used to hide the details of COW semantics and
1387 This class is used to hide the details of COW semantics and
1373 remote file access from higher level code.
1388 remote file access from higher level code.
1374 """
1389 """
1375 def __init__(self, base, audit=True):
1390 def __init__(self, base, audit=True):
1376 self.base = base
1391 self.base = base
1377 if audit:
1392 if audit:
1378 self.audit_path = path_auditor(base)
1393 self.audit_path = path_auditor(base)
1379 else:
1394 else:
1380 self.audit_path = always
1395 self.audit_path = always
1381
1396
1382 def __getattr__(self, name):
1397 def __getattr__(self, name):
1383 if name == '_can_symlink':
1398 if name == '_can_symlink':
1384 self._can_symlink = checklink(self.base)
1399 self._can_symlink = checklink(self.base)
1385 return self._can_symlink
1400 return self._can_symlink
1386 raise AttributeError(name)
1401 raise AttributeError(name)
1387
1402
1388 def __call__(self, path, mode="r", text=False, atomictemp=False):
1403 def __call__(self, path, mode="r", text=False, atomictemp=False):
1389 self.audit_path(path)
1404 self.audit_path(path)
1390 f = os.path.join(self.base, path)
1405 f = os.path.join(self.base, path)
1391
1406
1392 if not text and "b" not in mode:
1407 if not text and "b" not in mode:
1393 mode += "b" # for that other OS
1408 mode += "b" # for that other OS
1394
1409
1395 if mode[0] != "r":
1410 if mode[0] != "r":
1396 try:
1411 try:
1397 nlink = nlinks(f)
1412 nlink = nlinks(f)
1398 except OSError:
1413 except OSError:
1399 nlink = 0
1414 nlink = 0
1400 d = os.path.dirname(f)
1415 d = os.path.dirname(f)
1401 if not os.path.isdir(d):
1416 if not os.path.isdir(d):
1402 os.makedirs(d)
1417 os.makedirs(d)
1403 if atomictemp:
1418 if atomictemp:
1404 return atomictempfile(f, mode)
1419 return atomictempfile(f, mode)
1405 if nlink > 1:
1420 if nlink > 1:
1406 rename(mktempcopy(f), f)
1421 rename(mktempcopy(f), f)
1407 return posixfile(f, mode)
1422 return posixfile(f, mode)
1408
1423
1409 def symlink(self, src, dst):
1424 def symlink(self, src, dst):
1410 self.audit_path(dst)
1425 self.audit_path(dst)
1411 linkname = os.path.join(self.base, dst)
1426 linkname = os.path.join(self.base, dst)
1412 try:
1427 try:
1413 os.unlink(linkname)
1428 os.unlink(linkname)
1414 except OSError:
1429 except OSError:
1415 pass
1430 pass
1416
1431
1417 dirname = os.path.dirname(linkname)
1432 dirname = os.path.dirname(linkname)
1418 if not os.path.exists(dirname):
1433 if not os.path.exists(dirname):
1419 os.makedirs(dirname)
1434 os.makedirs(dirname)
1420
1435
1421 if self._can_symlink:
1436 if self._can_symlink:
1422 try:
1437 try:
1423 os.symlink(src, linkname)
1438 os.symlink(src, linkname)
1424 except OSError, err:
1439 except OSError, err:
1425 raise OSError(err.errno, _('could not symlink to %r: %s') %
1440 raise OSError(err.errno, _('could not symlink to %r: %s') %
1426 (src, err.strerror), linkname)
1441 (src, err.strerror), linkname)
1427 else:
1442 else:
1428 f = self(dst, "w")
1443 f = self(dst, "w")
1429 f.write(src)
1444 f.write(src)
1430 f.close()
1445 f.close()
1431
1446
1432 class chunkbuffer(object):
1447 class chunkbuffer(object):
1433 """Allow arbitrary sized chunks of data to be efficiently read from an
1448 """Allow arbitrary sized chunks of data to be efficiently read from an
1434 iterator over chunks of arbitrary size."""
1449 iterator over chunks of arbitrary size."""
1435
1450
1436 def __init__(self, in_iter):
1451 def __init__(self, in_iter):
1437 """in_iter is the iterator that's iterating over the input chunks.
1452 """in_iter is the iterator that's iterating over the input chunks.
1438 targetsize is how big a buffer to try to maintain."""
1453 targetsize is how big a buffer to try to maintain."""
1439 self.iter = iter(in_iter)
1454 self.iter = iter(in_iter)
1440 self.buf = ''
1455 self.buf = ''
1441 self.targetsize = 2**16
1456 self.targetsize = 2**16
1442
1457
1443 def read(self, l):
1458 def read(self, l):
1444 """Read L bytes of data from the iterator of chunks of data.
1459 """Read L bytes of data from the iterator of chunks of data.
1445 Returns less than L bytes if the iterator runs dry."""
1460 Returns less than L bytes if the iterator runs dry."""
1446 if l > len(self.buf) and self.iter:
1461 if l > len(self.buf) and self.iter:
1447 # Clamp to a multiple of self.targetsize
1462 # Clamp to a multiple of self.targetsize
1448 targetsize = max(l, self.targetsize)
1463 targetsize = max(l, self.targetsize)
1449 collector = cStringIO.StringIO()
1464 collector = cStringIO.StringIO()
1450 collector.write(self.buf)
1465 collector.write(self.buf)
1451 collected = len(self.buf)
1466 collected = len(self.buf)
1452 for chunk in self.iter:
1467 for chunk in self.iter:
1453 collector.write(chunk)
1468 collector.write(chunk)
1454 collected += len(chunk)
1469 collected += len(chunk)
1455 if collected >= targetsize:
1470 if collected >= targetsize:
1456 break
1471 break
1457 if collected < targetsize:
1472 if collected < targetsize:
1458 self.iter = False
1473 self.iter = False
1459 self.buf = collector.getvalue()
1474 self.buf = collector.getvalue()
1460 if len(self.buf) == l:
1475 if len(self.buf) == l:
1461 s, self.buf = str(self.buf), ''
1476 s, self.buf = str(self.buf), ''
1462 else:
1477 else:
1463 s, self.buf = self.buf[:l], buffer(self.buf, l)
1478 s, self.buf = self.buf[:l], buffer(self.buf, l)
1464 return s
1479 return s
1465
1480
1466 def filechunkiter(f, size=65536, limit=None):
1481 def filechunkiter(f, size=65536, limit=None):
1467 """Create a generator that produces the data in the file size
1482 """Create a generator that produces the data in the file size
1468 (default 65536) bytes at a time, up to optional limit (default is
1483 (default 65536) bytes at a time, up to optional limit (default is
1469 to read all data). Chunks may be less than size bytes if the
1484 to read all data). Chunks may be less than size bytes if the
1470 chunk is the last chunk in the file, or the file is a socket or
1485 chunk is the last chunk in the file, or the file is a socket or
1471 some other type of file that sometimes reads less data than is
1486 some other type of file that sometimes reads less data than is
1472 requested."""
1487 requested."""
1473 assert size >= 0
1488 assert size >= 0
1474 assert limit is None or limit >= 0
1489 assert limit is None or limit >= 0
1475 while True:
1490 while True:
1476 if limit is None: nbytes = size
1491 if limit is None: nbytes = size
1477 else: nbytes = min(limit, size)
1492 else: nbytes = min(limit, size)
1478 s = nbytes and f.read(nbytes)
1493 s = nbytes and f.read(nbytes)
1479 if not s: break
1494 if not s: break
1480 if limit: limit -= len(s)
1495 if limit: limit -= len(s)
1481 yield s
1496 yield s
1482
1497
1483 def makedate():
1498 def makedate():
1484 lt = time.localtime()
1499 lt = time.localtime()
1485 if lt[8] == 1 and time.daylight:
1500 if lt[8] == 1 and time.daylight:
1486 tz = time.altzone
1501 tz = time.altzone
1487 else:
1502 else:
1488 tz = time.timezone
1503 tz = time.timezone
1489 return time.mktime(lt), tz
1504 return time.mktime(lt), tz
1490
1505
1491 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True, timezone_format=" %+03d%02d"):
1506 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True, timezone_format=" %+03d%02d"):
1492 """represent a (unixtime, offset) tuple as a localized time.
1507 """represent a (unixtime, offset) tuple as a localized time.
1493 unixtime is seconds since the epoch, and offset is the time zone's
1508 unixtime is seconds since the epoch, and offset is the time zone's
1494 number of seconds away from UTC. if timezone is false, do not
1509 number of seconds away from UTC. if timezone is false, do not
1495 append time zone to string."""
1510 append time zone to string."""
1496 t, tz = date or makedate()
1511 t, tz = date or makedate()
1497 s = time.strftime(format, time.gmtime(float(t) - tz))
1512 s = time.strftime(format, time.gmtime(float(t) - tz))
1498 if timezone:
1513 if timezone:
1499 s += timezone_format % (-tz / 3600, ((-tz % 3600) / 60))
1514 s += timezone_format % (-tz / 3600, ((-tz % 3600) / 60))
1500 return s
1515 return s
1501
1516
1502 def strdate(string, format, defaults=[]):
1517 def strdate(string, format, defaults=[]):
1503 """parse a localized time string and return a (unixtime, offset) tuple.
1518 """parse a localized time string and return a (unixtime, offset) tuple.
1504 if the string cannot be parsed, ValueError is raised."""
1519 if the string cannot be parsed, ValueError is raised."""
1505 def timezone(string):
1520 def timezone(string):
1506 tz = string.split()[-1]
1521 tz = string.split()[-1]
1507 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1522 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1508 tz = int(tz)
1523 tz = int(tz)
1509 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1524 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1510 return offset
1525 return offset
1511 if tz == "GMT" or tz == "UTC":
1526 if tz == "GMT" or tz == "UTC":
1512 return 0
1527 return 0
1513 return None
1528 return None
1514
1529
1515 # NOTE: unixtime = localunixtime + offset
1530 # NOTE: unixtime = localunixtime + offset
1516 offset, date = timezone(string), string
1531 offset, date = timezone(string), string
1517 if offset != None:
1532 if offset != None:
1518 date = " ".join(string.split()[:-1])
1533 date = " ".join(string.split()[:-1])
1519
1534
1520 # add missing elements from defaults
1535 # add missing elements from defaults
1521 for part in defaults:
1536 for part in defaults:
1522 found = [True for p in part if ("%"+p) in format]
1537 found = [True for p in part if ("%"+p) in format]
1523 if not found:
1538 if not found:
1524 date += "@" + defaults[part]
1539 date += "@" + defaults[part]
1525 format += "@%" + part[0]
1540 format += "@%" + part[0]
1526
1541
1527 timetuple = time.strptime(date, format)
1542 timetuple = time.strptime(date, format)
1528 localunixtime = int(calendar.timegm(timetuple))
1543 localunixtime = int(calendar.timegm(timetuple))
1529 if offset is None:
1544 if offset is None:
1530 # local timezone
1545 # local timezone
1531 unixtime = int(time.mktime(timetuple))
1546 unixtime = int(time.mktime(timetuple))
1532 offset = unixtime - localunixtime
1547 offset = unixtime - localunixtime
1533 else:
1548 else:
1534 unixtime = localunixtime + offset
1549 unixtime = localunixtime + offset
1535 return unixtime, offset
1550 return unixtime, offset
1536
1551
1537 def parsedate(string, formats=None, defaults=None):
1552 def parsedate(string, formats=None, defaults=None):
1538 """parse a localized time string and return a (unixtime, offset) tuple.
1553 """parse a localized time string and return a (unixtime, offset) tuple.
1539 The date may be a "unixtime offset" string or in one of the specified
1554 The date may be a "unixtime offset" string or in one of the specified
1540 formats."""
1555 formats."""
1541 if not string:
1556 if not string:
1542 return 0, 0
1557 return 0, 0
1543 if not formats:
1558 if not formats:
1544 formats = defaultdateformats
1559 formats = defaultdateformats
1545 string = string.strip()
1560 string = string.strip()
1546 try:
1561 try:
1547 when, offset = map(int, string.split(' '))
1562 when, offset = map(int, string.split(' '))
1548 except ValueError:
1563 except ValueError:
1549 # fill out defaults
1564 # fill out defaults
1550 if not defaults:
1565 if not defaults:
1551 defaults = {}
1566 defaults = {}
1552 now = makedate()
1567 now = makedate()
1553 for part in "d mb yY HI M S".split():
1568 for part in "d mb yY HI M S".split():
1554 if part not in defaults:
1569 if part not in defaults:
1555 if part[0] in "HMS":
1570 if part[0] in "HMS":
1556 defaults[part] = "00"
1571 defaults[part] = "00"
1557 elif part[0] in "dm":
1572 elif part[0] in "dm":
1558 defaults[part] = "1"
1573 defaults[part] = "1"
1559 else:
1574 else:
1560 defaults[part] = datestr(now, "%" + part[0], False)
1575 defaults[part] = datestr(now, "%" + part[0], False)
1561
1576
1562 for format in formats:
1577 for format in formats:
1563 try:
1578 try:
1564 when, offset = strdate(string, format, defaults)
1579 when, offset = strdate(string, format, defaults)
1565 except ValueError:
1580 except ValueError:
1566 pass
1581 pass
1567 else:
1582 else:
1568 break
1583 break
1569 else:
1584 else:
1570 raise Abort(_('invalid date: %r ') % string)
1585 raise Abort(_('invalid date: %r ') % string)
1571 # validate explicit (probably user-specified) date and
1586 # validate explicit (probably user-specified) date and
1572 # time zone offset. values must fit in signed 32 bits for
1587 # time zone offset. values must fit in signed 32 bits for
1573 # current 32-bit linux runtimes. timezones go from UTC-12
1588 # current 32-bit linux runtimes. timezones go from UTC-12
1574 # to UTC+14
1589 # to UTC+14
1575 if abs(when) > 0x7fffffff:
1590 if abs(when) > 0x7fffffff:
1576 raise Abort(_('date exceeds 32 bits: %d') % when)
1591 raise Abort(_('date exceeds 32 bits: %d') % when)
1577 if offset < -50400 or offset > 43200:
1592 if offset < -50400 or offset > 43200:
1578 raise Abort(_('impossible time zone offset: %d') % offset)
1593 raise Abort(_('impossible time zone offset: %d') % offset)
1579 return when, offset
1594 return when, offset
1580
1595
1581 def matchdate(date):
1596 def matchdate(date):
1582 """Return a function that matches a given date match specifier
1597 """Return a function that matches a given date match specifier
1583
1598
1584 Formats include:
1599 Formats include:
1585
1600
1586 '{date}' match a given date to the accuracy provided
1601 '{date}' match a given date to the accuracy provided
1587
1602
1588 '<{date}' on or before a given date
1603 '<{date}' on or before a given date
1589
1604
1590 '>{date}' on or after a given date
1605 '>{date}' on or after a given date
1591
1606
1592 """
1607 """
1593
1608
1594 def lower(date):
1609 def lower(date):
1595 return parsedate(date, extendeddateformats)[0]
1610 return parsedate(date, extendeddateformats)[0]
1596
1611
1597 def upper(date):
1612 def upper(date):
1598 d = dict(mb="12", HI="23", M="59", S="59")
1613 d = dict(mb="12", HI="23", M="59", S="59")
1599 for days in "31 30 29".split():
1614 for days in "31 30 29".split():
1600 try:
1615 try:
1601 d["d"] = days
1616 d["d"] = days
1602 return parsedate(date, extendeddateformats, d)[0]
1617 return parsedate(date, extendeddateformats, d)[0]
1603 except:
1618 except:
1604 pass
1619 pass
1605 d["d"] = "28"
1620 d["d"] = "28"
1606 return parsedate(date, extendeddateformats, d)[0]
1621 return parsedate(date, extendeddateformats, d)[0]
1607
1622
1608 if date[0] == "<":
1623 if date[0] == "<":
1609 when = upper(date[1:])
1624 when = upper(date[1:])
1610 return lambda x: x <= when
1625 return lambda x: x <= when
1611 elif date[0] == ">":
1626 elif date[0] == ">":
1612 when = lower(date[1:])
1627 when = lower(date[1:])
1613 return lambda x: x >= when
1628 return lambda x: x >= when
1614 elif date[0] == "-":
1629 elif date[0] == "-":
1615 try:
1630 try:
1616 days = int(date[1:])
1631 days = int(date[1:])
1617 except ValueError:
1632 except ValueError:
1618 raise Abort(_("invalid day spec: %s") % date[1:])
1633 raise Abort(_("invalid day spec: %s") % date[1:])
1619 when = makedate()[0] - days * 3600 * 24
1634 when = makedate()[0] - days * 3600 * 24
1620 return lambda x: x >= when
1635 return lambda x: x >= when
1621 elif " to " in date:
1636 elif " to " in date:
1622 a, b = date.split(" to ")
1637 a, b = date.split(" to ")
1623 start, stop = lower(a), upper(b)
1638 start, stop = lower(a), upper(b)
1624 return lambda x: x >= start and x <= stop
1639 return lambda x: x >= start and x <= stop
1625 else:
1640 else:
1626 start, stop = lower(date), upper(date)
1641 start, stop = lower(date), upper(date)
1627 return lambda x: x >= start and x <= stop
1642 return lambda x: x >= start and x <= stop
1628
1643
1629 def shortuser(user):
1644 def shortuser(user):
1630 """Return a short representation of a user name or email address."""
1645 """Return a short representation of a user name or email address."""
1631 f = user.find('@')
1646 f = user.find('@')
1632 if f >= 0:
1647 if f >= 0:
1633 user = user[:f]
1648 user = user[:f]
1634 f = user.find('<')
1649 f = user.find('<')
1635 if f >= 0:
1650 if f >= 0:
1636 user = user[f+1:]
1651 user = user[f+1:]
1637 f = user.find(' ')
1652 f = user.find(' ')
1638 if f >= 0:
1653 if f >= 0:
1639 user = user[:f]
1654 user = user[:f]
1640 f = user.find('.')
1655 f = user.find('.')
1641 if f >= 0:
1656 if f >= 0:
1642 user = user[:f]
1657 user = user[:f]
1643 return user
1658 return user
1644
1659
1645 def ellipsis(text, maxlength=400):
1660 def ellipsis(text, maxlength=400):
1646 """Trim string to at most maxlength (default: 400) characters."""
1661 """Trim string to at most maxlength (default: 400) characters."""
1647 if len(text) <= maxlength:
1662 if len(text) <= maxlength:
1648 return text
1663 return text
1649 else:
1664 else:
1650 return "%s..." % (text[:maxlength-3])
1665 return "%s..." % (text[:maxlength-3])
1651
1666
1652 def walkrepos(path):
1667 def walkrepos(path):
1653 '''yield every hg repository under path, recursively.'''
1668 '''yield every hg repository under path, recursively.'''
1654 def errhandler(err):
1669 def errhandler(err):
1655 if err.filename == path:
1670 if err.filename == path:
1656 raise err
1671 raise err
1657
1672
1658 for root, dirs, files in os.walk(path, onerror=errhandler):
1673 for root, dirs, files in os.walk(path, onerror=errhandler):
1659 for d in dirs:
1674 for d in dirs:
1660 if d == '.hg':
1675 if d == '.hg':
1661 yield root
1676 yield root
1662 dirs[:] = []
1677 dirs[:] = []
1663 break
1678 break
1664
1679
1665 _rcpath = None
1680 _rcpath = None
1666
1681
1667 def os_rcpath():
1682 def os_rcpath():
1668 '''return default os-specific hgrc search path'''
1683 '''return default os-specific hgrc search path'''
1669 path = system_rcpath()
1684 path = system_rcpath()
1670 path.extend(user_rcpath())
1685 path.extend(user_rcpath())
1671 path = [os.path.normpath(f) for f in path]
1686 path = [os.path.normpath(f) for f in path]
1672 return path
1687 return path
1673
1688
1674 def rcpath():
1689 def rcpath():
1675 '''return hgrc search path. if env var HGRCPATH is set, use it.
1690 '''return hgrc search path. if env var HGRCPATH is set, use it.
1676 for each item in path, if directory, use files ending in .rc,
1691 for each item in path, if directory, use files ending in .rc,
1677 else use item.
1692 else use item.
1678 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1693 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1679 if no HGRCPATH, use default os-specific path.'''
1694 if no HGRCPATH, use default os-specific path.'''
1680 global _rcpath
1695 global _rcpath
1681 if _rcpath is None:
1696 if _rcpath is None:
1682 if 'HGRCPATH' in os.environ:
1697 if 'HGRCPATH' in os.environ:
1683 _rcpath = []
1698 _rcpath = []
1684 for p in os.environ['HGRCPATH'].split(os.pathsep):
1699 for p in os.environ['HGRCPATH'].split(os.pathsep):
1685 if not p: continue
1700 if not p: continue
1686 if os.path.isdir(p):
1701 if os.path.isdir(p):
1687 for f, kind in osutil.listdir(p):
1702 for f, kind in osutil.listdir(p):
1688 if f.endswith('.rc'):
1703 if f.endswith('.rc'):
1689 _rcpath.append(os.path.join(p, f))
1704 _rcpath.append(os.path.join(p, f))
1690 else:
1705 else:
1691 _rcpath.append(p)
1706 _rcpath.append(p)
1692 else:
1707 else:
1693 _rcpath = os_rcpath()
1708 _rcpath = os_rcpath()
1694 return _rcpath
1709 return _rcpath
1695
1710
1696 def bytecount(nbytes):
1711 def bytecount(nbytes):
1697 '''return byte count formatted as readable string, with units'''
1712 '''return byte count formatted as readable string, with units'''
1698
1713
1699 units = (
1714 units = (
1700 (100, 1<<30, _('%.0f GB')),
1715 (100, 1<<30, _('%.0f GB')),
1701 (10, 1<<30, _('%.1f GB')),
1716 (10, 1<<30, _('%.1f GB')),
1702 (1, 1<<30, _('%.2f GB')),
1717 (1, 1<<30, _('%.2f GB')),
1703 (100, 1<<20, _('%.0f MB')),
1718 (100, 1<<20, _('%.0f MB')),
1704 (10, 1<<20, _('%.1f MB')),
1719 (10, 1<<20, _('%.1f MB')),
1705 (1, 1<<20, _('%.2f MB')),
1720 (1, 1<<20, _('%.2f MB')),
1706 (100, 1<<10, _('%.0f KB')),
1721 (100, 1<<10, _('%.0f KB')),
1707 (10, 1<<10, _('%.1f KB')),
1722 (10, 1<<10, _('%.1f KB')),
1708 (1, 1<<10, _('%.2f KB')),
1723 (1, 1<<10, _('%.2f KB')),
1709 (1, 1, _('%.0f bytes')),
1724 (1, 1, _('%.0f bytes')),
1710 )
1725 )
1711
1726
1712 for multiplier, divisor, format in units:
1727 for multiplier, divisor, format in units:
1713 if nbytes >= divisor * multiplier:
1728 if nbytes >= divisor * multiplier:
1714 return format % (nbytes / float(divisor))
1729 return format % (nbytes / float(divisor))
1715 return units[-1][2] % nbytes
1730 return units[-1][2] % nbytes
1716
1731
1717 def drop_scheme(scheme, path):
1732 def drop_scheme(scheme, path):
1718 sc = scheme + ':'
1733 sc = scheme + ':'
1719 if path.startswith(sc):
1734 if path.startswith(sc):
1720 path = path[len(sc):]
1735 path = path[len(sc):]
1721 if path.startswith('//'):
1736 if path.startswith('//'):
1722 path = path[2:]
1737 path = path[2:]
1723 return path
1738 return path
1724
1739
1725 def uirepr(s):
1740 def uirepr(s):
1726 # Avoid double backslash in Windows path repr()
1741 # Avoid double backslash in Windows path repr()
1727 return repr(s).replace('\\\\', '\\')
1742 return repr(s).replace('\\\\', '\\')
1728
1743
1729 def hidepassword(url):
1744 def hidepassword(url):
1730 '''replaces the password in the url string by three asterisks (***)
1745 '''replaces the password in the url string by three asterisks (***)
1731
1746
1732 >>> hidepassword('http://www.example.com/some/path#fragment')
1747 >>> hidepassword('http://www.example.com/some/path#fragment')
1733 'http://www.example.com/some/path#fragment'
1748 'http://www.example.com/some/path#fragment'
1734 >>> hidepassword('http://me@www.example.com/some/path#fragment')
1749 >>> hidepassword('http://me@www.example.com/some/path#fragment')
1735 'http://me@www.example.com/some/path#fragment'
1750 'http://me@www.example.com/some/path#fragment'
1736 >>> hidepassword('http://me:simplepw@www.example.com/path#frag')
1751 >>> hidepassword('http://me:simplepw@www.example.com/path#frag')
1737 'http://me:***@www.example.com/path#frag'
1752 'http://me:***@www.example.com/path#frag'
1738 >>> hidepassword('http://me:complex:pw@www.example.com/path#frag')
1753 >>> hidepassword('http://me:complex:pw@www.example.com/path#frag')
1739 'http://me:***@www.example.com/path#frag'
1754 'http://me:***@www.example.com/path#frag'
1740 >>> hidepassword('/path/to/repo')
1755 >>> hidepassword('/path/to/repo')
1741 '/path/to/repo'
1756 '/path/to/repo'
1742 >>> hidepassword('relative/path/to/repo')
1757 >>> hidepassword('relative/path/to/repo')
1743 'relative/path/to/repo'
1758 'relative/path/to/repo'
1744 >>> hidepassword('c:\\\\path\\\\to\\\\repo')
1759 >>> hidepassword('c:\\\\path\\\\to\\\\repo')
1745 'c:\\\\path\\\\to\\\\repo'
1760 'c:\\\\path\\\\to\\\\repo'
1746 >>> hidepassword('c:/path/to/repo')
1761 >>> hidepassword('c:/path/to/repo')
1747 'c:/path/to/repo'
1762 'c:/path/to/repo'
1748 >>> hidepassword('bundle://path/to/bundle')
1763 >>> hidepassword('bundle://path/to/bundle')
1749 'bundle://path/to/bundle'
1764 'bundle://path/to/bundle'
1750 '''
1765 '''
1751 url_parts = list(urlparse.urlparse(url))
1766 url_parts = list(urlparse.urlparse(url))
1752 host_with_pw_pattern = re.compile('^([^:]*):([^@]*)@(.*)$')
1767 host_with_pw_pattern = re.compile('^([^:]*):([^@]*)@(.*)$')
1753 if host_with_pw_pattern.match(url_parts[1]):
1768 if host_with_pw_pattern.match(url_parts[1]):
1754 url_parts[1] = re.sub(host_with_pw_pattern, r'\1:***@\3',
1769 url_parts[1] = re.sub(host_with_pw_pattern, r'\1:***@\3',
1755 url_parts[1])
1770 url_parts[1])
1756 return urlparse.urlunparse(url_parts)
1771 return urlparse.urlunparse(url_parts)
1757
1772
General Comments 0
You need to be logged in to leave comments. Login now