##// END OF EJS Templates
hgweb: urlescape all urls, HTML escape repo/tag/branch/... names...
hgweb: urlescape all urls, HTML escape repo/tag/branch/... names Without this, repository paths or names containing e.g. & characters or html tags yielded strange results, possibly allowing cross-site scripting attacks.

File last commit:

r18501:a3b2dc1a stable
r18526:9409aeaa stable
Show More
posix.py
559 lines | 17.2 KiB | text/x-python | PythonLexer
Martin Geisler
put license and copyright info into comment blocks
r8226 # posix.py - Posix 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
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
from i18n import _
Adrian Buehlmann
util, posix: eliminate encodinglower and encodingupper...
r17203 import encoding
Bryan O'Sullivan
posix: move server side of unix domain sockets out of inotify...
r18097 import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
Alejandro Santos
compat: use open() instead of file() everywhere
r9031 posixfile = open
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 normpath = os.path.normpath
samestat = os.path.samestat
Adrian Buehlmann
rename util.os_link to oslink
r14235 oslink = os.link
Adrian Buehlmann
reintroduces util.unlink, for POSIX and Windows....
r13280 unlink = os.unlink
Adrian Buehlmann
util: move rename into posix.py and windows.py
r9549 rename = os.rename
Matt Mackall
cmdutils: Take over glob expansion duties from util
r8614 expandglobs = False
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
umask = os.umask(0)
os.umask(umask)
Bryan O'Sullivan
util: implement a faster os.path.split for posix systems...
r17560 def split(p):
Remy Blank
posix: fix split() for the case where the path is at the root of the filesystem...
r18288 '''Same as posixpath.split, but faster
>>> import posixpath
>>> for f in ['/absolute/path/to/file',
... 'relative/path/to/file',
... 'file_alone',
... 'path/to/directory/',
... '/multiple/path//separators',
... '/file_at_root',
... '///multiple_leading_separators_at_root',
... '']:
... assert split(f) == posixpath.split(f), f
'''
Bryan O'Sullivan
util: implement a faster os.path.split for posix systems...
r17560 ht = p.rsplit('/', 1)
if len(ht) == 1:
return '', p
nh = ht[0].rstrip('/')
if nh:
return nh, ht[1]
Remy Blank
posix: fix split() for the case where the path is at the root of the filesystem...
r18288 return ht[0] + '/', ht[1]
Bryan O'Sullivan
util: implement a faster os.path.split for posix systems...
r17560
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 def openhardlinks():
'''return true if it is safe to hold open file handles to hardlinks'''
return True
Adrian Buehlmann
port win32.py to using the Python ctypes library...
r13375 def nlinks(name):
'''return number of hardlinks for the given file'''
return os.lstat(name).st_nlink
Adrian Buehlmann
rename util.parse_patch_output to parsepatchoutput
r14231 def parsepatchoutput(output_line):
timeless
Generally replace "file name" with "filename" in help and comments.
r8761 """parses the output produced by patch and returns the filename"""
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 pf = output_line[14:]
if os.sys.platform == 'OpenVMS':
if pf[0] == '`':
pf = pf[1:-1] # Remove the quotes
else:
Peter Arrenbrecht
whitespace cleanup
r8219 if pf.startswith("'") and pf.endswith("'") and " " in pf:
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 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
Adrian Buehlmann
rename util.is_exec to isexec
r14273 def isexec(f):
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 """check whether a file is executable"""
return (os.lstat(f).st_mode & 0100 != 0)
Adrian Buehlmann
rename util.set_flags to setflags
r14232 def setflags(f, l, x):
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 s = os.lstat(f).st_mode
if l:
if not stat.S_ISLNK(s):
# switch file to link
Dan Villiom Podlaski Christiansen
explicitly close files...
r13400 fp = open(f)
data = fp.read()
fp.close()
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 os.unlink(f)
try:
os.symlink(data, f)
Idan Kamara
eliminate various naked except clauses
r14004 except OSError:
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 # failed to make a link, rewrite file
Dan Villiom Podlaski Christiansen
explicitly close files...
r13400 fp = open(f, "w")
fp.write(data)
fp.close()
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 # no chmod needed at this point
return
if stat.S_ISLNK(s):
# switch link to file
data = os.readlink(f)
os.unlink(f)
Dan Villiom Podlaski Christiansen
explicitly close files...
r13400 fp = open(f, "w")
fp.write(data)
fp.close()
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 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)
Adrian Buehlmann
util: move copymode into posix.py and windows.py...
r15011 def copymode(src, dst, mode=None):
'''Copy the file mode from the file at path src to dst.
If src doesn't exist, we're using mode instead. If mode is None, we're
using umask.'''
try:
st_mode = os.lstat(src).st_mode & 0777
except OSError, inst:
if inst.errno != errno.ENOENT:
raise
st_mode = mode
if st_mode is None:
st_mode = ~umask
st_mode &= 0666
os.chmod(dst, st_mode)
Adrian Buehlmann
util: move checkexec() to posix.py and return False on Windows
r13879 def checkexec(path):
"""
Check whether the given path is on a filesystem with UNIX-like exec flags
Requires a directory (like /foo/.hg)
"""
# VFAT on some Linux versions can flip mode but it doesn't persist
# a FS remount. Frequently we can detect it if files are created
# with exec bit on.
try:
EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
fh, fn = tempfile.mkstemp(dir=path, prefix='hg-checkexec-')
try:
os.close(fh)
m = os.stat(fn).st_mode & 0777
new_file_has_exec = m & EXECFLAGS
os.chmod(fn, m ^ EXECFLAGS)
exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
finally:
os.unlink(fn)
except (IOError, OSError):
# we don't care, the user probably won't be able to commit anyway
return False
return not (new_file_has_exec or exec_flags_cannot_flip)
Adrian Buehlmann
util: move checklink() to posix.py and return False on Windows...
r13890 def checklink(path):
"""check whether the given path is on a symlink-capable filesystem"""
# mktemp is not racy because symlink creation will fail if the
# file already exists
name = tempfile.mktemp(dir=path, prefix='hg-checklink-')
try:
os.symlink(".", name)
os.unlink(name)
return True
except (OSError, AttributeError):
return False
Adrian Buehlmann
path_auditor: check filenames for basic platform validity (issue2755)...
r13916 def checkosfilename(path):
'''Check that the base-relative path is a valid filename on this platform.
Returns None if the path is ok, or a UI string describing the problem.'''
pass # on posix platforms, every path is ok
Adrian Buehlmann
rename util.set_binary to setbinary
r14233 def setbinary(fd):
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 pass
def pconvert(path):
return path
def localpath(path):
return path
Siddharth Agarwal
Add support for relinking on Windows....
r10218 def samefile(fpath1, fpath2):
"""Returns whether path1 and path2 refer to the same file. This is only
guaranteed to work for files, not directories."""
return os.path.samefile(fpath1, fpath2)
def samedevice(fpath1, fpath2):
"""Returns whether fpath1 and fpath2 are on the same device. This is only
guaranteed to work for files, not directories."""
st1 = os.lstat(fpath1)
st2 = os.lstat(fpath2)
return st1.st_dev == st2.st_dev
Matt Mackall
dirstate: fix case-folding identity for traditional Unix...
r15488 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
def normcase(path):
return path.lower()
Dan Villiom Podlaski Christiansen
util: add realpath() for getting the 'true' path....
r9238 if sys.platform == 'darwin':
Ronny Pfannschmidt
posix: move a global fcntl import to keep it from breaking jython...
r10757 import fcntl # only needed on darwin, missing on jython
Matt Mackall
posix: add extended support for OS X path folding...
r15551
def normcase(path):
try:
Mads Kiilerich
OS X: try cheap ascii .lower() in normcase before making full unicode dance...
r18501 path.decode('ascii') # throw exception for non-ASCII character
return path.lower()
except UnicodeDecodeError:
pass
try:
Matt Mackall
posix: add extended support for OS X path folding...
r15551 u = path.decode('utf-8')
except UnicodeDecodeError:
# percent-encode any characters that don't round-trip
Matt Mackall
posix: fix HFS+ percent-encoding folding...
r15563 p2 = path.decode('utf-8', 'ignore').encode('utf-8')
Matt Mackall
posix: add extended support for OS X path folding...
r15551 s = ""
Matt Mackall
posix: fix HFS+ percent-encoding folding...
r15563 pos = 0
for c in path:
if p2[pos:pos + 1] == c:
s += c
pos += 1
Matt Mackall
posix: add extended support for OS X path folding...
r15551 else:
Matt Mackall
posix: fix HFS+ percent-encoding folding...
r15563 s += "%%%02X" % ord(c)
Matt Mackall
posix: add extended support for OS X path folding...
r15551 u = s.decode('utf-8')
# Decompose then lowercase (HFS+ technote specifies lower)
return unicodedata.normalize('NFD', u).lower().encode('utf-8')
Dan Villiom Podlaski Christiansen
util: add realpath() for getting the 'true' path....
r9238 def realpath(path):
'''
Returns the true, canonical file system path equivalent to the given
path.
Equivalent means, in this case, resulting in the same, unique
file system link to the path. Every file system entry, whether a file,
directory, hard link or symbolic link or special, will have a single
path preferred by the system, but may allow multiple, differing path
lookups to point to it.
Most regular UNIX file systems only allow a file system entry to be
looked up by its distinct path. Obviously, this does not apply to case
insensitive file systems, whether case preserving or not. The most
complex issue to deal with is file systems transparently reencoding the
path, such as the non-standard Unicode normalisation required for HFS+
and HFSX.
'''
# Constants copied from /usr/include/sys/fcntl.h
F_GETPATH = 50
O_SYMLINK = 0x200000
try:
fd = os.open(path, O_SYMLINK)
except OSError, err:
Dan Villiom Podlaski Christiansen
posix: remove is-comparison between integers...
r13007 if err.errno == errno.ENOENT:
Dan Villiom Podlaski Christiansen
util: add realpath() for getting the 'true' path....
r9238 return path
raise
try:
return fcntl.fcntl(fd, F_GETPATH, '\0' * 1024).rstrip('\0')
finally:
os.close(fd)
Thomas Arendsen Hein
posix: workaround for os.path.realpath bug in Python 2.4.1 and before...
r15353 elif sys.version_info < (2, 4, 2, 'final'):
# Workaround for http://bugs.python.org/issue1213894 (os.path.realpath
# didn't resolve symlinks that were the first component of the path.)
def realpath(path):
if os.path.isabs(path):
return os.path.realpath(path)
else:
return os.path.realpath('./' + path)
Dan Villiom Podlaski Christiansen
util: add realpath() for getting the 'true' path....
r9238 else:
# Fallback to the likely inadequate Python builtin function.
realpath = os.path.realpath
FUJIWARA Katsunori
cygwin: add cygwin specific normcase logic...
r15711 if sys.platform == 'cygwin':
# workaround for cygwin, in which mount point part of path is
# treated as case sensitive, even though underlying NTFS is case
# insensitive.
# default mount points
cygwinmountpoints = sorted([
"/usr/bin",
"/usr/lib",
"/cygdrive",
], reverse=True)
# use upper-ing as normcase as same as NTFS workaround
def normcase(path):
pathlen = len(path)
if (pathlen == 0) or (path[0] != os.sep):
# treat as relative
Adrian Buehlmann
util, posix: eliminate encodinglower and encodingupper...
r17203 return encoding.upper(path)
FUJIWARA Katsunori
cygwin: add cygwin specific normcase logic...
r15711
# to preserve case of mountpoint part
for mp in cygwinmountpoints:
if not path.startswith(mp):
continue
mplen = len(mp)
if mplen == pathlen: # mount point itself
return mp
if path[mplen] == os.sep:
Adrian Buehlmann
util, posix: eliminate encodinglower and encodingupper...
r17203 return mp + encoding.upper(path[mplen:])
FUJIWARA Katsunori
cygwin: add cygwin specific normcase logic...
r15711
Adrian Buehlmann
util, posix: eliminate encodinglower and encodingupper...
r17203 return encoding.upper(path)
FUJIWARA Katsunori
cygwin: add cygwin specific normcase logic...
r15711
A. S. Budden
posix: ignore execution bit in cygwin (issue3301)
r16240 # Cygwin translates native ACLs to POSIX permissions,
# but these translations are not supported by native
# tools, so the exec bit tends to be set erroneously.
# Therefore, disable executable bit access on Cygwin.
def checkexec(path):
return False
Matt Mackall
posix: disable cygwin's symlink emulation
r16241 # Similarly, Cygwin's symlink emulation is likely to create
# problems when Mercurial is used from both Cygwin and native
# Windows, with other native tools, or on shared volumes
def checklink(path):
return False
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 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
Adrian Buehlmann
rename explain_exit to explainexit
r14234 def explainexit(code):
Mads Kiilerich
util.system: Use subprocess instead of os.system...
r9517 """return a 2-tuple (desc, code) describing a subprocess status
(codes from kill are negative - not os.system/wait encoding)"""
if code >= 0:
return _("exited with status %d") % code, code
return _("killed by signal %d") % -code, -code
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
Martin Geisler
posix: do not use fstat in isowner...
r8657 def isowner(st):
"""Return True if the stat object st is from the current user."""
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 return st.st_uid == os.getuid()
Adrian Buehlmann
rename util.find_exe to findexe
r14271 def findexe(command):
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 '''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'
Marc-Antoine Ruel
posix: fix findexe() to check for file type and access
r15499 if os.path.isfile(executable) and os.access(executable, os.X_OK):
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 return executable
return None
if os.sep in command:
return findexisting(command)
Steven Stallion
plan9: initial support for plan 9 from bell labs...
r16383 if sys.platform == 'plan9':
return findexisting(os.path.join('/bin', command))
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 for path in os.environ.get('PATH', '').split(os.pathsep):
executable = findexisting(os.path.join(path, command))
if executable is not None:
Marc-Antoine Ruel
posix: fix findexe() to check for file type and access
r15499 return executable
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 return None
Adrian Buehlmann
rename util.set_signal_handler to setsignalhandler
r14237 def setsignalhandler():
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 pass
Bryan O'Sullivan
dirstate: move file type filtering to its source...
r18017 _wantedkinds = set([stat.S_IFREG, stat.S_IFLNK])
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 def statfiles(files):
Bryan O'Sullivan
dirstate: move file type filtering to its source...
r18017 '''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.'''
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 lstat = os.lstat
Bryan O'Sullivan
dirstate: move file type filtering to its source...
r18017 getkind = stat.S_IFMT
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 for nf in files:
try:
st = lstat(nf)
Bryan O'Sullivan
dirstate: move file type filtering to its source...
r18017 if getkind(st.st_mode) not in _wantedkinds:
st = None
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 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 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)
Patrick Mezard
serve: add and use portable spawnvp replacement...
r10237
Patrick Mezard
acl: grp module is not available on windows
r11138 def groupmembers(name):
"""Return the list of members of the group with the given
name, KeyError if the group does not exist.
"""
return list(grp.getgrnam(name).gr_mem)
Patrick Mezard
serve: add and use portable spawnvp replacement...
r10237 def spawndetached(args):
return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
args[0], args)
Patrick Mezard
Find right hg command for detached process...
r10239 def gethgcmd():
return sys.argv[:1]
Patrick Mezard
util: fix default termwidth() under Windows...
r11010
Augie Fackler
termwidth: move to ui.ui from util
r12689 def termwidth():
Patrick Mezard
util: fix default termwidth() under Windows...
r11010 try:
import termios, array, fcntl
for dev in (sys.stderr, sys.stdout, sys.stdin):
try:
try:
fd = dev.fileno()
except AttributeError:
continue
if not os.isatty(fd):
continue
Mark Round
posix: workaround lack of TIOCGWINSZ on Irix (issue3449)...
r16726 try:
arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
width = array.array('h', arri)[1]
if width > 0:
return width
except AttributeError:
pass
Patrick Mezard
util: fix default termwidth() under Windows...
r11010 except ValueError:
pass
except IOError, e:
if e[0] == errno.EINVAL:
pass
else:
raise
except ImportError:
pass
return 80
Adrian Buehlmann
util: move "default" makedir to posix.py...
r14908
def makedir(path, notindexed):
os.mkdir(path)
Adrian Buehlmann
util: move "default" unlinkpath to posix.py...
r14909
Mads Kiilerich
util: fold ENOENT check into unlinkpath, controlled by new ignoremissing flag...
r18143 def unlinkpath(f, ignoremissing=False):
Adrian Buehlmann
util: move "default" unlinkpath to posix.py...
r14909 """unlink and remove the directory if it is empty"""
Mads Kiilerich
util: fold ENOENT check into unlinkpath, controlled by new ignoremissing flag...
r18143 try:
os.unlink(f)
except OSError, e:
if not (ignoremissing and e.errno == errno.ENOENT):
raise
Adrian Buehlmann
util: move "default" unlinkpath to posix.py...
r14909 # try removing directories that might now be empty
try:
os.removedirs(os.path.dirname(f))
except OSError:
pass
Adrian Buehlmann
util: move "default" lookupreg to posix.py...
r14910 def lookupreg(key, name=None, scope=None):
return None
Adrian Buehlmann
util: move "default" hidewindow to posix.py...
r14911
def hidewindow():
"""Hide current shell window.
Used to hide the window opened when starting asynchronous
child process under Windows, unneeded on other systems.
"""
pass
Adrian Buehlmann
util: eliminate wildcard imports
r14926
Idan Kamara
posix, windows: introduce cachestat...
r14927 class cachestat(object):
def __init__(self, path):
self.stat = os.stat(path)
def cacheable(self):
return bool(self.stat.st_ino)
Martin Geisler
Use explicit integer division...
r15791 __hash__ = object.__hash__
Idan Kamara
posix, windows: introduce cachestat...
r14927 def __eq__(self, other):
try:
Siddharth Agarwal
posix: don't compare atime when determining if a file has changed...
r18442 # Only dev, ino, size, mtime and atime are likely to change. Out
# of these, we shouldn't compare atime but should compare the
# rest. However, one of the other fields changing indicates
# something fishy going on, so return False if anything but atime
# changes.
return (self.stat.st_mode == other.stat.st_mode and
self.stat.st_ino == other.stat.st_ino and
self.stat.st_dev == other.stat.st_dev and
self.stat.st_nlink == other.stat.st_nlink and
self.stat.st_uid == other.stat.st_uid and
self.stat.st_gid == other.stat.st_gid and
self.stat.st_size == other.stat.st_size and
self.stat.st_mtime == other.stat.st_mtime and
self.stat.st_ctime == other.stat.st_ctime)
Idan Kamara
posix, windows: introduce cachestat...
r14927 except AttributeError:
return False
def __ne__(self, other):
return not self == other
Adrian Buehlmann
util: eliminate wildcard imports
r14926 def executablepath():
return None # available on Windows only
Bryan O'Sullivan
posix: move server side of unix domain sockets out of inotify...
r18097
class unixdomainserver(socket.socket):
def __init__(self, join, subsystem):
'''Create a unix domain socket with the given prefix.'''
super(unixdomainserver, self).__init__(socket.AF_UNIX)
sockname = subsystem + '.sock'
self.realpath = self.path = join(sockname)
if os.path.islink(self.path):
if os.path.exists(self.path):
self.realpath = os.readlink(self.path)
else:
os.unlink(self.path)
try:
self.bind(self.realpath)
except socket.error, err:
if err.args[0] == 'AF_UNIX path too long':
tmpdir = tempfile.mkdtemp(prefix='hg-%s-' % subsystem)
self.realpath = os.path.join(tmpdir, sockname)
try:
self.bind(self.realpath)
os.symlink(self.realpath, self.path)
except (OSError, socket.error):
self.cleanup()
raise
else:
raise
self.listen(5)
def cleanup(self):
def okayifmissing(f, path):
try:
f(path)
except OSError, err:
if err.errno != errno.ENOENT:
raise
okayifmissing(os.unlink, self.path)
if self.realpath != self.path:
okayifmissing(os.unlink, self.realpath)
okayifmissing(os.rmdir, os.path.dirname(self.realpath))