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