~ubuntu-branches/ubuntu/precise/mercurial/precise-updates

« back to all changes in this revision

Viewing changes to mercurial/win32.py

  • Committer: Bazaar Package Importer
  • Author(s): Javi Merino
  • Date: 2011-03-06 16:01:58 UTC
  • mto: (28.1.2 sid) (1.1.14)
  • mto: This revision was merged to the branch mainline in revision 32.
  • Revision ID: james.westby@ubuntu.com-20110306160158-y94pzpmtd7b1xgjk
Tags: upstream-1.8
ImportĀ upstreamĀ versionĀ 1.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
# This software may be used and distributed according to the terms of the
6
6
# GNU General Public License version 2 or any later version.
7
7
 
8
 
"""Utility functions that use win32 API.
9
 
 
10
 
Mark Hammond's win32all package allows better functionality on
11
 
Windows. This module overrides definitions in util.py. If not
12
 
available, import of this module will fail, and generic code will be
13
 
used.
14
 
"""
15
 
 
16
 
import win32api
17
 
 
18
 
import errno, os, sys, pywintypes, win32con, win32file, win32process
19
 
import winerror, win32gui, win32console
20
 
import osutil, encoding
21
 
from win32com.shell import shell, shellcon
 
8
import encoding
 
9
import ctypes, errno, os, struct, subprocess
 
10
 
 
11
_kernel32 = ctypes.windll.kernel32
 
12
 
 
13
_BOOL = ctypes.c_long
 
14
_WORD = ctypes.c_ushort
 
15
_DWORD = ctypes.c_ulong
 
16
_LPCSTR = _LPSTR = ctypes.c_char_p
 
17
_HANDLE = ctypes.c_void_p
 
18
_HWND = _HANDLE
 
19
 
 
20
_INVALID_HANDLE_VALUE = -1
 
21
 
 
22
# GetLastError
 
23
_ERROR_SUCCESS = 0
 
24
_ERROR_INVALID_PARAMETER = 87
 
25
_ERROR_INSUFFICIENT_BUFFER = 122
 
26
 
 
27
# WPARAM is defined as UINT_PTR (unsigned type)
 
28
# LPARAM is defined as LONG_PTR (signed type)
 
29
if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
 
30
    _WPARAM = ctypes.c_ulong
 
31
    _LPARAM = ctypes.c_long
 
32
elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
 
33
    _WPARAM = ctypes.c_ulonglong
 
34
    _LPARAM = ctypes.c_longlong
 
35
 
 
36
class _FILETIME(ctypes.Structure):
 
37
    _fields_ = [('dwLowDateTime', _DWORD),
 
38
                ('dwHighDateTime', _DWORD)]
 
39
 
 
40
class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
 
41
    _fields_ = [('dwFileAttributes', _DWORD),
 
42
                ('ftCreationTime', _FILETIME),
 
43
                ('ftLastAccessTime', _FILETIME),
 
44
                ('ftLastWriteTime', _FILETIME),
 
45
                ('dwVolumeSerialNumber', _DWORD),
 
46
                ('nFileSizeHigh', _DWORD),
 
47
                ('nFileSizeLow', _DWORD),
 
48
                ('nNumberOfLinks', _DWORD),
 
49
                ('nFileIndexHigh', _DWORD),
 
50
                ('nFileIndexLow', _DWORD)]
 
51
 
 
52
# CreateFile 
 
53
_FILE_SHARE_READ = 0x00000001
 
54
_FILE_SHARE_WRITE = 0x00000002
 
55
_FILE_SHARE_DELETE = 0x00000004
 
56
 
 
57
_OPEN_EXISTING = 3
 
58
 
 
59
# Process Security and Access Rights
 
60
_PROCESS_QUERY_INFORMATION = 0x0400
 
61
 
 
62
# GetExitCodeProcess
 
63
_STILL_ACTIVE = 259
 
64
 
 
65
# registry
 
66
_HKEY_CURRENT_USER = 0x80000001L
 
67
_HKEY_LOCAL_MACHINE = 0x80000002L
 
68
_KEY_READ = 0x20019
 
69
_REG_SZ = 1
 
