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