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