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