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