eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/windows.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # windows.py - Windows utility function implementations for Mercurial
       
     2 #
       
     3 #  Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 from i18n import _
       
     9 import osutil, error
       
    10 import errno, msvcrt, os, re, sys, random, subprocess
       
    11 
       
    12 nulldev = 'NUL:'
       
    13 umask = 002
       
    14 
       
    15 # wrap osutil.posixfile to provide friendlier exceptions
       
    16 def posixfile(name, mode='r', buffering=-1):
       
    17     try:
       
    18         return osutil.posixfile(name, mode, buffering)
       
    19     except WindowsError, err:
       
    20         raise IOError(err.errno, '%s: %s' % (name, err.strerror))
       
    21 posixfile.__doc__ = osutil.posixfile.__doc__
       
    22 
       
    23 class winstdout(object):
       
    24     '''stdout on windows misbehaves if sent through a pipe'''
       
    25 
       
    26     def __init__(self, fp):
       
    27         self.fp = fp
       
    28 
       
    29     def __getattr__(self, key):
       
    30         return getattr(self.fp, key)
       
    31 
       
    32     def close(self):
       
    33         try:
       
    34             self.fp.close()
       
    35         except: pass
       
    36 
       
    37     def write(self, s):
       
    38         try:
       
    39             # This is workaround for "Not enough space" error on
       
    40             # writing large size of data to console.
       
    41             limit = 16000
       
    42             l = len(s)
       
    43             start = 0
       
    44             self.softspace = 0
       
    45             while start < l:
       
    46                 end = start + limit
       
    47                 self.fp.write(s[start:end])
       
    48                 start = end
       
    49         except IOError, inst:
       
    50             if inst.errno != 0:
       
    51                 raise
       
    52             self.close()
       
    53             raise IOError(errno.EPIPE, 'Broken pipe')
       
    54 
       
    55     def flush(self):
       
    56         try:
       
    57             return self.fp.flush()
       
    58         except IOError, inst:
       
    59             if inst.errno != errno.EINVAL:
       
    60                 raise
       
    61             self.close()
       
    62             raise IOError(errno.EPIPE, 'Broken pipe')
       
    63 
       
    64 sys.stdout = winstdout(sys.stdout)
       
    65 
       
    66 def _is_win_9x():
       
    67     '''return true if run on windows 95, 98 or me.'''
       
    68     try:
       
    69         return sys.getwindowsversion()[3] == 1
       
    70     except AttributeError:
       
    71         return 'command' in os.environ.get('comspec', '')
       
    72 
       
    73 def openhardlinks():
       
    74     return not _is_win_9x() and "win32api" in globals()
       
    75 
       
    76 def system_rcpath():
       
    77     try:
       
    78         return system_rcpath_win32()
       
    79     except:
       
    80         return [r'c:\mercurial\mercurial.ini']
       
    81 
       
    82 def user_rcpath():
       
    83     '''return os-specific hgrc search path to the user dir'''
       
    84     try:
       
    85         path = user_rcpath_win32()
       
    86     except:
       
    87         home = os.path.expanduser('~')
       
    88         path = [os.path.join(home, 'mercurial.ini'),
       
    89                 os.path.join(home, '.hgrc')]
       
    90     userprofile = os.environ.get('USERPROFILE')
       
    91     if userprofile:
       
    92         path.append(os.path.join(userprofile, 'mercurial.ini'))
       
    93         path.append(os.path.join(userprofile, '.hgrc'))
       
    94     return path
       
    95 
       
    96 def parse_patch_output(output_line):
       
    97     """parses the output produced by patch and returns the filename"""
       
    98     pf = output_line[14:]
       
    99     if pf[0] == '`':
       
   100         pf = pf[1:-1] # Remove the quotes
       
   101     return pf
       
   102 
       
   103 def sshargs(sshcmd, host, user, port):
       
   104     '''Build argument list for ssh or Plink'''
       
   105     pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
       
   106     args = user and ("%s@%s" % (user, host)) or host
       
   107     return port and ("%s %s %s" % (args, pflag, port)) or args
       
   108 
       
   109 def testpid(pid):
       
   110     '''return False if pid dead, True if running or not known'''
       
   111     return True
       
   112 
       
   113 def set_flags(f, l, x):
       
   114     pass
       
   115 
       
   116 def set_binary(fd):
       
   117     # When run without console, pipes may expose invalid
       
   118     # fileno(), usually set to -1.
       
   119     if hasattr(fd, 'fileno') and fd.fileno() >= 0:
       
   120         msvcrt.setmode(fd.fileno(), os.O_BINARY)
       
   121 
       
   122 def pconvert(path):
       
   123     return '/'.join(path.split(os.sep))
       
   124 
       
   125 def localpath(path):
       
   126     return path.replace('/', '\\')
       
   127 
       
   128 def normpath(path):
       
   129     return pconvert(os.path.normpath(path))
       
   130 
       
   131 def realpath(path):
       
   132     '''
       
   133     Returns the true, canonical file system path equivalent to the given
       
   134     path.
       
   135     '''
       
   136     # TODO: There may be a more clever way to do this that also handles other,
       
   137     # less common file systems.
       
   138     return os.path.normpath(os.path.normcase(os.path.realpath(path)))
       
   139 
       
   140 def samestat(s1, s2):
       
   141     return False
       
   142 
       
   143 # A sequence of backslashes is special iff it precedes a double quote:
       
   144 # - if there's an even number of backslashes, the double quote is not
       
   145 #   quoted (i.e. it ends the quoted region)
       
   146 # - if there's an odd number of backslashes, the double quote is quoted
       
   147 # - in both cases, every pair of backslashes is unquoted into a single
       
   148 #   backslash
       
   149 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
       
   150 # So, to quote a string, we must surround it in double quotes, double
       
   151 # the number of backslashes that preceed double quotes and add another
       
   152 # backslash before every double quote (being careful with the double
       
   153 # quote we've appended to the end)
       
   154 _quotere = None
       
   155 def shellquote(s):
       
   156     global _quotere
       
   157     if _quotere is None:
       
   158         _quotere = re.compile(r'(\\*)("|\\$)')
       
   159     return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
       
   160 
       
   161 def quotecommand(cmd):
       
   162     """Build a command string suitable for os.popen* calls."""
       
   163     if sys.version_info < (2, 7, 1):
       
   164         # Python versions since 2.7.1 do this extra quoting themselves
       
   165         return '"' + cmd + '"'
       
   166     return cmd
       
   167 
       
   168 def popen(command, mode='r'):
       
   169     # Work around "popen spawned process may not write to stdout
       
   170     # under windows"
       
   171     # http://bugs.python.org/issue1366
       
   172     command += " 2> %s" % nulldev
       
   173     return os.popen(quotecommand(command), mode)
       
   174 
       
   175 def explain_exit(code):
       
   176     return _("exited with status %d") % code, code
       
   177 
       
   178 # if you change this stub into a real check, please try to implement the
       
   179 # username and groupname functions above, too.
       
   180 def isowner(st):
       
   181     return True
       
   182 
       
   183 def find_exe(command):
       
   184     '''Find executable for command searching like cmd.exe does.
       
   185     If command is a basename then PATH is searched for command.
       
   186     PATH isn't searched if command is an absolute or relative path.
       
   187     An extension from PATHEXT is found and added if not present.
       
   188     If command isn't found None is returned.'''
       
   189     pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
       
   190     pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
       
   191     if os.path.splitext(command)[1].lower() in pathexts:
       
   192         pathexts = ['']
       
   193 
       
   194     def findexisting(pathcommand):
       
   195         'Will append extension (if needed) and return existing file'
       
   196         for ext in pathexts:
       
   197             executable = pathcommand + ext
       
   198             if os.path.exists(executable):
       
   199                 return executable
       
   200         return None
       
   201 
       
   202     if os.sep in command:
       
   203         return findexisting(command)
       
   204 
       
   205     for path in os.environ.get('PATH', '').split(os.pathsep):
       
   206         executable = findexisting(os.path.join(path, command))
       
   207         if executable is not None:
       
   208             return executable
       
   209     return findexisting(os.path.expanduser(os.path.expandvars(command)))
       
   210 
       
   211 def set_signal_handler():
       
   212     try:
       
   213         set_signal_handler_win32()
       
   214     except NameError:
       
   215         pass
       
   216 
       
   217 def statfiles(files):
       
   218     '''Stat each file in files and yield stat or None if file does not exist.
       
   219     Cluster and cache stat per directory to minimize number of OS stat calls.'''
       
   220     ncase = os.path.normcase
       
   221     dircache = {} # dirname -> filename -> status | None if file does not exist
       
   222     for nf in files:
       
   223         nf  = ncase(nf)
       
   224         dir, base = os.path.split(nf)
       
   225         if not dir:
       
   226             dir = '.'
       
   227         cache = dircache.get(dir, None)
       
   228         if cache is None:
       
   229             try:
       
   230                 dmap = dict([(ncase(n), s)
       
   231                     for n, k, s in osutil.listdir(dir, True)])
       
   232             except OSError, err:
       
   233                 # handle directory not found in Python version prior to 2.5
       
   234                 # Python <= 2.4 returns native Windows code 3 in errno
       
   235                 # Python >= 2.5 returns ENOENT and adds winerror field
       
   236                 # EINVAL is raised if dir is not a directory.
       
   237                 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
       
   238                                      errno.ENOTDIR):
       
   239                     raise
       
   240                 dmap = {}
       
   241             cache = dircache.setdefault(dir, dmap)
       
   242         yield cache.get(base, None)
       
   243 
       
   244 def getuser():
       
   245     '''return name of current user'''
       
   246     raise error.Abort(_('user name not available - set USERNAME '
       
   247                        'environment variable'))
       
   248 
       
   249 def username(uid=None):
       
   250     """Return the name of the user with the given uid.
       
   251 
       
   252     If uid is None, return the name of the current user."""
       
   253     return None
       
   254 
       
   255 def groupname(gid=None):
       
   256     """Return the name of the group with the given gid.
       
   257 
       
   258     If gid is None, return the name of the current group."""
       
   259     return None
       
   260 
       
   261 def _removedirs(name):
       
   262     """special version of os.removedirs that does not remove symlinked
       
   263     directories or junction points if they actually contain files"""
       
   264     if osutil.listdir(name):
       
   265         return
       
   266     os.rmdir(name)
       
   267     head, tail = os.path.split(name)
       
   268     if not tail:
       
   269         head, tail = os.path.split(head)
       
   270     while head and tail:
       
   271         try:
       
   272             if osutil.listdir(head):
       
   273                 return
       
   274             os.rmdir(head)
       
   275         except:
       
   276             break
       
   277         head, tail = os.path.split(head)
       
   278 
       
   279 def unlink(f):
       
   280     """unlink and remove the directory if it is empty"""
       
   281     os.unlink(f)
       
   282     # try removing directories that might now be empty
       
   283     try:
       
   284         _removedirs(os.path.dirname(f))
       
   285     except OSError:
       
   286         pass
       
   287 
       
   288 def rename(src, dst):
       
   289     '''atomically rename file src to dst, replacing dst if it exists'''
       
   290     try:
       
   291         os.rename(src, dst)
       
   292     except OSError: # FIXME: check err (EEXIST ?)
       
   293 
       
   294         # On windows, rename to existing file is not allowed, so we
       
   295         # must delete destination first. But if a file is open, unlink
       
   296         # schedules it for delete but does not delete it. Rename
       
   297         # happens immediately even for open files, so we rename
       
   298         # destination to a temporary name, then delete that. Then
       
   299         # rename is safe to do.
       
   300         # The temporary name is chosen at random to avoid the situation
       
   301         # where a file is left lying around from a previous aborted run.
       
   302 
       
   303         for tries in xrange(10):
       
   304             temp = '%s-%08x' % (dst, random.randint(0, 0xffffffff))
       
   305             try:
       
   306                 os.rename(dst, temp)  # raises OSError EEXIST if temp exists
       
   307                 break
       
   308             except OSError, e:
       
   309                 if e.errno != errno.EEXIST:
       
   310                     raise
       
   311         else:
       
   312             raise IOError, (errno.EEXIST, "No usable temporary filename found")
       
   313 
       
   314         try:
       
   315             os.unlink(temp)
       
   316         except:
       
   317             # Some rude AV-scanners on Windows may cause the unlink to
       
   318             # fail. Not aborting here just leaks the temp file, whereas
       
   319             # aborting at this point may leave serious inconsistencies.
       
   320             # Ideally, we would notify the user here.
       
   321             pass
       
   322         os.rename(src, dst)
       
   323 
       
   324 def spawndetached(args):
       
   325     # No standard library function really spawns a fully detached
       
   326     # process under win32 because they allocate pipes or other objects
       
   327     # to handle standard streams communications. Passing these objects
       
   328     # to the child process requires handle inheritance to be enabled
       
   329     # which makes really detached processes impossible.
       
   330     class STARTUPINFO:
       
   331         dwFlags = subprocess.STARTF_USESHOWWINDOW
       
   332         hStdInput = None
       
   333         hStdOutput = None
       
   334         hStdError = None
       
   335         wShowWindow = subprocess.SW_HIDE
       
   336 
       
   337     args = subprocess.list2cmdline(args)
       
   338     # Not running the command in shell mode makes python26 hang when
       
   339     # writing to hgweb output socket.
       
   340     comspec = os.environ.get("COMSPEC", "cmd.exe")
       
   341     args = comspec + " /c " + args
       
   342     hp, ht, pid, tid = subprocess.CreateProcess(
       
   343         None, args,
       
   344         # no special security
       
   345         None, None,
       
   346         # Do not inherit handles
       
   347         0,
       
   348         # DETACHED_PROCESS
       
   349         0x00000008,
       
   350         os.environ,
       
   351         os.getcwd(),
       
   352         STARTUPINFO())
       
   353     return pid
       
   354 
       
   355 def gethgcmd():
       
   356     return [sys.executable] + sys.argv[:1]
       
   357 
       
   358 def termwidth():
       
   359     # cmd.exe does not handle CR like a unix console, the CR is
       
   360     # counted in the line length. On 80 columns consoles, if 80
       
   361     # characters are written, the following CR won't apply on the
       
   362     # current line but on the new one. Keep room for it.
       
   363     return 79
       
   364 
       
   365 def groupmembers(name):
       
   366     # Don't support groups on Windows for now
       
   367     raise KeyError()
       
   368 
       
   369 try:
       
   370     # override functions with win32 versions if possible
       
   371     from win32 import *
       
   372 except ImportError:
       
   373     pass
       
   374 
       
   375 expandglobs = True