##// END OF EJS Templates
parsebool: accept always as true and never as false
Augie Fackler -
r12088:1f71dffa default
parent child Browse files
Show More
@@ -1,1447 +1,1448 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 if os.name == 'nt':
395 395 cmd = '"%s"' % cmd
396 396 env = dict(os.environ)
397 397 env.update((k, py2shell(v)) for k, v in environ.iteritems())
398 398 env['HG'] = hgexecutable()
399 399 if out is None:
400 400 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
401 401 env=env, cwd=cwd)
402 402 else:
403 403 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
404 404 env=env, cwd=cwd, stdout=subprocess.PIPE,
405 405 stderr=subprocess.STDOUT)
406 406 for line in proc.stdout:
407 407 out.write(line)
408 408 proc.wait()
409 409 rc = proc.returncode
410 410 if sys.platform == 'OpenVMS' and rc & 1:
411 411 rc = 0
412 412 if rc and onerr:
413 413 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
414 414 explain_exit(rc)[0])
415 415 if errprefix:
416 416 errmsg = '%s: %s' % (errprefix, errmsg)
417 417 try:
418 418 onerr.warn(errmsg + '\n')
419 419 except AttributeError:
420 420 raise onerr(errmsg)
421 421 return rc
422 422
423 423 def checksignature(func):
424 424 '''wrap a function with code to check for calling errors'''
425 425 def check(*args, **kwargs):
426 426 try:
427 427 return func(*args, **kwargs)
428 428 except TypeError:
429 429 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
430 430 raise error.SignatureError
431 431 raise
432 432
433 433 return check
434 434
435 435 def unlink(f):
436 436 """unlink and remove the directory if it is empty"""
437 437 os.unlink(f)
438 438 # try removing directories that might now be empty
439 439 try:
440 440 os.removedirs(os.path.dirname(f))
441 441 except OSError:
442 442 pass
443 443
444 444 def copyfile(src, dest):
445 445 "copy a file, preserving mode and atime/mtime"
446 446 if os.path.islink(src):
447 447 try:
448 448 os.unlink(dest)
449 449 except:
450 450 pass
451 451 os.symlink(os.readlink(src), dest)
452 452 else:
453 453 try:
454 454 shutil.copyfile(src, dest)
455 455 shutil.copystat(src, dest)
456 456 except shutil.Error, inst:
457 457 raise Abort(str(inst))
458 458
459 459 def copyfiles(src, dst, hardlink=None):
460 460 """Copy a directory tree using hardlinks if possible"""
461 461
462 462 if hardlink is None:
463 463 hardlink = (os.stat(src).st_dev ==
464 464 os.stat(os.path.dirname(dst)).st_dev)
465 465
466 466 num = 0
467 467 if os.path.isdir(src):
468 468 os.mkdir(dst)
469 469 for name, kind in osutil.listdir(src):
470 470 srcname = os.path.join(src, name)
471 471 dstname = os.path.join(dst, name)
472 472 hardlink, n = copyfiles(srcname, dstname, hardlink)
473 473 num += n
474 474 else:
475 475 if hardlink:
476 476 try:
477 477 os_link(src, dst)
478 478 except (IOError, OSError):
479 479 hardlink = False
480 480 shutil.copy(src, dst)
481 481 else:
482 482 shutil.copy(src, dst)
483 483 num += 1
484 484
485 485 return hardlink, num
486 486
487 487 class path_auditor(object):
488 488 '''ensure that a filesystem path contains no banned components.
489 489 the following properties of a path are checked:
490 490
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 normpath = os.path.normcase(path)
509 509 parts = splitpath(normpath)
510 510 if (os.path.splitdrive(path)[0]
511 511 or parts[0].lower() in ('.hg', '.hg.', '')
512 512 or os.pardir in parts):
513 513 raise Abort(_("path contains illegal component: %s") % path)
514 514 if '.hg' in path.lower():
515 515 lparts = [p.lower() for p in parts]
516 516 for p in '.hg', '.hg.':
517 517 if p in lparts[1:]:
518 518 pos = lparts.index(p)
519 519 base = os.path.join(*parts[:pos])
520 520 raise Abort(_('path %r is inside repo %r') % (path, base))
521 521 def check(prefix):
522 522 curpath = os.path.join(self.root, prefix)
523 523 try:
524 524 st = os.lstat(curpath)
525 525 except OSError, err:
526 526 # EINVAL can be raised as invalid path syntax under win32.
527 527 # They must be ignored for patterns can be checked too.
528 528 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
529 529 raise
530 530 else:
531 531 if stat.S_ISLNK(st.st_mode):
532 532 raise Abort(_('path %r traverses symbolic link %r') %
533 533 (path, prefix))
534 534 elif (stat.S_ISDIR(st.st_mode) and
535 535 os.path.isdir(os.path.join(curpath, '.hg'))):
536 536 if not self.callback or not self.callback(curpath):
537 537 raise Abort(_('path %r is inside repo %r') %
538 538 (path, prefix))
539 539 parts.pop()
540 540 prefixes = []
541 541 while parts:
542 542 prefix = os.sep.join(parts)
543 543 if prefix in self.auditeddir:
544 544 break
545 545 check(prefix)
546 546 prefixes.append(prefix)
547 547 parts.pop()
548 548
549 549 self.audited.add(path)
550 550 # only add prefixes to the cache after checking everything: we don't
551 551 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
552 552 self.auditeddir.update(prefixes)
553 553
554 554 def nlinks(pathname):
555 555 """Return number of hardlinks for the given file."""
556 556 return os.lstat(pathname).st_nlink
557 557
558 558 if hasattr(os, 'link'):
559 559 os_link = os.link
560 560 else:
561 561 def os_link(src, dst):
562 562 raise OSError(0, _("Hardlinks not supported"))
563 563
564 564 def lookup_reg(key, name=None, scope=None):
565 565 return None
566 566
567 567 def hidewindow():
568 568 """Hide current shell window.
569 569
570 570 Used to hide the window opened when starting asynchronous
571 571 child process under Windows, unneeded on other systems.
572 572 """
573 573 pass
574 574
575 575 if os.name == 'nt':
576 576 from windows import *
577 577 else:
578 578 from posix import *
579 579
580 580 def makelock(info, pathname):
581 581 try:
582 582 return os.symlink(info, pathname)
583 583 except OSError, why:
584 584 if why.errno == errno.EEXIST:
585 585 raise
586 586 except AttributeError: # no symlink in os
587 587 pass
588 588
589 589 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
590 590 os.write(ld, info)
591 591 os.close(ld)
592 592
593 593 def readlock(pathname):
594 594 try:
595 595 return os.readlink(pathname)
596 596 except OSError, why:
597 597 if why.errno not in (errno.EINVAL, errno.ENOSYS):
598 598 raise
599 599 except AttributeError: # no symlink in os
600 600 pass
601 601 return posixfile(pathname).read()
602 602
603 603 def fstat(fp):
604 604 '''stat file object that may not have fileno method.'''
605 605 try:
606 606 return os.fstat(fp.fileno())
607 607 except AttributeError:
608 608 return os.stat(fp.name)
609 609
610 610 # File system features
611 611
612 612 def checkcase(path):
613 613 """
614 614 Check whether the given path is on a case-sensitive filesystem
615 615
616 616 Requires a path (like /foo/.hg) ending with a foldable final
617 617 directory component.
618 618 """
619 619 s1 = os.stat(path)
620 620 d, b = os.path.split(path)
621 621 p2 = os.path.join(d, b.upper())
622 622 if path == p2:
623 623 p2 = os.path.join(d, b.lower())
624 624 try:
625 625 s2 = os.stat(p2)
626 626 if s2 == s1:
627 627 return False
628 628 return True
629 629 except:
630 630 return True
631 631
632 632 _fspathcache = {}
633 633 def fspath(name, root):
634 634 '''Get name in the case stored in the filesystem
635 635
636 636 The name is either relative to root, or it is an absolute path starting
637 637 with root. Note that this function is unnecessary, and should not be
638 638 called, for case-sensitive filesystems (simply because it's expensive).
639 639 '''
640 640 # If name is absolute, make it relative
641 641 if name.lower().startswith(root.lower()):
642 642 l = len(root)
643 643 if name[l] == os.sep or name[l] == os.altsep:
644 644 l = l + 1
645 645 name = name[l:]
646 646
647 647 if not os.path.exists(os.path.join(root, name)):
648 648 return None
649 649
650 650 seps = os.sep
651 651 if os.altsep:
652 652 seps = seps + os.altsep
653 653 # Protect backslashes. This gets silly very quickly.
654 654 seps.replace('\\','\\\\')
655 655 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
656 656 dir = os.path.normcase(os.path.normpath(root))
657 657 result = []
658 658 for part, sep in pattern.findall(name):
659 659 if sep:
660 660 result.append(sep)
661 661 continue
662 662
663 663 if dir not in _fspathcache:
664 664 _fspathcache[dir] = os.listdir(dir)
665 665 contents = _fspathcache[dir]
666 666
667 667 lpart = part.lower()
668 668 lenp = len(part)
669 669 for n in contents:
670 670 if lenp == len(n) and n.lower() == lpart:
671 671 result.append(n)
672 672 break
673 673 else:
674 674 # Cannot happen, as the file exists!
675 675 result.append(part)
676 676 dir = os.path.join(dir, lpart)
677 677
678 678 return ''.join(result)
679 679
680 680 def checkexec(path):
681 681 """
682 682 Check whether the given path is on a filesystem with UNIX-like exec flags
683 683
684 684 Requires a directory (like /foo/.hg)
685 685 """
686 686
687 687 # VFAT on some Linux versions can flip mode but it doesn't persist
688 688 # a FS remount. Frequently we can detect it if files are created
689 689 # with exec bit on.
690 690
691 691 try:
692 692 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
693 693 fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
694 694 try:
695 695 os.close(fh)
696 696 m = os.stat(fn).st_mode & 0777
697 697 new_file_has_exec = m & EXECFLAGS
698 698 os.chmod(fn, m ^ EXECFLAGS)
699 699 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
700 700 finally:
701 701 os.unlink(fn)
702 702 except (IOError, OSError):
703 703 # we don't care, the user probably won't be able to commit anyway
704 704 return False
705 705 return not (new_file_has_exec or exec_flags_cannot_flip)
706 706
707 707 def checklink(path):
708 708 """check whether the given path is on a symlink-capable filesystem"""
709 709 # mktemp is not racy because symlink creation will fail if the
710 710 # file already exists
711 711 name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
712 712 try:
713 713 os.symlink(".", name)
714 714 os.unlink(name)
715 715 return True
716 716 except (OSError, AttributeError):
717 717 return False
718 718
719 719 def needbinarypatch():
720 720 """return True if patches should be applied in binary mode by default."""
721 721 return os.name == 'nt'
722 722
723 723 def endswithsep(path):
724 724 '''Check path ends with os.sep or os.altsep.'''
725 725 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
726 726
727 727 def splitpath(path):
728 728 '''Split path by os.sep.
729 729 Note that this function does not use os.altsep because this is
730 730 an alternative of simple "xxx.split(os.sep)".
731 731 It is recommended to use os.path.normpath() before using this
732 732 function if need.'''
733 733 return path.split(os.sep)
734 734
735 735 def gui():
736 736 '''Are we running in a GUI?'''
737 737 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
738 738
739 739 def mktempcopy(name, emptyok=False, createmode=None):
740 740 """Create a temporary file with the same contents from name
741 741
742 742 The permission bits are copied from the original file.
743 743
744 744 If the temporary file is going to be truncated immediately, you
745 745 can use emptyok=True as an optimization.
746 746
747 747 Returns the name of the temporary file.
748 748 """
749 749 d, fn = os.path.split(name)
750 750 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
751 751 os.close(fd)
752 752 # Temporary files are created with mode 0600, which is usually not
753 753 # what we want. If the original file already exists, just copy
754 754 # its mode. Otherwise, manually obey umask.
755 755 try:
756 756 st_mode = os.lstat(name).st_mode & 0777
757 757 except OSError, inst:
758 758 if inst.errno != errno.ENOENT:
759 759 raise
760 760 st_mode = createmode
761 761 if st_mode is None:
762 762 st_mode = ~umask
763 763 st_mode &= 0666
764 764 os.chmod(temp, st_mode)
765 765 if emptyok:
766 766 return temp
767 767 try:
768 768 try:
769 769 ifp = posixfile(name, "rb")
770 770 except IOError, inst:
771 771 if inst.errno == errno.ENOENT:
772 772 return temp
773 773 if not getattr(inst, 'filename', None):
774 774 inst.filename = name
775 775 raise
776 776 ofp = posixfile(temp, "wb")
777 777 for chunk in filechunkiter(ifp):
778 778 ofp.write(chunk)
779 779 ifp.close()
780 780 ofp.close()
781 781 except:
782 782 try: os.unlink(temp)
783 783 except: pass
784 784 raise
785 785 return temp
786 786
787 787 class atomictempfile(object):
788 788 """file-like object that atomically updates a file
789 789
790 790 All writes will be redirected to a temporary copy of the original
791 791 file. When rename is called, the copy is renamed to the original
792 792 name, making the changes visible.
793 793 """
794 794 def __init__(self, name, mode='w+b', createmode=None):
795 795 self.__name = name
796 796 self._fp = None
797 797 self.temp = mktempcopy(name, emptyok=('w' in mode),
798 798 createmode=createmode)
799 799 self._fp = posixfile(self.temp, mode)
800 800
801 801 def __getattr__(self, name):
802 802 return getattr(self._fp, name)
803 803
804 804 def rename(self):
805 805 if not self._fp.closed:
806 806 self._fp.close()
807 807 rename(self.temp, localpath(self.__name))
808 808
809 809 def __del__(self):
810 810 if not self._fp:
811 811 return
812 812 if not self._fp.closed:
813 813 try:
814 814 os.unlink(self.temp)
815 815 except: pass
816 816 self._fp.close()
817 817
818 818 def makedirs(name, mode=None):
819 819 """recursive directory creation with parent mode inheritance"""
820 820 try:
821 821 os.mkdir(name)
822 822 if mode is not None:
823 823 os.chmod(name, mode)
824 824 return
825 825 except OSError, err:
826 826 if err.errno == errno.EEXIST:
827 827 return
828 828 if err.errno != errno.ENOENT:
829 829 raise
830 830 parent = os.path.abspath(os.path.dirname(name))
831 831 makedirs(parent, mode)
832 832 makedirs(name, mode)
833 833
834 834 class opener(object):
835 835 """Open files relative to a base directory
836 836
837 837 This class is used to hide the details of COW semantics and
838 838 remote file access from higher level code.
839 839 """
840 840 def __init__(self, base, audit=True):
841 841 self.base = base
842 842 if audit:
843 843 self.auditor = path_auditor(base)
844 844 else:
845 845 self.auditor = always
846 846 self.createmode = None
847 847
848 848 @propertycache
849 849 def _can_symlink(self):
850 850 return checklink(self.base)
851 851
852 852 def _fixfilemode(self, name):
853 853 if self.createmode is None:
854 854 return
855 855 os.chmod(name, self.createmode & 0666)
856 856
857 857 def __call__(self, path, mode="r", text=False, atomictemp=False):
858 858 self.auditor(path)
859 859 f = os.path.join(self.base, path)
860 860
861 861 if not text and "b" not in mode:
862 862 mode += "b" # for that other OS
863 863
864 864 nlink = -1
865 865 if mode not in ("r", "rb"):
866 866 try:
867 867 nlink = nlinks(f)
868 868 except OSError:
869 869 nlink = 0
870 870 d = os.path.dirname(f)
871 871 if not os.path.isdir(d):
872 872 makedirs(d, self.createmode)
873 873 if atomictemp:
874 874 return atomictempfile(f, mode, self.createmode)
875 875 if nlink > 1:
876 876 rename(mktempcopy(f), f)
877 877 fp = posixfile(f, mode)
878 878 if nlink == 0:
879 879 self._fixfilemode(f)
880 880 return fp
881 881
882 882 def symlink(self, src, dst):
883 883 self.auditor(dst)
884 884 linkname = os.path.join(self.base, dst)
885 885 try:
886 886 os.unlink(linkname)
887 887 except OSError:
888 888 pass
889 889
890 890 dirname = os.path.dirname(linkname)
891 891 if not os.path.exists(dirname):
892 892 makedirs(dirname, self.createmode)
893 893
894 894 if self._can_symlink:
895 895 try:
896 896 os.symlink(src, linkname)
897 897 except OSError, err:
898 898 raise OSError(err.errno, _('could not symlink to %r: %s') %
899 899 (src, err.strerror), linkname)
900 900 else:
901 901 f = self(dst, "w")
902 902 f.write(src)
903 903 f.close()
904 904 self._fixfilemode(dst)
905 905
906 906 class chunkbuffer(object):
907 907 """Allow arbitrary sized chunks of data to be efficiently read from an
908 908 iterator over chunks of arbitrary size."""
909 909
910 910 def __init__(self, in_iter):
911 911 """in_iter is the iterator that's iterating over the input chunks.
912 912 targetsize is how big a buffer to try to maintain."""
913 913 def splitbig(chunks):
914 914 for chunk in chunks:
915 915 if len(chunk) > 2**20:
916 916 pos = 0
917 917 while pos < len(chunk):
918 918 end = pos + 2 ** 18
919 919 yield chunk[pos:end]
920 920 pos = end
921 921 else:
922 922 yield chunk
923 923 self.iter = splitbig(in_iter)
924 924 self._queue = []
925 925
926 926 def read(self, l):
927 927 """Read L bytes of data from the iterator of chunks of data.
928 928 Returns less than L bytes if the iterator runs dry."""
929 929 left = l
930 930 buf = ''
931 931 queue = self._queue
932 932 while left > 0:
933 933 # refill the queue
934 934 if not queue:
935 935 target = 2**18
936 936 for chunk in self.iter:
937 937 queue.append(chunk)
938 938 target -= len(chunk)
939 939 if target <= 0:
940 940 break
941 941 if not queue:
942 942 break
943 943
944 944 chunk = queue.pop(0)
945 945 left -= len(chunk)
946 946 if left < 0:
947 947 queue.insert(0, chunk[left:])
948 948 buf += chunk[:left]
949 949 else:
950 950 buf += chunk
951 951
952 952 return buf
953 953
954 954 def filechunkiter(f, size=65536, limit=None):
955 955 """Create a generator that produces the data in the file size
956 956 (default 65536) bytes at a time, up to optional limit (default is
957 957 to read all data). Chunks may be less than size bytes if the
958 958 chunk is the last chunk in the file, or the file is a socket or
959 959 some other type of file that sometimes reads less data than is
960 960 requested."""
961 961 assert size >= 0
962 962 assert limit is None or limit >= 0
963 963 while True:
964 964 if limit is None:
965 965 nbytes = size
966 966 else:
967 967 nbytes = min(limit, size)
968 968 s = nbytes and f.read(nbytes)
969 969 if not s:
970 970 break
971 971 if limit:
972 972 limit -= len(s)
973 973 yield s
974 974
975 975 def makedate():
976 976 lt = time.localtime()
977 977 if lt[8] == 1 and time.daylight:
978 978 tz = time.altzone
979 979 else:
980 980 tz = time.timezone
981 981 return time.mktime(lt), tz
982 982
983 983 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
984 984 """represent a (unixtime, offset) tuple as a localized time.
985 985 unixtime is seconds since the epoch, and offset is the time zone's
986 986 number of seconds away from UTC. if timezone is false, do not
987 987 append time zone to string."""
988 988 t, tz = date or makedate()
989 989 if "%1" in format or "%2" in format:
990 990 sign = (tz > 0) and "-" or "+"
991 991 minutes = abs(tz) // 60
992 992 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
993 993 format = format.replace("%2", "%02d" % (minutes % 60))
994 994 s = time.strftime(format, time.gmtime(float(t) - tz))
995 995 return s
996 996
997 997 def shortdate(date=None):
998 998 """turn (timestamp, tzoff) tuple into iso 8631 date."""
999 999 return datestr(date, format='%Y-%m-%d')
1000 1000
1001 1001 def strdate(string, format, defaults=[]):
1002 1002 """parse a localized time string and return a (unixtime, offset) tuple.
1003 1003 if the string cannot be parsed, ValueError is raised."""
1004 1004 def timezone(string):
1005 1005 tz = string.split()[-1]
1006 1006 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1007 1007 sign = (tz[0] == "+") and 1 or -1
1008 1008 hours = int(tz[1:3])
1009 1009 minutes = int(tz[3:5])
1010 1010 return -sign * (hours * 60 + minutes) * 60
1011 1011 if tz == "GMT" or tz == "UTC":
1012 1012 return 0
1013 1013 return None
1014 1014
1015 1015 # NOTE: unixtime = localunixtime + offset
1016 1016 offset, date = timezone(string), string
1017 1017 if offset != None:
1018 1018 date = " ".join(string.split()[:-1])
1019 1019
1020 1020 # add missing elements from defaults
1021 1021 for part in defaults:
1022 1022 found = [True for p in part if ("%"+p) in format]
1023 1023 if not found:
1024 1024 date += "@" + defaults[part]
1025 1025 format += "@%" + part[0]
1026 1026
1027 1027 timetuple = time.strptime(date, format)
1028 1028 localunixtime = int(calendar.timegm(timetuple))
1029 1029 if offset is None:
1030 1030 # local timezone
1031 1031 unixtime = int(time.mktime(timetuple))
1032 1032 offset = unixtime - localunixtime
1033 1033 else:
1034 1034 unixtime = localunixtime + offset
1035 1035 return unixtime, offset
1036 1036
1037 1037 def parsedate(date, formats=None, defaults=None):
1038 1038 """parse a localized date/time string and return a (unixtime, offset) tuple.
1039 1039
1040 1040 The date may be a "unixtime offset" string or in one of the specified
1041 1041 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1042 1042 """
1043 1043 if not date:
1044 1044 return 0, 0
1045 1045 if isinstance(date, tuple) and len(date) == 2:
1046 1046 return date
1047 1047 if not formats:
1048 1048 formats = defaultdateformats
1049 1049 date = date.strip()
1050 1050 try:
1051 1051 when, offset = map(int, date.split(' '))
1052 1052 except ValueError:
1053 1053 # fill out defaults
1054 1054 if not defaults:
1055 1055 defaults = {}
1056 1056 now = makedate()
1057 1057 for part in "d mb yY HI M S".split():
1058 1058 if part not in defaults:
1059 1059 if part[0] in "HMS":
1060 1060 defaults[part] = "00"
1061 1061 else:
1062 1062 defaults[part] = datestr(now, "%" + part[0])
1063 1063
1064 1064 for format in formats:
1065 1065 try:
1066 1066 when, offset = strdate(date, format, defaults)
1067 1067 except (ValueError, OverflowError):
1068 1068 pass
1069 1069 else:
1070 1070 break
1071 1071 else:
1072 1072 raise Abort(_('invalid date: %r ') % date)
1073 1073 # validate explicit (probably user-specified) date and
1074 1074 # time zone offset. values must fit in signed 32 bits for
1075 1075 # current 32-bit linux runtimes. timezones go from UTC-12
1076 1076 # to UTC+14
1077 1077 if abs(when) > 0x7fffffff:
1078 1078 raise Abort(_('date exceeds 32 bits: %d') % when)
1079 1079 if offset < -50400 or offset > 43200:
1080 1080 raise Abort(_('impossible time zone offset: %d') % offset)
1081 1081 return when, offset
1082 1082
1083 1083 def matchdate(date):
1084 1084 """Return a function that matches a given date match specifier
1085 1085
1086 1086 Formats include:
1087 1087
1088 1088 '{date}' match a given date to the accuracy provided
1089 1089
1090 1090 '<{date}' on or before a given date
1091 1091
1092 1092 '>{date}' on or after a given date
1093 1093
1094 1094 """
1095 1095
1096 1096 def lower(date):
1097 1097 d = dict(mb="1", d="1")
1098 1098 return parsedate(date, extendeddateformats, d)[0]
1099 1099
1100 1100 def upper(date):
1101 1101 d = dict(mb="12", HI="23", M="59", S="59")
1102 1102 for days in "31 30 29".split():
1103 1103 try:
1104 1104 d["d"] = days
1105 1105 return parsedate(date, extendeddateformats, d)[0]
1106 1106 except:
1107 1107 pass
1108 1108 d["d"] = "28"
1109 1109 return parsedate(date, extendeddateformats, d)[0]
1110 1110
1111 1111 date = date.strip()
1112 1112 if date[0] == "<":
1113 1113 when = upper(date[1:])
1114 1114 return lambda x: x <= when
1115 1115 elif date[0] == ">":
1116 1116 when = lower(date[1:])
1117 1117 return lambda x: x >= when
1118 1118 elif date[0] == "-":
1119 1119 try:
1120 1120 days = int(date[1:])
1121 1121 except ValueError:
1122 1122 raise Abort(_("invalid day spec: %s") % date[1:])
1123 1123 when = makedate()[0] - days * 3600 * 24
1124 1124 return lambda x: x >= when
1125 1125 elif " to " in date:
1126 1126 a, b = date.split(" to ")
1127 1127 start, stop = lower(a), upper(b)
1128 1128 return lambda x: x >= start and x <= stop
1129 1129 else:
1130 1130 start, stop = lower(date), upper(date)
1131 1131 return lambda x: x >= start and x <= stop
1132 1132
1133 1133 def shortuser(user):
1134 1134 """Return a short representation of a user name or email address."""
1135 1135 f = user.find('@')
1136 1136 if f >= 0:
1137 1137 user = user[:f]
1138 1138 f = user.find('<')
1139 1139 if f >= 0:
1140 1140 user = user[f + 1:]
1141 1141 f = user.find(' ')
1142 1142 if f >= 0:
1143 1143 user = user[:f]
1144 1144 f = user.find('.')
1145 1145 if f >= 0:
1146 1146 user = user[:f]
1147 1147 return user
1148 1148
1149 1149 def email(author):
1150 1150 '''get email of author.'''
1151 1151 r = author.find('>')
1152 1152 if r == -1:
1153 1153 r = None
1154 1154 return author[author.find('<') + 1:r]
1155 1155
1156 1156 def ellipsis(text, maxlength=400):
1157 1157 """Trim string to at most maxlength (default: 400) characters."""
1158 1158 if len(text) <= maxlength:
1159 1159 return text
1160 1160 else:
1161 1161 return "%s..." % (text[:maxlength - 3])
1162 1162
1163 1163 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1164 1164 '''yield every hg repository under path, recursively.'''
1165 1165 def errhandler(err):
1166 1166 if err.filename == path:
1167 1167 raise err
1168 1168 if followsym and hasattr(os.path, 'samestat'):
1169 1169 def _add_dir_if_not_there(dirlst, dirname):
1170 1170 match = False
1171 1171 samestat = os.path.samestat
1172 1172 dirstat = os.stat(dirname)
1173 1173 for lstdirstat in dirlst:
1174 1174 if samestat(dirstat, lstdirstat):
1175 1175 match = True
1176 1176 break
1177 1177 if not match:
1178 1178 dirlst.append(dirstat)
1179 1179 return not match
1180 1180 else:
1181 1181 followsym = False
1182 1182
1183 1183 if (seen_dirs is None) and followsym:
1184 1184 seen_dirs = []
1185 1185 _add_dir_if_not_there(seen_dirs, path)
1186 1186 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1187 1187 dirs.sort()
1188 1188 if '.hg' in dirs:
1189 1189 yield root # found a repository
1190 1190 qroot = os.path.join(root, '.hg', 'patches')
1191 1191 if os.path.isdir(os.path.join(qroot, '.hg')):
1192 1192 yield qroot # we have a patch queue repo here
1193 1193 if recurse:
1194 1194 # avoid recursing inside the .hg directory
1195 1195 dirs.remove('.hg')
1196 1196 else:
1197 1197 dirs[:] = [] # don't descend further
1198 1198 elif followsym:
1199 1199 newdirs = []
1200 1200 for d in dirs:
1201 1201 fname = os.path.join(root, d)
1202 1202 if _add_dir_if_not_there(seen_dirs, fname):
1203 1203 if os.path.islink(fname):
1204 1204 for hgname in walkrepos(fname, True, seen_dirs):
1205 1205 yield hgname
1206 1206 else:
1207 1207 newdirs.append(d)
1208 1208 dirs[:] = newdirs
1209 1209
1210 1210 _rcpath = None
1211 1211
1212 1212 def os_rcpath():
1213 1213 '''return default os-specific hgrc search path'''
1214 1214 path = system_rcpath()
1215 1215 path.extend(user_rcpath())
1216 1216 path = [os.path.normpath(f) for f in path]
1217 1217 return path
1218 1218
1219 1219 def rcpath():
1220 1220 '''return hgrc search path. if env var HGRCPATH is set, use it.
1221 1221 for each item in path, if directory, use files ending in .rc,
1222 1222 else use item.
1223 1223 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1224 1224 if no HGRCPATH, use default os-specific path.'''
1225 1225 global _rcpath
1226 1226 if _rcpath is None:
1227 1227 if 'HGRCPATH' in os.environ:
1228 1228 _rcpath = []
1229 1229 for p in os.environ['HGRCPATH'].split(os.pathsep):
1230 1230 if not p:
1231 1231 continue
1232 1232 p = expandpath(p)
1233 1233 if os.path.isdir(p):
1234 1234 for f, kind in osutil.listdir(p):
1235 1235 if f.endswith('.rc'):
1236 1236 _rcpath.append(os.path.join(p, f))
1237 1237 else:
1238 1238 _rcpath.append(p)
1239 1239 else:
1240 1240 _rcpath = os_rcpath()
1241 1241 return _rcpath
1242 1242
1243 1243 def bytecount(nbytes):
1244 1244 '''return byte count formatted as readable string, with units'''
1245 1245
1246 1246 units = (
1247 1247 (100, 1 << 30, _('%.0f GB')),
1248 1248 (10, 1 << 30, _('%.1f GB')),
1249 1249 (1, 1 << 30, _('%.2f GB')),
1250 1250 (100, 1 << 20, _('%.0f MB')),
1251 1251 (10, 1 << 20, _('%.1f MB')),
1252 1252 (1, 1 << 20, _('%.2f MB')),
1253 1253 (100, 1 << 10, _('%.0f KB')),
1254 1254 (10, 1 << 10, _('%.1f KB')),
1255 1255 (1, 1 << 10, _('%.2f KB')),
1256 1256 (1, 1, _('%.0f bytes')),
1257 1257 )
1258 1258
1259 1259 for multiplier, divisor, format in units:
1260 1260 if nbytes >= divisor * multiplier:
1261 1261 return format % (nbytes / float(divisor))
1262 1262 return units[-1][2] % nbytes
1263 1263
1264 1264 def drop_scheme(scheme, path):
1265 1265 sc = scheme + ':'
1266 1266 if path.startswith(sc):
1267 1267 path = path[len(sc):]
1268 1268 if path.startswith('//'):
1269 1269 if scheme == 'file':
1270 1270 i = path.find('/', 2)
1271 1271 if i == -1:
1272 1272 return ''
1273 1273 # On Windows, absolute paths are rooted at the current drive
1274 1274 # root. On POSIX they are rooted at the file system root.
1275 1275 if os.name == 'nt':
1276 1276 droot = os.path.splitdrive(os.getcwd())[0] + '/'
1277 1277 path = os.path.join(droot, path[i + 1:])
1278 1278 else:
1279 1279 path = path[i:]
1280 1280 else:
1281 1281 path = path[2:]
1282 1282 return path
1283 1283
1284 1284 def uirepr(s):
1285 1285 # Avoid double backslash in Windows path repr()
1286 1286 return repr(s).replace('\\\\', '\\')
1287 1287
1288 1288 #### naming convention of below implementation follows 'textwrap' module
1289 1289
1290 1290 class MBTextWrapper(textwrap.TextWrapper):
1291 1291 def __init__(self, **kwargs):
1292 1292 textwrap.TextWrapper.__init__(self, **kwargs)
1293 1293
1294 1294 def _cutdown(self, str, space_left):
1295 1295 l = 0
1296 1296 ucstr = unicode(str, encoding.encoding)
1297 1297 w = unicodedata.east_asian_width
1298 1298 for i in xrange(len(ucstr)):
1299 1299 l += w(ucstr[i]) in 'WFA' and 2 or 1
1300 1300 if space_left < l:
1301 1301 return (ucstr[:i].encode(encoding.encoding),
1302 1302 ucstr[i:].encode(encoding.encoding))
1303 1303 return str, ''
1304 1304
1305 1305 # ----------------------------------------
1306 1306 # overriding of base class
1307 1307
1308 1308 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1309 1309 space_left = max(width - cur_len, 1)
1310 1310
1311 1311 if self.break_long_words:
1312 1312 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1313 1313 cur_line.append(cut)
1314 1314 reversed_chunks[-1] = res
1315 1315 elif not cur_line:
1316 1316 cur_line.append(reversed_chunks.pop())
1317 1317
1318 1318 #### naming convention of above implementation follows 'textwrap' module
1319 1319
1320 1320 def wrap(line, width=None, initindent='', hangindent=''):
1321 1321 if width is None:
1322 1322 width = termwidth() - 2
1323 1323 maxindent = max(len(hangindent), len(initindent))
1324 1324 if width <= maxindent:
1325 1325 # adjust for weird terminal size
1326 1326 width = max(78, maxindent + 1)
1327 1327 wrapper = MBTextWrapper(width=width,
1328 1328 initial_indent=initindent,
1329 1329 subsequent_indent=hangindent)
1330 1330 return wrapper.fill(line)
1331 1331
1332 1332 def iterlines(iterator):
1333 1333 for chunk in iterator:
1334 1334 for line in chunk.splitlines():
1335 1335 yield line
1336 1336
1337 1337 def expandpath(path):
1338 1338 return os.path.expanduser(os.path.expandvars(path))
1339 1339
1340 1340 def hgcmd():
1341 1341 """Return the command used to execute current hg
1342 1342
1343 1343 This is different from hgexecutable() because on Windows we want
1344 1344 to avoid things opening new shell windows like batch files, so we
1345 1345 get either the python call or current executable.
1346 1346 """
1347 1347 if main_is_frozen():
1348 1348 return [sys.executable]
1349 1349 return gethgcmd()
1350 1350
1351 1351 def rundetached(args, condfn):
1352 1352 """Execute the argument list in a detached process.
1353 1353
1354 1354 condfn is a callable which is called repeatedly and should return
1355 1355 True once the child process is known to have started successfully.
1356 1356 At this point, the child process PID is returned. If the child
1357 1357 process fails to start or finishes before condfn() evaluates to
1358 1358 True, return -1.
1359 1359 """
1360 1360 # Windows case is easier because the child process is either
1361 1361 # successfully starting and validating the condition or exiting
1362 1362 # on failure. We just poll on its PID. On Unix, if the child
1363 1363 # process fails to start, it will be left in a zombie state until
1364 1364 # the parent wait on it, which we cannot do since we expect a long
1365 1365 # running process on success. Instead we listen for SIGCHLD telling
1366 1366 # us our child process terminated.
1367 1367 terminated = set()
1368 1368 def handler(signum, frame):
1369 1369 terminated.add(os.wait())
1370 1370 prevhandler = None
1371 1371 if hasattr(signal, 'SIGCHLD'):
1372 1372 prevhandler = signal.signal(signal.SIGCHLD, handler)
1373 1373 try:
1374 1374 pid = spawndetached(args)
1375 1375 while not condfn():
1376 1376 if ((pid in terminated or not testpid(pid))
1377 1377 and not condfn()):
1378 1378 return -1
1379 1379 time.sleep(0.1)
1380 1380 return pid
1381 1381 finally:
1382 1382 if prevhandler is not None:
1383 1383 signal.signal(signal.SIGCHLD, prevhandler)
1384 1384
1385 1385 try:
1386 1386 any, all = any, all
1387 1387 except NameError:
1388 1388 def any(iterable):
1389 1389 for i in iterable:
1390 1390 if i:
1391 1391 return True
1392 1392 return False
1393 1393
1394 1394 def all(iterable):
1395 1395 for i in iterable:
1396 1396 if not i:
1397 1397 return False
1398 1398 return True
1399 1399
1400 1400 def termwidth():
1401 1401 if 'COLUMNS' in os.environ:
1402 1402 try:
1403 1403 return int(os.environ['COLUMNS'])
1404 1404 except ValueError:
1405 1405 pass
1406 1406 return termwidth_()
1407 1407
1408 1408 def interpolate(prefix, mapping, s, fn=None):
1409 1409 """Return the result of interpolating items in the mapping into string s.
1410 1410
1411 1411 prefix is a single character string, or a two character string with
1412 1412 a backslash as the first character if the prefix needs to be escaped in
1413 1413 a regular expression.
1414 1414
1415 1415 fn is an optional function that will be applied to the replacement text
1416 1416 just before replacement.
1417 1417 """
1418 1418 fn = fn or (lambda s: s)
1419 1419 r = re.compile(r'%s(%s)' % (prefix, '|'.join(mapping.keys())))
1420 1420 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1421 1421
1422 1422 def getport(port):
1423 1423 """Return the port for a given network service.
1424 1424
1425 1425 If port is an integer, it's returned as is. If it's a string, it's
1426 1426 looked up using socket.getservbyname(). If there's no matching
1427 1427 service, util.Abort is raised.
1428 1428 """
1429 1429 try:
1430 1430 return int(port)
1431 1431 except ValueError:
1432 1432 pass
1433 1433
1434 1434 try:
1435 1435 return socket.getservbyname(port)
1436 1436 except socket.error:
1437 1437 raise Abort(_("no port number associated with service '%s'") % port)
1438 1438
1439 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
1440 '0': False, 'no': False, 'false': False, 'off': False}
1439 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1440 '0': False, 'no': False, 'false': False, 'off': False,
1441 'never': False}
1441 1442
1442 1443 def parsebool(s):
1443 1444 """Parse s into a boolean.
1444 1445
1445 1446 If s is not a valid boolean, returns None.
1446 1447 """
1447 1448 return _booleans.get(s.lower(), None)
General Comments 0
You need to be logged in to leave comments. Login now