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