70
_REG_DWORD = 4
 
71
 
 
72
class _STARTUPINFO(ctypes.Structure):
 
73
    _fields_ = [('cb', _DWORD),
 
74
                ('lpReserved', _LPSTR),
 
75
                ('lpDesktop', _LPSTR),
 
76
                ('lpTitle', _LPSTR),
 
77
                ('dwX', _DWORD),
 
78
                ('dwY', _DWORD),
 
79
                ('dwXSize', _DWORD),
 
80
                ('dwYSize', _DWORD),
 
81
                ('dwXCountChars', _DWORD),
 
82
                ('dwYCountChars', _DWORD),
 
83
                ('dwFillAttribute', _DWORD),
 
84
                ('dwFlags', _DWORD),
 
85
                ('wShowWindow', _WORD),
 
86
                ('cbReserved2', _WORD),
 
87
                ('lpReserved2', ctypes.c_char_p),
 
88
                ('hStdInput', _HANDLE),
 
89
                ('hStdOutput', _HANDLE),
 
90
                ('hStdError', _HANDLE)]
 
91
 
 
92
class _PROCESS_INFORMATION(ctypes.Structure):
 
93
    _fields_ = [('hProcess', _HANDLE),
 
94
                ('hThread', _HANDLE),
 
95
                ('dwProcessId', _DWORD),
 
96
                ('dwThreadId', _DWORD)]
 
97
 
 
98
_DETACHED_PROCESS = 0x00000008
 
99
_STARTF_USESHOWWINDOW = 0x00000001
 
100
_SW_HIDE = 0
 
101
 
 
102
class _COORD(ctypes.Structure):
 
103
    _fields_ = [('X', ctypes.c_short),
 
104
                ('Y', ctypes.c_short)]
 
105
 
 
106
class _SMALL_RECT(ctypes.Structure):
 
107
    _fields_ = [('Left', ctypes.c_short),
 
108
                ('Top', ctypes.c_short),
 
109
                ('Right', ctypes.c_short),
 
110
                ('Bottom', ctypes.c_short)]
 
111
 
 
112
class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
 
113
    _fields_ = [('dwSize', _COORD),
 
114
                ('dwCursorPosition', _COORD),
 
115
                ('wAttributes', _WORD),
 
116
                ('srWindow', _SMALL_RECT),
 
117
                ('dwMaximumWindowSize', _COORD)]
 
118
 
 
119
_STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
 
120
 
 
121
def _raiseoserror(name):
 
122
    err = ctypes.WinError()
 
123
    raise OSError(err.errno, '%s: %s' % (name, err.strerror))
 
124
 
 
125
def _getfileinfo(name):
 
126
    fh = _kernel32.CreateFileA(name, 0,
 
127
            _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
 
128
            None, _OPEN_EXISTING, 0, None)
 
129
    if fh == _INVALID_HANDLE_VALUE:
 
130
        _raiseoserror(name)
 
131
    try:
 
132
        fi = _BY_HANDLE_FILE_INFORMATION()
 
133
        if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
 
134
            _raiseoserror(name)
 
135
        return fi
 
136
    finally:
 
137
        _kernel32.CloseHandle(fh)
22
138
 
23
139
def os_link(src, dst):
24
 
    try:
25
 
        win32file.CreateHardLink(dst, src)
26
 
    except pywintypes.error:
27
 
        raise OSError(errno.EINVAL, 'target implements hardlinks improperly')
28
 
    except NotImplementedError: # Another fake error win Win98
29
 
        raise OSError(errno.EINVAL, 'Hardlinking not supported')
30
 
 
31
 
def _getfileinfo(pathname):
32
 
    """Return number of hardlinks for the given file."""
33
 
    try:
34
 
        fh = win32file.CreateFile(pathname,
35
 
            win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
36
 
            None, win32file.OPEN_EXISTING, 0, None)
37
 
    except pywintypes.error:
38
 
        raise OSError(errno.ENOENT, 'The system cannot find the file specified')
39
 
    try:
40
 
        return win32file.GetFileInformationByHandle(fh)
41
 
    finally:
42
 
        fh.Close()
