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