##// END OF EJS Templates
opener: use posixfile to hold file open when calling nlinks()...
Adrian Buehlmann -
r13342:2dc7a2a9 default
parent child Browse files
Show More
@@ -1,1555 +1,1555
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 fd = open(f2)
745 fd = posixfile(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 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 fd = open(f)
919 fd = posixfile(f)
920 920 nlink = nlinks(f)
921 921 if nlink < 1:
922 922 nlink = 2 # force mktempcopy (issue1922)
923 923 fd.close()
924 924 except (OSError, IOError), e:
925 925 if e.errno != errno.ENOENT:
926 926 raise
927 927 nlink = 0
928 928 if not os.path.isdir(dirname):
929 929 makedirs(dirname, self.createmode)
930 930 if nlink > 0:
931 931 if self._trustnlink is None:
932 932 self._trustnlink = nlink > 1 or checknlink(f)
933 933 if nlink > 1 or not self._trustnlink:
934 934 rename(mktempcopy(f), f)
935 935 fp = posixfile(f, mode)
936 936 if nlink == 0:
937 937 self._fixfilemode(f)
938 938 return fp
939 939
940 940 def symlink(self, src, dst):
941 941 self.auditor(dst)
942 942 linkname = os.path.join(self.base, dst)
943 943 try:
944 944 os.unlink(linkname)
945 945 except OSError:
946 946 pass
947 947
948 948 dirname = os.path.dirname(linkname)
949 949 if not os.path.exists(dirname):
950 950 makedirs(dirname, self.createmode)
951 951
952 952 if self._can_symlink:
953 953 try:
954 954 os.symlink(src, linkname)
955 955 except OSError, err:
956 956 raise OSError(err.errno, _('could not symlink to %r: %s') %
957 957 (src, err.strerror), linkname)
958 958 else:
959 959 f = self(dst, "w")
960 960 f.write(src)
961 961 f.close()
962 962 self._fixfilemode(dst)
963 963
964 964 class chunkbuffer(object):
965 965 """Allow arbitrary sized chunks of data to be efficiently read from an
966 966 iterator over chunks of arbitrary size."""
967 967
968 968 def __init__(self, in_iter):
969 969 """in_iter is the iterator that's iterating over the input chunks.
970 970 targetsize is how big a buffer to try to maintain."""
971 971 def splitbig(chunks):
972 972 for chunk in chunks:
973 973 if len(chunk) > 2**20:
974 974 pos = 0
975 975 while pos < len(chunk):
976 976 end = pos + 2 ** 18
977 977 yield chunk[pos:end]
978 978 pos = end
979 979 else:
980 980 yield chunk
981 981 self.iter = splitbig(in_iter)
982 982 self._queue = []
983 983
984 984 def read(self, l):
985 985 """Read L bytes of data from the iterator of chunks of data.
986 986 Returns less than L bytes if the iterator runs dry."""
987 987 left = l
988 988 buf = ''
989 989 queue = self._queue
990 990 while left > 0:
991 991 # refill the queue
992 992 if not queue:
993 993 target = 2**18
994 994 for chunk in self.iter:
995 995 queue.append(chunk)
996 996 target -= len(chunk)
997 997 if target <= 0:
998 998 break
999 999 if not queue:
1000 1000 break
1001 1001
1002 1002 chunk = queue.pop(0)
1003 1003 left -= len(chunk)
1004 1004 if left < 0:
1005 1005 queue.insert(0, chunk[left:])
1006 1006 buf += chunk[:left]
1007 1007 else:
1008 1008 buf += chunk
1009 1009
1010 1010 return buf
1011 1011
1012 1012 def filechunkiter(f, size=65536, limit=None):
1013 1013 """Create a generator that produces the data in the file size
1014 1014 (default 65536) bytes at a time, up to optional limit (default is
1015 1015 to read all data). Chunks may be less than size bytes if the
1016 1016 chunk is the last chunk in the file, or the file is a socket or
1017 1017 some other type of file that sometimes reads less data than is
1018 1018 requested."""
1019 1019 assert size >= 0
1020 1020 assert limit is None or limit >= 0
1021 1021 while True:
1022 1022 if limit is None:
1023 1023 nbytes = size
1024 1024 else:
1025 1025 nbytes = min(limit, size)
1026 1026 s = nbytes and f.read(nbytes)
1027 1027 if not s:
1028 1028 break
1029 1029 if limit:
1030 1030 limit -= len(s)
1031 1031 yield s
1032 1032
1033 1033 def makedate():
1034 1034 lt = time.localtime()
1035 1035 if lt[8] == 1 and time.daylight:
1036 1036 tz = time.altzone
1037 1037 else:
1038 1038 tz = time.timezone
1039 1039 t = time.mktime(lt)
1040 1040 if t < 0:
1041 1041 hint = _("check your clock")
1042 1042 raise Abort(_("negative timestamp: %d") % t, hint=hint)
1043 1043 return t, tz
1044 1044
1045 1045 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1046 1046 """represent a (unixtime, offset) tuple as a localized time.
1047 1047 unixtime is seconds since the epoch, and offset is the time zone's
1048 1048 number of seconds away from UTC. if timezone is false, do not
1049 1049 append time zone to string."""
1050 1050 t, tz = date or makedate()
1051 1051 if t < 0:
1052 1052 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
1053 1053 tz = 0
1054 1054 if "%1" in format or "%2" in format:
1055 1055 sign = (tz > 0) and "-" or "+"
1056 1056 minutes = abs(tz) // 60
1057 1057 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
1058 1058 format = format.replace("%2", "%02d" % (minutes % 60))
1059 1059 s = time.strftime(format, time.gmtime(float(t) - tz))
1060 1060 return s
1061 1061
1062 1062 def shortdate(date=None):
1063 1063 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1064 1064 return datestr(date, format='%Y-%m-%d')
1065 1065
1066 1066 def strdate(string, format, defaults=[]):
1067 1067 """parse a localized time string and return a (unixtime, offset) tuple.
1068 1068 if the string cannot be parsed, ValueError is raised."""
1069 1069 def timezone(string):
1070 1070 tz = string.split()[-1]
1071 1071 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1072 1072 sign = (tz[0] == "+") and 1 or -1
1073 1073 hours = int(tz[1:3])
1074 1074 minutes = int(tz[3:5])
1075 1075 return -sign * (hours * 60 + minutes) * 60
1076 1076 if tz == "GMT" or tz == "UTC":
1077 1077 return 0
1078 1078 return None
1079 1079
1080 1080 # NOTE: unixtime = localunixtime + offset
1081 1081 offset, date = timezone(string), string
1082 1082 if offset is not None:
1083 1083 date = " ".join(string.split()[:-1])
1084 1084
1085 1085 # add missing elements from defaults
1086 1086 usenow = False # default to using biased defaults
1087 1087 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
1088 1088 found = [True for p in part if ("%"+p) in format]
1089 1089 if not found:
1090 1090 date += "@" + defaults[part][usenow]
1091 1091 format += "@%" + part[0]
1092 1092 else:
1093 1093 # We've found a specific time element, less specific time
1094 1094 # elements are relative to today
1095 1095 usenow = True
1096 1096
1097 1097 timetuple = time.strptime(date, format)
1098 1098 localunixtime = int(calendar.timegm(timetuple))
1099 1099 if offset is None:
1100 1100 # local timezone
1101 1101 unixtime = int(time.mktime(timetuple))
1102 1102 offset = unixtime - localunixtime
1103 1103 else:
1104 1104 unixtime = localunixtime + offset
1105 1105 return unixtime, offset
1106 1106
1107 1107 def parsedate(date, formats=None, bias={}):
1108 1108 """parse a localized date/time and return a (unixtime, offset) tuple.
1109 1109
1110 1110 The date may be a "unixtime offset" string or in one of the specified
1111 1111 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1112 1112 """
1113 1113 if not date:
1114 1114 return 0, 0
1115 1115 if isinstance(date, tuple) and len(date) == 2:
1116 1116 return date
1117 1117 if not formats:
1118 1118 formats = defaultdateformats
1119 1119 date = date.strip()
1120 1120 try:
1121 1121 when, offset = map(int, date.split(' '))
1122 1122 except ValueError:
1123 1123 # fill out defaults
1124 1124 now = makedate()
1125 1125 defaults = {}
1126 1126 nowmap = {}
1127 1127 for part in ("d", "mb", "yY", "HI", "M", "S"):
1128 1128 # this piece is for rounding the specific end of unknowns
1129 1129 b = bias.get(part)
1130 1130 if b is None:
1131 1131 if part[0] in "HMS":
1132 1132 b = "00"
1133 1133 else:
1134 1134 b = "0"
1135 1135
1136 1136 # this piece is for matching the generic end to today's date
1137 1137 n = datestr(now, "%" + part[0])
1138 1138
1139 1139 defaults[part] = (b, n)
1140 1140
1141 1141 for format in formats:
1142 1142 try:
1143 1143 when, offset = strdate(date, format, defaults)
1144 1144 except (ValueError, OverflowError):
1145 1145 pass
1146 1146 else:
1147 1147 break
1148 1148 else:
1149 1149 raise Abort(_('invalid date: %r') % date)
1150 1150 # validate explicit (probably user-specified) date and
1151 1151 # time zone offset. values must fit in signed 32 bits for
1152 1152 # current 32-bit linux runtimes. timezones go from UTC-12
1153 1153 # to UTC+14
1154 1154 if abs(when) > 0x7fffffff:
1155 1155 raise Abort(_('date exceeds 32 bits: %d') % when)
1156 1156 if when < 0:
1157 1157 raise Abort(_('negative date value: %d') % when)
1158 1158 if offset < -50400 or offset > 43200:
1159 1159 raise Abort(_('impossible time zone offset: %d') % offset)
1160 1160 return when, offset
1161 1161
1162 1162 def matchdate(date):
1163 1163 """Return a function that matches a given date match specifier
1164 1164
1165 1165 Formats include:
1166 1166
1167 1167 '{date}' match a given date to the accuracy provided
1168 1168
1169 1169 '<{date}' on or before a given date
1170 1170
1171 1171 '>{date}' on or after a given date
1172 1172
1173 1173 >>> p1 = parsedate("10:29:59")
1174 1174 >>> p2 = parsedate("10:30:00")
1175 1175 >>> p3 = parsedate("10:30:59")
1176 1176 >>> p4 = parsedate("10:31:00")
1177 1177 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1178 1178 >>> f = matchdate("10:30")
1179 1179 >>> f(p1[0])
1180 1180 False
1181 1181 >>> f(p2[0])
1182 1182 True
1183 1183 >>> f(p3[0])
1184 1184 True
1185 1185 >>> f(p4[0])
1186 1186 False
1187 1187 >>> f(p5[0])
1188 1188 False
1189 1189 """
1190 1190
1191 1191 def lower(date):
1192 1192 d = dict(mb="1", d="1")
1193 1193 return parsedate(date, extendeddateformats, d)[0]
1194 1194
1195 1195 def upper(date):
1196 1196 d = dict(mb="12", HI="23", M="59", S="59")
1197 1197 for days in ("31", "30", "29"):
1198 1198 try:
1199 1199 d["d"] = days
1200 1200 return parsedate(date, extendeddateformats, d)[0]
1201 1201 except:
1202 1202 pass
1203 1203 d["d"] = "28"
1204 1204 return parsedate(date, extendeddateformats, d)[0]
1205 1205
1206 1206 date = date.strip()
1207 1207 if date[0] == "<":
1208 1208 when = upper(date[1:])
1209 1209 return lambda x: x <= when
1210 1210 elif date[0] == ">":
1211 1211 when = lower(date[1:])
1212 1212 return lambda x: x >= when
1213 1213 elif date[0] == "-":
1214 1214 try:
1215 1215 days = int(date[1:])
1216 1216 except ValueError:
1217 1217 raise Abort(_("invalid day spec: %s") % date[1:])
1218 1218 when = makedate()[0] - days * 3600 * 24
1219 1219 return lambda x: x >= when
1220 1220 elif " to " in date:
1221 1221 a, b = date.split(" to ")
1222 1222 start, stop = lower(a), upper(b)
1223 1223 return lambda x: x >= start and x <= stop
1224 1224 else:
1225 1225 start, stop = lower(date), upper(date)
1226 1226 return lambda x: x >= start and x <= stop
1227 1227
1228 1228 def shortuser(user):
1229 1229 """Return a short representation of a user name or email address."""
1230 1230 f = user.find('@')
1231 1231 if f >= 0:
1232 1232 user = user[:f]
1233 1233 f = user.find('<')
1234 1234 if f >= 0:
1235 1235 user = user[f + 1:]
1236 1236 f = user.find(' ')
1237 1237 if f >= 0:
1238 1238 user = user[:f]
1239 1239 f = user.find('.')
1240 1240 if f >= 0:
1241 1241 user = user[:f]
1242 1242 return user
1243 1243
1244 1244 def email(author):
1245 1245 '''get email of author.'''
1246 1246 r = author.find('>')
1247 1247 if r == -1:
1248 1248 r = None
1249 1249 return author[author.find('<') + 1:r]
1250 1250
1251 1251 def _ellipsis(text, maxlength):
1252 1252 if len(text) <= maxlength:
1253 1253 return text, False
1254 1254 else:
1255 1255 return "%s..." % (text[:maxlength - 3]), True
1256 1256
1257 1257 def ellipsis(text, maxlength=400):
1258 1258 """Trim string to at most maxlength (default: 400) characters."""
1259 1259 try:
1260 1260 # use unicode not to split at intermediate multi-byte sequence
1261 1261 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1262 1262 maxlength)
1263 1263 if not truncated:
1264 1264 return text
1265 1265 return utext.encode(encoding.encoding)
1266 1266 except (UnicodeDecodeError, UnicodeEncodeError):
1267 1267 return _ellipsis(text, maxlength)[0]
1268 1268
1269 1269 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1270 1270 '''yield every hg repository under path, recursively.'''
1271 1271 def errhandler(err):
1272 1272 if err.filename == path:
1273 1273 raise err
1274 1274 if followsym and hasattr(os.path, 'samestat'):
1275 1275 def _add_dir_if_not_there(dirlst, dirname):
1276 1276 match = False
1277 1277 samestat = os.path.samestat
1278 1278 dirstat = os.stat(dirname)
1279 1279 for lstdirstat in dirlst:
1280 1280 if samestat(dirstat, lstdirstat):
1281 1281 match = True
1282 1282 break
1283 1283 if not match:
1284 1284 dirlst.append(dirstat)
1285 1285 return not match
1286 1286 else:
1287 1287 followsym = False
1288 1288
1289 1289 if (seen_dirs is None) and followsym:
1290 1290 seen_dirs = []
1291 1291 _add_dir_if_not_there(seen_dirs, path)
1292 1292 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1293 1293 dirs.sort()
1294 1294 if '.hg' in dirs:
1295 1295 yield root # found a repository
1296 1296 qroot = os.path.join(root, '.hg', 'patches')
1297 1297 if os.path.isdir(os.path.join(qroot, '.hg')):
1298 1298 yield qroot # we have a patch queue repo here
1299 1299 if recurse:
1300 1300 # avoid recursing inside the .hg directory
1301 1301 dirs.remove('.hg')
1302 1302 else:
1303 1303 dirs[:] = [] # don't descend further
1304 1304 elif followsym:
1305 1305 newdirs = []
1306 1306 for d in dirs:
1307 1307 fname = os.path.join(root, d)
1308 1308 if _add_dir_if_not_there(seen_dirs, fname):
1309 1309 if os.path.islink(fname):
1310 1310 for hgname in walkrepos(fname, True, seen_dirs):
1311 1311 yield hgname
1312 1312 else:
1313 1313 newdirs.append(d)
1314 1314 dirs[:] = newdirs
1315 1315
1316 1316 _rcpath = None
1317 1317
1318 1318 def os_rcpath():
1319 1319 '''return default os-specific hgrc search path'''
1320 1320 path = system_rcpath()
1321 1321 path.extend(user_rcpath())
1322 1322 path = [os.path.normpath(f) for f in path]
1323 1323 return path
1324 1324
1325 1325 def rcpath():
1326 1326 '''return hgrc search path. if env var HGRCPATH is set, use it.
1327 1327 for each item in path, if directory, use files ending in .rc,
1328 1328 else use item.
1329 1329 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1330 1330 if no HGRCPATH, use default os-specific path.'''
1331 1331 global _rcpath
1332 1332 if _rcpath is None:
1333 1333 if 'HGRCPATH' in os.environ:
1334 1334 _rcpath = []
1335 1335 for p in os.environ['HGRCPATH'].split(os.pathsep):
1336 1336 if not p:
1337 1337 continue
1338 1338 p = expandpath(p)
1339 1339 if os.path.isdir(p):
1340 1340 for f, kind in osutil.listdir(p):
1341 1341 if f.endswith('.rc'):
1342 1342 _rcpath.append(os.path.join(p, f))
1343 1343 else:
1344 1344 _rcpath.append(p)
1345 1345 else:
1346 1346 _rcpath = os_rcpath()
1347 1347 return _rcpath
1348 1348
1349 1349 def bytecount(nbytes):
1350 1350 '''return byte count formatted as readable string, with units'''
1351 1351
1352 1352 units = (
1353 1353 (100, 1 << 30, _('%.0f GB')),
1354 1354 (10, 1 << 30, _('%.1f GB')),
1355 1355 (1, 1 << 30, _('%.2f GB')),
1356 1356 (100, 1 << 20, _('%.0f MB')),
1357 1357 (10, 1 << 20, _('%.1f MB')),
1358 1358 (1, 1 << 20, _('%.2f MB')),
1359 1359 (100, 1 << 10, _('%.0f KB')),
1360 1360 (10, 1 << 10, _('%.1f KB')),
1361 1361 (1, 1 << 10, _('%.2f KB')),
1362 1362 (1, 1, _('%.0f bytes')),
1363 1363 )
1364 1364
1365 1365 for multiplier, divisor, format in units:
1366 1366 if nbytes >= divisor * multiplier:
1367 1367 return format % (nbytes / float(divisor))
1368 1368 return units[-1][2] % nbytes
1369 1369
1370 1370 def drop_scheme(scheme, path):
1371 1371 sc = scheme + ':'
1372 1372 if path.startswith(sc):
1373 1373 path = path[len(sc):]
1374 1374 if path.startswith('//'):
1375 1375 if scheme == 'file':
1376 1376 i = path.find('/', 2)
1377 1377 if i == -1:
1378 1378 return ''
1379 1379 # On Windows, absolute paths are rooted at the current drive
1380 1380 # root. On POSIX they are rooted at the file system root.
1381 1381 if os.name == 'nt':
1382 1382 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1383 1383 path = os.path.join(droot, path[i + 1:])
1384 1384 else:
1385 1385 path = path[i:]
1386 1386 else:
1387 1387 path = path[2:]
1388 1388 return path
1389 1389
1390 1390 def uirepr(s):
1391 1391 # Avoid double backslash in Windows path repr()
1392 1392 return repr(s).replace('\\\\', '\\')
1393 1393
1394 1394 # delay import of textwrap
1395 1395 def MBTextWrapper(**kwargs):
1396 1396 class tw(textwrap.TextWrapper):
1397 1397 """
1398 1398 Extend TextWrapper for double-width characters.
1399 1399
1400 1400 Some Asian characters use two terminal columns instead of one.
1401 1401 A good example of this behavior can be seen with u'\u65e5\u672c',
1402 1402 the two Japanese characters for "Japan":
1403 1403 len() returns 2, but when printed to a terminal, they eat 4 columns.
1404 1404
1405 1405 (Note that this has nothing to do whatsoever with unicode
1406 1406 representation, or encoding of the underlying string)
1407 1407 """
1408 1408 def __init__(self, **kwargs):
1409 1409 textwrap.TextWrapper.__init__(self, **kwargs)
1410 1410
1411 1411 def _cutdown(self, str, space_left):
1412 1412 l = 0
1413 1413 ucstr = unicode(str, encoding.encoding)
1414 1414 colwidth = unicodedata.east_asian_width
1415 1415 for i in xrange(len(ucstr)):
1416 1416 l += colwidth(ucstr[i]) in 'WFA' and 2 or 1
1417 1417 if space_left < l:
1418 1418 return (ucstr[:i].encode(encoding.encoding),
1419 1419 ucstr[i:].encode(encoding.encoding))
1420 1420 return str, ''
1421 1421
1422 1422 # overriding of base class
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 global MBTextWrapper
1434 1434 MBTextWrapper = tw
1435 1435 return tw(**kwargs)
1436 1436
1437 1437 def wrap(line, width, initindent='', hangindent=''):
1438 1438 maxindent = max(len(hangindent), len(initindent))
1439 1439 if width <= maxindent:
1440 1440 # adjust for weird terminal size
1441 1441 width = max(78, maxindent + 1)
1442 1442 wrapper = MBTextWrapper(width=width,
1443 1443 initial_indent=initindent,
1444 1444 subsequent_indent=hangindent)
1445 1445 return wrapper.fill(line)
1446 1446
1447 1447 def iterlines(iterator):
1448 1448 for chunk in iterator:
1449 1449 for line in chunk.splitlines():
1450 1450 yield line
1451 1451
1452 1452 def expandpath(path):
1453 1453 return os.path.expanduser(os.path.expandvars(path))
1454 1454
1455 1455 def hgcmd():
1456 1456 """Return the command used to execute current hg
1457 1457
1458 1458 This is different from hgexecutable() because on Windows we want
1459 1459 to avoid things opening new shell windows like batch files, so we
1460 1460 get either the python call or current executable.
1461 1461 """
1462 1462 if main_is_frozen():
1463 1463 return [sys.executable]
1464 1464 return gethgcmd()
1465 1465
1466 1466 def rundetached(args, condfn):
1467 1467 """Execute the argument list in a detached process.
1468 1468
1469 1469 condfn is a callable which is called repeatedly and should return
1470 1470 True once the child process is known to have started successfully.
1471 1471 At this point, the child process PID is returned. If the child
1472 1472 process fails to start or finishes before condfn() evaluates to
1473 1473 True, return -1.
1474 1474 """
1475 1475 # Windows case is easier because the child process is either
1476 1476 # successfully starting and validating the condition or exiting
1477 1477 # on failure. We just poll on its PID. On Unix, if the child
1478 1478 # process fails to start, it will be left in a zombie state until
1479 1479 # the parent wait on it, which we cannot do since we expect a long
1480 1480 # running process on success. Instead we listen for SIGCHLD telling
1481 1481 # us our child process terminated.
1482 1482 terminated = set()
1483 1483 def handler(signum, frame):
1484 1484 terminated.add(os.wait())
1485 1485 prevhandler = None
1486 1486 if hasattr(signal, 'SIGCHLD'):
1487 1487 prevhandler = signal.signal(signal.SIGCHLD, handler)
1488 1488 try:
1489 1489 pid = spawndetached(args)
1490 1490 while not condfn():
1491 1491 if ((pid in terminated or not testpid(pid))
1492 1492 and not condfn()):
1493 1493 return -1
1494 1494 time.sleep(0.1)
1495 1495 return pid
1496 1496 finally:
1497 1497 if prevhandler is not None:
1498 1498 signal.signal(signal.SIGCHLD, prevhandler)
1499 1499
1500 1500 try:
1501 1501 any, all = any, all
1502 1502 except NameError:
1503 1503 def any(iterable):
1504 1504 for i in iterable:
1505 1505 if i:
1506 1506 return True
1507 1507 return False
1508 1508
1509 1509 def all(iterable):
1510 1510 for i in iterable:
1511 1511 if not i:
1512 1512 return False
1513 1513 return True
1514 1514
1515 1515 def interpolate(prefix, mapping, s, fn=None):
1516 1516 """Return the result of interpolating items in the mapping into string s.
1517 1517
1518 1518 prefix is a single character string, or a two character string with
1519 1519 a backslash as the first character if the prefix needs to be escaped in
1520 1520 a regular expression.
1521 1521
1522 1522 fn is an optional function that will be applied to the replacement text
1523 1523 just before replacement.
1524 1524 """
1525 1525 fn = fn or (lambda s: s)
1526 1526 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1527 1527 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1528 1528
1529 1529 def getport(port):
1530 1530 """Return the port for a given network service.
1531 1531
1532 1532 If port is an integer, it's returned as is. If it's a string, it's
1533 1533 looked up using socket.getservbyname(). If there's no matching
1534 1534 service, util.Abort is raised.
1535 1535 """
1536 1536 try:
1537 1537 return int(port)
1538 1538 except ValueError:
1539 1539 pass
1540 1540
1541 1541 try:
1542 1542 return socket.getservbyname(port)
1543 1543 except socket.error:
1544 1544 raise Abort(_("no port number associated with service '%s'") % port)
1545 1545
1546 1546 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1547 1547 '0': False, 'no': False, 'false': False, 'off': False,
1548 1548 'never': False}
1549 1549
1550 1550 def parsebool(s):
1551 1551 """Parse s into a boolean.
1552 1552
1553 1553 If s is not a valid boolean, returns None.
1554 1554 """
1555 1555 return _booleans.get(s.lower(), None)
General Comments 0
You need to be logged in to leave comments. Login now