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