##// END OF EJS Templates
opener: use posixfile to hold file open when calling nlinks()...
opener: use posixfile to hold file open when calling nlinks() Mercurial's posixfile is less intrusive on Windows than Python's open

File last commit:

r13280:6052bbc7 default
r13342:2dc7a2a9 default
Show More
windows.py
389 lines | 12.8 KiB | text/x-python | PythonLexer
Martin Geisler
put license and copyright info into comment blocks
r8226 # windows.py - Windows utility function implementations for Mercurial
#
# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
#
# This software may be used and distributed according to the terms of the
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
Steve Borho
windows: add various missing import
r7914 from i18n import _
Simon Heimberg
separate import lines from mercurial and general python modules
r8312 import osutil, error
Patrick Mezard
serve: add and use portable spawnvp replacement...
r10237 import errno, msvcrt, os, re, sys, random, subprocess
Sune Foldager
posixfile: remove posixfile_nt and fix import bug in windows.py...
r8421
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 nulldev = 'NUL:'
Sune Foldager
posixfile: remove posixfile_nt and fix import bug in windows.py...
r8421 umask = 002
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
Sune Foldager
posixfile: remove posixfile_nt and fix import bug in windows.py...
r8421 # wrap osutil.posixfile to provide friendlier exceptions
def posixfile(name, mode='r', buffering=-1):
try:
return osutil.posixfile(name, mode, buffering)
except WindowsError, err:
Steve Borho
windows: provide filename in IOError exceptions...
r9448 raise IOError(err.errno, '%s: %s' % (name, err.strerror))
Sune Foldager
posixfile: remove posixfile_nt and fix import bug in windows.py...
r8421 posixfile.__doc__ = osutil.posixfile.__doc__
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
Benoit Boissinot
use new style classes
r8778 class winstdout(object):
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 '''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
Benoit Boissinot
fix coding style (reported by pylint)
r10394 self.softspace = 0
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 while start < l:
end = start + limit
self.fp.write(s[start:end])
start = end
except IOError, inst:
Matt Mackall
many, many trivial check-code fixups
r10282 if inst.errno != 0:
raise
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 self.close()
raise IOError(errno.EPIPE, 'Broken pipe')
def flush(self):
try:
return self.fp.flush()
except IOError, inst:
Matt Mackall
many, many trivial check-code fixups
r10282 if inst.errno != errno.EINVAL:
raise
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 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():
Henrik Stuart
windows: make openhardlinks work...
r8559 return not _is_win_9x() and "win32api" in globals()
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
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):
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 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))
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.
'''
# TODO: There may be a more clever way to do this that also handles other,
# less common file systems.
return os.path.normpath(os.path.normcase(os.path.realpath(path)))
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 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."""
Steve Borho
util: concentrate quoting knowledge to windows.py quotecommand()...
r13188 if sys.version_info < (2, 7, 1):
# Python versions since 2.7.1 do this extra quoting themselves
return '"' + cmd + '"'
return cmd
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
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.
Martin Geisler
posix: do not use fstat in isowner...
r8657 def isowner(st):
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 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
Steve Borho
windows: expand environment vars in find_exe...
r10156 return findexisting(os.path.expanduser(os.path.expandvars(command)))
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
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
dircache = {} # dirname -> filename -> status | None if file does not exist
for nf in files:
nf = ncase(nf)
Shun-ichi GOTO
Use os.path.split() for MBCS with win32mbcs extension.
r9099 dir, base = os.path.split(nf)
if not dir:
dir = '.'
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 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'''
Matt Mackall
windows: get Abort from error
r7949 raise error.Abort(_('user name not available - set USERNAME '
Martin Geisler
windows: break import cycle with util
r7934 'environment variable'))
Matt Mackall
util: split out posix, windows, and win32 modules
r7890
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
Henrik Stuart
windows: avoid deleting non-empty reparse points...
r8364 def _removedirs(name):
"""special version of os.removedirs that does not remove symlinked
directories or junction points if they actually contain files"""
if osutil.listdir(name):
return
os.rmdir(name)
head, tail = os.path.split(name)
if not tail:
head, tail = os.path.split(head)
while head and tail:
try:
Patrick Mezard
windows: fix unlink() not dropping empty tree (issue1861)
r9572 if osutil.listdir(head):
Henrik Stuart
windows: avoid deleting non-empty reparse points...
r8364 return
os.rmdir(head)
except:
break
head, tail = os.path.split(head)
Adrian Buehlmann
rename util.unlink to unlinkpath
r13235 def unlinkpath(f):
Henrik Stuart
windows: avoid deleting non-empty reparse points...
r8364 """unlink and remove the directory if it is empty"""
os.unlink(f)
# try removing directories that might now be empty
try:
_removedirs(os.path.dirname(f))
except OSError:
pass
Adrian Buehlmann
reintroduces util.unlink, for POSIX and Windows....
r13280 def unlink(f):
'''try to implement POSIX' unlink semantics on Windows'''
# POSIX allows to unlink and rename open files. Windows has serious
# problems with doing that:
# - Calling os.unlink (or os.rename) on a file f fails if f or any
# hardlinked copy of f has been opened with Python's open(). There is no
# way such a file can be deleted or renamed on Windows (other than
# scheduling the delete or rename for the next reboot).
# - Calling os.unlink on a file that has been opened with Mercurial's
# posixfile (or comparable methods) will delay the actual deletion of
# the file for as long as the file is held open. The filename is blocked
# during that time and cannot be used for recreating a new file under
# that same name ("zombie file"). Directories containing such zombie files
# cannot be removed or moved.
# A file that has been opened with posixfile can be renamed, so we rename
# f to a random temporary name before calling os.unlink on it. This allows
# callers to recreate f immediately while having other readers do their
# implicit zombie filename blocking on a temporary name.
for tries in xrange(10):
temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
try:
os.rename(f, temp) # raises OSError EEXIST if temp exists
break
except OSError, e:
if e.errno != errno.EEXIST:
raise
else:
raise IOError, (errno.EEXIST, "No usable temporary filename found")
try:
os.unlink(temp)
except:
# Some very rude AV-scanners on Windows may cause this unlink to fail.
# Not aborting here just leaks the temp file, whereas aborting at this
# point may leave serious inconsistencies. Ideally, we would notify
# the user in this case here.
pass
Adrian Buehlmann
util: move rename into posix.py and windows.py
r9549 def rename(src, dst):
'''atomically rename file src to dst, replacing dst if it exists'''
try:
os.rename(src, dst)
Adrian Buehlmann
windows.rename: check OSError for EEXIST...
r13278 except OSError, e:
if e.errno != errno.EEXIST:
raise
Adrian Buehlmann
reintroduces util.unlink, for POSIX and Windows....
r13280 unlink(dst)
Adrian Buehlmann
util: move rename into posix.py and windows.py
r9549 os.rename(src, dst)
Patrick Mezard
serve: add and use portable spawnvp replacement...
r10237 def spawndetached(args):
# No standard library function really spawns a fully detached
# process under win32 because they allocate pipes or other objects
# to handle standard streams communications. Passing these objects
# to the child process requires handle inheritance to be enabled
# which makes really detached processes impossible.
class STARTUPINFO:
dwFlags = subprocess.STARTF_USESHOWWINDOW
hStdInput = None
hStdOutput = None
hStdError = None
wShowWindow = subprocess.SW_HIDE
Matt Mackall
many, many trivial check-code fixups
r10282
Patrick Mezard
serve: add and use portable spawnvp replacement...
r10237 args = subprocess.list2cmdline(args)
# Not running the command in shell mode makes python26 hang when
# writing to hgweb output socket.
comspec = os.environ.get("COMSPEC", "cmd.exe")
args = comspec + " /c " + args
hp, ht, pid, tid = subprocess.CreateProcess(
None, args,
# no special security
None, None,
# Do not inherit handles
0,
# DETACHED_PROCESS
0x00000008,
os.environ,
os.getcwd(),
STARTUPINFO())
return pid
Patrick Mezard
Find right hg command for detached process...
r10239 def gethgcmd():
return [sys.executable] + sys.argv[:1]
Augie Fackler
termwidth: move to ui.ui from util
r12689 def termwidth():
Patrick Mezard
util: fix default termwidth() under Windows...
r11010 # cmd.exe does not handle CR like a unix console, the CR is
# counted in the line length. On 80 columns consoles, if 80
# characters are written, the following CR won't apply on the
# current line but on the new one. Keep room for it.
return 79
Patrick Mezard
acl: grp module is not available on windows
r11138 def groupmembers(name):
# Don't support groups on Windows for now
raise KeyError()
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 try:
# override functions with win32 versions if possible
Sune Foldager
windows: fix import of win32.py (was util_win32.py)
r7932 from win32 import *
Matt Mackall
util: split out posix, windows, and win32 modules
r7890 except ImportError:
Sune Foldager
posixfile: remove posixfile_nt and fix import bug in windows.py...
r8421 pass
Matt Mackall
cmdutils: Take over glob expansion duties from util
r8614
expandglobs = True