# HG changeset patch # User Matt Mackall # Date 1238093684 18000 # Node ID e710f0f592b2db54a75fe9131f17b8fe3519c6e9 # Parent 5ac1a72e5b74c74527c618b86216a4002f601edb util: split out posix, windows, and win32 modules diff -r 5ac1a72e5b74 -r e710f0f592b2 mercurial/posix.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/posix.py Thu Mar 26 13:54:44 2009 -0500 @@ -0,0 +1,222 @@ +""" +posix.py - Posix utility function implementations for Mercurial + + Copyright 2005-2009 Matt Mackall and others + +This software may be used and distributed according to the terms of +the GNU General Public License version 2, incorporated herein by +reference. +""" + +from i18n import _ +import os, sys, osutil, errno, stat, getpass + +posixfile = file +nulldev = '/dev/null' +normpath = os.path.normpath +samestat = os.path.samestat + +umask = os.umask(0) +os.umask(umask) + +def openhardlinks(): + '''return true if it is safe to hold open file handles to hardlinks''' + return True + +def rcfiles(path): + rcs = [os.path.join(path, 'hgrc')] + rcdir = os.path.join(path, 'hgrc.d') + try: + rcs.extend([os.path.join(rcdir, f) + for f, kind in osutil.listdir(rcdir) + if f.endswith(".rc")]) + except OSError: + pass + return rcs + +def system_rcpath(): + path = [] + # old mod_python does not set sys.argv + if len(getattr(sys, 'argv', [])) > 0: + path.extend(rcfiles(os.path.dirname(sys.argv[0]) + + '/../etc/mercurial')) + path.extend(rcfiles('/etc/mercurial')) + return path + +def user_rcpath(): + return [os.path.expanduser('~/.hgrc')] + +def parse_patch_output(output_line): + """parses the output produced by patch and returns the file name""" + pf = output_line[14:] + if os.sys.platform == 'OpenVMS': + if pf[0] == '`': + pf = pf[1:-1] # Remove the quotes + else: + if pf.startswith("'") and pf.endswith("'") and " " in pf: + pf = pf[1:-1] # Remove the quotes + return pf + +def sshargs(sshcmd, host, user, port): + '''Build argument list for ssh''' + args = user and ("%s@%s" % (user, host)) or host + return port and ("%s -p %s" % (args, port)) or args + +def is_exec(f): + """check whether a file is executable""" + return (os.lstat(f).st_mode & 0100 != 0) + +def set_flags(f, l, x): + s = os.lstat(f).st_mode + if l: + if not stat.S_ISLNK(s): + # switch file to link + data = file(f).read() + os.unlink(f) + try: + os.symlink(data, f) + except: + # failed to make a link, rewrite file + file(f, "w").write(data) + # no chmod needed at this point + return + if stat.S_ISLNK(s): + # switch link to file + data = os.readlink(f) + os.unlink(f) + file(f, "w").write(data) + s = 0666 & ~umask # avoid restatting for chmod + + sx = s & 0100 + if x and not sx: + # Turn on +x for every +r bit when making a file executable + # and obey umask. + os.chmod(f, s | (s & 0444) >> 2 & ~umask) + elif not x and sx: + # Turn off all +x bits + os.chmod(f, s & 0666) + +def set_binary(fd): + pass + +def pconvert(path): + return path + +def localpath(path): + return path + +def shellquote(s): + if os.sys.platform == 'OpenVMS': + return '"%s"' % s + else: + return "'%s'" % s.replace("'", "'\\''") + +def quotecommand(cmd): + return cmd + +def popen(command, mode='r'): + return os.popen(command, mode) + +def testpid(pid): + '''return False if pid dead, True if running or not sure''' + if os.sys.platform == 'OpenVMS': + return True + try: + os.kill(pid, 0) + return True + except OSError, inst: + return inst.errno != errno.ESRCH + +def explain_exit(code): + """return a 2-tuple (desc, code) describing a process's status""" + if os.WIFEXITED(code): + val = os.WEXITSTATUS(code) + return _("exited with status %d") % val, val + elif os.WIFSIGNALED(code): + val = os.WTERMSIG(code) + return _("killed by signal %d") % val, val + elif os.WIFSTOPPED(code): + val = os.WSTOPSIG(code) + return _("stopped by signal %d") % val, val + raise ValueError(_("invalid exit code")) + +def isowner(fp, st=None): + """Return True if the file object f belongs to the current user. + + The return value of a util.fstat(f) may be passed as the st argument. + """ + if st is None: + st = fstat(fp) + return st.st_uid == os.getuid() + +def find_exe(command): + '''Find executable for command searching like which does. + If command is a basename then PATH is searched for command. + PATH isn't searched if command is an absolute or relative path. + If command isn't found None is returned.''' + if sys.platform == 'OpenVMS': + return command + + def findexisting(executable): + 'Will return executable if existing file' + 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 None + +def set_signal_handler(): + pass + +def statfiles(files): + 'Stat each file in files and yield stat or None if file does not exist.' + lstat = os.lstat + for nf in files: + try: + st = lstat(nf) + except OSError, err: + if err.errno not in (errno.ENOENT, errno.ENOTDIR): + raise + st = None + yield st + +def getuser(): + '''return name of current user''' + return getpass.getuser() + +def expand_glob(pats): + '''On Windows, expand the implicit globs in a list of patterns''' + return list(pats) + +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.""" + + if uid is None: + uid = os.getuid() + try: + return pwd.getpwuid(uid)[0] + except KeyError: + return str(uid) + +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.""" + + if gid is None: + gid = os.getgid() + try: + return grp.getgrgid(gid)[0] + except KeyError: + return str(gid) + + diff -r 5ac1a72e5b74 -r e710f0f592b2 mercurial/store.py --- a/mercurial/store.py Sat Mar 14 10:46:48 2009 -0400 +++ b/mercurial/store.py Thu Mar 26 13:54:44 2009 -0500 @@ -136,7 +136,7 @@ # files in .hg/ will be created using this mode mode = os.stat(path).st_mode # avoid some useless chmods - if (0777 & ~util._umask) == (0777 & mode): + if (0777 & ~util.umask) == (0777 & mode): mode = None except OSError: mode = None diff -r 5ac1a72e5b74 -r e710f0f592b2 mercurial/util.py --- a/mercurial/util.py Sat Mar 14 10:46:48 2009 -0400 +++ b/mercurial/util.py Thu Mar 26 13:54:44 2009 -0500 @@ -13,7 +13,7 @@ """ from i18n import _ -import cStringIO, errno, getpass, re, shutil, sys, tempfile, traceback, error +import cStringIO, errno, re, shutil, sys, tempfile, traceback, error import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil import imp, unicodedata @@ -339,22 +339,6 @@ def always(fn): return True def never(fn): return False -def expand_glob(pats): - '''On Windows, expand the implicit globs in a list of patterns''' - if os.name != 'nt': - return list(pats) - ret = [] - for p in pats: - kind, name = patkind(p, None) - if kind is None: - globbed = glob.glob(name) - if globbed: - ret.extend(globbed) - continue - # if we couldn't expand the glob, just keep it around - ret.append(p) - return ret - def patkind(name, default): """Split a string into an optional pattern kind prefix and the actual pattern.""" @@ -858,12 +842,32 @@ # want to add "foo/bar/baz" before checking if there's a "foo/.hg" self.auditeddir.update(prefixes) -def _makelock_file(info, pathname): +if os.name == 'nt': + from windows import * +else: + from posix import * + +def makelock(info, pathname): + try: + return os.symlink(info, pathname) + except OSError, why: + if why.errno == errno.EEXIST: + raise + except AttributeError: # no symlink in os + pass + ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL) os.write(ld, info) os.close(ld) -def _readlock_file(pathname): +def readlock(pathname): + try: + return os.readlink(pathname) + except OSError, why: + if why.errno not in (errno.EINVAL, errno.ENOSYS): + raise + except AttributeError: # no symlink in os + pass return posixfile(pathname).read() def nlinks(pathname): @@ -883,103 +887,6 @@ except AttributeError: return os.stat(fp.name) -posixfile = file - -def openhardlinks(): - '''return true if it is safe to hold open file handles to hardlinks''' - return True - -def _statfiles(files): - 'Stat each file in files and yield stat or None if file does not exist.' - lstat = os.lstat - for nf in files: - try: - st = lstat(nf) - except OSError, err: - if err.errno not in (errno.ENOENT, errno.ENOTDIR): - raise - st = None - yield st - -def _statfiles_clustered(files): - '''Stat each file in files and yield stat or None if file does not exist. - Cluster and cache stat per directory to minimize number of OS stat calls.''' - ncase = os.path.normcase - sep = os.sep - dircache = {} # dirname -> filename -> status | None if file does not exist - for nf in files: - nf = ncase(nf) - pos = nf.rfind(sep) - if pos == -1: - dir, base = '.', nf - else: - dir, base = nf[:pos+1], nf[pos+1:] - cache = dircache.get(dir, None) - if cache is None: - try: - dmap = dict([(ncase(n), s) - for n, k, s in osutil.listdir(dir, True)]) - 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) - -if sys.platform == 'win32': - statfiles = _statfiles_clustered -else: - statfiles = _statfiles - -getuser_fallback = None - -def getuser(): - '''return name of current user''' - try: - return getpass.getuser() - except ImportError: - # import of pwd will fail on windows - try fallback - if getuser_fallback: - return getuser_fallback() - # raised if win32api not available - raise Abort(_('user name not available - set USERNAME ' - 'environment variable')) - -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.""" - try: - import pwd - if uid is None: - uid = os.getuid() - try: - return pwd.getpwuid(uid)[0] - except KeyError: - return str(uid) - except ImportError: - 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.""" - try: - import grp - if gid is None: - gid = os.getgid() - try: - return grp.getgrgid(gid)[0] - except KeyError: - return str(gid) - except ImportError: - return None - # File system features def checkcase(path): @@ -1088,9 +995,6 @@ except (OSError, AttributeError): return False -_umask = os.umask(0) -os.umask(_umask) - def needbinarypatch(): """return True if patches should be applied in binary mode by default.""" return os.name == 'nt' @@ -1114,379 +1018,6 @@ def lookup_reg(key, name=None, scope=None): return None -# Platform specific variants -if os.name == 'nt': - import msvcrt - nulldev = 'NUL:' - - class winstdout: - '''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: 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 - 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 = 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 and "win32api" in locals() - - def system_rcpath(): - try: - return system_rcpath_win32() - except: - return [r'c:\mercurial\mercurial.ini'] - - def user_rcpath(): - '''return os-specific hgrc search path to the user dir''' - try: - path = user_rcpath_win32() - except: - home = os.path.expanduser('~') - path = [os.path.join(home, 'mercurial.ini'), - os.path.join(home, '.hgrc')] - userprofile = os.environ.get('USERPROFILE') - if userprofile: - path.append(os.path.join(userprofile, 'mercurial.ini')) - path.append(os.path.join(userprofile, '.hgrc')) - return path - - def parse_patch_output(output_line): - """parses the output produced by patch and returns the file name""" - 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 testpid(pid): - '''return False if pid dead, True if running or not known''' - return True - - def set_flags(f, l, x): - pass - - def set_binary(fd): - # When run without console, pipes may expose invalid - # fileno(), usually set to -1. - if hasattr(fd, 'fileno') and fd.fileno() >= 0: - msvcrt.setmode(fd.fileno(), os.O_BINARY) - - def pconvert(path): - return '/'.join(splitpath(path)) - - def localpath(path): - return path.replace('/', '\\') - - def normpath(path): - return pconvert(os.path.normpath(path)) - - makelock = _makelock_file - readlock = _readlock_file - - 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 preceed 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.""" - # The extra quotes are needed because popen* runs the command - # through the current COMSPEC. cmd.exe suppress enclosing quotes. - 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" % nulldev - return os.popen(quotecommand(command), mode) - - def explain_exit(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(fp, st=None): - return True - - def find_exe(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 None - - def set_signal_handler(): - try: - set_signal_handler_win32() - except NameError: - pass - - try: - # override functions with win32 versions if possible - from util_win32 import * - if not _is_win_9x(): - posixfile = posixfile_nt - except ImportError: - pass - -else: - nulldev = '/dev/null' - - def rcfiles(path): - rcs = [os.path.join(path, 'hgrc')] - rcdir = os.path.join(path, 'hgrc.d') - try: - rcs.extend([os.path.join(rcdir, f) - for f, kind in osutil.listdir(rcdir) - if f.endswith(".rc")]) - except OSError: - pass - return rcs - - def system_rcpath(): - path = [] - # old mod_python does not set sys.argv - if len(getattr(sys, 'argv', [])) > 0: - path.extend(rcfiles(os.path.dirname(sys.argv[0]) + - '/../etc/mercurial')) - path.extend(rcfiles('/etc/mercurial')) - return path - - def user_rcpath(): - return [os.path.expanduser('~/.hgrc')] - - def parse_patch_output(output_line): - """parses the output produced by patch and returns the file name""" - pf = output_line[14:] - if os.sys.platform == 'OpenVMS': - if pf[0] == '`': - pf = pf[1:-1] # Remove the quotes - else: - if pf.startswith("'") and pf.endswith("'") and " " in pf: - pf = pf[1:-1] # Remove the quotes - return pf - - def sshargs(sshcmd, host, user, port): - '''Build argument list for ssh''' - args = user and ("%s@%s" % (user, host)) or host - return port and ("%s -p %s" % (args, port)) or args - - def is_exec(f): - """check whether a file is executable""" - return (os.lstat(f).st_mode & 0100 != 0) - - def set_flags(f, l, x): - s = os.lstat(f).st_mode - if l: - if not stat.S_ISLNK(s): - # switch file to link - data = file(f).read() - os.unlink(f) - try: - os.symlink(data, f) - except: - # failed to make a link, rewrite file - file(f, "w").write(data) - # no chmod needed at this point - return - if stat.S_ISLNK(s): - # switch link to file - data = os.readlink(f) - os.unlink(f) - file(f, "w").write(data) - s = 0666 & ~_umask # avoid restatting for chmod - - sx = s & 0100 - if x and not sx: - # Turn on +x for every +r bit when making a file executable - # and obey umask. - os.chmod(f, s | (s & 0444) >> 2 & ~_umask) - elif not x and sx: - # Turn off all +x bits - os.chmod(f, s & 0666) - - def set_binary(fd): - pass - - def pconvert(path): - return path - - def localpath(path): - return path - - normpath = os.path.normpath - samestat = os.path.samestat - - def makelock(info, pathname): - try: - os.symlink(info, pathname) - except OSError, why: - if why.errno == errno.EEXIST: - raise - else: - _makelock_file(info, pathname) - - def readlock(pathname): - try: - return os.readlink(pathname) - except OSError, why: - if why.errno in (errno.EINVAL, errno.ENOSYS): - return _readlock_file(pathname) - else: - raise - - def shellquote(s): - if os.sys.platform == 'OpenVMS': - return '"%s"' % s - else: - return "'%s'" % s.replace("'", "'\\''") - - def quotecommand(cmd): - return cmd - - def popen(command, mode='r'): - return os.popen(command, mode) - - def testpid(pid): - '''return False if pid dead, True if running or not sure''' - if os.sys.platform == 'OpenVMS': - return True - try: - os.kill(pid, 0) - return True - except OSError, inst: - return inst.errno != errno.ESRCH - - def explain_exit(code): - """return a 2-tuple (desc, code) describing a process's status""" - if os.WIFEXITED(code): - val = os.WEXITSTATUS(code) - return _("exited with status %d") % val, val - elif os.WIFSIGNALED(code): - val = os.WTERMSIG(code) - return _("killed by signal %d") % val, val - elif os.WIFSTOPPED(code): - val = os.WSTOPSIG(code) - return _("stopped by signal %d") % val, val - raise ValueError(_("invalid exit code")) - - def isowner(fp, st=None): - """Return True if the file object f belongs to the current user. - - The return value of a util.fstat(f) may be passed as the st argument. - """ - if st is None: - st = fstat(fp) - return st.st_uid == os.getuid() - - def find_exe(command): - '''Find executable for command searching like which does. - If command is a basename then PATH is searched for command. - PATH isn't searched if command is an absolute or relative path. - If command isn't found None is returned.''' - if sys.platform == 'OpenVMS': - return command - - def findexisting(executable): - 'Will return executable if existing file' - 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 None - - def set_signal_handler(): - pass - def mktempcopy(name, emptyok=False, createmode=None): """Create a temporary file with the same contents from name @@ -1510,7 +1041,7 @@ raise st_mode = createmode if st_mode is None: - st_mode = ~_umask + st_mode = ~umask st_mode &= 0666 os.chmod(temp, st_mode) if emptyok: diff -r 5ac1a72e5b74 -r e710f0f592b2 mercurial/util_win32.py --- a/mercurial/util_win32.py Sat Mar 14 10:46:48 2009 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,375 +0,0 @@ -# util_win32.py - utility functions that use win32 API -# -# Copyright 2005 Matt Mackall -# Copyright 2006 Vadim Gelfer -# -# This software may be used and distributed according to the terms of -# the GNU General Public License, incorporated herein by reference. - -# Mark Hammond's win32all package allows better functionality on -# Windows. this module overrides definitions in util.py. if not -# available, import of this module will fail, and generic code will be -# used. - -import win32api - -import errno, os, sys, pywintypes, win32con, win32file, win32process -import cStringIO, winerror -import osutil -import util -from win32com.shell import shell,shellcon - -class WinError(Exception): - winerror_map = { - winerror.ERROR_ACCESS_DENIED: errno.EACCES, - winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES, - winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES, - winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY, - winerror.ERROR_ALREADY_EXISTS: errno.EEXIST, - winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE, - winerror.ERROR_BAD_COMMAND: errno.EIO, - winerror.ERROR_BAD_DEVICE: errno.ENODEV, - winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO, - winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC, - winerror.ERROR_BAD_FORMAT: errno.ENOEXEC, - winerror.ERROR_BAD_LENGTH: errno.EINVAL, - winerror.ERROR_BAD_PATHNAME: errno.ENOENT, - winerror.ERROR_BAD_PIPE: errno.EPIPE, - winerror.ERROR_BAD_UNIT: errno.ENODEV, - winerror.ERROR_BAD_USERNAME: errno.EINVAL, - winerror.ERROR_BROKEN_PIPE: errno.EPIPE, - winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG, - winerror.ERROR_BUSY: errno.EBUSY, - winerror.ERROR_BUSY_DRIVE: errno.EBUSY, - winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS, - winerror.ERROR_CANNOT_MAKE: errno.EACCES, - winerror.ERROR_CANTOPEN: errno.EIO, - winerror.ERROR_CANTREAD: errno.EIO, - winerror.ERROR_CANTWRITE: errno.EIO, - winerror.ERROR_CRC: errno.EIO, - winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES, - winerror.ERROR_DEVICE_IN_USE: errno.EBUSY, - winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV, - winerror.ERROR_DIRECTORY: errno.EINVAL, - winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY, - winerror.ERROR_DISK_CHANGE: errno.EIO, - winerror.ERROR_DISK_FULL: errno.ENOSPC, - winerror.ERROR_DRIVE_LOCKED: errno.EBUSY, - winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL, - winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC, - winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG, - winerror.ERROR_FILE_EXISTS: errno.EEXIST, - winerror.ERROR_FILE_INVALID: errno.ENODEV, - winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT, - winerror.ERROR_GEN_FAILURE: errno.EIO, - winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC, - winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM, - winerror.ERROR_INVALID_ACCESS: errno.EACCES, - winerror.ERROR_INVALID_ADDRESS: errno.EFAULT, - winerror.ERROR_INVALID_BLOCK: errno.EFAULT, - winerror.ERROR_INVALID_DATA: errno.EINVAL, - winerror.ERROR_INVALID_DRIVE: errno.ENODEV, - winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC, - winerror.ERROR_INVALID_FLAGS: errno.EINVAL, - winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS, - winerror.ERROR_INVALID_HANDLE: errno.EBADF, - winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES, - winerror.ERROR_INVALID_NAME: errno.EINVAL, - winerror.ERROR_INVALID_OWNER: errno.EINVAL, - winerror.ERROR_INVALID_PARAMETER: errno.EINVAL, - winerror.ERROR_INVALID_PASSWORD: errno.EPERM, - winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL, - winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL, - winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO, - winerror.ERROR_INVALID_WORKSTATION: errno.EACCES, - winerror.ERROR_IO_DEVICE: errno.EIO, - winerror.ERROR_IO_INCOMPLETE: errno.EINTR, - winerror.ERROR_LOCKED: errno.EBUSY, - winerror.ERROR_LOCK_VIOLATION: errno.EACCES, - winerror.ERROR_LOGON_FAILURE: errno.EACCES, - winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL, - winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG, - winerror.ERROR_MORE_DATA: errno.EPIPE, - winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE, - winerror.ERROR_NOACCESS: errno.EFAULT, - winerror.ERROR_NONE_MAPPED: errno.EINVAL, - winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM, - winerror.ERROR_NOT_READY: errno.EAGAIN, - winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV, - winerror.ERROR_NO_DATA: errno.EPIPE, - winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO, - winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN, - winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES, - winerror.ERROR_OPEN_FAILED: errno.EIO, - winerror.ERROR_OPEN_FILES: errno.EBUSY, - winerror.ERROR_OPERATION_ABORTED: errno.EINTR, - winerror.ERROR_OUTOFMEMORY: errno.ENOMEM, - winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES, - winerror.ERROR_PATH_BUSY: errno.EBUSY, - winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT, - winerror.ERROR_PIPE_BUSY: errno.EBUSY, - winerror.ERROR_PIPE_CONNECTED: errno.EPIPE, - winerror.ERROR_PIPE_LISTENING: errno.EPIPE, - winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE, - winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES, - winerror.ERROR_READ_FAULT: errno.EIO, - winerror.ERROR_SEEK: errno.EIO, - winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE, - winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE, - winerror.ERROR_SHARING_VIOLATION: errno.EACCES, - winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM, - winerror.ERROR_SWAPERROR: errno.ENOENT, - winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE, - winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE, - winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO, - winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV, - winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD, - winerror.ERROR_WRITE_FAULT: errno.EIO, - winerror.ERROR_WRITE_PROTECT: errno.EROFS, - } - - def __init__(self, err): - self.win_errno, self.win_function, self.win_strerror = err - if self.win_strerror.endswith('.'): - self.win_strerror = self.win_strerror[:-1] - -class WinIOError(WinError, IOError): - def __init__(self, err, filename=None): - WinError.__init__(self, err) - IOError.__init__(self, self.winerror_map.get(self.win_errno, 0), - self.win_strerror) - self.filename = filename - -class WinOSError(WinError, OSError): - def __init__(self, err): - WinError.__init__(self, err) - OSError.__init__(self, self.winerror_map.get(self.win_errno, 0), - self.win_strerror) - -def os_link(src, dst): - try: - win32file.CreateHardLink(dst, src) - # CreateHardLink sometimes succeeds on mapped drives but - # following nlinks() returns 1. Check it now and bail out. - if nlinks(src) < 2: - try: - win32file.DeleteFile(dst) - except: - pass - # Fake hardlinking error - raise WinOSError((18, 'CreateHardLink', 'The system cannot ' - 'move the file to a different disk drive')) - except pywintypes.error, details: - raise WinOSError(details) - except NotImplementedError: # Another fake error win Win98 - raise WinOSError((18, 'CreateHardLink', 'Hardlinking not supported')) - -def nlinks(pathname): - """Return number of hardlinks for the given file.""" - try: - fh = win32file.CreateFile(pathname, - win32file.GENERIC_READ, win32file.FILE_SHARE_READ, - None, win32file.OPEN_EXISTING, 0, None) - res = win32file.GetFileInformationByHandle(fh) - fh.Close() - return res[7] - except pywintypes.error: - return os.lstat(pathname).st_nlink - -def testpid(pid): - '''return True if pid is still running or unable to - determine, False otherwise''' - try: - handle = win32api.OpenProcess( - win32con.PROCESS_QUERY_INFORMATION, False, pid) - if handle: - status = win32process.GetExitCodeProcess(handle) - return status == win32con.STILL_ACTIVE - except pywintypes.error, details: - return details[0] != winerror.ERROR_INVALID_PARAMETER - return True - -def lookup_reg(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). - ''' - try: - from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \ - QueryValueEx, OpenKey - except ImportError: - return None - - if scope is None: - scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE) - elif not isinstance(scope, (list, tuple)): - scope = (scope,) - for s in scope: - try: - val = QueryValueEx(OpenKey(s, key), valname)[0] - # never let a Unicode string escape into the wild - return util.tolocal(val.encode('UTF-8')) - except EnvironmentError: - pass - -def system_rcpath_win32(): - '''return default os-specific hgrc search path''' - proc = win32api.GetCurrentProcess() - try: - # This will fail on windows < NT - filename = win32process.GetModuleFileNameEx(proc, 0) - except: - filename = win32api.GetModuleFileName(0) - # Use mercurial.ini found in directory with hg.exe - progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini') - if os.path.isfile(progrc): - return [progrc] - # else look for a system rcpath in the registry - try: - value = win32api.RegQueryValue( - win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial') - rcpath = [] - for p in value.split(os.pathsep): - if p.lower().endswith('mercurial.ini'): - rcpath.append(p) - elif os.path.isdir(p): - for f, kind in osutil.listdir(p): - if f.endswith('.rc'): - rcpath.append(os.path.join(p, f)) - return rcpath - except pywintypes.error: - return [] - -def user_rcpath_win32(): - '''return os-specific hgrc search path to the user dir''' - userdir = os.path.expanduser('~') - if sys.getwindowsversion()[3] != 2 and userdir == '~': - # We are on win < nt: fetch the APPDATA directory location and use - # the parent directory as the user home dir. - appdir = shell.SHGetPathFromIDList( - shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA)) - userdir = os.path.dirname(appdir) - return [os.path.join(userdir, 'mercurial.ini'), - os.path.join(userdir, '.hgrc')] - -class posixfile_nt(object): - '''file object with posix-like semantics. on windows, normal - files can not be deleted or renamed if they are open. must open - with win32file.FILE_SHARE_DELETE. this flag does not exist on - windows < nt, so do not use this class there.''' - - # tried to use win32file._open_osfhandle to pass fd to os.fdopen, - # but does not work at all. wrap win32 file api instead. - - def __init__(self, name, mode='rb'): - self.closed = False - self.name = name - self.mode = mode - access = 0 - if 'r' in mode or '+' in mode: - access |= win32file.GENERIC_READ - if 'w' in mode or 'a' in mode or '+' in mode: - access |= win32file.GENERIC_WRITE - if 'r' in mode: - creation = win32file.OPEN_EXISTING - elif 'a' in mode: - creation = win32file.OPEN_ALWAYS - else: - creation = win32file.CREATE_ALWAYS - try: - self.handle = win32file.CreateFile(name, - access, - win32file.FILE_SHARE_READ | - win32file.FILE_SHARE_WRITE | - win32file.FILE_SHARE_DELETE, - None, - creation, - win32file.FILE_ATTRIBUTE_NORMAL, - 0) - except pywintypes.error, err: - raise WinIOError(err, name) - - def __iter__(self): - for line in self.readlines(): - yield line - - def read(self, count=-1): - try: - cs = cStringIO.StringIO() - while count: - wincount = int(count) - if wincount == -1: - wincount = 1048576 - val, data = win32file.ReadFile(self.handle, wincount) - if not data: break - cs.write(data) - if count != -1: - count -= len(data) - return cs.getvalue() - except pywintypes.error, err: - raise WinIOError(err) - - def readlines(self, sizehint=None): - # splitlines() splits on single '\r' while readlines() - # does not. cStringIO has a well behaving readlines() and is fast. - return cStringIO.StringIO(self.read()).readlines() - - def write(self, data): - try: - if 'a' in self.mode: - win32file.SetFilePointer(self.handle, 0, win32file.FILE_END) - nwrit = 0 - while nwrit < len(data): - val, nwrit = win32file.WriteFile(self.handle, data) - data = data[nwrit:] - except pywintypes.error, err: - raise WinIOError(err) - - def writelines(self, sequence): - for s in sequence: - self.write(s) - - def seek(self, pos, whence=0): - try: - win32file.SetFilePointer(self.handle, int(pos), whence) - except pywintypes.error, err: - raise WinIOError(err) - - def tell(self): - try: - return win32file.SetFilePointer(self.handle, 0, - win32file.FILE_CURRENT) - except pywintypes.error, err: - raise WinIOError(err) - - def close(self): - if not self.closed: - self.handle = None - self.closed = True - - def flush(self): - # we have no application-level buffering - pass - - def truncate(self, pos=0): - try: - win32file.SetFilePointer(self.handle, int(pos), - win32file.FILE_BEGIN) - win32file.SetEndOfFile(self.handle) - except pywintypes.error, err: - raise WinIOError(err) - -getuser_fallback = win32api.GetUserName - -def set_signal_handler_win32(): - """Register a termination handler for console events including - CTRL+C. python signal handlers do not work well with socket - operations. - """ - def handler(event): - win32process.ExitProcess(1) - win32api.SetConsoleCtrlHandler(handler) diff -r 5ac1a72e5b74 -r e710f0f592b2 mercurial/win32.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/win32.py Thu Mar 26 13:54:44 2009 -0500 @@ -0,0 +1,379 @@ +''' +win32.py - utility functions that use win32 API + +Copyright 2005-2009 Matt Mackall and others + +This software may be used and distributed according to the terms of +the GNU General Public License, incorporated herein by reference. + +Mark Hammond's win32all package allows better functionality on +Windows. this module overrides definitions in util.py. if not +available, import of this module will fail, and generic code will be +used. +''' + +import win32api + +import errno, os, sys, pywintypes, win32con, win32file, win32process +import cStringIO, winerror +import osutil +import util +from win32com.shell import shell,shellcon + +class WinError(Exception): + winerror_map = { + winerror.ERROR_ACCESS_DENIED: errno.EACCES, + winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES, + winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES, + winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY, + winerror.ERROR_ALREADY_EXISTS: errno.EEXIST, + winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE, + winerror.ERROR_BAD_COMMAND: errno.EIO, + winerror.ERROR_BAD_DEVICE: errno.ENODEV, + winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO, + winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC, + winerror.ERROR_BAD_FORMAT: errno.ENOEXEC, + winerror.ERROR_BAD_LENGTH: errno.EINVAL, + winerror.ERROR_BAD_PATHNAME: errno.ENOENT, + winerror.ERROR_BAD_PIPE: errno.EPIPE, + winerror.ERROR_BAD_UNIT: errno.ENODEV, + winerror.ERROR_BAD_USERNAME: errno.EINVAL, + winerror.ERROR_BROKEN_PIPE: errno.EPIPE, + winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG, + winerror.ERROR_BUSY: errno.EBUSY, + winerror.ERROR_BUSY_DRIVE: errno.EBUSY, + winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS, + winerror.ERROR_CANNOT_MAKE: errno.EACCES, + winerror.ERROR_CANTOPEN: errno.EIO, + winerror.ERROR_CANTREAD: errno.EIO, + winerror.ERROR_CANTWRITE: errno.EIO, + winerror.ERROR_CRC: errno.EIO, + winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES, + winerror.ERROR_DEVICE_IN_USE: errno.EBUSY, + winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV, + winerror.ERROR_DIRECTORY: errno.EINVAL, + winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY, + winerror.ERROR_DISK_CHANGE: errno.EIO, + winerror.ERROR_DISK_FULL: errno.ENOSPC, + winerror.ERROR_DRIVE_LOCKED: errno.EBUSY, + winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL, + winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC, + winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG, + winerror.ERROR_FILE_EXISTS: errno.EEXIST, + winerror.ERROR_FILE_INVALID: errno.ENODEV, + winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT, + winerror.ERROR_GEN_FAILURE: errno.EIO, + winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC, + winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM, + winerror.ERROR_INVALID_ACCESS: errno.EACCES, + winerror.ERROR_INVALID_ADDRESS: errno.EFAULT, + winerror.ERROR_INVALID_BLOCK: errno.EFAULT, + winerror.ERROR_INVALID_DATA: errno.EINVAL, + winerror.ERROR_INVALID_DRIVE: errno.ENODEV, + winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC, + winerror.ERROR_INVALID_FLAGS: errno.EINVAL, + winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS, + winerror.ERROR_INVALID_HANDLE: errno.EBADF, + winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES, + winerror.ERROR_INVALID_NAME: errno.EINVAL, + winerror.ERROR_INVALID_OWNER: errno.EINVAL, + winerror.ERROR_INVALID_PARAMETER: errno.EINVAL, + winerror.ERROR_INVALID_PASSWORD: errno.EPERM, + winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL, + winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL, + winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO, + winerror.ERROR_INVALID_WORKSTATION: errno.EACCES, + winerror.ERROR_IO_DEVICE: errno.EIO, + winerror.ERROR_IO_INCOMPLETE: errno.EINTR, + winerror.ERROR_LOCKED: errno.EBUSY, + winerror.ERROR_LOCK_VIOLATION: errno.EACCES, + winerror.ERROR_LOGON_FAILURE: errno.EACCES, + winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL, + winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG, + winerror.ERROR_MORE_DATA: errno.EPIPE, + winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE, + winerror.ERROR_NOACCESS: errno.EFAULT, + winerror.ERROR_NONE_MAPPED: errno.EINVAL, + winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM, + winerror.ERROR_NOT_READY: errno.EAGAIN, + winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV, + winerror.ERROR_NO_DATA: errno.EPIPE, + winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO, + winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN, + winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES, + winerror.ERROR_OPEN_FAILED: errno.EIO, + winerror.ERROR_OPEN_FILES: errno.EBUSY, + winerror.ERROR_OPERATION_ABORTED: errno.EINTR, + winerror.ERROR_OUTOFMEMORY: errno.ENOMEM, + winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES, + winerror.ERROR_PATH_BUSY: errno.EBUSY, + winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT, + winerror.ERROR_PIPE_BUSY: errno.EBUSY, + winerror.ERROR_PIPE_CONNECTED: errno.EPIPE, + winerror.ERROR_PIPE_LISTENING: errno.EPIPE, + winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE, + winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES, + winerror.ERROR_READ_FAULT: errno.EIO, + winerror.ERROR_SEEK: errno.EIO, + winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE, + winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE, + winerror.ERROR_SHARING_VIOLATION: errno.EACCES, + winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM, + winerror.ERROR_SWAPERROR: errno.ENOENT, + winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE, + winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE, + winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO, + winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV, + winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD, + winerror.ERROR_WRITE_FAULT: errno.EIO, + winerror.ERROR_WRITE_PROTECT: errno.EROFS, + } + + def __init__(self, err): + self.win_errno, self.win_function, self.win_strerror = err + if self.win_strerror.endswith('.'): + self.win_strerror = self.win_strerror[:-1] + +class WinIOError(WinError, IOError): + def __init__(self, err, filename=None): + WinError.__init__(self, err) + IOError.__init__(self, self.winerror_map.get(self.win_errno, 0), + self.win_strerror) + self.filename = filename + +class WinOSError(WinError, OSError): + def __init__(self, err): + WinError.__init__(self, err) + OSError.__init__(self, self.winerror_map.get(self.win_errno, 0), + self.win_strerror) + +def os_link(src, dst): + try: + win32file.CreateHardLink(dst, src) + # CreateHardLink sometimes succeeds on mapped drives but + # following nlinks() returns 1. Check it now and bail out. + if nlinks(src) < 2: + try: + win32file.DeleteFile(dst) + except: + pass + # Fake hardlinking error + raise WinOSError((18, 'CreateHardLink', 'The system cannot ' + 'move the file to a different disk drive')) + except pywintypes.error, details: + raise WinOSError(details) + except NotImplementedError: # Another fake error win Win98 + raise WinOSError((18, 'CreateHardLink', 'Hardlinking not supported')) + +def nlinks(pathname): + """Return number of hardlinks for the given file.""" + try: + fh = win32file.CreateFile(pathname, + win32file.GENERIC_READ, win32file.FILE_SHARE_READ, + None, win32file.OPEN_EXISTING, 0, None) + res = win32file.GetFileInformationByHandle(fh) + fh.Close() + return res[7] + except pywintypes.error: + return os.lstat(pathname).st_nlink + +def testpid(pid): + '''return True if pid is still running or unable to + determine, False otherwise''' + try: + handle = win32api.OpenProcess( + win32con.PROCESS_QUERY_INFORMATION, False, pid) + if handle: + status = win32process.GetExitCodeProcess(handle) + return status == win32con.STILL_ACTIVE + except pywintypes.error, details: + return details[0] != winerror.ERROR_INVALID_PARAMETER + return True + +def lookup_reg(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). + ''' + try: + from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \ + QueryValueEx, OpenKey + except ImportError: + return None + + if scope is None: + scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE) + elif not isinstance(scope, (list, tuple)): + scope = (scope,) + for s in scope: + try: + val = QueryValueEx(OpenKey(s, key), valname)[0] + # never let a Unicode string escape into the wild + return util.tolocal(val.encode('UTF-8')) + except EnvironmentError: + pass + +def system_rcpath_win32(): + '''return default os-specific hgrc search path''' + proc = win32api.GetCurrentProcess() + try: + # This will fail on windows < NT + filename = win32process.GetModuleFileNameEx(proc, 0) + except: + filename = win32api.GetModuleFileName(0) + # Use mercurial.ini found in directory with hg.exe + progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini') + if os.path.isfile(progrc): + return [progrc] + # else look for a system rcpath in the registry + try: + value = win32api.RegQueryValue( + win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial') + rcpath = [] + for p in value.split(os.pathsep): + if p.lower().endswith('mercurial.ini'): + rcpath.append(p) + elif os.path.isdir(p): + for f, kind in osutil.listdir(p): + if f.endswith('.rc'): + rcpath.append(os.path.join(p, f)) + return rcpath + except pywintypes.error: + return [] + +def user_rcpath_win32(): + '''return os-specific hgrc search path to the user dir''' + userdir = os.path.expanduser('~') + if sys.getwindowsversion()[3] != 2 and userdir == '~': + # We are on win < nt: fetch the APPDATA directory location and use + # the parent directory as the user home dir. + appdir = shell.SHGetPathFromIDList( + shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA)) + userdir = os.path.dirname(appdir) + return [os.path.join(userdir, 'mercurial.ini'), + os.path.join(userdir, '.hgrc')] + +class posixfile_nt(object): + '''file object with posix-like semantics. on windows, normal + files can not be deleted or renamed if they are open. must open + with win32file.FILE_SHARE_DELETE. this flag does not exist on + windows < nt, so do not use this class there.''' + + # tried to use win32file._open_osfhandle to pass fd to os.fdopen, + # but does not work at all. wrap win32 file api instead. + + def __init__(self, name, mode='rb'): + self.closed = False + self.name = name + self.mode = mode + access = 0 + if 'r' in mode or '+' in mode: + access |= win32file.GENERIC_READ + if 'w' in mode or 'a' in mode or '+' in mode: + access |= win32file.GENERIC_WRITE + if 'r' in mode: + creation = win32file.OPEN_EXISTING + elif 'a' in mode: + creation = win32file.OPEN_ALWAYS + else: + creation = win32file.CREATE_ALWAYS + try: + self.handle = win32file.CreateFile(name, + access, + win32file.FILE_SHARE_READ | + win32file.FILE_SHARE_WRITE | + win32file.FILE_SHARE_DELETE, + None, + creation, + win32file.FILE_ATTRIBUTE_NORMAL, + 0) + except pywintypes.error, err: + raise WinIOError(err, name) + + def __iter__(self): + for line in self.readlines(): + yield line + + def read(self, count=-1): + try: + cs = cStringIO.StringIO() + while count: + wincount = int(count) + if wincount == -1: + wincount = 1048576 + val, data = win32file.ReadFile(self.handle, wincount) + if not data: break + cs.write(data) + if count != -1: + count -= len(data) + return cs.getvalue() + except pywintypes.error, err: + raise WinIOError(err) + + def readlines(self, sizehint=None): + # splitlines() splits on single '\r' while readlines() + # does not. cStringIO has a well behaving readlines() and is fast. + return cStringIO.StringIO(self.read()).readlines() + + def write(self, data): + try: + if 'a' in self.mode: + win32file.SetFilePointer(self.handle, 0, win32file.FILE_END) + nwrit = 0 + while nwrit < len(data): + val, nwrit = win32file.WriteFile(self.handle, data) + data = data[nwrit:] + except pywintypes.error, err: + raise WinIOError(err) + + def writelines(self, sequence): + for s in sequence: + self.write(s) + + def seek(self, pos, whence=0): + try: + win32file.SetFilePointer(self.handle, int(pos), whence) + except pywintypes.error, err: + raise WinIOError(err) + + def tell(self): + try: + return win32file.SetFilePointer(self.handle, 0, + win32file.FILE_CURRENT) + except pywintypes.error, err: + raise WinIOError(err) + + def close(self): + if not self.closed: + self.handle = None + self.closed = True + + def flush(self): + # we have no application-level buffering + pass + + def truncate(self, pos=0): + try: + win32file.SetFilePointer(self.handle, int(pos), + win32file.FILE_BEGIN) + win32file.SetEndOfFile(self.handle) + except pywintypes.error, err: + raise WinIOError(err) + +def getuser(): + '''return name of current user''' + return win32api.GetUserName() + +def set_signal_handler_win32(): + """Register a termination handler for console events including + CTRL+C. python signal handlers do not work well with socket + operations. + """ + def handler(event): + win32process.ExitProcess(1) + win32api.SetConsoleCtrlHandler(handler) + diff -r 5ac1a72e5b74 -r e710f0f592b2 mercurial/windows.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/windows.py Thu Mar 26 13:54:44 2009 -0500 @@ -0,0 +1,266 @@ +""" +windows.py - Windows utility function implementations for Mercurial + + Copyright 2005-2009 Matt Mackall and others + +This software may be used and distributed according to the terms of +the GNU General Public License version 2, incorporated herein by +reference. +""" + +import msvcrt, sys, os +nulldev = 'NUL:' + +umask = 002 + +class winstdout: + '''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: 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 + 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 = 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 and "win32api" in locals() + +def system_rcpath(): + try: + return system_rcpath_win32() + except: + return [r'c:\mercurial\mercurial.ini'] + +def user_rcpath(): + '''return os-specific hgrc search path to the user dir''' + try: + path = user_rcpath_win32() + except: + home = os.path.expanduser('~') + path = [os.path.join(home, 'mercurial.ini'), + os.path.join(home, '.hgrc')] + userprofile = os.environ.get('USERPROFILE') + if userprofile: + path.append(os.path.join(userprofile, 'mercurial.ini')) + path.append(os.path.join(userprofile, '.hgrc')) + return path + +def parse_patch_output(output_line): + """parses the output produced by patch and returns the file name""" + 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 testpid(pid): + '''return False if pid dead, True if running or not known''' + return True + +def set_flags(f, l, x): + pass + +def set_binary(fd): + # When run without console, pipes may expose invalid + # fileno(), usually set to -1. + if hasattr(fd, 'fileno') and fd.fileno() >= 0: + msvcrt.setmode(fd.fileno(), os.O_BINARY) + +def pconvert(path): + return '/'.join(path.split(os.sep)) + +def localpath(path): + return path.replace('/', '\\') + +def normpath(path): + return pconvert(os.path.normpath(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 preceed 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.""" + # The extra quotes are needed because popen* runs the command + # through the current COMSPEC. cmd.exe suppress enclosing quotes. + 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" % nulldev + return os.popen(quotecommand(command), mode) + +def explain_exit(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(fp, st=None): + return True + +def find_exe(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 None + +def set_signal_handler(): + try: + set_signal_handler_win32() + except NameError: + pass + +def statfiles(files): + '''Stat each file in files and yield stat or None if file does not exist. + Cluster and cache stat per directory to minimize number of OS stat calls.''' + ncase = os.path.normcase + sep = os.sep + dircache = {} # dirname -> filename -> status | None if file does not exist + for nf in files: + nf = ncase(nf) + pos = nf.rfind(sep) + if pos == -1: + dir, base = '.', nf + else: + dir, base = nf[:pos+1], nf[pos+1:] + cache = dircache.get(dir, None) + if cache is None: + try: + dmap = dict([(ncase(n), s) + for n, k, s in osutil.listdir(dir, True)]) + 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 getuser(): + '''return name of current user''' + raise Abort(_('user name not available - set USERNAME ' + 'environment variable')) + +def expand_glob(pats): + '''On Windows, expand the implicit globs in a list of patterns''' + ret = [] + for p in pats: + kind, name = patkind(p, None) + if kind is None: + globbed = glob.glob(name) + if globbed: + ret.extend(globbed) + continue + # if we couldn't expand the glob, just keep it around + ret.append(p) + return ret + +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 + +posixfile = file +try: + # override functions with win32 versions if possible + from util_win32 import * + if not _is_win_9x(): + posixfile = posixfile_nt +except ImportError: + pass +