##// END OF EJS Templates
win32.py: let samefile and samedevice work on directories too
Adrian Buehlmann -
r17006:6fc7fd72 default
parent child Browse files
Show More
@@ -1,396 +1,396 b''
1 # win32.py - utility functions that use win32 API
1 # win32.py - utility functions that use win32 API
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import ctypes, errno, os, subprocess, random
8 import ctypes, errno, os, subprocess, random
9
9
10 _kernel32 = ctypes.windll.kernel32
10 _kernel32 = ctypes.windll.kernel32
11 _advapi32 = ctypes.windll.advapi32
11 _advapi32 = ctypes.windll.advapi32
12 _user32 = ctypes.windll.user32
12 _user32 = ctypes.windll.user32
13
13
14 _BOOL = ctypes.c_long
14 _BOOL = ctypes.c_long
15 _WORD = ctypes.c_ushort
15 _WORD = ctypes.c_ushort
16 _DWORD = ctypes.c_ulong
16 _DWORD = ctypes.c_ulong
17 _UINT = ctypes.c_uint
17 _UINT = ctypes.c_uint
18 _LONG = ctypes.c_long
18 _LONG = ctypes.c_long
19 _LPCSTR = _LPSTR = ctypes.c_char_p
19 _LPCSTR = _LPSTR = ctypes.c_char_p
20 _HANDLE = ctypes.c_void_p
20 _HANDLE = ctypes.c_void_p
21 _HWND = _HANDLE
21 _HWND = _HANDLE
22
22
23 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
23 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
24
24
25 # GetLastError
25 # GetLastError
26 _ERROR_SUCCESS = 0
26 _ERROR_SUCCESS = 0
27 _ERROR_INVALID_PARAMETER = 87
27 _ERROR_INVALID_PARAMETER = 87
28 _ERROR_INSUFFICIENT_BUFFER = 122
28 _ERROR_INSUFFICIENT_BUFFER = 122
29
29
30 # WPARAM is defined as UINT_PTR (unsigned type)
30 # WPARAM is defined as UINT_PTR (unsigned type)
31 # LPARAM is defined as LONG_PTR (signed type)
31 # LPARAM is defined as LONG_PTR (signed type)
32 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
32 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
33 _WPARAM = ctypes.c_ulong
33 _WPARAM = ctypes.c_ulong
34 _LPARAM = ctypes.c_long
34 _LPARAM = ctypes.c_long
35 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
35 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
36 _WPARAM = ctypes.c_ulonglong
36 _WPARAM = ctypes.c_ulonglong
37 _LPARAM = ctypes.c_longlong
37 _LPARAM = ctypes.c_longlong
38
38
39 class _FILETIME(ctypes.Structure):
39 class _FILETIME(ctypes.Structure):
40 _fields_ = [('dwLowDateTime', _DWORD),
40 _fields_ = [('dwLowDateTime', _DWORD),
41 ('dwHighDateTime', _DWORD)]
41 ('dwHighDateTime', _DWORD)]
42
42
43 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
43 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
44 _fields_ = [('dwFileAttributes', _DWORD),
44 _fields_ = [('dwFileAttributes', _DWORD),
45 ('ftCreationTime', _FILETIME),
45 ('ftCreationTime', _FILETIME),
46 ('ftLastAccessTime', _FILETIME),
46 ('ftLastAccessTime', _FILETIME),
47 ('ftLastWriteTime', _FILETIME),
47 ('ftLastWriteTime', _FILETIME),
48 ('dwVolumeSerialNumber', _DWORD),
48 ('dwVolumeSerialNumber', _DWORD),
49 ('nFileSizeHigh', _DWORD),
49 ('nFileSizeHigh', _DWORD),
50 ('nFileSizeLow', _DWORD),
50 ('nFileSizeLow', _DWORD),
51 ('nNumberOfLinks', _DWORD),
51 ('nNumberOfLinks', _DWORD),
52 ('nFileIndexHigh', _DWORD),
52 ('nFileIndexHigh', _DWORD),
53 ('nFileIndexLow', _DWORD)]
53 ('nFileIndexLow', _DWORD)]
54
54
55 # CreateFile
55 # CreateFile
56 _FILE_SHARE_READ = 0x00000001
56 _FILE_SHARE_READ = 0x00000001
57 _FILE_SHARE_WRITE = 0x00000002
57 _FILE_SHARE_WRITE = 0x00000002
58 _FILE_SHARE_DELETE = 0x00000004
58 _FILE_SHARE_DELETE = 0x00000004
59
59
60 _OPEN_EXISTING = 3
60 _OPEN_EXISTING = 3
61
61
62 _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
63
62 # SetFileAttributes
64 # SetFileAttributes
63 _FILE_ATTRIBUTE_NORMAL = 0x80
65 _FILE_ATTRIBUTE_NORMAL = 0x80
64 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
66 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
65
67
66 # Process Security and Access Rights
68 # Process Security and Access Rights
67 _PROCESS_QUERY_INFORMATION = 0x0400
69 _PROCESS_QUERY_INFORMATION = 0x0400
68
70
69 # GetExitCodeProcess
71 # GetExitCodeProcess
70 _STILL_ACTIVE = 259
72 _STILL_ACTIVE = 259
71
73
72 class _STARTUPINFO(ctypes.Structure):
74 class _STARTUPINFO(ctypes.Structure):
73 _fields_ = [('cb', _DWORD),
75 _fields_ = [('cb', _DWORD),
74 ('lpReserved', _LPSTR),
76 ('lpReserved', _LPSTR),
75 ('lpDesktop', _LPSTR),
77 ('lpDesktop', _LPSTR),
76 ('lpTitle', _LPSTR),
78 ('lpTitle', _LPSTR),
77 ('dwX', _DWORD),
79 ('dwX', _DWORD),
78 ('dwY', _DWORD),
80 ('dwY', _DWORD),
79 ('dwXSize', _DWORD),
81 ('dwXSize', _DWORD),
80 ('dwYSize', _DWORD),
82 ('dwYSize', _DWORD),
81 ('dwXCountChars', _DWORD),
83 ('dwXCountChars', _DWORD),
82 ('dwYCountChars', _DWORD),
84 ('dwYCountChars', _DWORD),
83 ('dwFillAttribute', _DWORD),
85 ('dwFillAttribute', _DWORD),
84 ('dwFlags', _DWORD),
86 ('dwFlags', _DWORD),
85 ('wShowWindow', _WORD),
87 ('wShowWindow', _WORD),
86 ('cbReserved2', _WORD),
88 ('cbReserved2', _WORD),
87 ('lpReserved2', ctypes.c_char_p),
89 ('lpReserved2', ctypes.c_char_p),
88 ('hStdInput', _HANDLE),
90 ('hStdInput', _HANDLE),
89 ('hStdOutput', _HANDLE),
91 ('hStdOutput', _HANDLE),
90 ('hStdError', _HANDLE)]
92 ('hStdError', _HANDLE)]
91
93
92 class _PROCESS_INFORMATION(ctypes.Structure):
94 class _PROCESS_INFORMATION(ctypes.Structure):
93 _fields_ = [('hProcess', _HANDLE),
95 _fields_ = [('hProcess', _HANDLE),
94 ('hThread', _HANDLE),
96 ('hThread', _HANDLE),
95 ('dwProcessId', _DWORD),
97 ('dwProcessId', _DWORD),
96 ('dwThreadId', _DWORD)]
98 ('dwThreadId', _DWORD)]
97
99
98 _DETACHED_PROCESS = 0x00000008
100 _DETACHED_PROCESS = 0x00000008
99 _STARTF_USESHOWWINDOW = 0x00000001
101 _STARTF_USESHOWWINDOW = 0x00000001
100 _SW_HIDE = 0
102 _SW_HIDE = 0
101
103
102 class _COORD(ctypes.Structure):
104 class _COORD(ctypes.Structure):
103 _fields_ = [('X', ctypes.c_short),
105 _fields_ = [('X', ctypes.c_short),
104 ('Y', ctypes.c_short)]
106 ('Y', ctypes.c_short)]
105
107
106 class _SMALL_RECT(ctypes.Structure):
108 class _SMALL_RECT(ctypes.Structure):
107 _fields_ = [('Left', ctypes.c_short),
109 _fields_ = [('Left', ctypes.c_short),
108 ('Top', ctypes.c_short),
110 ('Top', ctypes.c_short),
109 ('Right', ctypes.c_short),
111 ('Right', ctypes.c_short),
110 ('Bottom', ctypes.c_short)]
112 ('Bottom', ctypes.c_short)]
111
113
112 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
114 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
113 _fields_ = [('dwSize', _COORD),
115 _fields_ = [('dwSize', _COORD),
114 ('dwCursorPosition', _COORD),
116 ('dwCursorPosition', _COORD),
115 ('wAttributes', _WORD),
117 ('wAttributes', _WORD),
116 ('srWindow', _SMALL_RECT),
118 ('srWindow', _SMALL_RECT),
117 ('dwMaximumWindowSize', _COORD)]
119 ('dwMaximumWindowSize', _COORD)]
118
120
119 _STD_ERROR_HANDLE = _DWORD(-12).value
121 _STD_ERROR_HANDLE = _DWORD(-12).value
120
122
121 # types of parameters of C functions used (required by pypy)
123 # types of parameters of C functions used (required by pypy)
122
124
123 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
125 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
124 _DWORD, _DWORD, _HANDLE]
126 _DWORD, _DWORD, _HANDLE]
125 _kernel32.CreateFileA.restype = _HANDLE
127 _kernel32.CreateFileA.restype = _HANDLE
126
128
127 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
129 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
128 _kernel32.GetFileInformationByHandle.restype = _BOOL
130 _kernel32.GetFileInformationByHandle.restype = _BOOL
129
131
130 _kernel32.CloseHandle.argtypes = [_HANDLE]
132 _kernel32.CloseHandle.argtypes = [_HANDLE]
131 _kernel32.CloseHandle.restype = _BOOL
133 _kernel32.CloseHandle.restype = _BOOL
132
134
133 try:
135 try:
134 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
136 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
135 _kernel32.CreateHardLinkA.restype = _BOOL
137 _kernel32.CreateHardLinkA.restype = _BOOL
136 except AttributeError:
138 except AttributeError:
137 pass
139 pass
138
140
139 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
141 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
140 _kernel32.SetFileAttributesA.restype = _BOOL
142 _kernel32.SetFileAttributesA.restype = _BOOL
141
143
142 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
144 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
143 _kernel32.OpenProcess.restype = _HANDLE
145 _kernel32.OpenProcess.restype = _HANDLE
144
146
145 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
147 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
146 _kernel32.GetExitCodeProcess.restype = _BOOL
148 _kernel32.GetExitCodeProcess.restype = _BOOL
147
149
148 _kernel32.GetLastError.argtypes = []
150 _kernel32.GetLastError.argtypes = []
149 _kernel32.GetLastError.restype = _DWORD
151 _kernel32.GetLastError.restype = _DWORD
150
152
151 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
153 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
152 _kernel32.GetModuleFileNameA.restype = _DWORD
154 _kernel32.GetModuleFileNameA.restype = _DWORD
153
155
154 _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
156 _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
155 ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
157 ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
156 ctypes.c_void_p]
158 ctypes.c_void_p]
157 _kernel32.CreateProcessA.restype = _BOOL
159 _kernel32.CreateProcessA.restype = _BOOL
158
160
159 _kernel32.ExitProcess.argtypes = [_UINT]
161 _kernel32.ExitProcess.argtypes = [_UINT]
160 _kernel32.ExitProcess.restype = None
162 _kernel32.ExitProcess.restype = None
161
163
162 _kernel32.GetCurrentProcessId.argtypes = []
164 _kernel32.GetCurrentProcessId.argtypes = []
163 _kernel32.GetCurrentProcessId.restype = _DWORD
165 _kernel32.GetCurrentProcessId.restype = _DWORD
164
166
165 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
167 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
166 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
168 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
167 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
169 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
168
170
169 _kernel32.GetStdHandle.argtypes = [_DWORD]
171 _kernel32.GetStdHandle.argtypes = [_DWORD]
170 _kernel32.GetStdHandle.restype = _HANDLE
172 _kernel32.GetStdHandle.restype = _HANDLE
171
173
172 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
174 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
173 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
175 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
174
176
175 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
177 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
176 _advapi32.GetUserNameA.restype = _BOOL
178 _advapi32.GetUserNameA.restype = _BOOL
177
179
178 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
180 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
179 _user32.GetWindowThreadProcessId.restype = _DWORD
181 _user32.GetWindowThreadProcessId.restype = _DWORD
180
182
181 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
183 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
182 _user32.ShowWindow.restype = _BOOL
184 _user32.ShowWindow.restype = _BOOL
183
185
184 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
186 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
185 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
187 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
186 _user32.EnumWindows.restype = _BOOL
188 _user32.EnumWindows.restype = _BOOL
187
189
188 def _raiseoserror(name):
190 def _raiseoserror(name):
189 err = ctypes.WinError()
191 err = ctypes.WinError()
190 raise OSError(err.errno, '%s: %s' % (name, err.strerror))
192 raise OSError(err.errno, '%s: %s' % (name, err.strerror))
191
193
192 def _getfileinfo(name):
194 def _getfileinfo(name):
193 fh = _kernel32.CreateFileA(name, 0,
195 fh = _kernel32.CreateFileA(name, 0,
194 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
196 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
195 None, _OPEN_EXISTING, 0, None)
197 None, _OPEN_EXISTING, _FILE_FLAG_BACKUP_SEMANTICS, None)
196 if fh == _INVALID_HANDLE_VALUE:
198 if fh == _INVALID_HANDLE_VALUE:
197 _raiseoserror(name)
199 _raiseoserror(name)
198 try:
200 try:
199 fi = _BY_HANDLE_FILE_INFORMATION()
201 fi = _BY_HANDLE_FILE_INFORMATION()
200 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
202 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
201 _raiseoserror(name)
203 _raiseoserror(name)
202 return fi
204 return fi
203 finally:
205 finally:
204 _kernel32.CloseHandle(fh)
206 _kernel32.CloseHandle(fh)
205
207
206 def oslink(src, dst):
208 def oslink(src, dst):
207 try:
209 try:
208 if not _kernel32.CreateHardLinkA(dst, src, None):
210 if not _kernel32.CreateHardLinkA(dst, src, None):
209 _raiseoserror(src)
211 _raiseoserror(src)
210 except AttributeError: # Wine doesn't support this function
212 except AttributeError: # Wine doesn't support this function
211 _raiseoserror(src)
213 _raiseoserror(src)
212
214
213 def nlinks(name):
215 def nlinks(name):
214 '''return number of hardlinks for the given file'''
216 '''return number of hardlinks for the given file'''
215 return _getfileinfo(name).nNumberOfLinks
217 return _getfileinfo(name).nNumberOfLinks
216
218
217 def samefile(fpath1, fpath2):
219 def samefile(path1, path2):
218 '''Returns whether fpath1 and fpath2 refer to the same file. This is only
220 '''Returns whether path1 and path2 refer to the same file or directory.'''
219 guaranteed to work for files, not directories.'''
221 res1 = _getfileinfo(path1)
220 res1 = _getfileinfo(fpath1)
222 res2 = _getfileinfo(path2)
221 res2 = _getfileinfo(fpath2)
222 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
223 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
223 and res1.nFileIndexHigh == res2.nFileIndexHigh
224 and res1.nFileIndexHigh == res2.nFileIndexHigh
224 and res1.nFileIndexLow == res2.nFileIndexLow)
225 and res1.nFileIndexLow == res2.nFileIndexLow)
225
226
226 def samedevice(fpath1, fpath2):
227 def samedevice(path1, path2):
227 '''Returns whether fpath1 and fpath2 are on the same device. This is only
228 '''Returns whether path1 and path2 are on the same device.'''
228 guaranteed to work for files, not directories.'''
229 res1 = _getfileinfo(path1)
229 res1 = _getfileinfo(fpath1)
230 res2 = _getfileinfo(path2)
230 res2 = _getfileinfo(fpath2)
231 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
231 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
232
232
233 def testpid(pid):
233 def testpid(pid):
234 '''return True if pid is still running or unable to
234 '''return True if pid is still running or unable to
235 determine, False otherwise'''
235 determine, False otherwise'''
236 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
236 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
237 if h:
237 if h:
238 try:
238 try:
239 status = _DWORD()
239 status = _DWORD()
240 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
240 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
241 return status.value == _STILL_ACTIVE
241 return status.value == _STILL_ACTIVE
242 finally:
242 finally:
243 _kernel32.CloseHandle(h)
243 _kernel32.CloseHandle(h)
244 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
244 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
245
245
246 def executablepath():
246 def executablepath():
247 '''return full path of hg.exe'''
247 '''return full path of hg.exe'''
248 size = 600
248 size = 600
249 buf = ctypes.create_string_buffer(size + 1)
249 buf = ctypes.create_string_buffer(size + 1)
250 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
250 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
251 if len == 0:
251 if len == 0:
252 raise ctypes.WinError
252 raise ctypes.WinError
253 elif len == size:
253 elif len == size:
254 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
254 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
255 return buf.value
255 return buf.value
256
256
257 def getuser():
257 def getuser():
258 '''return name of current user'''
258 '''return name of current user'''
259 size = _DWORD(300)
259 size = _DWORD(300)
260 buf = ctypes.create_string_buffer(size.value + 1)
260 buf = ctypes.create_string_buffer(size.value + 1)
261 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
261 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
262 raise ctypes.WinError
262 raise ctypes.WinError
263 return buf.value
263 return buf.value
264
264
265 _signalhandler = []
265 _signalhandler = []
266
266
267 def setsignalhandler():
267 def setsignalhandler():
268 '''Register a termination handler for console events including
268 '''Register a termination handler for console events including
269 CTRL+C. python signal handlers do not work well with socket
269 CTRL+C. python signal handlers do not work well with socket
270 operations.
270 operations.
271 '''
271 '''
272 def handler(event):
272 def handler(event):
273 _kernel32.ExitProcess(1)
273 _kernel32.ExitProcess(1)
274
274
275 if _signalhandler:
275 if _signalhandler:
276 return # already registered
276 return # already registered
277 h = _SIGNAL_HANDLER(handler)
277 h = _SIGNAL_HANDLER(handler)
278 _signalhandler.append(h) # needed to prevent garbage collection
278 _signalhandler.append(h) # needed to prevent garbage collection
279 if not _kernel32.SetConsoleCtrlHandler(h, True):
279 if not _kernel32.SetConsoleCtrlHandler(h, True):
280 raise ctypes.WinError
280 raise ctypes.WinError
281
281
282 def hidewindow():
282 def hidewindow():
283
283
284 def callback(hwnd, pid):
284 def callback(hwnd, pid):
285 wpid = _DWORD()
285 wpid = _DWORD()
286 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
286 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
287 if pid == wpid.value:
287 if pid == wpid.value:
288 _user32.ShowWindow(hwnd, _SW_HIDE)
288 _user32.ShowWindow(hwnd, _SW_HIDE)
289 return False # stop enumerating windows
289 return False # stop enumerating windows
290 return True
290 return True
291
291
292 pid = _kernel32.GetCurrentProcessId()
292 pid = _kernel32.GetCurrentProcessId()
293 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
293 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
294
294
295 def termwidth():
295 def termwidth():
296 # cmd.exe does not handle CR like a unix console, the CR is
296 # cmd.exe does not handle CR like a unix console, the CR is
297 # counted in the line length. On 80 columns consoles, if 80
297 # counted in the line length. On 80 columns consoles, if 80
298 # characters are written, the following CR won't apply on the
298 # characters are written, the following CR won't apply on the
299 # current line but on the new one. Keep room for it.
299 # current line but on the new one. Keep room for it.
300 width = 79
300 width = 79
301 # Query stderr to avoid problems with redirections
301 # Query stderr to avoid problems with redirections
302 screenbuf = _kernel32.GetStdHandle(
302 screenbuf = _kernel32.GetStdHandle(
303 _STD_ERROR_HANDLE) # don't close the handle returned
303 _STD_ERROR_HANDLE) # don't close the handle returned
304 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
304 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
305 return width
305 return width
306 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
306 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
307 if not _kernel32.GetConsoleScreenBufferInfo(
307 if not _kernel32.GetConsoleScreenBufferInfo(
308 screenbuf, ctypes.byref(csbi)):
308 screenbuf, ctypes.byref(csbi)):
309 return width
309 return width
310 width = csbi.srWindow.Right - csbi.srWindow.Left
310 width = csbi.srWindow.Right - csbi.srWindow.Left
311 return width
311 return width
312
312
313 def spawndetached(args):
313 def spawndetached(args):
314 # No standard library function really spawns a fully detached
314 # No standard library function really spawns a fully detached
315 # process under win32 because they allocate pipes or other objects
315 # process under win32 because they allocate pipes or other objects
316 # to handle standard streams communications. Passing these objects
316 # to handle standard streams communications. Passing these objects
317 # to the child process requires handle inheritance to be enabled
317 # to the child process requires handle inheritance to be enabled
318 # which makes really detached processes impossible.
318 # which makes really detached processes impossible.
319 si = _STARTUPINFO()
319 si = _STARTUPINFO()
320 si.cb = ctypes.sizeof(_STARTUPINFO)
320 si.cb = ctypes.sizeof(_STARTUPINFO)
321 si.dwFlags = _STARTF_USESHOWWINDOW
321 si.dwFlags = _STARTF_USESHOWWINDOW
322 si.wShowWindow = _SW_HIDE
322 si.wShowWindow = _SW_HIDE
323
323
324 pi = _PROCESS_INFORMATION()
324 pi = _PROCESS_INFORMATION()
325
325
326 env = ''
326 env = ''
327 for k in os.environ:
327 for k in os.environ:
328 env += "%s=%s\0" % (k, os.environ[k])
328 env += "%s=%s\0" % (k, os.environ[k])
329 if not env:
329 if not env:
330 env = '\0'
330 env = '\0'
331 env += '\0'
331 env += '\0'
332
332
333 args = subprocess.list2cmdline(args)
333 args = subprocess.list2cmdline(args)
334 # Not running the command in shell mode makes python26 hang when
334 # Not running the command in shell mode makes python26 hang when
335 # writing to hgweb output socket.
335 # writing to hgweb output socket.
336 comspec = os.environ.get("COMSPEC", "cmd.exe")
336 comspec = os.environ.get("COMSPEC", "cmd.exe")
337 args = comspec + " /c " + args
337 args = comspec + " /c " + args
338
338
339 res = _kernel32.CreateProcessA(
339 res = _kernel32.CreateProcessA(
340 None, args, None, None, False, _DETACHED_PROCESS,
340 None, args, None, None, False, _DETACHED_PROCESS,
341 env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
341 env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
342 if not res:
342 if not res:
343 raise ctypes.WinError
343 raise ctypes.WinError
344
344
345 return pi.dwProcessId
345 return pi.dwProcessId
346
346
347 def unlink(f):
347 def unlink(f):
348 '''try to implement POSIX' unlink semantics on Windows'''
348 '''try to implement POSIX' unlink semantics on Windows'''
349
349
350 # POSIX allows to unlink and rename open files. Windows has serious
350 # POSIX allows to unlink and rename open files. Windows has serious
351 # problems with doing that:
351 # problems with doing that:
352 # - Calling os.unlink (or os.rename) on a file f fails if f or any
352 # - Calling os.unlink (or os.rename) on a file f fails if f or any
353 # hardlinked copy of f has been opened with Python's open(). There is no
353 # hardlinked copy of f has been opened with Python's open(). There is no
354 # way such a file can be deleted or renamed on Windows (other than
354 # way such a file can be deleted or renamed on Windows (other than
355 # scheduling the delete or rename for the next reboot).
355 # scheduling the delete or rename for the next reboot).
356 # - Calling os.unlink on a file that has been opened with Mercurial's
356 # - Calling os.unlink on a file that has been opened with Mercurial's
357 # posixfile (or comparable methods) will delay the actual deletion of
357 # posixfile (or comparable methods) will delay the actual deletion of
358 # the file for as long as the file is held open. The filename is blocked
358 # the file for as long as the file is held open. The filename is blocked
359 # during that time and cannot be used for recreating a new file under
359 # during that time and cannot be used for recreating a new file under
360 # that same name ("zombie file"). Directories containing such zombie files
360 # that same name ("zombie file"). Directories containing such zombie files
361 # cannot be removed or moved.
361 # cannot be removed or moved.
362 # A file that has been opened with posixfile can be renamed, so we rename
362 # A file that has been opened with posixfile can be renamed, so we rename
363 # f to a random temporary name before calling os.unlink on it. This allows
363 # f to a random temporary name before calling os.unlink on it. This allows
364 # callers to recreate f immediately while having other readers do their
364 # callers to recreate f immediately while having other readers do their
365 # implicit zombie filename blocking on a temporary name.
365 # implicit zombie filename blocking on a temporary name.
366
366
367 for tries in xrange(10):
367 for tries in xrange(10):
368 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
368 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
369 try:
369 try:
370 os.rename(f, temp) # raises OSError EEXIST if temp exists
370 os.rename(f, temp) # raises OSError EEXIST if temp exists
371 break
371 break
372 except OSError, e:
372 except OSError, e:
373 if e.errno != errno.EEXIST:
373 if e.errno != errno.EEXIST:
374 raise
374 raise
375 else:
375 else:
376 raise IOError, (errno.EEXIST, "No usable temporary filename found")
376 raise IOError, (errno.EEXIST, "No usable temporary filename found")
377
377
378 try:
378 try:
379 os.unlink(temp)
379 os.unlink(temp)
380 except OSError:
380 except OSError:
381 # The unlink might have failed because the READONLY attribute may heave
381 # The unlink might have failed because the READONLY attribute may heave
382 # been set on the original file. Rename works fine with READONLY set,
382 # been set on the original file. Rename works fine with READONLY set,
383 # but not os.unlink. Reset all attributes and try again.
383 # but not os.unlink. Reset all attributes and try again.
384 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
384 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
385 try:
385 try:
386 os.unlink(temp)
386 os.unlink(temp)
387 except OSError:
387 except OSError:
388 # The unlink might have failed due to some very rude AV-Scanners.
388 # The unlink might have failed due to some very rude AV-Scanners.
389 # Leaking a tempfile is the lesser evil than aborting here and
389 # Leaking a tempfile is the lesser evil than aborting here and
390 # leaving some potentially serious inconsistencies.
390 # leaving some potentially serious inconsistencies.
391 pass
391 pass
392
392
393 def makedir(path, notindexed):
393 def makedir(path, notindexed):
394 os.mkdir(path)
394 os.mkdir(path)
395 if notindexed:
395 if notindexed:
396 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
396 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
General Comments 0
You need to be logged in to leave comments. Login now