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