##// END OF EJS Templates
util: state docstring of rename more precisely
Adrian Buehlmann -
r9548:7732606b default
parent child Browse files
Show More
@@ -1,1285 +1,1285 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, incorporated herein by reference.
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 cStringIO, errno, re, shutil, sys, tempfile, traceback
19 19 import os, stat, time, calendar, random, textwrap
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 subprocess
40 40 closefds = os.name == 'posix'
41 41 def popen2(cmd):
42 42 # Setting bufsize to -1 lets the system decide the buffer size.
43 43 # The default for bufsize is 0, meaning unbuffered. This leads to
44 44 # poor performance on Mac OS X: http://bugs.python.org/issue4194
45 45 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
46 46 close_fds=closefds,
47 47 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
48 48 return p.stdin, p.stdout
49 49 def popen3(cmd):
50 50 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
51 51 close_fds=closefds,
52 52 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
53 53 stderr=subprocess.PIPE)
54 54 return p.stdin, p.stdout, p.stderr
55 55
56 56 def version():
57 57 """Return version information if available."""
58 58 try:
59 59 import __version__
60 60 return __version__.version
61 61 except ImportError:
62 62 return 'unknown'
63 63
64 64 # used by parsedate
65 65 defaultdateformats = (
66 66 '%Y-%m-%d %H:%M:%S',
67 67 '%Y-%m-%d %I:%M:%S%p',
68 68 '%Y-%m-%d %H:%M',
69 69 '%Y-%m-%d %I:%M%p',
70 70 '%Y-%m-%d',
71 71 '%m-%d',
72 72 '%m/%d',
73 73 '%m/%d/%y',
74 74 '%m/%d/%Y',
75 75 '%a %b %d %H:%M:%S %Y',
76 76 '%a %b %d %I:%M:%S%p %Y',
77 77 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
78 78 '%b %d %H:%M:%S %Y',
79 79 '%b %d %I:%M:%S%p %Y',
80 80 '%b %d %H:%M:%S',
81 81 '%b %d %I:%M:%S%p',
82 82 '%b %d %H:%M',
83 83 '%b %d %I:%M%p',
84 84 '%b %d %Y',
85 85 '%b %d',
86 86 '%H:%M:%S',
87 87 '%I:%M:%S%p',
88 88 '%H:%M',
89 89 '%I:%M%p',
90 90 )
91 91
92 92 extendeddateformats = defaultdateformats + (
93 93 "%Y",
94 94 "%Y-%m",
95 95 "%b",
96 96 "%b %Y",
97 97 )
98 98
99 99 def cachefunc(func):
100 100 '''cache the result of function calls'''
101 101 # XXX doesn't handle keywords args
102 102 cache = {}
103 103 if func.func_code.co_argcount == 1:
104 104 # we gain a small amount of time because
105 105 # we don't need to pack/unpack the list
106 106 def f(arg):
107 107 if arg not in cache:
108 108 cache[arg] = func(arg)
109 109 return cache[arg]
110 110 else:
111 111 def f(*args):
112 112 if args not in cache:
113 113 cache[args] = func(*args)
114 114 return cache[args]
115 115
116 116 return f
117 117
118 118 def lrucachefunc(func):
119 119 '''cache most recent results of function calls'''
120 120 cache = {}
121 121 order = []
122 122 if func.func_code.co_argcount == 1:
123 123 def f(arg):
124 124 if arg not in cache:
125 125 if len(cache) > 20:
126 126 del cache[order.pop(0)]
127 127 cache[arg] = func(arg)
128 128 else:
129 129 order.remove(arg)
130 130 order.append(arg)
131 131 return cache[arg]
132 132 else:
133 133 def f(*args):
134 134 if args not in cache:
135 135 if len(cache) > 20:
136 136 del cache[order.pop(0)]
137 137 cache[args] = func(*args)
138 138 else:
139 139 order.remove(args)
140 140 order.append(args)
141 141 return cache[args]
142 142
143 143 return f
144 144
145 145 class propertycache(object):
146 146 def __init__(self, func):
147 147 self.func = func
148 148 self.name = func.__name__
149 149 def __get__(self, obj, type=None):
150 150 result = self.func(obj)
151 151 setattr(obj, self.name, result)
152 152 return result
153 153
154 154 def pipefilter(s, cmd):
155 155 '''filter string S through command CMD, returning its output'''
156 156 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
157 157 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
158 158 pout, perr = p.communicate(s)
159 159 return pout
160 160
161 161 def tempfilter(s, cmd):
162 162 '''filter string S through a pair of temporary files with CMD.
163 163 CMD is used as a template to create the real command to be run,
164 164 with the strings INFILE and OUTFILE replaced by the real names of
165 165 the temporary files generated.'''
166 166 inname, outname = None, None
167 167 try:
168 168 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
169 169 fp = os.fdopen(infd, 'wb')
170 170 fp.write(s)
171 171 fp.close()
172 172 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
173 173 os.close(outfd)
174 174 cmd = cmd.replace('INFILE', inname)
175 175 cmd = cmd.replace('OUTFILE', outname)
176 176 code = os.system(cmd)
177 177 if sys.platform == 'OpenVMS' and code & 1:
178 178 code = 0
179 179 if code: raise Abort(_("command '%s' failed: %s") %
180 180 (cmd, explain_exit(code)))
181 181 return open(outname, 'rb').read()
182 182 finally:
183 183 try:
184 184 if inname: os.unlink(inname)
185 185 except: pass
186 186 try:
187 187 if outname: os.unlink(outname)
188 188 except: pass
189 189
190 190 filtertable = {
191 191 'tempfile:': tempfilter,
192 192 'pipe:': pipefilter,
193 193 }
194 194
195 195 def filter(s, cmd):
196 196 "filter a string through a command that transforms its input to its output"
197 197 for name, fn in filtertable.iteritems():
198 198 if cmd.startswith(name):
199 199 return fn(s, cmd[len(name):].lstrip())
200 200 return pipefilter(s, cmd)
201 201
202 202 def binary(s):
203 203 """return true if a string is binary data"""
204 204 return bool(s and '\0' in s)
205 205
206 206 def increasingchunks(source, min=1024, max=65536):
207 207 '''return no less than min bytes per chunk while data remains,
208 208 doubling min after each chunk until it reaches max'''
209 209 def log2(x):
210 210 if not x:
211 211 return 0
212 212 i = 0
213 213 while x:
214 214 x >>= 1
215 215 i += 1
216 216 return i - 1
217 217
218 218 buf = []
219 219 blen = 0
220 220 for chunk in source:
221 221 buf.append(chunk)
222 222 blen += len(chunk)
223 223 if blen >= min:
224 224 if min < max:
225 225 min = min << 1
226 226 nmin = 1 << log2(blen)
227 227 if nmin > min:
228 228 min = nmin
229 229 if min > max:
230 230 min = max
231 231 yield ''.join(buf)
232 232 blen = 0
233 233 buf = []
234 234 if buf:
235 235 yield ''.join(buf)
236 236
237 237 Abort = error.Abort
238 238
239 239 def always(fn): return True
240 240 def never(fn): return False
241 241
242 242 def pathto(root, n1, n2):
243 243 '''return the relative path from one place to another.
244 244 root should use os.sep to separate directories
245 245 n1 should use os.sep to separate directories
246 246 n2 should use "/" to separate directories
247 247 returns an os.sep-separated path.
248 248
249 249 If n1 is a relative path, it's assumed it's
250 250 relative to root.
251 251 n2 should always be relative to root.
252 252 '''
253 253 if not n1: return localpath(n2)
254 254 if os.path.isabs(n1):
255 255 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
256 256 return os.path.join(root, localpath(n2))
257 257 n2 = '/'.join((pconvert(root), n2))
258 258 a, b = splitpath(n1), n2.split('/')
259 259 a.reverse()
260 260 b.reverse()
261 261 while a and b and a[-1] == b[-1]:
262 262 a.pop()
263 263 b.pop()
264 264 b.reverse()
265 265 return os.sep.join((['..'] * len(a)) + b) or '.'
266 266
267 267 def canonpath(root, cwd, myname):
268 268 """return the canonical path of myname, given cwd and root"""
269 269 if endswithsep(root):
270 270 rootsep = root
271 271 else:
272 272 rootsep = root + os.sep
273 273 name = myname
274 274 if not os.path.isabs(name):
275 275 name = os.path.join(root, cwd, name)
276 276 name = os.path.normpath(name)
277 277 audit_path = path_auditor(root)
278 278 if name != rootsep and name.startswith(rootsep):
279 279 name = name[len(rootsep):]
280 280 audit_path(name)
281 281 return pconvert(name)
282 282 elif name == root:
283 283 return ''
284 284 else:
285 285 # Determine whether `name' is in the hierarchy at or beneath `root',
286 286 # by iterating name=dirname(name) until that causes no change (can't
287 287 # check name == '/', because that doesn't work on windows). For each
288 288 # `name', compare dev/inode numbers. If they match, the list `rel'
289 289 # holds the reversed list of components making up the relative file
290 290 # name we want.
291 291 root_st = os.stat(root)
292 292 rel = []
293 293 while True:
294 294 try:
295 295 name_st = os.stat(name)
296 296 except OSError:
297 297 break
298 298 if samestat(name_st, root_st):
299 299 if not rel:
300 300 # name was actually the same as root (maybe a symlink)
301 301 return ''
302 302 rel.reverse()
303 303 name = os.path.join(*rel)
304 304 audit_path(name)
305 305 return pconvert(name)
306 306 dirname, basename = os.path.split(name)
307 307 rel.append(basename)
308 308 if dirname == name:
309 309 break
310 310 name = dirname
311 311
312 312 raise Abort('%s not under root' % myname)
313 313
314 314 _hgexecutable = None
315 315
316 316 def main_is_frozen():
317 317 """return True if we are a frozen executable.
318 318
319 319 The code supports py2exe (most common, Windows only) and tools/freeze
320 320 (portable, not much used).
321 321 """
322 322 return (hasattr(sys, "frozen") or # new py2exe
323 323 hasattr(sys, "importers") or # old py2exe
324 324 imp.is_frozen("__main__")) # tools/freeze
325 325
326 326 def hgexecutable():
327 327 """return location of the 'hg' executable.
328 328
329 329 Defaults to $HG or 'hg' in the search path.
330 330 """
331 331 if _hgexecutable is None:
332 332 hg = os.environ.get('HG')
333 333 if hg:
334 334 set_hgexecutable(hg)
335 335 elif main_is_frozen():
336 336 set_hgexecutable(sys.executable)
337 337 else:
338 338 set_hgexecutable(find_exe('hg') or 'hg')
339 339 return _hgexecutable
340 340
341 341 def set_hgexecutable(path):
342 342 """set location of the 'hg' executable"""
343 343 global _hgexecutable
344 344 _hgexecutable = path
345 345
346 346 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
347 347 '''enhanced shell command execution.
348 348 run with environment maybe modified, maybe in different dir.
349 349
350 350 if command fails and onerr is None, return status. if ui object,
351 351 print error message and return status, else raise onerr object as
352 352 exception.'''
353 353 def py2shell(val):
354 354 'convert python object into string that is useful to shell'
355 355 if val is None or val is False:
356 356 return '0'
357 357 if val is True:
358 358 return '1'
359 359 return str(val)
360 360 origcmd = cmd
361 361 if os.name == 'nt':
362 362 cmd = '"%s"' % cmd
363 363 env = dict(os.environ)
364 364 env.update((k, py2shell(v)) for k, v in environ.iteritems())
365 365 env['HG'] = hgexecutable()
366 366 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
367 367 env=env, cwd=cwd)
368 368 if sys.platform == 'OpenVMS' and rc & 1:
369 369 rc = 0
370 370 if rc and onerr:
371 371 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
372 372 explain_exit(rc)[0])
373 373 if errprefix:
374 374 errmsg = '%s: %s' % (errprefix, errmsg)
375 375 try:
376 376 onerr.warn(errmsg + '\n')
377 377 except AttributeError:
378 378 raise onerr(errmsg)
379 379 return rc
380 380
381 381 def checksignature(func):
382 382 '''wrap a function with code to check for calling errors'''
383 383 def check(*args, **kwargs):
384 384 try:
385 385 return func(*args, **kwargs)
386 386 except TypeError:
387 387 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
388 388 raise error.SignatureError
389 389 raise
390 390
391 391 return check
392 392
393 393 # os.path.lexists is not available on python2.3
394 394 def lexists(filename):
395 395 "test whether a file with this name exists. does not follow symlinks"
396 396 try:
397 397 os.lstat(filename)
398 398 except:
399 399 return False
400 400 return True
401 401
402 402 def rename(src, dst):
403 """forcibly rename a file"""
403 '''atomically rename file src to dst, replacing dst if it exists'''
404 404 try:
405 405 os.rename(src, dst)
406 406 except OSError, err: # FIXME: check err (EEXIST ?)
407 407
408 408 # On windows, rename to existing file is not allowed, so we
409 409 # must delete destination first. But if a file is open, unlink
410 410 # schedules it for delete but does not delete it. Rename
411 411 # happens immediately even for open files, so we rename
412 412 # destination to a temporary name, then delete that. Then
413 413 # rename is safe to do.
414 414 # The temporary name is chosen at random to avoid the situation
415 415 # where a file is left lying around from a previous aborted run.
416 416 # The usual race condition this introduces can't be avoided as
417 417 # we need the name to rename into, and not the file itself. Due
418 418 # to the nature of the operation however, any races will at worst
419 419 # lead to the rename failing and the current operation aborting.
420 420
421 421 def tempname(prefix):
422 422 for tries in xrange(10):
423 423 temp = '%s-%08x' % (prefix, random.randint(0, 0xffffffff))
424 424 if not os.path.exists(temp):
425 425 return temp
426 426 raise IOError, (errno.EEXIST, "No usable temporary filename found")
427 427
428 428 temp = tempname(dst)
429 429 os.rename(dst, temp)
430 430 try:
431 431 os.unlink(temp)
432 432 except:
433 433 # Some rude AV-scanners on Windows may cause the unlink to
434 434 # fail. Not aborting here just leaks the temp file, whereas
435 435 # aborting at this point may leave serious inconsistencies.
436 436 # Ideally, we would notify the user here.
437 437 pass
438 438 os.rename(src, dst)
439 439
440 440 def unlink(f):
441 441 """unlink and remove the directory if it is empty"""
442 442 os.unlink(f)
443 443 # try removing directories that might now be empty
444 444 try:
445 445 os.removedirs(os.path.dirname(f))
446 446 except OSError:
447 447 pass
448 448
449 449 def copyfile(src, dest):
450 450 "copy a file, preserving mode and atime/mtime"
451 451 if os.path.islink(src):
452 452 try:
453 453 os.unlink(dest)
454 454 except:
455 455 pass
456 456 os.symlink(os.readlink(src), dest)
457 457 else:
458 458 try:
459 459 shutil.copyfile(src, dest)
460 460 shutil.copystat(src, dest)
461 461 except shutil.Error, inst:
462 462 raise Abort(str(inst))
463 463
464 464 def copyfiles(src, dst, hardlink=None):
465 465 """Copy a directory tree using hardlinks if possible"""
466 466
467 467 if hardlink is None:
468 468 hardlink = (os.stat(src).st_dev ==
469 469 os.stat(os.path.dirname(dst)).st_dev)
470 470
471 471 if os.path.isdir(src):
472 472 os.mkdir(dst)
473 473 for name, kind in osutil.listdir(src):
474 474 srcname = os.path.join(src, name)
475 475 dstname = os.path.join(dst, name)
476 476 copyfiles(srcname, dstname, hardlink)
477 477 else:
478 478 if hardlink:
479 479 try:
480 480 os_link(src, dst)
481 481 except (IOError, OSError):
482 482 hardlink = False
483 483 shutil.copy(src, dst)
484 484 else:
485 485 shutil.copy(src, dst)
486 486
487 487 class path_auditor(object):
488 488 '''ensure that a filesystem path contains no banned components.
489 489 the following properties of a path are checked:
490 490
491 491 - under top-level .hg
492 492 - starts at the root of a windows drive
493 493 - contains ".."
494 494 - traverses a symlink (e.g. a/symlink_here/b)
495 495 - inside a nested repository'''
496 496
497 497 def __init__(self, root):
498 498 self.audited = set()
499 499 self.auditeddir = set()
500 500 self.root = root
501 501
502 502 def __call__(self, path):
503 503 if path in self.audited:
504 504 return
505 505 normpath = os.path.normcase(path)
506 506 parts = splitpath(normpath)
507 507 if (os.path.splitdrive(path)[0]
508 508 or parts[0].lower() in ('.hg', '.hg.', '')
509 509 or os.pardir in parts):
510 510 raise Abort(_("path contains illegal component: %s") % path)
511 511 if '.hg' in path.lower():
512 512 lparts = [p.lower() for p in parts]
513 513 for p in '.hg', '.hg.':
514 514 if p in lparts[1:]:
515 515 pos = lparts.index(p)
516 516 base = os.path.join(*parts[:pos])
517 517 raise Abort(_('path %r is inside repo %r') % (path, base))
518 518 def check(prefix):
519 519 curpath = os.path.join(self.root, prefix)
520 520 try:
521 521 st = os.lstat(curpath)
522 522 except OSError, err:
523 523 # EINVAL can be raised as invalid path syntax under win32.
524 524 # They must be ignored for patterns can be checked too.
525 525 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
526 526 raise
527 527 else:
528 528 if stat.S_ISLNK(st.st_mode):
529 529 raise Abort(_('path %r traverses symbolic link %r') %
530 530 (path, prefix))
531 531 elif (stat.S_ISDIR(st.st_mode) and
532 532 os.path.isdir(os.path.join(curpath, '.hg'))):
533 533 raise Abort(_('path %r is inside repo %r') %
534 534 (path, prefix))
535 535 parts.pop()
536 536 prefixes = []
537 537 while parts:
538 538 prefix = os.sep.join(parts)
539 539 if prefix in self.auditeddir:
540 540 break
541 541 check(prefix)
542 542 prefixes.append(prefix)
543 543 parts.pop()
544 544
545 545 self.audited.add(path)
546 546 # only add prefixes to the cache after checking everything: we don't
547 547 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
548 548 self.auditeddir.update(prefixes)
549 549
550 550 def nlinks(pathname):
551 551 """Return number of hardlinks for the given file."""
552 552 return os.lstat(pathname).st_nlink
553 553
554 554 if hasattr(os, 'link'):
555 555 os_link = os.link
556 556 else:
557 557 def os_link(src, dst):
558 558 raise OSError(0, _("Hardlinks not supported"))
559 559
560 560 def lookup_reg(key, name=None, scope=None):
561 561 return None
562 562
563 563 if os.name == 'nt':
564 564 from windows import *
565 565 else:
566 566 from posix import *
567 567
568 568 def makelock(info, pathname):
569 569 try:
570 570 return os.symlink(info, pathname)
571 571 except OSError, why:
572 572 if why.errno == errno.EEXIST:
573 573 raise
574 574 except AttributeError: # no symlink in os
575 575 pass
576 576
577 577 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
578 578 os.write(ld, info)
579 579 os.close(ld)
580 580
581 581 def readlock(pathname):
582 582 try:
583 583 return os.readlink(pathname)
584 584 except OSError, why:
585 585 if why.errno not in (errno.EINVAL, errno.ENOSYS):
586 586 raise
587 587 except AttributeError: # no symlink in os
588 588 pass
589 589 return posixfile(pathname).read()
590 590
591 591 def fstat(fp):
592 592 '''stat file object that may not have fileno method.'''
593 593 try:
594 594 return os.fstat(fp.fileno())
595 595 except AttributeError:
596 596 return os.stat(fp.name)
597 597
598 598 # File system features
599 599
600 600 def checkcase(path):
601 601 """
602 602 Check whether the given path is on a case-sensitive filesystem
603 603
604 604 Requires a path (like /foo/.hg) ending with a foldable final
605 605 directory component.
606 606 """
607 607 s1 = os.stat(path)
608 608 d, b = os.path.split(path)
609 609 p2 = os.path.join(d, b.upper())
610 610 if path == p2:
611 611 p2 = os.path.join(d, b.lower())
612 612 try:
613 613 s2 = os.stat(p2)
614 614 if s2 == s1:
615 615 return False
616 616 return True
617 617 except:
618 618 return True
619 619
620 620 _fspathcache = {}
621 621 def fspath(name, root):
622 622 '''Get name in the case stored in the filesystem
623 623
624 624 The name is either relative to root, or it is an absolute path starting
625 625 with root. Note that this function is unnecessary, and should not be
626 626 called, for case-sensitive filesystems (simply because it's expensive).
627 627 '''
628 628 # If name is absolute, make it relative
629 629 if name.lower().startswith(root.lower()):
630 630 l = len(root)
631 631 if name[l] == os.sep or name[l] == os.altsep:
632 632 l = l + 1
633 633 name = name[l:]
634 634
635 635 if not os.path.exists(os.path.join(root, name)):
636 636 return None
637 637
638 638 seps = os.sep
639 639 if os.altsep:
640 640 seps = seps + os.altsep
641 641 # Protect backslashes. This gets silly very quickly.
642 642 seps.replace('\\','\\\\')
643 643 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
644 644 dir = os.path.normcase(os.path.normpath(root))
645 645 result = []
646 646 for part, sep in pattern.findall(name):
647 647 if sep:
648 648 result.append(sep)
649 649 continue
650 650
651 651 if dir not in _fspathcache:
652 652 _fspathcache[dir] = os.listdir(dir)
653 653 contents = _fspathcache[dir]
654 654
655 655 lpart = part.lower()
656 656 lenp = len(part)
657 657 for n in contents:
658 658 if lenp == len(n) and n.lower() == lpart:
659 659 result.append(n)
660 660 break
661 661 else:
662 662 # Cannot happen, as the file exists!
663 663 result.append(part)
664 664 dir = os.path.join(dir, lpart)
665 665
666 666 return ''.join(result)
667 667
668 668 def checkexec(path):
669 669 """
670 670 Check whether the given path is on a filesystem with UNIX-like exec flags
671 671
672 672 Requires a directory (like /foo/.hg)
673 673 """
674 674
675 675 # VFAT on some Linux versions can flip mode but it doesn't persist
676 676 # a FS remount. Frequently we can detect it if files are created
677 677 # with exec bit on.
678 678
679 679 try:
680 680 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
681 681 fh, fn = tempfile.mkstemp("", "", path)
682 682 try:
683 683 os.close(fh)
684 684 m = os.stat(fn).st_mode & 0777
685 685 new_file_has_exec = m & EXECFLAGS
686 686 os.chmod(fn, m ^ EXECFLAGS)
687 687 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
688 688 finally:
689 689 os.unlink(fn)
690 690 except (IOError, OSError):
691 691 # we don't care, the user probably won't be able to commit anyway
692 692 return False
693 693 return not (new_file_has_exec or exec_flags_cannot_flip)
694 694
695 695 def checklink(path):
696 696 """check whether the given path is on a symlink-capable filesystem"""
697 697 # mktemp is not racy because symlink creation will fail if the
698 698 # file already exists
699 699 name = tempfile.mktemp(dir=path)
700 700 try:
701 701 os.symlink(".", name)
702 702 os.unlink(name)
703 703 return True
704 704 except (OSError, AttributeError):
705 705 return False
706 706
707 707 def needbinarypatch():
708 708 """return True if patches should be applied in binary mode by default."""
709 709 return os.name == 'nt'
710 710
711 711 def endswithsep(path):
712 712 '''Check path ends with os.sep or os.altsep.'''
713 713 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
714 714
715 715 def splitpath(path):
716 716 '''Split path by os.sep.
717 717 Note that this function does not use os.altsep because this is
718 718 an alternative of simple "xxx.split(os.sep)".
719 719 It is recommended to use os.path.normpath() before using this
720 720 function if need.'''
721 721 return path.split(os.sep)
722 722
723 723 def gui():
724 724 '''Are we running in a GUI?'''
725 725 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
726 726
727 727 def mktempcopy(name, emptyok=False, createmode=None):
728 728 """Create a temporary file with the same contents from name
729 729
730 730 The permission bits are copied from the original file.
731 731
732 732 If the temporary file is going to be truncated immediately, you
733 733 can use emptyok=True as an optimization.
734 734
735 735 Returns the name of the temporary file.
736 736 """
737 737 d, fn = os.path.split(name)
738 738 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
739 739 os.close(fd)
740 740 # Temporary files are created with mode 0600, which is usually not
741 741 # what we want. If the original file already exists, just copy
742 742 # its mode. Otherwise, manually obey umask.
743 743 try:
744 744 st_mode = os.lstat(name).st_mode & 0777
745 745 except OSError, inst:
746 746 if inst.errno != errno.ENOENT:
747 747 raise
748 748 st_mode = createmode
749 749 if st_mode is None:
750 750 st_mode = ~umask
751 751 st_mode &= 0666
752 752 os.chmod(temp, st_mode)
753 753 if emptyok:
754 754 return temp
755 755 try:
756 756 try:
757 757 ifp = posixfile(name, "rb")
758 758 except IOError, inst:
759 759 if inst.errno == errno.ENOENT:
760 760 return temp
761 761 if not getattr(inst, 'filename', None):
762 762 inst.filename = name
763 763 raise
764 764 ofp = posixfile(temp, "wb")
765 765 for chunk in filechunkiter(ifp):
766 766 ofp.write(chunk)
767 767 ifp.close()
768 768 ofp.close()
769 769 except:
770 770 try: os.unlink(temp)
771 771 except: pass
772 772 raise
773 773 return temp
774 774
775 775 class atomictempfile(object):
776 776 """file-like object that atomically updates a file
777 777
778 778 All writes will be redirected to a temporary copy of the original
779 779 file. When rename is called, the copy is renamed to the original
780 780 name, making the changes visible.
781 781 """
782 782 def __init__(self, name, mode, createmode):
783 783 self.__name = name
784 784 self._fp = None
785 785 self.temp = mktempcopy(name, emptyok=('w' in mode),
786 786 createmode=createmode)
787 787 self._fp = posixfile(self.temp, mode)
788 788
789 789 def __getattr__(self, name):
790 790 return getattr(self._fp, name)
791 791
792 792 def rename(self):
793 793 if not self._fp.closed:
794 794 self._fp.close()
795 795 rename(self.temp, localpath(self.__name))
796 796
797 797 def __del__(self):
798 798 if not self._fp:
799 799 return
800 800 if not self._fp.closed:
801 801 try:
802 802 os.unlink(self.temp)
803 803 except: pass
804 804 self._fp.close()
805 805
806 806 def makedirs(name, mode=None):
807 807 """recursive directory creation with parent mode inheritance"""
808 808 try:
809 809 os.mkdir(name)
810 810 if mode is not None:
811 811 os.chmod(name, mode)
812 812 return
813 813 except OSError, err:
814 814 if err.errno == errno.EEXIST:
815 815 return
816 816 if err.errno != errno.ENOENT:
817 817 raise
818 818 parent = os.path.abspath(os.path.dirname(name))
819 819 makedirs(parent, mode)
820 820 makedirs(name, mode)
821 821
822 822 class opener(object):
823 823 """Open files relative to a base directory
824 824
825 825 This class is used to hide the details of COW semantics and
826 826 remote file access from higher level code.
827 827 """
828 828 def __init__(self, base, audit=True):
829 829 self.base = base
830 830 if audit:
831 831 self.audit_path = path_auditor(base)
832 832 else:
833 833 self.audit_path = always
834 834 self.createmode = None
835 835
836 836 @propertycache
837 837 def _can_symlink(self):
838 838 return checklink(self.base)
839 839
840 840 def _fixfilemode(self, name):
841 841 if self.createmode is None:
842 842 return
843 843 os.chmod(name, self.createmode & 0666)
844 844
845 845 def __call__(self, path, mode="r", text=False, atomictemp=False):
846 846 self.audit_path(path)
847 847 f = os.path.join(self.base, path)
848 848
849 849 if not text and "b" not in mode:
850 850 mode += "b" # for that other OS
851 851
852 852 nlink = -1
853 853 if mode not in ("r", "rb"):
854 854 try:
855 855 nlink = nlinks(f)
856 856 except OSError:
857 857 nlink = 0
858 858 d = os.path.dirname(f)
859 859 if not os.path.isdir(d):
860 860 makedirs(d, self.createmode)
861 861 if atomictemp:
862 862 return atomictempfile(f, mode, self.createmode)
863 863 if nlink > 1:
864 864 rename(mktempcopy(f), f)
865 865 fp = posixfile(f, mode)
866 866 if nlink == 0:
867 867 self._fixfilemode(f)
868 868 return fp
869 869
870 870 def symlink(self, src, dst):
871 871 self.audit_path(dst)
872 872 linkname = os.path.join(self.base, dst)
873 873 try:
874 874 os.unlink(linkname)
875 875 except OSError:
876 876 pass
877 877
878 878 dirname = os.path.dirname(linkname)
879 879 if not os.path.exists(dirname):
880 880 makedirs(dirname, self.createmode)
881 881
882 882 if self._can_symlink:
883 883 try:
884 884 os.symlink(src, linkname)
885 885 except OSError, err:
886 886 raise OSError(err.errno, _('could not symlink to %r: %s') %
887 887 (src, err.strerror), linkname)
888 888 else:
889 889 f = self(dst, "w")
890 890 f.write(src)
891 891 f.close()
892 892 self._fixfilemode(dst)
893 893
894 894 class chunkbuffer(object):
895 895 """Allow arbitrary sized chunks of data to be efficiently read from an
896 896 iterator over chunks of arbitrary size."""
897 897
898 898 def __init__(self, in_iter):
899 899 """in_iter is the iterator that's iterating over the input chunks.
900 900 targetsize is how big a buffer to try to maintain."""
901 901 self.iter = iter(in_iter)
902 902 self.buf = ''
903 903 self.targetsize = 2**16
904 904
905 905 def read(self, l):
906 906 """Read L bytes of data from the iterator of chunks of data.
907 907 Returns less than L bytes if the iterator runs dry."""
908 908 if l > len(self.buf) and self.iter:
909 909 # Clamp to a multiple of self.targetsize
910 910 targetsize = max(l, self.targetsize)
911 911 collector = cStringIO.StringIO()
912 912 collector.write(self.buf)
913 913 collected = len(self.buf)
914 914 for chunk in self.iter:
915 915 collector.write(chunk)
916 916 collected += len(chunk)
917 917 if collected >= targetsize:
918 918 break
919 919 if collected < targetsize:
920 920 self.iter = False
921 921 self.buf = collector.getvalue()
922 922 if len(self.buf) == l:
923 923 s, self.buf = str(self.buf), ''
924 924 else:
925 925 s, self.buf = self.buf[:l], buffer(self.buf, l)
926 926 return s
927 927
928 928 def filechunkiter(f, size=65536, limit=None):
929 929 """Create a generator that produces the data in the file size
930 930 (default 65536) bytes at a time, up to optional limit (default is
931 931 to read all data). Chunks may be less than size bytes if the
932 932 chunk is the last chunk in the file, or the file is a socket or
933 933 some other type of file that sometimes reads less data than is
934 934 requested."""
935 935 assert size >= 0
936 936 assert limit is None or limit >= 0
937 937 while True:
938 938 if limit is None: nbytes = size
939 939 else: nbytes = min(limit, size)
940 940 s = nbytes and f.read(nbytes)
941 941 if not s: break
942 942 if limit: limit -= len(s)
943 943 yield s
944 944
945 945 def makedate():
946 946 lt = time.localtime()
947 947 if lt[8] == 1 and time.daylight:
948 948 tz = time.altzone
949 949 else:
950 950 tz = time.timezone
951 951 return time.mktime(lt), tz
952 952
953 953 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
954 954 """represent a (unixtime, offset) tuple as a localized time.
955 955 unixtime is seconds since the epoch, and offset is the time zone's
956 956 number of seconds away from UTC. if timezone is false, do not
957 957 append time zone to string."""
958 958 t, tz = date or makedate()
959 959 if "%1" in format or "%2" in format:
960 960 sign = (tz > 0) and "-" or "+"
961 961 minutes = abs(tz) // 60
962 962 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
963 963 format = format.replace("%2", "%02d" % (minutes % 60))
964 964 s = time.strftime(format, time.gmtime(float(t) - tz))
965 965 return s
966 966
967 967 def shortdate(date=None):
968 968 """turn (timestamp, tzoff) tuple into iso 8631 date."""
969 969 return datestr(date, format='%Y-%m-%d')
970 970
971 971 def strdate(string, format, defaults=[]):
972 972 """parse a localized time string and return a (unixtime, offset) tuple.
973 973 if the string cannot be parsed, ValueError is raised."""
974 974 def timezone(string):
975 975 tz = string.split()[-1]
976 976 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
977 977 sign = (tz[0] == "+") and 1 or -1
978 978 hours = int(tz[1:3])
979 979 minutes = int(tz[3:5])
980 980 return -sign * (hours * 60 + minutes) * 60
981 981 if tz == "GMT" or tz == "UTC":
982 982 return 0
983 983 return None
984 984
985 985 # NOTE: unixtime = localunixtime + offset
986 986 offset, date = timezone(string), string
987 987 if offset != None:
988 988 date = " ".join(string.split()[:-1])
989 989
990 990 # add missing elements from defaults
991 991 for part in defaults:
992 992 found = [True for p in part if ("%"+p) in format]
993 993 if not found:
994 994 date += "@" + defaults[part]
995 995 format += "@%" + part[0]
996 996
997 997 timetuple = time.strptime(date, format)
998 998 localunixtime = int(calendar.timegm(timetuple))
999 999 if offset is None:
1000 1000 # local timezone
1001 1001 unixtime = int(time.mktime(timetuple))
1002 1002 offset = unixtime - localunixtime
1003 1003 else:
1004 1004 unixtime = localunixtime + offset
1005 1005 return unixtime, offset
1006 1006
1007 1007 def parsedate(date, formats=None, defaults=None):
1008 1008 """parse a localized date/time string and return a (unixtime, offset) tuple.
1009 1009
1010 1010 The date may be a "unixtime offset" string or in one of the specified
1011 1011 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1012 1012 """
1013 1013 if not date:
1014 1014 return 0, 0
1015 1015 if isinstance(date, tuple) and len(date) == 2:
1016 1016 return date
1017 1017 if not formats:
1018 1018 formats = defaultdateformats
1019 1019 date = date.strip()
1020 1020 try:
1021 1021 when, offset = map(int, date.split(' '))
1022 1022 except ValueError:
1023 1023 # fill out defaults
1024 1024 if not defaults:
1025 1025 defaults = {}
1026 1026 now = makedate()
1027 1027 for part in "d mb yY HI M S".split():
1028 1028 if part not in defaults:
1029 1029 if part[0] in "HMS":
1030 1030 defaults[part] = "00"
1031 1031 else:
1032 1032 defaults[part] = datestr(now, "%" + part[0])
1033 1033
1034 1034 for format in formats:
1035 1035 try:
1036 1036 when, offset = strdate(date, format, defaults)
1037 1037 except (ValueError, OverflowError):
1038 1038 pass
1039 1039 else:
1040 1040 break
1041 1041 else:
1042 1042 raise Abort(_('invalid date: %r ') % date)
1043 1043 # validate explicit (probably user-specified) date and
1044 1044 # time zone offset. values must fit in signed 32 bits for
1045 1045 # current 32-bit linux runtimes. timezones go from UTC-12
1046 1046 # to UTC+14
1047 1047 if abs(when) > 0x7fffffff:
1048 1048 raise Abort(_('date exceeds 32 bits: %d') % when)
1049 1049 if offset < -50400 or offset > 43200:
1050 1050 raise Abort(_('impossible time zone offset: %d') % offset)
1051 1051 return when, offset
1052 1052
1053 1053 def matchdate(date):
1054 1054 """Return a function that matches a given date match specifier
1055 1055
1056 1056 Formats include:
1057 1057
1058 1058 '{date}' match a given date to the accuracy provided
1059 1059
1060 1060 '<{date}' on or before a given date
1061 1061
1062 1062 '>{date}' on or after a given date
1063 1063
1064 1064 """
1065 1065
1066 1066 def lower(date):
1067 1067 d = dict(mb="1", d="1")
1068 1068 return parsedate(date, extendeddateformats, d)[0]
1069 1069
1070 1070 def upper(date):
1071 1071 d = dict(mb="12", HI="23", M="59", S="59")
1072 1072 for days in "31 30 29".split():
1073 1073 try:
1074 1074 d["d"] = days
1075 1075 return parsedate(date, extendeddateformats, d)[0]
1076 1076 except:
1077 1077 pass
1078 1078 d["d"] = "28"
1079 1079 return parsedate(date, extendeddateformats, d)[0]
1080 1080
1081 1081 date = date.strip()
1082 1082 if date[0] == "<":
1083 1083 when = upper(date[1:])
1084 1084 return lambda x: x <= when
1085 1085 elif date[0] == ">":
1086 1086 when = lower(date[1:])
1087 1087 return lambda x: x >= when
1088 1088 elif date[0] == "-":
1089 1089 try:
1090 1090 days = int(date[1:])
1091 1091 except ValueError:
1092 1092 raise Abort(_("invalid day spec: %s") % date[1:])
1093 1093 when = makedate()[0] - days * 3600 * 24
1094 1094 return lambda x: x >= when
1095 1095 elif " to " in date:
1096 1096 a, b = date.split(" to ")
1097 1097 start, stop = lower(a), upper(b)
1098 1098 return lambda x: x >= start and x <= stop
1099 1099 else:
1100 1100 start, stop = lower(date), upper(date)
1101 1101 return lambda x: x >= start and x <= stop
1102 1102
1103 1103 def shortuser(user):
1104 1104 """Return a short representation of a user name or email address."""
1105 1105 f = user.find('@')
1106 1106 if f >= 0:
1107 1107 user = user[:f]
1108 1108 f = user.find('<')
1109 1109 if f >= 0:
1110 1110 user = user[f+1:]
1111 1111 f = user.find(' ')
1112 1112 if f >= 0:
1113 1113 user = user[:f]
1114 1114 f = user.find('.')
1115 1115 if f >= 0:
1116 1116 user = user[:f]
1117 1117 return user
1118 1118
1119 1119 def email(author):
1120 1120 '''get email of author.'''
1121 1121 r = author.find('>')
1122 1122 if r == -1: r = None
1123 1123 return author[author.find('<')+1:r]
1124 1124
1125 1125 def ellipsis(text, maxlength=400):
1126 1126 """Trim string to at most maxlength (default: 400) characters."""
1127 1127 if len(text) <= maxlength:
1128 1128 return text
1129 1129 else:
1130 1130 return "%s..." % (text[:maxlength-3])
1131 1131
1132 1132 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1133 1133 '''yield every hg repository under path, recursively.'''
1134 1134 def errhandler(err):
1135 1135 if err.filename == path:
1136 1136 raise err
1137 1137 if followsym and hasattr(os.path, 'samestat'):
1138 1138 def _add_dir_if_not_there(dirlst, dirname):
1139 1139 match = False
1140 1140 samestat = os.path.samestat
1141 1141 dirstat = os.stat(dirname)
1142 1142 for lstdirstat in dirlst:
1143 1143 if samestat(dirstat, lstdirstat):
1144 1144 match = True
1145 1145 break
1146 1146 if not match:
1147 1147 dirlst.append(dirstat)
1148 1148 return not match
1149 1149 else:
1150 1150 followsym = False
1151 1151
1152 1152 if (seen_dirs is None) and followsym:
1153 1153 seen_dirs = []
1154 1154 _add_dir_if_not_there(seen_dirs, path)
1155 1155 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1156 1156 if '.hg' in dirs:
1157 1157 yield root # found a repository
1158 1158 qroot = os.path.join(root, '.hg', 'patches')
1159 1159 if os.path.isdir(os.path.join(qroot, '.hg')):
1160 1160 yield qroot # we have a patch queue repo here
1161 1161 if recurse:
1162 1162 # avoid recursing inside the .hg directory
1163 1163 dirs.remove('.hg')
1164 1164 else:
1165 1165 dirs[:] = [] # don't descend further
1166 1166 elif followsym:
1167 1167 newdirs = []
1168 1168 for d in dirs:
1169 1169 fname = os.path.join(root, d)
1170 1170 if _add_dir_if_not_there(seen_dirs, fname):
1171 1171 if os.path.islink(fname):
1172 1172 for hgname in walkrepos(fname, True, seen_dirs):
1173 1173 yield hgname
1174 1174 else:
1175 1175 newdirs.append(d)
1176 1176 dirs[:] = newdirs
1177 1177
1178 1178 _rcpath = None
1179 1179
1180 1180 def os_rcpath():
1181 1181 '''return default os-specific hgrc search path'''
1182 1182 path = system_rcpath()
1183 1183 path.extend(user_rcpath())
1184 1184 path = [os.path.normpath(f) for f in path]
1185 1185 return path
1186 1186
1187 1187 def rcpath():
1188 1188 '''return hgrc search path. if env var HGRCPATH is set, use it.
1189 1189 for each item in path, if directory, use files ending in .rc,
1190 1190 else use item.
1191 1191 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1192 1192 if no HGRCPATH, use default os-specific path.'''
1193 1193 global _rcpath
1194 1194 if _rcpath is None:
1195 1195 if 'HGRCPATH' in os.environ:
1196 1196 _rcpath = []
1197 1197 for p in os.environ['HGRCPATH'].split(os.pathsep):
1198 1198 if not p: continue
1199 1199 if os.path.isdir(p):
1200 1200 for f, kind in osutil.listdir(p):
1201 1201 if f.endswith('.rc'):
1202 1202 _rcpath.append(os.path.join(p, f))
1203 1203 else:
1204 1204 _rcpath.append(p)
1205 1205 else:
1206 1206 _rcpath = os_rcpath()
1207 1207 return _rcpath
1208 1208
1209 1209 def bytecount(nbytes):
1210 1210 '''return byte count formatted as readable string, with units'''
1211 1211
1212 1212 units = (
1213 1213 (100, 1<<30, _('%.0f GB')),
1214 1214 (10, 1<<30, _('%.1f GB')),
1215 1215 (1, 1<<30, _('%.2f GB')),
1216 1216 (100, 1<<20, _('%.0f MB')),
1217 1217 (10, 1<<20, _('%.1f MB')),
1218 1218 (1, 1<<20, _('%.2f MB')),
1219 1219 (100, 1<<10, _('%.0f KB')),
1220 1220 (10, 1<<10, _('%.1f KB')),
1221 1221 (1, 1<<10, _('%.2f KB')),
1222 1222 (1, 1, _('%.0f bytes')),
1223 1223 )
1224 1224
1225 1225 for multiplier, divisor, format in units:
1226 1226 if nbytes >= divisor * multiplier:
1227 1227 return format % (nbytes / float(divisor))
1228 1228 return units[-1][2] % nbytes
1229 1229
1230 1230 def drop_scheme(scheme, path):
1231 1231 sc = scheme + ':'
1232 1232 if path.startswith(sc):
1233 1233 path = path[len(sc):]
1234 1234 if path.startswith('//'):
1235 1235 path = path[2:]
1236 1236 return path
1237 1237
1238 1238 def uirepr(s):
1239 1239 # Avoid double backslash in Windows path repr()
1240 1240 return repr(s).replace('\\\\', '\\')
1241 1241
1242 1242 def termwidth():
1243 1243 if 'COLUMNS' in os.environ:
1244 1244 try:
1245 1245 return int(os.environ['COLUMNS'])
1246 1246 except ValueError:
1247 1247 pass
1248 1248 try:
1249 1249 import termios, array, fcntl
1250 1250 for dev in (sys.stdout, sys.stdin):
1251 1251 try:
1252 1252 try:
1253 1253 fd = dev.fileno()
1254 1254 except AttributeError:
1255 1255 continue
1256 1256 if not os.isatty(fd):
1257 1257 continue
1258 1258 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
1259 1259 return array.array('h', arri)[1]
1260 1260 except ValueError:
1261 1261 pass
1262 1262 except ImportError:
1263 1263 pass
1264 1264 return 80
1265 1265
1266 1266 def wrap(line, hangindent, width=None):
1267 1267 if width is None:
1268 1268 width = termwidth() - 2
1269 1269 if width <= hangindent:
1270 1270 # adjust for weird terminal size
1271 1271 width = max(78, hangindent + 1)
1272 1272 padding = '\n' + ' ' * hangindent
1273 1273 # To avoid corrupting multi-byte characters in line, we must wrap
1274 1274 # a Unicode string instead of a bytestring.
1275 1275 try:
1276 1276 u = line.decode(encoding.encoding)
1277 1277 w = padding.join(textwrap.wrap(u, width=width - hangindent))
1278 1278 return w.encode(encoding.encoding)
1279 1279 except UnicodeDecodeError:
1280 1280 return padding.join(textwrap.wrap(line, width=width - hangindent))
1281 1281
1282 1282 def iterlines(iterator):
1283 1283 for chunk in iterator:
1284 1284 for line in chunk.splitlines():
1285 1285 yield line
General Comments 0
You need to be logged in to leave comments. Login now