##// END OF EJS Templates
Combined the two os_rcpath methods into a single one near rcpath in mercurial/util.py...
Shane Holloway -
r4097:403c4ddd default
parent child Browse files
Show More
@@ -1,1443 +1,1436 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, 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 find_in_path(name, path, default=None):
214 def find_in_path(name, path, default=None):
215 '''find name in search path. path can be string (will be split
215 '''find name in search path. path can be string (will be split
216 with os.pathsep), or iterable thing that returns strings. if name
216 with os.pathsep), or iterable thing that returns strings. if name
217 found, return path to name. else return default.'''
217 found, return path to name. else return default.'''
218 if isinstance(path, str):
218 if isinstance(path, str):
219 path = path.split(os.pathsep)
219 path = path.split(os.pathsep)
220 for p in path:
220 for p in path:
221 p_name = os.path.join(p, name)
221 p_name = os.path.join(p, name)
222 if os.path.exists(p_name):
222 if os.path.exists(p_name):
223 return p_name
223 return p_name
224 return default
224 return default
225
225
226 def binary(s):
226 def binary(s):
227 """return true if a string is binary data using diff's heuristic"""
227 """return true if a string is binary data using diff's heuristic"""
228 if s and '\0' in s[:4096]:
228 if s and '\0' in s[:4096]:
229 return True
229 return True
230 return False
230 return False
231
231
232 def unique(g):
232 def unique(g):
233 """return the uniq elements of iterable g"""
233 """return the uniq elements of iterable g"""
234 seen = {}
234 seen = {}
235 l = []
235 l = []
236 for f in g:
236 for f in g:
237 if f not in seen:
237 if f not in seen:
238 seen[f] = 1
238 seen[f] = 1
239 l.append(f)
239 l.append(f)
240 return l
240 return l
241
241
242 class Abort(Exception):
242 class Abort(Exception):
243 """Raised if a command needs to print an error and exit."""
243 """Raised if a command needs to print an error and exit."""
244
244
245 class UnexpectedOutput(Abort):
245 class UnexpectedOutput(Abort):
246 """Raised to print an error with part of output and exit."""
246 """Raised to print an error with part of output and exit."""
247
247
248 def always(fn): return True
248 def always(fn): return True
249 def never(fn): return False
249 def never(fn): return False
250
250
251 def expand_glob(pats):
251 def expand_glob(pats):
252 '''On Windows, expand the implicit globs in a list of patterns'''
252 '''On Windows, expand the implicit globs in a list of patterns'''
253 if os.name != 'nt':
253 if os.name != 'nt':
254 return list(pats)
254 return list(pats)
255 ret = []
255 ret = []
256 for p in pats:
256 for p in pats:
257 kind, name = patkind(p, None)
257 kind, name = patkind(p, None)
258 if kind is None:
258 if kind is None:
259 globbed = glob.glob(name)
259 globbed = glob.glob(name)
260 if globbed:
260 if globbed:
261 ret.extend(globbed)
261 ret.extend(globbed)
262 continue
262 continue
263 # if we couldn't expand the glob, just keep it around
263 # if we couldn't expand the glob, just keep it around
264 ret.append(p)
264 ret.append(p)
265 return ret
265 return ret
266
266
267 def patkind(name, dflt_pat='glob'):
267 def patkind(name, dflt_pat='glob'):
268 """Split a string into an optional pattern kind prefix and the
268 """Split a string into an optional pattern kind prefix and the
269 actual pattern."""
269 actual pattern."""
270 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
270 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
271 if name.startswith(prefix + ':'): return name.split(':', 1)
271 if name.startswith(prefix + ':'): return name.split(':', 1)
272 return dflt_pat, name
272 return dflt_pat, name
273
273
274 def globre(pat, head='^', tail='$'):
274 def globre(pat, head='^', tail='$'):
275 "convert a glob pattern into a regexp"
275 "convert a glob pattern into a regexp"
276 i, n = 0, len(pat)
276 i, n = 0, len(pat)
277 res = ''
277 res = ''
278 group = False
278 group = False
279 def peek(): return i < n and pat[i]
279 def peek(): return i < n and pat[i]
280 while i < n:
280 while i < n:
281 c = pat[i]
281 c = pat[i]
282 i = i+1
282 i = i+1
283 if c == '*':
283 if c == '*':
284 if peek() == '*':
284 if peek() == '*':
285 i += 1
285 i += 1
286 res += '.*'
286 res += '.*'
287 else:
287 else:
288 res += '[^/]*'
288 res += '[^/]*'
289 elif c == '?':
289 elif c == '?':
290 res += '.'
290 res += '.'
291 elif c == '[':
291 elif c == '[':
292 j = i
292 j = i
293 if j < n and pat[j] in '!]':
293 if j < n and pat[j] in '!]':
294 j += 1
294 j += 1
295 while j < n and pat[j] != ']':
295 while j < n and pat[j] != ']':
296 j += 1
296 j += 1
297 if j >= n:
297 if j >= n:
298 res += '\\['
298 res += '\\['
299 else:
299 else:
300 stuff = pat[i:j].replace('\\','\\\\')
300 stuff = pat[i:j].replace('\\','\\\\')
301 i = j + 1
301 i = j + 1
302 if stuff[0] == '!':
302 if stuff[0] == '!':
303 stuff = '^' + stuff[1:]
303 stuff = '^' + stuff[1:]
304 elif stuff[0] == '^':
304 elif stuff[0] == '^':
305 stuff = '\\' + stuff
305 stuff = '\\' + stuff
306 res = '%s[%s]' % (res, stuff)
306 res = '%s[%s]' % (res, stuff)
307 elif c == '{':
307 elif c == '{':
308 group = True
308 group = True
309 res += '(?:'
309 res += '(?:'
310 elif c == '}' and group:
310 elif c == '}' and group:
311 res += ')'
311 res += ')'
312 group = False
312 group = False
313 elif c == ',' and group:
313 elif c == ',' and group:
314 res += '|'
314 res += '|'
315 elif c == '\\':
315 elif c == '\\':
316 p = peek()
316 p = peek()
317 if p:
317 if p:
318 i += 1
318 i += 1
319 res += re.escape(p)
319 res += re.escape(p)
320 else:
320 else:
321 res += re.escape(c)
321 res += re.escape(c)
322 else:
322 else:
323 res += re.escape(c)
323 res += re.escape(c)
324 return head + res + tail
324 return head + res + tail
325
325
326 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
326 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
327
327
328 def pathto(n1, n2):
328 def pathto(n1, n2):
329 '''return the relative path from one place to another.
329 '''return the relative path from one place to another.
330 n1 should use os.sep to separate directories
330 n1 should use os.sep to separate directories
331 n2 should use "/" to separate directories
331 n2 should use "/" to separate directories
332 returns an os.sep-separated path.
332 returns an os.sep-separated path.
333 '''
333 '''
334 if not n1: return localpath(n2)
334 if not n1: return localpath(n2)
335 a, b = n1.split(os.sep), n2.split('/')
335 a, b = n1.split(os.sep), n2.split('/')
336 a.reverse()
336 a.reverse()
337 b.reverse()
337 b.reverse()
338 while a and b and a[-1] == b[-1]:
338 while a and b and a[-1] == b[-1]:
339 a.pop()
339 a.pop()
340 b.pop()
340 b.pop()
341 b.reverse()
341 b.reverse()
342 return os.sep.join((['..'] * len(a)) + b)
342 return os.sep.join((['..'] * len(a)) + b)
343
343
344 def canonpath(root, cwd, myname):
344 def canonpath(root, cwd, myname):
345 """return the canonical path of myname, given cwd and root"""
345 """return the canonical path of myname, given cwd and root"""
346 if root == os.sep:
346 if root == os.sep:
347 rootsep = os.sep
347 rootsep = os.sep
348 elif root.endswith(os.sep):
348 elif root.endswith(os.sep):
349 rootsep = root
349 rootsep = root
350 else:
350 else:
351 rootsep = root + os.sep
351 rootsep = root + os.sep
352 name = myname
352 name = myname
353 if not os.path.isabs(name):
353 if not os.path.isabs(name):
354 name = os.path.join(root, cwd, name)
354 name = os.path.join(root, cwd, name)
355 name = os.path.normpath(name)
355 name = os.path.normpath(name)
356 if name != rootsep and name.startswith(rootsep):
356 if name != rootsep and name.startswith(rootsep):
357 name = name[len(rootsep):]
357 name = name[len(rootsep):]
358 audit_path(name)
358 audit_path(name)
359 return pconvert(name)
359 return pconvert(name)
360 elif name == root:
360 elif name == root:
361 return ''
361 return ''
362 else:
362 else:
363 # Determine whether `name' is in the hierarchy at or beneath `root',
363 # Determine whether `name' is in the hierarchy at or beneath `root',
364 # by iterating name=dirname(name) until that causes no change (can't
364 # by iterating name=dirname(name) until that causes no change (can't
365 # check name == '/', because that doesn't work on windows). For each
365 # check name == '/', because that doesn't work on windows). For each
366 # `name', compare dev/inode numbers. If they match, the list `rel'
366 # `name', compare dev/inode numbers. If they match, the list `rel'
367 # holds the reversed list of components making up the relative file
367 # holds the reversed list of components making up the relative file
368 # name we want.
368 # name we want.
369 root_st = os.stat(root)
369 root_st = os.stat(root)
370 rel = []
370 rel = []
371 while True:
371 while True:
372 try:
372 try:
373 name_st = os.stat(name)
373 name_st = os.stat(name)
374 except OSError:
374 except OSError:
375 break
375 break
376 if samestat(name_st, root_st):
376 if samestat(name_st, root_st):
377 if not rel:
377 if not rel:
378 # name was actually the same as root (maybe a symlink)
378 # name was actually the same as root (maybe a symlink)
379 return ''
379 return ''
380 rel.reverse()
380 rel.reverse()
381 name = os.path.join(*rel)
381 name = os.path.join(*rel)
382 audit_path(name)
382 audit_path(name)
383 return pconvert(name)
383 return pconvert(name)
384 dirname, basename = os.path.split(name)
384 dirname, basename = os.path.split(name)
385 rel.append(basename)
385 rel.append(basename)
386 if dirname == name:
386 if dirname == name:
387 break
387 break
388 name = dirname
388 name = dirname
389
389
390 raise Abort('%s not under root' % myname)
390 raise Abort('%s not under root' % myname)
391
391
392 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
392 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
393 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
393 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
394
394
395 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='',
395 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='',
396 src=None, globbed=False):
396 src=None, globbed=False):
397 if not globbed:
397 if not globbed:
398 names = expand_glob(names)
398 names = expand_glob(names)
399 return _matcher(canonroot, cwd, names, inc, exc, head, 'relpath', src)
399 return _matcher(canonroot, cwd, names, inc, exc, head, 'relpath', src)
400
400
401 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
401 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
402 """build a function to match a set of file patterns
402 """build a function to match a set of file patterns
403
403
404 arguments:
404 arguments:
405 canonroot - the canonical root of the tree you're matching against
405 canonroot - the canonical root of the tree you're matching against
406 cwd - the current working directory, if relevant
406 cwd - the current working directory, if relevant
407 names - patterns to find
407 names - patterns to find
408 inc - patterns to include
408 inc - patterns to include
409 exc - patterns to exclude
409 exc - patterns to exclude
410 head - a regex to prepend to patterns to control whether a match is rooted
410 head - a regex to prepend to patterns to control whether a match is rooted
411
411
412 a pattern is one of:
412 a pattern is one of:
413 'glob:<rooted glob>'
413 'glob:<rooted glob>'
414 're:<rooted regexp>'
414 're:<rooted regexp>'
415 'path:<rooted path>'
415 'path:<rooted path>'
416 'relglob:<relative glob>'
416 'relglob:<relative glob>'
417 'relpath:<relative path>'
417 'relpath:<relative path>'
418 'relre:<relative regexp>'
418 'relre:<relative regexp>'
419 '<rooted path or regexp>'
419 '<rooted path or regexp>'
420
420
421 returns:
421 returns:
422 a 3-tuple containing
422 a 3-tuple containing
423 - list of explicit non-pattern names passed in
423 - list of explicit non-pattern names passed in
424 - a bool match(filename) function
424 - a bool match(filename) function
425 - a bool indicating if any patterns were passed in
425 - a bool indicating if any patterns were passed in
426
426
427 todo:
427 todo:
428 make head regex a rooted bool
428 make head regex a rooted bool
429 """
429 """
430
430
431 def contains_glob(name):
431 def contains_glob(name):
432 for c in name:
432 for c in name:
433 if c in _globchars: return True
433 if c in _globchars: return True
434 return False
434 return False
435
435
436 def regex(kind, name, tail):
436 def regex(kind, name, tail):
437 '''convert a pattern into a regular expression'''
437 '''convert a pattern into a regular expression'''
438 if kind == 're':
438 if kind == 're':
439 return name
439 return name
440 elif kind == 'path':
440 elif kind == 'path':
441 return '^' + re.escape(name) + '(?:/|$)'
441 return '^' + re.escape(name) + '(?:/|$)'
442 elif kind == 'relglob':
442 elif kind == 'relglob':
443 return head + globre(name, '(?:|.*/)', tail)
443 return head + globre(name, '(?:|.*/)', tail)
444 elif kind == 'relpath':
444 elif kind == 'relpath':
445 return head + re.escape(name) + tail
445 return head + re.escape(name) + tail
446 elif kind == 'relre':
446 elif kind == 'relre':
447 if name.startswith('^'):
447 if name.startswith('^'):
448 return name
448 return name
449 return '.*' + name
449 return '.*' + name
450 return head + globre(name, '', tail)
450 return head + globre(name, '', tail)
451
451
452 def matchfn(pats, tail):
452 def matchfn(pats, tail):
453 """build a matching function from a set of patterns"""
453 """build a matching function from a set of patterns"""
454 if not pats:
454 if not pats:
455 return
455 return
456 matches = []
456 matches = []
457 for k, p in pats:
457 for k, p in pats:
458 try:
458 try:
459 pat = '(?:%s)' % regex(k, p, tail)
459 pat = '(?:%s)' % regex(k, p, tail)
460 matches.append(re.compile(pat).match)
460 matches.append(re.compile(pat).match)
461 except re.error:
461 except re.error:
462 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
462 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
463 else: raise Abort("invalid pattern (%s): %s" % (k, p))
463 else: raise Abort("invalid pattern (%s): %s" % (k, p))
464
464
465 def buildfn(text):
465 def buildfn(text):
466 for m in matches:
466 for m in matches:
467 r = m(text)
467 r = m(text)
468 if r:
468 if r:
469 return r
469 return r
470
470
471 return buildfn
471 return buildfn
472
472
473 def globprefix(pat):
473 def globprefix(pat):
474 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
474 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
475 root = []
475 root = []
476 for p in pat.split(os.sep):
476 for p in pat.split(os.sep):
477 if contains_glob(p): break
477 if contains_glob(p): break
478 root.append(p)
478 root.append(p)
479 return '/'.join(root)
479 return '/'.join(root)
480
480
481 pats = []
481 pats = []
482 files = []
482 files = []
483 roots = []
483 roots = []
484 for kind, name in [patkind(p, dflt_pat) for p in names]:
484 for kind, name in [patkind(p, dflt_pat) for p in names]:
485 if kind in ('glob', 'relpath'):
485 if kind in ('glob', 'relpath'):
486 name = canonpath(canonroot, cwd, name)
486 name = canonpath(canonroot, cwd, name)
487 if name == '':
487 if name == '':
488 kind, name = 'glob', '**'
488 kind, name = 'glob', '**'
489 if kind in ('glob', 'path', 're'):
489 if kind in ('glob', 'path', 're'):
490 pats.append((kind, name))
490 pats.append((kind, name))
491 if kind == 'glob':
491 if kind == 'glob':
492 root = globprefix(name)
492 root = globprefix(name)
493 if root: roots.append(root)
493 if root: roots.append(root)
494 elif kind == 'relpath':
494 elif kind == 'relpath':
495 files.append((kind, name))
495 files.append((kind, name))
496 roots.append(name)
496 roots.append(name)
497
497
498 patmatch = matchfn(pats, '$') or always
498 patmatch = matchfn(pats, '$') or always
499 filematch = matchfn(files, '(?:/|$)') or always
499 filematch = matchfn(files, '(?:/|$)') or always
500 incmatch = always
500 incmatch = always
501 if inc:
501 if inc:
502 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
502 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
503 incmatch = matchfn(inckinds, '(?:/|$)')
503 incmatch = matchfn(inckinds, '(?:/|$)')
504 excmatch = lambda fn: False
504 excmatch = lambda fn: False
505 if exc:
505 if exc:
506 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
506 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
507 excmatch = matchfn(exckinds, '(?:/|$)')
507 excmatch = matchfn(exckinds, '(?:/|$)')
508
508
509 return (roots,
509 return (roots,
510 lambda fn: (incmatch(fn) and not excmatch(fn) and
510 lambda fn: (incmatch(fn) and not excmatch(fn) and
511 (fn.endswith('/') or
511 (fn.endswith('/') or
512 (not pats and not files) or
512 (not pats and not files) or
513 (pats and patmatch(fn)) or
513 (pats and patmatch(fn)) or
514 (files and filematch(fn)))),
514 (files and filematch(fn)))),
515 (inc or exc or (pats and pats != [('glob', '**')])) and True)
515 (inc or exc or (pats and pats != [('glob', '**')])) and True)
516
516
517 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
517 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
518 '''enhanced shell command execution.
518 '''enhanced shell command execution.
519 run with environment maybe modified, maybe in different dir.
519 run with environment maybe modified, maybe in different dir.
520
520
521 if command fails and onerr is None, return status. if ui object,
521 if command fails and onerr is None, return status. if ui object,
522 print error message and return status, else raise onerr object as
522 print error message and return status, else raise onerr object as
523 exception.'''
523 exception.'''
524 def py2shell(val):
524 def py2shell(val):
525 'convert python object into string that is useful to shell'
525 'convert python object into string that is useful to shell'
526 if val in (None, False):
526 if val in (None, False):
527 return '0'
527 return '0'
528 if val == True:
528 if val == True:
529 return '1'
529 return '1'
530 return str(val)
530 return str(val)
531 oldenv = {}
531 oldenv = {}
532 for k in environ:
532 for k in environ:
533 oldenv[k] = os.environ.get(k)
533 oldenv[k] = os.environ.get(k)
534 if cwd is not None:
534 if cwd is not None:
535 oldcwd = os.getcwd()
535 oldcwd = os.getcwd()
536 origcmd = cmd
536 origcmd = cmd
537 if os.name == 'nt':
537 if os.name == 'nt':
538 cmd = '"%s"' % cmd
538 cmd = '"%s"' % cmd
539 try:
539 try:
540 for k, v in environ.iteritems():
540 for k, v in environ.iteritems():
541 os.environ[k] = py2shell(v)
541 os.environ[k] = py2shell(v)
542 if cwd is not None and oldcwd != cwd:
542 if cwd is not None and oldcwd != cwd:
543 os.chdir(cwd)
543 os.chdir(cwd)
544 rc = os.system(cmd)
544 rc = os.system(cmd)
545 if rc and onerr:
545 if rc and onerr:
546 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
546 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
547 explain_exit(rc)[0])
547 explain_exit(rc)[0])
548 if errprefix:
548 if errprefix:
549 errmsg = '%s: %s' % (errprefix, errmsg)
549 errmsg = '%s: %s' % (errprefix, errmsg)
550 try:
550 try:
551 onerr.warn(errmsg + '\n')
551 onerr.warn(errmsg + '\n')
552 except AttributeError:
552 except AttributeError:
553 raise onerr(errmsg)
553 raise onerr(errmsg)
554 return rc
554 return rc
555 finally:
555 finally:
556 for k, v in oldenv.iteritems():
556 for k, v in oldenv.iteritems():
557 if v is None:
557 if v is None:
558 del os.environ[k]
558 del os.environ[k]
559 else:
559 else:
560 os.environ[k] = v
560 os.environ[k] = v
561 if cwd is not None and oldcwd != cwd:
561 if cwd is not None and oldcwd != cwd:
562 os.chdir(oldcwd)
562 os.chdir(oldcwd)
563
563
564 def rename(src, dst):
564 def rename(src, dst):
565 """forcibly rename a file"""
565 """forcibly rename a file"""
566 try:
566 try:
567 os.rename(src, dst)
567 os.rename(src, dst)
568 except OSError, err:
568 except OSError, err:
569 # on windows, rename to existing file is not allowed, so we
569 # on windows, rename to existing file is not allowed, so we
570 # must delete destination first. but if file is open, unlink
570 # must delete destination first. but if file is open, unlink
571 # schedules it for delete but does not delete it. rename
571 # schedules it for delete but does not delete it. rename
572 # happens immediately even for open files, so we create
572 # happens immediately even for open files, so we create
573 # temporary file, delete it, rename destination to that name,
573 # temporary file, delete it, rename destination to that name,
574 # then delete that. then rename is safe to do.
574 # then delete that. then rename is safe to do.
575 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
575 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
576 os.close(fd)
576 os.close(fd)
577 os.unlink(temp)
577 os.unlink(temp)
578 os.rename(dst, temp)
578 os.rename(dst, temp)
579 os.unlink(temp)
579 os.unlink(temp)
580 os.rename(src, dst)
580 os.rename(src, dst)
581
581
582 def unlink(f):
582 def unlink(f):
583 """unlink and remove the directory if it is empty"""
583 """unlink and remove the directory if it is empty"""
584 os.unlink(f)
584 os.unlink(f)
585 # try removing directories that might now be empty
585 # try removing directories that might now be empty
586 try:
586 try:
587 os.removedirs(os.path.dirname(f))
587 os.removedirs(os.path.dirname(f))
588 except OSError:
588 except OSError:
589 pass
589 pass
590
590
591 def copyfile(src, dest):
591 def copyfile(src, dest):
592 "copy a file, preserving mode"
592 "copy a file, preserving mode"
593 try:
593 try:
594 shutil.copyfile(src, dest)
594 shutil.copyfile(src, dest)
595 shutil.copymode(src, dest)
595 shutil.copymode(src, dest)
596 except shutil.Error, inst:
596 except shutil.Error, inst:
597 raise Abort(str(inst))
597 raise Abort(str(inst))
598
598
599 def copyfiles(src, dst, hardlink=None):
599 def copyfiles(src, dst, hardlink=None):
600 """Copy a directory tree using hardlinks if possible"""
600 """Copy a directory tree using hardlinks if possible"""
601
601
602 if hardlink is None:
602 if hardlink is None:
603 hardlink = (os.stat(src).st_dev ==
603 hardlink = (os.stat(src).st_dev ==
604 os.stat(os.path.dirname(dst)).st_dev)
604 os.stat(os.path.dirname(dst)).st_dev)
605
605
606 if os.path.isdir(src):
606 if os.path.isdir(src):
607 os.mkdir(dst)
607 os.mkdir(dst)
608 for name in os.listdir(src):
608 for name in os.listdir(src):
609 srcname = os.path.join(src, name)
609 srcname = os.path.join(src, name)
610 dstname = os.path.join(dst, name)
610 dstname = os.path.join(dst, name)
611 copyfiles(srcname, dstname, hardlink)
611 copyfiles(srcname, dstname, hardlink)
612 else:
612 else:
613 if hardlink:
613 if hardlink:
614 try:
614 try:
615 os_link(src, dst)
615 os_link(src, dst)
616 except (IOError, OSError):
616 except (IOError, OSError):
617 hardlink = False
617 hardlink = False
618 shutil.copy(src, dst)
618 shutil.copy(src, dst)
619 else:
619 else:
620 shutil.copy(src, dst)
620 shutil.copy(src, dst)
621
621
622 def audit_path(path):
622 def audit_path(path):
623 """Abort if path contains dangerous components"""
623 """Abort if path contains dangerous components"""
624 parts = os.path.normcase(path).split(os.sep)
624 parts = os.path.normcase(path).split(os.sep)
625 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
625 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
626 or os.pardir in parts):
626 or os.pardir in parts):
627 raise Abort(_("path contains illegal component: %s\n") % path)
627 raise Abort(_("path contains illegal component: %s\n") % path)
628
628
629 def _makelock_file(info, pathname):
629 def _makelock_file(info, pathname):
630 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
630 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
631 os.write(ld, info)
631 os.write(ld, info)
632 os.close(ld)
632 os.close(ld)
633
633
634 def _readlock_file(pathname):
634 def _readlock_file(pathname):
635 return posixfile(pathname).read()
635 return posixfile(pathname).read()
636
636
637 def nlinks(pathname):
637 def nlinks(pathname):
638 """Return number of hardlinks for the given file."""
638 """Return number of hardlinks for the given file."""
639 return os.lstat(pathname).st_nlink
639 return os.lstat(pathname).st_nlink
640
640
641 if hasattr(os, 'link'):
641 if hasattr(os, 'link'):
642 os_link = os.link
642 os_link = os.link
643 else:
643 else:
644 def os_link(src, dst):
644 def os_link(src, dst):
645 raise OSError(0, _("Hardlinks not supported"))
645 raise OSError(0, _("Hardlinks not supported"))
646
646
647 def fstat(fp):
647 def fstat(fp):
648 '''stat file object that may not have fileno method.'''
648 '''stat file object that may not have fileno method.'''
649 try:
649 try:
650 return os.fstat(fp.fileno())
650 return os.fstat(fp.fileno())
651 except AttributeError:
651 except AttributeError:
652 return os.stat(fp.name)
652 return os.stat(fp.name)
653
653
654 posixfile = file
654 posixfile = file
655
655
656 def is_win_9x():
656 def is_win_9x():
657 '''return true if run on windows 95, 98 or me.'''
657 '''return true if run on windows 95, 98 or me.'''
658 try:
658 try:
659 return sys.getwindowsversion()[3] == 1
659 return sys.getwindowsversion()[3] == 1
660 except AttributeError:
660 except AttributeError:
661 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
661 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
662
662
663 getuser_fallback = None
663 getuser_fallback = None
664
664
665 def getuser():
665 def getuser():
666 '''return name of current user'''
666 '''return name of current user'''
667 try:
667 try:
668 return getpass.getuser()
668 return getpass.getuser()
669 except ImportError:
669 except ImportError:
670 # import of pwd will fail on windows - try fallback
670 # import of pwd will fail on windows - try fallback
671 if getuser_fallback:
671 if getuser_fallback:
672 return getuser_fallback()
672 return getuser_fallback()
673 # raised if win32api not available
673 # raised if win32api not available
674 raise Abort(_('user name not available - set USERNAME '
674 raise Abort(_('user name not available - set USERNAME '
675 'environment variable'))
675 'environment variable'))
676
676
677 def username(uid=None):
677 def username(uid=None):
678 """Return the name of the user with the given uid.
678 """Return the name of the user with the given uid.
679
679
680 If uid is None, return the name of the current user."""
680 If uid is None, return the name of the current user."""
681 try:
681 try:
682 import pwd
682 import pwd
683 if uid is None:
683 if uid is None:
684 uid = os.getuid()
684 uid = os.getuid()
685 try:
685 try:
686 return pwd.getpwuid(uid)[0]
686 return pwd.getpwuid(uid)[0]
687 except KeyError:
687 except KeyError:
688 return str(uid)
688 return str(uid)
689 except ImportError:
689 except ImportError:
690 return None
690 return None
691
691
692 def groupname(gid=None):
692 def groupname(gid=None):
693 """Return the name of the group with the given gid.
693 """Return the name of the group with the given gid.
694
694
695 If gid is None, return the name of the current group."""
695 If gid is None, return the name of the current group."""
696 try:
696 try:
697 import grp
697 import grp
698 if gid is None:
698 if gid is None:
699 gid = os.getgid()
699 gid = os.getgid()
700 try:
700 try:
701 return grp.getgrgid(gid)[0]
701 return grp.getgrgid(gid)[0]
702 except KeyError:
702 except KeyError:
703 return str(gid)
703 return str(gid)
704 except ImportError:
704 except ImportError:
705 return None
705 return None
706
706
707 # File system features
707 # File system features
708
708
709 def checkfolding(path):
709 def checkfolding(path):
710 """
710 """
711 Check whether the given path is on a case-sensitive filesystem
711 Check whether the given path is on a case-sensitive filesystem
712
712
713 Requires a path (like /foo/.hg) ending with a foldable final
713 Requires a path (like /foo/.hg) ending with a foldable final
714 directory component.
714 directory component.
715 """
715 """
716 s1 = os.stat(path)
716 s1 = os.stat(path)
717 d, b = os.path.split(path)
717 d, b = os.path.split(path)
718 p2 = os.path.join(d, b.upper())
718 p2 = os.path.join(d, b.upper())
719 if path == p2:
719 if path == p2:
720 p2 = os.path.join(d, b.lower())
720 p2 = os.path.join(d, b.lower())
721 try:
721 try:
722 s2 = os.stat(p2)
722 s2 = os.stat(p2)
723 if s2 == s1:
723 if s2 == s1:
724 return False
724 return False
725 return True
725 return True
726 except:
726 except:
727 return True
727 return True
728
728
729 def checkexec(path):
729 def checkexec(path):
730 """
730 """
731 Check whether the given path is on a filesystem with UNIX-like exec flags
731 Check whether the given path is on a filesystem with UNIX-like exec flags
732
732
733 Requires a directory (like /foo/.hg)
733 Requires a directory (like /foo/.hg)
734 """
734 """
735 fh, fn = tempfile.mkstemp("", "", path)
735 fh, fn = tempfile.mkstemp("", "", path)
736 os.close(fh)
736 os.close(fh)
737 m = os.stat(fn).st_mode
737 m = os.stat(fn).st_mode
738 os.chmod(fn, m ^ 0111)
738 os.chmod(fn, m ^ 0111)
739 r = (os.stat(fn).st_mode != m)
739 r = (os.stat(fn).st_mode != m)
740 os.unlink(fn)
740 os.unlink(fn)
741 return r
741 return r
742
742
743 def execfunc(path, fallback):
743 def execfunc(path, fallback):
744 '''return an is_exec() function with default to fallback'''
744 '''return an is_exec() function with default to fallback'''
745 if checkexec(path):
745 if checkexec(path):
746 return lambda x: is_exec(os.path.join(path, x))
746 return lambda x: is_exec(os.path.join(path, x))
747 return fallback
747 return fallback
748
748
749 def checklink(path):
749 def checklink(path):
750 """check whether the given path is on a symlink-capable filesystem"""
750 """check whether the given path is on a symlink-capable filesystem"""
751 # mktemp is not racy because symlink creation will fail if the
751 # mktemp is not racy because symlink creation will fail if the
752 # file already exists
752 # file already exists
753 name = tempfile.mktemp(dir=path)
753 name = tempfile.mktemp(dir=path)
754 try:
754 try:
755 os.symlink(".", name)
755 os.symlink(".", name)
756 os.unlink(name)
756 os.unlink(name)
757 return True
757 return True
758 except (OSError, AttributeError):
758 except (OSError, AttributeError):
759 return False
759 return False
760
760
761 def linkfunc(path, fallback):
761 def linkfunc(path, fallback):
762 '''return an is_link() function with default to fallback'''
762 '''return an is_link() function with default to fallback'''
763 if checklink(path):
763 if checklink(path):
764 return lambda x: is_link(os.path.join(path, x))
764 return lambda x: is_link(os.path.join(path, x))
765 return fallback
765 return fallback
766
766
767 # Platform specific variants
767 # Platform specific variants
768 if os.name == 'nt':
768 if os.name == 'nt':
769 import msvcrt
769 import msvcrt
770 nulldev = 'NUL:'
770 nulldev = 'NUL:'
771
771
772 class winstdout:
772 class winstdout:
773 '''stdout on windows misbehaves if sent through a pipe'''
773 '''stdout on windows misbehaves if sent through a pipe'''
774
774
775 def __init__(self, fp):
775 def __init__(self, fp):
776 self.fp = fp
776 self.fp = fp
777
777
778 def __getattr__(self, key):
778 def __getattr__(self, key):
779 return getattr(self.fp, key)
779 return getattr(self.fp, key)
780
780
781 def close(self):
781 def close(self):
782 try:
782 try:
783 self.fp.close()
783 self.fp.close()
784 except: pass
784 except: pass
785
785
786 def write(self, s):
786 def write(self, s):
787 try:
787 try:
788 return self.fp.write(s)
788 return self.fp.write(s)
789 except IOError, inst:
789 except IOError, inst:
790 if inst.errno != 0: raise
790 if inst.errno != 0: raise
791 self.close()
791 self.close()
792 raise IOError(errno.EPIPE, 'Broken pipe')
792 raise IOError(errno.EPIPE, 'Broken pipe')
793
793
794 sys.stdout = winstdout(sys.stdout)
794 sys.stdout = winstdout(sys.stdout)
795
795
796 def system_rcpath():
796 def system_rcpath():
797 try:
797 try:
798 return system_rcpath_win32()
798 return system_rcpath_win32()
799 except:
799 except:
800 return [r'c:\mercurial\mercurial.ini']
800 return [r'c:\mercurial\mercurial.ini']
801
801
802 def os_rcpath():
803 '''return default os-specific hgrc search path'''
804 path = system_rcpath()
805 path.extend(user_rcpath())
806 path = [os.path.normpath(f) for f in path]
807 return path
808
809 def user_rcpath():
802 def user_rcpath():
810 '''return os-specific hgrc search path to the user dir'''
803 '''return os-specific hgrc search path to the user dir'''
811 path = [os.path.join(os.path.expanduser('~'), 'mercurial.ini')]
804 path = [os.path.join(os.path.expanduser('~'), 'mercurial.ini')]
812 userprofile = os.environ.get('USERPROFILE')
805 userprofile = os.environ.get('USERPROFILE')
813 if userprofile:
806 if userprofile:
814 path.append(os.path.join(userprofile, 'mercurial.ini'))
807 path.append(os.path.join(userprofile, 'mercurial.ini'))
815 return path
808 return path
816
809
817 def parse_patch_output(output_line):
810 def parse_patch_output(output_line):
818 """parses the output produced by patch and returns the file name"""
811 """parses the output produced by patch and returns the file name"""
819 pf = output_line[14:]
812 pf = output_line[14:]
820 if pf[0] == '`':
813 if pf[0] == '`':
821 pf = pf[1:-1] # Remove the quotes
814 pf = pf[1:-1] # Remove the quotes
822 return pf
815 return pf
823
816
824 def testpid(pid):
817 def testpid(pid):
825 '''return False if pid dead, True if running or not known'''
818 '''return False if pid dead, True if running or not known'''
826 return True
819 return True
827
820
828 def set_exec(f, mode):
821 def set_exec(f, mode):
829 pass
822 pass
830
823
831 def set_link(f, mode):
824 def set_link(f, mode):
832 pass
825 pass
833
826
834 def set_binary(fd):
827 def set_binary(fd):
835 msvcrt.setmode(fd.fileno(), os.O_BINARY)
828 msvcrt.setmode(fd.fileno(), os.O_BINARY)
836
829
837 def pconvert(path):
830 def pconvert(path):
838 return path.replace("\\", "/")
831 return path.replace("\\", "/")
839
832
840 def localpath(path):
833 def localpath(path):
841 return path.replace('/', '\\')
834 return path.replace('/', '\\')
842
835
843 def normpath(path):
836 def normpath(path):
844 return pconvert(os.path.normpath(path))
837 return pconvert(os.path.normpath(path))
845
838
846 makelock = _makelock_file
839 makelock = _makelock_file
847 readlock = _readlock_file
840 readlock = _readlock_file
848
841
849 def samestat(s1, s2):
842 def samestat(s1, s2):
850 return False
843 return False
851
844
852 # A sequence of backslashes is special iff it precedes a double quote:
845 # A sequence of backslashes is special iff it precedes a double quote:
853 # - if there's an even number of backslashes, the double quote is not
846 # - if there's an even number of backslashes, the double quote is not
854 # quoted (i.e. it ends the quoted region)
847 # quoted (i.e. it ends the quoted region)
855 # - if there's an odd number of backslashes, the double quote is quoted
848 # - if there's an odd number of backslashes, the double quote is quoted
856 # - in both cases, every pair of backslashes is unquoted into a single
849 # - in both cases, every pair of backslashes is unquoted into a single
857 # backslash
850 # backslash
858 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
851 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
859 # So, to quote a string, we must surround it in double quotes, double
852 # So, to quote a string, we must surround it in double quotes, double
860 # the number of backslashes that preceed double quotes and add another
853 # the number of backslashes that preceed double quotes and add another
861 # backslash before every double quote (being careful with the double
854 # backslash before every double quote (being careful with the double
862 # quote we've appended to the end)
855 # quote we've appended to the end)
863 _quotere = None
856 _quotere = None
864 def shellquote(s):
857 def shellquote(s):
865 global _quotere
858 global _quotere
866 if _quotere is None:
859 if _quotere is None:
867 _quotere = re.compile(r'(\\*)("|\\$)')
860 _quotere = re.compile(r'(\\*)("|\\$)')
868 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
861 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
869
862
870 def explain_exit(code):
863 def explain_exit(code):
871 return _("exited with status %d") % code, code
864 return _("exited with status %d") % code, code
872
865
873 # if you change this stub into a real check, please try to implement the
866 # if you change this stub into a real check, please try to implement the
874 # username and groupname functions above, too.
867 # username and groupname functions above, too.
875 def isowner(fp, st=None):
868 def isowner(fp, st=None):
876 return True
869 return True
877
870
878 try:
871 try:
879 # override functions with win32 versions if possible
872 # override functions with win32 versions if possible
880 from util_win32 import *
873 from util_win32 import *
881 if not is_win_9x():
874 if not is_win_9x():
882 posixfile = posixfile_nt
875 posixfile = posixfile_nt
883 except ImportError:
876 except ImportError:
884 pass
877 pass
885
878
886 else:
879 else:
887 nulldev = '/dev/null'
880 nulldev = '/dev/null'
888 _umask = os.umask(0)
881 _umask = os.umask(0)
889 os.umask(_umask)
882 os.umask(_umask)
890
883
891 def rcfiles(path):
884 def rcfiles(path):
892 rcs = [os.path.join(path, 'hgrc')]
885 rcs = [os.path.join(path, 'hgrc')]
893 rcdir = os.path.join(path, 'hgrc.d')
886 rcdir = os.path.join(path, 'hgrc.d')
894 try:
887 try:
895 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
888 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
896 if f.endswith(".rc")])
889 if f.endswith(".rc")])
897 except OSError:
890 except OSError:
898 pass
891 pass
899 return rcs
892 return rcs
900
893
901 def os_rcpath():
902 '''return default os-specific hgrc search path'''
903 path = system_rcpath()
904 path.extend(user_rcpath())
905 path = [os.path.normpath(f) for f in path]
906 return path
907
908 def system_rcpath():
894 def system_rcpath():
909 path = []
895 path = []
910 # old mod_python does not set sys.argv
896 # old mod_python does not set sys.argv
911 if len(getattr(sys, 'argv', [])) > 0:
897 if len(getattr(sys, 'argv', [])) > 0:
912 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
898 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
913 '/../etc/mercurial'))
899 '/../etc/mercurial'))
914 path.extend(rcfiles('/etc/mercurial'))
900 path.extend(rcfiles('/etc/mercurial'))
915 return path
901 return path
916
902
917 def user_rcpath():
903 def user_rcpath():
918 return [os.path.expanduser('~/.hgrc')]
904 return [os.path.expanduser('~/.hgrc')]
919
905
920 def parse_patch_output(output_line):
906 def parse_patch_output(output_line):
921 """parses the output produced by patch and returns the file name"""
907 """parses the output produced by patch and returns the file name"""
922 pf = output_line[14:]
908 pf = output_line[14:]
923 if pf.startswith("'") and pf.endswith("'") and " " in pf:
909 if pf.startswith("'") and pf.endswith("'") and " " in pf:
924 pf = pf[1:-1] # Remove the quotes
910 pf = pf[1:-1] # Remove the quotes
925 return pf
911 return pf
926
912
927 def is_exec(f):
913 def is_exec(f):
928 """check whether a file is executable"""
914 """check whether a file is executable"""
929 return (os.lstat(f).st_mode & 0100 != 0)
915 return (os.lstat(f).st_mode & 0100 != 0)
930
916
931 def set_exec(f, mode):
917 def set_exec(f, mode):
932 s = os.lstat(f).st_mode
918 s = os.lstat(f).st_mode
933 if (s & 0100 != 0) == mode:
919 if (s & 0100 != 0) == mode:
934 return
920 return
935 if mode:
921 if mode:
936 # Turn on +x for every +r bit when making a file executable
922 # Turn on +x for every +r bit when making a file executable
937 # and obey umask.
923 # and obey umask.
938 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
924 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
939 else:
925 else:
940 os.chmod(f, s & 0666)
926 os.chmod(f, s & 0666)
941
927
942 def is_link(f):
928 def is_link(f):
943 """check whether a file is a symlink"""
929 """check whether a file is a symlink"""
944 return (os.lstat(f).st_mode & 0120000 == 0120000)
930 return (os.lstat(f).st_mode & 0120000 == 0120000)
945
931
946 def set_link(f, mode):
932 def set_link(f, mode):
947 """make a file a symbolic link/regular file
933 """make a file a symbolic link/regular file
948
934
949 if a file is changed to a link, its contents become the link data
935 if a file is changed to a link, its contents become the link data
950 if a link is changed to a file, its link data become its contents
936 if a link is changed to a file, its link data become its contents
951 """
937 """
952
938
953 m = is_link(f)
939 m = is_link(f)
954 if m == bool(mode):
940 if m == bool(mode):
955 return
941 return
956
942
957 if mode: # switch file to link
943 if mode: # switch file to link
958 data = file(f).read()
944 data = file(f).read()
959 os.unlink(f)
945 os.unlink(f)
960 os.symlink(data, f)
946 os.symlink(data, f)
961 else:
947 else:
962 data = os.readlink(f)
948 data = os.readlink(f)
963 os.unlink(f)
949 os.unlink(f)
964 file(f, "w").write(data)
950 file(f, "w").write(data)
965
951
966 def set_binary(fd):
952 def set_binary(fd):
967 pass
953 pass
968
954
969 def pconvert(path):
955 def pconvert(path):
970 return path
956 return path
971
957
972 def localpath(path):
958 def localpath(path):
973 return path
959 return path
974
960
975 normpath = os.path.normpath
961 normpath = os.path.normpath
976 samestat = os.path.samestat
962 samestat = os.path.samestat
977
963
978 def makelock(info, pathname):
964 def makelock(info, pathname):
979 try:
965 try:
980 os.symlink(info, pathname)
966 os.symlink(info, pathname)
981 except OSError, why:
967 except OSError, why:
982 if why.errno == errno.EEXIST:
968 if why.errno == errno.EEXIST:
983 raise
969 raise
984 else:
970 else:
985 _makelock_file(info, pathname)
971 _makelock_file(info, pathname)
986
972
987 def readlock(pathname):
973 def readlock(pathname):
988 try:
974 try:
989 return os.readlink(pathname)
975 return os.readlink(pathname)
990 except OSError, why:
976 except OSError, why:
991 if why.errno == errno.EINVAL:
977 if why.errno == errno.EINVAL:
992 return _readlock_file(pathname)
978 return _readlock_file(pathname)
993 else:
979 else:
994 raise
980 raise
995
981
996 def shellquote(s):
982 def shellquote(s):
997 return "'%s'" % s.replace("'", "'\\''")
983 return "'%s'" % s.replace("'", "'\\''")
998
984
999 def testpid(pid):
985 def testpid(pid):
1000 '''return False if pid dead, True if running or not sure'''
986 '''return False if pid dead, True if running or not sure'''
1001 try:
987 try:
1002 os.kill(pid, 0)
988 os.kill(pid, 0)
1003 return True
989 return True
1004 except OSError, inst:
990 except OSError, inst:
1005 return inst.errno != errno.ESRCH
991 return inst.errno != errno.ESRCH
1006
992
1007 def explain_exit(code):
993 def explain_exit(code):
1008 """return a 2-tuple (desc, code) describing a process's status"""
994 """return a 2-tuple (desc, code) describing a process's status"""
1009 if os.WIFEXITED(code):
995 if os.WIFEXITED(code):
1010 val = os.WEXITSTATUS(code)
996 val = os.WEXITSTATUS(code)
1011 return _("exited with status %d") % val, val
997 return _("exited with status %d") % val, val
1012 elif os.WIFSIGNALED(code):
998 elif os.WIFSIGNALED(code):
1013 val = os.WTERMSIG(code)
999 val = os.WTERMSIG(code)
1014 return _("killed by signal %d") % val, val
1000 return _("killed by signal %d") % val, val
1015 elif os.WIFSTOPPED(code):
1001 elif os.WIFSTOPPED(code):
1016 val = os.WSTOPSIG(code)
1002 val = os.WSTOPSIG(code)
1017 return _("stopped by signal %d") % val, val
1003 return _("stopped by signal %d") % val, val
1018 raise ValueError(_("invalid exit code"))
1004 raise ValueError(_("invalid exit code"))
1019
1005
1020 def isowner(fp, st=None):
1006 def isowner(fp, st=None):
1021 """Return True if the file object f belongs to the current user.
1007 """Return True if the file object f belongs to the current user.
1022
1008
1023 The return value of a util.fstat(f) may be passed as the st argument.
1009 The return value of a util.fstat(f) may be passed as the st argument.
1024 """
1010 """
1025 if st is None:
1011 if st is None:
1026 st = fstat(fp)
1012 st = fstat(fp)
1027 return st.st_uid == os.getuid()
1013 return st.st_uid == os.getuid()
1028
1014
1029 def _buildencodefun():
1015 def _buildencodefun():
1030 e = '_'
1016 e = '_'
1031 win_reserved = [ord(x) for x in '\\:*?"<>|']
1017 win_reserved = [ord(x) for x in '\\:*?"<>|']
1032 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1018 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1033 for x in (range(32) + range(126, 256) + win_reserved):
1019 for x in (range(32) + range(126, 256) + win_reserved):
1034 cmap[chr(x)] = "~%02x" % x
1020 cmap[chr(x)] = "~%02x" % x
1035 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1021 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1036 cmap[chr(x)] = e + chr(x).lower()
1022 cmap[chr(x)] = e + chr(x).lower()
1037 dmap = {}
1023 dmap = {}
1038 for k, v in cmap.iteritems():
1024 for k, v in cmap.iteritems():
1039 dmap[v] = k
1025 dmap[v] = k
1040 def decode(s):
1026 def decode(s):
1041 i = 0
1027 i = 0
1042 while i < len(s):
1028 while i < len(s):
1043 for l in xrange(1, 4):
1029 for l in xrange(1, 4):
1044 try:
1030 try:
1045 yield dmap[s[i:i+l]]
1031 yield dmap[s[i:i+l]]
1046 i += l
1032 i += l
1047 break
1033 break
1048 except KeyError:
1034 except KeyError:
1049 pass
1035 pass
1050 else:
1036 else:
1051 raise KeyError
1037 raise KeyError
1052 return (lambda s: "".join([cmap[c] for c in s]),
1038 return (lambda s: "".join([cmap[c] for c in s]),
1053 lambda s: "".join(list(decode(s))))
1039 lambda s: "".join(list(decode(s))))
1054
1040
1055 encodefilename, decodefilename = _buildencodefun()
1041 encodefilename, decodefilename = _buildencodefun()
1056
1042
1057 def encodedopener(openerfn, fn):
1043 def encodedopener(openerfn, fn):
1058 def o(path, *args, **kw):
1044 def o(path, *args, **kw):
1059 return openerfn(fn(path), *args, **kw)
1045 return openerfn(fn(path), *args, **kw)
1060 return o
1046 return o
1061
1047
1062 def opener(base, audit=True):
1048 def opener(base, audit=True):
1063 """
1049 """
1064 return a function that opens files relative to base
1050 return a function that opens files relative to base
1065
1051
1066 this function is used to hide the details of COW semantics and
1052 this function is used to hide the details of COW semantics and
1067 remote file access from higher level code.
1053 remote file access from higher level code.
1068 """
1054 """
1069 p = base
1055 p = base
1070 audit_p = audit
1056 audit_p = audit
1071
1057
1072 def mktempcopy(name):
1058 def mktempcopy(name):
1073 d, fn = os.path.split(name)
1059 d, fn = os.path.split(name)
1074 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1060 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1075 os.close(fd)
1061 os.close(fd)
1076 ofp = posixfile(temp, "wb")
1062 ofp = posixfile(temp, "wb")
1077 try:
1063 try:
1078 try:
1064 try:
1079 ifp = posixfile(name, "rb")
1065 ifp = posixfile(name, "rb")
1080 except IOError, inst:
1066 except IOError, inst:
1081 if not getattr(inst, 'filename', None):
1067 if not getattr(inst, 'filename', None):
1082 inst.filename = name
1068 inst.filename = name
1083 raise
1069 raise
1084 for chunk in filechunkiter(ifp):
1070 for chunk in filechunkiter(ifp):
1085 ofp.write(chunk)
1071 ofp.write(chunk)
1086 ifp.close()
1072 ifp.close()
1087 ofp.close()
1073 ofp.close()
1088 except:
1074 except:
1089 try: os.unlink(temp)
1075 try: os.unlink(temp)
1090 except: pass
1076 except: pass
1091 raise
1077 raise
1092 st = os.lstat(name)
1078 st = os.lstat(name)
1093 os.chmod(temp, st.st_mode)
1079 os.chmod(temp, st.st_mode)
1094 return temp
1080 return temp
1095
1081
1096 class atomictempfile(posixfile):
1082 class atomictempfile(posixfile):
1097 """the file will only be copied when rename is called"""
1083 """the file will only be copied when rename is called"""
1098 def __init__(self, name, mode):
1084 def __init__(self, name, mode):
1099 self.__name = name
1085 self.__name = name
1100 self.temp = mktempcopy(name)
1086 self.temp = mktempcopy(name)
1101 posixfile.__init__(self, self.temp, mode)
1087 posixfile.__init__(self, self.temp, mode)
1102 def rename(self):
1088 def rename(self):
1103 if not self.closed:
1089 if not self.closed:
1104 posixfile.close(self)
1090 posixfile.close(self)
1105 rename(self.temp, localpath(self.__name))
1091 rename(self.temp, localpath(self.__name))
1106 def __del__(self):
1092 def __del__(self):
1107 if not self.closed:
1093 if not self.closed:
1108 try:
1094 try:
1109 os.unlink(self.temp)
1095 os.unlink(self.temp)
1110 except: pass
1096 except: pass
1111 posixfile.close(self)
1097 posixfile.close(self)
1112
1098
1113 class atomicfile(atomictempfile):
1099 class atomicfile(atomictempfile):
1114 """the file will only be copied on close"""
1100 """the file will only be copied on close"""
1115 def __init__(self, name, mode):
1101 def __init__(self, name, mode):
1116 atomictempfile.__init__(self, name, mode)
1102 atomictempfile.__init__(self, name, mode)
1117 def close(self):
1103 def close(self):
1118 self.rename()
1104 self.rename()
1119 def __del__(self):
1105 def __del__(self):
1120 self.rename()
1106 self.rename()
1121
1107
1122 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1108 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1123 if audit_p:
1109 if audit_p:
1124 audit_path(path)
1110 audit_path(path)
1125 f = os.path.join(p, path)
1111 f = os.path.join(p, path)
1126
1112
1127 if not text:
1113 if not text:
1128 mode += "b" # for that other OS
1114 mode += "b" # for that other OS
1129
1115
1130 if mode[0] != "r":
1116 if mode[0] != "r":
1131 try:
1117 try:
1132 nlink = nlinks(f)
1118 nlink = nlinks(f)
1133 except OSError:
1119 except OSError:
1134 d = os.path.dirname(f)
1120 d = os.path.dirname(f)
1135 if not os.path.isdir(d):
1121 if not os.path.isdir(d):
1136 os.makedirs(d)
1122 os.makedirs(d)
1137 else:
1123 else:
1138 if atomic:
1124 if atomic:
1139 return atomicfile(f, mode)
1125 return atomicfile(f, mode)
1140 elif atomictemp:
1126 elif atomictemp:
1141 return atomictempfile(f, mode)
1127 return atomictempfile(f, mode)
1142 if nlink > 1:
1128 if nlink > 1:
1143 rename(mktempcopy(f), f)
1129 rename(mktempcopy(f), f)
1144 return posixfile(f, mode)
1130 return posixfile(f, mode)
1145
1131
1146 return o
1132 return o
1147
1133
1148 class chunkbuffer(object):
1134 class chunkbuffer(object):
1149 """Allow arbitrary sized chunks of data to be efficiently read from an
1135 """Allow arbitrary sized chunks of data to be efficiently read from an
1150 iterator over chunks of arbitrary size."""
1136 iterator over chunks of arbitrary size."""
1151
1137
1152 def __init__(self, in_iter, targetsize = 2**16):
1138 def __init__(self, in_iter, targetsize = 2**16):
1153 """in_iter is the iterator that's iterating over the input chunks.
1139 """in_iter is the iterator that's iterating over the input chunks.
1154 targetsize is how big a buffer to try to maintain."""
1140 targetsize is how big a buffer to try to maintain."""
1155 self.in_iter = iter(in_iter)
1141 self.in_iter = iter(in_iter)
1156 self.buf = ''
1142 self.buf = ''
1157 self.targetsize = int(targetsize)
1143 self.targetsize = int(targetsize)
1158 if self.targetsize <= 0:
1144 if self.targetsize <= 0:
1159 raise ValueError(_("targetsize must be greater than 0, was %d") %
1145 raise ValueError(_("targetsize must be greater than 0, was %d") %
1160 targetsize)
1146 targetsize)
1161 self.iterempty = False
1147 self.iterempty = False
1162
1148
1163 def fillbuf(self):
1149 def fillbuf(self):
1164 """Ignore target size; read every chunk from iterator until empty."""
1150 """Ignore target size; read every chunk from iterator until empty."""
1165 if not self.iterempty:
1151 if not self.iterempty:
1166 collector = cStringIO.StringIO()
1152 collector = cStringIO.StringIO()
1167 collector.write(self.buf)
1153 collector.write(self.buf)
1168 for ch in self.in_iter:
1154 for ch in self.in_iter:
1169 collector.write(ch)
1155 collector.write(ch)
1170 self.buf = collector.getvalue()
1156 self.buf = collector.getvalue()
1171 self.iterempty = True
1157 self.iterempty = True
1172
1158
1173 def read(self, l):
1159 def read(self, l):
1174 """Read L bytes of data from the iterator of chunks of data.
1160 """Read L bytes of data from the iterator of chunks of data.
1175 Returns less than L bytes if the iterator runs dry."""
1161 Returns less than L bytes if the iterator runs dry."""
1176 if l > len(self.buf) and not self.iterempty:
1162 if l > len(self.buf) and not self.iterempty:
1177 # Clamp to a multiple of self.targetsize
1163 # Clamp to a multiple of self.targetsize
1178 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1164 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1179 collector = cStringIO.StringIO()
1165 collector = cStringIO.StringIO()
1180 collector.write(self.buf)
1166 collector.write(self.buf)
1181 collected = len(self.buf)
1167 collected = len(self.buf)
1182 for chunk in self.in_iter:
1168 for chunk in self.in_iter:
1183 collector.write(chunk)
1169 collector.write(chunk)
1184 collected += len(chunk)
1170 collected += len(chunk)
1185 if collected >= targetsize:
1171 if collected >= targetsize:
1186 break
1172 break
1187 if collected < targetsize:
1173 if collected < targetsize:
1188 self.iterempty = True
1174 self.iterempty = True
1189 self.buf = collector.getvalue()
1175 self.buf = collector.getvalue()
1190 s, self.buf = self.buf[:l], buffer(self.buf, l)
1176 s, self.buf = self.buf[:l], buffer(self.buf, l)
1191 return s
1177 return s
1192
1178
1193 def filechunkiter(f, size=65536, limit=None):
1179 def filechunkiter(f, size=65536, limit=None):
1194 """Create a generator that produces the data in the file size
1180 """Create a generator that produces the data in the file size
1195 (default 65536) bytes at a time, up to optional limit (default is
1181 (default 65536) bytes at a time, up to optional limit (default is
1196 to read all data). Chunks may be less than size bytes if the
1182 to read all data). Chunks may be less than size bytes if the
1197 chunk is the last chunk in the file, or the file is a socket or
1183 chunk is the last chunk in the file, or the file is a socket or
1198 some other type of file that sometimes reads less data than is
1184 some other type of file that sometimes reads less data than is
1199 requested."""
1185 requested."""
1200 assert size >= 0
1186 assert size >= 0
1201 assert limit is None or limit >= 0
1187 assert limit is None or limit >= 0
1202 while True:
1188 while True:
1203 if limit is None: nbytes = size
1189 if limit is None: nbytes = size
1204 else: nbytes = min(limit, size)
1190 else: nbytes = min(limit, size)
1205 s = nbytes and f.read(nbytes)
1191 s = nbytes and f.read(nbytes)
1206 if not s: break
1192 if not s: break
1207 if limit: limit -= len(s)
1193 if limit: limit -= len(s)
1208 yield s
1194 yield s
1209
1195
1210 def makedate():
1196 def makedate():
1211 lt = time.localtime()
1197 lt = time.localtime()
1212 if lt[8] == 1 and time.daylight:
1198 if lt[8] == 1 and time.daylight:
1213 tz = time.altzone
1199 tz = time.altzone
1214 else:
1200 else:
1215 tz = time.timezone
1201 tz = time.timezone
1216 return time.mktime(lt), tz
1202 return time.mktime(lt), tz
1217
1203
1218 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1204 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1219 """represent a (unixtime, offset) tuple as a localized time.
1205 """represent a (unixtime, offset) tuple as a localized time.
1220 unixtime is seconds since the epoch, and offset is the time zone's
1206 unixtime is seconds since the epoch, and offset is the time zone's
1221 number of seconds away from UTC. if timezone is false, do not
1207 number of seconds away from UTC. if timezone is false, do not
1222 append time zone to string."""
1208 append time zone to string."""
1223 t, tz = date or makedate()
1209 t, tz = date or makedate()
1224 s = time.strftime(format, time.gmtime(float(t) - tz))
1210 s = time.strftime(format, time.gmtime(float(t) - tz))
1225 if timezone:
1211 if timezone:
1226 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1212 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1227 return s
1213 return s
1228
1214
1229 def strdate(string, format, defaults):
1215 def strdate(string, format, defaults):
1230 """parse a localized time string and return a (unixtime, offset) tuple.
1216 """parse a localized time string and return a (unixtime, offset) tuple.
1231 if the string cannot be parsed, ValueError is raised."""
1217 if the string cannot be parsed, ValueError is raised."""
1232 def timezone(string):
1218 def timezone(string):
1233 tz = string.split()[-1]
1219 tz = string.split()[-1]
1234 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1220 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1235 tz = int(tz)
1221 tz = int(tz)
1236 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1222 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1237 return offset
1223 return offset
1238 if tz == "GMT" or tz == "UTC":
1224 if tz == "GMT" or tz == "UTC":
1239 return 0
1225 return 0
1240 return None
1226 return None
1241
1227
1242 # NOTE: unixtime = localunixtime + offset
1228 # NOTE: unixtime = localunixtime + offset
1243 offset, date = timezone(string), string
1229 offset, date = timezone(string), string
1244 if offset != None:
1230 if offset != None:
1245 date = " ".join(string.split()[:-1])
1231 date = " ".join(string.split()[:-1])
1246
1232
1247 # add missing elements from defaults
1233 # add missing elements from defaults
1248 for part in defaults:
1234 for part in defaults:
1249 found = [True for p in part if ("%"+p) in format]
1235 found = [True for p in part if ("%"+p) in format]
1250 if not found:
1236 if not found:
1251 date += "@" + defaults[part]
1237 date += "@" + defaults[part]
1252 format += "@%" + part[0]
1238 format += "@%" + part[0]
1253
1239
1254 timetuple = time.strptime(date, format)
1240 timetuple = time.strptime(date, format)
1255 localunixtime = int(calendar.timegm(timetuple))
1241 localunixtime = int(calendar.timegm(timetuple))
1256 if offset is None:
1242 if offset is None:
1257 # local timezone
1243 # local timezone
1258 unixtime = int(time.mktime(timetuple))
1244 unixtime = int(time.mktime(timetuple))
1259 offset = unixtime - localunixtime
1245 offset = unixtime - localunixtime
1260 else:
1246 else:
1261 unixtime = localunixtime + offset
1247 unixtime = localunixtime + offset
1262 return unixtime, offset
1248 return unixtime, offset
1263
1249
1264 def parsedate(string, formats=None, defaults=None):
1250 def parsedate(string, formats=None, defaults=None):
1265 """parse a localized time string and return a (unixtime, offset) tuple.
1251 """parse a localized time string and return a (unixtime, offset) tuple.
1266 The date may be a "unixtime offset" string or in one of the specified
1252 The date may be a "unixtime offset" string or in one of the specified
1267 formats."""
1253 formats."""
1268 if not string:
1254 if not string:
1269 return 0, 0
1255 return 0, 0
1270 if not formats:
1256 if not formats:
1271 formats = defaultdateformats
1257 formats = defaultdateformats
1272 string = string.strip()
1258 string = string.strip()
1273 try:
1259 try:
1274 when, offset = map(int, string.split(' '))
1260 when, offset = map(int, string.split(' '))
1275 except ValueError:
1261 except ValueError:
1276 # fill out defaults
1262 # fill out defaults
1277 if not defaults:
1263 if not defaults:
1278 defaults = {}
1264 defaults = {}
1279 now = makedate()
1265 now = makedate()
1280 for part in "d mb yY HI M S".split():
1266 for part in "d mb yY HI M S".split():
1281 if part not in defaults:
1267 if part not in defaults:
1282 if part[0] in "HMS":
1268 if part[0] in "HMS":
1283 defaults[part] = "00"
1269 defaults[part] = "00"
1284 elif part[0] in "dm":
1270 elif part[0] in "dm":
1285 defaults[part] = "1"
1271 defaults[part] = "1"
1286 else:
1272 else:
1287 defaults[part] = datestr(now, "%" + part[0], False)
1273 defaults[part] = datestr(now, "%" + part[0], False)
1288
1274
1289 for format in formats:
1275 for format in formats:
1290 try:
1276 try:
1291 when, offset = strdate(string, format, defaults)
1277 when, offset = strdate(string, format, defaults)
1292 except ValueError:
1278 except ValueError:
1293 pass
1279 pass
1294 else:
1280 else:
1295 break
1281 break
1296 else:
1282 else:
1297 raise Abort(_('invalid date: %r ') % string)
1283 raise Abort(_('invalid date: %r ') % string)
1298 # validate explicit (probably user-specified) date and
1284 # validate explicit (probably user-specified) date and
1299 # time zone offset. values must fit in signed 32 bits for
1285 # time zone offset. values must fit in signed 32 bits for
1300 # current 32-bit linux runtimes. timezones go from UTC-12
1286 # current 32-bit linux runtimes. timezones go from UTC-12
1301 # to UTC+14
1287 # to UTC+14
1302 if abs(when) > 0x7fffffff:
1288 if abs(when) > 0x7fffffff:
1303 raise Abort(_('date exceeds 32 bits: %d') % when)
1289 raise Abort(_('date exceeds 32 bits: %d') % when)
1304 if offset < -50400 or offset > 43200:
1290 if offset < -50400 or offset > 43200:
1305 raise Abort(_('impossible time zone offset: %d') % offset)
1291 raise Abort(_('impossible time zone offset: %d') % offset)
1306 return when, offset
1292 return when, offset
1307
1293
1308 def matchdate(date):
1294 def matchdate(date):
1309 """Return a function that matches a given date match specifier
1295 """Return a function that matches a given date match specifier
1310
1296
1311 Formats include:
1297 Formats include:
1312
1298
1313 '{date}' match a given date to the accuracy provided
1299 '{date}' match a given date to the accuracy provided
1314
1300
1315 '<{date}' on or before a given date
1301 '<{date}' on or before a given date
1316
1302
1317 '>{date}' on or after a given date
1303 '>{date}' on or after a given date
1318
1304
1319 """
1305 """
1320
1306
1321 def lower(date):
1307 def lower(date):
1322 return parsedate(date, extendeddateformats)[0]
1308 return parsedate(date, extendeddateformats)[0]
1323
1309
1324 def upper(date):
1310 def upper(date):
1325 d = dict(mb="12", HI="23", M="59", S="59")
1311 d = dict(mb="12", HI="23", M="59", S="59")
1326 for days in "31 30 29".split():
1312 for days in "31 30 29".split():
1327 try:
1313 try:
1328 d["d"] = days
1314 d["d"] = days
1329 return parsedate(date, extendeddateformats, d)[0]
1315 return parsedate(date, extendeddateformats, d)[0]
1330 except:
1316 except:
1331 pass
1317 pass
1332 d["d"] = "28"
1318 d["d"] = "28"
1333 return parsedate(date, extendeddateformats, d)[0]
1319 return parsedate(date, extendeddateformats, d)[0]
1334
1320
1335 if date[0] == "<":
1321 if date[0] == "<":
1336 when = upper(date[1:])
1322 when = upper(date[1:])
1337 return lambda x: x <= when
1323 return lambda x: x <= when
1338 elif date[0] == ">":
1324 elif date[0] == ">":
1339 when = lower(date[1:])
1325 when = lower(date[1:])
1340 return lambda x: x >= when
1326 return lambda x: x >= when
1341 elif date[0] == "-":
1327 elif date[0] == "-":
1342 try:
1328 try:
1343 days = int(date[1:])
1329 days = int(date[1:])
1344 except ValueError:
1330 except ValueError:
1345 raise Abort(_("invalid day spec: %s") % date[1:])
1331 raise Abort(_("invalid day spec: %s") % date[1:])
1346 when = makedate()[0] - days * 3600 * 24
1332 when = makedate()[0] - days * 3600 * 24
1347 return lambda x: x >= when
1333 return lambda x: x >= when
1348 elif " to " in date:
1334 elif " to " in date:
1349 a, b = date.split(" to ")
1335 a, b = date.split(" to ")
1350 start, stop = lower(a), upper(b)
1336 start, stop = lower(a), upper(b)
1351 return lambda x: x >= start and x <= stop
1337 return lambda x: x >= start and x <= stop
1352 else:
1338 else:
1353 start, stop = lower(date), upper(date)
1339 start, stop = lower(date), upper(date)
1354 return lambda x: x >= start and x <= stop
1340 return lambda x: x >= start and x <= stop
1355
1341
1356 def shortuser(user):
1342 def shortuser(user):
1357 """Return a short representation of a user name or email address."""
1343 """Return a short representation of a user name or email address."""
1358 f = user.find('@')
1344 f = user.find('@')
1359 if f >= 0:
1345 if f >= 0:
1360 user = user[:f]
1346 user = user[:f]
1361 f = user.find('<')
1347 f = user.find('<')
1362 if f >= 0:
1348 if f >= 0:
1363 user = user[f+1:]
1349 user = user[f+1:]
1364 f = user.find(' ')
1350 f = user.find(' ')
1365 if f >= 0:
1351 if f >= 0:
1366 user = user[:f]
1352 user = user[:f]
1367 f = user.find('.')
1353 f = user.find('.')
1368 if f >= 0:
1354 if f >= 0:
1369 user = user[:f]
1355 user = user[:f]
1370 return user
1356 return user
1371
1357
1372 def ellipsis(text, maxlength=400):
1358 def ellipsis(text, maxlength=400):
1373 """Trim string to at most maxlength (default: 400) characters."""
1359 """Trim string to at most maxlength (default: 400) characters."""
1374 if len(text) <= maxlength:
1360 if len(text) <= maxlength:
1375 return text
1361 return text
1376 else:
1362 else:
1377 return "%s..." % (text[:maxlength-3])
1363 return "%s..." % (text[:maxlength-3])
1378
1364
1379 def walkrepos(path):
1365 def walkrepos(path):
1380 '''yield every hg repository under path, recursively.'''
1366 '''yield every hg repository under path, recursively.'''
1381 def errhandler(err):
1367 def errhandler(err):
1382 if err.filename == path:
1368 if err.filename == path:
1383 raise err
1369 raise err
1384
1370
1385 for root, dirs, files in os.walk(path, onerror=errhandler):
1371 for root, dirs, files in os.walk(path, onerror=errhandler):
1386 for d in dirs:
1372 for d in dirs:
1387 if d == '.hg':
1373 if d == '.hg':
1388 yield root
1374 yield root
1389 dirs[:] = []
1375 dirs[:] = []
1390 break
1376 break
1391
1377
1392 _rcpath = None
1378 _rcpath = None
1393
1379
1380 def os_rcpath():
1381 '''return default os-specific hgrc search path'''
1382 path = system_rcpath()
1383 path.extend(user_rcpath())
1384 path = [os.path.normpath(f) for f in path]
1385 return path
1386
1394 def rcpath():
1387 def rcpath():
1395 '''return hgrc search path. if env var HGRCPATH is set, use it.
1388 '''return hgrc search path. if env var HGRCPATH is set, use it.
1396 for each item in path, if directory, use files ending in .rc,
1389 for each item in path, if directory, use files ending in .rc,
1397 else use item.
1390 else use item.
1398 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1391 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1399 if no HGRCPATH, use default os-specific path.'''
1392 if no HGRCPATH, use default os-specific path.'''
1400 global _rcpath
1393 global _rcpath
1401 if _rcpath is None:
1394 if _rcpath is None:
1402 if 'HGRCPATH' in os.environ:
1395 if 'HGRCPATH' in os.environ:
1403 _rcpath = []
1396 _rcpath = []
1404 for p in os.environ['HGRCPATH'].split(os.pathsep):
1397 for p in os.environ['HGRCPATH'].split(os.pathsep):
1405 if not p: continue
1398 if not p: continue
1406 if os.path.isdir(p):
1399 if os.path.isdir(p):
1407 for f in os.listdir(p):
1400 for f in os.listdir(p):
1408 if f.endswith('.rc'):
1401 if f.endswith('.rc'):
1409 _rcpath.append(os.path.join(p, f))
1402 _rcpath.append(os.path.join(p, f))
1410 else:
1403 else:
1411 _rcpath.append(p)
1404 _rcpath.append(p)
1412 else:
1405 else:
1413 _rcpath = os_rcpath()
1406 _rcpath = os_rcpath()
1414 return _rcpath
1407 return _rcpath
1415
1408
1416 def bytecount(nbytes):
1409 def bytecount(nbytes):
1417 '''return byte count formatted as readable string, with units'''
1410 '''return byte count formatted as readable string, with units'''
1418
1411
1419 units = (
1412 units = (
1420 (100, 1<<30, _('%.0f GB')),
1413 (100, 1<<30, _('%.0f GB')),
1421 (10, 1<<30, _('%.1f GB')),
1414 (10, 1<<30, _('%.1f GB')),
1422 (1, 1<<30, _('%.2f GB')),
1415 (1, 1<<30, _('%.2f GB')),
1423 (100, 1<<20, _('%.0f MB')),
1416 (100, 1<<20, _('%.0f MB')),
1424 (10, 1<<20, _('%.1f MB')),
1417 (10, 1<<20, _('%.1f MB')),
1425 (1, 1<<20, _('%.2f MB')),
1418 (1, 1<<20, _('%.2f MB')),
1426 (100, 1<<10, _('%.0f KB')),
1419 (100, 1<<10, _('%.0f KB')),
1427 (10, 1<<10, _('%.1f KB')),
1420 (10, 1<<10, _('%.1f KB')),
1428 (1, 1<<10, _('%.2f KB')),
1421 (1, 1<<10, _('%.2f KB')),
1429 (1, 1, _('%.0f bytes')),
1422 (1, 1, _('%.0f bytes')),
1430 )
1423 )
1431
1424
1432 for multiplier, divisor, format in units:
1425 for multiplier, divisor, format in units:
1433 if nbytes >= divisor * multiplier:
1426 if nbytes >= divisor * multiplier:
1434 return format % (nbytes / float(divisor))
1427 return format % (nbytes / float(divisor))
1435 return units[-1][2] % nbytes
1428 return units[-1][2] % nbytes
1436
1429
1437 def drop_scheme(scheme, path):
1430 def drop_scheme(scheme, path):
1438 sc = scheme + ':'
1431 sc = scheme + ':'
1439 if path.startswith(sc):
1432 if path.startswith(sc):
1440 path = path[len(sc):]
1433 path = path[len(sc):]
1441 if path.startswith('//'):
1434 if path.startswith('//'):
1442 path = path[2:]
1435 path = path[2:]
1443 return path
1436 return path
General Comments 0
You need to be logged in to leave comments. Login now