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