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