43
 
 
44
 
def nlinks(pathname):
45
 
    """Return number of hardlinks for the given file."""
46
 
    return _getfileinfo(pathname)[7]
 
140
    if not _kernel32.CreateHardLinkA(dst, src, None):
 
141
        _raiseoserror(src)
 
142
 
 
143
def nlinks(name):
 
144
    '''return number of hardlinks for the given file'''
 
145
    return _getfileinfo(name).nNumberOfLinks
47
146
 
48
147
def samefile(fpath1, fpath2):
49
 
    """Returns whether fpath1 and fpath2 refer to the same file. This is only
50
 
    guaranteed to work for files, not directories."""
 
148
    '''Returns whether fpath1 and fpath2 refer to the same file. This is only
 
149
    guaranteed to work for files, not directories.'''
51
150
    res1 = _getfileinfo(fpath1)
52
151
    res2 = _getfileinfo(fpath2)
53
 
    # Index 4 is the volume serial number, and 8 and 9 contain the file ID
54
 
    return res1[4] == res2[4] and res1[8] == res2[8] and res1[9] == res2[9]
 
152
    return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
 
153
        and res1.nFileIndexHigh == res2.nFileIndexHigh
 
154
        and res1.nFileIndexLow == res2.nFileIndexLow)
55
155
 
56
156
def samedevice(fpath1, fpath2):
57
 
    """Returns whether fpath1 and fpath2 are on the same device. This is only
58
 
    guaranteed to work for files, not directories."""
 
157
    '''Returns whether fpath1 and fpath2 are on the same device. This is only
 
158
    guaranteed to work for files, not directories.'''
59
159
    res1 = _getfileinfo(fpath1)
60
160
    res2 = _getfileinfo(fpath2)
61
 
    return res1[4] == res2[4]
 
161
    return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
62
162
 
63
163
def testpid(pid):
64
164
    '''return True if pid is still running or unable to
65
165
    determine, False otherwise'''
66
 
    try:
67
 
        handle = win32api.OpenProcess(
68
 
            win32con.PROCESS_QUERY_INFORMATION, False, pid)
69
 
        if handle:
70
 
            status = win32process.GetExitCodeProcess(handle)
71
 
            return status == win32con.STILL_ACTIVE
72
 
    except pywintypes.error, details:
73
 
        return details[0] != winerror.ERROR_INVALID_PARAMETER
74
 
    return True
 
166
    h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
 
167
    if h:
 
168
        try:
 
169
            status = _DWORD()
 
170
            if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
 
171
                return status.value == _STILL_ACTIVE
 
172
        finally:
 
173
            _kernel32.CloseHandle(h)
 
174
    return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
75
175
 
76
176
def lookup_reg(key, valname=None, scope=None):
77
177
    ''' Look up a key/value name in the Windows registry.
82
182
    a sequence of scopes to look up in order. Default (CURRENT_USER,
83
183
    LOCAL_MACHINE).
84
184
    '''
85
 
    try:
86
 
        from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
87
 
            QueryValueEx, OpenKey
88
 
    except ImportError:
89
 
        return None
90
 
 
 
185
    adv = ctypes.windll.advapi32
 
186
    byref = ctypes.byref
91
187
    if scope is None:
92
 
        scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
 
188
        scope = (_HKEY_CURRENT_USER, _HKEY_LOCAL_MACHINE)
93
189
    elif not isinstance(scope, (list, tuple)):
94
190
        scope = (scope,)
95
191
    for s in scope:
 
192
        kh = _HANDLE()
 
193
        res = adv.RegOpenKeyExA(s, key, 0, _KEY_READ, ctypes.byref(kh))
 
194
        if res != _ERROR_SUCCESS:
 
195
            continue
96
196
        try:
97
 
            val = QueryValueEx(OpenKey(s, key), valname)[0]
98
 
            # never let a Unicode string escape into the wild
99
 
            return encoding.tolocal(val.encode('UTF-8'))
100
 
        except EnvironmentError:
101
 
            pass
102
 
 
103
 
def system_rcpath_win32():
104
 
    '''return default os-specific hgrc search path'''
