/mercurial/windows.py
Python | 338 lines | 267 code | 29 blank | 42 comment | 27 complexity | 7d5dcabd251f4e05a24599a4cd7ba726 MD5 | raw file
Possible License(s): GPL-2.0
- # windows.py - Windows utility function implementations for Mercurial
- #
- # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
- #
- # This software may be used and distributed according to the terms of the
- # GNU General Public License version 2 or any later version.
- from i18n import _
- import osutil, encoding
- import errno, msvcrt, os, re, stat, sys, _winreg
- import win32
- executablepath = win32.executablepath
- getuser = win32.getuser
- hidewindow = win32.hidewindow
- makedir = win32.makedir
- nlinks = win32.nlinks
- oslink = win32.oslink
- samedevice = win32.samedevice
- samefile = win32.samefile
- setsignalhandler = win32.setsignalhandler
- spawndetached = win32.spawndetached
- split = os.path.split
- termwidth = win32.termwidth
- testpid = win32.testpid
- unlink = win32.unlink
- umask = 0022
- # wrap osutil.posixfile to provide friendlier exceptions
- def posixfile(name, mode='r', buffering=-1):
- try:
- return osutil.posixfile(name, mode, buffering)
- except WindowsError, err:
- raise IOError(err.errno, '%s: %s' % (name, err.strerror))
- posixfile.__doc__ = osutil.posixfile.__doc__
- class winstdout(object):
- '''stdout on windows misbehaves if sent through a pipe'''
- def __init__(self, fp):
- self.fp = fp
- def __getattr__(self, key):
- return getattr(self.fp, key)
- def close(self):
- try:
- self.fp.close()
- except IOError:
- pass
- def write(self, s):
- try:
- # This is workaround for "Not enough space" error on
- # writing large size of data to console.
- limit = 16000
- l = len(s)
- start = 0
- self.softspace = 0
- while start < l:
- end = start + limit
- self.fp.write(s[start:end])
- start = end
- except IOError, inst:
- if inst.errno != 0:
- raise
- self.close()
- raise IOError(errno.EPIPE, 'Broken pipe')
- def flush(self):
- try:
- return self.fp.flush()
- except IOError, inst:
- if inst.errno != errno.EINVAL:
- raise
- self.close()
- raise IOError(errno.EPIPE, 'Broken pipe')
- sys.__stdout__ = sys.stdout = winstdout(sys.stdout)
- def _is_win_9x():
- '''return true if run on windows 95, 98 or me.'''
- try:
- return sys.getwindowsversion()[3] == 1
- except AttributeError:
- return 'command' in os.environ.get('comspec', '')
- def openhardlinks():
- return not _is_win_9x()
- def parsepatchoutput(output_line):
- """parses the output produced by patch and returns the filename"""
- pf = output_line[14:]
- if pf[0] == '`':
- pf = pf[1:-1] # Remove the quotes
- return pf
- def sshargs(sshcmd, host, user, port):
- '''Build argument list for ssh or Plink'''
- pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
- args = user and ("%s@%s" % (user, host)) or host
- return port and ("%s %s %s" % (args, pflag, port)) or args
- def setflags(f, l, x):
- pass
- def copymode(src, dst, mode=None):
- pass
- def checkexec(path):
- return False
- def checklink(path):
- return False
- def setbinary(fd):
- # When run without console, pipes may expose invalid
- # fileno(), usually set to -1.
- fno = getattr(fd, 'fileno', None)
- if fno is not None and fno() >= 0:
- msvcrt.setmode(fno(), os.O_BINARY)
- def pconvert(path):
- return path.replace(os.sep, '/')
- def localpath(path):
- return path.replace('/', '\\')
- def normpath(path):
- return pconvert(os.path.normpath(path))
- def normcase(path):
- return encoding.upper(path)
- def samestat(s1, s2):
- return False
- # A sequence of backslashes is special iff it precedes a double quote:
- # - if there's an even number of backslashes, the double quote is not
- # quoted (i.e. it ends the quoted region)
- # - if there's an odd number of backslashes, the double quote is quoted
- # - in both cases, every pair of backslashes is unquoted into a single
- # backslash
- # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
- # So, to quote a string, we must surround it in double quotes, double
- # the number of backslashes that precede double quotes and add another
- # backslash before every double quote (being careful with the double
- # quote we've appended to the end)
- _quotere = None
- def shellquote(s):
- global _quotere
- if _quotere is None:
- _quotere = re.compile(r'(\\*)("|\\$)')
- return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
- def quotecommand(cmd):
- """Build a command string suitable for os.popen* calls."""
- if sys.version_info < (2, 7, 1):
- # Python versions since 2.7.1 do this extra quoting themselves
- return '"' + cmd + '"'
- return cmd
- def popen(command, mode='r'):
- # Work around "popen spawned process may not write to stdout
- # under windows"
- # http://bugs.python.org/issue1366
- command += " 2> %s" % os.devnull
- return os.popen(quotecommand(command), mode)
- def explainexit(code):
- return _("exited with status %d") % code, code
- # if you change this stub into a real check, please try to implement the
- # username and groupname functions above, too.
- def isowner(st):
- return True
- def findexe(command):
- '''Find executable for command searching like cmd.exe does.
- If command is a basename then PATH is searched for command.
- PATH isn't searched if command is an absolute or relative path.
- An extension from PATHEXT is found and added if not present.
- If command isn't found None is returned.'''
- pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
- pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
- if os.path.splitext(command)[1].lower() in pathexts:
- pathexts = ['']
- def findexisting(pathcommand):
- 'Will append extension (if needed) and return existing file'
- for ext in pathexts:
- executable = pathcommand + ext
- if os.path.exists(executable):
- return executable
- return None
- if os.sep in command:
- return findexisting(command)
- for path in os.environ.get('PATH', '').split(os.pathsep):
- executable = findexisting(os.path.join(path, command))
- if executable is not None:
- return executable
- return findexisting(os.path.expanduser(os.path.expandvars(command)))
- _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
- def statfiles(files):
- '''Stat each file in files. Yield each stat, or None if a file
- does not exist or has a type we don't care about.
- Cluster and cache stat per directory to minimize number of OS stat calls.'''
- dircache = {} # dirname -> filename -> status | None if file does not exist
- getkind = stat.S_IFMT
- for nf in files:
- nf = normcase(nf)
- dir, base = os.path.split(nf)
- if not dir:
- dir = '.'
- cache = dircache.get(dir, None)
- if cache is None:
- try:
- dmap = dict([(normcase(n), s)
- for n, k, s in osutil.listdir(dir, True)
- if getkind(s.st_mode) in _wantedkinds])
- except OSError, err:
- # handle directory not found in Python version prior to 2.5
- # Python <= 2.4 returns native Windows code 3 in errno
- # Python >= 2.5 returns ENOENT and adds winerror field
- # EINVAL is raised if dir is not a directory.
- if err.errno not in (3, errno.ENOENT, errno.EINVAL,
- errno.ENOTDIR):
- raise
- dmap = {}
- cache = dircache.setdefault(dir, dmap)
- yield cache.get(base, None)
- def username(uid=None):
- """Return the name of the user with the given uid.
- If uid is None, return the name of the current user."""
- return None
- def groupname(gid=None):
- """Return the name of the group with the given gid.
- If gid is None, return the name of the current group."""
- return None
- def _removedirs(name):
- """special version of os.removedirs that does not remove symlinked
- directories or junction points if they actually contain files"""
- if osutil.listdir(name):
- return
- os.rmdir(name)
- head, tail = os.path.split(name)
- if not tail:
- head, tail = os.path.split(head)
- while head and tail:
- try:
- if osutil.listdir(head):
- return
- os.rmdir(head)
- except (ValueError, OSError):
- break
- head, tail = os.path.split(head)
- def unlinkpath(f, ignoremissing=False):
- """unlink and remove the directory if it is empty"""
- try:
- unlink(f)
- except OSError, e:
- if not (ignoremissing and e.errno == errno.ENOENT):
- raise
- # try removing directories that might now be empty
- try:
- _removedirs(os.path.dirname(f))
- except OSError:
- pass
- def rename(src, dst):
- '''atomically rename file src to dst, replacing dst if it exists'''
- try:
- os.rename(src, dst)
- except OSError, e:
- if e.errno != errno.EEXIST:
- raise
- unlink(dst)
- os.rename(src, dst)
- def gethgcmd():
- return [sys.executable] + sys.argv[:1]
- def groupmembers(name):
- # Don't support groups on Windows for now
- raise KeyError
- def isexec(f):
- return False
- class cachestat(object):
- def __init__(self, path):
- pass
- def cacheable(self):
- return False
- def lookupreg(key, valname=None, scope=None):
- ''' Look up a key/value name in the Windows registry.
- valname: value name. If unspecified, the default value for the key
- is used.
- scope: optionally specify scope for registry lookup, this can be
- a sequence of scopes to look up in order. Default (CURRENT_USER,
- LOCAL_MACHINE).
- '''
- if scope is None:
- scope = (_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE)
- elif not isinstance(scope, (list, tuple)):
- scope = (scope,)
- for s in scope:
- try:
- val = _winreg.QueryValueEx(_winreg.OpenKey(s, key), valname)[0]
- # never let a Unicode string escape into the wild
- return encoding.tolocal(val.encode('UTF-8'))
- except EnvironmentError:
- pass
- expandglobs = True
- def statislink(st):
- '''check whether a stat result is a symlink'''
- return False
- def statisexec(st):
- '''check whether a stat result is an executable file'''
- return False