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