Mercurial > public > mercurial-scm > hg-stable
diff mercurial/win32.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 | mercurial/util_win32.py@82f7145b304c |
children | de377b1a9a84 |
line wrap: on
line diff
--- /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 <mpm@selenic.com> 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) +