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