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