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