##// END OF EJS Templates
py3: resolve Unicode issues around `hg serve` on Windows...
Matt Harbison -
r39755:255d1885 default
parent child Browse files
Show More
@@ -1,647 +1,648 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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import ctypes
10 import ctypes
11 import ctypes.wintypes as wintypes
11 import ctypes.wintypes as wintypes
12 import errno
12 import errno
13 import msvcrt
13 import msvcrt
14 import os
14 import os
15 import random
15 import random
16 import subprocess
16 import subprocess
17
17
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 pycompat,
20 pycompat,
21 )
21 )
22
22
23 _kernel32 = ctypes.windll.kernel32
23 _kernel32 = ctypes.windll.kernel32
24 _advapi32 = ctypes.windll.advapi32
24 _advapi32 = ctypes.windll.advapi32
25 _user32 = ctypes.windll.user32
25 _user32 = ctypes.windll.user32
26 _crypt32 = ctypes.windll.crypt32
26 _crypt32 = ctypes.windll.crypt32
27
27
28 _BOOL = ctypes.c_long
28 _BOOL = ctypes.c_long
29 _WORD = ctypes.c_ushort
29 _WORD = ctypes.c_ushort
30 _DWORD = ctypes.c_ulong
30 _DWORD = ctypes.c_ulong
31 _UINT = ctypes.c_uint
31 _UINT = ctypes.c_uint
32 _LONG = ctypes.c_long
32 _LONG = ctypes.c_long
33 _LPCSTR = _LPSTR = ctypes.c_char_p
33 _LPCSTR = _LPSTR = ctypes.c_char_p
34 _HANDLE = ctypes.c_void_p
34 _HANDLE = ctypes.c_void_p
35 _HWND = _HANDLE
35 _HWND = _HANDLE
36 _PCCERT_CONTEXT = ctypes.c_void_p
36 _PCCERT_CONTEXT = ctypes.c_void_p
37 _MAX_PATH = wintypes.MAX_PATH
37 _MAX_PATH = wintypes.MAX_PATH
38
38
39 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
39 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
40
40
41 # GetLastError
41 # GetLastError
42 _ERROR_SUCCESS = 0
42 _ERROR_SUCCESS = 0
43 _ERROR_NO_MORE_FILES = 18
43 _ERROR_NO_MORE_FILES = 18
44 _ERROR_INVALID_PARAMETER = 87
44 _ERROR_INVALID_PARAMETER = 87
45 _ERROR_BROKEN_PIPE = 109
45 _ERROR_BROKEN_PIPE = 109
46 _ERROR_INSUFFICIENT_BUFFER = 122
46 _ERROR_INSUFFICIENT_BUFFER = 122
47 _ERROR_NO_DATA = 232
47 _ERROR_NO_DATA = 232
48
48
49 # WPARAM is defined as UINT_PTR (unsigned type)
49 # WPARAM is defined as UINT_PTR (unsigned type)
50 # LPARAM is defined as LONG_PTR (signed type)
50 # LPARAM is defined as LONG_PTR (signed type)
51 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
51 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
52 _WPARAM = ctypes.c_ulong
52 _WPARAM = ctypes.c_ulong
53 _LPARAM = ctypes.c_long
53 _LPARAM = ctypes.c_long
54 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
54 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
55 _WPARAM = ctypes.c_ulonglong
55 _WPARAM = ctypes.c_ulonglong
56 _LPARAM = ctypes.c_longlong
56 _LPARAM = ctypes.c_longlong
57
57
58 class _FILETIME(ctypes.Structure):
58 class _FILETIME(ctypes.Structure):
59 _fields_ = [(r'dwLowDateTime', _DWORD),
59 _fields_ = [(r'dwLowDateTime', _DWORD),
60 (r'dwHighDateTime', _DWORD)]
60 (r'dwHighDateTime', _DWORD)]
61
61
62 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
62 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
63 _fields_ = [(r'dwFileAttributes', _DWORD),
63 _fields_ = [(r'dwFileAttributes', _DWORD),
64 (r'ftCreationTime', _FILETIME),
64 (r'ftCreationTime', _FILETIME),
65 (r'ftLastAccessTime', _FILETIME),
65 (r'ftLastAccessTime', _FILETIME),
66 (r'ftLastWriteTime', _FILETIME),
66 (r'ftLastWriteTime', _FILETIME),
67 (r'dwVolumeSerialNumber', _DWORD),
67 (r'dwVolumeSerialNumber', _DWORD),
68 (r'nFileSizeHigh', _DWORD),
68 (r'nFileSizeHigh', _DWORD),
69 (r'nFileSizeLow', _DWORD),
69 (r'nFileSizeLow', _DWORD),
70 (r'nNumberOfLinks', _DWORD),
70 (r'nNumberOfLinks', _DWORD),
71 (r'nFileIndexHigh', _DWORD),
71 (r'nFileIndexHigh', _DWORD),
72 (r'nFileIndexLow', _DWORD)]
72 (r'nFileIndexLow', _DWORD)]
73
73
74 # CreateFile
74 # CreateFile
75 _FILE_SHARE_READ = 0x00000001
75 _FILE_SHARE_READ = 0x00000001
76 _FILE_SHARE_WRITE = 0x00000002
76 _FILE_SHARE_WRITE = 0x00000002
77 _FILE_SHARE_DELETE = 0x00000004
77 _FILE_SHARE_DELETE = 0x00000004
78
78
79 _OPEN_EXISTING = 3
79 _OPEN_EXISTING = 3
80
80
81 _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
81 _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
82
82
83 # SetFileAttributes
83 # SetFileAttributes
84 _FILE_ATTRIBUTE_NORMAL = 0x80
84 _FILE_ATTRIBUTE_NORMAL = 0x80
85 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
85 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
86
86
87 # Process Security and Access Rights
87 # Process Security and Access Rights
88 _PROCESS_QUERY_INFORMATION = 0x0400
88 _PROCESS_QUERY_INFORMATION = 0x0400
89
89
90 # GetExitCodeProcess
90 # GetExitCodeProcess
91 _STILL_ACTIVE = 259
91 _STILL_ACTIVE = 259
92
92
93 class _STARTUPINFO(ctypes.Structure):
93 class _STARTUPINFO(ctypes.Structure):
94 _fields_ = [(r'cb', _DWORD),
94 _fields_ = [(r'cb', _DWORD),
95 (r'lpReserved', _LPSTR),
95 (r'lpReserved', _LPSTR),
96 (r'lpDesktop', _LPSTR),
96 (r'lpDesktop', _LPSTR),
97 (r'lpTitle', _LPSTR),
97 (r'lpTitle', _LPSTR),
98 (r'dwX', _DWORD),
98 (r'dwX', _DWORD),
99 (r'dwY', _DWORD),
99 (r'dwY', _DWORD),
100 (r'dwXSize', _DWORD),
100 (r'dwXSize', _DWORD),
101 (r'dwYSize', _DWORD),
101 (r'dwYSize', _DWORD),
102 (r'dwXCountChars', _DWORD),
102 (r'dwXCountChars', _DWORD),
103 (r'dwYCountChars', _DWORD),
103 (r'dwYCountChars', _DWORD),
104 (r'dwFillAttribute', _DWORD),
104 (r'dwFillAttribute', _DWORD),
105 (r'dwFlags', _DWORD),
105 (r'dwFlags', _DWORD),
106 (r'wShowWindow', _WORD),
106 (r'wShowWindow', _WORD),
107 (r'cbReserved2', _WORD),
107 (r'cbReserved2', _WORD),
108 (r'lpReserved2', ctypes.c_char_p),
108 (r'lpReserved2', ctypes.c_char_p),
109 (r'hStdInput', _HANDLE),
109 (r'hStdInput', _HANDLE),
110 (r'hStdOutput', _HANDLE),
110 (r'hStdOutput', _HANDLE),
111 (r'hStdError', _HANDLE)]
111 (r'hStdError', _HANDLE)]
112
112
113 class _PROCESS_INFORMATION(ctypes.Structure):
113 class _PROCESS_INFORMATION(ctypes.Structure):
114 _fields_ = [(r'hProcess', _HANDLE),
114 _fields_ = [(r'hProcess', _HANDLE),
115 (r'hThread', _HANDLE),
115 (r'hThread', _HANDLE),
116 (r'dwProcessId', _DWORD),
116 (r'dwProcessId', _DWORD),
117 (r'dwThreadId', _DWORD)]
117 (r'dwThreadId', _DWORD)]
118
118
119 _CREATE_NO_WINDOW = 0x08000000
119 _CREATE_NO_WINDOW = 0x08000000
120 _SW_HIDE = 0
120 _SW_HIDE = 0
121
121
122 class _COORD(ctypes.Structure):
122 class _COORD(ctypes.Structure):
123 _fields_ = [(r'X', ctypes.c_short),
123 _fields_ = [(r'X', ctypes.c_short),
124 (r'Y', ctypes.c_short)]
124 (r'Y', ctypes.c_short)]
125
125
126 class _SMALL_RECT(ctypes.Structure):
126 class _SMALL_RECT(ctypes.Structure):
127 _fields_ = [(r'Left', ctypes.c_short),
127 _fields_ = [(r'Left', ctypes.c_short),
128 (r'Top', ctypes.c_short),
128 (r'Top', ctypes.c_short),
129 (r'Right', ctypes.c_short),
129 (r'Right', ctypes.c_short),
130 (r'Bottom', ctypes.c_short)]
130 (r'Bottom', ctypes.c_short)]
131
131
132 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
132 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
133 _fields_ = [(r'dwSize', _COORD),
133 _fields_ = [(r'dwSize', _COORD),
134 (r'dwCursorPosition', _COORD),
134 (r'dwCursorPosition', _COORD),
135 (r'wAttributes', _WORD),
135 (r'wAttributes', _WORD),
136 (r'srWindow', _SMALL_RECT),
136 (r'srWindow', _SMALL_RECT),
137 (r'dwMaximumWindowSize', _COORD)]
137 (r'dwMaximumWindowSize', _COORD)]
138
138
139 _STD_OUTPUT_HANDLE = _DWORD(-11).value
139 _STD_OUTPUT_HANDLE = _DWORD(-11).value
140 _STD_ERROR_HANDLE = _DWORD(-12).value
140 _STD_ERROR_HANDLE = _DWORD(-12).value
141
141
142 # CERT_TRUST_STATUS dwErrorStatus
142 # CERT_TRUST_STATUS dwErrorStatus
143 CERT_TRUST_IS_PARTIAL_CHAIN = 0x10000
143 CERT_TRUST_IS_PARTIAL_CHAIN = 0x10000
144
144
145 # CertCreateCertificateContext encodings
145 # CertCreateCertificateContext encodings
146 X509_ASN_ENCODING = 0x00000001
146 X509_ASN_ENCODING = 0x00000001
147 PKCS_7_ASN_ENCODING = 0x00010000
147 PKCS_7_ASN_ENCODING = 0x00010000
148
148
149 # These structs are only complete enough to achieve what we need.
149 # These structs are only complete enough to achieve what we need.
150 class CERT_CHAIN_CONTEXT(ctypes.Structure):
150 class CERT_CHAIN_CONTEXT(ctypes.Structure):
151 _fields_ = (
151 _fields_ = (
152 (r"cbSize", _DWORD),
152 (r"cbSize", _DWORD),
153
153
154 # CERT_TRUST_STATUS struct
154 # CERT_TRUST_STATUS struct
155 (r"dwErrorStatus", _DWORD),
155 (r"dwErrorStatus", _DWORD),
156 (r"dwInfoStatus", _DWORD),
156 (r"dwInfoStatus", _DWORD),
157
157
158 (r"cChain", _DWORD),
158 (r"cChain", _DWORD),
159 (r"rgpChain", ctypes.c_void_p),
159 (r"rgpChain", ctypes.c_void_p),
160 (r"cLowerQualityChainContext", _DWORD),
160 (r"cLowerQualityChainContext", _DWORD),
161 (r"rgpLowerQualityChainContext", ctypes.c_void_p),
161 (r"rgpLowerQualityChainContext", ctypes.c_void_p),
162 (r"fHasRevocationFreshnessTime", _BOOL),
162 (r"fHasRevocationFreshnessTime", _BOOL),
163 (r"dwRevocationFreshnessTime", _DWORD),
163 (r"dwRevocationFreshnessTime", _DWORD),
164 )
164 )
165
165
166 class CERT_USAGE_MATCH(ctypes.Structure):
166 class CERT_USAGE_MATCH(ctypes.Structure):
167 _fields_ = (
167 _fields_ = (
168 (r"dwType", _DWORD),
168 (r"dwType", _DWORD),
169
169
170 # CERT_ENHKEY_USAGE struct
170 # CERT_ENHKEY_USAGE struct
171 (r"cUsageIdentifier", _DWORD),
171 (r"cUsageIdentifier", _DWORD),
172 (r"rgpszUsageIdentifier", ctypes.c_void_p), # LPSTR *
172 (r"rgpszUsageIdentifier", ctypes.c_void_p), # LPSTR *
173 )
173 )
174
174
175 class CERT_CHAIN_PARA(ctypes.Structure):
175 class CERT_CHAIN_PARA(ctypes.Structure):
176 _fields_ = (
176 _fields_ = (
177 (r"cbSize", _DWORD),
177 (r"cbSize", _DWORD),
178 (r"RequestedUsage", CERT_USAGE_MATCH),
178 (r"RequestedUsage", CERT_USAGE_MATCH),
179 (r"RequestedIssuancePolicy", CERT_USAGE_MATCH),
179 (r"RequestedIssuancePolicy", CERT_USAGE_MATCH),
180 (r"dwUrlRetrievalTimeout", _DWORD),
180 (r"dwUrlRetrievalTimeout", _DWORD),
181 (r"fCheckRevocationFreshnessTime", _BOOL),
181 (r"fCheckRevocationFreshnessTime", _BOOL),
182 (r"dwRevocationFreshnessTime", _DWORD),
182 (r"dwRevocationFreshnessTime", _DWORD),
183 (r"pftCacheResync", ctypes.c_void_p), # LPFILETIME
183 (r"pftCacheResync", ctypes.c_void_p), # LPFILETIME
184 (r"pStrongSignPara", ctypes.c_void_p), # PCCERT_STRONG_SIGN_PARA
184 (r"pStrongSignPara", ctypes.c_void_p), # PCCERT_STRONG_SIGN_PARA
185 (r"dwStrongSignFlags", _DWORD),
185 (r"dwStrongSignFlags", _DWORD),
186 )
186 )
187
187
188 # types of parameters of C functions used (required by pypy)
188 # types of parameters of C functions used (required by pypy)
189
189
190 _crypt32.CertCreateCertificateContext.argtypes = [_DWORD, # cert encoding
190 _crypt32.CertCreateCertificateContext.argtypes = [_DWORD, # cert encoding
191 ctypes.c_char_p, # cert
191 ctypes.c_char_p, # cert
192 _DWORD] # cert size
192 _DWORD] # cert size
193 _crypt32.CertCreateCertificateContext.restype = _PCCERT_CONTEXT
193 _crypt32.CertCreateCertificateContext.restype = _PCCERT_CONTEXT
194
194
195 _crypt32.CertGetCertificateChain.argtypes = [
195 _crypt32.CertGetCertificateChain.argtypes = [
196 ctypes.c_void_p, # HCERTCHAINENGINE
196 ctypes.c_void_p, # HCERTCHAINENGINE
197 _PCCERT_CONTEXT,
197 _PCCERT_CONTEXT,
198 ctypes.c_void_p, # LPFILETIME
198 ctypes.c_void_p, # LPFILETIME
199 ctypes.c_void_p, # HCERTSTORE
199 ctypes.c_void_p, # HCERTSTORE
200 ctypes.c_void_p, # PCERT_CHAIN_PARA
200 ctypes.c_void_p, # PCERT_CHAIN_PARA
201 _DWORD,
201 _DWORD,
202 ctypes.c_void_p, # LPVOID
202 ctypes.c_void_p, # LPVOID
203 ctypes.c_void_p # PCCERT_CHAIN_CONTEXT *
203 ctypes.c_void_p # PCCERT_CHAIN_CONTEXT *
204 ]
204 ]
205 _crypt32.CertGetCertificateChain.restype = _BOOL
205 _crypt32.CertGetCertificateChain.restype = _BOOL
206
206
207 _crypt32.CertFreeCertificateContext.argtypes = [_PCCERT_CONTEXT]
207 _crypt32.CertFreeCertificateContext.argtypes = [_PCCERT_CONTEXT]
208 _crypt32.CertFreeCertificateContext.restype = _BOOL
208 _crypt32.CertFreeCertificateContext.restype = _BOOL
209
209
210 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
210 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
211 _DWORD, _DWORD, _HANDLE]
211 _DWORD, _DWORD, _HANDLE]
212 _kernel32.CreateFileA.restype = _HANDLE
212 _kernel32.CreateFileA.restype = _HANDLE
213
213
214 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
214 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
215 _kernel32.GetFileInformationByHandle.restype = _BOOL
215 _kernel32.GetFileInformationByHandle.restype = _BOOL
216
216
217 _kernel32.CloseHandle.argtypes = [_HANDLE]
217 _kernel32.CloseHandle.argtypes = [_HANDLE]
218 _kernel32.CloseHandle.restype = _BOOL
218 _kernel32.CloseHandle.restype = _BOOL
219
219
220 try:
220 try:
221 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
221 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
222 _kernel32.CreateHardLinkA.restype = _BOOL
222 _kernel32.CreateHardLinkA.restype = _BOOL
223 except AttributeError:
223 except AttributeError:
224 pass
224 pass
225
225
226 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
226 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
227 _kernel32.SetFileAttributesA.restype = _BOOL
227 _kernel32.SetFileAttributesA.restype = _BOOL
228
228
229 _DRIVE_UNKNOWN = 0
229 _DRIVE_UNKNOWN = 0
230 _DRIVE_NO_ROOT_DIR = 1
230 _DRIVE_NO_ROOT_DIR = 1
231 _DRIVE_REMOVABLE = 2
231 _DRIVE_REMOVABLE = 2
232 _DRIVE_FIXED = 3
232 _DRIVE_FIXED = 3
233 _DRIVE_REMOTE = 4
233 _DRIVE_REMOTE = 4
234 _DRIVE_CDROM = 5
234 _DRIVE_CDROM = 5
235 _DRIVE_RAMDISK = 6
235 _DRIVE_RAMDISK = 6
236
236
237 _kernel32.GetDriveTypeA.argtypes = [_LPCSTR]
237 _kernel32.GetDriveTypeA.argtypes = [_LPCSTR]
238 _kernel32.GetDriveTypeA.restype = _UINT
238 _kernel32.GetDriveTypeA.restype = _UINT
239
239
240 _kernel32.GetVolumeInformationA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD,
240 _kernel32.GetVolumeInformationA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD,
241 ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, _DWORD]
241 ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, _DWORD]
242 _kernel32.GetVolumeInformationA.restype = _BOOL
242 _kernel32.GetVolumeInformationA.restype = _BOOL
243
243
244 _kernel32.GetVolumePathNameA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD]
244 _kernel32.GetVolumePathNameA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD]
245 _kernel32.GetVolumePathNameA.restype = _BOOL
245 _kernel32.GetVolumePathNameA.restype = _BOOL
246
246
247 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
247 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
248 _kernel32.OpenProcess.restype = _HANDLE
248 _kernel32.OpenProcess.restype = _HANDLE
249
249
250 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
250 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
251 _kernel32.GetExitCodeProcess.restype = _BOOL
251 _kernel32.GetExitCodeProcess.restype = _BOOL
252
252
253 _kernel32.GetLastError.argtypes = []
253 _kernel32.GetLastError.argtypes = []
254 _kernel32.GetLastError.restype = _DWORD
254 _kernel32.GetLastError.restype = _DWORD
255
255
256 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
256 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
257 _kernel32.GetModuleFileNameA.restype = _DWORD
257 _kernel32.GetModuleFileNameA.restype = _DWORD
258
258
259 _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
259 _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
260 ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
260 ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
261 ctypes.c_void_p]
261 ctypes.c_void_p]
262 _kernel32.CreateProcessA.restype = _BOOL
262 _kernel32.CreateProcessA.restype = _BOOL
263
263
264 _kernel32.ExitProcess.argtypes = [_UINT]
264 _kernel32.ExitProcess.argtypes = [_UINT]
265 _kernel32.ExitProcess.restype = None
265 _kernel32.ExitProcess.restype = None
266
266
267 _kernel32.GetCurrentProcessId.argtypes = []
267 _kernel32.GetCurrentProcessId.argtypes = []
268 _kernel32.GetCurrentProcessId.restype = _DWORD
268 _kernel32.GetCurrentProcessId.restype = _DWORD
269
269
270 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
270 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
271 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
271 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
272 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
272 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
273
273
274 _kernel32.SetConsoleMode.argtypes = [_HANDLE, _DWORD]
274 _kernel32.SetConsoleMode.argtypes = [_HANDLE, _DWORD]
275 _kernel32.SetConsoleMode.restype = _BOOL
275 _kernel32.SetConsoleMode.restype = _BOOL
276
276
277 _kernel32.GetConsoleMode.argtypes = [_HANDLE, ctypes.c_void_p]
277 _kernel32.GetConsoleMode.argtypes = [_HANDLE, ctypes.c_void_p]
278 _kernel32.GetConsoleMode.restype = _BOOL
278 _kernel32.GetConsoleMode.restype = _BOOL
279
279
280 _kernel32.GetStdHandle.argtypes = [_DWORD]
280 _kernel32.GetStdHandle.argtypes = [_DWORD]
281 _kernel32.GetStdHandle.restype = _HANDLE
281 _kernel32.GetStdHandle.restype = _HANDLE
282
282
283 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
283 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
284 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
284 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
285
285
286 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
286 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
287 _advapi32.GetUserNameA.restype = _BOOL
287 _advapi32.GetUserNameA.restype = _BOOL
288
288
289 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
289 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
290 _user32.GetWindowThreadProcessId.restype = _DWORD
290 _user32.GetWindowThreadProcessId.restype = _DWORD
291
291
292 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
292 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
293 _user32.ShowWindow.restype = _BOOL
293 _user32.ShowWindow.restype = _BOOL
294
294
295 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
295 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
296 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
296 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
297 _user32.EnumWindows.restype = _BOOL
297 _user32.EnumWindows.restype = _BOOL
298
298
299 _kernel32.PeekNamedPipe.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD,
299 _kernel32.PeekNamedPipe.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD,
300 ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
300 ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
301 _kernel32.PeekNamedPipe.restype = _BOOL
301 _kernel32.PeekNamedPipe.restype = _BOOL
302
302
303 def _raiseoserror(name):
303 def _raiseoserror(name):
304 # Force the code to a signed int to avoid an 'int too large' error.
304 # Force the code to a signed int to avoid an 'int too large' error.
305 # See https://bugs.python.org/issue28474
305 # See https://bugs.python.org/issue28474
306 code = _kernel32.GetLastError()
306 code = _kernel32.GetLastError()
307 if code > 0x7fffffff:
307 if code > 0x7fffffff:
308 code -= 2**32
308 code -= 2**32
309 err = ctypes.WinError(code=code)
309 err = ctypes.WinError(code=code)
310 raise OSError(err.errno, '%s: %s' % (name,
310 raise OSError(err.errno, '%s: %s' % (name,
311 encoding.strtolocal(err.strerror)))
311 encoding.strtolocal(err.strerror)))
312
312
313 def _getfileinfo(name):
313 def _getfileinfo(name):
314 fh = _kernel32.CreateFileA(name, 0,
314 fh = _kernel32.CreateFileA(name, 0,
315 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
315 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
316 None, _OPEN_EXISTING, _FILE_FLAG_BACKUP_SEMANTICS, None)
316 None, _OPEN_EXISTING, _FILE_FLAG_BACKUP_SEMANTICS, None)
317 if fh == _INVALID_HANDLE_VALUE:
317 if fh == _INVALID_HANDLE_VALUE:
318 _raiseoserror(name)
318 _raiseoserror(name)
319 try:
319 try:
320 fi = _BY_HANDLE_FILE_INFORMATION()
320 fi = _BY_HANDLE_FILE_INFORMATION()
321 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
321 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
322 _raiseoserror(name)
322 _raiseoserror(name)
323 return fi
323 return fi
324 finally:
324 finally:
325 _kernel32.CloseHandle(fh)
325 _kernel32.CloseHandle(fh)
326
326
327 def checkcertificatechain(cert, build=True):
327 def checkcertificatechain(cert, build=True):
328 '''Tests the given certificate to see if there is a complete chain to a
328 '''Tests the given certificate to see if there is a complete chain to a
329 trusted root certificate. As a side effect, missing certificates are
329 trusted root certificate. As a side effect, missing certificates are
330 downloaded and installed unless ``build=False``. True is returned if a
330 downloaded and installed unless ``build=False``. True is returned if a
331 chain to a trusted root exists (even if built on the fly), otherwise
331 chain to a trusted root exists (even if built on the fly), otherwise
332 False. NB: A chain to a trusted root does NOT imply that the certificate
332 False. NB: A chain to a trusted root does NOT imply that the certificate
333 is valid.
333 is valid.
334 '''
334 '''
335
335
336 chainctxptr = ctypes.POINTER(CERT_CHAIN_CONTEXT)
336 chainctxptr = ctypes.POINTER(CERT_CHAIN_CONTEXT)
337
337
338 pchainctx = chainctxptr()
338 pchainctx = chainctxptr()
339 chainpara = CERT_CHAIN_PARA(cbSize=ctypes.sizeof(CERT_CHAIN_PARA),
339 chainpara = CERT_CHAIN_PARA(cbSize=ctypes.sizeof(CERT_CHAIN_PARA),
340 RequestedUsage=CERT_USAGE_MATCH())
340 RequestedUsage=CERT_USAGE_MATCH())
341
341
342 certctx = _crypt32.CertCreateCertificateContext(X509_ASN_ENCODING, cert,
342 certctx = _crypt32.CertCreateCertificateContext(X509_ASN_ENCODING, cert,
343 len(cert))
343 len(cert))
344 if certctx is None:
344 if certctx is None:
345 _raiseoserror('CertCreateCertificateContext')
345 _raiseoserror('CertCreateCertificateContext')
346
346
347 flags = 0
347 flags = 0
348
348
349 if not build:
349 if not build:
350 flags |= 0x100 # CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE
350 flags |= 0x100 # CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE
351
351
352 try:
352 try:
353 # Building the certificate chain will update root certs as necessary.
353 # Building the certificate chain will update root certs as necessary.
354 if not _crypt32.CertGetCertificateChain(None, # hChainEngine
354 if not _crypt32.CertGetCertificateChain(None, # hChainEngine
355 certctx, # pCertContext
355 certctx, # pCertContext
356 None, # pTime
356 None, # pTime
357 None, # hAdditionalStore
357 None, # hAdditionalStore
358 ctypes.byref(chainpara),
358 ctypes.byref(chainpara),
359 flags,
359 flags,
360 None, # pvReserved
360 None, # pvReserved
361 ctypes.byref(pchainctx)):
361 ctypes.byref(pchainctx)):
362 _raiseoserror('CertGetCertificateChain')
362 _raiseoserror('CertGetCertificateChain')
363
363
364 chainctx = pchainctx.contents
364 chainctx = pchainctx.contents
365
365
366 return chainctx.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN == 0
366 return chainctx.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN == 0
367 finally:
367 finally:
368 if pchainctx:
368 if pchainctx:
369 _crypt32.CertFreeCertificateChain(pchainctx)
369 _crypt32.CertFreeCertificateChain(pchainctx)
370 _crypt32.CertFreeCertificateContext(certctx)
370 _crypt32.CertFreeCertificateContext(certctx)
371
371
372 def oslink(src, dst):
372 def oslink(src, dst):
373 try:
373 try:
374 if not _kernel32.CreateHardLinkA(dst, src, None):
374 if not _kernel32.CreateHardLinkA(dst, src, None):
375 _raiseoserror(src)
375 _raiseoserror(src)
376 except AttributeError: # Wine doesn't support this function
376 except AttributeError: # Wine doesn't support this function
377 _raiseoserror(src)
377 _raiseoserror(src)
378
378
379 def nlinks(name):
379 def nlinks(name):
380 '''return number of hardlinks for the given file'''
380 '''return number of hardlinks for the given file'''
381 return _getfileinfo(name).nNumberOfLinks
381 return _getfileinfo(name).nNumberOfLinks
382
382
383 def samefile(path1, path2):
383 def samefile(path1, path2):
384 '''Returns whether path1 and path2 refer to the same file or directory.'''
384 '''Returns whether path1 and path2 refer to the same file or directory.'''
385 res1 = _getfileinfo(path1)
385 res1 = _getfileinfo(path1)
386 res2 = _getfileinfo(path2)
386 res2 = _getfileinfo(path2)
387 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
387 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
388 and res1.nFileIndexHigh == res2.nFileIndexHigh
388 and res1.nFileIndexHigh == res2.nFileIndexHigh
389 and res1.nFileIndexLow == res2.nFileIndexLow)
389 and res1.nFileIndexLow == res2.nFileIndexLow)
390
390
391 def samedevice(path1, path2):
391 def samedevice(path1, path2):
392 '''Returns whether path1 and path2 are on the same device.'''
392 '''Returns whether path1 and path2 are on the same device.'''
393 res1 = _getfileinfo(path1)
393 res1 = _getfileinfo(path1)
394 res2 = _getfileinfo(path2)
394 res2 = _getfileinfo(path2)
395 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
395 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
396
396
397 def peekpipe(pipe):
397 def peekpipe(pipe):
398 handle = msvcrt.get_osfhandle(pipe.fileno())
398 handle = msvcrt.get_osfhandle(pipe.fileno())
399 avail = _DWORD()
399 avail = _DWORD()
400
400
401 if not _kernel32.PeekNamedPipe(handle, None, 0, None, ctypes.byref(avail),
401 if not _kernel32.PeekNamedPipe(handle, None, 0, None, ctypes.byref(avail),
402 None):
402 None):
403 err = _kernel32.GetLastError()
403 err = _kernel32.GetLastError()
404 if err == _ERROR_BROKEN_PIPE:
404 if err == _ERROR_BROKEN_PIPE:
405 return 0
405 return 0
406 raise ctypes.WinError(err)
406 raise ctypes.WinError(err)
407
407
408 return avail.value
408 return avail.value
409
409
410 def lasterrorwaspipeerror(err):
410 def lasterrorwaspipeerror(err):
411 if err.errno != errno.EINVAL:
411 if err.errno != errno.EINVAL:
412 return False
412 return False
413 err = _kernel32.GetLastError()
413 err = _kernel32.GetLastError()
414 return err == _ERROR_BROKEN_PIPE or err == _ERROR_NO_DATA
414 return err == _ERROR_BROKEN_PIPE or err == _ERROR_NO_DATA
415
415
416 def testpid(pid):
416 def testpid(pid):
417 '''return True if pid is still running or unable to
417 '''return True if pid is still running or unable to
418 determine, False otherwise'''
418 determine, False otherwise'''
419 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
419 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
420 if h:
420 if h:
421 try:
421 try:
422 status = _DWORD()
422 status = _DWORD()
423 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
423 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
424 return status.value == _STILL_ACTIVE
424 return status.value == _STILL_ACTIVE
425 finally:
425 finally:
426 _kernel32.CloseHandle(h)
426 _kernel32.CloseHandle(h)
427 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
427 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
428
428
429 def executablepath():
429 def executablepath():
430 '''return full path of hg.exe'''
430 '''return full path of hg.exe'''
431 size = 600
431 size = 600
432 buf = ctypes.create_string_buffer(size + 1)
432 buf = ctypes.create_string_buffer(size + 1)
433 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
433 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
434 if len == 0:
434 if len == 0:
435 raise ctypes.WinError() # Note: WinError is a function
435 raise ctypes.WinError() # Note: WinError is a function
436 elif len == size:
436 elif len == size:
437 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
437 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
438 return buf.value
438 return buf.value
439
439
440 def getvolumename(path):
440 def getvolumename(path):
441 """Get the mount point of the filesystem from a directory or file
441 """Get the mount point of the filesystem from a directory or file
442 (best-effort)
442 (best-effort)
443
443
444 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
444 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
445 """
445 """
446 # realpath() calls GetFullPathName()
446 # realpath() calls GetFullPathName()
447 realpath = os.path.realpath(path)
447 realpath = os.path.realpath(path)
448
448
449 # allocate at least MAX_PATH long since GetVolumePathName('c:\\', buf, 4)
449 # allocate at least MAX_PATH long since GetVolumePathName('c:\\', buf, 4)
450 # somehow fails on Windows XP
450 # somehow fails on Windows XP
451 size = max(len(realpath), _MAX_PATH) + 1
451 size = max(len(realpath), _MAX_PATH) + 1
452 buf = ctypes.create_string_buffer(size)
452 buf = ctypes.create_string_buffer(size)
453
453
454 if not _kernel32.GetVolumePathNameA(realpath, ctypes.byref(buf), size):
454 if not _kernel32.GetVolumePathNameA(realpath, ctypes.byref(buf), size):
455 raise ctypes.WinError() # Note: WinError is a function
455 raise ctypes.WinError() # Note: WinError is a function
456
456
457 return buf.value
457 return buf.value
458
458
459 def getfstype(path):
459 def getfstype(path):
460 """Get the filesystem type name from a directory or file (best-effort)
460 """Get the filesystem type name from a directory or file (best-effort)
461
461
462 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
462 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
463 """
463 """
464 volume = getvolumename(path)
464 volume = getvolumename(path)
465
465
466 t = _kernel32.GetDriveTypeA(volume)
466 t = _kernel32.GetDriveTypeA(volume)
467
467
468 if t == _DRIVE_REMOTE:
468 if t == _DRIVE_REMOTE:
469 return 'cifs'
469 return 'cifs'
470 elif t not in (_DRIVE_REMOVABLE, _DRIVE_FIXED, _DRIVE_CDROM,
470 elif t not in (_DRIVE_REMOVABLE, _DRIVE_FIXED, _DRIVE_CDROM,
471 _DRIVE_RAMDISK):
471 _DRIVE_RAMDISK):
472 return None
472 return None
473
473
474 size = _MAX_PATH + 1
474 size = _MAX_PATH + 1
475 name = ctypes.create_string_buffer(size)
475 name = ctypes.create_string_buffer(size)
476
476
477 if not _kernel32.GetVolumeInformationA(volume, None, 0, None, None, None,
477 if not _kernel32.GetVolumeInformationA(volume, None, 0, None, None, None,
478 ctypes.byref(name), size):
478 ctypes.byref(name), size):
479 raise ctypes.WinError() # Note: WinError is a function
479 raise ctypes.WinError() # Note: WinError is a function
480
480
481 return name.value
481 return name.value
482
482
483 def getuser():
483 def getuser():
484 '''return name of current user'''
484 '''return name of current user'''
485 size = _DWORD(300)
485 size = _DWORD(300)
486 buf = ctypes.create_string_buffer(size.value + 1)
486 buf = ctypes.create_string_buffer(size.value + 1)
487 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
487 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
488 raise ctypes.WinError()
488 raise ctypes.WinError()
489 return buf.value
489 return buf.value
490
490
491 _signalhandler = []
491 _signalhandler = []
492
492
493 def setsignalhandler():
493 def setsignalhandler():
494 '''Register a termination handler for console events including
494 '''Register a termination handler for console events including
495 CTRL+C. python signal handlers do not work well with socket
495 CTRL+C. python signal handlers do not work well with socket
496 operations.
496 operations.
497 '''
497 '''
498 def handler(event):
498 def handler(event):
499 _kernel32.ExitProcess(1)
499 _kernel32.ExitProcess(1)
500
500
501 if _signalhandler:
501 if _signalhandler:
502 return # already registered
502 return # already registered
503 h = _SIGNAL_HANDLER(handler)
503 h = _SIGNAL_HANDLER(handler)
504 _signalhandler.append(h) # needed to prevent garbage collection
504 _signalhandler.append(h) # needed to prevent garbage collection
505 if not _kernel32.SetConsoleCtrlHandler(h, True):
505 if not _kernel32.SetConsoleCtrlHandler(h, True):
506 raise ctypes.WinError()
506 raise ctypes.WinError()
507
507
508 def hidewindow():
508 def hidewindow():
509
509
510 def callback(hwnd, pid):
510 def callback(hwnd, pid):
511 wpid = _DWORD()
511 wpid = _DWORD()
512 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
512 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
513 if pid == wpid.value:
513 if pid == wpid.value:
514 _user32.ShowWindow(hwnd, _SW_HIDE)
514 _user32.ShowWindow(hwnd, _SW_HIDE)
515 return False # stop enumerating windows
515 return False # stop enumerating windows
516 return True
516 return True
517
517
518 pid = _kernel32.GetCurrentProcessId()
518 pid = _kernel32.GetCurrentProcessId()
519 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
519 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
520
520
521 def termsize():
521 def termsize():
522 # cmd.exe does not handle CR like a unix console, the CR is
522 # cmd.exe does not handle CR like a unix console, the CR is
523 # counted in the line length. On 80 columns consoles, if 80
523 # counted in the line length. On 80 columns consoles, if 80
524 # characters are written, the following CR won't apply on the
524 # characters are written, the following CR won't apply on the
525 # current line but on the new one. Keep room for it.
525 # current line but on the new one. Keep room for it.
526 width = 80 - 1
526 width = 80 - 1
527 height = 25
527 height = 25
528 # Query stderr to avoid problems with redirections
528 # Query stderr to avoid problems with redirections
529 screenbuf = _kernel32.GetStdHandle(
529 screenbuf = _kernel32.GetStdHandle(
530 _STD_ERROR_HANDLE) # don't close the handle returned
530 _STD_ERROR_HANDLE) # don't close the handle returned
531 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
531 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
532 return width, height
532 return width, height
533 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
533 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
534 if not _kernel32.GetConsoleScreenBufferInfo(
534 if not _kernel32.GetConsoleScreenBufferInfo(
535 screenbuf, ctypes.byref(csbi)):
535 screenbuf, ctypes.byref(csbi)):
536 return width, height
536 return width, height
537 width = csbi.srWindow.Right - csbi.srWindow.Left # don't '+ 1'
537 width = csbi.srWindow.Right - csbi.srWindow.Left # don't '+ 1'
538 height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1
538 height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1
539 return width, height
539 return width, height
540
540
541 def enablevtmode():
541 def enablevtmode():
542 '''Enable virtual terminal mode for the associated console. Return True if
542 '''Enable virtual terminal mode for the associated console. Return True if
543 enabled, else False.'''
543 enabled, else False.'''
544
544
545 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
545 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
546
546
547 handle = _kernel32.GetStdHandle(_STD_OUTPUT_HANDLE) # don't close the handle
547 handle = _kernel32.GetStdHandle(_STD_OUTPUT_HANDLE) # don't close the handle
548 if handle == _INVALID_HANDLE_VALUE:
548 if handle == _INVALID_HANDLE_VALUE:
549 return False
549 return False
550
550
551 mode = _DWORD(0)
551 mode = _DWORD(0)
552
552
553 if not _kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
553 if not _kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
554 return False
554 return False
555
555
556 if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
556 if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
557 mode.value |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
557 mode.value |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
558
558
559 if not _kernel32.SetConsoleMode(handle, mode):
559 if not _kernel32.SetConsoleMode(handle, mode):
560 return False
560 return False
561
561
562 return True
562 return True
563
563
564 def spawndetached(args):
564 def spawndetached(args):
565 # No standard library function really spawns a fully detached
565 # No standard library function really spawns a fully detached
566 # process under win32 because they allocate pipes or other objects
566 # process under win32 because they allocate pipes or other objects
567 # to handle standard streams communications. Passing these objects
567 # to handle standard streams communications. Passing these objects
568 # to the child process requires handle inheritance to be enabled
568 # to the child process requires handle inheritance to be enabled
569 # which makes really detached processes impossible.
569 # which makes really detached processes impossible.
570 si = _STARTUPINFO()
570 si = _STARTUPINFO()
571 si.cb = ctypes.sizeof(_STARTUPINFO)
571 si.cb = ctypes.sizeof(_STARTUPINFO)
572
572
573 pi = _PROCESS_INFORMATION()
573 pi = _PROCESS_INFORMATION()
574
574
575 env = ''
575 env = ''
576 for k in encoding.environ:
576 for k in encoding.environ:
577 env += "%s=%s\0" % (k, encoding.environ[k])
577 env += "%s=%s\0" % (k, encoding.environ[k])
578 if not env:
578 if not env:
579 env = '\0'
579 env = '\0'
580 env += '\0'
580 env += '\0'
581
581
582 args = subprocess.list2cmdline(args)
582 args = subprocess.list2cmdline(pycompat.rapply(encoding.strfromlocal, args))
583
583
584 # TODO: CreateProcessW on py3?
584 res = _kernel32.CreateProcessA(
585 res = _kernel32.CreateProcessA(
585 None, args, None, None, False, _CREATE_NO_WINDOW,
586 None, encoding.strtolocal(args), None, None, False, _CREATE_NO_WINDOW,
586 env, pycompat.getcwd(), ctypes.byref(si), ctypes.byref(pi))
587 env, pycompat.getcwd(), ctypes.byref(si), ctypes.byref(pi))
587 if not res:
588 if not res:
588 raise ctypes.WinError()
589 raise ctypes.WinError()
589
590
590 return pi.dwProcessId
591 return pi.dwProcessId
591
592
592 def unlink(f):
593 def unlink(f):
593 '''try to implement POSIX' unlink semantics on Windows'''
594 '''try to implement POSIX' unlink semantics on Windows'''
594
595
595 if os.path.isdir(f):
596 if os.path.isdir(f):
596 # use EPERM because it is POSIX prescribed value, even though
597 # use EPERM because it is POSIX prescribed value, even though
597 # unlink(2) on directories returns EISDIR on Linux
598 # unlink(2) on directories returns EISDIR on Linux
598 raise IOError(errno.EPERM,
599 raise IOError(errno.EPERM,
599 "Unlinking directory not permitted: '%s'" % f)
600 "Unlinking directory not permitted: '%s'" % f)
600
601
601 # POSIX allows to unlink and rename open files. Windows has serious
602 # POSIX allows to unlink and rename open files. Windows has serious
602 # problems with doing that:
603 # problems with doing that:
603 # - Calling os.unlink (or os.rename) on a file f fails if f or any
604 # - Calling os.unlink (or os.rename) on a file f fails if f or any
604 # hardlinked copy of f has been opened with Python's open(). There is no
605 # hardlinked copy of f has been opened with Python's open(). There is no
605 # way such a file can be deleted or renamed on Windows (other than
606 # way such a file can be deleted or renamed on Windows (other than
606 # scheduling the delete or rename for the next reboot).
607 # scheduling the delete or rename for the next reboot).
607 # - Calling os.unlink on a file that has been opened with Mercurial's
608 # - Calling os.unlink on a file that has been opened with Mercurial's
608 # posixfile (or comparable methods) will delay the actual deletion of
609 # posixfile (or comparable methods) will delay the actual deletion of
609 # the file for as long as the file is held open. The filename is blocked
610 # the file for as long as the file is held open. The filename is blocked
610 # during that time and cannot be used for recreating a new file under
611 # during that time and cannot be used for recreating a new file under
611 # that same name ("zombie file"). Directories containing such zombie files
612 # that same name ("zombie file"). Directories containing such zombie files
612 # cannot be removed or moved.
613 # cannot be removed or moved.
613 # A file that has been opened with posixfile can be renamed, so we rename
614 # A file that has been opened with posixfile can be renamed, so we rename
614 # f to a random temporary name before calling os.unlink on it. This allows
615 # f to a random temporary name before calling os.unlink on it. This allows
615 # callers to recreate f immediately while having other readers do their
616 # callers to recreate f immediately while having other readers do their
616 # implicit zombie filename blocking on a temporary name.
617 # implicit zombie filename blocking on a temporary name.
617
618
618 for tries in pycompat.xrange(10):
619 for tries in pycompat.xrange(10):
619 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
620 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
620 try:
621 try:
621 os.rename(f, temp) # raises OSError EEXIST if temp exists
622 os.rename(f, temp) # raises OSError EEXIST if temp exists
622 break
623 break
623 except OSError as e:
624 except OSError as e:
624 if e.errno != errno.EEXIST:
625 if e.errno != errno.EEXIST:
625 raise
626 raise
626 else:
627 else:
627 raise IOError(errno.EEXIST, "No usable temporary filename found")
628 raise IOError(errno.EEXIST, "No usable temporary filename found")
628
629
629 try:
630 try:
630 os.unlink(temp)
631 os.unlink(temp)
631 except OSError:
632 except OSError:
632 # The unlink might have failed because the READONLY attribute may heave
633 # The unlink might have failed because the READONLY attribute may heave
633 # been set on the original file. Rename works fine with READONLY set,
634 # been set on the original file. Rename works fine with READONLY set,
634 # but not os.unlink. Reset all attributes and try again.
635 # but not os.unlink. Reset all attributes and try again.
635 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
636 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
636 try:
637 try:
637 os.unlink(temp)
638 os.unlink(temp)
638 except OSError:
639 except OSError:
639 # The unlink might have failed due to some very rude AV-Scanners.
640 # The unlink might have failed due to some very rude AV-Scanners.
640 # Leaking a tempfile is the lesser evil than aborting here and
641 # Leaking a tempfile is the lesser evil than aborting here and
641 # leaving some potentially serious inconsistencies.
642 # leaving some potentially serious inconsistencies.
642 pass
643 pass
643
644
644 def makedir(path, notindexed):
645 def makedir(path, notindexed):
645 os.mkdir(path)
646 os.mkdir(path)
646 if notindexed:
647 if notindexed:
647 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
648 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
@@ -1,594 +1,594 b''
1 # windows.py - Windows utility function implementations for Mercurial
1 # windows.py - Windows utility function implementations for Mercurial
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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import msvcrt
11 import msvcrt
12 import os
12 import os
13 import re
13 import re
14 import stat
14 import stat
15 import string
15 import string
16 import sys
16 import sys
17
17
18 from .i18n import _
18 from .i18n import _
19 from . import (
19 from . import (
20 encoding,
20 encoding,
21 error,
21 error,
22 policy,
22 policy,
23 pycompat,
23 pycompat,
24 win32,
24 win32,
25 )
25 )
26
26
27 try:
27 try:
28 import _winreg as winreg
28 import _winreg as winreg
29 winreg.CloseKey
29 winreg.CloseKey
30 except ImportError:
30 except ImportError:
31 import winreg
31 import winreg
32
32
33 osutil = policy.importmod(r'osutil')
33 osutil = policy.importmod(r'osutil')
34
34
35 getfsmountpoint = win32.getvolumename
35 getfsmountpoint = win32.getvolumename
36 getfstype = win32.getfstype
36 getfstype = win32.getfstype
37 getuser = win32.getuser
37 getuser = win32.getuser
38 hidewindow = win32.hidewindow
38 hidewindow = win32.hidewindow
39 makedir = win32.makedir
39 makedir = win32.makedir
40 nlinks = win32.nlinks
40 nlinks = win32.nlinks
41 oslink = win32.oslink
41 oslink = win32.oslink
42 samedevice = win32.samedevice
42 samedevice = win32.samedevice
43 samefile = win32.samefile
43 samefile = win32.samefile
44 setsignalhandler = win32.setsignalhandler
44 setsignalhandler = win32.setsignalhandler
45 spawndetached = win32.spawndetached
45 spawndetached = win32.spawndetached
46 split = os.path.split
46 split = os.path.split
47 testpid = win32.testpid
47 testpid = win32.testpid
48 unlink = win32.unlink
48 unlink = win32.unlink
49
49
50 umask = 0o022
50 umask = 0o022
51
51
52 class mixedfilemodewrapper(object):
52 class mixedfilemodewrapper(object):
53 """Wraps a file handle when it is opened in read/write mode.
53 """Wraps a file handle when it is opened in read/write mode.
54
54
55 fopen() and fdopen() on Windows have a specific-to-Windows requirement
55 fopen() and fdopen() on Windows have a specific-to-Windows requirement
56 that files opened with mode r+, w+, or a+ make a call to a file positioning
56 that files opened with mode r+, w+, or a+ make a call to a file positioning
57 function when switching between reads and writes. Without this extra call,
57 function when switching between reads and writes. Without this extra call,
58 Python will raise a not very intuitive "IOError: [Errno 0] Error."
58 Python will raise a not very intuitive "IOError: [Errno 0] Error."
59
59
60 This class wraps posixfile instances when the file is opened in read/write
60 This class wraps posixfile instances when the file is opened in read/write
61 mode and automatically adds checks or inserts appropriate file positioning
61 mode and automatically adds checks or inserts appropriate file positioning
62 calls when necessary.
62 calls when necessary.
63 """
63 """
64 OPNONE = 0
64 OPNONE = 0
65 OPREAD = 1
65 OPREAD = 1
66 OPWRITE = 2
66 OPWRITE = 2
67
67
68 def __init__(self, fp):
68 def __init__(self, fp):
69 object.__setattr__(self, r'_fp', fp)
69 object.__setattr__(self, r'_fp', fp)
70 object.__setattr__(self, r'_lastop', 0)
70 object.__setattr__(self, r'_lastop', 0)
71
71
72 def __enter__(self):
72 def __enter__(self):
73 return self._fp.__enter__()
73 return self._fp.__enter__()
74
74
75 def __exit__(self, exc_type, exc_val, exc_tb):
75 def __exit__(self, exc_type, exc_val, exc_tb):
76 self._fp.__exit__(exc_type, exc_val, exc_tb)
76 self._fp.__exit__(exc_type, exc_val, exc_tb)
77
77
78 def __getattr__(self, name):
78 def __getattr__(self, name):
79 return getattr(self._fp, name)
79 return getattr(self._fp, name)
80
80
81 def __setattr__(self, name, value):
81 def __setattr__(self, name, value):
82 return self._fp.__setattr__(name, value)
82 return self._fp.__setattr__(name, value)
83
83
84 def _noopseek(self):
84 def _noopseek(self):
85 self._fp.seek(0, os.SEEK_CUR)
85 self._fp.seek(0, os.SEEK_CUR)
86
86
87 def seek(self, *args, **kwargs):
87 def seek(self, *args, **kwargs):
88 object.__setattr__(self, r'_lastop', self.OPNONE)
88 object.__setattr__(self, r'_lastop', self.OPNONE)
89 return self._fp.seek(*args, **kwargs)
89 return self._fp.seek(*args, **kwargs)
90
90
91 def write(self, d):
91 def write(self, d):
92 if self._lastop == self.OPREAD:
92 if self._lastop == self.OPREAD:
93 self._noopseek()
93 self._noopseek()
94
94
95 object.__setattr__(self, r'_lastop', self.OPWRITE)
95 object.__setattr__(self, r'_lastop', self.OPWRITE)
96 return self._fp.write(d)
96 return self._fp.write(d)
97
97
98 def writelines(self, *args, **kwargs):
98 def writelines(self, *args, **kwargs):
99 if self._lastop == self.OPREAD:
99 if self._lastop == self.OPREAD:
100 self._noopeseek()
100 self._noopeseek()
101
101
102 object.__setattr__(self, r'_lastop', self.OPWRITE)
102 object.__setattr__(self, r'_lastop', self.OPWRITE)
103 return self._fp.writelines(*args, **kwargs)
103 return self._fp.writelines(*args, **kwargs)
104
104
105 def read(self, *args, **kwargs):
105 def read(self, *args, **kwargs):
106 if self._lastop == self.OPWRITE:
106 if self._lastop == self.OPWRITE:
107 self._noopseek()
107 self._noopseek()
108
108
109 object.__setattr__(self, r'_lastop', self.OPREAD)
109 object.__setattr__(self, r'_lastop', self.OPREAD)
110 return self._fp.read(*args, **kwargs)
110 return self._fp.read(*args, **kwargs)
111
111
112 def readline(self, *args, **kwargs):
112 def readline(self, *args, **kwargs):
113 if self._lastop == self.OPWRITE:
113 if self._lastop == self.OPWRITE:
114 self._noopseek()
114 self._noopseek()
115
115
116 object.__setattr__(self, r'_lastop', self.OPREAD)
116 object.__setattr__(self, r'_lastop', self.OPREAD)
117 return self._fp.readline(*args, **kwargs)
117 return self._fp.readline(*args, **kwargs)
118
118
119 def readlines(self, *args, **kwargs):
119 def readlines(self, *args, **kwargs):
120 if self._lastop == self.OPWRITE:
120 if self._lastop == self.OPWRITE:
121 self._noopseek()
121 self._noopseek()
122
122
123 object.__setattr__(self, r'_lastop', self.OPREAD)
123 object.__setattr__(self, r'_lastop', self.OPREAD)
124 return self._fp.readlines(*args, **kwargs)
124 return self._fp.readlines(*args, **kwargs)
125
125
126 def posixfile(name, mode='r', buffering=-1):
126 def posixfile(name, mode='r', buffering=-1):
127 '''Open a file with even more POSIX-like semantics'''
127 '''Open a file with even more POSIX-like semantics'''
128 try:
128 try:
129 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
129 fp = osutil.posixfile(name, mode, buffering) # may raise WindowsError
130
130
131 # The position when opening in append mode is implementation defined, so
131 # The position when opening in append mode is implementation defined, so
132 # make it consistent with other platforms, which position at EOF.
132 # make it consistent with other platforms, which position at EOF.
133 if 'a' in mode:
133 if 'a' in mode:
134 fp.seek(0, os.SEEK_END)
134 fp.seek(0, os.SEEK_END)
135
135
136 if '+' in mode:
136 if '+' in mode:
137 return mixedfilemodewrapper(fp)
137 return mixedfilemodewrapper(fp)
138
138
139 return fp
139 return fp
140 except WindowsError as err:
140 except WindowsError as err:
141 # convert to a friendlier exception
141 # convert to a friendlier exception
142 raise IOError(err.errno, '%s: %s' % (
142 raise IOError(err.errno, '%s: %s' % (
143 name, encoding.strtolocal(err.strerror)))
143 name, encoding.strtolocal(err.strerror)))
144
144
145 # may be wrapped by win32mbcs extension
145 # may be wrapped by win32mbcs extension
146 listdir = osutil.listdir
146 listdir = osutil.listdir
147
147
148 class winstdout(object):
148 class winstdout(object):
149 '''stdout on windows misbehaves if sent through a pipe'''
149 '''stdout on windows misbehaves if sent through a pipe'''
150
150
151 def __init__(self, fp):
151 def __init__(self, fp):
152 self.fp = fp
152 self.fp = fp
153
153
154 def __getattr__(self, key):
154 def __getattr__(self, key):
155 return getattr(self.fp, key)
155 return getattr(self.fp, key)
156
156
157 def close(self):
157 def close(self):
158 try:
158 try:
159 self.fp.close()
159 self.fp.close()
160 except IOError:
160 except IOError:
161 pass
161 pass
162
162
163 def write(self, s):
163 def write(self, s):
164 try:
164 try:
165 # This is workaround for "Not enough space" error on
165 # This is workaround for "Not enough space" error on
166 # writing large size of data to console.
166 # writing large size of data to console.
167 limit = 16000
167 limit = 16000
168 l = len(s)
168 l = len(s)
169 start = 0
169 start = 0
170 self.softspace = 0
170 self.softspace = 0
171 while start < l:
171 while start < l:
172 end = start + limit
172 end = start + limit
173 self.fp.write(s[start:end])
173 self.fp.write(s[start:end])
174 start = end
174 start = end
175 except IOError as inst:
175 except IOError as inst:
176 if inst.errno != 0 and not win32.lasterrorwaspipeerror(inst):
176 if inst.errno != 0 and not win32.lasterrorwaspipeerror(inst):
177 raise
177 raise
178 self.close()
178 self.close()
179 raise IOError(errno.EPIPE, 'Broken pipe')
179 raise IOError(errno.EPIPE, 'Broken pipe')
180
180
181 def flush(self):
181 def flush(self):
182 try:
182 try:
183 return self.fp.flush()
183 return self.fp.flush()
184 except IOError as inst:
184 except IOError as inst:
185 if not win32.lasterrorwaspipeerror(inst):
185 if not win32.lasterrorwaspipeerror(inst):
186 raise
186 raise
187 raise IOError(errno.EPIPE, 'Broken pipe')
187 raise IOError(errno.EPIPE, 'Broken pipe')
188
188
189 def _is_win_9x():
189 def _is_win_9x():
190 '''return true if run on windows 95, 98 or me.'''
190 '''return true if run on windows 95, 98 or me.'''
191 try:
191 try:
192 return sys.getwindowsversion()[3] == 1
192 return sys.getwindowsversion()[3] == 1
193 except AttributeError:
193 except AttributeError:
194 return 'command' in encoding.environ.get('comspec', '')
194 return 'command' in encoding.environ.get('comspec', '')
195
195
196 def openhardlinks():
196 def openhardlinks():
197 return not _is_win_9x()
197 return not _is_win_9x()
198
198
199 def parsepatchoutput(output_line):
199 def parsepatchoutput(output_line):
200 """parses the output produced by patch and returns the filename"""
200 """parses the output produced by patch and returns the filename"""
201 pf = output_line[14:]
201 pf = output_line[14:]
202 if pf[0] == '`':
202 if pf[0] == '`':
203 pf = pf[1:-1] # Remove the quotes
203 pf = pf[1:-1] # Remove the quotes
204 return pf
204 return pf
205
205
206 def sshargs(sshcmd, host, user, port):
206 def sshargs(sshcmd, host, user, port):
207 '''Build argument list for ssh or Plink'''
207 '''Build argument list for ssh or Plink'''
208 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
208 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
209 args = user and ("%s@%s" % (user, host)) or host
209 args = user and ("%s@%s" % (user, host)) or host
210 if args.startswith('-') or args.startswith('/'):
210 if args.startswith('-') or args.startswith('/'):
211 raise error.Abort(
211 raise error.Abort(
212 _('illegal ssh hostname or username starting with - or /: %s') %
212 _('illegal ssh hostname or username starting with - or /: %s') %
213 args)
213 args)
214 args = shellquote(args)
214 args = shellquote(args)
215 if port:
215 if port:
216 args = '%s %s %s' % (pflag, shellquote(port), args)
216 args = '%s %s %s' % (pflag, shellquote(port), args)
217 return args
217 return args
218
218
219 def setflags(f, l, x):
219 def setflags(f, l, x):
220 pass
220 pass
221
221
222 def copymode(src, dst, mode=None):
222 def copymode(src, dst, mode=None):
223 pass
223 pass
224
224
225 def checkexec(path):
225 def checkexec(path):
226 return False
226 return False
227
227
228 def checklink(path):
228 def checklink(path):
229 return False
229 return False
230
230
231 def setbinary(fd):
231 def setbinary(fd):
232 # When run without console, pipes may expose invalid
232 # When run without console, pipes may expose invalid
233 # fileno(), usually set to -1.
233 # fileno(), usually set to -1.
234 fno = getattr(fd, 'fileno', None)
234 fno = getattr(fd, 'fileno', None)
235 if fno is not None and fno() >= 0:
235 if fno is not None and fno() >= 0:
236 msvcrt.setmode(fno(), os.O_BINARY)
236 msvcrt.setmode(fno(), os.O_BINARY)
237
237
238 def pconvert(path):
238 def pconvert(path):
239 return path.replace(pycompat.ossep, '/')
239 return path.replace(pycompat.ossep, '/')
240
240
241 def localpath(path):
241 def localpath(path):
242 return path.replace('/', '\\')
242 return path.replace('/', '\\')
243
243
244 def normpath(path):
244 def normpath(path):
245 return pconvert(os.path.normpath(path))
245 return pconvert(os.path.normpath(path))
246
246
247 def normcase(path):
247 def normcase(path):
248 return encoding.upper(path) # NTFS compares via upper()
248 return encoding.upper(path) # NTFS compares via upper()
249
249
250 # see posix.py for definitions
250 # see posix.py for definitions
251 normcasespec = encoding.normcasespecs.upper
251 normcasespec = encoding.normcasespecs.upper
252 normcasefallback = encoding.upperfallback
252 normcasefallback = encoding.upperfallback
253
253
254 def samestat(s1, s2):
254 def samestat(s1, s2):
255 return False
255 return False
256
256
257 def shelltocmdexe(path, env):
257 def shelltocmdexe(path, env):
258 r"""Convert shell variables in the form $var and ${var} inside ``path``
258 r"""Convert shell variables in the form $var and ${var} inside ``path``
259 to %var% form. Existing Windows style variables are left unchanged.
259 to %var% form. Existing Windows style variables are left unchanged.
260
260
261 The variables are limited to the given environment. Unknown variables are
261 The variables are limited to the given environment. Unknown variables are
262 left unchanged.
262 left unchanged.
263
263
264 >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'}
264 >>> e = {b'var1': b'v1', b'var2': b'v2', b'var3': b'v3'}
265 >>> # Only valid values are expanded
265 >>> # Only valid values are expanded
266 >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%',
266 >>> shelltocmdexe(b'cmd $var1 ${var2} %var3% $missing ${missing} %missing%',
267 ... e)
267 ... e)
268 'cmd %var1% %var2% %var3% $missing ${missing} %missing%'
268 'cmd %var1% %var2% %var3% $missing ${missing} %missing%'
269 >>> # Single quote prevents expansion, as does \$ escaping
269 >>> # Single quote prevents expansion, as does \$ escaping
270 >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e)
270 >>> shelltocmdexe(b"cmd '$var1 ${var2} %var3%' \$var1 \${var2} \\", e)
271 'cmd "$var1 ${var2} %var3%" $var1 ${var2} \\'
271 'cmd "$var1 ${var2} %var3%" $var1 ${var2} \\'
272 >>> # $$ is not special. %% is not special either, but can be the end and
272 >>> # $$ is not special. %% is not special either, but can be the end and
273 >>> # start of consecutive variables
273 >>> # start of consecutive variables
274 >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e)
274 >>> shelltocmdexe(b"cmd $$ %% %var1%%var2%", e)
275 'cmd $$ %% %var1%%var2%'
275 'cmd $$ %% %var1%%var2%'
276 >>> # No double substitution
276 >>> # No double substitution
277 >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'})
277 >>> shelltocmdexe(b"$var1 %var1%", {b'var1': b'%var2%', b'var2': b'boom'})
278 '%var1% %var1%'
278 '%var1% %var1%'
279 >>> # Tilde expansion
279 >>> # Tilde expansion
280 >>> shelltocmdexe(b"~/dir ~\dir2 ~tmpfile \~/", {})
280 >>> shelltocmdexe(b"~/dir ~\dir2 ~tmpfile \~/", {})
281 '%USERPROFILE%/dir %USERPROFILE%\\dir2 ~tmpfile ~/'
281 '%USERPROFILE%/dir %USERPROFILE%\\dir2 ~tmpfile ~/'
282 """
282 """
283 if not any(c in path for c in b"$'~"):
283 if not any(c in path for c in b"$'~"):
284 return path
284 return path
285
285
286 varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-'
286 varchars = pycompat.sysbytes(string.ascii_letters + string.digits) + b'_-'
287
287
288 res = b''
288 res = b''
289 index = 0
289 index = 0
290 pathlen = len(path)
290 pathlen = len(path)
291 while index < pathlen:
291 while index < pathlen:
292 c = path[index]
292 c = path[index]
293 if c == b'\'': # no expansion within single quotes
293 if c == b'\'': # no expansion within single quotes
294 path = path[index + 1:]
294 path = path[index + 1:]
295 pathlen = len(path)
295 pathlen = len(path)
296 try:
296 try:
297 index = path.index(b'\'')
297 index = path.index(b'\'')
298 res += b'"' + path[:index] + b'"'
298 res += b'"' + path[:index] + b'"'
299 except ValueError:
299 except ValueError:
300 res += c + path
300 res += c + path
301 index = pathlen - 1
301 index = pathlen - 1
302 elif c == b'%': # variable
302 elif c == b'%': # variable
303 path = path[index + 1:]
303 path = path[index + 1:]
304 pathlen = len(path)
304 pathlen = len(path)
305 try:
305 try:
306 index = path.index(b'%')
306 index = path.index(b'%')
307 except ValueError:
307 except ValueError:
308 res += b'%' + path
308 res += b'%' + path
309 index = pathlen - 1
309 index = pathlen - 1
310 else:
310 else:
311 var = path[:index]
311 var = path[:index]
312 res += b'%' + var + b'%'
312 res += b'%' + var + b'%'
313 elif c == b'$': # variable
313 elif c == b'$': # variable
314 if path[index + 1:index + 2] == b'{':
314 if path[index + 1:index + 2] == b'{':
315 path = path[index + 2:]
315 path = path[index + 2:]
316 pathlen = len(path)
316 pathlen = len(path)
317 try:
317 try:
318 index = path.index(b'}')
318 index = path.index(b'}')
319 var = path[:index]
319 var = path[:index]
320
320
321 # See below for why empty variables are handled specially
321 # See below for why empty variables are handled specially
322 if env.get(var, '') != '':
322 if env.get(var, '') != '':
323 res += b'%' + var + b'%'
323 res += b'%' + var + b'%'
324 else:
324 else:
325 res += b'${' + var + b'}'
325 res += b'${' + var + b'}'
326 except ValueError:
326 except ValueError:
327 res += b'${' + path
327 res += b'${' + path
328 index = pathlen - 1
328 index = pathlen - 1
329 else:
329 else:
330 var = b''
330 var = b''
331 index += 1
331 index += 1
332 c = path[index:index + 1]
332 c = path[index:index + 1]
333 while c != b'' and c in varchars:
333 while c != b'' and c in varchars:
334 var += c
334 var += c
335 index += 1
335 index += 1
336 c = path[index:index + 1]
336 c = path[index:index + 1]
337 # Some variables (like HG_OLDNODE) may be defined, but have an
337 # Some variables (like HG_OLDNODE) may be defined, but have an
338 # empty value. Those need to be skipped because when spawning
338 # empty value. Those need to be skipped because when spawning
339 # cmd.exe to run the hook, it doesn't replace %VAR% for an empty
339 # cmd.exe to run the hook, it doesn't replace %VAR% for an empty
340 # VAR, and that really confuses things like revset expressions.
340 # VAR, and that really confuses things like revset expressions.
341 # OTOH, if it's left in Unix format and the hook runs sh.exe, it
341 # OTOH, if it's left in Unix format and the hook runs sh.exe, it
342 # will substitute to an empty string, and everything is happy.
342 # will substitute to an empty string, and everything is happy.
343 if env.get(var, '') != '':
343 if env.get(var, '') != '':
344 res += b'%' + var + b'%'
344 res += b'%' + var + b'%'
345 else:
345 else:
346 res += b'$' + var
346 res += b'$' + var
347
347
348 if c != '':
348 if c != '':
349 index -= 1
349 index -= 1
350 elif (c == b'~' and index + 1 < pathlen
350 elif (c == b'~' and index + 1 < pathlen
351 and path[index + 1] in (b'\\', b'/')):
351 and path[index + 1] in (b'\\', b'/')):
352 res += "%USERPROFILE%"
352 res += "%USERPROFILE%"
353 elif (c == b'\\' and index + 1 < pathlen
353 elif (c == b'\\' and index + 1 < pathlen
354 and path[index + 1] in (b'$', b'~')):
354 and path[index + 1] in (b'$', b'~')):
355 # Skip '\', but only if it is escaping $ or ~
355 # Skip '\', but only if it is escaping $ or ~
356 res += path[index + 1]
356 res += path[index + 1]
357 index += 1
357 index += 1
358 else:
358 else:
359 res += c
359 res += c
360
360
361 index += 1
361 index += 1
362 return res
362 return res
363
363
364 # A sequence of backslashes is special iff it precedes a double quote:
364 # A sequence of backslashes is special iff it precedes a double quote:
365 # - if there's an even number of backslashes, the double quote is not
365 # - if there's an even number of backslashes, the double quote is not
366 # quoted (i.e. it ends the quoted region)
366 # quoted (i.e. it ends the quoted region)
367 # - if there's an odd number of backslashes, the double quote is quoted
367 # - if there's an odd number of backslashes, the double quote is quoted
368 # - in both cases, every pair of backslashes is unquoted into a single
368 # - in both cases, every pair of backslashes is unquoted into a single
369 # backslash
369 # backslash
370 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
370 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
371 # So, to quote a string, we must surround it in double quotes, double
371 # So, to quote a string, we must surround it in double quotes, double
372 # the number of backslashes that precede double quotes and add another
372 # the number of backslashes that precede double quotes and add another
373 # backslash before every double quote (being careful with the double
373 # backslash before every double quote (being careful with the double
374 # quote we've appended to the end)
374 # quote we've appended to the end)
375 _quotere = None
375 _quotere = None
376 _needsshellquote = None
376 _needsshellquote = None
377 def shellquote(s):
377 def shellquote(s):
378 r"""
378 r"""
379 >>> shellquote(br'C:\Users\xyz')
379 >>> shellquote(br'C:\Users\xyz')
380 '"C:\\Users\\xyz"'
380 '"C:\\Users\\xyz"'
381 >>> shellquote(br'C:\Users\xyz/mixed')
381 >>> shellquote(br'C:\Users\xyz/mixed')
382 '"C:\\Users\\xyz/mixed"'
382 '"C:\\Users\\xyz/mixed"'
383 >>> # Would be safe not to quote too, since it is all double backslashes
383 >>> # Would be safe not to quote too, since it is all double backslashes
384 >>> shellquote(br'C:\\Users\\xyz')
384 >>> shellquote(br'C:\\Users\\xyz')
385 '"C:\\\\Users\\\\xyz"'
385 '"C:\\\\Users\\\\xyz"'
386 >>> # But this must be quoted
386 >>> # But this must be quoted
387 >>> shellquote(br'C:\\Users\\xyz/abc')
387 >>> shellquote(br'C:\\Users\\xyz/abc')
388 '"C:\\\\Users\\\\xyz/abc"'
388 '"C:\\\\Users\\\\xyz/abc"'
389 """
389 """
390 global _quotere
390 global _quotere
391 if _quotere is None:
391 if _quotere is None:
392 _quotere = re.compile(br'(\\*)("|\\$)')
392 _quotere = re.compile(br'(\\*)("|\\$)')
393 global _needsshellquote
393 global _needsshellquote
394 if _needsshellquote is None:
394 if _needsshellquote is None:
395 # ":" is also treated as "safe character", because it is used as a part
395 # ":" is also treated as "safe character", because it is used as a part
396 # of path name on Windows. "\" is also part of a path name, but isn't
396 # of path name on Windows. "\" is also part of a path name, but isn't
397 # safe because shlex.split() (kind of) treats it as an escape char and
397 # safe because shlex.split() (kind of) treats it as an escape char and
398 # drops it. It will leave the next character, even if it is another
398 # drops it. It will leave the next character, even if it is another
399 # "\".
399 # "\".
400 _needsshellquote = re.compile(br'[^a-zA-Z0-9._:/-]').search
400 _needsshellquote = re.compile(br'[^a-zA-Z0-9._:/-]').search
401 if s and not _needsshellquote(s) and not _quotere.search(s):
401 if s and not _needsshellquote(s) and not _quotere.search(s):
402 # "s" shouldn't have to be quoted
402 # "s" shouldn't have to be quoted
403 return s
403 return s
404 return b'"%s"' % _quotere.sub(br'\1\1\\\2', s)
404 return b'"%s"' % _quotere.sub(br'\1\1\\\2', s)
405
405
406 def _unquote(s):
406 def _unquote(s):
407 if s.startswith(b'"') and s.endswith(b'"'):
407 if s.startswith(b'"') and s.endswith(b'"'):
408 return s[1:-1]
408 return s[1:-1]
409 return s
409 return s
410
410
411 def shellsplit(s):
411 def shellsplit(s):
412 """Parse a command string in cmd.exe way (best-effort)"""
412 """Parse a command string in cmd.exe way (best-effort)"""
413 return pycompat.maplist(_unquote, pycompat.shlexsplit(s, posix=False))
413 return pycompat.maplist(_unquote, pycompat.shlexsplit(s, posix=False))
414
414
415 def quotecommand(cmd):
415 def quotecommand(cmd):
416 """Build a command string suitable for os.popen* calls."""
416 """Build a command string suitable for os.popen* calls."""
417 if sys.version_info < (2, 7, 1):
417 if sys.version_info < (2, 7, 1):
418 # Python versions since 2.7.1 do this extra quoting themselves
418 # Python versions since 2.7.1 do this extra quoting themselves
419 return '"' + cmd + '"'
419 return '"' + cmd + '"'
420 return cmd
420 return cmd
421
421
422 # if you change this stub into a real check, please try to implement the
422 # if you change this stub into a real check, please try to implement the
423 # username and groupname functions above, too.
423 # username and groupname functions above, too.
424 def isowner(st):
424 def isowner(st):
425 return True
425 return True
426
426
427 def findexe(command):
427 def findexe(command):
428 '''Find executable for command searching like cmd.exe does.
428 '''Find executable for command searching like cmd.exe does.
429 If command is a basename then PATH is searched for command.
429 If command is a basename then PATH is searched for command.
430 PATH isn't searched if command is an absolute or relative path.
430 PATH isn't searched if command is an absolute or relative path.
431 An extension from PATHEXT is found and added if not present.
431 An extension from PATHEXT is found and added if not present.
432 If command isn't found None is returned.'''
432 If command isn't found None is returned.'''
433 pathext = encoding.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
433 pathext = encoding.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
434 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
434 pathexts = [ext for ext in pathext.lower().split(pycompat.ospathsep)]
435 if os.path.splitext(command)[1].lower() in pathexts:
435 if os.path.splitext(command)[1].lower() in pathexts:
436 pathexts = ['']
436 pathexts = ['']
437
437
438 def findexisting(pathcommand):
438 def findexisting(pathcommand):
439 'Will append extension (if needed) and return existing file'
439 'Will append extension (if needed) and return existing file'
440 for ext in pathexts:
440 for ext in pathexts:
441 executable = pathcommand + ext
441 executable = pathcommand + ext
442 if os.path.exists(executable):
442 if os.path.exists(executable):
443 return executable
443 return executable
444 return None
444 return None
445
445
446 if pycompat.ossep in command:
446 if pycompat.ossep in command:
447 return findexisting(command)
447 return findexisting(command)
448
448
449 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
449 for path in encoding.environ.get('PATH', '').split(pycompat.ospathsep):
450 executable = findexisting(os.path.join(path, command))
450 executable = findexisting(os.path.join(path, command))
451 if executable is not None:
451 if executable is not None:
452 return executable
452 return executable
453 return findexisting(os.path.expanduser(os.path.expandvars(command)))
453 return findexisting(os.path.expanduser(os.path.expandvars(command)))
454
454
455 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
455 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
456
456
457 def statfiles(files):
457 def statfiles(files):
458 '''Stat each file in files. Yield each stat, or None if a file
458 '''Stat each file in files. Yield each stat, or None if a file
459 does not exist or has a type we don't care about.
459 does not exist or has a type we don't care about.
460
460
461 Cluster and cache stat per directory to minimize number of OS stat calls.'''
461 Cluster and cache stat per directory to minimize number of OS stat calls.'''
462 dircache = {} # dirname -> filename -> status | None if file does not exist
462 dircache = {} # dirname -> filename -> status | None if file does not exist
463 getkind = stat.S_IFMT
463 getkind = stat.S_IFMT
464 for nf in files:
464 for nf in files:
465 nf = normcase(nf)
465 nf = normcase(nf)
466 dir, base = os.path.split(nf)
466 dir, base = os.path.split(nf)
467 if not dir:
467 if not dir:
468 dir = '.'
468 dir = '.'
469 cache = dircache.get(dir, None)
469 cache = dircache.get(dir, None)
470 if cache is None:
470 if cache is None:
471 try:
471 try:
472 dmap = dict([(normcase(n), s)
472 dmap = dict([(normcase(n), s)
473 for n, k, s in listdir(dir, True)
473 for n, k, s in listdir(dir, True)
474 if getkind(s.st_mode) in _wantedkinds])
474 if getkind(s.st_mode) in _wantedkinds])
475 except OSError as err:
475 except OSError as err:
476 # Python >= 2.5 returns ENOENT and adds winerror field
476 # Python >= 2.5 returns ENOENT and adds winerror field
477 # EINVAL is raised if dir is not a directory.
477 # EINVAL is raised if dir is not a directory.
478 if err.errno not in (errno.ENOENT, errno.EINVAL,
478 if err.errno not in (errno.ENOENT, errno.EINVAL,
479 errno.ENOTDIR):
479 errno.ENOTDIR):
480 raise
480 raise
481 dmap = {}
481 dmap = {}
482 cache = dircache.setdefault(dir, dmap)
482 cache = dircache.setdefault(dir, dmap)
483 yield cache.get(base, None)
483 yield cache.get(base, None)
484
484
485 def username(uid=None):
485 def username(uid=None):
486 """Return the name of the user with the given uid.
486 """Return the name of the user with the given uid.
487
487
488 If uid is None, return the name of the current user."""
488 If uid is None, return the name of the current user."""
489 return None
489 return None
490
490
491 def groupname(gid=None):
491 def groupname(gid=None):
492 """Return the name of the group with the given gid.
492 """Return the name of the group with the given gid.
493
493
494 If gid is None, return the name of the current group."""
494 If gid is None, return the name of the current group."""
495 return None
495 return None
496
496
497 def removedirs(name):
497 def removedirs(name):
498 """special version of os.removedirs that does not remove symlinked
498 """special version of os.removedirs that does not remove symlinked
499 directories or junction points if they actually contain files"""
499 directories or junction points if they actually contain files"""
500 if listdir(name):
500 if listdir(name):
501 return
501 return
502 os.rmdir(name)
502 os.rmdir(name)
503 head, tail = os.path.split(name)
503 head, tail = os.path.split(name)
504 if not tail:
504 if not tail:
505 head, tail = os.path.split(head)
505 head, tail = os.path.split(head)
506 while head and tail:
506 while head and tail:
507 try:
507 try:
508 if listdir(head):
508 if listdir(head):
509 return
509 return
510 os.rmdir(head)
510 os.rmdir(head)
511 except (ValueError, OSError):
511 except (ValueError, OSError):
512 break
512 break
513 head, tail = os.path.split(head)
513 head, tail = os.path.split(head)
514
514
515 def rename(src, dst):
515 def rename(src, dst):
516 '''atomically rename file src to dst, replacing dst if it exists'''
516 '''atomically rename file src to dst, replacing dst if it exists'''
517 try:
517 try:
518 os.rename(src, dst)
518 os.rename(src, dst)
519 except OSError as e:
519 except OSError as e:
520 if e.errno != errno.EEXIST:
520 if e.errno != errno.EEXIST:
521 raise
521 raise
522 unlink(dst)
522 unlink(dst)
523 os.rename(src, dst)
523 os.rename(src, dst)
524
524
525 def gethgcmd():
525 def gethgcmd():
526 return [sys.executable] + sys.argv[:1]
526 return [encoding.strtolocal(arg) for arg in [sys.executable] + sys.argv[:1]]
527
527
528 def groupmembers(name):
528 def groupmembers(name):
529 # Don't support groups on Windows for now
529 # Don't support groups on Windows for now
530 raise KeyError
530 raise KeyError
531
531
532 def isexec(f):
532 def isexec(f):
533 return False
533 return False
534
534
535 class cachestat(object):
535 class cachestat(object):
536 def __init__(self, path):
536 def __init__(self, path):
537 pass
537 pass
538
538
539 def cacheable(self):
539 def cacheable(self):
540 return False
540 return False
541
541
542 def lookupreg(key, valname=None, scope=None):
542 def lookupreg(key, valname=None, scope=None):
543 ''' Look up a key/value name in the Windows registry.
543 ''' Look up a key/value name in the Windows registry.
544
544
545 valname: value name. If unspecified, the default value for the key
545 valname: value name. If unspecified, the default value for the key
546 is used.
546 is used.
547 scope: optionally specify scope for registry lookup, this can be
547 scope: optionally specify scope for registry lookup, this can be
548 a sequence of scopes to look up in order. Default (CURRENT_USER,
548 a sequence of scopes to look up in order. Default (CURRENT_USER,
549 LOCAL_MACHINE).
549 LOCAL_MACHINE).
550 '''
550 '''
551 if scope is None:
551 if scope is None:
552 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
552 scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE)
553 elif not isinstance(scope, (list, tuple)):
553 elif not isinstance(scope, (list, tuple)):
554 scope = (scope,)
554 scope = (scope,)
555 for s in scope:
555 for s in scope:
556 try:
556 try:
557 with winreg.OpenKey(s, encoding.strfromlocal(key)) as hkey:
557 with winreg.OpenKey(s, encoding.strfromlocal(key)) as hkey:
558 val = winreg.QueryValueEx(hkey, valname)[0]
558 val = winreg.QueryValueEx(hkey, valname)[0]
559 # never let a Unicode string escape into the wild
559 # never let a Unicode string escape into the wild
560 return encoding.unitolocal(val)
560 return encoding.unitolocal(val)
561 except EnvironmentError:
561 except EnvironmentError:
562 pass
562 pass
563
563
564 expandglobs = True
564 expandglobs = True
565
565
566 def statislink(st):
566 def statislink(st):
567 '''check whether a stat result is a symlink'''
567 '''check whether a stat result is a symlink'''
568 return False
568 return False
569
569
570 def statisexec(st):
570 def statisexec(st):
571 '''check whether a stat result is an executable file'''
571 '''check whether a stat result is an executable file'''
572 return False
572 return False
573
573
574 def poll(fds):
574 def poll(fds):
575 # see posix.py for description
575 # see posix.py for description
576 raise NotImplementedError()
576 raise NotImplementedError()
577
577
578 def readpipe(pipe):
578 def readpipe(pipe):
579 """Read all available data from a pipe."""
579 """Read all available data from a pipe."""
580 chunks = []
580 chunks = []
581 while True:
581 while True:
582 size = win32.peekpipe(pipe)
582 size = win32.peekpipe(pipe)
583 if not size:
583 if not size:
584 break
584 break
585
585
586 s = pipe.read(size)
586 s = pipe.read(size)
587 if not s:
587 if not s:
588 break
588 break
589 chunks.append(s)
589 chunks.append(s)
590
590
591 return ''.join(chunks)
591 return ''.join(chunks)
592
592
593 def bindunixsocket(sock, path):
593 def bindunixsocket(sock, path):
594 raise NotImplementedError('unsupported platform')
594 raise NotImplementedError('unsupported platform')
General Comments 0
You need to be logged in to leave comments. Login now