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