##// END OF EJS Templates
improve date parsing for numerous new date formats...
Matt Mackall -
r3808:d6529582 default
parent child Browse files
Show More
@@ -1,1182 +1,1216
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 gettext as _
15 from i18n import gettext as _
16 from demandload import *
16 from demandload import *
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 demandload(globals(), "os threading time calendar ConfigParser locale")
18 demandload(globals(), "os threading time calendar ConfigParser locale")
19
19
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding()
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding()
21 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
21 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
22
22
23 def tolocal(s):
23 def tolocal(s):
24 """
24 """
25 Convert a string from internal UTF-8 to local encoding
25 Convert a string from internal UTF-8 to local encoding
26
26
27 All internal strings should be UTF-8 but some repos before the
27 All internal strings should be UTF-8 but some repos before the
28 implementation of locale support may contain latin1 or possibly
28 implementation of locale support may contain latin1 or possibly
29 other character sets. We attempt to decode everything strictly
29 other character sets. We attempt to decode everything strictly
30 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
30 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
31 replace unknown characters.
31 replace unknown characters.
32 """
32 """
33 for e in "utf-8 latin1".split():
33 for e in "utf-8 latin1".split():
34 try:
34 try:
35 u = s.decode(e) # attempt strict decoding
35 u = s.decode(e) # attempt strict decoding
36 return u.encode(_encoding, "replace")
36 return u.encode(_encoding, "replace")
37 except UnicodeDecodeError:
37 except UnicodeDecodeError:
38 pass
38 pass
39 u = s.decode("utf-8", "replace") # last ditch
39 u = s.decode("utf-8", "replace") # last ditch
40 return u.encode(_encoding, "replace")
40 return u.encode(_encoding, "replace")
41
41
42 def fromlocal(s):
42 def fromlocal(s):
43 """
43 """
44 Convert a string from the local character encoding to UTF-8
44 Convert a string from the local character encoding to UTF-8
45
45
46 We attempt to decode strings using the encoding mode set by
46 We attempt to decode strings using the encoding mode set by
47 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
47 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
48 characters will cause an error message. Other modes include
48 characters will cause an error message. Other modes include
49 'replace', which replaces unknown characters with a special
49 'replace', which replaces unknown characters with a special
50 Unicode character, and 'ignore', which drops the character.
50 Unicode character, and 'ignore', which drops the character.
51 """
51 """
52 try:
52 try:
53 return s.decode(_encoding, _encodingmode).encode("utf-8")
53 return s.decode(_encoding, _encodingmode).encode("utf-8")
54 except UnicodeDecodeError, inst:
54 except UnicodeDecodeError, inst:
55 sub = s[max(0, inst.start-10):inst.start+10]
55 sub = s[max(0, inst.start-10):inst.start+10]
56 raise Abort("decoding near '%s': %s!\n" % (sub, inst))
56 raise Abort("decoding near '%s': %s!\n" % (sub, inst))
57
57
58 def locallen(s):
58 def locallen(s):
59 """Find the length in characters of a local string"""
59 """Find the length in characters of a local string"""
60 return len(s.decode(_encoding, "replace"))
60 return len(s.decode(_encoding, "replace"))
61
61
62 def localsub(s, a, b=None):
62 def localsub(s, a, b=None):
63 try:
63 try:
64 u = s.decode(_encoding, _encodingmode)
64 u = s.decode(_encoding, _encodingmode)
65 if b is not None:
65 if b is not None:
66 u = u[a:b]
66 u = u[a:b]
67 else:
67 else:
68 u = u[:a]
68 u = u[:a]
69 return u.encode(_encoding, _encodingmode)
69 return u.encode(_encoding, _encodingmode)
70 except UnicodeDecodeError, inst:
70 except UnicodeDecodeError, inst:
71 sub = s[max(0, inst.start-10), inst.start+10]
71 sub = s[max(0, inst.start-10), inst.start+10]
72 raise Abort("decoding near '%s': %s!\n" % (sub, inst))
72 raise Abort("decoding near '%s': %s!\n" % (sub, inst))
73
73
74 # used by parsedate
74 # used by parsedate
75 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
75 defaultdateformats = (
76 '%a %b %d %H:%M:%S %Y')
76 '%Y-%m-%d %H:%M:%S',
77 '%Y-%m-%d %I:%M:%S%p',
78 '%Y-%m-%d %H:%M',
79 '%Y-%m-%d %I:%M%p',
80 '%Y-%m-%d',
81 '%m-%d',
82 '%m/%d',
83 '%m/%d/%y',
84 '%m/%d/%Y',
85 '%a %b %d %H:%M:%S %Y',
86 '%a %b %d %I:%M:%S%p %Y',
87 '%b %d %H:%M:%S %Y',
88 '%b %d %I:%M:%S%p',
89 '%b %d %H:%M',
90 '%b %d %I:%M%p',
91 '%b %d %Y',
92 '%b %d',
93 '%H:%M:%S',
94 '%I:%M:%SP',
95 '%H:%M',
96 '%I:%M%p',
97 )
77
98
78 class SignalInterrupt(Exception):
99 class SignalInterrupt(Exception):
79 """Exception raised on SIGTERM and SIGHUP."""
100 """Exception raised on SIGTERM and SIGHUP."""
80
101
81 # like SafeConfigParser but with case-sensitive keys
102 # like SafeConfigParser but with case-sensitive keys
82 class configparser(ConfigParser.SafeConfigParser):
103 class configparser(ConfigParser.SafeConfigParser):
83 def optionxform(self, optionstr):
104 def optionxform(self, optionstr):
84 return optionstr
105 return optionstr
85
106
86 def cachefunc(func):
107 def cachefunc(func):
87 '''cache the result of function calls'''
108 '''cache the result of function calls'''
88 # XXX doesn't handle keywords args
109 # XXX doesn't handle keywords args
89 cache = {}
110 cache = {}
90 if func.func_code.co_argcount == 1:
111 if func.func_code.co_argcount == 1:
91 # we gain a small amount of time because
112 # we gain a small amount of time because
92 # we don't need to pack/unpack the list
113 # we don't need to pack/unpack the list
93 def f(arg):
114 def f(arg):
94 if arg not in cache:
115 if arg not in cache:
95 cache[arg] = func(arg)
116 cache[arg] = func(arg)
96 return cache[arg]
117 return cache[arg]
97 else:
118 else:
98 def f(*args):
119 def f(*args):
99 if args not in cache:
120 if args not in cache:
100 cache[args] = func(*args)
121 cache[args] = func(*args)
101 return cache[args]
122 return cache[args]
102
123
103 return f
124 return f
104
125
105 def pipefilter(s, cmd):
126 def pipefilter(s, cmd):
106 '''filter string S through command CMD, returning its output'''
127 '''filter string S through command CMD, returning its output'''
107 (pout, pin) = popen2.popen2(cmd, -1, 'b')
128 (pout, pin) = popen2.popen2(cmd, -1, 'b')
108 def writer():
129 def writer():
109 try:
130 try:
110 pin.write(s)
131 pin.write(s)
111 pin.close()
132 pin.close()
112 except IOError, inst:
133 except IOError, inst:
113 if inst.errno != errno.EPIPE:
134 if inst.errno != errno.EPIPE:
114 raise
135 raise
115
136
116 # we should use select instead on UNIX, but this will work on most
137 # we should use select instead on UNIX, but this will work on most
117 # systems, including Windows
138 # systems, including Windows
118 w = threading.Thread(target=writer)
139 w = threading.Thread(target=writer)
119 w.start()
140 w.start()
120 f = pout.read()
141 f = pout.read()
121 pout.close()
142 pout.close()
122 w.join()
143 w.join()
123 return f
144 return f
124
145
125 def tempfilter(s, cmd):
146 def tempfilter(s, cmd):
126 '''filter string S through a pair of temporary files with CMD.
147 '''filter string S through a pair of temporary files with CMD.
127 CMD is used as a template to create the real command to be run,
148 CMD is used as a template to create the real command to be run,
128 with the strings INFILE and OUTFILE replaced by the real names of
149 with the strings INFILE and OUTFILE replaced by the real names of
129 the temporary files generated.'''
150 the temporary files generated.'''
130 inname, outname = None, None
151 inname, outname = None, None
131 try:
152 try:
132 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
153 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
133 fp = os.fdopen(infd, 'wb')
154 fp = os.fdopen(infd, 'wb')
134 fp.write(s)
155 fp.write(s)
135 fp.close()
156 fp.close()
136 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
157 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
137 os.close(outfd)
158 os.close(outfd)
138 cmd = cmd.replace('INFILE', inname)
159 cmd = cmd.replace('INFILE', inname)
139 cmd = cmd.replace('OUTFILE', outname)
160 cmd = cmd.replace('OUTFILE', outname)
140 code = os.system(cmd)
161 code = os.system(cmd)
141 if code: raise Abort(_("command '%s' failed: %s") %
162 if code: raise Abort(_("command '%s' failed: %s") %
142 (cmd, explain_exit(code)))
163 (cmd, explain_exit(code)))
143 return open(outname, 'rb').read()
164 return open(outname, 'rb').read()
144 finally:
165 finally:
145 try:
166 try:
146 if inname: os.unlink(inname)
167 if inname: os.unlink(inname)
147 except: pass
168 except: pass
148 try:
169 try:
149 if outname: os.unlink(outname)
170 if outname: os.unlink(outname)
150 except: pass
171 except: pass
151
172
152 filtertable = {
173 filtertable = {
153 'tempfile:': tempfilter,
174 'tempfile:': tempfilter,
154 'pipe:': pipefilter,
175 'pipe:': pipefilter,
155 }
176 }
156
177
157 def filter(s, cmd):
178 def filter(s, cmd):
158 "filter a string through a command that transforms its input to its output"
179 "filter a string through a command that transforms its input to its output"
159 for name, fn in filtertable.iteritems():
180 for name, fn in filtertable.iteritems():
160 if cmd.startswith(name):
181 if cmd.startswith(name):
161 return fn(s, cmd[len(name):].lstrip())
182 return fn(s, cmd[len(name):].lstrip())
162 return pipefilter(s, cmd)
183 return pipefilter(s, cmd)
163
184
164 def find_in_path(name, path, default=None):
185 def find_in_path(name, path, default=None):
165 '''find name in search path. path can be string (will be split
186 '''find name in search path. path can be string (will be split
166 with os.pathsep), or iterable thing that returns strings. if name
187 with os.pathsep), or iterable thing that returns strings. if name
167 found, return path to name. else return default.'''
188 found, return path to name. else return default.'''
168 if isinstance(path, str):
189 if isinstance(path, str):
169 path = path.split(os.pathsep)
190 path = path.split(os.pathsep)
170 for p in path:
191 for p in path:
171 p_name = os.path.join(p, name)
192 p_name = os.path.join(p, name)
172 if os.path.exists(p_name):
193 if os.path.exists(p_name):
173 return p_name
194 return p_name
174 return default
195 return default
175
196
176 def binary(s):
197 def binary(s):
177 """return true if a string is binary data using diff's heuristic"""
198 """return true if a string is binary data using diff's heuristic"""
178 if s and '\0' in s[:4096]:
199 if s and '\0' in s[:4096]:
179 return True
200 return True
180 return False
201 return False
181
202
182 def unique(g):
203 def unique(g):
183 """return the uniq elements of iterable g"""
204 """return the uniq elements of iterable g"""
184 seen = {}
205 seen = {}
185 l = []
206 l = []
186 for f in g:
207 for f in g:
187 if f not in seen:
208 if f not in seen:
188 seen[f] = 1
209 seen[f] = 1
189 l.append(f)
210 l.append(f)
190 return l
211 return l
191
212
192 class Abort(Exception):
213 class Abort(Exception):
193 """Raised if a command needs to print an error and exit."""
214 """Raised if a command needs to print an error and exit."""
194
215
195 class UnexpectedOutput(Abort):
216 class UnexpectedOutput(Abort):
196 """Raised to print an error with part of output and exit."""
217 """Raised to print an error with part of output and exit."""
197
218
198 def always(fn): return True
219 def always(fn): return True
199 def never(fn): return False
220 def never(fn): return False
200
221
201 def patkind(name, dflt_pat='glob'):
222 def patkind(name, dflt_pat='glob'):
202 """Split a string into an optional pattern kind prefix and the
223 """Split a string into an optional pattern kind prefix and the
203 actual pattern."""
224 actual pattern."""
204 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
225 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
205 if name.startswith(prefix + ':'): return name.split(':', 1)
226 if name.startswith(prefix + ':'): return name.split(':', 1)
206 return dflt_pat, name
227 return dflt_pat, name
207
228
208 def globre(pat, head='^', tail='$'):
229 def globre(pat, head='^', tail='$'):
209 "convert a glob pattern into a regexp"
230 "convert a glob pattern into a regexp"
210 i, n = 0, len(pat)
231 i, n = 0, len(pat)
211 res = ''
232 res = ''
212 group = False
233 group = False
213 def peek(): return i < n and pat[i]
234 def peek(): return i < n and pat[i]
214 while i < n:
235 while i < n:
215 c = pat[i]
236 c = pat[i]
216 i = i+1
237 i = i+1
217 if c == '*':
238 if c == '*':
218 if peek() == '*':
239 if peek() == '*':
219 i += 1
240 i += 1
220 res += '.*'
241 res += '.*'
221 else:
242 else:
222 res += '[^/]*'
243 res += '[^/]*'
223 elif c == '?':
244 elif c == '?':
224 res += '.'
245 res += '.'
225 elif c == '[':
246 elif c == '[':
226 j = i
247 j = i
227 if j < n and pat[j] in '!]':
248 if j < n and pat[j] in '!]':
228 j += 1
249 j += 1
229 while j < n and pat[j] != ']':
250 while j < n and pat[j] != ']':
230 j += 1
251 j += 1
231 if j >= n:
252 if j >= n:
232 res += '\\['
253 res += '\\['
233 else:
254 else:
234 stuff = pat[i:j].replace('\\','\\\\')
255 stuff = pat[i:j].replace('\\','\\\\')
235 i = j + 1
256 i = j + 1
236 if stuff[0] == '!':
257 if stuff[0] == '!':
237 stuff = '^' + stuff[1:]
258 stuff = '^' + stuff[1:]
238 elif stuff[0] == '^':
259 elif stuff[0] == '^':
239 stuff = '\\' + stuff
260 stuff = '\\' + stuff
240 res = '%s[%s]' % (res, stuff)
261 res = '%s[%s]' % (res, stuff)
241 elif c == '{':
262 elif c == '{':
242 group = True
263 group = True
243 res += '(?:'
264 res += '(?:'
244 elif c == '}' and group:
265 elif c == '}' and group:
245 res += ')'
266 res += ')'
246 group = False
267 group = False
247 elif c == ',' and group:
268 elif c == ',' and group:
248 res += '|'
269 res += '|'
249 elif c == '\\':
270 elif c == '\\':
250 p = peek()
271 p = peek()
251 if p:
272 if p:
252 i += 1
273 i += 1
253 res += re.escape(p)
274 res += re.escape(p)
254 else:
275 else:
255 res += re.escape(c)
276 res += re.escape(c)
256 else:
277 else:
257 res += re.escape(c)
278 res += re.escape(c)
258 return head + res + tail
279 return head + res + tail
259
280
260 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
281 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
261
282
262 def pathto(n1, n2):
283 def pathto(n1, n2):
263 '''return the relative path from one place to another.
284 '''return the relative path from one place to another.
264 n1 should use os.sep to separate directories
285 n1 should use os.sep to separate directories
265 n2 should use "/" to separate directories
286 n2 should use "/" to separate directories
266 returns an os.sep-separated path.
287 returns an os.sep-separated path.
267 '''
288 '''
268 if not n1: return localpath(n2)
289 if not n1: return localpath(n2)
269 a, b = n1.split(os.sep), n2.split('/')
290 a, b = n1.split(os.sep), n2.split('/')
270 a.reverse()
291 a.reverse()
271 b.reverse()
292 b.reverse()
272 while a and b and a[-1] == b[-1]:
293 while a and b and a[-1] == b[-1]:
273 a.pop()
294 a.pop()
274 b.pop()
295 b.pop()
275 b.reverse()
296 b.reverse()
276 return os.sep.join((['..'] * len(a)) + b)
297 return os.sep.join((['..'] * len(a)) + b)
277
298
278 def canonpath(root, cwd, myname):
299 def canonpath(root, cwd, myname):
279 """return the canonical path of myname, given cwd and root"""
300 """return the canonical path of myname, given cwd and root"""
280 if root == os.sep:
301 if root == os.sep:
281 rootsep = os.sep
302 rootsep = os.sep
282 elif root.endswith(os.sep):
303 elif root.endswith(os.sep):
283 rootsep = root
304 rootsep = root
284 else:
305 else:
285 rootsep = root + os.sep
306 rootsep = root + os.sep
286 name = myname
307 name = myname
287 if not os.path.isabs(name):
308 if not os.path.isabs(name):
288 name = os.path.join(root, cwd, name)
309 name = os.path.join(root, cwd, name)
289 name = os.path.normpath(name)
310 name = os.path.normpath(name)
290 if name != rootsep and name.startswith(rootsep):
311 if name != rootsep and name.startswith(rootsep):
291 name = name[len(rootsep):]
312 name = name[len(rootsep):]
292 audit_path(name)
313 audit_path(name)
293 return pconvert(name)
314 return pconvert(name)
294 elif name == root:
315 elif name == root:
295 return ''
316 return ''
296 else:
317 else:
297 # Determine whether `name' is in the hierarchy at or beneath `root',
318 # Determine whether `name' is in the hierarchy at or beneath `root',
298 # by iterating name=dirname(name) until that causes no change (can't
319 # by iterating name=dirname(name) until that causes no change (can't
299 # check name == '/', because that doesn't work on windows). For each
320 # check name == '/', because that doesn't work on windows). For each
300 # `name', compare dev/inode numbers. If they match, the list `rel'
321 # `name', compare dev/inode numbers. If they match, the list `rel'
301 # holds the reversed list of components making up the relative file
322 # holds the reversed list of components making up the relative file
302 # name we want.
323 # name we want.
303 root_st = os.stat(root)
324 root_st = os.stat(root)
304 rel = []
325 rel = []
305 while True:
326 while True:
306 try:
327 try:
307 name_st = os.stat(name)
328 name_st = os.stat(name)
308 except OSError:
329 except OSError:
309 break
330 break
310 if samestat(name_st, root_st):
331 if samestat(name_st, root_st):
311 rel.reverse()
332 rel.reverse()
312 name = os.path.join(*rel)
333 name = os.path.join(*rel)
313 audit_path(name)
334 audit_path(name)
314 return pconvert(name)
335 return pconvert(name)
315 dirname, basename = os.path.split(name)
336 dirname, basename = os.path.split(name)
316 rel.append(basename)
337 rel.append(basename)
317 if dirname == name:
338 if dirname == name:
318 break
339 break
319 name = dirname
340 name = dirname
320
341
321 raise Abort('%s not under root' % myname)
342 raise Abort('%s not under root' % myname)
322
343
323 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
344 def matcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
324 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
345 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
325
346
326 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
347 def cmdmatcher(canonroot, cwd='', names=['.'], inc=[], exc=[], head='', src=None):
327 if os.name == 'nt':
348 if os.name == 'nt':
328 dflt_pat = 'glob'
349 dflt_pat = 'glob'
329 else:
350 else:
330 dflt_pat = 'relpath'
351 dflt_pat = 'relpath'
331 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
352 return _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src)
332
353
333 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
354 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
334 """build a function to match a set of file patterns
355 """build a function to match a set of file patterns
335
356
336 arguments:
357 arguments:
337 canonroot - the canonical root of the tree you're matching against
358 canonroot - the canonical root of the tree you're matching against
338 cwd - the current working directory, if relevant
359 cwd - the current working directory, if relevant
339 names - patterns to find
360 names - patterns to find
340 inc - patterns to include
361 inc - patterns to include
341 exc - patterns to exclude
362 exc - patterns to exclude
342 head - a regex to prepend to patterns to control whether a match is rooted
363 head - a regex to prepend to patterns to control whether a match is rooted
343
364
344 a pattern is one of:
365 a pattern is one of:
345 'glob:<rooted glob>'
366 'glob:<rooted glob>'
346 're:<rooted regexp>'
367 're:<rooted regexp>'
347 'path:<rooted path>'
368 'path:<rooted path>'
348 'relglob:<relative glob>'
369 'relglob:<relative glob>'
349 'relpath:<relative path>'
370 'relpath:<relative path>'
350 'relre:<relative regexp>'
371 'relre:<relative regexp>'
351 '<rooted path or regexp>'
372 '<rooted path or regexp>'
352
373
353 returns:
374 returns:
354 a 3-tuple containing
375 a 3-tuple containing
355 - list of explicit non-pattern names passed in
376 - list of explicit non-pattern names passed in
356 - a bool match(filename) function
377 - a bool match(filename) function
357 - a bool indicating if any patterns were passed in
378 - a bool indicating if any patterns were passed in
358
379
359 todo:
380 todo:
360 make head regex a rooted bool
381 make head regex a rooted bool
361 """
382 """
362
383
363 def contains_glob(name):
384 def contains_glob(name):
364 for c in name:
385 for c in name:
365 if c in _globchars: return True
386 if c in _globchars: return True
366 return False
387 return False
367
388
368 def regex(kind, name, tail):
389 def regex(kind, name, tail):
369 '''convert a pattern into a regular expression'''
390 '''convert a pattern into a regular expression'''
370 if kind == 're':
391 if kind == 're':
371 return name
392 return name
372 elif kind == 'path':
393 elif kind == 'path':
373 return '^' + re.escape(name) + '(?:/|$)'
394 return '^' + re.escape(name) + '(?:/|$)'
374 elif kind == 'relglob':
395 elif kind == 'relglob':
375 return head + globre(name, '(?:|.*/)', tail)
396 return head + globre(name, '(?:|.*/)', tail)
376 elif kind == 'relpath':
397 elif kind == 'relpath':
377 return head + re.escape(name) + tail
398 return head + re.escape(name) + tail
378 elif kind == 'relre':
399 elif kind == 'relre':
379 if name.startswith('^'):
400 if name.startswith('^'):
380 return name
401 return name
381 return '.*' + name
402 return '.*' + name
382 return head + globre(name, '', tail)
403 return head + globre(name, '', tail)
383
404
384 def matchfn(pats, tail):
405 def matchfn(pats, tail):
385 """build a matching function from a set of patterns"""
406 """build a matching function from a set of patterns"""
386 if not pats:
407 if not pats:
387 return
408 return
388 matches = []
409 matches = []
389 for k, p in pats:
410 for k, p in pats:
390 try:
411 try:
391 pat = '(?:%s)' % regex(k, p, tail)
412 pat = '(?:%s)' % regex(k, p, tail)
392 matches.append(re.compile(pat).match)
413 matches.append(re.compile(pat).match)
393 except re.error:
414 except re.error:
394 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
415 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
395 else: raise Abort("invalid pattern (%s): %s" % (k, p))
416 else: raise Abort("invalid pattern (%s): %s" % (k, p))
396
417
397 def buildfn(text):
418 def buildfn(text):
398 for m in matches:
419 for m in matches:
399 r = m(text)
420 r = m(text)
400 if r:
421 if r:
401 return r
422 return r
402
423
403 return buildfn
424 return buildfn
404
425
405 def globprefix(pat):
426 def globprefix(pat):
406 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
427 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
407 root = []
428 root = []
408 for p in pat.split(os.sep):
429 for p in pat.split(os.sep):
409 if contains_glob(p): break
430 if contains_glob(p): break
410 root.append(p)
431 root.append(p)
411 return '/'.join(root)
432 return '/'.join(root)
412
433
413 pats = []
434 pats = []
414 files = []
435 files = []
415 roots = []
436 roots = []
416 for kind, name in [patkind(p, dflt_pat) for p in names]:
437 for kind, name in [patkind(p, dflt_pat) for p in names]:
417 if kind in ('glob', 'relpath'):
438 if kind in ('glob', 'relpath'):
418 name = canonpath(canonroot, cwd, name)
439 name = canonpath(canonroot, cwd, name)
419 if name == '':
440 if name == '':
420 kind, name = 'glob', '**'
441 kind, name = 'glob', '**'
421 if kind in ('glob', 'path', 're'):
442 if kind in ('glob', 'path', 're'):
422 pats.append((kind, name))
443 pats.append((kind, name))
423 if kind == 'glob':
444 if kind == 'glob':
424 root = globprefix(name)
445 root = globprefix(name)
425 if root: roots.append(root)
446 if root: roots.append(root)
426 elif kind == 'relpath':
447 elif kind == 'relpath':
427 files.append((kind, name))
448 files.append((kind, name))
428 roots.append(name)
449 roots.append(name)
429
450
430 patmatch = matchfn(pats, '$') or always
451 patmatch = matchfn(pats, '$') or always
431 filematch = matchfn(files, '(?:/|$)') or always
452 filematch = matchfn(files, '(?:/|$)') or always
432 incmatch = always
453 incmatch = always
433 if inc:
454 if inc:
434 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
455 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
435 incmatch = matchfn(inckinds, '(?:/|$)')
456 incmatch = matchfn(inckinds, '(?:/|$)')
436 excmatch = lambda fn: False
457 excmatch = lambda fn: False
437 if exc:
458 if exc:
438 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
459 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
439 excmatch = matchfn(exckinds, '(?:/|$)')
460 excmatch = matchfn(exckinds, '(?:/|$)')
440
461
441 return (roots,
462 return (roots,
442 lambda fn: (incmatch(fn) and not excmatch(fn) and
463 lambda fn: (incmatch(fn) and not excmatch(fn) and
443 (fn.endswith('/') or
464 (fn.endswith('/') or
444 (not pats and not files) or
465 (not pats and not files) or
445 (pats and patmatch(fn)) or
466 (pats and patmatch(fn)) or
446 (files and filematch(fn)))),
467 (files and filematch(fn)))),
447 (inc or exc or (pats and pats != [('glob', '**')])) and True)
468 (inc or exc or (pats and pats != [('glob', '**')])) and True)
448
469
449 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
470 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
450 '''enhanced shell command execution.
471 '''enhanced shell command execution.
451 run with environment maybe modified, maybe in different dir.
472 run with environment maybe modified, maybe in different dir.
452
473
453 if command fails and onerr is None, return status. if ui object,
474 if command fails and onerr is None, return status. if ui object,
454 print error message and return status, else raise onerr object as
475 print error message and return status, else raise onerr object as
455 exception.'''
476 exception.'''
456 def py2shell(val):
477 def py2shell(val):
457 'convert python object into string that is useful to shell'
478 'convert python object into string that is useful to shell'
458 if val in (None, False):
479 if val in (None, False):
459 return '0'
480 return '0'
460 if val == True:
481 if val == True:
461 return '1'
482 return '1'
462 return str(val)
483 return str(val)
463 oldenv = {}
484 oldenv = {}
464 for k in environ:
485 for k in environ:
465 oldenv[k] = os.environ.get(k)
486 oldenv[k] = os.environ.get(k)
466 if cwd is not None:
487 if cwd is not None:
467 oldcwd = os.getcwd()
488 oldcwd = os.getcwd()
468 try:
489 try:
469 for k, v in environ.iteritems():
490 for k, v in environ.iteritems():
470 os.environ[k] = py2shell(v)
491 os.environ[k] = py2shell(v)
471 if cwd is not None and oldcwd != cwd:
492 if cwd is not None and oldcwd != cwd:
472 os.chdir(cwd)
493 os.chdir(cwd)
473 rc = os.system(cmd)
494 rc = os.system(cmd)
474 if rc and onerr:
495 if rc and onerr:
475 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
496 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
476 explain_exit(rc)[0])
497 explain_exit(rc)[0])
477 if errprefix:
498 if errprefix:
478 errmsg = '%s: %s' % (errprefix, errmsg)
499 errmsg = '%s: %s' % (errprefix, errmsg)
479 try:
500 try:
480 onerr.warn(errmsg + '\n')
501 onerr.warn(errmsg + '\n')
481 except AttributeError:
502 except AttributeError:
482 raise onerr(errmsg)
503 raise onerr(errmsg)
483 return rc
504 return rc
484 finally:
505 finally:
485 for k, v in oldenv.iteritems():
506 for k, v in oldenv.iteritems():
486 if v is None:
507 if v is None:
487 del os.environ[k]
508 del os.environ[k]
488 else:
509 else:
489 os.environ[k] = v
510 os.environ[k] = v
490 if cwd is not None and oldcwd != cwd:
511 if cwd is not None and oldcwd != cwd:
491 os.chdir(oldcwd)
512 os.chdir(oldcwd)
492
513
493 def rename(src, dst):
514 def rename(src, dst):
494 """forcibly rename a file"""
515 """forcibly rename a file"""
495 try:
516 try:
496 os.rename(src, dst)
517 os.rename(src, dst)
497 except OSError, err:
518 except OSError, err:
498 # on windows, rename to existing file is not allowed, so we
519 # on windows, rename to existing file is not allowed, so we
499 # must delete destination first. but if file is open, unlink
520 # must delete destination first. but if file is open, unlink
500 # schedules it for delete but does not delete it. rename
521 # schedules it for delete but does not delete it. rename
501 # happens immediately even for open files, so we create
522 # happens immediately even for open files, so we create
502 # temporary file, delete it, rename destination to that name,
523 # temporary file, delete it, rename destination to that name,
503 # then delete that. then rename is safe to do.
524 # then delete that. then rename is safe to do.
504 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
525 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
505 os.close(fd)
526 os.close(fd)
506 os.unlink(temp)
527 os.unlink(temp)
507 os.rename(dst, temp)
528 os.rename(dst, temp)
508 os.unlink(temp)
529 os.unlink(temp)
509 os.rename(src, dst)
530 os.rename(src, dst)
510
531
511 def unlink(f):
532 def unlink(f):
512 """unlink and remove the directory if it is empty"""
533 """unlink and remove the directory if it is empty"""
513 os.unlink(f)
534 os.unlink(f)
514 # try removing directories that might now be empty
535 # try removing directories that might now be empty
515 try:
536 try:
516 os.removedirs(os.path.dirname(f))
537 os.removedirs(os.path.dirname(f))
517 except OSError:
538 except OSError:
518 pass
539 pass
519
540
520 def copyfile(src, dest):
541 def copyfile(src, dest):
521 "copy a file, preserving mode"
542 "copy a file, preserving mode"
522 try:
543 try:
523 shutil.copyfile(src, dest)
544 shutil.copyfile(src, dest)
524 shutil.copymode(src, dest)
545 shutil.copymode(src, dest)
525 except shutil.Error, inst:
546 except shutil.Error, inst:
526 raise util.Abort(str(inst))
547 raise util.Abort(str(inst))
527
548
528 def copyfiles(src, dst, hardlink=None):
549 def copyfiles(src, dst, hardlink=None):
529 """Copy a directory tree using hardlinks if possible"""
550 """Copy a directory tree using hardlinks if possible"""
530
551
531 if hardlink is None:
552 if hardlink is None:
532 hardlink = (os.stat(src).st_dev ==
553 hardlink = (os.stat(src).st_dev ==
533 os.stat(os.path.dirname(dst)).st_dev)
554 os.stat(os.path.dirname(dst)).st_dev)
534
555
535 if os.path.isdir(src):
556 if os.path.isdir(src):
536 os.mkdir(dst)
557 os.mkdir(dst)
537 for name in os.listdir(src):
558 for name in os.listdir(src):
538 srcname = os.path.join(src, name)
559 srcname = os.path.join(src, name)
539 dstname = os.path.join(dst, name)
560 dstname = os.path.join(dst, name)
540 copyfiles(srcname, dstname, hardlink)
561 copyfiles(srcname, dstname, hardlink)
541 else:
562 else:
542 if hardlink:
563 if hardlink:
543 try:
564 try:
544 os_link(src, dst)
565 os_link(src, dst)
545 except (IOError, OSError):
566 except (IOError, OSError):
546 hardlink = False
567 hardlink = False
547 shutil.copy(src, dst)
568 shutil.copy(src, dst)
548 else:
569 else:
549 shutil.copy(src, dst)
570 shutil.copy(src, dst)
550
571
551 def audit_path(path):
572 def audit_path(path):
552 """Abort if path contains dangerous components"""
573 """Abort if path contains dangerous components"""
553 parts = os.path.normcase(path).split(os.sep)
574 parts = os.path.normcase(path).split(os.sep)
554 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
575 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
555 or os.pardir in parts):
576 or os.pardir in parts):
556 raise Abort(_("path contains illegal component: %s\n") % path)
577 raise Abort(_("path contains illegal component: %s\n") % path)
557
578
558 def _makelock_file(info, pathname):
579 def _makelock_file(info, pathname):
559 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
580 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
560 os.write(ld, info)
581 os.write(ld, info)
561 os.close(ld)
582 os.close(ld)
562
583
563 def _readlock_file(pathname):
584 def _readlock_file(pathname):
564 return posixfile(pathname).read()
585 return posixfile(pathname).read()
565
586
566 def nlinks(pathname):
587 def nlinks(pathname):
567 """Return number of hardlinks for the given file."""
588 """Return number of hardlinks for the given file."""
568 return os.lstat(pathname).st_nlink
589 return os.lstat(pathname).st_nlink
569
590
570 if hasattr(os, 'link'):
591 if hasattr(os, 'link'):
571 os_link = os.link
592 os_link = os.link
572 else:
593 else:
573 def os_link(src, dst):
594 def os_link(src, dst):
574 raise OSError(0, _("Hardlinks not supported"))
595 raise OSError(0, _("Hardlinks not supported"))
575
596
576 def fstat(fp):
597 def fstat(fp):
577 '''stat file object that may not have fileno method.'''
598 '''stat file object that may not have fileno method.'''
578 try:
599 try:
579 return os.fstat(fp.fileno())
600 return os.fstat(fp.fileno())
580 except AttributeError:
601 except AttributeError:
581 return os.stat(fp.name)
602 return os.stat(fp.name)
582
603
583 posixfile = file
604 posixfile = file
584
605
585 def is_win_9x():
606 def is_win_9x():
586 '''return true if run on windows 95, 98 or me.'''
607 '''return true if run on windows 95, 98 or me.'''
587 try:
608 try:
588 return sys.getwindowsversion()[3] == 1
609 return sys.getwindowsversion()[3] == 1
589 except AttributeError:
610 except AttributeError:
590 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
611 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
591
612
592 getuser_fallback = None
613 getuser_fallback = None
593
614
594 def getuser():
615 def getuser():
595 '''return name of current user'''
616 '''return name of current user'''
596 try:
617 try:
597 return getpass.getuser()
618 return getpass.getuser()
598 except ImportError:
619 except ImportError:
599 # import of pwd will fail on windows - try fallback
620 # import of pwd will fail on windows - try fallback
600 if getuser_fallback:
621 if getuser_fallback:
601 return getuser_fallback()
622 return getuser_fallback()
602 # raised if win32api not available
623 # raised if win32api not available
603 raise Abort(_('user name not available - set USERNAME '
624 raise Abort(_('user name not available - set USERNAME '
604 'environment variable'))
625 'environment variable'))
605
626
606 def username(uid=None):
627 def username(uid=None):
607 """Return the name of the user with the given uid.
628 """Return the name of the user with the given uid.
608
629
609 If uid is None, return the name of the current user."""
630 If uid is None, return the name of the current user."""
610 try:
631 try:
611 import pwd
632 import pwd
612 if uid is None:
633 if uid is None:
613 uid = os.getuid()
634 uid = os.getuid()
614 try:
635 try:
615 return pwd.getpwuid(uid)[0]
636 return pwd.getpwuid(uid)[0]
616 except KeyError:
637 except KeyError:
617 return str(uid)
638 return str(uid)
618 except ImportError:
639 except ImportError:
619 return None
640 return None
620
641
621 def groupname(gid=None):
642 def groupname(gid=None):
622 """Return the name of the group with the given gid.
643 """Return the name of the group with the given gid.
623
644
624 If gid is None, return the name of the current group."""
645 If gid is None, return the name of the current group."""
625 try:
646 try:
626 import grp
647 import grp
627 if gid is None:
648 if gid is None:
628 gid = os.getgid()
649 gid = os.getgid()
629 try:
650 try:
630 return grp.getgrgid(gid)[0]
651 return grp.getgrgid(gid)[0]
631 except KeyError:
652 except KeyError:
632 return str(gid)
653 return str(gid)
633 except ImportError:
654 except ImportError:
634 return None
655 return None
635
656
636 # File system features
657 # File system features
637
658
638 def checkfolding(path):
659 def checkfolding(path):
639 """
660 """
640 Check whether the given path is on a case-sensitive filesystem
661 Check whether the given path is on a case-sensitive filesystem
641
662
642 Requires a path (like /foo/.hg) ending with a foldable final
663 Requires a path (like /foo/.hg) ending with a foldable final
643 directory component.
664 directory component.
644 """
665 """
645 s1 = os.stat(path)
666 s1 = os.stat(path)
646 d, b = os.path.split(path)
667 d, b = os.path.split(path)
647 p2 = os.path.join(d, b.upper())
668 p2 = os.path.join(d, b.upper())
648 if path == p2:
669 if path == p2:
649 p2 = os.path.join(d, b.lower())
670 p2 = os.path.join(d, b.lower())
650 try:
671 try:
651 s2 = os.stat(p2)
672 s2 = os.stat(p2)
652 if s2 == s1:
673 if s2 == s1:
653 return False
674 return False
654 return True
675 return True
655 except:
676 except:
656 return True
677 return True
657
678
658 # Platform specific variants
679 # Platform specific variants
659 if os.name == 'nt':
680 if os.name == 'nt':
660 demandload(globals(), "msvcrt")
681 demandload(globals(), "msvcrt")
661 nulldev = 'NUL:'
682 nulldev = 'NUL:'
662
683
663 class winstdout:
684 class winstdout:
664 '''stdout on windows misbehaves if sent through a pipe'''
685 '''stdout on windows misbehaves if sent through a pipe'''
665
686
666 def __init__(self, fp):
687 def __init__(self, fp):
667 self.fp = fp
688 self.fp = fp
668
689
669 def __getattr__(self, key):
690 def __getattr__(self, key):
670 return getattr(self.fp, key)
691 return getattr(self.fp, key)
671
692
672 def close(self):
693 def close(self):
673 try:
694 try:
674 self.fp.close()
695 self.fp.close()
675 except: pass
696 except: pass
676
697
677 def write(self, s):
698 def write(self, s):
678 try:
699 try:
679 return self.fp.write(s)
700 return self.fp.write(s)
680 except IOError, inst:
701 except IOError, inst:
681 if inst.errno != 0: raise
702 if inst.errno != 0: raise
682 self.close()
703 self.close()
683 raise IOError(errno.EPIPE, 'Broken pipe')
704 raise IOError(errno.EPIPE, 'Broken pipe')
684
705
685 sys.stdout = winstdout(sys.stdout)
706 sys.stdout = winstdout(sys.stdout)
686
707
687 def system_rcpath():
708 def system_rcpath():
688 try:
709 try:
689 return system_rcpath_win32()
710 return system_rcpath_win32()
690 except:
711 except:
691 return [r'c:\mercurial\mercurial.ini']
712 return [r'c:\mercurial\mercurial.ini']
692
713
693 def os_rcpath():
714 def os_rcpath():
694 '''return default os-specific hgrc search path'''
715 '''return default os-specific hgrc search path'''
695 path = system_rcpath()
716 path = system_rcpath()
696 path.append(user_rcpath())
717 path.append(user_rcpath())
697 userprofile = os.environ.get('USERPROFILE')
718 userprofile = os.environ.get('USERPROFILE')
698 if userprofile:
719 if userprofile:
699 path.append(os.path.join(userprofile, 'mercurial.ini'))
720 path.append(os.path.join(userprofile, 'mercurial.ini'))
700 return path
721 return path
701
722
702 def user_rcpath():
723 def user_rcpath():
703 '''return os-specific hgrc search path to the user dir'''
724 '''return os-specific hgrc search path to the user dir'''
704 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
725 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
705
726
706 def parse_patch_output(output_line):
727 def parse_patch_output(output_line):
707 """parses the output produced by patch and returns the file name"""
728 """parses the output produced by patch and returns the file name"""
708 pf = output_line[14:]
729 pf = output_line[14:]
709 if pf[0] == '`':
730 if pf[0] == '`':
710 pf = pf[1:-1] # Remove the quotes
731 pf = pf[1:-1] # Remove the quotes
711 return pf
732 return pf
712
733
713 def testpid(pid):
734 def testpid(pid):
714 '''return False if pid dead, True if running or not known'''
735 '''return False if pid dead, True if running or not known'''
715 return True
736 return True
716
737
717 def is_exec(f, last):
738 def is_exec(f, last):
718 return last
739 return last
719
740
720 def set_exec(f, mode):
741 def set_exec(f, mode):
721 pass
742 pass
722
743
723 def set_binary(fd):
744 def set_binary(fd):
724 msvcrt.setmode(fd.fileno(), os.O_BINARY)
745 msvcrt.setmode(fd.fileno(), os.O_BINARY)
725
746
726 def pconvert(path):
747 def pconvert(path):
727 return path.replace("\\", "/")
748 return path.replace("\\", "/")
728
749
729 def localpath(path):
750 def localpath(path):
730 return path.replace('/', '\\')
751 return path.replace('/', '\\')
731
752
732 def normpath(path):
753 def normpath(path):
733 return pconvert(os.path.normpath(path))
754 return pconvert(os.path.normpath(path))
734
755
735 makelock = _makelock_file
756 makelock = _makelock_file
736 readlock = _readlock_file
757 readlock = _readlock_file
737
758
738 def samestat(s1, s2):
759 def samestat(s1, s2):
739 return False
760 return False
740
761
741 def shellquote(s):
762 def shellquote(s):
742 return '"%s"' % s.replace('"', '\\"')
763 return '"%s"' % s.replace('"', '\\"')
743
764
744 def explain_exit(code):
765 def explain_exit(code):
745 return _("exited with status %d") % code, code
766 return _("exited with status %d") % code, code
746
767
747 # if you change this stub into a real check, please try to implement the
768 # if you change this stub into a real check, please try to implement the
748 # username and groupname functions above, too.
769 # username and groupname functions above, too.
749 def isowner(fp, st=None):
770 def isowner(fp, st=None):
750 return True
771 return True
751
772
752 try:
773 try:
753 # override functions with win32 versions if possible
774 # override functions with win32 versions if possible
754 from util_win32 import *
775 from util_win32 import *
755 if not is_win_9x():
776 if not is_win_9x():
756 posixfile = posixfile_nt
777 posixfile = posixfile_nt
757 except ImportError:
778 except ImportError:
758 pass
779 pass
759
780
760 else:
781 else:
761 nulldev = '/dev/null'
782 nulldev = '/dev/null'
762
783
763 def rcfiles(path):
784 def rcfiles(path):
764 rcs = [os.path.join(path, 'hgrc')]
785 rcs = [os.path.join(path, 'hgrc')]
765 rcdir = os.path.join(path, 'hgrc.d')
786 rcdir = os.path.join(path, 'hgrc.d')
766 try:
787 try:
767 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
788 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
768 if f.endswith(".rc")])
789 if f.endswith(".rc")])
769 except OSError:
790 except OSError:
770 pass
791 pass
771 return rcs
792 return rcs
772
793
773 def os_rcpath():
794 def os_rcpath():
774 '''return default os-specific hgrc search path'''
795 '''return default os-specific hgrc search path'''
775 path = []
796 path = []
776 # old mod_python does not set sys.argv
797 # old mod_python does not set sys.argv
777 if len(getattr(sys, 'argv', [])) > 0:
798 if len(getattr(sys, 'argv', [])) > 0:
778 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
799 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
779 '/../etc/mercurial'))
800 '/../etc/mercurial'))
780 path.extend(rcfiles('/etc/mercurial'))
801 path.extend(rcfiles('/etc/mercurial'))
781 path.append(os.path.expanduser('~/.hgrc'))
802 path.append(os.path.expanduser('~/.hgrc'))
782 path = [os.path.normpath(f) for f in path]
803 path = [os.path.normpath(f) for f in path]
783 return path
804 return path
784
805
785 def parse_patch_output(output_line):
806 def parse_patch_output(output_line):
786 """parses the output produced by patch and returns the file name"""
807 """parses the output produced by patch and returns the file name"""
787 pf = output_line[14:]
808 pf = output_line[14:]
788 if pf.startswith("'") and pf.endswith("'") and " " in pf:
809 if pf.startswith("'") and pf.endswith("'") and " " in pf:
789 pf = pf[1:-1] # Remove the quotes
810 pf = pf[1:-1] # Remove the quotes
790 return pf
811 return pf
791
812
792 def is_exec(f, last):
813 def is_exec(f, last):
793 """check whether a file is executable"""
814 """check whether a file is executable"""
794 return (os.lstat(f).st_mode & 0100 != 0)
815 return (os.lstat(f).st_mode & 0100 != 0)
795
816
796 def set_exec(f, mode):
817 def set_exec(f, mode):
797 s = os.lstat(f).st_mode
818 s = os.lstat(f).st_mode
798 if (s & 0100 != 0) == mode:
819 if (s & 0100 != 0) == mode:
799 return
820 return
800 if mode:
821 if mode:
801 # Turn on +x for every +r bit when making a file executable
822 # Turn on +x for every +r bit when making a file executable
802 # and obey umask.
823 # and obey umask.
803 umask = os.umask(0)
824 umask = os.umask(0)
804 os.umask(umask)
825 os.umask(umask)
805 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
826 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
806 else:
827 else:
807 os.chmod(f, s & 0666)
828 os.chmod(f, s & 0666)
808
829
809 def set_binary(fd):
830 def set_binary(fd):
810 pass
831 pass
811
832
812 def pconvert(path):
833 def pconvert(path):
813 return path
834 return path
814
835
815 def localpath(path):
836 def localpath(path):
816 return path
837 return path
817
838
818 normpath = os.path.normpath
839 normpath = os.path.normpath
819 samestat = os.path.samestat
840 samestat = os.path.samestat
820
841
821 def makelock(info, pathname):
842 def makelock(info, pathname):
822 try:
843 try:
823 os.symlink(info, pathname)
844 os.symlink(info, pathname)
824 except OSError, why:
845 except OSError, why:
825 if why.errno == errno.EEXIST:
846 if why.errno == errno.EEXIST:
826 raise
847 raise
827 else:
848 else:
828 _makelock_file(info, pathname)
849 _makelock_file(info, pathname)
829
850
830 def readlock(pathname):
851 def readlock(pathname):
831 try:
852 try:
832 return os.readlink(pathname)
853 return os.readlink(pathname)
833 except OSError, why:
854 except OSError, why:
834 if why.errno == errno.EINVAL:
855 if why.errno == errno.EINVAL:
835 return _readlock_file(pathname)
856 return _readlock_file(pathname)
836 else:
857 else:
837 raise
858 raise
838
859
839 def shellquote(s):
860 def shellquote(s):
840 return "'%s'" % s.replace("'", "'\\''")
861 return "'%s'" % s.replace("'", "'\\''")
841
862
842 def testpid(pid):
863 def testpid(pid):
843 '''return False if pid dead, True if running or not sure'''
864 '''return False if pid dead, True if running or not sure'''
844 try:
865 try:
845 os.kill(pid, 0)
866 os.kill(pid, 0)
846 return True
867 return True
847 except OSError, inst:
868 except OSError, inst:
848 return inst.errno != errno.ESRCH
869 return inst.errno != errno.ESRCH
849
870
850 def explain_exit(code):
871 def explain_exit(code):
851 """return a 2-tuple (desc, code) describing a process's status"""
872 """return a 2-tuple (desc, code) describing a process's status"""
852 if os.WIFEXITED(code):
873 if os.WIFEXITED(code):
853 val = os.WEXITSTATUS(code)
874 val = os.WEXITSTATUS(code)
854 return _("exited with status %d") % val, val
875 return _("exited with status %d") % val, val
855 elif os.WIFSIGNALED(code):
876 elif os.WIFSIGNALED(code):
856 val = os.WTERMSIG(code)
877 val = os.WTERMSIG(code)
857 return _("killed by signal %d") % val, val
878 return _("killed by signal %d") % val, val
858 elif os.WIFSTOPPED(code):
879 elif os.WIFSTOPPED(code):
859 val = os.WSTOPSIG(code)
880 val = os.WSTOPSIG(code)
860 return _("stopped by signal %d") % val, val
881 return _("stopped by signal %d") % val, val
861 raise ValueError(_("invalid exit code"))
882 raise ValueError(_("invalid exit code"))
862
883
863 def isowner(fp, st=None):
884 def isowner(fp, st=None):
864 """Return True if the file object f belongs to the current user.
885 """Return True if the file object f belongs to the current user.
865
886
866 The return value of a util.fstat(f) may be passed as the st argument.
887 The return value of a util.fstat(f) may be passed as the st argument.
867 """
888 """
868 if st is None:
889 if st is None:
869 st = fstat(f)
890 st = fstat(f)
870 return st.st_uid == os.getuid()
891 return st.st_uid == os.getuid()
871
892
872
893
873 def opener(base, audit=True):
894 def opener(base, audit=True):
874 """
895 """
875 return a function that opens files relative to base
896 return a function that opens files relative to base
876
897
877 this function is used to hide the details of COW semantics and
898 this function is used to hide the details of COW semantics and
878 remote file access from higher level code.
899 remote file access from higher level code.
879 """
900 """
880 p = base
901 p = base
881 audit_p = audit
902 audit_p = audit
882
903
883 def mktempcopy(name):
904 def mktempcopy(name):
884 d, fn = os.path.split(name)
905 d, fn = os.path.split(name)
885 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
906 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
886 os.close(fd)
907 os.close(fd)
887 ofp = posixfile(temp, "wb")
908 ofp = posixfile(temp, "wb")
888 try:
909 try:
889 try:
910 try:
890 ifp = posixfile(name, "rb")
911 ifp = posixfile(name, "rb")
891 except IOError, inst:
912 except IOError, inst:
892 if not getattr(inst, 'filename', None):
913 if not getattr(inst, 'filename', None):
893 inst.filename = name
914 inst.filename = name
894 raise
915 raise
895 for chunk in filechunkiter(ifp):
916 for chunk in filechunkiter(ifp):
896 ofp.write(chunk)
917 ofp.write(chunk)
897 ifp.close()
918 ifp.close()
898 ofp.close()
919 ofp.close()
899 except:
920 except:
900 try: os.unlink(temp)
921 try: os.unlink(temp)
901 except: pass
922 except: pass
902 raise
923 raise
903 st = os.lstat(name)
924 st = os.lstat(name)
904 os.chmod(temp, st.st_mode)
925 os.chmod(temp, st.st_mode)
905 return temp
926 return temp
906
927
907 class atomictempfile(posixfile):
928 class atomictempfile(posixfile):
908 """the file will only be copied when rename is called"""
929 """the file will only be copied when rename is called"""
909 def __init__(self, name, mode):
930 def __init__(self, name, mode):
910 self.__name = name
931 self.__name = name
911 self.temp = mktempcopy(name)
932 self.temp = mktempcopy(name)
912 posixfile.__init__(self, self.temp, mode)
933 posixfile.__init__(self, self.temp, mode)
913 def rename(self):
934 def rename(self):
914 if not self.closed:
935 if not self.closed:
915 posixfile.close(self)
936 posixfile.close(self)
916 rename(self.temp, localpath(self.__name))
937 rename(self.temp, localpath(self.__name))
917 def __del__(self):
938 def __del__(self):
918 if not self.closed:
939 if not self.closed:
919 try:
940 try:
920 os.unlink(self.temp)
941 os.unlink(self.temp)
921 except: pass
942 except: pass
922 posixfile.close(self)
943 posixfile.close(self)
923
944
924 class atomicfile(atomictempfile):
945 class atomicfile(atomictempfile):
925 """the file will only be copied on close"""
946 """the file will only be copied on close"""
926 def __init__(self, name, mode):
947 def __init__(self, name, mode):
927 atomictempfile.__init__(self, name, mode)
948 atomictempfile.__init__(self, name, mode)
928 def close(self):
949 def close(self):
929 self.rename()
950 self.rename()
930 def __del__(self):
951 def __del__(self):
931 self.rename()
952 self.rename()
932
953
933 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
954 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
934 if audit_p:
955 if audit_p:
935 audit_path(path)
956 audit_path(path)
936 f = os.path.join(p, path)
957 f = os.path.join(p, path)
937
958
938 if not text:
959 if not text:
939 mode += "b" # for that other OS
960 mode += "b" # for that other OS
940
961
941 if mode[0] != "r":
962 if mode[0] != "r":
942 try:
963 try:
943 nlink = nlinks(f)
964 nlink = nlinks(f)
944 except OSError:
965 except OSError:
945 d = os.path.dirname(f)
966 d = os.path.dirname(f)
946 if not os.path.isdir(d):
967 if not os.path.isdir(d):
947 os.makedirs(d)
968 os.makedirs(d)
948 else:
969 else:
949 if atomic:
970 if atomic:
950 return atomicfile(f, mode)
971 return atomicfile(f, mode)
951 elif atomictemp:
972 elif atomictemp:
952 return atomictempfile(f, mode)
973 return atomictempfile(f, mode)
953 if nlink > 1:
974 if nlink > 1:
954 rename(mktempcopy(f), f)
975 rename(mktempcopy(f), f)
955 return posixfile(f, mode)
976 return posixfile(f, mode)
956
977
957 return o
978 return o
958
979
959 class chunkbuffer(object):
980 class chunkbuffer(object):
960 """Allow arbitrary sized chunks of data to be efficiently read from an
981 """Allow arbitrary sized chunks of data to be efficiently read from an
961 iterator over chunks of arbitrary size."""
982 iterator over chunks of arbitrary size."""
962
983
963 def __init__(self, in_iter, targetsize = 2**16):
984 def __init__(self, in_iter, targetsize = 2**16):
964 """in_iter is the iterator that's iterating over the input chunks.
985 """in_iter is the iterator that's iterating over the input chunks.
965 targetsize is how big a buffer to try to maintain."""
986 targetsize is how big a buffer to try to maintain."""
966 self.in_iter = iter(in_iter)
987 self.in_iter = iter(in_iter)
967 self.buf = ''
988 self.buf = ''
968 self.targetsize = int(targetsize)
989 self.targetsize = int(targetsize)
969 if self.targetsize <= 0:
990 if self.targetsize <= 0:
970 raise ValueError(_("targetsize must be greater than 0, was %d") %
991 raise ValueError(_("targetsize must be greater than 0, was %d") %
971 targetsize)
992 targetsize)
972 self.iterempty = False
993 self.iterempty = False
973
994
974 def fillbuf(self):
995 def fillbuf(self):
975 """Ignore target size; read every chunk from iterator until empty."""
996 """Ignore target size; read every chunk from iterator until empty."""
976 if not self.iterempty:
997 if not self.iterempty:
977 collector = cStringIO.StringIO()
998 collector = cStringIO.StringIO()
978 collector.write(self.buf)
999 collector.write(self.buf)
979 for ch in self.in_iter:
1000 for ch in self.in_iter:
980 collector.write(ch)
1001 collector.write(ch)
981 self.buf = collector.getvalue()
1002 self.buf = collector.getvalue()
982 self.iterempty = True
1003 self.iterempty = True
983
1004
984 def read(self, l):
1005 def read(self, l):
985 """Read L bytes of data from the iterator of chunks of data.
1006 """Read L bytes of data from the iterator of chunks of data.
986 Returns less than L bytes if the iterator runs dry."""
1007 Returns less than L bytes if the iterator runs dry."""
987 if l > len(self.buf) and not self.iterempty:
1008 if l > len(self.buf) and not self.iterempty:
988 # Clamp to a multiple of self.targetsize
1009 # Clamp to a multiple of self.targetsize
989 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1010 targetsize = self.targetsize * ((l // self.targetsize) + 1)
990 collector = cStringIO.StringIO()
1011 collector = cStringIO.StringIO()
991 collector.write(self.buf)
1012 collector.write(self.buf)
992 collected = len(self.buf)
1013 collected = len(self.buf)
993 for chunk in self.in_iter:
1014 for chunk in self.in_iter:
994 collector.write(chunk)
1015 collector.write(chunk)
995 collected += len(chunk)
1016 collected += len(chunk)
996 if collected >= targetsize:
1017 if collected >= targetsize:
997 break
1018 break
998 if collected < targetsize:
1019 if collected < targetsize:
999 self.iterempty = True
1020 self.iterempty = True
1000 self.buf = collector.getvalue()
1021 self.buf = collector.getvalue()
1001 s, self.buf = self.buf[:l], buffer(self.buf, l)
1022 s, self.buf = self.buf[:l], buffer(self.buf, l)
1002 return s
1023 return s
1003
1024
1004 def filechunkiter(f, size=65536, limit=None):
1025 def filechunkiter(f, size=65536, limit=None):
1005 """Create a generator that produces the data in the file size
1026 """Create a generator that produces the data in the file size
1006 (default 65536) bytes at a time, up to optional limit (default is
1027 (default 65536) bytes at a time, up to optional limit (default is
1007 to read all data). Chunks may be less than size bytes if the
1028 to read all data). Chunks may be less than size bytes if the
1008 chunk is the last chunk in the file, or the file is a socket or
1029 chunk is the last chunk in the file, or the file is a socket or
1009 some other type of file that sometimes reads less data than is
1030 some other type of file that sometimes reads less data than is
1010 requested."""
1031 requested."""
1011 assert size >= 0
1032 assert size >= 0
1012 assert limit is None or limit >= 0
1033 assert limit is None or limit >= 0
1013 while True:
1034 while True:
1014 if limit is None: nbytes = size
1035 if limit is None: nbytes = size
1015 else: nbytes = min(limit, size)
1036 else: nbytes = min(limit, size)
1016 s = nbytes and f.read(nbytes)
1037 s = nbytes and f.read(nbytes)
1017 if not s: break
1038 if not s: break
1018 if limit: limit -= len(s)
1039 if limit: limit -= len(s)
1019 yield s
1040 yield s
1020
1041
1021 def makedate():
1042 def makedate():
1022 lt = time.localtime()
1043 lt = time.localtime()
1023 if lt[8] == 1 and time.daylight:
1044 if lt[8] == 1 and time.daylight:
1024 tz = time.altzone
1045 tz = time.altzone
1025 else:
1046 else:
1026 tz = time.timezone
1047 tz = time.timezone
1027 return time.mktime(lt), tz
1048 return time.mktime(lt), tz
1028
1049
1029 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1050 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1030 """represent a (unixtime, offset) tuple as a localized time.
1051 """represent a (unixtime, offset) tuple as a localized time.
1031 unixtime is seconds since the epoch, and offset is the time zone's
1052 unixtime is seconds since the epoch, and offset is the time zone's
1032 number of seconds away from UTC. if timezone is false, do not
1053 number of seconds away from UTC. if timezone is false, do not
1033 append time zone to string."""
1054 append time zone to string."""
1034 t, tz = date or makedate()
1055 t, tz = date or makedate()
1035 s = time.strftime(format, time.gmtime(float(t) - tz))
1056 s = time.strftime(format, time.gmtime(float(t) - tz))
1036 if timezone:
1057 if timezone:
1037 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1058 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1038 return s
1059 return s
1039
1060
1040 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
1061 def strdate(string, format='%a %b %d %H:%M:%S %Y'):
1041 """parse a localized time string and return a (unixtime, offset) tuple.
1062 """parse a localized time string and return a (unixtime, offset) tuple.
1042 if the string cannot be parsed, ValueError is raised."""
1063 if the string cannot be parsed, ValueError is raised."""
1043 def hastimezone(string):
1064 def hastimezone(string):
1044 return (string[-4:].isdigit() and
1065 return (string[-4:].isdigit() and
1045 (string[-5] == '+' or string[-5] == '-') and
1066 (string[-5] == '+' or string[-5] == '-') and
1046 string[-6].isspace())
1067 string[-6].isspace())
1047
1068
1048 # NOTE: unixtime = localunixtime + offset
1069 # NOTE: unixtime = localunixtime + offset
1049 if hastimezone(string):
1070 if hastimezone(string):
1050 date, tz = string[:-6], string[-5:]
1071 date, tz = string[:-6], string[-5:]
1051 tz = int(tz)
1072 tz = int(tz)
1052 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1073 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1053 else:
1074 else:
1054 date, offset = string, None
1075 date, offset = string, None
1076
1077 # add missing elements
1078 if '%y' not in format.lower():
1079 date += "@" + datestr(makedate(), "%Y", False)
1080 format += "@%Y"
1081 if '%m' not in format and '%b' not in format:
1082 date += "@" + datestr(makedate(), "%m", False)
1083 format += "@%m"
1084 if '%d' not in format:
1085 date += "@" + datestr(makedate(), "%d", False)
1086 format += "@%d"
1087
1055 timetuple = time.strptime(date, format)
1088 timetuple = time.strptime(date, format)
1056 localunixtime = int(calendar.timegm(timetuple))
1089 localunixtime = int(calendar.timegm(timetuple))
1057 if offset is None:
1090 if offset is None:
1058 # local timezone
1091 # local timezone
1059 unixtime = int(time.mktime(timetuple))
1092 unixtime = int(time.mktime(timetuple))
1060 offset = unixtime - localunixtime
1093 offset = unixtime - localunixtime
1061 else:
1094 else:
1062 unixtime = localunixtime + offset
1095 unixtime = localunixtime + offset
1063 return unixtime, offset
1096 return unixtime, offset
1064
1097
1065 def parsedate(string, formats=None):
1098 def parsedate(string, formats=None):
1066 """parse a localized time string and return a (unixtime, offset) tuple.
1099 """parse a localized time string and return a (unixtime, offset) tuple.
1067 The date may be a "unixtime offset" string or in one of the specified
1100 The date may be a "unixtime offset" string or in one of the specified
1068 formats."""
1101 formats."""
1069 if not string:
1102 if not string:
1070 return 0, 0
1103 return 0, 0
1071 if not formats:
1104 if not formats:
1072 formats = defaultdateformats
1105 formats = defaultdateformats
1106 string = string.strip()
1073 try:
1107 try:
1074 when, offset = map(int, string.split(' '))
1108 when, offset = map(int, string.split(' '))
1075 except ValueError:
1109 except ValueError:
1076 for format in formats:
1110 for format in formats:
1077 try:
1111 try:
1078 when, offset = strdate(string, format)
1112 when, offset = strdate(string, format)
1079 except ValueError:
1113 except ValueError:
1080 pass
1114 pass
1081 else:
1115 else:
1082 break
1116 break
1083 else:
1117 else:
1084 raise Abort(_('invalid date: %r ') % string)
1118 raise Abort(_('invalid date: %r ') % string)
1085 # validate explicit (probably user-specified) date and
1119 # validate explicit (probably user-specified) date and
1086 # time zone offset. values must fit in signed 32 bits for
1120 # time zone offset. values must fit in signed 32 bits for
1087 # current 32-bit linux runtimes. timezones go from UTC-12
1121 # current 32-bit linux runtimes. timezones go from UTC-12
1088 # to UTC+14
1122 # to UTC+14
1089 if abs(when) > 0x7fffffff:
1123 if abs(when) > 0x7fffffff:
1090 raise Abort(_('date exceeds 32 bits: %d') % when)
1124 raise Abort(_('date exceeds 32 bits: %d') % when)
1091 if offset < -50400 or offset > 43200:
1125 if offset < -50400 or offset > 43200:
1092 raise Abort(_('impossible time zone offset: %d') % offset)
1126 raise Abort(_('impossible time zone offset: %d') % offset)
1093 return when, offset
1127 return when, offset
1094
1128
1095 def shortuser(user):
1129 def shortuser(user):
1096 """Return a short representation of a user name or email address."""
1130 """Return a short representation of a user name or email address."""
1097 f = user.find('@')
1131 f = user.find('@')
1098 if f >= 0:
1132 if f >= 0:
1099 user = user[:f]
1133 user = user[:f]
1100 f = user.find('<')
1134 f = user.find('<')
1101 if f >= 0:
1135 if f >= 0:
1102 user = user[f+1:]
1136 user = user[f+1:]
1103 f = user.find(' ')
1137 f = user.find(' ')
1104 if f >= 0:
1138 if f >= 0:
1105 user = user[:f]
1139 user = user[:f]
1106 f = user.find('.')
1140 f = user.find('.')
1107 if f >= 0:
1141 if f >= 0:
1108 user = user[:f]
1142 user = user[:f]
1109 return user
1143 return user
1110
1144
1111 def ellipsis(text, maxlength=400):
1145 def ellipsis(text, maxlength=400):
1112 """Trim string to at most maxlength (default: 400) characters."""
1146 """Trim string to at most maxlength (default: 400) characters."""
1113 if len(text) <= maxlength:
1147 if len(text) <= maxlength:
1114 return text
1148 return text
1115 else:
1149 else:
1116 return "%s..." % (text[:maxlength-3])
1150 return "%s..." % (text[:maxlength-3])
1117
1151
1118 def walkrepos(path):
1152 def walkrepos(path):
1119 '''yield every hg repository under path, recursively.'''
1153 '''yield every hg repository under path, recursively.'''
1120 def errhandler(err):
1154 def errhandler(err):
1121 if err.filename == path:
1155 if err.filename == path:
1122 raise err
1156 raise err
1123
1157
1124 for root, dirs, files in os.walk(path, onerror=errhandler):
1158 for root, dirs, files in os.walk(path, onerror=errhandler):
1125 for d in dirs:
1159 for d in dirs:
1126 if d == '.hg':
1160 if d == '.hg':
1127 yield root
1161 yield root
1128 dirs[:] = []
1162 dirs[:] = []
1129 break
1163 break
1130
1164
1131 _rcpath = None
1165 _rcpath = None
1132
1166
1133 def rcpath():
1167 def rcpath():
1134 '''return hgrc search path. if env var HGRCPATH is set, use it.
1168 '''return hgrc search path. if env var HGRCPATH is set, use it.
1135 for each item in path, if directory, use files ending in .rc,
1169 for each item in path, if directory, use files ending in .rc,
1136 else use item.
1170 else use item.
1137 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1171 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1138 if no HGRCPATH, use default os-specific path.'''
1172 if no HGRCPATH, use default os-specific path.'''
1139 global _rcpath
1173 global _rcpath
1140 if _rcpath is None:
1174 if _rcpath is None:
1141 if 'HGRCPATH' in os.environ:
1175 if 'HGRCPATH' in os.environ:
1142 _rcpath = []
1176 _rcpath = []
1143 for p in os.environ['HGRCPATH'].split(os.pathsep):
1177 for p in os.environ['HGRCPATH'].split(os.pathsep):
1144 if not p: continue
1178 if not p: continue
1145 if os.path.isdir(p):
1179 if os.path.isdir(p):
1146 for f in os.listdir(p):
1180 for f in os.listdir(p):
1147 if f.endswith('.rc'):
1181 if f.endswith('.rc'):
1148 _rcpath.append(os.path.join(p, f))
1182 _rcpath.append(os.path.join(p, f))
1149 else:
1183 else:
1150 _rcpath.append(p)
1184 _rcpath.append(p)
1151 else:
1185 else:
1152 _rcpath = os_rcpath()
1186 _rcpath = os_rcpath()
1153 return _rcpath
1187 return _rcpath
1154
1188
1155 def bytecount(nbytes):
1189 def bytecount(nbytes):
1156 '''return byte count formatted as readable string, with units'''
1190 '''return byte count formatted as readable string, with units'''
1157
1191
1158 units = (
1192 units = (
1159 (100, 1<<30, _('%.0f GB')),
1193 (100, 1<<30, _('%.0f GB')),
1160 (10, 1<<30, _('%.1f GB')),
1194 (10, 1<<30, _('%.1f GB')),
1161 (1, 1<<30, _('%.2f GB')),
1195 (1, 1<<30, _('%.2f GB')),
1162 (100, 1<<20, _('%.0f MB')),
1196 (100, 1<<20, _('%.0f MB')),
1163 (10, 1<<20, _('%.1f MB')),
1197 (10, 1<<20, _('%.1f MB')),
1164 (1, 1<<20, _('%.2f MB')),
1198 (1, 1<<20, _('%.2f MB')),
1165 (100, 1<<10, _('%.0f KB')),
1199 (100, 1<<10, _('%.0f KB')),
1166 (10, 1<<10, _('%.1f KB')),
1200 (10, 1<<10, _('%.1f KB')),
1167 (1, 1<<10, _('%.2f KB')),
1201 (1, 1<<10, _('%.2f KB')),
1168 (1, 1, _('%.0f bytes')),
1202 (1, 1, _('%.0f bytes')),
1169 )
1203 )
1170
1204
1171 for multiplier, divisor, format in units:
1205 for multiplier, divisor, format in units:
1172 if nbytes >= divisor * multiplier:
1206 if nbytes >= divisor * multiplier:
1173 return format % (nbytes / float(divisor))
1207 return format % (nbytes / float(divisor))
1174 return units[-1][2] % nbytes
1208 return units[-1][2] % nbytes
1175
1209
1176 def drop_scheme(scheme, path):
1210 def drop_scheme(scheme, path):
1177 sc = scheme + ':'
1211 sc = scheme + ':'
1178 if path.startswith(sc):
1212 if path.startswith(sc):
1179 path = path[len(sc):]
1213 path = path[len(sc):]
1180 if path.startswith('//'):
1214 if path.startswith('//'):
1181 path = path[2:]
1215 path = path[2:]
1182 return path
1216 return path
General Comments 0
You need to be logged in to leave comments. Login now