##// END OF EJS Templates
Fix missing import from e68e149f4d44 merge
Patrick Mezard -
r8257:e745063b default
parent child Browse files
Show More
@@ -1,1493 +1,1493 b''
1 1 # util.py - Mercurial utility functions and platform specfic implementations
2 2 #
3 3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2, incorporated herein by reference.
9 9
10 10 """Mercurial utility functions and platform specfic implementations.
11 11
12 12 This contains helper routines that are independent of the SCM core and
13 13 hide platform-specific details from the core.
14 14 """
15 15
16 16 from i18n import _
17 17 import cStringIO, errno, re, shutil, sys, tempfile, traceback, error
18 import os, stat, threading, time, calendar, glob, osutil
18 import os, stat, threading, time, calendar, glob, osutil, random
19 19 import imp
20 20
21 21 # Python compatibility
22 22
23 23 _md5 = None
24 24 def md5(s):
25 25 global _md5
26 26 if _md5 is None:
27 27 try:
28 28 import hashlib
29 29 _md5 = hashlib.md5
30 30 except ImportError:
31 31 import md5
32 32 _md5 = md5.md5
33 33 return _md5(s)
34 34
35 35 _sha1 = None
36 36 def sha1(s):
37 37 global _sha1
38 38 if _sha1 is None:
39 39 try:
40 40 import hashlib
41 41 _sha1 = hashlib.sha1
42 42 except ImportError:
43 43 import sha
44 44 _sha1 = sha.sha
45 45 return _sha1(s)
46 46
47 47 try:
48 48 import subprocess
49 49 subprocess.Popen # trigger ImportError early
50 50 closefds = os.name == 'posix'
51 51 def popen2(cmd, mode='t', bufsize=-1):
52 52 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
53 53 close_fds=closefds,
54 54 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
55 55 return p.stdin, p.stdout
56 56 def popen3(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 stderr=subprocess.PIPE)
61 61 return p.stdin, p.stdout, p.stderr
62 62 def Popen3(cmd, capturestderr=False, bufsize=-1):
63 63 stderr = capturestderr and subprocess.PIPE or None
64 64 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
65 65 close_fds=closefds,
66 66 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
67 67 stderr=stderr)
68 68 p.fromchild = p.stdout
69 69 p.tochild = p.stdin
70 70 p.childerr = p.stderr
71 71 return p
72 72 except ImportError:
73 73 subprocess = None
74 74 from popen2 import Popen3
75 75 popen2 = os.popen2
76 76 popen3 = os.popen3
77 77
78 78
79 79 def version():
80 80 """Return version information if available."""
81 81 try:
82 82 import __version__
83 83 return __version__.version
84 84 except ImportError:
85 85 return 'unknown'
86 86
87 87 # used by parsedate
88 88 defaultdateformats = (
89 89 '%Y-%m-%d %H:%M:%S',
90 90 '%Y-%m-%d %I:%M:%S%p',
91 91 '%Y-%m-%d %H:%M',
92 92 '%Y-%m-%d %I:%M%p',
93 93 '%Y-%m-%d',
94 94 '%m-%d',
95 95 '%m/%d',
96 96 '%m/%d/%y',
97 97 '%m/%d/%Y',
98 98 '%a %b %d %H:%M:%S %Y',
99 99 '%a %b %d %I:%M:%S%p %Y',
100 100 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
101 101 '%b %d %H:%M:%S %Y',
102 102 '%b %d %I:%M:%S%p %Y',
103 103 '%b %d %H:%M:%S',
104 104 '%b %d %I:%M:%S%p',
105 105 '%b %d %H:%M',
106 106 '%b %d %I:%M%p',
107 107 '%b %d %Y',
108 108 '%b %d',
109 109 '%H:%M:%S',
110 110 '%I:%M:%SP',
111 111 '%H:%M',
112 112 '%I:%M%p',
113 113 )
114 114
115 115 extendeddateformats = defaultdateformats + (
116 116 "%Y",
117 117 "%Y-%m",
118 118 "%b",
119 119 "%b %Y",
120 120 )
121 121
122 122 def cachefunc(func):
123 123 '''cache the result of function calls'''
124 124 # XXX doesn't handle keywords args
125 125 cache = {}
126 126 if func.func_code.co_argcount == 1:
127 127 # we gain a small amount of time because
128 128 # we don't need to pack/unpack the list
129 129 def f(arg):
130 130 if arg not in cache:
131 131 cache[arg] = func(arg)
132 132 return cache[arg]
133 133 else:
134 134 def f(*args):
135 135 if args not in cache:
136 136 cache[args] = func(*args)
137 137 return cache[args]
138 138
139 139 return f
140 140
141 141 class propertycache(object):
142 142 def __init__(self, func):
143 143 self.func = func
144 144 self.name = func.__name__
145 145 def __get__(self, obj, type=None):
146 146 result = self.func(obj)
147 147 setattr(obj, self.name, result)
148 148 return result
149 149
150 150 def pipefilter(s, cmd):
151 151 '''filter string S through command CMD, returning its output'''
152 152 (pin, pout) = popen2(cmd, 'b')
153 153 def writer():
154 154 try:
155 155 pin.write(s)
156 156 pin.close()
157 157 except IOError, inst:
158 158 if inst.errno != errno.EPIPE:
159 159 raise
160 160
161 161 # we should use select instead on UNIX, but this will work on most
162 162 # systems, including Windows
163 163 w = threading.Thread(target=writer)
164 164 w.start()
165 165 f = pout.read()
166 166 pout.close()
167 167 w.join()
168 168 return f
169 169
170 170 def tempfilter(s, cmd):
171 171 '''filter string S through a pair of temporary files with CMD.
172 172 CMD is used as a template to create the real command to be run,
173 173 with the strings INFILE and OUTFILE replaced by the real names of
174 174 the temporary files generated.'''
175 175 inname, outname = None, None
176 176 try:
177 177 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
178 178 fp = os.fdopen(infd, 'wb')
179 179 fp.write(s)
180 180 fp.close()
181 181 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
182 182 os.close(outfd)
183 183 cmd = cmd.replace('INFILE', inname)
184 184 cmd = cmd.replace('OUTFILE', outname)
185 185 code = os.system(cmd)
186 186 if sys.platform == 'OpenVMS' and code & 1:
187 187 code = 0
188 188 if code: raise Abort(_("command '%s' failed: %s") %
189 189 (cmd, explain_exit(code)))
190 190 return open(outname, 'rb').read()
191 191 finally:
192 192 try:
193 193 if inname: os.unlink(inname)
194 194 except: pass
195 195 try:
196 196 if outname: os.unlink(outname)
197 197 except: pass
198 198
199 199 filtertable = {
200 200 'tempfile:': tempfilter,
201 201 'pipe:': pipefilter,
202 202 }
203 203
204 204 def filter(s, cmd):
205 205 "filter a string through a command that transforms its input to its output"
206 206 for name, fn in filtertable.iteritems():
207 207 if cmd.startswith(name):
208 208 return fn(s, cmd[len(name):].lstrip())
209 209 return pipefilter(s, cmd)
210 210
211 211 def binary(s):
212 212 """return true if a string is binary data"""
213 213 return bool(s and '\0' in s)
214 214
215 215 def increasingchunks(source, min=1024, max=65536):
216 216 '''return no less than min bytes per chunk while data remains,
217 217 doubling min after each chunk until it reaches max'''
218 218 def log2(x):
219 219 if not x:
220 220 return 0
221 221 i = 0
222 222 while x:
223 223 x >>= 1
224 224 i += 1
225 225 return i - 1
226 226
227 227 buf = []
228 228 blen = 0
229 229 for chunk in source:
230 230 buf.append(chunk)
231 231 blen += len(chunk)
232 232 if blen >= min:
233 233 if min < max:
234 234 min = min << 1
235 235 nmin = 1 << log2(blen)
236 236 if nmin > min:
237 237 min = nmin
238 238 if min > max:
239 239 min = max
240 240 yield ''.join(buf)
241 241 blen = 0
242 242 buf = []
243 243 if buf:
244 244 yield ''.join(buf)
245 245
246 246 Abort = error.Abort
247 247
248 248 def always(fn): return True
249 249 def never(fn): return False
250 250
251 251 def patkind(name, default):
252 252 """Split a string into an optional pattern kind prefix and the
253 253 actual pattern."""
254 254 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
255 255 if name.startswith(prefix + ':'): return name.split(':', 1)
256 256 return default, name
257 257
258 258 def globre(pat, head='^', tail='$'):
259 259 "convert a glob pattern into a regexp"
260 260 i, n = 0, len(pat)
261 261 res = ''
262 262 group = 0
263 263 def peek(): return i < n and pat[i]
264 264 while i < n:
265 265 c = pat[i]
266 266 i = i+1
267 267 if c == '*':
268 268 if peek() == '*':
269 269 i += 1
270 270 res += '.*'
271 271 else:
272 272 res += '[^/]*'
273 273 elif c == '?':
274 274 res += '.'
275 275 elif c == '[':
276 276 j = i
277 277 if j < n and pat[j] in '!]':
278 278 j += 1
279 279 while j < n and pat[j] != ']':
280 280 j += 1
281 281 if j >= n:
282 282 res += '\\['
283 283 else:
284 284 stuff = pat[i:j].replace('\\','\\\\')
285 285 i = j + 1
286 286 if stuff[0] == '!':
287 287 stuff = '^' + stuff[1:]
288 288 elif stuff[0] == '^':
289 289 stuff = '\\' + stuff
290 290 res = '%s[%s]' % (res, stuff)
291 291 elif c == '{':
292 292 group += 1
293 293 res += '(?:'
294 294 elif c == '}' and group:
295 295 res += ')'
296 296 group -= 1
297 297 elif c == ',' and group:
298 298 res += '|'
299 299 elif c == '\\':
300 300 p = peek()
301 301 if p:
302 302 i += 1
303 303 res += re.escape(p)
304 304 else:
305 305 res += re.escape(c)
306 306 else:
307 307 res += re.escape(c)
308 308 return head + res + tail
309 309
310 310 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
311 311
312 312 def pathto(root, n1, n2):
313 313 '''return the relative path from one place to another.
314 314 root should use os.sep to separate directories
315 315 n1 should use os.sep to separate directories
316 316 n2 should use "/" to separate directories
317 317 returns an os.sep-separated path.
318 318
319 319 If n1 is a relative path, it's assumed it's
320 320 relative to root.
321 321 n2 should always be relative to root.
322 322 '''
323 323 if not n1: return localpath(n2)
324 324 if os.path.isabs(n1):
325 325 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
326 326 return os.path.join(root, localpath(n2))
327 327 n2 = '/'.join((pconvert(root), n2))
328 328 a, b = splitpath(n1), n2.split('/')
329 329 a.reverse()
330 330 b.reverse()
331 331 while a and b and a[-1] == b[-1]:
332 332 a.pop()
333 333 b.pop()
334 334 b.reverse()
335 335 return os.sep.join((['..'] * len(a)) + b) or '.'
336 336
337 337 def canonpath(root, cwd, myname):
338 338 """return the canonical path of myname, given cwd and root"""
339 339 if root == os.sep:
340 340 rootsep = os.sep
341 341 elif endswithsep(root):
342 342 rootsep = root
343 343 else:
344 344 rootsep = root + os.sep
345 345 name = myname
346 346 if not os.path.isabs(name):
347 347 name = os.path.join(root, cwd, name)
348 348 name = os.path.normpath(name)
349 349 audit_path = path_auditor(root)
350 350 if name != rootsep and name.startswith(rootsep):
351 351 name = name[len(rootsep):]
352 352 audit_path(name)
353 353 return pconvert(name)
354 354 elif name == root:
355 355 return ''
356 356 else:
357 357 # Determine whether `name' is in the hierarchy at or beneath `root',
358 358 # by iterating name=dirname(name) until that causes no change (can't
359 359 # check name == '/', because that doesn't work on windows). For each
360 360 # `name', compare dev/inode numbers. If they match, the list `rel'
361 361 # holds the reversed list of components making up the relative file
362 362 # name we want.
363 363 root_st = os.stat(root)
364 364 rel = []
365 365 while True:
366 366 try:
367 367 name_st = os.stat(name)
368 368 except OSError:
369 369 break
370 370 if samestat(name_st, root_st):
371 371 if not rel:
372 372 # name was actually the same as root (maybe a symlink)
373 373 return ''
374 374 rel.reverse()
375 375 name = os.path.join(*rel)
376 376 audit_path(name)
377 377 return pconvert(name)
378 378 dirname, basename = os.path.split(name)
379 379 rel.append(basename)
380 380 if dirname == name:
381 381 break
382 382 name = dirname
383 383
384 384 raise Abort('%s not under root' % myname)
385 385
386 386 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
387 387 """build a function to match a set of file patterns
388 388
389 389 arguments:
390 390 canonroot - the canonical root of the tree you're matching against
391 391 cwd - the current working directory, if relevant
392 392 names - patterns to find
393 393 inc - patterns to include
394 394 exc - patterns to exclude
395 395 dflt_pat - if a pattern in names has no explicit type, assume this one
396 396 src - where these patterns came from (e.g. .hgignore)
397 397
398 398 a pattern is one of:
399 399 'glob:<glob>' - a glob relative to cwd
400 400 're:<regexp>' - a regular expression
401 401 'path:<path>' - a path relative to canonroot
402 402 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
403 403 'relpath:<path>' - a path relative to cwd
404 404 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
405 405 '<something>' - one of the cases above, selected by the dflt_pat argument
406 406
407 407 returns:
408 408 a 3-tuple containing
409 409 - list of roots (places where one should start a recursive walk of the fs);
410 410 this often matches the explicit non-pattern names passed in, but also
411 411 includes the initial part of glob: patterns that has no glob characters
412 412 - a bool match(filename) function
413 413 - a bool indicating if any patterns were passed in
414 414 """
415 415
416 416 # a common case: no patterns at all
417 417 if not names and not inc and not exc:
418 418 return [], always, False
419 419
420 420 def contains_glob(name):
421 421 for c in name:
422 422 if c in _globchars: return True
423 423 return False
424 424
425 425 def regex(kind, name, tail):
426 426 '''convert a pattern into a regular expression'''
427 427 if not name:
428 428 return ''
429 429 if kind == 're':
430 430 return name
431 431 elif kind == 'path':
432 432 return '^' + re.escape(name) + '(?:/|$)'
433 433 elif kind == 'relglob':
434 434 return globre(name, '(?:|.*/)', tail)
435 435 elif kind == 'relpath':
436 436 return re.escape(name) + '(?:/|$)'
437 437 elif kind == 'relre':
438 438 if name.startswith('^'):
439 439 return name
440 440 return '.*' + name
441 441 return globre(name, '', tail)
442 442
443 443 def matchfn(pats, tail):
444 444 """build a matching function from a set of patterns"""
445 445 if not pats:
446 446 return
447 447 try:
448 448 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
449 449 if len(pat) > 20000:
450 450 raise OverflowError()
451 451 return re.compile(pat).match
452 452 except OverflowError:
453 453 # We're using a Python with a tiny regex engine and we
454 454 # made it explode, so we'll divide the pattern list in two
455 455 # until it works
456 456 l = len(pats)
457 457 if l < 2:
458 458 raise
459 459 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
460 460 return lambda s: a(s) or b(s)
461 461 except re.error:
462 462 for k, p in pats:
463 463 try:
464 464 re.compile('(?:%s)' % regex(k, p, tail))
465 465 except re.error:
466 466 if src:
467 467 raise Abort("%s: invalid pattern (%s): %s" %
468 468 (src, k, p))
469 469 else:
470 470 raise Abort("invalid pattern (%s): %s" % (k, p))
471 471 raise Abort("invalid pattern")
472 472
473 473 def globprefix(pat):
474 474 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
475 475 root = []
476 476 for p in pat.split('/'):
477 477 if contains_glob(p): break
478 478 root.append(p)
479 479 return '/'.join(root) or '.'
480 480
481 481 def normalizepats(names, default):
482 482 pats = []
483 483 roots = []
484 484 anypats = False
485 485 for kind, name in [patkind(p, default) for p in names]:
486 486 if kind in ('glob', 'relpath'):
487 487 name = canonpath(canonroot, cwd, name)
488 488 elif kind in ('relglob', 'path'):
489 489 name = normpath(name)
490 490
491 491 pats.append((kind, name))
492 492
493 493 if kind in ('glob', 're', 'relglob', 'relre'):
494 494 anypats = True
495 495
496 496 if kind == 'glob':
497 497 root = globprefix(name)
498 498 roots.append(root)
499 499 elif kind in ('relpath', 'path'):
500 500 roots.append(name or '.')
501 501 elif kind == 'relglob':
502 502 roots.append('.')
503 503 return roots, pats, anypats
504 504
505 505 roots, pats, anypats = normalizepats(names, dflt_pat)
506 506
507 507 patmatch = matchfn(pats, '$') or always
508 508 incmatch = always
509 509 if inc:
510 510 dummy, inckinds, dummy = normalizepats(inc, 'glob')
511 511 incmatch = matchfn(inckinds, '(?:/|$)')
512 512 excmatch = never
513 513 if exc:
514 514 dummy, exckinds, dummy = normalizepats(exc, 'glob')
515 515 excmatch = matchfn(exckinds, '(?:/|$)')
516 516
517 517 if not names and inc and not exc:
518 518 # common case: hgignore patterns
519 519 match = incmatch
520 520 else:
521 521 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
522 522
523 523 return (roots, match, (inc or exc or anypats) and True)
524 524
525 525 _hgexecutable = None
526 526
527 527 def main_is_frozen():
528 528 """return True if we are a frozen executable.
529 529
530 530 The code supports py2exe (most common, Windows only) and tools/freeze
531 531 (portable, not much used).
532 532 """
533 533 return (hasattr(sys, "frozen") or # new py2exe
534 534 hasattr(sys, "importers") or # old py2exe
535 535 imp.is_frozen("__main__")) # tools/freeze
536 536
537 537 def hgexecutable():
538 538 """return location of the 'hg' executable.
539 539
540 540 Defaults to $HG or 'hg' in the search path.
541 541 """
542 542 if _hgexecutable is None:
543 543 hg = os.environ.get('HG')
544 544 if hg:
545 545 set_hgexecutable(hg)
546 546 elif main_is_frozen():
547 547 set_hgexecutable(sys.executable)
548 548 else:
549 549 set_hgexecutable(find_exe('hg') or 'hg')
550 550 return _hgexecutable
551 551
552 552 def set_hgexecutable(path):
553 553 """set location of the 'hg' executable"""
554 554 global _hgexecutable
555 555 _hgexecutable = path
556 556
557 557 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
558 558 '''enhanced shell command execution.
559 559 run with environment maybe modified, maybe in different dir.
560 560
561 561 if command fails and onerr is None, return status. if ui object,
562 562 print error message and return status, else raise onerr object as
563 563 exception.'''
564 564 def py2shell(val):
565 565 'convert python object into string that is useful to shell'
566 566 if val in (None, False):
567 567 return '0'
568 568 if val == True:
569 569 return '1'
570 570 return str(val)
571 571 oldenv = {}
572 572 for k in environ:
573 573 oldenv[k] = os.environ.get(k)
574 574 if cwd is not None:
575 575 oldcwd = os.getcwd()
576 576 origcmd = cmd
577 577 if os.name == 'nt':
578 578 cmd = '"%s"' % cmd
579 579 try:
580 580 for k, v in environ.iteritems():
581 581 os.environ[k] = py2shell(v)
582 582 os.environ['HG'] = hgexecutable()
583 583 if cwd is not None and oldcwd != cwd:
584 584 os.chdir(cwd)
585 585 rc = os.system(cmd)
586 586 if sys.platform == 'OpenVMS' and rc & 1:
587 587 rc = 0
588 588 if rc and onerr:
589 589 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
590 590 explain_exit(rc)[0])
591 591 if errprefix:
592 592 errmsg = '%s: %s' % (errprefix, errmsg)
593 593 try:
594 594 onerr.warn(errmsg + '\n')
595 595 except AttributeError:
596 596 raise onerr(errmsg)
597 597 return rc
598 598 finally:
599 599 for k, v in oldenv.iteritems():
600 600 if v is None:
601 601 del os.environ[k]
602 602 else:
603 603 os.environ[k] = v
604 604 if cwd is not None and oldcwd != cwd:
605 605 os.chdir(oldcwd)
606 606
607 607 def checksignature(func):
608 608 '''wrap a function with code to check for calling errors'''
609 609 def check(*args, **kwargs):
610 610 try:
611 611 return func(*args, **kwargs)
612 612 except TypeError:
613 613 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
614 614 raise error.SignatureError
615 615 raise
616 616
617 617 return check
618 618
619 619 # os.path.lexists is not available on python2.3
620 620 def lexists(filename):
621 621 "test whether a file with this name exists. does not follow symlinks"
622 622 try:
623 623 os.lstat(filename)
624 624 except:
625 625 return False
626 626 return True
627 627
628 628 def rename(src, dst):
629 629 """forcibly rename a file"""
630 630 try:
631 631 os.rename(src, dst)
632 632 except OSError, err: # FIXME: check err (EEXIST ?)
633 633
634 634 # On windows, rename to existing file is not allowed, so we
635 635 # must delete destination first. But if a file is open, unlink
636 636 # schedules it for delete but does not delete it. Rename
637 637 # happens immediately even for open files, so we rename
638 638 # destination to a temporary name, then delete that. Then
639 639 # rename is safe to do.
640 640 # The temporary name is chosen at random to avoid the situation
641 641 # where a file is left lying around from a previous aborted run.
642 642 # The usual race condition this introduces can't be avoided as
643 643 # we need the name to rename into, and not the file itself. Due
644 644 # to the nature of the operation however, any races will at worst
645 645 # lead to the rename failing and the current operation aborting.
646 646
647 647 def tempname(prefix):
648 648 for tries in xrange(10):
649 649 temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
650 650 if not os.path.exists(temp):
651 651 return temp
652 652 raise IOError, (errno.EEXIST, "No usable temporary filename found")
653 653
654 654 temp = tempname(dst)
655 655 os.rename(dst, temp)
656 656 os.unlink(temp)
657 657 os.rename(src, dst)
658 658
659 659 def unlink(f):
660 660 """unlink and remove the directory if it is empty"""
661 661 os.unlink(f)
662 662 # try removing directories that might now be empty
663 663 try:
664 664 os.removedirs(os.path.dirname(f))
665 665 except OSError:
666 666 pass
667 667
668 668 def copyfile(src, dest):
669 669 "copy a file, preserving mode and atime/mtime"
670 670 if os.path.islink(src):
671 671 try:
672 672 os.unlink(dest)
673 673 except:
674 674 pass
675 675 os.symlink(os.readlink(src), dest)
676 676 else:
677 677 try:
678 678 shutil.copyfile(src, dest)
679 679 shutil.copystat(src, dest)
680 680 except shutil.Error, inst:
681 681 raise Abort(str(inst))
682 682
683 683 def copyfiles(src, dst, hardlink=None):
684 684 """Copy a directory tree using hardlinks if possible"""
685 685
686 686 if hardlink is None:
687 687 hardlink = (os.stat(src).st_dev ==
688 688 os.stat(os.path.dirname(dst)).st_dev)
689 689
690 690 if os.path.isdir(src):
691 691 os.mkdir(dst)
692 692 for name, kind in osutil.listdir(src):
693 693 srcname = os.path.join(src, name)
694 694 dstname = os.path.join(dst, name)
695 695 copyfiles(srcname, dstname, hardlink)
696 696 else:
697 697 if hardlink:
698 698 try:
699 699 os_link(src, dst)
700 700 except (IOError, OSError):
701 701 hardlink = False
702 702 shutil.copy(src, dst)
703 703 else:
704 704 shutil.copy(src, dst)
705 705
706 706 class path_auditor(object):
707 707 '''ensure that a filesystem path contains no banned components.
708 708 the following properties of a path are checked:
709 709
710 710 - under top-level .hg
711 711 - starts at the root of a windows drive
712 712 - contains ".."
713 713 - traverses a symlink (e.g. a/symlink_here/b)
714 714 - inside a nested repository'''
715 715
716 716 def __init__(self, root):
717 717 self.audited = set()
718 718 self.auditeddir = set()
719 719 self.root = root
720 720
721 721 def __call__(self, path):
722 722 if path in self.audited:
723 723 return
724 724 normpath = os.path.normcase(path)
725 725 parts = splitpath(normpath)
726 726 if (os.path.splitdrive(path)[0]
727 727 or parts[0].lower() in ('.hg', '.hg.', '')
728 728 or os.pardir in parts):
729 729 raise Abort(_("path contains illegal component: %s") % path)
730 730 if '.hg' in path.lower():
731 731 lparts = [p.lower() for p in parts]
732 732 for p in '.hg', '.hg.':
733 733 if p in lparts[1:]:
734 734 pos = lparts.index(p)
735 735 base = os.path.join(*parts[:pos])
736 736 raise Abort(_('path %r is inside repo %r') % (path, base))
737 737 def check(prefix):
738 738 curpath = os.path.join(self.root, prefix)
739 739 try:
740 740 st = os.lstat(curpath)
741 741 except OSError, err:
742 742 # EINVAL can be raised as invalid path syntax under win32.
743 743 # They must be ignored for patterns can be checked too.
744 744 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
745 745 raise
746 746 else:
747 747 if stat.S_ISLNK(st.st_mode):
748 748 raise Abort(_('path %r traverses symbolic link %r') %
749 749 (path, prefix))
750 750 elif (stat.S_ISDIR(st.st_mode) and
751 751 os.path.isdir(os.path.join(curpath, '.hg'))):
752 752 raise Abort(_('path %r is inside repo %r') %
753 753 (path, prefix))
754 754 parts.pop()
755 755 prefixes = []
756 756 for n in range(len(parts)):
757 757 prefix = os.sep.join(parts)
758 758 if prefix in self.auditeddir:
759 759 break
760 760 check(prefix)
761 761 prefixes.append(prefix)
762 762 parts.pop()
763 763
764 764 self.audited.add(path)
765 765 # only add prefixes to the cache after checking everything: we don't
766 766 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
767 767 self.auditeddir.update(prefixes)
768 768
769 769 def nlinks(pathname):
770 770 """Return number of hardlinks for the given file."""
771 771 return os.lstat(pathname).st_nlink
772 772
773 773 if hasattr(os, 'link'):
774 774 os_link = os.link
775 775 else:
776 776 def os_link(src, dst):
777 777 raise OSError(0, _("Hardlinks not supported"))
778 778
779 779 def lookup_reg(key, name=None, scope=None):
780 780 return None
781 781
782 782 if os.name == 'nt':
783 783 from windows import *
784 784 def expand_glob(pats):
785 785 '''On Windows, expand the implicit globs in a list of patterns'''
786 786 ret = []
787 787 for p in pats:
788 788 kind, name = patkind(p, None)
789 789 if kind is None:
790 790 globbed = glob.glob(name)
791 791 if globbed:
792 792 ret.extend(globbed)
793 793 continue
794 794 # if we couldn't expand the glob, just keep it around
795 795 ret.append(p)
796 796 return ret
797 797 else:
798 798 from posix import *
799 799
800 800 def makelock(info, pathname):
801 801 try:
802 802 return os.symlink(info, pathname)
803 803 except OSError, why:
804 804 if why.errno == errno.EEXIST:
805 805 raise
806 806 except AttributeError: # no symlink in os
807 807 pass
808 808
809 809 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
810 810 os.write(ld, info)
811 811 os.close(ld)
812 812
813 813 def readlock(pathname):
814 814 try:
815 815 return os.readlink(pathname)
816 816 except OSError, why:
817 817 if why.errno not in (errno.EINVAL, errno.ENOSYS):
818 818 raise
819 819 except AttributeError: # no symlink in os
820 820 pass
821 821 return posixfile(pathname).read()
822 822
823 823 def fstat(fp):
824 824 '''stat file object that may not have fileno method.'''
825 825 try:
826 826 return os.fstat(fp.fileno())
827 827 except AttributeError:
828 828 return os.stat(fp.name)
829 829
830 830 # File system features
831 831
832 832 def checkcase(path):
833 833 """
834 834 Check whether the given path is on a case-sensitive filesystem
835 835
836 836 Requires a path (like /foo/.hg) ending with a foldable final
837 837 directory component.
838 838 """
839 839 s1 = os.stat(path)
840 840 d, b = os.path.split(path)
841 841 p2 = os.path.join(d, b.upper())
842 842 if path == p2:
843 843 p2 = os.path.join(d, b.lower())
844 844 try:
845 845 s2 = os.stat(p2)
846 846 if s2 == s1:
847 847 return False
848 848 return True
849 849 except:
850 850 return True
851 851
852 852 _fspathcache = {}
853 853 def fspath(name, root):
854 854 '''Get name in the case stored in the filesystem
855 855
856 856 The name is either relative to root, or it is an absolute path starting
857 857 with root. Note that this function is unnecessary, and should not be
858 858 called, for case-sensitive filesystems (simply because it's expensive).
859 859 '''
860 860 # If name is absolute, make it relative
861 861 if name.lower().startswith(root.lower()):
862 862 l = len(root)
863 863 if name[l] == os.sep or name[l] == os.altsep:
864 864 l = l + 1
865 865 name = name[l:]
866 866
867 867 if not os.path.exists(os.path.join(root, name)):
868 868 return None
869 869
870 870 seps = os.sep
871 871 if os.altsep:
872 872 seps = seps + os.altsep
873 873 # Protect backslashes. This gets silly very quickly.
874 874 seps.replace('\\','\\\\')
875 875 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
876 876 dir = os.path.normcase(os.path.normpath(root))
877 877 result = []
878 878 for part, sep in pattern.findall(name):
879 879 if sep:
880 880 result.append(sep)
881 881 continue
882 882
883 883 if dir not in _fspathcache:
884 884 _fspathcache[dir] = os.listdir(dir)
885 885 contents = _fspathcache[dir]
886 886
887 887 lpart = part.lower()
888 888 for n in contents:
889 889 if n.lower() == lpart:
890 890 result.append(n)
891 891 break
892 892 else:
893 893 # Cannot happen, as the file exists!
894 894 result.append(part)
895 895 dir = os.path.join(dir, lpart)
896 896
897 897 return ''.join(result)
898 898
899 899 def checkexec(path):
900 900 """
901 901 Check whether the given path is on a filesystem with UNIX-like exec flags
902 902
903 903 Requires a directory (like /foo/.hg)
904 904 """
905 905
906 906 # VFAT on some Linux versions can flip mode but it doesn't persist
907 907 # a FS remount. Frequently we can detect it if files are created
908 908 # with exec bit on.
909 909
910 910 try:
911 911 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
912 912 fh, fn = tempfile.mkstemp("", "", path)
913 913 try:
914 914 os.close(fh)
915 915 m = os.stat(fn).st_mode & 0777
916 916 new_file_has_exec = m & EXECFLAGS
917 917 os.chmod(fn, m ^ EXECFLAGS)
918 918 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
919 919 finally:
920 920 os.unlink(fn)
921 921 except (IOError, OSError):
922 922 # we don't care, the user probably won't be able to commit anyway
923 923 return False
924 924 return not (new_file_has_exec or exec_flags_cannot_flip)
925 925
926 926 def checklink(path):
927 927 """check whether the given path is on a symlink-capable filesystem"""
928 928 # mktemp is not racy because symlink creation will fail if the
929 929 # file already exists
930 930 name = tempfile.mktemp(dir=path)
931 931 try:
932 932 os.symlink(".", name)
933 933 os.unlink(name)
934 934 return True
935 935 except (OSError, AttributeError):
936 936 return False
937 937
938 938 def needbinarypatch():
939 939 """return True if patches should be applied in binary mode by default."""
940 940 return os.name == 'nt'
941 941
942 942 def endswithsep(path):
943 943 '''Check path ends with os.sep or os.altsep.'''
944 944 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
945 945
946 946 def splitpath(path):
947 947 '''Split path by os.sep.
948 948 Note that this function does not use os.altsep because this is
949 949 an alternative of simple "xxx.split(os.sep)".
950 950 It is recommended to use os.path.normpath() before using this
951 951 function if need.'''
952 952 return path.split(os.sep)
953 953
954 954 def gui():
955 955 '''Are we running in a GUI?'''
956 956 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
957 957
958 958 def mktempcopy(name, emptyok=False, createmode=None):
959 959 """Create a temporary file with the same contents from name
960 960
961 961 The permission bits are copied from the original file.
962 962
963 963 If the temporary file is going to be truncated immediately, you
964 964 can use emptyok=True as an optimization.
965 965
966 966 Returns the name of the temporary file.
967 967 """
968 968 d, fn = os.path.split(name)
969 969 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
970 970 os.close(fd)
971 971 # Temporary files are created with mode 0600, which is usually not
972 972 # what we want. If the original file already exists, just copy
973 973 # its mode. Otherwise, manually obey umask.
974 974 try:
975 975 st_mode = os.lstat(name).st_mode & 0777
976 976 except OSError, inst:
977 977 if inst.errno != errno.ENOENT:
978 978 raise
979 979 st_mode = createmode
980 980 if st_mode is None:
981 981 st_mode = ~umask
982 982 st_mode &= 0666
983 983 os.chmod(temp, st_mode)
984 984 if emptyok:
985 985 return temp
986 986 try:
987 987 try:
988 988 ifp = posixfile(name, "rb")
989 989 except IOError, inst:
990 990 if inst.errno == errno.ENOENT:
991 991 return temp
992 992 if not getattr(inst, 'filename', None):
993 993 inst.filename = name
994 994 raise
995 995 ofp = posixfile(temp, "wb")
996 996 for chunk in filechunkiter(ifp):
997 997 ofp.write(chunk)
998 998 ifp.close()
999 999 ofp.close()
1000 1000 except:
1001 1001 try: os.unlink(temp)
1002 1002 except: pass
1003 1003 raise
1004 1004 return temp
1005 1005
1006 1006 class atomictempfile(posixfile):
1007 1007 """file-like object that atomically updates a file
1008 1008
1009 1009 All writes will be redirected to a temporary copy of the original
1010 1010 file. When rename is called, the copy is renamed to the original
1011 1011 name, making the changes visible.
1012 1012 """
1013 1013 def __init__(self, name, mode, createmode):
1014 1014 self.__name = name
1015 1015 self.temp = mktempcopy(name, emptyok=('w' in mode),
1016 1016 createmode=createmode)
1017 1017 posixfile.__init__(self, self.temp, mode)
1018 1018
1019 1019 def rename(self):
1020 1020 if not self.closed:
1021 1021 posixfile.close(self)
1022 1022 rename(self.temp, localpath(self.__name))
1023 1023
1024 1024 def __del__(self):
1025 1025 if not self.closed:
1026 1026 try:
1027 1027 os.unlink(self.temp)
1028 1028 except: pass
1029 1029 posixfile.close(self)
1030 1030
1031 1031 def makedirs(name, mode=None):
1032 1032 """recursive directory creation with parent mode inheritance"""
1033 1033 try:
1034 1034 os.mkdir(name)
1035 1035 if mode is not None:
1036 1036 os.chmod(name, mode)
1037 1037 return
1038 1038 except OSError, err:
1039 1039 if err.errno == errno.EEXIST:
1040 1040 return
1041 1041 if err.errno != errno.ENOENT:
1042 1042 raise
1043 1043 parent = os.path.abspath(os.path.dirname(name))
1044 1044 makedirs(parent, mode)
1045 1045 makedirs(name, mode)
1046 1046
1047 1047 class opener(object):
1048 1048 """Open files relative to a base directory
1049 1049
1050 1050 This class is used to hide the details of COW semantics and
1051 1051 remote file access from higher level code.
1052 1052 """
1053 1053 def __init__(self, base, audit=True):
1054 1054 self.base = base
1055 1055 if audit:
1056 1056 self.audit_path = path_auditor(base)
1057 1057 else:
1058 1058 self.audit_path = always
1059 1059 self.createmode = None
1060 1060
1061 1061 def __getattr__(self, name):
1062 1062 if name == '_can_symlink':
1063 1063 self._can_symlink = checklink(self.base)
1064 1064 return self._can_symlink
1065 1065 raise AttributeError(name)
1066 1066
1067 1067 def _fixfilemode(self, name):
1068 1068 if self.createmode is None:
1069 1069 return
1070 1070 os.chmod(name, self.createmode & 0666)
1071 1071
1072 1072 def __call__(self, path, mode="r", text=False, atomictemp=False):
1073 1073 self.audit_path(path)
1074 1074 f = os.path.join(self.base, path)
1075 1075
1076 1076 if not text and "b" not in mode:
1077 1077 mode += "b" # for that other OS
1078 1078
1079 1079 nlink = -1
1080 1080 if mode not in ("r", "rb"):
1081 1081 try:
1082 1082 nlink = nlinks(f)
1083 1083 except OSError:
1084 1084 nlink = 0
1085 1085 d = os.path.dirname(f)
1086 1086 if not os.path.isdir(d):
1087 1087 makedirs(d, self.createmode)
1088 1088 if atomictemp:
1089 1089 return atomictempfile(f, mode, self.createmode)
1090 1090 if nlink > 1:
1091 1091 rename(mktempcopy(f), f)
1092 1092 fp = posixfile(f, mode)
1093 1093 if nlink == 0:
1094 1094 self._fixfilemode(f)
1095 1095 return fp
1096 1096
1097 1097 def symlink(self, src, dst):
1098 1098 self.audit_path(dst)
1099 1099 linkname = os.path.join(self.base, dst)
1100 1100 try:
1101 1101 os.unlink(linkname)
1102 1102 except OSError:
1103 1103 pass
1104 1104
1105 1105 dirname = os.path.dirname(linkname)
1106 1106 if not os.path.exists(dirname):
1107 1107 makedirs(dirname, self.createmode)
1108 1108
1109 1109 if self._can_symlink:
1110 1110 try:
1111 1111 os.symlink(src, linkname)
1112 1112 except OSError, err:
1113 1113 raise OSError(err.errno, _('could not symlink to %r: %s') %
1114 1114 (src, err.strerror), linkname)
1115 1115 else:
1116 1116 f = self(dst, "w")
1117 1117 f.write(src)
1118 1118 f.close()
1119 1119 self._fixfilemode(dst)
1120 1120
1121 1121 class chunkbuffer(object):
1122 1122 """Allow arbitrary sized chunks of data to be efficiently read from an
1123 1123 iterator over chunks of arbitrary size."""
1124 1124
1125 1125 def __init__(self, in_iter):
1126 1126 """in_iter is the iterator that's iterating over the input chunks.
1127 1127 targetsize is how big a buffer to try to maintain."""
1128 1128 self.iter = iter(in_iter)
1129 1129 self.buf = ''
1130 1130 self.targetsize = 2**16
1131 1131
1132 1132 def read(self, l):
1133 1133 """Read L bytes of data from the iterator of chunks of data.
1134 1134 Returns less than L bytes if the iterator runs dry."""
1135 1135 if l > len(self.buf) and self.iter:
1136 1136 # Clamp to a multiple of self.targetsize
1137 1137 targetsize = max(l, self.targetsize)
1138 1138 collector = cStringIO.StringIO()
1139 1139 collector.write(self.buf)
1140 1140 collected = len(self.buf)
1141 1141 for chunk in self.iter:
1142 1142 collector.write(chunk)
1143 1143 collected += len(chunk)
1144 1144 if collected >= targetsize:
1145 1145 break
1146 1146 if collected < targetsize:
1147 1147 self.iter = False
1148 1148 self.buf = collector.getvalue()
1149 1149 if len(self.buf) == l:
1150 1150 s, self.buf = str(self.buf), ''
1151 1151 else:
1152 1152 s, self.buf = self.buf[:l], buffer(self.buf, l)
1153 1153 return s
1154 1154
1155 1155 def filechunkiter(f, size=65536, limit=None):
1156 1156 """Create a generator that produces the data in the file size
1157 1157 (default 65536) bytes at a time, up to optional limit (default is
1158 1158 to read all data). Chunks may be less than size bytes if the
1159 1159 chunk is the last chunk in the file, or the file is a socket or
1160 1160 some other type of file that sometimes reads less data than is
1161 1161 requested."""
1162 1162 assert size >= 0
1163 1163 assert limit is None or limit >= 0
1164 1164 while True:
1165 1165 if limit is None: nbytes = size
1166 1166 else: nbytes = min(limit, size)
1167 1167 s = nbytes and f.read(nbytes)
1168 1168 if not s: break
1169 1169 if limit: limit -= len(s)
1170 1170 yield s
1171 1171
1172 1172 def makedate():
1173 1173 lt = time.localtime()
1174 1174 if lt[8] == 1 and time.daylight:
1175 1175 tz = time.altzone
1176 1176 else:
1177 1177 tz = time.timezone
1178 1178 return time.mktime(lt), tz
1179 1179
1180 1180 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1181 1181 """represent a (unixtime, offset) tuple as a localized time.
1182 1182 unixtime is seconds since the epoch, and offset is the time zone's
1183 1183 number of seconds away from UTC. if timezone is false, do not
1184 1184 append time zone to string."""
1185 1185 t, tz = date or makedate()
1186 1186 if "%1" in format or "%2" in format:
1187 1187 sign = (tz > 0) and "-" or "+"
1188 1188 minutes = abs(tz) / 60
1189 1189 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1190 1190 format = format.replace("%2", "%02d" % (minutes % 60))
1191 1191 s = time.strftime(format, time.gmtime(float(t) - tz))
1192 1192 return s
1193 1193
1194 1194 def shortdate(date=None):
1195 1195 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1196 1196 return datestr(date, format='%Y-%m-%d')
1197 1197
1198 1198 def strdate(string, format, defaults=[]):
1199 1199 """parse a localized time string and return a (unixtime, offset) tuple.
1200 1200 if the string cannot be parsed, ValueError is raised."""
1201 1201 def timezone(string):
1202 1202 tz = string.split()[-1]
1203 1203 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1204 1204 sign = (tz[0] == "+") and 1 or -1
1205 1205 hours = int(tz[1:3])
1206 1206 minutes = int(tz[3:5])
1207 1207 return -sign * (hours * 60 + minutes) * 60
1208 1208 if tz == "GMT" or tz == "UTC":
1209 1209 return 0
1210 1210 return None
1211 1211
1212 1212 # NOTE: unixtime = localunixtime + offset
1213 1213 offset, date = timezone(string), string
1214 1214 if offset != None:
1215 1215 date = " ".join(string.split()[:-1])
1216 1216
1217 1217 # add missing elements from defaults
1218 1218 for part in defaults:
1219 1219 found = [True for p in part if ("%"+p) in format]
1220 1220 if not found:
1221 1221 date += "@" + defaults[part]
1222 1222 format += "@%" + part[0]
1223 1223
1224 1224 timetuple = time.strptime(date, format)
1225 1225 localunixtime = int(calendar.timegm(timetuple))
1226 1226 if offset is None:
1227 1227 # local timezone
1228 1228 unixtime = int(time.mktime(timetuple))
1229 1229 offset = unixtime - localunixtime
1230 1230 else:
1231 1231 unixtime = localunixtime + offset
1232 1232 return unixtime, offset
1233 1233
1234 1234 def parsedate(date, formats=None, defaults=None):
1235 1235 """parse a localized date/time string and return a (unixtime, offset) tuple.
1236 1236
1237 1237 The date may be a "unixtime offset" string or in one of the specified
1238 1238 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1239 1239 """
1240 1240 if not date:
1241 1241 return 0, 0
1242 1242 if isinstance(date, tuple) and len(date) == 2:
1243 1243 return date
1244 1244 if not formats:
1245 1245 formats = defaultdateformats
1246 1246 date = date.strip()
1247 1247 try:
1248 1248 when, offset = map(int, date.split(' '))
1249 1249 except ValueError:
1250 1250 # fill out defaults
1251 1251 if not defaults:
1252 1252 defaults = {}
1253 1253 now = makedate()
1254 1254 for part in "d mb yY HI M S".split():
1255 1255 if part not in defaults:
1256 1256 if part[0] in "HMS":
1257 1257 defaults[part] = "00"
1258 1258 else:
1259 1259 defaults[part] = datestr(now, "%" + part[0])
1260 1260
1261 1261 for format in formats:
1262 1262 try:
1263 1263 when, offset = strdate(date, format, defaults)
1264 1264 except (ValueError, OverflowError):
1265 1265 pass
1266 1266 else:
1267 1267 break
1268 1268 else:
1269 1269 raise Abort(_('invalid date: %r ') % date)
1270 1270 # validate explicit (probably user-specified) date and
1271 1271 # time zone offset. values must fit in signed 32 bits for
1272 1272 # current 32-bit linux runtimes. timezones go from UTC-12
1273 1273 # to UTC+14
1274 1274 if abs(when) > 0x7fffffff:
1275 1275 raise Abort(_('date exceeds 32 bits: %d') % when)
1276 1276 if offset < -50400 or offset > 43200:
1277 1277 raise Abort(_('impossible time zone offset: %d') % offset)
1278 1278 return when, offset
1279 1279
1280 1280 def matchdate(date):
1281 1281 """Return a function that matches a given date match specifier
1282 1282
1283 1283 Formats include:
1284 1284
1285 1285 '{date}' match a given date to the accuracy provided
1286 1286
1287 1287 '<{date}' on or before a given date
1288 1288
1289 1289 '>{date}' on or after a given date
1290 1290
1291 1291 """
1292 1292
1293 1293 def lower(date):
1294 1294 d = dict(mb="1", d="1")
1295 1295 return parsedate(date, extendeddateformats, d)[0]
1296 1296
1297 1297 def upper(date):
1298 1298 d = dict(mb="12", HI="23", M="59", S="59")
1299 1299 for days in "31 30 29".split():
1300 1300 try:
1301 1301 d["d"] = days
1302 1302 return parsedate(date, extendeddateformats, d)[0]
1303 1303 except:
1304 1304 pass
1305 1305 d["d"] = "28"
1306 1306 return parsedate(date, extendeddateformats, d)[0]
1307 1307
1308 1308 date = date.strip()
1309 1309 if date[0] == "<":
1310 1310 when = upper(date[1:])
1311 1311 return lambda x: x <= when
1312 1312 elif date[0] == ">":
1313 1313 when = lower(date[1:])
1314 1314 return lambda x: x >= when
1315 1315 elif date[0] == "-":
1316 1316 try:
1317 1317 days = int(date[1:])
1318 1318 except ValueError:
1319 1319 raise Abort(_("invalid day spec: %s") % date[1:])
1320 1320 when = makedate()[0] - days * 3600 * 24
1321 1321 return lambda x: x >= when
1322 1322 elif " to " in date:
1323 1323 a, b = date.split(" to ")
1324 1324 start, stop = lower(a), upper(b)
1325 1325 return lambda x: x >= start and x <= stop
1326 1326 else:
1327 1327 start, stop = lower(date), upper(date)
1328 1328 return lambda x: x >= start and x <= stop
1329 1329
1330 1330 def shortuser(user):
1331 1331 """Return a short representation of a user name or email address."""
1332 1332 f = user.find('@')
1333 1333 if f >= 0:
1334 1334 user = user[:f]
1335 1335 f = user.find('<')
1336 1336 if f >= 0:
1337 1337 user = user[f+1:]
1338 1338 f = user.find(' ')
1339 1339 if f >= 0:
1340 1340 user = user[:f]
1341 1341 f = user.find('.')
1342 1342 if f >= 0:
1343 1343 user = user[:f]
1344 1344 return user
1345 1345
1346 1346 def email(author):
1347 1347 '''get email of author.'''
1348 1348 r = author.find('>')
1349 1349 if r == -1: r = None
1350 1350 return author[author.find('<')+1:r]
1351 1351
1352 1352 def ellipsis(text, maxlength=400):
1353 1353 """Trim string to at most maxlength (default: 400) characters."""
1354 1354 if len(text) <= maxlength:
1355 1355 return text
1356 1356 else:
1357 1357 return "%s..." % (text[:maxlength-3])
1358 1358
1359 1359 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1360 1360 '''yield every hg repository under path, recursively.'''
1361 1361 def errhandler(err):
1362 1362 if err.filename == path:
1363 1363 raise err
1364 1364 if followsym and hasattr(os.path, 'samestat'):
1365 1365 def _add_dir_if_not_there(dirlst, dirname):
1366 1366 match = False
1367 1367 samestat = os.path.samestat
1368 1368 dirstat = os.stat(dirname)
1369 1369 for lstdirstat in dirlst:
1370 1370 if samestat(dirstat, lstdirstat):
1371 1371 match = True
1372 1372 break
1373 1373 if not match:
1374 1374 dirlst.append(dirstat)
1375 1375 return not match
1376 1376 else:
1377 1377 followsym = False
1378 1378
1379 1379 if (seen_dirs is None) and followsym:
1380 1380 seen_dirs = []
1381 1381 _add_dir_if_not_there(seen_dirs, path)
1382 1382 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1383 1383 if '.hg' in dirs:
1384 1384 yield root # found a repository
1385 1385 qroot = os.path.join(root, '.hg', 'patches')
1386 1386 if os.path.isdir(os.path.join(qroot, '.hg')):
1387 1387 yield qroot # we have a patch queue repo here
1388 1388 if recurse:
1389 1389 # avoid recursing inside the .hg directory
1390 1390 dirs.remove('.hg')
1391 1391 else:
1392 1392 dirs[:] = [] # don't descend further
1393 1393 elif followsym:
1394 1394 newdirs = []
1395 1395 for d in dirs:
1396 1396 fname = os.path.join(root, d)
1397 1397 if _add_dir_if_not_there(seen_dirs, fname):
1398 1398 if os.path.islink(fname):
1399 1399 for hgname in walkrepos(fname, True, seen_dirs):
1400 1400 yield hgname
1401 1401 else:
1402 1402 newdirs.append(d)
1403 1403 dirs[:] = newdirs
1404 1404
1405 1405 _rcpath = None
1406 1406
1407 1407 def os_rcpath():
1408 1408 '''return default os-specific hgrc search path'''
1409 1409 path = system_rcpath()
1410 1410 path.extend(user_rcpath())
1411 1411 path = [os.path.normpath(f) for f in path]
1412 1412 return path
1413 1413
1414 1414 def rcpath():
1415 1415 '''return hgrc search path. if env var HGRCPATH is set, use it.
1416 1416 for each item in path, if directory, use files ending in .rc,
1417 1417 else use item.
1418 1418 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1419 1419 if no HGRCPATH, use default os-specific path.'''
1420 1420 global _rcpath
1421 1421 if _rcpath is None:
1422 1422 if 'HGRCPATH' in os.environ:
1423 1423 _rcpath = []
1424 1424 for p in os.environ['HGRCPATH'].split(os.pathsep):
1425 1425 if not p: continue
1426 1426 if os.path.isdir(p):
1427 1427 for f, kind in osutil.listdir(p):
1428 1428 if f.endswith('.rc'):
1429 1429 _rcpath.append(os.path.join(p, f))
1430 1430 else:
1431 1431 _rcpath.append(p)
1432 1432 else:
1433 1433 _rcpath = os_rcpath()
1434 1434 return _rcpath
1435 1435
1436 1436 def bytecount(nbytes):
1437 1437 '''return byte count formatted as readable string, with units'''
1438 1438
1439 1439 units = (
1440 1440 (100, 1<<30, _('%.0f GB')),
1441 1441 (10, 1<<30, _('%.1f GB')),
1442 1442 (1, 1<<30, _('%.2f GB')),
1443 1443 (100, 1<<20, _('%.0f MB')),
1444 1444 (10, 1<<20, _('%.1f MB')),
1445 1445 (1, 1<<20, _('%.2f MB')),
1446 1446 (100, 1<<10, _('%.0f KB')),
1447 1447 (10, 1<<10, _('%.1f KB')),
1448 1448 (1, 1<<10, _('%.2f KB')),
1449 1449 (1, 1, _('%.0f bytes')),
1450 1450 )
1451 1451
1452 1452 for multiplier, divisor, format in units:
1453 1453 if nbytes >= divisor * multiplier:
1454 1454 return format % (nbytes / float(divisor))
1455 1455 return units[-1][2] % nbytes
1456 1456
1457 1457 def drop_scheme(scheme, path):
1458 1458 sc = scheme + ':'
1459 1459 if path.startswith(sc):
1460 1460 path = path[len(sc):]
1461 1461 if path.startswith('//'):
1462 1462 path = path[2:]
1463 1463 return path
1464 1464
1465 1465 def uirepr(s):
1466 1466 # Avoid double backslash in Windows path repr()
1467 1467 return repr(s).replace('\\\\', '\\')
1468 1468
1469 1469 def termwidth():
1470 1470 if 'COLUMNS' in os.environ:
1471 1471 try:
1472 1472 return int(os.environ['COLUMNS'])
1473 1473 except ValueError:
1474 1474 pass
1475 1475 try:
1476 1476 import termios, array, fcntl
1477 1477 for dev in (sys.stdout, sys.stdin):
1478 1478 try:
1479 1479 fd = dev.fileno()
1480 1480 if not os.isatty(fd):
1481 1481 continue
1482 1482 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
1483 1483 return array.array('h', arri)[1]
1484 1484 except ValueError:
1485 1485 pass
1486 1486 except ImportError:
1487 1487 pass
1488 1488 return 80
1489 1489
1490 1490 def iterlines(iterator):
1491 1491 for chunk in iterator:
1492 1492 for line in chunk.splitlines():
1493 1493 yield line
General Comments 0
You need to be logged in to leave comments. Login now