##// END OF EJS Templates
util: move checkosfilename call from path_auditor to opener...
Adrian Buehlmann -
r13926:61ba09d8 default
parent child Browse files
Show More
@@ -1,1578 +1,1578
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 or any later version.
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 error, osutil, encoding
18 18 import errno, re, shutil, sys, tempfile, traceback
19 19 import os, stat, time, calendar, textwrap, unicodedata, signal
20 20 import imp, socket
21 21
22 22 # Python compatibility
23 23
24 24 def sha1(s):
25 25 return _fastsha1(s)
26 26
27 27 def _fastsha1(s):
28 28 # This function will import sha1 from hashlib or sha (whichever is
29 29 # available) and overwrite itself with it on the first call.
30 30 # Subsequent calls will go directly to the imported function.
31 31 if sys.version_info >= (2, 5):
32 32 from hashlib import sha1 as _sha1
33 33 else:
34 34 from sha import sha as _sha1
35 35 global _fastsha1, sha1
36 36 _fastsha1 = sha1 = _sha1
37 37 return _sha1(s)
38 38
39 39 import __builtin__
40 40
41 41 if sys.version_info[0] < 3:
42 42 def fakebuffer(sliceable, offset=0):
43 43 return sliceable[offset:]
44 44 else:
45 45 def fakebuffer(sliceable, offset=0):
46 46 return memoryview(sliceable)[offset:]
47 47 try:
48 48 buffer
49 49 except NameError:
50 50 __builtin__.buffer = fakebuffer
51 51
52 52 import subprocess
53 53 closefds = os.name == 'posix'
54 54
55 55 def popen2(cmd, env=None, newlines=False):
56 56 # Setting bufsize to -1 lets the system decide the buffer size.
57 57 # The default for bufsize is 0, meaning unbuffered. This leads to
58 58 # poor performance on Mac OS X: http://bugs.python.org/issue4194
59 59 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
60 60 close_fds=closefds,
61 61 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
62 62 universal_newlines=newlines,
63 63 env=env)
64 64 return p.stdin, p.stdout
65 65
66 66 def popen3(cmd, env=None, newlines=False):
67 67 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
68 68 close_fds=closefds,
69 69 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
70 70 stderr=subprocess.PIPE,
71 71 universal_newlines=newlines,
72 72 env=env)
73 73 return p.stdin, p.stdout, p.stderr
74 74
75 75 def version():
76 76 """Return version information if available."""
77 77 try:
78 78 import __version__
79 79 return __version__.version
80 80 except ImportError:
81 81 return 'unknown'
82 82
83 83 # used by parsedate
84 84 defaultdateformats = (
85 85 '%Y-%m-%d %H:%M:%S',
86 86 '%Y-%m-%d %I:%M:%S%p',
87 87 '%Y-%m-%d %H:%M',
88 88 '%Y-%m-%d %I:%M%p',
89 89 '%Y-%m-%d',
90 90 '%m-%d',
91 91 '%m/%d',
92 92 '%m/%d/%y',
93 93 '%m/%d/%Y',
94 94 '%a %b %d %H:%M:%S %Y',
95 95 '%a %b %d %I:%M:%S%p %Y',
96 96 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
97 97 '%b %d %H:%M:%S %Y',
98 98 '%b %d %I:%M:%S%p %Y',
99 99 '%b %d %H:%M:%S',
100 100 '%b %d %I:%M:%S%p',
101 101 '%b %d %H:%M',
102 102 '%b %d %I:%M%p',
103 103 '%b %d %Y',
104 104 '%b %d',
105 105 '%H:%M:%S',
106 106 '%I:%M:%S%p',
107 107 '%H:%M',
108 108 '%I:%M%p',
109 109 )
110 110
111 111 extendeddateformats = defaultdateformats + (
112 112 "%Y",
113 113 "%Y-%m",
114 114 "%b",
115 115 "%b %Y",
116 116 )
117 117
118 118 def cachefunc(func):
119 119 '''cache the result of function calls'''
120 120 # XXX doesn't handle keywords args
121 121 cache = {}
122 122 if func.func_code.co_argcount == 1:
123 123 # we gain a small amount of time because
124 124 # we don't need to pack/unpack the list
125 125 def f(arg):
126 126 if arg not in cache:
127 127 cache[arg] = func(arg)
128 128 return cache[arg]
129 129 else:
130 130 def f(*args):
131 131 if args not in cache:
132 132 cache[args] = func(*args)
133 133 return cache[args]
134 134
135 135 return f
136 136
137 137 def lrucachefunc(func):
138 138 '''cache most recent results of function calls'''
139 139 cache = {}
140 140 order = []
141 141 if func.func_code.co_argcount == 1:
142 142 def f(arg):
143 143 if arg not in cache:
144 144 if len(cache) > 20:
145 145 del cache[order.pop(0)]
146 146 cache[arg] = func(arg)
147 147 else:
148 148 order.remove(arg)
149 149 order.append(arg)
150 150 return cache[arg]
151 151 else:
152 152 def f(*args):
153 153 if args not in cache:
154 154 if len(cache) > 20:
155 155 del cache[order.pop(0)]
156 156 cache[args] = func(*args)
157 157 else:
158 158 order.remove(args)
159 159 order.append(args)
160 160 return cache[args]
161 161
162 162 return f
163 163
164 164 class propertycache(object):
165 165 def __init__(self, func):
166 166 self.func = func
167 167 self.name = func.__name__
168 168 def __get__(self, obj, type=None):
169 169 result = self.func(obj)
170 170 setattr(obj, self.name, result)
171 171 return result
172 172
173 173 def pipefilter(s, cmd):
174 174 '''filter string S through command CMD, returning its output'''
175 175 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
176 176 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
177 177 pout, perr = p.communicate(s)
178 178 return pout
179 179
180 180 def tempfilter(s, cmd):
181 181 '''filter string S through a pair of temporary files with CMD.
182 182 CMD is used as a template to create the real command to be run,
183 183 with the strings INFILE and OUTFILE replaced by the real names of
184 184 the temporary files generated.'''
185 185 inname, outname = None, None
186 186 try:
187 187 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
188 188 fp = os.fdopen(infd, 'wb')
189 189 fp.write(s)
190 190 fp.close()
191 191 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
192 192 os.close(outfd)
193 193 cmd = cmd.replace('INFILE', inname)
194 194 cmd = cmd.replace('OUTFILE', outname)
195 195 code = os.system(cmd)
196 196 if sys.platform == 'OpenVMS' and code & 1:
197 197 code = 0
198 198 if code:
199 199 raise Abort(_("command '%s' failed: %s") %
200 200 (cmd, explain_exit(code)))
201 201 fp = open(outname, 'rb')
202 202 r = fp.read()
203 203 fp.close()
204 204 return r
205 205 finally:
206 206 try:
207 207 if inname:
208 208 os.unlink(inname)
209 209 except:
210 210 pass
211 211 try:
212 212 if outname:
213 213 os.unlink(outname)
214 214 except:
215 215 pass
216 216
217 217 filtertable = {
218 218 'tempfile:': tempfilter,
219 219 'pipe:': pipefilter,
220 220 }
221 221
222 222 def filter(s, cmd):
223 223 "filter a string through a command that transforms its input to its output"
224 224 for name, fn in filtertable.iteritems():
225 225 if cmd.startswith(name):
226 226 return fn(s, cmd[len(name):].lstrip())
227 227 return pipefilter(s, cmd)
228 228
229 229 def binary(s):
230 230 """return true if a string is binary data"""
231 231 return bool(s and '\0' in s)
232 232
233 233 def increasingchunks(source, min=1024, max=65536):
234 234 '''return no less than min bytes per chunk while data remains,
235 235 doubling min after each chunk until it reaches max'''
236 236 def log2(x):
237 237 if not x:
238 238 return 0
239 239 i = 0
240 240 while x:
241 241 x >>= 1
242 242 i += 1
243 243 return i - 1
244 244
245 245 buf = []
246 246 blen = 0
247 247 for chunk in source:
248 248 buf.append(chunk)
249 249 blen += len(chunk)
250 250 if blen >= min:
251 251 if min < max:
252 252 min = min << 1
253 253 nmin = 1 << log2(blen)
254 254 if nmin > min:
255 255 min = nmin
256 256 if min > max:
257 257 min = max
258 258 yield ''.join(buf)
259 259 blen = 0
260 260 buf = []
261 261 if buf:
262 262 yield ''.join(buf)
263 263
264 264 Abort = error.Abort
265 265
266 266 def always(fn):
267 267 return True
268 268
269 269 def never(fn):
270 270 return False
271 271
272 272 def pathto(root, n1, n2):
273 273 '''return the relative path from one place to another.
274 274 root should use os.sep to separate directories
275 275 n1 should use os.sep to separate directories
276 276 n2 should use "/" to separate directories
277 277 returns an os.sep-separated path.
278 278
279 279 If n1 is a relative path, it's assumed it's
280 280 relative to root.
281 281 n2 should always be relative to root.
282 282 '''
283 283 if not n1:
284 284 return localpath(n2)
285 285 if os.path.isabs(n1):
286 286 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
287 287 return os.path.join(root, localpath(n2))
288 288 n2 = '/'.join((pconvert(root), n2))
289 289 a, b = splitpath(n1), n2.split('/')
290 290 a.reverse()
291 291 b.reverse()
292 292 while a and b and a[-1] == b[-1]:
293 293 a.pop()
294 294 b.pop()
295 295 b.reverse()
296 296 return os.sep.join((['..'] * len(a)) + b) or '.'
297 297
298 298 def canonpath(root, cwd, myname, auditor=None):
299 299 """return the canonical path of myname, given cwd and root"""
300 300 if endswithsep(root):
301 301 rootsep = root
302 302 else:
303 303 rootsep = root + os.sep
304 304 name = myname
305 305 if not os.path.isabs(name):
306 306 name = os.path.join(root, cwd, name)
307 307 name = os.path.normpath(name)
308 308 if auditor is None:
309 309 auditor = path_auditor(root)
310 310 if name != rootsep and name.startswith(rootsep):
311 311 name = name[len(rootsep):]
312 312 auditor(name)
313 313 return pconvert(name)
314 314 elif name == root:
315 315 return ''
316 316 else:
317 317 # Determine whether `name' is in the hierarchy at or beneath `root',
318 318 # by iterating name=dirname(name) until that causes no change (can't
319 319 # check name == '/', because that doesn't work on windows). For each
320 320 # `name', compare dev/inode numbers. If they match, the list `rel'
321 321 # holds the reversed list of components making up the relative file
322 322 # name we want.
323 323 root_st = os.stat(root)
324 324 rel = []
325 325 while True:
326 326 try:
327 327 name_st = os.stat(name)
328 328 except OSError:
329 329 break
330 330 if samestat(name_st, root_st):
331 331 if not rel:
332 332 # name was actually the same as root (maybe a symlink)
333 333 return ''
334 334 rel.reverse()
335 335 name = os.path.join(*rel)
336 336 auditor(name)
337 337 return pconvert(name)
338 338 dirname, basename = os.path.split(name)
339 339 rel.append(basename)
340 340 if dirname == name:
341 341 break
342 342 name = dirname
343 343
344 344 raise Abort('%s not under root' % myname)
345 345
346 346 _hgexecutable = None
347 347
348 348 def main_is_frozen():
349 349 """return True if we are a frozen executable.
350 350
351 351 The code supports py2exe (most common, Windows only) and tools/freeze
352 352 (portable, not much used).
353 353 """
354 354 return (hasattr(sys, "frozen") or # new py2exe
355 355 hasattr(sys, "importers") or # old py2exe
356 356 imp.is_frozen("__main__")) # tools/freeze
357 357
358 358 def hgexecutable():
359 359 """return location of the 'hg' executable.
360 360
361 361 Defaults to $HG or 'hg' in the search path.
362 362 """
363 363 if _hgexecutable is None:
364 364 hg = os.environ.get('HG')
365 365 if hg:
366 366 set_hgexecutable(hg)
367 367 elif main_is_frozen():
368 368 set_hgexecutable(sys.executable)
369 369 else:
370 370 exe = find_exe('hg') or os.path.basename(sys.argv[0])
371 371 set_hgexecutable(exe)
372 372 return _hgexecutable
373 373
374 374 def set_hgexecutable(path):
375 375 """set location of the 'hg' executable"""
376 376 global _hgexecutable
377 377 _hgexecutable = path
378 378
379 379 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
380 380 '''enhanced shell command execution.
381 381 run with environment maybe modified, maybe in different dir.
382 382
383 383 if command fails and onerr is None, return status. if ui object,
384 384 print error message and return status, else raise onerr object as
385 385 exception.
386 386
387 387 if out is specified, it is assumed to be a file-like object that has a
388 388 write() method. stdout and stderr will be redirected to out.'''
389 389 try:
390 390 sys.stdout.flush()
391 391 except Exception:
392 392 pass
393 393 def py2shell(val):
394 394 'convert python object into string that is useful to shell'
395 395 if val is None or val is False:
396 396 return '0'
397 397 if val is True:
398 398 return '1'
399 399 return str(val)
400 400 origcmd = cmd
401 401 cmd = quotecommand(cmd)
402 402 env = dict(os.environ)
403 403 env.update((k, py2shell(v)) for k, v in environ.iteritems())
404 404 env['HG'] = hgexecutable()
405 405 if out is None:
406 406 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
407 407 env=env, cwd=cwd)
408 408 else:
409 409 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
410 410 env=env, cwd=cwd, stdout=subprocess.PIPE,
411 411 stderr=subprocess.STDOUT)
412 412 for line in proc.stdout:
413 413 out.write(line)
414 414 proc.wait()
415 415 rc = proc.returncode
416 416 if sys.platform == 'OpenVMS' and rc & 1:
417 417 rc = 0
418 418 if rc and onerr:
419 419 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
420 420 explain_exit(rc)[0])
421 421 if errprefix:
422 422 errmsg = '%s: %s' % (errprefix, errmsg)
423 423 try:
424 424 onerr.warn(errmsg + '\n')
425 425 except AttributeError:
426 426 raise onerr(errmsg)
427 427 return rc
428 428
429 429 def checksignature(func):
430 430 '''wrap a function with code to check for calling errors'''
431 431 def check(*args, **kwargs):
432 432 try:
433 433 return func(*args, **kwargs)
434 434 except TypeError:
435 435 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
436 436 raise error.SignatureError
437 437 raise
438 438
439 439 return check
440 440
441 441 def makedir(path, notindexed):
442 442 os.mkdir(path)
443 443
444 444 def unlinkpath(f):
445 445 """unlink and remove the directory if it is empty"""
446 446 os.unlink(f)
447 447 # try removing directories that might now be empty
448 448 try:
449 449 os.removedirs(os.path.dirname(f))
450 450 except OSError:
451 451 pass
452 452
453 453 def copyfile(src, dest):
454 454 "copy a file, preserving mode and atime/mtime"
455 455 if os.path.islink(src):
456 456 try:
457 457 os.unlink(dest)
458 458 except:
459 459 pass
460 460 os.symlink(os.readlink(src), dest)
461 461 else:
462 462 try:
463 463 shutil.copyfile(src, dest)
464 464 shutil.copymode(src, dest)
465 465 except shutil.Error, inst:
466 466 raise Abort(str(inst))
467 467
468 468 def copyfiles(src, dst, hardlink=None):
469 469 """Copy a directory tree using hardlinks if possible"""
470 470
471 471 if hardlink is None:
472 472 hardlink = (os.stat(src).st_dev ==
473 473 os.stat(os.path.dirname(dst)).st_dev)
474 474
475 475 num = 0
476 476 if os.path.isdir(src):
477 477 os.mkdir(dst)
478 478 for name, kind in osutil.listdir(src):
479 479 srcname = os.path.join(src, name)
480 480 dstname = os.path.join(dst, name)
481 481 hardlink, n = copyfiles(srcname, dstname, hardlink)
482 482 num += n
483 483 else:
484 484 if hardlink:
485 485 try:
486 486 os_link(src, dst)
487 487 except (IOError, OSError):
488 488 hardlink = False
489 489 shutil.copy(src, dst)
490 490 else:
491 491 shutil.copy(src, dst)
492 492 num += 1
493 493
494 494 return hardlink, num
495 495
496 496 _windows_reserved_filenames = '''con prn aux nul
497 497 com1 com2 com3 com4 com5 com6 com7 com8 com9
498 498 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
499 499 _windows_reserved_chars = ':*?"<>|'
500 500 def checkwinfilename(path):
501 501 '''Check that the base-relative path is a valid filename on Windows.
502 502 Returns None if the path is ok, or a UI string describing the problem.
503 503
504 504 >>> checkwinfilename("just/a/normal/path")
505 505 >>> checkwinfilename("foo/bar/con.xml")
506 506 "filename contains 'con', which is reserved on Windows"
507 507 >>> checkwinfilename("foo/con.xml/bar")
508 508 "filename contains 'con', which is reserved on Windows"
509 509 >>> checkwinfilename("foo/bar/xml.con")
510 510 >>> checkwinfilename("foo/bar/AUX/bla.txt")
511 511 "filename contains 'AUX', which is reserved on Windows"
512 512 >>> checkwinfilename("foo/bar/bla:.txt")
513 513 "filename contains ':', which is reserved on Windows"
514 514 >>> checkwinfilename("foo/bar/b\07la.txt")
515 515 "filename contains '\\x07', which is invalid on Windows"
516 516 >>> checkwinfilename("foo/bar/bla ")
517 517 "filename ends with ' ', which is not allowed on Windows"
518 518 '''
519 519 for n in path.replace('\\', '/').split('/'):
520 520 if not n:
521 521 continue
522 522 for c in n:
523 523 if c in _windows_reserved_chars:
524 524 return _("filename contains '%s', which is reserved "
525 525 "on Windows") % c
526 526 if ord(c) <= 31:
527 527 return _("filename contains '%s', which is invalid "
528 528 "on Windows") % c
529 529 base = n.split('.')[0]
530 530 if base and base.lower() in _windows_reserved_filenames:
531 531 return _("filename contains '%s', which is reserved "
532 532 "on Windows") % base
533 533 t = n[-1]
534 534 if t in '. ':
535 535 return _("filename ends with '%s', which is not allowed "
536 536 "on Windows") % t
537 537
538 538 class path_auditor(object):
539 539 '''ensure that a filesystem path contains no banned components.
540 540 the following properties of a path are checked:
541 541
542 542 - ends with a directory separator
543 543 - under top-level .hg
544 544 - starts at the root of a windows drive
545 545 - contains ".."
546 546 - traverses a symlink (e.g. a/symlink_here/b)
547 547 - inside a nested repository (a callback can be used to approve
548 548 some nested repositories, e.g., subrepositories)
549 549 '''
550 550
551 551 def __init__(self, root, callback=None):
552 552 self.audited = set()
553 553 self.auditeddir = set()
554 554 self.root = root
555 555 self.callback = callback
556 556
557 557 def __call__(self, path):
558 558 if path in self.audited:
559 559 return
560 560 # AIX ignores "/" at end of path, others raise EISDIR.
561 561 if endswithsep(path):
562 562 raise Abort(_("path ends in directory separator: %s") % path)
563 563 normpath = os.path.normcase(path)
564 564 parts = splitpath(normpath)
565 565 if (os.path.splitdrive(path)[0]
566 566 or parts[0].lower() in ('.hg', '.hg.', '')
567 567 or os.pardir in parts):
568 568 raise Abort(_("path contains illegal component: %s") % path)
569 569 if '.hg' in path.lower():
570 570 lparts = [p.lower() for p in parts]
571 571 for p in '.hg', '.hg.':
572 572 if p in lparts[1:]:
573 573 pos = lparts.index(p)
574 574 base = os.path.join(*parts[:pos])
575 575 raise Abort(_('path %r is inside nested repo %r')
576 576 % (path, base))
577 577 def check(prefix):
578 578 curpath = os.path.join(self.root, prefix)
579 579 try:
580 580 st = os.lstat(curpath)
581 581 except OSError, err:
582 582 # EINVAL can be raised as invalid path syntax under win32.
583 583 # They must be ignored for patterns can be checked too.
584 584 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
585 585 raise
586 586 else:
587 587 if stat.S_ISLNK(st.st_mode):
588 588 raise Abort(_('path %r traverses symbolic link %r') %
589 589 (path, prefix))
590 590 elif (stat.S_ISDIR(st.st_mode) and
591 591 os.path.isdir(os.path.join(curpath, '.hg'))):
592 592 if not self.callback or not self.callback(curpath):
593 593 raise Abort(_('path %r is inside nested repo %r') %
594 594 (path, prefix))
595 595 parts.pop()
596 596 prefixes = []
597 597 while parts:
598 598 prefix = os.sep.join(parts)
599 599 if prefix in self.auditeddir:
600 600 break
601 601 check(prefix)
602 602 prefixes.append(prefix)
603 603 parts.pop()
604 604
605 r = checkosfilename(path)
606 if r:
607 raise Abort("%s: %s" % (r, path))
608 605 self.audited.add(path)
609 606 # only add prefixes to the cache after checking everything: we don't
610 607 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
611 608 self.auditeddir.update(prefixes)
612 609
613 610 def lookup_reg(key, name=None, scope=None):
614 611 return None
615 612
616 613 def hidewindow():
617 614 """Hide current shell window.
618 615
619 616 Used to hide the window opened when starting asynchronous
620 617 child process under Windows, unneeded on other systems.
621 618 """
622 619 pass
623 620
624 621 if os.name == 'nt':
625 622 checkosfilename = checkwinfilename
626 623 from windows import *
627 624 else:
628 625 from posix import *
629 626
630 627 def makelock(info, pathname):
631 628 try:
632 629 return os.symlink(info, pathname)
633 630 except OSError, why:
634 631 if why.errno == errno.EEXIST:
635 632 raise
636 633 except AttributeError: # no symlink in os
637 634 pass
638 635
639 636 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
640 637 os.write(ld, info)
641 638 os.close(ld)
642 639
643 640 def readlock(pathname):
644 641 try:
645 642 return os.readlink(pathname)
646 643 except OSError, why:
647 644 if why.errno not in (errno.EINVAL, errno.ENOSYS):
648 645 raise
649 646 except AttributeError: # no symlink in os
650 647 pass
651 648 fp = posixfile(pathname)
652 649 r = fp.read()
653 650 fp.close()
654 651 return r
655 652
656 653 def fstat(fp):
657 654 '''stat file object that may not have fileno method.'''
658 655 try:
659 656 return os.fstat(fp.fileno())
660 657 except AttributeError:
661 658 return os.stat(fp.name)
662 659
663 660 # File system features
664 661
665 662 def checkcase(path):
666 663 """
667 664 Check whether the given path is on a case-sensitive filesystem
668 665
669 666 Requires a path (like /foo/.hg) ending with a foldable final
670 667 directory component.
671 668 """
672 669 s1 = os.stat(path)
673 670 d, b = os.path.split(path)
674 671 p2 = os.path.join(d, b.upper())
675 672 if path == p2:
676 673 p2 = os.path.join(d, b.lower())
677 674 try:
678 675 s2 = os.stat(p2)
679 676 if s2 == s1:
680 677 return False
681 678 return True
682 679 except:
683 680 return True
684 681
685 682 _fspathcache = {}
686 683 def fspath(name, root):
687 684 '''Get name in the case stored in the filesystem
688 685
689 686 The name is either relative to root, or it is an absolute path starting
690 687 with root. Note that this function is unnecessary, and should not be
691 688 called, for case-sensitive filesystems (simply because it's expensive).
692 689 '''
693 690 # If name is absolute, make it relative
694 691 if name.lower().startswith(root.lower()):
695 692 l = len(root)
696 693 if name[l] == os.sep or name[l] == os.altsep:
697 694 l = l + 1
698 695 name = name[l:]
699 696
700 697 if not os.path.lexists(os.path.join(root, name)):
701 698 return None
702 699
703 700 seps = os.sep
704 701 if os.altsep:
705 702 seps = seps + os.altsep
706 703 # Protect backslashes. This gets silly very quickly.
707 704 seps.replace('\\','\\\\')
708 705 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
709 706 dir = os.path.normcase(os.path.normpath(root))
710 707 result = []
711 708 for part, sep in pattern.findall(name):
712 709 if sep:
713 710 result.append(sep)
714 711 continue
715 712
716 713 if dir not in _fspathcache:
717 714 _fspathcache[dir] = os.listdir(dir)
718 715 contents = _fspathcache[dir]
719 716
720 717 lpart = part.lower()
721 718 lenp = len(part)
722 719 for n in contents:
723 720 if lenp == len(n) and n.lower() == lpart:
724 721 result.append(n)
725 722 break
726 723 else:
727 724 # Cannot happen, as the file exists!
728 725 result.append(part)
729 726 dir = os.path.join(dir, lpart)
730 727
731 728 return ''.join(result)
732 729
733 730 def checknlink(testfile):
734 731 '''check whether hardlink count reporting works properly'''
735 732
736 733 # testfile may be open, so we need a separate file for checking to
737 734 # work around issue2543 (or testfile may get lost on Samba shares)
738 735 f1 = testfile + ".hgtmp1"
739 736 if os.path.lexists(f1):
740 737 return False
741 738 try:
742 739 posixfile(f1, 'w').close()
743 740 except IOError:
744 741 return False
745 742
746 743 f2 = testfile + ".hgtmp2"
747 744 fd = None
748 745 try:
749 746 try:
750 747 os_link(f1, f2)
751 748 except OSError:
752 749 return False
753 750
754 751 # nlinks() may behave differently for files on Windows shares if
755 752 # the file is open.
756 753 fd = posixfile(f2)
757 754 return nlinks(f2) > 1
758 755 finally:
759 756 if fd is not None:
760 757 fd.close()
761 758 for f in (f1, f2):
762 759 try:
763 760 os.unlink(f)
764 761 except OSError:
765 762 pass
766 763
767 764 return False
768 765
769 766 def endswithsep(path):
770 767 '''Check path ends with os.sep or os.altsep.'''
771 768 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
772 769
773 770 def splitpath(path):
774 771 '''Split path by os.sep.
775 772 Note that this function does not use os.altsep because this is
776 773 an alternative of simple "xxx.split(os.sep)".
777 774 It is recommended to use os.path.normpath() before using this
778 775 function if need.'''
779 776 return path.split(os.sep)
780 777
781 778 def gui():
782 779 '''Are we running in a GUI?'''
783 780 if sys.platform == 'darwin':
784 781 if 'SSH_CONNECTION' in os.environ:
785 782 # handle SSH access to a box where the user is logged in
786 783 return False
787 784 elif getattr(osutil, 'isgui', None):
788 785 # check if a CoreGraphics session is available
789 786 return osutil.isgui()
790 787 else:
791 788 # pure build; use a safe default
792 789 return True
793 790 else:
794 791 return os.name == "nt" or os.environ.get("DISPLAY")
795 792
796 793 def mktempcopy(name, emptyok=False, createmode=None):
797 794 """Create a temporary file with the same contents from name
798 795
799 796 The permission bits are copied from the original file.
800 797
801 798 If the temporary file is going to be truncated immediately, you
802 799 can use emptyok=True as an optimization.
803 800
804 801 Returns the name of the temporary file.
805 802 """
806 803 d, fn = os.path.split(name)
807 804 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
808 805 os.close(fd)
809 806 # Temporary files are created with mode 0600, which is usually not
810 807 # what we want. If the original file already exists, just copy
811 808 # its mode. Otherwise, manually obey umask.
812 809 try:
813 810 st_mode = os.lstat(name).st_mode & 0777
814 811 except OSError, inst:
815 812 if inst.errno != errno.ENOENT:
816 813 raise
817 814 st_mode = createmode
818 815 if st_mode is None:
819 816 st_mode = ~umask
820 817 st_mode &= 0666
821 818 os.chmod(temp, st_mode)
822 819 if emptyok:
823 820 return temp
824 821 try:
825 822 try:
826 823 ifp = posixfile(name, "rb")
827 824 except IOError, inst:
828 825 if inst.errno == errno.ENOENT:
829 826 return temp
830 827 if not getattr(inst, 'filename', None):
831 828 inst.filename = name
832 829 raise
833 830 ofp = posixfile(temp, "wb")
834 831 for chunk in filechunkiter(ifp):
835 832 ofp.write(chunk)
836 833 ifp.close()
837 834 ofp.close()
838 835 except:
839 836 try: os.unlink(temp)
840 837 except: pass
841 838 raise
842 839 return temp
843 840
844 841 class atomictempfile(object):
845 842 """file-like object that atomically updates a file
846 843
847 844 All writes will be redirected to a temporary copy of the original
848 845 file. When rename is called, the copy is renamed to the original
849 846 name, making the changes visible.
850 847 """
851 848 def __init__(self, name, mode='w+b', createmode=None):
852 849 self.__name = name
853 850 self._fp = None
854 851 self.temp = mktempcopy(name, emptyok=('w' in mode),
855 852 createmode=createmode)
856 853 self._fp = posixfile(self.temp, mode)
857 854
858 855 def __getattr__(self, name):
859 856 return getattr(self._fp, name)
860 857
861 858 def rename(self):
862 859 if not self._fp.closed:
863 860 self._fp.close()
864 861 rename(self.temp, localpath(self.__name))
865 862
866 863 def close(self):
867 864 if not self._fp:
868 865 return
869 866 if not self._fp.closed:
870 867 try:
871 868 os.unlink(self.temp)
872 869 except: pass
873 870 self._fp.close()
874 871
875 872 def __del__(self):
876 873 self.close()
877 874
878 875 def makedirs(name, mode=None):
879 876 """recursive directory creation with parent mode inheritance"""
880 877 parent = os.path.abspath(os.path.dirname(name))
881 878 try:
882 879 os.mkdir(name)
883 880 if mode is not None:
884 881 os.chmod(name, mode)
885 882 return
886 883 except OSError, err:
887 884 if err.errno == errno.EEXIST:
888 885 return
889 886 if not name or parent == name or err.errno != errno.ENOENT:
890 887 raise
891 888 makedirs(parent, mode)
892 889 makedirs(name, mode)
893 890
894 891 class opener(object):
895 892 """Open files relative to a base directory
896 893
897 894 This class is used to hide the details of COW semantics and
898 895 remote file access from higher level code.
899 896 """
900 897 def __init__(self, base, audit=True):
901 898 self.base = base
902 899 if audit:
903 900 self.auditor = path_auditor(base)
904 901 else:
905 902 self.auditor = always
906 903 self.createmode = None
907 904 self._trustnlink = None
908 905
909 906 @propertycache
910 907 def _can_symlink(self):
911 908 return checklink(self.base)
912 909
913 910 def _fixfilemode(self, name):
914 911 if self.createmode is None:
915 912 return
916 913 os.chmod(name, self.createmode & 0666)
917 914
918 915 def __call__(self, path, mode="r", text=False, atomictemp=False):
916 r = checkosfilename(path)
917 if r:
918 raise Abort("%s: %s" % (r, path))
919 919 self.auditor(path)
920 920 f = os.path.join(self.base, path)
921 921
922 922 if not text and "b" not in mode:
923 923 mode += "b" # for that other OS
924 924
925 925 nlink = -1
926 926 dirname, basename = os.path.split(f)
927 927 # If basename is empty, then the path is malformed because it points
928 928 # to a directory. Let the posixfile() call below raise IOError.
929 929 if basename and mode not in ('r', 'rb'):
930 930 if atomictemp:
931 931 if not os.path.isdir(dirname):
932 932 makedirs(dirname, self.createmode)
933 933 return atomictempfile(f, mode, self.createmode)
934 934 try:
935 935 if 'w' in mode:
936 936 unlink(f)
937 937 nlink = 0
938 938 else:
939 939 # nlinks() may behave differently for files on Windows
940 940 # shares if the file is open.
941 941 fd = posixfile(f)
942 942 nlink = nlinks(f)
943 943 if nlink < 1:
944 944 nlink = 2 # force mktempcopy (issue1922)
945 945 fd.close()
946 946 except (OSError, IOError), e:
947 947 if e.errno != errno.ENOENT:
948 948 raise
949 949 nlink = 0
950 950 if not os.path.isdir(dirname):
951 951 makedirs(dirname, self.createmode)
952 952 if nlink > 0:
953 953 if self._trustnlink is None:
954 954 self._trustnlink = nlink > 1 or checknlink(f)
955 955 if nlink > 1 or not self._trustnlink:
956 956 rename(mktempcopy(f), f)
957 957 fp = posixfile(f, mode)
958 958 if nlink == 0:
959 959 self._fixfilemode(f)
960 960 return fp
961 961
962 962 def symlink(self, src, dst):
963 963 self.auditor(dst)
964 964 linkname = os.path.join(self.base, dst)
965 965 try:
966 966 os.unlink(linkname)
967 967 except OSError:
968 968 pass
969 969
970 970 dirname = os.path.dirname(linkname)
971 971 if not os.path.exists(dirname):
972 972 makedirs(dirname, self.createmode)
973 973
974 974 if self._can_symlink:
975 975 try:
976 976 os.symlink(src, linkname)
977 977 except OSError, err:
978 978 raise OSError(err.errno, _('could not symlink to %r: %s') %
979 979 (src, err.strerror), linkname)
980 980 else:
981 981 f = self(dst, "w")
982 982 f.write(src)
983 983 f.close()
984 984 self._fixfilemode(dst)
985 985
986 986 class chunkbuffer(object):
987 987 """Allow arbitrary sized chunks of data to be efficiently read from an
988 988 iterator over chunks of arbitrary size."""
989 989
990 990 def __init__(self, in_iter):
991 991 """in_iter is the iterator that's iterating over the input chunks.
992 992 targetsize is how big a buffer to try to maintain."""
993 993 def splitbig(chunks):
994 994 for chunk in chunks:
995 995 if len(chunk) > 2**20:
996 996 pos = 0
997 997 while pos < len(chunk):
998 998 end = pos + 2 ** 18
999 999 yield chunk[pos:end]
1000 1000 pos = end
1001 1001 else:
1002 1002 yield chunk
1003 1003 self.iter = splitbig(in_iter)
1004 1004 self._queue = []
1005 1005
1006 1006 def read(self, l):
1007 1007 """Read L bytes of data from the iterator of chunks of data.
1008 1008 Returns less than L bytes if the iterator runs dry."""
1009 1009 left = l
1010 1010 buf = ''
1011 1011 queue = self._queue
1012 1012 while left > 0:
1013 1013 # refill the queue
1014 1014 if not queue:
1015 1015 target = 2**18
1016 1016 for chunk in self.iter:
1017 1017 queue.append(chunk)
1018 1018 target -= len(chunk)
1019 1019 if target <= 0:
1020 1020 break
1021 1021 if not queue:
1022 1022 break
1023 1023
1024 1024 chunk = queue.pop(0)
1025 1025 left -= len(chunk)
1026 1026 if left < 0:
1027 1027 queue.insert(0, chunk[left:])
1028 1028 buf += chunk[:left]
1029 1029 else:
1030 1030 buf += chunk
1031 1031
1032 1032 return buf
1033 1033
1034 1034 def filechunkiter(f, size=65536, limit=None):
1035 1035 """Create a generator that produces the data in the file size
1036 1036 (default 65536) bytes at a time, up to optional limit (default is
1037 1037 to read all data). Chunks may be less than size bytes if the
1038 1038 chunk is the last chunk in the file, or the file is a socket or
1039 1039 some other type of file that sometimes reads less data than is
1040 1040 requested."""
1041 1041 assert size >= 0
1042 1042 assert limit is None or limit >= 0
1043 1043 while True:
1044 1044 if limit is None:
1045 1045 nbytes = size
1046 1046 else:
1047 1047 nbytes = min(limit, size)
1048 1048 s = nbytes and f.read(nbytes)
1049 1049 if not s:
1050 1050 break
1051 1051 if limit:
1052 1052 limit -= len(s)
1053 1053 yield s
1054 1054
1055 1055 def makedate():
1056 1056 lt = time.localtime()
1057 1057 if lt[8] == 1 and time.daylight:
1058 1058 tz = time.altzone
1059 1059 else:
1060 1060 tz = time.timezone
1061 1061 t = time.mktime(lt)
1062 1062 if t < 0:
1063 1063 hint = _("check your clock")
1064 1064 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1065 1065 return t, tz
1066 1066
1067 1067 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1068 1068 """represent a (unixtime, offset) tuple as a localized time.
1069 1069 unixtime is seconds since the epoch, and offset is the time zone's
1070 1070 number of seconds away from UTC. if timezone is false, do not
1071 1071 append time zone to string."""
1072 1072 t, tz = date or makedate()
1073 1073 if t < 0:
1074 1074 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1075 1075 tz = 0
1076 1076 if "%1" in format or "%2" in format:
1077 1077 sign = (tz > 0) and "-" or "+"
1078 1078 minutes = abs(tz) // 60
1079 1079 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1080 1080 format = format.replace("%2", "%02d" % (minutes % 60))
1081 1081 s = time.strftime(format, time.gmtime(float(t) - tz))
1082 1082 return s
1083 1083
1084 1084 def shortdate(date=None):
1085 1085 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1086 1086 return datestr(date, format='%Y-%m-%d')
1087 1087
1088 1088 def strdate(string, format, defaults=[]):
1089 1089 """parse a localized time string and return a (unixtime, offset) tuple.
1090 1090 if the string cannot be parsed, ValueError is raised."""
1091 1091 def timezone(string):
1092 1092 tz = string.split()[-1]
1093 1093 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1094 1094 sign = (tz[0] == "+") and 1 or -1
1095 1095 hours = int(tz[1:3])
1096 1096 minutes = int(tz[3:5])
1097 1097 return -sign * (hours * 60 + minutes) * 60
1098 1098 if tz == "GMT" or tz == "UTC":
1099 1099 return 0
1100 1100 return None
1101 1101
1102 1102 # NOTE: unixtime = localunixtime + offset
1103 1103 offset, date = timezone(string), string
1104 1104 if offset is not None:
1105 1105 date = " ".join(string.split()[:-1])
1106 1106
1107 1107 # add missing elements from defaults
1108 1108 usenow = False # default to using biased defaults
1109 1109 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1110 1110 found = [True for p in part if ("%"+p) in format]
1111 1111 if not found:
1112 1112 date += "@" + defaults[part][usenow]
1113 1113 format += "@%" + part[0]
1114 1114 else:
1115 1115 # We've found a specific time element, less specific time
1116 1116 # elements are relative to today
1117 1117 usenow = True
1118 1118
1119 1119 timetuple = time.strptime(date, format)
1120 1120 localunixtime = int(calendar.timegm(timetuple))
1121 1121 if offset is None:
1122 1122 # local timezone
1123 1123 unixtime = int(time.mktime(timetuple))
1124 1124 offset = unixtime - localunixtime
1125 1125 else:
1126 1126 unixtime = localunixtime + offset
1127 1127 return unixtime, offset
1128 1128
1129 1129 def parsedate(date, formats=None, bias={}):
1130 1130 """parse a localized date/time and return a (unixtime, offset) tuple.
1131 1131
1132 1132 The date may be a "unixtime offset" string or in one of the specified
1133 1133 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1134 1134 """
1135 1135 if not date:
1136 1136 return 0, 0
1137 1137 if isinstance(date, tuple) and len(date) == 2:
1138 1138 return date
1139 1139 if not formats:
1140 1140 formats = defaultdateformats
1141 1141 date = date.strip()
1142 1142 try:
1143 1143 when, offset = map(int, date.split(' '))
1144 1144 except ValueError:
1145 1145 # fill out defaults
1146 1146 now = makedate()
1147 1147 defaults = {}
1148 1148 nowmap = {}
1149 1149 for part in ("d", "mb", "yY", "HI", "M", "S"):
1150 1150 # this piece is for rounding the specific end of unknowns
1151 1151 b = bias.get(part)
1152 1152 if b is None:
1153 1153 if part[0] in "HMS":
1154 1154 b = "00"
1155 1155 else:
1156 1156 b = "0"
1157 1157
1158 1158 # this piece is for matching the generic end to today's date
1159 1159 n = datestr(now, "%" + part[0])
1160 1160
1161 1161 defaults[part] = (b, n)
1162 1162
1163 1163 for format in formats:
1164 1164 try:
1165 1165 when, offset = strdate(date, format, defaults)
1166 1166 except (ValueError, OverflowError):
1167 1167 pass
1168 1168 else:
1169 1169 break
1170 1170 else:
1171 1171 raise Abort(_('invalid date: %r') % date)
1172 1172 # validate explicit (probably user-specified) date and
1173 1173 # time zone offset. values must fit in signed 32 bits for
1174 1174 # current 32-bit linux runtimes. timezones go from UTC-12
1175 1175 # to UTC+14
1176 1176 if abs(when) > 0x7fffffff:
1177 1177 raise Abort(_('date exceeds 32 bits: %d') % when)
1178 1178 if when < 0:
1179 1179 raise Abort(_('negative date value: %d') % when)
1180 1180 if offset < -50400 or offset > 43200:
1181 1181 raise Abort(_('impossible time zone offset: %d') % offset)
1182 1182 return when, offset
1183 1183
1184 1184 def matchdate(date):
1185 1185 """Return a function that matches a given date match specifier
1186 1186
1187 1187 Formats include:
1188 1188
1189 1189 '{date}' match a given date to the accuracy provided
1190 1190
1191 1191 '<{date}' on or before a given date
1192 1192
1193 1193 '>{date}' on or after a given date
1194 1194
1195 1195 >>> p1 = parsedate("10:29:59")
1196 1196 >>> p2 = parsedate("10:30:00")
1197 1197 >>> p3 = parsedate("10:30:59")
1198 1198 >>> p4 = parsedate("10:31:00")
1199 1199 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1200 1200 >>> f = matchdate("10:30")
1201 1201 >>> f(p1[0])
1202 1202 False
1203 1203 >>> f(p2[0])
1204 1204 True
1205 1205 >>> f(p3[0])
1206 1206 True
1207 1207 >>> f(p4[0])
1208 1208 False
1209 1209 >>> f(p5[0])
1210 1210 False
1211 1211 """
1212 1212
1213 1213 def lower(date):
1214 1214 d = dict(mb="1", d="1")
1215 1215 return parsedate(date, extendeddateformats, d)[0]
1216 1216
1217 1217 def upper(date):
1218 1218 d = dict(mb="12", HI="23", M="59", S="59")
1219 1219 for days in ("31", "30", "29"):
1220 1220 try:
1221 1221 d["d"] = days
1222 1222 return parsedate(date, extendeddateformats, d)[0]
1223 1223 except:
1224 1224 pass
1225 1225 d["d"] = "28"
1226 1226 return parsedate(date, extendeddateformats, d)[0]
1227 1227
1228 1228 date = date.strip()
1229 1229
1230 1230 if not date:
1231 1231 raise Abort(_("dates cannot consist entirely of whitespace"))
1232 1232 elif date[0] == "<":
1233 1233 if not date[1:]:
1234 1234 raise Abort(_("invalid day spec, use '<DATE'"))
1235 1235 when = upper(date[1:])
1236 1236 return lambda x: x <= when
1237 1237 elif date[0] == ">":
1238 1238 if not date[1:]:
1239 1239 raise Abort(_("invalid day spec, use '>DATE'"))
1240 1240 when = lower(date[1:])
1241 1241 return lambda x: x >= when
1242 1242 elif date[0] == "-":
1243 1243 try:
1244 1244 days = int(date[1:])
1245 1245 except ValueError:
1246 1246 raise Abort(_("invalid day spec: %s") % date[1:])
1247 1247 if days < 0:
1248 1248 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1249 1249 % date[1:])
1250 1250 when = makedate()[0] - days * 3600 * 24
1251 1251 return lambda x: x >= when
1252 1252 elif " to " in date:
1253 1253 a, b = date.split(" to ")
1254 1254 start, stop = lower(a), upper(b)
1255 1255 return lambda x: x >= start and x <= stop
1256 1256 else:
1257 1257 start, stop = lower(date), upper(date)
1258 1258 return lambda x: x >= start and x <= stop
1259 1259
1260 1260 def shortuser(user):
1261 1261 """Return a short representation of a user name or email address."""
1262 1262 f = user.find('@')
1263 1263 if f >= 0:
1264 1264 user = user[:f]
1265 1265 f = user.find('<')
1266 1266 if f >= 0:
1267 1267 user = user[f + 1:]
1268 1268 f = user.find(' ')
1269 1269 if f >= 0:
1270 1270 user = user[:f]
1271 1271 f = user.find('.')
1272 1272 if f >= 0:
1273 1273 user = user[:f]
1274 1274 return user
1275 1275
1276 1276 def email(author):
1277 1277 '''get email of author.'''
1278 1278 r = author.find('>')
1279 1279 if r == -1:
1280 1280 r = None
1281 1281 return author[author.find('<') + 1:r]
1282 1282
1283 1283 def _ellipsis(text, maxlength):
1284 1284 if len(text) <= maxlength:
1285 1285 return text, False
1286 1286 else:
1287 1287 return "%s..." % (text[:maxlength - 3]), True
1288 1288
1289 1289 def ellipsis(text, maxlength=400):
1290 1290 """Trim string to at most maxlength (default: 400) characters."""
1291 1291 try:
1292 1292 # use unicode not to split at intermediate multi-byte sequence
1293 1293 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1294 1294 maxlength)
1295 1295 if not truncated:
1296 1296 return text
1297 1297 return utext.encode(encoding.encoding)
1298 1298 except (UnicodeDecodeError, UnicodeEncodeError):
1299 1299 return _ellipsis(text, maxlength)[0]
1300 1300
1301 1301 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1302 1302 '''yield every hg repository under path, recursively.'''
1303 1303 def errhandler(err):
1304 1304 if err.filename == path:
1305 1305 raise err
1306 1306 if followsym and hasattr(os.path, 'samestat'):
1307 1307 def _add_dir_if_not_there(dirlst, dirname):
1308 1308 match = False
1309 1309 samestat = os.path.samestat
1310 1310 dirstat = os.stat(dirname)
1311 1311 for lstdirstat in dirlst:
1312 1312 if samestat(dirstat, lstdirstat):
1313 1313 match = True
1314 1314 break
1315 1315 if not match:
1316 1316 dirlst.append(dirstat)
1317 1317 return not match
1318 1318 else:
1319 1319 followsym = False
1320 1320
1321 1321 if (seen_dirs is None) and followsym:
1322 1322 seen_dirs = []
1323 1323 _add_dir_if_not_there(seen_dirs, path)
1324 1324 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1325 1325 dirs.sort()
1326 1326 if '.hg' in dirs:
1327 1327 yield root # found a repository
1328 1328 qroot = os.path.join(root, '.hg', 'patches')
1329 1329 if os.path.isdir(os.path.join(qroot, '.hg')):
1330 1330 yield qroot # we have a patch queue repo here
1331 1331 if recurse:
1332 1332 # avoid recursing inside the .hg directory
1333 1333 dirs.remove('.hg')
1334 1334 else:
1335 1335 dirs[:] = [] # don't descend further
1336 1336 elif followsym:
1337 1337 newdirs = []
1338 1338 for d in dirs:
1339 1339 fname = os.path.join(root, d)
1340 1340 if _add_dir_if_not_there(seen_dirs, fname):
1341 1341 if os.path.islink(fname):
1342 1342 for hgname in walkrepos(fname, True, seen_dirs):
1343 1343 yield hgname
1344 1344 else:
1345 1345 newdirs.append(d)
1346 1346 dirs[:] = newdirs
1347 1347
1348 1348 _rcpath = None
1349 1349
1350 1350 def os_rcpath():
1351 1351 '''return default os-specific hgrc search path'''
1352 1352 path = system_rcpath()
1353 1353 path.extend(user_rcpath())
1354 1354 path = [os.path.normpath(f) for f in path]
1355 1355 return path
1356 1356
1357 1357 def rcpath():
1358 1358 '''return hgrc search path. if env var HGRCPATH is set, use it.
1359 1359 for each item in path, if directory, use files ending in .rc,
1360 1360 else use item.
1361 1361 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1362 1362 if no HGRCPATH, use default os-specific path.'''
1363 1363 global _rcpath
1364 1364 if _rcpath is None:
1365 1365 if 'HGRCPATH' in os.environ:
1366 1366 _rcpath = []
1367 1367 for p in os.environ['HGRCPATH'].split(os.pathsep):
1368 1368 if not p:
1369 1369 continue
1370 1370 p = expandpath(p)
1371 1371 if os.path.isdir(p):
1372 1372 for f, kind in osutil.listdir(p):
1373 1373 if f.endswith('.rc'):
1374 1374 _rcpath.append(os.path.join(p, f))
1375 1375 else:
1376 1376 _rcpath.append(p)
1377 1377 else:
1378 1378 _rcpath = os_rcpath()
1379 1379 return _rcpath
1380 1380
1381 1381 def bytecount(nbytes):
1382 1382 '''return byte count formatted as readable string, with units'''
1383 1383
1384 1384 units = (
1385 1385 (100, 1 << 30, _('%.0f GB')),
1386 1386 (10, 1 << 30, _('%.1f GB')),
1387 1387 (1, 1 << 30, _('%.2f GB')),
1388 1388 (100, 1 << 20, _('%.0f MB')),
1389 1389 (10, 1 << 20, _('%.1f MB')),
1390 1390 (1, 1 << 20, _('%.2f MB')),
1391 1391 (100, 1 << 10, _('%.0f KB')),
1392 1392 (10, 1 << 10, _('%.1f KB')),
1393 1393 (1, 1 << 10, _('%.2f KB')),
1394 1394 (1, 1, _('%.0f bytes')),
1395 1395 )
1396 1396
1397 1397 for multiplier, divisor, format in units:
1398 1398 if nbytes >= divisor * multiplier:
1399 1399 return format % (nbytes / float(divisor))
1400 1400 return units[-1][2] % nbytes
1401 1401
1402 1402 def uirepr(s):
1403 1403 # Avoid double backslash in Windows path repr()
1404 1404 return repr(s).replace('\\\\', '\\')
1405 1405
1406 1406 # delay import of textwrap
1407 1407 def MBTextWrapper(**kwargs):
1408 1408 class tw(textwrap.TextWrapper):
1409 1409 """
1410 1410 Extend TextWrapper for double-width characters.
1411 1411
1412 1412 Some Asian characters use two terminal columns instead of one.
1413 1413 A good example of this behavior can be seen with u'\u65e5\u672c',
1414 1414 the two Japanese characters for "Japan":
1415 1415 len() returns 2, but when printed to a terminal, they eat 4 columns.
1416 1416
1417 1417 (Note that this has nothing to do whatsoever with unicode
1418 1418 representation, or encoding of the underlying string)
1419 1419 """
1420 1420 def __init__(self, **kwargs):
1421 1421 textwrap.TextWrapper.__init__(self, **kwargs)
1422 1422
1423 1423 def _cutdown(self, str, space_left):
1424 1424 l = 0
1425 1425 ucstr = unicode(str, encoding.encoding)
1426 1426 colwidth = unicodedata.east_asian_width
1427 1427 for i in xrange(len(ucstr)):
1428 1428 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1429 1429 if space_left < l:
1430 1430 return (ucstr[:i].encode(encoding.encoding),
1431 1431 ucstr[i:].encode(encoding.encoding))
1432 1432 return str, ''
1433 1433
1434 1434 # overriding of base class
1435 1435 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1436 1436 space_left = max(width - cur_len, 1)
1437 1437
1438 1438 if self.break_long_words:
1439 1439 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1440 1440 cur_line.append(cut)
1441 1441 reversed_chunks[-1] = res
1442 1442 elif not cur_line:
1443 1443 cur_line.append(reversed_chunks.pop())
1444 1444
1445 1445 global MBTextWrapper
1446 1446 MBTextWrapper = tw
1447 1447 return tw(**kwargs)
1448 1448
1449 1449 def wrap(line, width, initindent='', hangindent=''):
1450 1450 maxindent = max(len(hangindent), len(initindent))
1451 1451 if width <= maxindent:
1452 1452 # adjust for weird terminal size
1453 1453 width = max(78, maxindent + 1)
1454 1454 wrapper = MBTextWrapper(width=width,
1455 1455 initial_indent=initindent,
1456 1456 subsequent_indent=hangindent)
1457 1457 return wrapper.fill(line)
1458 1458
1459 1459 def iterlines(iterator):
1460 1460 for chunk in iterator:
1461 1461 for line in chunk.splitlines():
1462 1462 yield line
1463 1463
1464 1464 def expandpath(path):
1465 1465 return os.path.expanduser(os.path.expandvars(path))
1466 1466
1467 1467 def hgcmd():
1468 1468 """Return the command used to execute current hg
1469 1469
1470 1470 This is different from hgexecutable() because on Windows we want
1471 1471 to avoid things opening new shell windows like batch files, so we
1472 1472 get either the python call or current executable.
1473 1473 """
1474 1474 if main_is_frozen():
1475 1475 return [sys.executable]
1476 1476 return gethgcmd()
1477 1477
1478 1478 def rundetached(args, condfn):
1479 1479 """Execute the argument list in a detached process.
1480 1480
1481 1481 condfn is a callable which is called repeatedly and should return
1482 1482 True once the child process is known to have started successfully.
1483 1483 At this point, the child process PID is returned. If the child
1484 1484 process fails to start or finishes before condfn() evaluates to
1485 1485 True, return -1.
1486 1486 """
1487 1487 # Windows case is easier because the child process is either
1488 1488 # successfully starting and validating the condition or exiting
1489 1489 # on failure. We just poll on its PID. On Unix, if the child
1490 1490 # process fails to start, it will be left in a zombie state until
1491 1491 # the parent wait on it, which we cannot do since we expect a long
1492 1492 # running process on success. Instead we listen for SIGCHLD telling
1493 1493 # us our child process terminated.
1494 1494 terminated = set()
1495 1495 def handler(signum, frame):
1496 1496 terminated.add(os.wait())
1497 1497 prevhandler = None
1498 1498 if hasattr(signal, 'SIGCHLD'):
1499 1499 prevhandler = signal.signal(signal.SIGCHLD, handler)
1500 1500 try:
1501 1501 pid = spawndetached(args)
1502 1502 while not condfn():
1503 1503 if ((pid in terminated or not testpid(pid))
1504 1504 and not condfn()):
1505 1505 return -1
1506 1506 time.sleep(0.1)
1507 1507 return pid
1508 1508 finally:
1509 1509 if prevhandler is not None:
1510 1510 signal.signal(signal.SIGCHLD, prevhandler)
1511 1511
1512 1512 try:
1513 1513 any, all = any, all
1514 1514 except NameError:
1515 1515 def any(iterable):
1516 1516 for i in iterable:
1517 1517 if i:
1518 1518 return True
1519 1519 return False
1520 1520
1521 1521 def all(iterable):
1522 1522 for i in iterable:
1523 1523 if not i:
1524 1524 return False
1525 1525 return True
1526 1526
1527 1527 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1528 1528 """Return the result of interpolating items in the mapping into string s.
1529 1529
1530 1530 prefix is a single character string, or a two character string with
1531 1531 a backslash as the first character if the prefix needs to be escaped in
1532 1532 a regular expression.
1533 1533
1534 1534 fn is an optional function that will be applied to the replacement text
1535 1535 just before replacement.
1536 1536
1537 1537 escape_prefix is an optional flag that allows using doubled prefix for
1538 1538 its escaping.
1539 1539 """
1540 1540 fn = fn or (lambda s: s)
1541 1541 patterns = '|'.join(mapping.keys())
1542 1542 if escape_prefix:
1543 1543 patterns += '|' + prefix
1544 1544 if len(prefix) > 1:
1545 1545 prefix_char = prefix[1:]
1546 1546 else:
1547 1547 prefix_char = prefix
1548 1548 mapping[prefix_char] = prefix_char
1549 1549 r = re.compile(r'%s(%s)' % (prefix, patterns))
1550 1550 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1551 1551
1552 1552 def getport(port):
1553 1553 """Return the port for a given network service.
1554 1554
1555 1555 If port is an integer, it's returned as is. If it's a string, it's
1556 1556 looked up using socket.getservbyname(). If there's no matching
1557 1557 service, util.Abort is raised.
1558 1558 """
1559 1559 try:
1560 1560 return int(port)
1561 1561 except ValueError:
1562 1562 pass
1563 1563
1564 1564 try:
1565 1565 return socket.getservbyname(port)
1566 1566 except socket.error:
1567 1567 raise Abort(_("no port number associated with service '%s'") % port)
1568 1568
1569 1569 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1570 1570 '0': False, 'no': False, 'false': False, 'off': False,
1571 1571 'never': False}
1572 1572
1573 1573 def parsebool(s):
1574 1574 """Parse s into a boolean.
1575 1575
1576 1576 If s is not a valid boolean, returns None.
1577 1577 """
1578 1578 return _booleans.get(s.lower(), None)
General Comments 0
You need to be logged in to leave comments. Login now