105
 
    filename = win32api.GetModuleFileName(0)
106
 
    # Use mercurial.ini found in directory with hg.exe
107
 
    progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
108
 
    if os.path.isfile(progrc):
109
 
        return [progrc]
110
 
    # Use hgrc.d found in directory with hg.exe
111
 
    progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
112
 
    if os.path.isdir(progrcd):
113
 
        rcpath = []
114
 
        for f, kind in osutil.listdir(progrcd):
115
 
            if f.endswith('.rc'):
116
 
                rcpath.append(os.path.join(progrcd, f))
117
 
        return rcpath
118
 
    # else look for a system rcpath in the registry
119
 
    try:
120
 
        value = win32api.RegQueryValue(
121
 
                win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
122
 
        rcpath = []
123
 
        for p in value.split(os.pathsep):
124
 
            if p.lower().endswith('mercurial.ini'):
125
 
                rcpath.append(p)
126
 
            elif os.path.isdir(p):
127
 
                for f, kind in osutil.listdir(p):
128
 
                    if f.endswith('.rc'):
129
 
                        rcpath.append(os.path.join(p, f))
130
 
        return rcpath
131
 
    except pywintypes.error:
132
 
        return []
133
 
 
134
 
def user_rcpath_win32():
135
 
    '''return os-specific hgrc search path to the user dir'''
136
 
    userdir = os.path.expanduser('~')
137
 
    if sys.getwindowsversion()[3] != 2 and userdir == '~':
138
 
        # We are on win < nt: fetch the APPDATA directory location and use
139
 
        # the parent directory as the user home dir.
140
 
        appdir = shell.SHGetPathFromIDList(
141
 
            shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
142
 
        userdir = os.path.dirname(appdir)
143
 
    return [os.path.join(userdir, 'mercurial.ini'),
144
 
            os.path.join(userdir, '.hgrc')]
 
197
            size = _DWORD(600)
 
198
            type = _DWORD()
 
199
            buf = ctypes.create_string_buffer(size.value + 1)
 
200
            res = adv.RegQueryValueExA(kh.value, valname, None,
 
201
                                       byref(type), buf, byref(size))
 
202
            if res != _ERROR_SUCCESS:
 
203
                continue
 
204
            if type.value == _REG_SZ:
 
205
                # never let a Unicode string escape into the wild
 
206
                return encoding.tolocal(buf.value.encode('UTF-8'))
 
207
            elif type.value == _REG_DWORD:
 
208
                fmt = '<L'
 
209
                s = ctypes.string_at(byref(buf), struct.calcsize(fmt))
 
210
                return struct.unpack(fmt, s)[0]
 
211
        finally:
 
212
            adv.RegCloseKey(kh.value)
 
213
 
 
214
def executable_path():
 
215
    '''return full path of hg.exe'''
 
216
    size = 600
 
217
    buf = ctypes.create_string_buffer(size + 1)
 
218
    len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
 
219
    if len == 0:
 
220
        raise ctypes.WinError()
 
221
    elif len == size:
 
222
        raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
 
223
    return buf.value
145
224
 
146
225
def getuser():
147
226
    '''return name of current user'''
148
 
    return win32api.GetUserName()
149
 
 
150
 
def set_signal_handler_win32():
151
 
    """Register a termination handler for console events including
 
227
    adv = ctypes.windll.advapi32
 
228
    size = _DWORD(300)
 
229
    buf = ctypes.create_string_buffer(size.value + 1)
 
230
    if not adv.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
 
231
        raise ctypes.WinError()
 
232
    return buf.value
 
233
 
 
234
_SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
 
235
_signal_handler = []
 
236
 
 
237
def set_signal_handler():
 
238
    '''Register a termination handler for console events including
152
239
    CTRL+C. python signal handlers do not work well with socket
153
240
    operations.
154
 
    """
 
241
    '''
155
242
    def handler(event):
156
 
        win32process.ExitProcess(1)
157
 
    win32api.SetConsoleCtrlHandler(handler)
 
243
        _kernel32.ExitProcess(1)
 
244
 
 
245
    if _signal_handler:
 
246
        return # already registered
 
247
    h = _SIGNAL_HANDLER(handler)
 
248
    _signal_handler.append(h) # needed to prevent garbage collection
 
249
    if not _kernel32.SetConsoleCtrlHandler(h, True):
 
250
        raise ctypes.WinError()
 
251
 
 
252
_WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
158
253
 
159
254
def hidewindow():
160
 
    def callback(*args, **kwargs):
161
 
        hwnd, pid = args
162
 
        wpid = win32process.GetWindowThreadProcessId(hwnd)[1]
163
 
        if pid == wpid:
164
 
            win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
165
 
 
166
 
    pid =  win32process.GetCurrentProcessId()
167
 
    win32gui.EnumWindows(callback, pid)
 
255
    user32 = ctypes.windll.user32
 
256
 
 
257
    def callback(hwnd, pid):
 
258
        wpid = _DWORD()
 
259
        user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
 
260
        if pid == wpid.value:
 
261
            user32.ShowWindow(hwnd, _SW_HIDE)
 
262
            return False # stop enumerating windows
 
263
        return True
 
264
 
 
265
    pid = _kernel32.GetCurrentProcessId()
 
266
    user32.EnumWindows(_WNDENUMPROC(callback), pid)
168
267
 
169
268
def termwidth():
170
 
    try:
171
 
        # Query stderr to avoid problems with redirections
172
 
        screenbuf = win32console.GetStdHandle(win32console.STD_ERROR_HANDLE)
173
 
        if screenbuf is None:
174
 
            return 79
175
 
        try:
176
 
            window = screenbuf.GetConsoleScreenBufferInfo()['Window']
177
 
            width = window.Right - window.Left
178
 
            return width
179
 
        finally:
180
 
            screenbuf.Detach()
181
 
    except pywintypes.error:
182
 
        return 79
 
269
    # cmd.exe does not handle CR like a unix console, the CR is
 
270
    # counted in the line length. On 80 columns consoles, if 80
 
271
    # characters are written, the following CR won't apply on the
 
272
    # current line but on the new one. Keep room for it.
 
273
    width = 79
 
274
    # Query stderr to avoid problems with redirections
 
275
    screenbuf = _kernel32.GetStdHandle(
 
276
                  _STD_ERROR_HANDLE) # don't close the handle returned
 
277
    if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
 
278
        return width
 
279
    csbi = _CONSOLE_SCREEN_BUFFER_INFO()
 
280
    if not _kernel32.GetConsoleScreenBufferInfo(
 
281
                        screenbuf, ctypes.byref(csbi)):
 
282
        return width
 
283
    width = csbi.srWindow.Right - csbi.srWindow.Left
 
284
    return width
 
285
 
 
286
def spawndetached(args):
 
287
    # No standard library function really spawns a fully detached
 
288
    # process under win32 because they allocate pipes or other objects
 
289
    # to handle standard streams communications. Passing these objects
 
290
    # to the child process requires handle inheritance to be enabled
 
291
    # which makes really detached processes impossible.
 
292
    si = _STARTUPINFO()
 
293
    si.cb = ctypes.sizeof(_STARTUPINFO)
 
294
    si.dwFlags = _STARTF_USESHOWWINDOW
 
295
    si.wShowWindow = _SW_HIDE
 
296
 
 
297
    pi = _PROCESS_INFORMATION()
 
298
 
 
299
    env = ''
 
300
    for k in os.environ:
 
301
        env += "%s=%s\0" % (k, os.environ[k])
 
302
    if not env:
 
303
        env = '\0'
 
304
    env += '\0'
 
305
 
 
306
    args = subprocess.list2cmdline(args)
 
307
    # Not running the command in shell mode makes python26 hang when
 
308
    # writing to hgweb output socket.
 
309
    comspec = os.environ.get("COMSPEC", "cmd.exe")
 
310
    args = comspec + " /c " + args
 
311
 
 
312
    res = _kernel32.CreateProcessA(
 
313
        None, args, None, None, False, _DETACHED_PROCESS,
 
314
        env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
 
315
    if not res:
 
316
        raise ctypes.WinError()
 
317
 
 
318
    return pi.dwProcessId