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