Mercurial > public > mercurial-scm > hg-stable
diff mercurial/util.py @ 7890:e710f0f592b2
util: split out posix, windows, and win32 modules
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Thu, 26 Mar 2009 13:54:44 -0500 |
parents | 5c4026a289a4 |
children | 1b1b3dd630a5 |
line wrap: on
line diff
--- 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: