##// END OF EJS Templates
audit: be even pickier (issue1450)
Matt Mackall -
r7820:346fafc1 default
parent child Browse files
Show More
@@ -1,2020 +1,2020 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 740 # on windows, rename to existing file is not allowed, so we
741 741 # must delete destination first. but if file is open, unlink
742 742 # schedules it for delete but does not delete it. rename
743 743 # happens immediately even for open files, so we rename
744 744 # destination to a temporary name, then delete that. then
745 745 # rename is safe to do.
746 746 temp = dst + "-force-rename"
747 747 os.rename(dst, temp)
748 748 os.unlink(temp)
749 749 os.rename(src, dst)
750 750
751 751 def unlink(f):
752 752 """unlink and remove the directory if it is empty"""
753 753 os.unlink(f)
754 754 # try removing directories that might now be empty
755 755 try:
756 756 os.removedirs(os.path.dirname(f))
757 757 except OSError:
758 758 pass
759 759
760 760 def copyfile(src, dest):
761 761 "copy a file, preserving mode and atime/mtime"
762 762 if os.path.islink(src):
763 763 try:
764 764 os.unlink(dest)
765 765 except:
766 766 pass
767 767 os.symlink(os.readlink(src), dest)
768 768 else:
769 769 try:
770 770 shutil.copyfile(src, dest)
771 771 shutil.copystat(src, dest)
772 772 except shutil.Error, inst:
773 773 raise Abort(str(inst))
774 774
775 775 def copyfiles(src, dst, hardlink=None):
776 776 """Copy a directory tree using hardlinks if possible"""
777 777
778 778 if hardlink is None:
779 779 hardlink = (os.stat(src).st_dev ==
780 780 os.stat(os.path.dirname(dst)).st_dev)
781 781
782 782 if os.path.isdir(src):
783 783 os.mkdir(dst)
784 784 for name, kind in osutil.listdir(src):
785 785 srcname = os.path.join(src, name)
786 786 dstname = os.path.join(dst, name)
787 787 copyfiles(srcname, dstname, hardlink)
788 788 else:
789 789 if hardlink:
790 790 try:
791 791 os_link(src, dst)
792 792 except (IOError, OSError):
793 793 hardlink = False
794 794 shutil.copy(src, dst)
795 795 else:
796 796 shutil.copy(src, dst)
797 797
798 798 class path_auditor(object):
799 799 '''ensure that a filesystem path contains no banned components.
800 800 the following properties of a path are checked:
801 801
802 802 - under top-level .hg
803 803 - starts at the root of a windows drive
804 804 - contains ".."
805 805 - traverses a symlink (e.g. a/symlink_here/b)
806 806 - inside a nested repository'''
807 807
808 808 def __init__(self, root):
809 809 self.audited = set()
810 810 self.auditeddir = set()
811 811 self.root = root
812 812
813 813 def __call__(self, path):
814 814 if path in self.audited:
815 815 return
816 816 normpath = os.path.normcase(path)
817 817 parts = splitpath(normpath)
818 818 if (os.path.splitdrive(path)[0]
819 819 or parts[0].lower() in ('.hg', '.hg.', '')
820 820 or os.pardir in parts):
821 821 raise Abort(_("path contains illegal component: %s") % path)
822 822 if '.hg' in path.lower():
823 823 lparts = [p.lower() for p in parts]
824 824 for p in '.hg', '.hg.':
825 if p in lparts[1:-1]:
825 if p in lparts[1:]:
826 826 pos = lparts.index(p)
827 827 base = os.path.join(*parts[:pos])
828 828 raise Abort(_('path %r is inside repo %r') % (path, base))
829 829 def check(prefix):
830 830 curpath = os.path.join(self.root, prefix)
831 831 try:
832 832 st = os.lstat(curpath)
833 833 except OSError, err:
834 834 # EINVAL can be raised as invalid path syntax under win32.
835 835 # They must be ignored for patterns can be checked too.
836 836 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
837 837 raise
838 838 else:
839 839 if stat.S_ISLNK(st.st_mode):
840 840 raise Abort(_('path %r traverses symbolic link %r') %
841 841 (path, prefix))
842 842 elif (stat.S_ISDIR(st.st_mode) and
843 843 os.path.isdir(os.path.join(curpath, '.hg'))):
844 844 raise Abort(_('path %r is inside repo %r') %
845 845 (path, prefix))
846 846 parts.pop()
847 847 prefixes = []
848 848 for n in range(len(parts)):
849 849 prefix = os.sep.join(parts)
850 850 if prefix in self.auditeddir:
851 851 break
852 852 check(prefix)
853 853 prefixes.append(prefix)
854 854 parts.pop()
855 855
856 856 self.audited.add(path)
857 857 # only add prefixes to the cache after checking everything: we don't
858 858 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
859 859 self.auditeddir.update(prefixes)
860 860
861 861 def _makelock_file(info, pathname):
862 862 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
863 863 os.write(ld, info)
864 864 os.close(ld)
865 865
866 866 def _readlock_file(pathname):
867 867 return posixfile(pathname).read()
868 868
869 869 def nlinks(pathname):
870 870 """Return number of hardlinks for the given file."""
871 871 return os.lstat(pathname).st_nlink
872 872
873 873 if hasattr(os, 'link'):
874 874 os_link = os.link
875 875 else:
876 876 def os_link(src, dst):
877 877 raise OSError(0, _("Hardlinks not supported"))
878 878
879 879 def fstat(fp):
880 880 '''stat file object that may not have fileno method.'''
881 881 try:
882 882 return os.fstat(fp.fileno())
883 883 except AttributeError:
884 884 return os.stat(fp.name)
885 885
886 886 posixfile = file
887 887
888 888 def openhardlinks():
889 889 '''return true if it is safe to hold open file handles to hardlinks'''
890 890 return True
891 891
892 892 def _statfiles(files):
893 893 'Stat each file in files and yield stat or None if file does not exist.'
894 894 lstat = os.lstat
895 895 for nf in files:
896 896 try:
897 897 st = lstat(nf)
898 898 except OSError, err:
899 899 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
900 900 raise
901 901 st = None
902 902 yield st
903 903
904 904 def _statfiles_clustered(files):
905 905 '''Stat each file in files and yield stat or None if file does not exist.
906 906 Cluster and cache stat per directory to minimize number of OS stat calls.'''
907 907 lstat = os.lstat
908 908 ncase = os.path.normcase
909 909 sep = os.sep
910 910 dircache = {} # dirname -> filename -> status | None if file does not exist
911 911 for nf in files:
912 912 nf = ncase(nf)
913 913 pos = nf.rfind(sep)
914 914 if pos == -1:
915 915 dir, base = '.', nf
916 916 else:
917 917 dir, base = nf[:pos+1], nf[pos+1:]
918 918 cache = dircache.get(dir, None)
919 919 if cache is None:
920 920 try:
921 921 dmap = dict([(ncase(n), s)
922 922 for n, k, s in osutil.listdir(dir, True)])
923 923 except OSError, err:
924 924 # handle directory not found in Python version prior to 2.5
925 925 # Python <= 2.4 returns native Windows code 3 in errno
926 926 # Python >= 2.5 returns ENOENT and adds winerror field
927 927 # EINVAL is raised if dir is not a directory.
928 928 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
929 929 errno.ENOTDIR):
930 930 raise
931 931 dmap = {}
932 932 cache = dircache.setdefault(dir, dmap)
933 933 yield cache.get(base, None)
934 934
935 935 if sys.platform == 'win32':
936 936 statfiles = _statfiles_clustered
937 937 else:
938 938 statfiles = _statfiles
939 939
940 940 getuser_fallback = None
941 941
942 942 def getuser():
943 943 '''return name of current user'''
944 944 try:
945 945 return getpass.getuser()
946 946 except ImportError:
947 947 # import of pwd will fail on windows - try fallback
948 948 if getuser_fallback:
949 949 return getuser_fallback()
950 950 # raised if win32api not available
951 951 raise Abort(_('user name not available - set USERNAME '
952 952 'environment variable'))
953 953
954 954 def username(uid=None):
955 955 """Return the name of the user with the given uid.
956 956
957 957 If uid is None, return the name of the current user."""
958 958 try:
959 959 import pwd
960 960 if uid is None:
961 961 uid = os.getuid()
962 962 try:
963 963 return pwd.getpwuid(uid)[0]
964 964 except KeyError:
965 965 return str(uid)
966 966 except ImportError:
967 967 return None
968 968
969 969 def groupname(gid=None):
970 970 """Return the name of the group with the given gid.
971 971
972 972 If gid is None, return the name of the current group."""
973 973 try:
974 974 import grp
975 975 if gid is None:
976 976 gid = os.getgid()
977 977 try:
978 978 return grp.getgrgid(gid)[0]
979 979 except KeyError:
980 980 return str(gid)
981 981 except ImportError:
982 982 return None
983 983
984 984 # File system features
985 985
986 986 def checkcase(path):
987 987 """
988 988 Check whether the given path is on a case-sensitive filesystem
989 989
990 990 Requires a path (like /foo/.hg) ending with a foldable final
991 991 directory component.
992 992 """
993 993 s1 = os.stat(path)
994 994 d, b = os.path.split(path)
995 995 p2 = os.path.join(d, b.upper())
996 996 if path == p2:
997 997 p2 = os.path.join(d, b.lower())
998 998 try:
999 999 s2 = os.stat(p2)
1000 1000 if s2 == s1:
1001 1001 return False
1002 1002 return True
1003 1003 except:
1004 1004 return True
1005 1005
1006 1006 _fspathcache = {}
1007 1007 def fspath(name, root):
1008 1008 '''Get name in the case stored in the filesystem
1009 1009
1010 1010 The name is either relative to root, or it is an absolute path starting
1011 1011 with root. Note that this function is unnecessary, and should not be
1012 1012 called, for case-sensitive filesystems (simply because it's expensive).
1013 1013 '''
1014 1014 # If name is absolute, make it relative
1015 1015 if name.lower().startswith(root.lower()):
1016 1016 l = len(root)
1017 1017 if name[l] == os.sep or name[l] == os.altsep:
1018 1018 l = l + 1
1019 1019 name = name[l:]
1020 1020
1021 1021 if not os.path.exists(os.path.join(root, name)):
1022 1022 return None
1023 1023
1024 1024 seps = os.sep
1025 1025 if os.altsep:
1026 1026 seps = seps + os.altsep
1027 1027 # Protect backslashes. This gets silly very quickly.
1028 1028 seps.replace('\\','\\\\')
1029 1029 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1030 1030 dir = os.path.normcase(os.path.normpath(root))
1031 1031 result = []
1032 1032 for part, sep in pattern.findall(name):
1033 1033 if sep:
1034 1034 result.append(sep)
1035 1035 continue
1036 1036
1037 1037 if dir not in _fspathcache:
1038 1038 _fspathcache[dir] = os.listdir(dir)
1039 1039 contents = _fspathcache[dir]
1040 1040
1041 1041 lpart = part.lower()
1042 1042 for n in contents:
1043 1043 if n.lower() == lpart:
1044 1044 result.append(n)
1045 1045 break
1046 1046 else:
1047 1047 # Cannot happen, as the file exists!
1048 1048 result.append(part)
1049 1049 dir = os.path.join(dir, lpart)
1050 1050
1051 1051 return ''.join(result)
1052 1052
1053 1053 def checkexec(path):
1054 1054 """
1055 1055 Check whether the given path is on a filesystem with UNIX-like exec flags
1056 1056
1057 1057 Requires a directory (like /foo/.hg)
1058 1058 """
1059 1059
1060 1060 # VFAT on some Linux versions can flip mode but it doesn't persist
1061 1061 # a FS remount. Frequently we can detect it if files are created
1062 1062 # with exec bit on.
1063 1063
1064 1064 try:
1065 1065 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1066 1066 fh, fn = tempfile.mkstemp("", "", path)
1067 1067 try:
1068 1068 os.close(fh)
1069 1069 m = os.stat(fn).st_mode & 0777
1070 1070 new_file_has_exec = m & EXECFLAGS
1071 1071 os.chmod(fn, m ^ EXECFLAGS)
1072 1072 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1073 1073 finally:
1074 1074 os.unlink(fn)
1075 1075 except (IOError, OSError):
1076 1076 # we don't care, the user probably won't be able to commit anyway
1077 1077 return False
1078 1078 return not (new_file_has_exec or exec_flags_cannot_flip)
1079 1079
1080 1080 def checklink(path):
1081 1081 """check whether the given path is on a symlink-capable filesystem"""
1082 1082 # mktemp is not racy because symlink creation will fail if the
1083 1083 # file already exists
1084 1084 name = tempfile.mktemp(dir=path)
1085 1085 try:
1086 1086 os.symlink(".", name)
1087 1087 os.unlink(name)
1088 1088 return True
1089 1089 except (OSError, AttributeError):
1090 1090 return False
1091 1091
1092 1092 _umask = os.umask(0)
1093 1093 os.umask(_umask)
1094 1094
1095 1095 def needbinarypatch():
1096 1096 """return True if patches should be applied in binary mode by default."""
1097 1097 return os.name == 'nt'
1098 1098
1099 1099 def endswithsep(path):
1100 1100 '''Check path ends with os.sep or os.altsep.'''
1101 1101 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1102 1102
1103 1103 def splitpath(path):
1104 1104 '''Split path by os.sep.
1105 1105 Note that this function does not use os.altsep because this is
1106 1106 an alternative of simple "xxx.split(os.sep)".
1107 1107 It is recommended to use os.path.normpath() before using this
1108 1108 function if need.'''
1109 1109 return path.split(os.sep)
1110 1110
1111 1111 def gui():
1112 1112 '''Are we running in a GUI?'''
1113 1113 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1114 1114
1115 1115 def lookup_reg(key, name=None, scope=None):
1116 1116 return None
1117 1117
1118 1118 # Platform specific variants
1119 1119 if os.name == 'nt':
1120 1120 import msvcrt
1121 1121 nulldev = 'NUL:'
1122 1122
1123 1123 class winstdout:
1124 1124 '''stdout on windows misbehaves if sent through a pipe'''
1125 1125
1126 1126 def __init__(self, fp):
1127 1127 self.fp = fp
1128 1128
1129 1129 def __getattr__(self, key):
1130 1130 return getattr(self.fp, key)
1131 1131
1132 1132 def close(self):
1133 1133 try:
1134 1134 self.fp.close()
1135 1135 except: pass
1136 1136
1137 1137 def write(self, s):
1138 1138 try:
1139 1139 # This is workaround for "Not enough space" error on
1140 1140 # writing large size of data to console.
1141 1141 limit = 16000
1142 1142 l = len(s)
1143 1143 start = 0
1144 1144 while start < l:
1145 1145 end = start + limit
1146 1146 self.fp.write(s[start:end])
1147 1147 start = end
1148 1148 except IOError, inst:
1149 1149 if inst.errno != 0: raise
1150 1150 self.close()
1151 1151 raise IOError(errno.EPIPE, 'Broken pipe')
1152 1152
1153 1153 def flush(self):
1154 1154 try:
1155 1155 return self.fp.flush()
1156 1156 except IOError, inst:
1157 1157 if inst.errno != errno.EINVAL: raise
1158 1158 self.close()
1159 1159 raise IOError(errno.EPIPE, 'Broken pipe')
1160 1160
1161 1161 sys.stdout = winstdout(sys.stdout)
1162 1162
1163 1163 def _is_win_9x():
1164 1164 '''return true if run on windows 95, 98 or me.'''
1165 1165 try:
1166 1166 return sys.getwindowsversion()[3] == 1
1167 1167 except AttributeError:
1168 1168 return 'command' in os.environ.get('comspec', '')
1169 1169
1170 1170 def openhardlinks():
1171 1171 return not _is_win_9x and "win32api" in locals()
1172 1172
1173 1173 def system_rcpath():
1174 1174 try:
1175 1175 return system_rcpath_win32()
1176 1176 except:
1177 1177 return [r'c:\mercurial\mercurial.ini']
1178 1178
1179 1179 def user_rcpath():
1180 1180 '''return os-specific hgrc search path to the user dir'''
1181 1181 try:
1182 1182 path = user_rcpath_win32()
1183 1183 except:
1184 1184 home = os.path.expanduser('~')
1185 1185 path = [os.path.join(home, 'mercurial.ini'),
1186 1186 os.path.join(home, '.hgrc')]
1187 1187 userprofile = os.environ.get('USERPROFILE')
1188 1188 if userprofile:
1189 1189 path.append(os.path.join(userprofile, 'mercurial.ini'))
1190 1190 path.append(os.path.join(userprofile, '.hgrc'))
1191 1191 return path
1192 1192
1193 1193 def parse_patch_output(output_line):
1194 1194 """parses the output produced by patch and returns the file name"""
1195 1195 pf = output_line[14:]
1196 1196 if pf[0] == '`':
1197 1197 pf = pf[1:-1] # Remove the quotes
1198 1198 return pf
1199 1199
1200 1200 def sshargs(sshcmd, host, user, port):
1201 1201 '''Build argument list for ssh or Plink'''
1202 1202 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1203 1203 args = user and ("%s@%s" % (user, host)) or host
1204 1204 return port and ("%s %s %s" % (args, pflag, port)) or args
1205 1205
1206 1206 def testpid(pid):
1207 1207 '''return False if pid dead, True if running or not known'''
1208 1208 return True
1209 1209
1210 1210 def set_flags(f, l, x):
1211 1211 pass
1212 1212
1213 1213 def set_binary(fd):
1214 1214 # When run without console, pipes may expose invalid
1215 1215 # fileno(), usually set to -1.
1216 1216 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1217 1217 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1218 1218
1219 1219 def pconvert(path):
1220 1220 return '/'.join(splitpath(path))
1221 1221
1222 1222 def localpath(path):
1223 1223 return path.replace('/', '\\')
1224 1224
1225 1225 def normpath(path):
1226 1226 return pconvert(os.path.normpath(path))
1227 1227
1228 1228 makelock = _makelock_file
1229 1229 readlock = _readlock_file
1230 1230
1231 1231 def samestat(s1, s2):
1232 1232 return False
1233 1233
1234 1234 # A sequence of backslashes is special iff it precedes a double quote:
1235 1235 # - if there's an even number of backslashes, the double quote is not
1236 1236 # quoted (i.e. it ends the quoted region)
1237 1237 # - if there's an odd number of backslashes, the double quote is quoted
1238 1238 # - in both cases, every pair of backslashes is unquoted into a single
1239 1239 # backslash
1240 1240 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1241 1241 # So, to quote a string, we must surround it in double quotes, double
1242 1242 # the number of backslashes that preceed double quotes and add another
1243 1243 # backslash before every double quote (being careful with the double
1244 1244 # quote we've appended to the end)
1245 1245 _quotere = None
1246 1246 def shellquote(s):
1247 1247 global _quotere
1248 1248 if _quotere is None:
1249 1249 _quotere = re.compile(r'(\\*)("|\\$)')
1250 1250 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1251 1251
1252 1252 def quotecommand(cmd):
1253 1253 """Build a command string suitable for os.popen* calls."""
1254 1254 # The extra quotes are needed because popen* runs the command
1255 1255 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1256 1256 return '"' + cmd + '"'
1257 1257
1258 1258 def popen(command, mode='r'):
1259 1259 # Work around "popen spawned process may not write to stdout
1260 1260 # under windows"
1261 1261 # http://bugs.python.org/issue1366
1262 1262 command += " 2> %s" % nulldev
1263 1263 return os.popen(quotecommand(command), mode)
1264 1264
1265 1265 def explain_exit(code):
1266 1266 return _("exited with status %d") % code, code
1267 1267
1268 1268 # if you change this stub into a real check, please try to implement the
1269 1269 # username and groupname functions above, too.
1270 1270 def isowner(fp, st=None):
1271 1271 return True
1272 1272
1273 1273 def find_exe(command):
1274 1274 '''Find executable for command searching like cmd.exe does.
1275 1275 If command is a basename then PATH is searched for command.
1276 1276 PATH isn't searched if command is an absolute or relative path.
1277 1277 An extension from PATHEXT is found and added if not present.
1278 1278 If command isn't found None is returned.'''
1279 1279 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1280 1280 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
1281 1281 if os.path.splitext(command)[1].lower() in pathexts:
1282 1282 pathexts = ['']
1283 1283
1284 1284 def findexisting(pathcommand):
1285 1285 'Will append extension (if needed) and return existing file'
1286 1286 for ext in pathexts:
1287 1287 executable = pathcommand + ext
1288 1288 if os.path.exists(executable):
1289 1289 return executable
1290 1290 return None
1291 1291
1292 1292 if os.sep in command:
1293 1293 return findexisting(command)
1294 1294
1295 1295 for path in os.environ.get('PATH', '').split(os.pathsep):
1296 1296 executable = findexisting(os.path.join(path, command))
1297 1297 if executable is not None:
1298 1298 return executable
1299 1299 return None
1300 1300
1301 1301 def set_signal_handler():
1302 1302 try:
1303 1303 set_signal_handler_win32()
1304 1304 except NameError:
1305 1305 pass
1306 1306
1307 1307 try:
1308 1308 # override functions with win32 versions if possible
1309 1309 from util_win32 import *
1310 1310 if not _is_win_9x():
1311 1311 posixfile = posixfile_nt
1312 1312 except ImportError:
1313 1313 pass
1314 1314
1315 1315 else:
1316 1316 nulldev = '/dev/null'
1317 1317
1318 1318 def rcfiles(path):
1319 1319 rcs = [os.path.join(path, 'hgrc')]
1320 1320 rcdir = os.path.join(path, 'hgrc.d')
1321 1321 try:
1322 1322 rcs.extend([os.path.join(rcdir, f)
1323 1323 for f, kind in osutil.listdir(rcdir)
1324 1324 if f.endswith(".rc")])
1325 1325 except OSError:
1326 1326 pass
1327 1327 return rcs
1328 1328
1329 1329 def system_rcpath():
1330 1330 path = []
1331 1331 # old mod_python does not set sys.argv
1332 1332 if len(getattr(sys, 'argv', [])) > 0:
1333 1333 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1334 1334 '/../etc/mercurial'))
1335 1335 path.extend(rcfiles('/etc/mercurial'))
1336 1336 return path
1337 1337
1338 1338 def user_rcpath():
1339 1339 return [os.path.expanduser('~/.hgrc')]
1340 1340
1341 1341 def parse_patch_output(output_line):
1342 1342 """parses the output produced by patch and returns the file name"""
1343 1343 pf = output_line[14:]
1344 1344 if os.sys.platform == 'OpenVMS':
1345 1345 if pf[0] == '`':
1346 1346 pf = pf[1:-1] # Remove the quotes
1347 1347 else:
1348 1348 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1349 1349 pf = pf[1:-1] # Remove the quotes
1350 1350 return pf
1351 1351
1352 1352 def sshargs(sshcmd, host, user, port):
1353 1353 '''Build argument list for ssh'''
1354 1354 args = user and ("%s@%s" % (user, host)) or host
1355 1355 return port and ("%s -p %s" % (args, port)) or args
1356 1356
1357 1357 def is_exec(f):
1358 1358 """check whether a file is executable"""
1359 1359 return (os.lstat(f).st_mode & 0100 != 0)
1360 1360
1361 1361 def set_flags(f, l, x):
1362 1362 s = os.lstat(f).st_mode
1363 1363 if l:
1364 1364 if not stat.S_ISLNK(s):
1365 1365 # switch file to link
1366 1366 data = file(f).read()
1367 1367 os.unlink(f)
1368 1368 try:
1369 1369 os.symlink(data, f)
1370 1370 except:
1371 1371 # failed to make a link, rewrite file
1372 1372 file(f, "w").write(data)
1373 1373 # no chmod needed at this point
1374 1374 return
1375 1375 if stat.S_ISLNK(s):
1376 1376 # switch link to file
1377 1377 data = os.readlink(f)
1378 1378 os.unlink(f)
1379 1379 file(f, "w").write(data)
1380 1380 s = 0666 & ~_umask # avoid restatting for chmod
1381 1381
1382 1382 sx = s & 0100
1383 1383 if x and not sx:
1384 1384 # Turn on +x for every +r bit when making a file executable
1385 1385 # and obey umask.
1386 1386 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1387 1387 elif not x and sx:
1388 1388 # Turn off all +x bits
1389 1389 os.chmod(f, s & 0666)
1390 1390
1391 1391 def set_binary(fd):
1392 1392 pass
1393 1393
1394 1394 def pconvert(path):
1395 1395 return path
1396 1396
1397 1397 def localpath(path):
1398 1398 return path
1399 1399
1400 1400 normpath = os.path.normpath
1401 1401 samestat = os.path.samestat
1402 1402
1403 1403 def makelock(info, pathname):
1404 1404 try:
1405 1405 os.symlink(info, pathname)
1406 1406 except OSError, why:
1407 1407 if why.errno == errno.EEXIST:
1408 1408 raise
1409 1409 else:
1410 1410 _makelock_file(info, pathname)
1411 1411
1412 1412 def readlock(pathname):
1413 1413 try:
1414 1414 return os.readlink(pathname)
1415 1415 except OSError, why:
1416 1416 if why.errno in (errno.EINVAL, errno.ENOSYS):
1417 1417 return _readlock_file(pathname)
1418 1418 else:
1419 1419 raise
1420 1420
1421 1421 def shellquote(s):
1422 1422 if os.sys.platform == 'OpenVMS':
1423 1423 return '"%s"' % s
1424 1424 else:
1425 1425 return "'%s'" % s.replace("'", "'\\''")
1426 1426
1427 1427 def quotecommand(cmd):
1428 1428 return cmd
1429 1429
1430 1430 def popen(command, mode='r'):
1431 1431 return os.popen(command, mode)
1432 1432
1433 1433 def testpid(pid):
1434 1434 '''return False if pid dead, True if running or not sure'''
1435 1435 if os.sys.platform == 'OpenVMS':
1436 1436 return True
1437 1437 try:
1438 1438 os.kill(pid, 0)
1439 1439 return True
1440 1440 except OSError, inst:
1441 1441 return inst.errno != errno.ESRCH
1442 1442
1443 1443 def explain_exit(code):
1444 1444 """return a 2-tuple (desc, code) describing a process's status"""
1445 1445 if os.WIFEXITED(code):
1446 1446 val = os.WEXITSTATUS(code)
1447 1447 return _("exited with status %d") % val, val
1448 1448 elif os.WIFSIGNALED(code):
1449 1449 val = os.WTERMSIG(code)
1450 1450 return _("killed by signal %d") % val, val
1451 1451 elif os.WIFSTOPPED(code):
1452 1452 val = os.WSTOPSIG(code)
1453 1453 return _("stopped by signal %d") % val, val
1454 1454 raise ValueError(_("invalid exit code"))
1455 1455
1456 1456 def isowner(fp, st=None):
1457 1457 """Return True if the file object f belongs to the current user.
1458 1458
1459 1459 The return value of a util.fstat(f) may be passed as the st argument.
1460 1460 """
1461 1461 if st is None:
1462 1462 st = fstat(fp)
1463 1463 return st.st_uid == os.getuid()
1464 1464
1465 1465 def find_exe(command):
1466 1466 '''Find executable for command searching like which does.
1467 1467 If command is a basename then PATH is searched for command.
1468 1468 PATH isn't searched if command is an absolute or relative path.
1469 1469 If command isn't found None is returned.'''
1470 1470 if sys.platform == 'OpenVMS':
1471 1471 return command
1472 1472
1473 1473 def findexisting(executable):
1474 1474 'Will return executable if existing file'
1475 1475 if os.path.exists(executable):
1476 1476 return executable
1477 1477 return None
1478 1478
1479 1479 if os.sep in command:
1480 1480 return findexisting(command)
1481 1481
1482 1482 for path in os.environ.get('PATH', '').split(os.pathsep):
1483 1483 executable = findexisting(os.path.join(path, command))
1484 1484 if executable is not None:
1485 1485 return executable
1486 1486 return None
1487 1487
1488 1488 def set_signal_handler():
1489 1489 pass
1490 1490
1491 1491 def mktempcopy(name, emptyok=False, createmode=None):
1492 1492 """Create a temporary file with the same contents from name
1493 1493
1494 1494 The permission bits are copied from the original file.
1495 1495
1496 1496 If the temporary file is going to be truncated immediately, you
1497 1497 can use emptyok=True as an optimization.
1498 1498
1499 1499 Returns the name of the temporary file.
1500 1500 """
1501 1501 d, fn = os.path.split(name)
1502 1502 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1503 1503 os.close(fd)
1504 1504 # Temporary files are created with mode 0600, which is usually not
1505 1505 # what we want. If the original file already exists, just copy
1506 1506 # its mode. Otherwise, manually obey umask.
1507 1507 try:
1508 1508 st_mode = os.lstat(name).st_mode & 0777
1509 1509 except OSError, inst:
1510 1510 if inst.errno != errno.ENOENT:
1511 1511 raise
1512 1512 st_mode = createmode
1513 1513 if st_mode is None:
1514 1514 st_mode = ~_umask
1515 1515 st_mode &= 0666
1516 1516 os.chmod(temp, st_mode)
1517 1517 if emptyok:
1518 1518 return temp
1519 1519 try:
1520 1520 try:
1521 1521 ifp = posixfile(name, "rb")
1522 1522 except IOError, inst:
1523 1523 if inst.errno == errno.ENOENT:
1524 1524 return temp
1525 1525 if not getattr(inst, 'filename', None):
1526 1526 inst.filename = name
1527 1527 raise
1528 1528 ofp = posixfile(temp, "wb")
1529 1529 for chunk in filechunkiter(ifp):
1530 1530 ofp.write(chunk)
1531 1531 ifp.close()
1532 1532 ofp.close()
1533 1533 except:
1534 1534 try: os.unlink(temp)
1535 1535 except: pass
1536 1536 raise
1537 1537 return temp
1538 1538
1539 1539 class atomictempfile(posixfile):
1540 1540 """file-like object that atomically updates a file
1541 1541
1542 1542 All writes will be redirected to a temporary copy of the original
1543 1543 file. When rename is called, the copy is renamed to the original
1544 1544 name, making the changes visible.
1545 1545 """
1546 1546 def __init__(self, name, mode, createmode):
1547 1547 self.__name = name
1548 1548 self.temp = mktempcopy(name, emptyok=('w' in mode),
1549 1549 createmode=createmode)
1550 1550 posixfile.__init__(self, self.temp, mode)
1551 1551
1552 1552 def rename(self):
1553 1553 if not self.closed:
1554 1554 posixfile.close(self)
1555 1555 rename(self.temp, localpath(self.__name))
1556 1556
1557 1557 def __del__(self):
1558 1558 if not self.closed:
1559 1559 try:
1560 1560 os.unlink(self.temp)
1561 1561 except: pass
1562 1562 posixfile.close(self)
1563 1563
1564 1564 def makedirs(name, mode=None):
1565 1565 """recursive directory creation with parent mode inheritance"""
1566 1566 try:
1567 1567 os.mkdir(name)
1568 1568 if mode is not None:
1569 1569 os.chmod(name, mode)
1570 1570 return
1571 1571 except OSError, err:
1572 1572 if err.errno == errno.EEXIST:
1573 1573 return
1574 1574 if err.errno != errno.ENOENT:
1575 1575 raise
1576 1576 parent = os.path.abspath(os.path.dirname(name))
1577 1577 makedirs(parent, mode)
1578 1578 makedirs(name, mode)
1579 1579
1580 1580 class opener(object):
1581 1581 """Open files relative to a base directory
1582 1582
1583 1583 This class is used to hide the details of COW semantics and
1584 1584 remote file access from higher level code.
1585 1585 """
1586 1586 def __init__(self, base, audit=True):
1587 1587 self.base = base
1588 1588 if audit:
1589 1589 self.audit_path = path_auditor(base)
1590 1590 else:
1591 1591 self.audit_path = always
1592 1592 self.createmode = None
1593 1593
1594 1594 def __getattr__(self, name):
1595 1595 if name == '_can_symlink':
1596 1596 self._can_symlink = checklink(self.base)
1597 1597 return self._can_symlink
1598 1598 raise AttributeError(name)
1599 1599
1600 1600 def _fixfilemode(self, name):
1601 1601 if self.createmode is None:
1602 1602 return
1603 1603 os.chmod(name, self.createmode & 0666)
1604 1604
1605 1605 def __call__(self, path, mode="r", text=False, atomictemp=False):
1606 1606 self.audit_path(path)
1607 1607 f = os.path.join(self.base, path)
1608 1608
1609 1609 if not text and "b" not in mode:
1610 1610 mode += "b" # for that other OS
1611 1611
1612 1612 nlink = -1
1613 1613 if mode not in ("r", "rb"):
1614 1614 try:
1615 1615 nlink = nlinks(f)
1616 1616 except OSError:
1617 1617 nlink = 0
1618 1618 d = os.path.dirname(f)
1619 1619 if not os.path.isdir(d):
1620 1620 makedirs(d, self.createmode)
1621 1621 if atomictemp:
1622 1622 return atomictempfile(f, mode, self.createmode)
1623 1623 if nlink > 1:
1624 1624 rename(mktempcopy(f), f)
1625 1625 fp = posixfile(f, mode)
1626 1626 if nlink == 0:
1627 1627 self._fixfilemode(f)
1628 1628 return fp
1629 1629
1630 1630 def symlink(self, src, dst):
1631 1631 self.audit_path(dst)
1632 1632 linkname = os.path.join(self.base, dst)
1633 1633 try:
1634 1634 os.unlink(linkname)
1635 1635 except OSError:
1636 1636 pass
1637 1637
1638 1638 dirname = os.path.dirname(linkname)
1639 1639 if not os.path.exists(dirname):
1640 1640 makedirs(dirname, self.createmode)
1641 1641
1642 1642 if self._can_symlink:
1643 1643 try:
1644 1644 os.symlink(src, linkname)
1645 1645 except OSError, err:
1646 1646 raise OSError(err.errno, _('could not symlink to %r: %s') %
1647 1647 (src, err.strerror), linkname)
1648 1648 else:
1649 1649 f = self(dst, "w")
1650 1650 f.write(src)
1651 1651 f.close()
1652 1652 self._fixfilemode(dst)
1653 1653
1654 1654 class chunkbuffer(object):
1655 1655 """Allow arbitrary sized chunks of data to be efficiently read from an
1656 1656 iterator over chunks of arbitrary size."""
1657 1657
1658 1658 def __init__(self, in_iter):
1659 1659 """in_iter is the iterator that's iterating over the input chunks.
1660 1660 targetsize is how big a buffer to try to maintain."""
1661 1661 self.iter = iter(in_iter)
1662 1662 self.buf = ''
1663 1663 self.targetsize = 2**16
1664 1664
1665 1665 def read(self, l):
1666 1666 """Read L bytes of data from the iterator of chunks of data.
1667 1667 Returns less than L bytes if the iterator runs dry."""
1668 1668 if l > len(self.buf) and self.iter:
1669 1669 # Clamp to a multiple of self.targetsize
1670 1670 targetsize = max(l, self.targetsize)
1671 1671 collector = cStringIO.StringIO()
1672 1672 collector.write(self.buf)
1673 1673 collected = len(self.buf)
1674 1674 for chunk in self.iter:
1675 1675 collector.write(chunk)
1676 1676 collected += len(chunk)
1677 1677 if collected >= targetsize:
1678 1678 break
1679 1679 if collected < targetsize:
1680 1680 self.iter = False
1681 1681 self.buf = collector.getvalue()
1682 1682 if len(self.buf) == l:
1683 1683 s, self.buf = str(self.buf), ''
1684 1684 else:
1685 1685 s, self.buf = self.buf[:l], buffer(self.buf, l)
1686 1686 return s
1687 1687
1688 1688 def filechunkiter(f, size=65536, limit=None):
1689 1689 """Create a generator that produces the data in the file size
1690 1690 (default 65536) bytes at a time, up to optional limit (default is
1691 1691 to read all data). Chunks may be less than size bytes if the
1692 1692 chunk is the last chunk in the file, or the file is a socket or
1693 1693 some other type of file that sometimes reads less data than is
1694 1694 requested."""
1695 1695 assert size >= 0
1696 1696 assert limit is None or limit >= 0
1697 1697 while True:
1698 1698 if limit is None: nbytes = size
1699 1699 else: nbytes = min(limit, size)
1700 1700 s = nbytes and f.read(nbytes)
1701 1701 if not s: break
1702 1702 if limit: limit -= len(s)
1703 1703 yield s
1704 1704
1705 1705 def makedate():
1706 1706 lt = time.localtime()
1707 1707 if lt[8] == 1 and time.daylight:
1708 1708 tz = time.altzone
1709 1709 else:
1710 1710 tz = time.timezone
1711 1711 return time.mktime(lt), tz
1712 1712
1713 1713 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1714 1714 """represent a (unixtime, offset) tuple as a localized time.
1715 1715 unixtime is seconds since the epoch, and offset is the time zone's
1716 1716 number of seconds away from UTC. if timezone is false, do not
1717 1717 append time zone to string."""
1718 1718 t, tz = date or makedate()
1719 1719 if "%1" in format or "%2" in format:
1720 1720 sign = (tz > 0) and "-" or "+"
1721 1721 minutes = abs(tz) / 60
1722 1722 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1723 1723 format = format.replace("%2", "%02d" % (minutes % 60))
1724 1724 s = time.strftime(format, time.gmtime(float(t) - tz))
1725 1725 return s
1726 1726
1727 1727 def shortdate(date=None):
1728 1728 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1729 1729 return datestr(date, format='%Y-%m-%d')
1730 1730
1731 1731 def strdate(string, format, defaults=[]):
1732 1732 """parse a localized time string and return a (unixtime, offset) tuple.
1733 1733 if the string cannot be parsed, ValueError is raised."""
1734 1734 def timezone(string):
1735 1735 tz = string.split()[-1]
1736 1736 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1737 1737 sign = (tz[0] == "+") and 1 or -1
1738 1738 hours = int(tz[1:3])
1739 1739 minutes = int(tz[3:5])
1740 1740 return -sign * (hours * 60 + minutes) * 60
1741 1741 if tz == "GMT" or tz == "UTC":
1742 1742 return 0
1743 1743 return None
1744 1744
1745 1745 # NOTE: unixtime = localunixtime + offset
1746 1746 offset, date = timezone(string), string
1747 1747 if offset != None:
1748 1748 date = " ".join(string.split()[:-1])
1749 1749
1750 1750 # add missing elements from defaults
1751 1751 for part in defaults:
1752 1752 found = [True for p in part if ("%"+p) in format]
1753 1753 if not found:
1754 1754 date += "@" + defaults[part]
1755 1755 format += "@%" + part[0]
1756 1756
1757 1757 timetuple = time.strptime(date, format)
1758 1758 localunixtime = int(calendar.timegm(timetuple))
1759 1759 if offset is None:
1760 1760 # local timezone
1761 1761 unixtime = int(time.mktime(timetuple))
1762 1762 offset = unixtime - localunixtime
1763 1763 else:
1764 1764 unixtime = localunixtime + offset
1765 1765 return unixtime, offset
1766 1766
1767 1767 def parsedate(date, formats=None, defaults=None):
1768 1768 """parse a localized date/time string and return a (unixtime, offset) tuple.
1769 1769
1770 1770 The date may be a "unixtime offset" string or in one of the specified
1771 1771 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1772 1772 """
1773 1773 if not date:
1774 1774 return 0, 0
1775 1775 if isinstance(date, tuple) and len(date) == 2:
1776 1776 return date
1777 1777 if not formats:
1778 1778 formats = defaultdateformats
1779 1779 date = date.strip()
1780 1780 try:
1781 1781 when, offset = map(int, date.split(' '))
1782 1782 except ValueError:
1783 1783 # fill out defaults
1784 1784 if not defaults:
1785 1785 defaults = {}
1786 1786 now = makedate()
1787 1787 for part in "d mb yY HI M S".split():
1788 1788 if part not in defaults:
1789 1789 if part[0] in "HMS":
1790 1790 defaults[part] = "00"
1791 1791 else:
1792 1792 defaults[part] = datestr(now, "%" + part[0])
1793 1793
1794 1794 for format in formats:
1795 1795 try:
1796 1796 when, offset = strdate(date, format, defaults)
1797 1797 except (ValueError, OverflowError):
1798 1798 pass
1799 1799 else:
1800 1800 break
1801 1801 else:
1802 1802 raise Abort(_('invalid date: %r ') % date)
1803 1803 # validate explicit (probably user-specified) date and
1804 1804 # time zone offset. values must fit in signed 32 bits for
1805 1805 # current 32-bit linux runtimes. timezones go from UTC-12
1806 1806 # to UTC+14
1807 1807 if abs(when) > 0x7fffffff:
1808 1808 raise Abort(_('date exceeds 32 bits: %d') % when)
1809 1809 if offset < -50400 or offset > 43200:
1810 1810 raise Abort(_('impossible time zone offset: %d') % offset)
1811 1811 return when, offset
1812 1812
1813 1813 def matchdate(date):
1814 1814 """Return a function that matches a given date match specifier
1815 1815
1816 1816 Formats include:
1817 1817
1818 1818 '{date}' match a given date to the accuracy provided
1819 1819
1820 1820 '<{date}' on or before a given date
1821 1821
1822 1822 '>{date}' on or after a given date
1823 1823
1824 1824 """
1825 1825
1826 1826 def lower(date):
1827 1827 d = dict(mb="1", d="1")
1828 1828 return parsedate(date, extendeddateformats, d)[0]
1829 1829
1830 1830 def upper(date):
1831 1831 d = dict(mb="12", HI="23", M="59", S="59")
1832 1832 for days in "31 30 29".split():
1833 1833 try:
1834 1834 d["d"] = days
1835 1835 return parsedate(date, extendeddateformats, d)[0]
1836 1836 except:
1837 1837 pass
1838 1838 d["d"] = "28"
1839 1839 return parsedate(date, extendeddateformats, d)[0]
1840 1840
1841 1841 if date[0] == "<":
1842 1842 when = upper(date[1:])
1843 1843 return lambda x: x <= when
1844 1844 elif date[0] == ">":
1845 1845 when = lower(date[1:])
1846 1846 return lambda x: x >= when
1847 1847 elif date[0] == "-":
1848 1848 try:
1849 1849 days = int(date[1:])
1850 1850 except ValueError:
1851 1851 raise Abort(_("invalid day spec: %s") % date[1:])
1852 1852 when = makedate()[0] - days * 3600 * 24
1853 1853 return lambda x: x >= when
1854 1854 elif " to " in date:
1855 1855 a, b = date.split(" to ")
1856 1856 start, stop = lower(a), upper(b)
1857 1857 return lambda x: x >= start and x <= stop
1858 1858 else:
1859 1859 start, stop = lower(date), upper(date)
1860 1860 return lambda x: x >= start and x <= stop
1861 1861
1862 1862 def shortuser(user):
1863 1863 """Return a short representation of a user name or email address."""
1864 1864 f = user.find('@')
1865 1865 if f >= 0:
1866 1866 user = user[:f]
1867 1867 f = user.find('<')
1868 1868 if f >= 0:
1869 1869 user = user[f+1:]
1870 1870 f = user.find(' ')
1871 1871 if f >= 0:
1872 1872 user = user[:f]
1873 1873 f = user.find('.')
1874 1874 if f >= 0:
1875 1875 user = user[:f]
1876 1876 return user
1877 1877
1878 1878 def email(author):
1879 1879 '''get email of author.'''
1880 1880 r = author.find('>')
1881 1881 if r == -1: r = None
1882 1882 return author[author.find('<')+1:r]
1883 1883
1884 1884 def ellipsis(text, maxlength=400):
1885 1885 """Trim string to at most maxlength (default: 400) characters."""
1886 1886 if len(text) <= maxlength:
1887 1887 return text
1888 1888 else:
1889 1889 return "%s..." % (text[:maxlength-3])
1890 1890
1891 1891 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1892 1892 '''yield every hg repository under path, recursively.'''
1893 1893 def errhandler(err):
1894 1894 if err.filename == path:
1895 1895 raise err
1896 1896 if followsym and hasattr(os.path, 'samestat'):
1897 1897 def _add_dir_if_not_there(dirlst, dirname):
1898 1898 match = False
1899 1899 samestat = os.path.samestat
1900 1900 dirstat = os.stat(dirname)
1901 1901 for lstdirstat in dirlst:
1902 1902 if samestat(dirstat, lstdirstat):
1903 1903 match = True
1904 1904 break
1905 1905 if not match:
1906 1906 dirlst.append(dirstat)
1907 1907 return not match
1908 1908 else:
1909 1909 followsym = False
1910 1910
1911 1911 if (seen_dirs is None) and followsym:
1912 1912 seen_dirs = []
1913 1913 _add_dir_if_not_there(seen_dirs, path)
1914 1914 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1915 1915 if '.hg' in dirs:
1916 1916 yield root # found a repository
1917 1917 qroot = os.path.join(root, '.hg', 'patches')
1918 1918 if os.path.isdir(os.path.join(qroot, '.hg')):
1919 1919 yield qroot # we have a patch queue repo here
1920 1920 if recurse:
1921 1921 # avoid recursing inside the .hg directory
1922 1922 dirs.remove('.hg')
1923 1923 else:
1924 1924 dirs[:] = [] # don't descend further
1925 1925 elif followsym:
1926 1926 newdirs = []
1927 1927 for d in dirs:
1928 1928 fname = os.path.join(root, d)
1929 1929 if _add_dir_if_not_there(seen_dirs, fname):
1930 1930 if os.path.islink(fname):
1931 1931 for hgname in walkrepos(fname, True, seen_dirs):
1932 1932 yield hgname
1933 1933 else:
1934 1934 newdirs.append(d)
1935 1935 dirs[:] = newdirs
1936 1936
1937 1937 _rcpath = None
1938 1938
1939 1939 def os_rcpath():
1940 1940 '''return default os-specific hgrc search path'''
1941 1941 path = system_rcpath()
1942 1942 path.extend(user_rcpath())
1943 1943 path = [os.path.normpath(f) for f in path]
1944 1944 return path
1945 1945
1946 1946 def rcpath():
1947 1947 '''return hgrc search path. if env var HGRCPATH is set, use it.
1948 1948 for each item in path, if directory, use files ending in .rc,
1949 1949 else use item.
1950 1950 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1951 1951 if no HGRCPATH, use default os-specific path.'''
1952 1952 global _rcpath
1953 1953 if _rcpath is None:
1954 1954 if 'HGRCPATH' in os.environ:
1955 1955 _rcpath = []
1956 1956 for p in os.environ['HGRCPATH'].split(os.pathsep):
1957 1957 if not p: continue
1958 1958 if os.path.isdir(p):
1959 1959 for f, kind in osutil.listdir(p):
1960 1960 if f.endswith('.rc'):
1961 1961 _rcpath.append(os.path.join(p, f))
1962 1962 else:
1963 1963 _rcpath.append(p)
1964 1964 else:
1965 1965 _rcpath = os_rcpath()
1966 1966 return _rcpath
1967 1967
1968 1968 def bytecount(nbytes):
1969 1969 '''return byte count formatted as readable string, with units'''
1970 1970
1971 1971 units = (
1972 1972 (100, 1<<30, _('%.0f GB')),
1973 1973 (10, 1<<30, _('%.1f GB')),
1974 1974 (1, 1<<30, _('%.2f GB')),
1975 1975 (100, 1<<20, _('%.0f MB')),
1976 1976 (10, 1<<20, _('%.1f MB')),
1977 1977 (1, 1<<20, _('%.2f MB')),
1978 1978 (100, 1<<10, _('%.0f KB')),
1979 1979 (10, 1<<10, _('%.1f KB')),
1980 1980 (1, 1<<10, _('%.2f KB')),
1981 1981 (1, 1, _('%.0f bytes')),
1982 1982 )
1983 1983
1984 1984 for multiplier, divisor, format in units:
1985 1985 if nbytes >= divisor * multiplier:
1986 1986 return format % (nbytes / float(divisor))
1987 1987 return units[-1][2] % nbytes
1988 1988
1989 1989 def drop_scheme(scheme, path):
1990 1990 sc = scheme + ':'
1991 1991 if path.startswith(sc):
1992 1992 path = path[len(sc):]
1993 1993 if path.startswith('//'):
1994 1994 path = path[2:]
1995 1995 return path
1996 1996
1997 1997 def uirepr(s):
1998 1998 # Avoid double backslash in Windows path repr()
1999 1999 return repr(s).replace('\\\\', '\\')
2000 2000
2001 2001 def termwidth():
2002 2002 if 'COLUMNS' in os.environ:
2003 2003 try:
2004 2004 return int(os.environ['COLUMNS'])
2005 2005 except ValueError:
2006 2006 pass
2007 2007 try:
2008 2008 import termios, array, fcntl
2009 2009 for dev in (sys.stdout, sys.stdin):
2010 2010 try:
2011 2011 fd = dev.fileno()
2012 2012 if not os.isatty(fd):
2013 2013 continue
2014 2014 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
2015 2015 return array.array('h', arri)[1]
2016 2016 except ValueError:
2017 2017 pass
2018 2018 except ImportError:
2019 2019 pass
2020 2020 return 80
@@ -1,303 +1,303 b''
1 1 adding beans/black
2 2 adding beans/borlotti
3 3 adding beans/kidney
4 4 adding beans/navy
5 5 adding beans/pinto
6 6 adding beans/turtle
7 7 adding fennel
8 8 adding fenugreek
9 9 adding fiddlehead
10 10 adding glob:glob
11 11 adding mammals/Procyonidae/cacomistle
12 12 adding mammals/Procyonidae/coatimundi
13 13 adding mammals/Procyonidae/raccoon
14 14 adding mammals/skunk
15 15 hg debugwalk
16 16 f beans/black beans/black
17 17 f beans/borlotti beans/borlotti
18 18 f beans/kidney beans/kidney
19 19 f beans/navy beans/navy
20 20 f beans/pinto beans/pinto
21 21 f beans/turtle beans/turtle
22 22 f fennel fennel
23 23 f fenugreek fenugreek
24 24 f fiddlehead fiddlehead
25 25 f glob:glob glob:glob
26 26 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
27 27 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
28 28 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
29 29 f mammals/skunk mammals/skunk
30 30
31 31 hg debugwalk -I.
32 32 f beans/black beans/black
33 33 f beans/borlotti beans/borlotti
34 34 f beans/kidney beans/kidney
35 35 f beans/navy beans/navy
36 36 f beans/pinto beans/pinto
37 37 f beans/turtle beans/turtle
38 38 f fennel fennel
39 39 f fenugreek fenugreek
40 40 f fiddlehead fiddlehead
41 41 f glob:glob glob:glob
42 42 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
43 43 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
44 44 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
45 45 f mammals/skunk mammals/skunk
46 46
47 47 cd mammals
48 48
49 49 hg debugwalk
50 50 f beans/black ../beans/black
51 51 f beans/borlotti ../beans/borlotti
52 52 f beans/kidney ../beans/kidney
53 53 f beans/navy ../beans/navy
54 54 f beans/pinto ../beans/pinto
55 55 f beans/turtle ../beans/turtle
56 56 f fennel ../fennel
57 57 f fenugreek ../fenugreek
58 58 f fiddlehead ../fiddlehead
59 59 f glob:glob ../glob:glob
60 60 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
61 61 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
62 62 f mammals/Procyonidae/raccoon Procyonidae/raccoon
63 63 f mammals/skunk skunk
64 64
65 65 hg debugwalk -X ../beans
66 66 f fennel ../fennel
67 67 f fenugreek ../fenugreek
68 68 f fiddlehead ../fiddlehead
69 69 f glob:glob ../glob:glob
70 70 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
71 71 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
72 72 f mammals/Procyonidae/raccoon Procyonidae/raccoon
73 73 f mammals/skunk skunk
74 74
75 75 hg debugwalk -I *k
76 76 f mammals/skunk skunk
77 77
78 78 hg debugwalk -I glob:*k
79 79 f mammals/skunk skunk
80 80
81 81 hg debugwalk -I relglob:*k
82 82 f beans/black ../beans/black
83 83 f fenugreek ../fenugreek
84 84 f mammals/skunk skunk
85 85
86 86 hg debugwalk -I relglob:*k .
87 87 f mammals/skunk skunk
88 88
89 89 hg debugwalk -I re:.*k$
90 90 f beans/black ../beans/black
91 91 f fenugreek ../fenugreek
92 92 f mammals/skunk skunk
93 93
94 94 hg debugwalk -I relre:.*k$
95 95 f beans/black ../beans/black
96 96 f fenugreek ../fenugreek
97 97 f mammals/skunk skunk
98 98
99 99 hg debugwalk -I path:beans
100 100 f beans/black ../beans/black
101 101 f beans/borlotti ../beans/borlotti
102 102 f beans/kidney ../beans/kidney
103 103 f beans/navy ../beans/navy
104 104 f beans/pinto ../beans/pinto
105 105 f beans/turtle ../beans/turtle
106 106
107 107 hg debugwalk -I relpath:../beans
108 108 f beans/black ../beans/black
109 109 f beans/borlotti ../beans/borlotti
110 110 f beans/kidney ../beans/kidney
111 111 f beans/navy ../beans/navy
112 112 f beans/pinto ../beans/pinto
113 113 f beans/turtle ../beans/turtle
114 114
115 115 hg debugwalk .
116 116 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
117 117 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
118 118 f mammals/Procyonidae/raccoon Procyonidae/raccoon
119 119 f mammals/skunk skunk
120 120
121 121 hg debugwalk -I.
122 122 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
123 123 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
124 124 f mammals/Procyonidae/raccoon Procyonidae/raccoon
125 125 f mammals/skunk skunk
126 126
127 127 hg debugwalk Procyonidae
128 128 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
129 129 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
130 130 f mammals/Procyonidae/raccoon Procyonidae/raccoon
131 131
132 132 cd Procyonidae
133 133
134 134 hg debugwalk .
135 135 f mammals/Procyonidae/cacomistle cacomistle
136 136 f mammals/Procyonidae/coatimundi coatimundi
137 137 f mammals/Procyonidae/raccoon raccoon
138 138
139 139 hg debugwalk ..
140 140 f mammals/Procyonidae/cacomistle cacomistle
141 141 f mammals/Procyonidae/coatimundi coatimundi
142 142 f mammals/Procyonidae/raccoon raccoon
143 143 f mammals/skunk ../skunk
144 144
145 145 cd ..
146 146
147 147 hg debugwalk ../beans
148 148 f beans/black ../beans/black
149 149 f beans/borlotti ../beans/borlotti
150 150 f beans/kidney ../beans/kidney
151 151 f beans/navy ../beans/navy
152 152 f beans/pinto ../beans/pinto
153 153 f beans/turtle ../beans/turtle
154 154
155 155 hg debugwalk .
156 156 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
157 157 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
158 158 f mammals/Procyonidae/raccoon Procyonidae/raccoon
159 159 f mammals/skunk skunk
160 160
161 161 hg debugwalk .hg
162 .hg: No such file or directory
162 abort: path 'mammals/.hg' is inside repo 'mammals'
163 163
164 164 hg debugwalk ../.hg
165 165 abort: path contains illegal component: .hg
166 166
167 167 cd ..
168 168
169 169 hg debugwalk -Ibeans
170 170 f beans/black beans/black
171 171 f beans/borlotti beans/borlotti
172 172 f beans/kidney beans/kidney
173 173 f beans/navy beans/navy
174 174 f beans/pinto beans/pinto
175 175 f beans/turtle beans/turtle
176 176
177 177 hg debugwalk -I {*,{b,m}*/*}k
178 178 f beans/black beans/black
179 179 f fenugreek fenugreek
180 180 f mammals/skunk mammals/skunk
181 181
182 182 hg debugwalk glob:mammals/../beans/b*
183 183 f beans/black beans/black
184 184 f beans/borlotti beans/borlotti
185 185
186 186 hg debugwalk -X*/Procyonidae mammals
187 187 f mammals/skunk mammals/skunk
188 188
189 189 hg debugwalk path:mammals
190 190 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
191 191 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
192 192 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
193 193 f mammals/skunk mammals/skunk
194 194
195 195 hg debugwalk ..
196 196 abort: .. not under root
197 197
198 198 hg debugwalk beans/../..
199 199 abort: beans/../.. not under root
200 200
201 201 hg debugwalk .hg
202 202 abort: path contains illegal component: .hg
203 203
204 204 hg debugwalk beans/../.hg
205 205 abort: path contains illegal component: .hg
206 206
207 207 hg debugwalk beans/../.hg/data
208 208 abort: path contains illegal component: .hg/data
209 209
210 210 hg debugwalk beans/.hg
211 beans/.hg: No such file or directory
211 abort: path 'beans/.hg' is inside repo 'beans'
212 212
213 213 hg debugwalk glob:*
214 214 f fennel fennel
215 215 f fenugreek fenugreek
216 216 f fiddlehead fiddlehead
217 217 f glob:glob glob:glob
218 218
219 219 hg debugwalk glob:**e
220 220 f beans/turtle beans/turtle
221 221 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
222 222
223 223 hg debugwalk re:.*[kb]$
224 224 f beans/black beans/black
225 225 f fenugreek fenugreek
226 226 f glob:glob glob:glob
227 227 f mammals/skunk mammals/skunk
228 228
229 229 hg debugwalk path:beans/black
230 230 f beans/black beans/black exact
231 231
232 232 hg debugwalk path:beans//black
233 233 f beans/black beans/black exact
234 234
235 235 hg debugwalk relglob:Procyonidae
236 236
237 237 hg debugwalk relglob:Procyonidae/**
238 238 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
239 239 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
240 240 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
241 241
242 242 hg debugwalk relglob:Procyonidae/** fennel
243 243 f fennel fennel exact
244 244 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
245 245 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
246 246 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
247 247
248 248 hg debugwalk beans glob:beans/*
249 249 f beans/black beans/black
250 250 f beans/borlotti beans/borlotti
251 251 f beans/kidney beans/kidney
252 252 f beans/navy beans/navy
253 253 f beans/pinto beans/pinto
254 254 f beans/turtle beans/turtle
255 255
256 256 hg debugwalk glob:mamm**
257 257 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
258 258 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
259 259 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
260 260 f mammals/skunk mammals/skunk
261 261
262 262 hg debugwalk glob:mamm** fennel
263 263 f fennel fennel exact
264 264 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
265 265 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
266 266 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
267 267 f mammals/skunk mammals/skunk
268 268
269 269 hg debugwalk glob:j*
270 270
271 271 hg debugwalk NOEXIST
272 272 NOEXIST: No such file or directory
273 273
274 274 hg debugwalk fifo
275 275 fifo: unsupported file type (type is fifo)
276 276
277 277 hg debugwalk fenugreek
278 278 f fenugreek fenugreek exact
279 279
280 280 hg debugwalk fenugreek
281 281 f fenugreek fenugreek exact
282 282
283 283 hg debugwalk new
284 284 f new new exact
285 285
286 286 hg debugwalk ignored
287 287
288 288 hg debugwalk ignored/file
289 289 f ignored/file ignored/file exact
290 290
291 291 cd ..
292 292
293 293 hg debugwalk -R t t/mammals/skunk
294 294 f mammals/skunk t/mammals/skunk exact
295 295
296 296 cd t2
297 297
298 298 hg debugwalk -R ../t ../t/mammals/skunk
299 299 f mammals/skunk ../t/mammals/skunk exact
300 300
301 301 hg debugwalk --cwd ../t mammals/skunk
302 302 f mammals/skunk mammals/skunk exact
303 303
General Comments 0
You need to be logged in to leave comments. Login now