/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/win32utils.py

  • Committer: Jelmer Vernooij
  • Date: 2020-05-06 02:13:25 UTC
  • mfrom: (7490.7.21 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200506021325-awbmmqu1zyorz7sj
Merge 3.1 branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
Only one dependency: ctypes should be installed.
20
20
"""
21
21
 
22
 
from __future__ import absolute_import
23
 
 
24
22
import glob
25
 
import operator
26
23
import os
27
24
import struct
28
25
import sys
32
29
    )
33
30
from breezy.i18n import gettext
34
31
 
35
 
# We can cope without it; use a separate variable to help pyflakes
36
 
try:
37
 
    import ctypes
38
 
    has_ctypes = True
39
 
except ImportError:
40
 
    has_ctypes = False
41
 
else:
42
 
    create_buffer = ctypes.create_unicode_buffer
43
 
    extract_buffer = operator.attrgetter("value")
44
 
    suffix = 'W'
45
 
try:
46
 
    import pywintypes
47
 
    has_pywintypes = True
48
 
except ImportError:
49
 
    has_pywintypes = has_win32file = has_win32api = False
50
 
else:
51
 
    try:
52
 
        import win32file
53
 
        has_win32file = True
54
 
    except ImportError:
55
 
        has_win32file = False
56
 
    try:
57
 
        import win32api
58
 
        has_win32api = True
59
 
    except ImportError:
60
 
        has_win32api = False
 
32
has_ctypes_win32 = False
 
33
if sys.platform == 'win32':
 
34
    try:
 
35
        import ctypes
 
36
    except ImportError:
 
37
        has_ctypes_win32 = False
61
38
 
62
 
# pulling in win32com.shell is a bit of overhead, and normally we don't need
63
 
# it as ctypes is preferred and common.  lazy_imports and "optional"
64
 
# modules don't work well, so we do our own lazy thing...
65
 
has_win32com_shell = None # Set to True or False once we know for sure...
66
39
 
67
40
# Special Win32 API constants
68
41
# Handles of std streams
72
45
 
73
46
# CSIDL constants (from MSDN 2003)
74
47
CSIDL_APPDATA = 0x001A      # Application Data folder
75
 
CSIDL_LOCAL_APPDATA = 0x001c# <user name>\Local Settings\Application Data (non roaming)
 
48
# <user name>\Local Settings\Application Data (non roaming)
 
49
CSIDL_LOCAL_APPDATA = 0x001c
76
50
CSIDL_PERSONAL = 0x0005     # My Documents folder
77
51
 
78
52
# from winapi C headers
88
62
def debug_memory_win32api(message='', short=True):
89
63
    """Use trace.note() to dump the running memory info."""
90
64
    from breezy import trace
91
 
    if has_ctypes:
 
65
    if has_ctypes_win32:
92
66
        class PROCESS_MEMORY_COUNTERS_EX(ctypes.Structure):
93
67
            """Used by GetProcessMemoryInfo"""
94
68
            _fields_ = [('cb', ctypes.c_ulong),
102
76
                        ('PagefileUsage', ctypes.c_size_t),
103
77
                        ('PeakPagefileUsage', ctypes.c_size_t),
104
78
                        ('PrivateUsage', ctypes.c_size_t),
105
 
                       ]
 
79
                        ]
106
80
        cur_process = ctypes.windll.kernel32.GetCurrentProcess()
107
81
        mem_struct = PROCESS_MEMORY_COUNTERS_EX()
108
 
        ret = ctypes.windll.psapi.GetProcessMemoryInfo(cur_process,
109
 
            ctypes.byref(mem_struct),
110
 
            ctypes.sizeof(mem_struct))
 
82
        ret = ctypes.windll.psapi.GetProcessMemoryInfo(
 
83
            cur_process, ctypes.byref(mem_struct), ctypes.sizeof(mem_struct))
111
84
        if not ret:
112
85
            trace.note(gettext('Failed to GetProcessMemoryInfo()'))
113
86
            return
122
95
                'PagefileUsage': mem_struct.PagefileUsage,
123
96
                'PeakPagefileUsage': mem_struct.PeakPagefileUsage,
124
97
                'PrivateUsage': mem_struct.PrivateUsage,
125
 
               }
126
 
    elif has_win32api:
127
 
        import win32process
128
 
        # win32process does not return PrivateUsage, because it doesn't use
129
 
        # PROCESS_MEMORY_COUNTERS_EX (it uses the one without _EX).
130
 
        proc = win32process.GetCurrentProcess()
131
 
        info = win32process.GetProcessMemoryInfo(proc)
 
98
                }
132
99
    else:
133
100
        trace.note(gettext('Cannot debug memory on win32 without ctypes'
134
 
                   ' or win32process'))
 
101
                           ' or win32process'))
135
102
        return
136
103
    if short:
137
104
        # using base-2 units (see HACKING.txt).
138
105
        trace.note(gettext('WorkingSize {0:>7}KiB'
139
 
                   '\tPeakWorking {1:>7}KiB\t{2}').format(
 
106
                           '\tPeakWorking {1:>7}KiB\t{2}').format(
140
107
                   info['WorkingSetSize'] / 1024,
141
108
                   info['PeakWorkingSetSize'] / 1024,
142
109
                   message))
143
110
        return
144
111
    if message:
145
112
        trace.note('%s', message)
146
 
    trace.note(gettext('WorkingSize       %8d KiB'), info['WorkingSetSize'] / 1024)
147
 
    trace.note(gettext('PeakWorking       %8d KiB'), info['PeakWorkingSetSize'] / 1024)
148
 
    trace.note(gettext('PagefileUsage     %8d KiB'), info.get('PagefileUsage', 0) / 1024)
 
113
    trace.note(gettext('WorkingSize       %8d KiB'),
 
114
               info['WorkingSetSize'] / 1024)
 
115
    trace.note(gettext('PeakWorking       %8d KiB'),
 
116
               info['PeakWorkingSetSize'] / 1024)
 
117
    trace.note(gettext('PagefileUsage     %8d KiB'),
 
118
               info.get('PagefileUsage', 0) / 1024)
149
119
    trace.note(gettext('PeakPagefileUsage %8d KiB'),
150
120
               info.get('PeakPagefileUsage', 0) / 1024)
151
 
    trace.note(gettext('PrivateUsage      %8d KiB'), info.get('PrivateUsage', 0) / 1024)
 
121
    trace.note(gettext('PrivateUsage      %8d KiB'),
 
122
               info.get('PrivateUsage', 0) / 1024)
152
123
    trace.note(gettext('PageFaultCount    %8d'), info.get('PageFaultCount', 0))
153
124
 
154
125
 
159
130
    console window and return tuple (sizex, sizey) if success,
160
131
    or default size (defaultx, defaulty) otherwise.
161
132
    """
162
 
    if not has_ctypes:
 
133
    if not has_ctypes_win32:
163
134
        # no ctypes is found
164
135
        return (defaultx, defaulty)
165
136
 
171
142
 
172
143
    if res:
173
144
        (bufx, bufy, curx, cury, wattr,
174
 
        left, top, right, bottom, maxx, maxy) = struct.unpack(
 
145
         left, top, right, bottom, maxx, maxy) = struct.unpack(
175
146
            "hhhhHhhhhhh", csbi.raw)
176
147
        sizex = right - left + 1
177
148
        sizey = bottom - top + 1
185
156
 
186
157
    Result is always unicode (or None).
187
158
    """
188
 
    if has_ctypes:
 
159
    if has_ctypes_win32:
189
160
        try:
190
161
            SHGetSpecialFolderPath = \
191
162
                ctypes.windll.shell32.SHGetSpecialFolderPathW
195
166
            buf = ctypes.create_unicode_buffer(MAX_PATH)
196
167
            if SHGetSpecialFolderPath(None, buf, csidl, 0):
197
168
                return buf.value
198
 
 
199
 
    global has_win32com_shell
200
 
    if has_win32com_shell is None:
201
 
        try:
202
 
            from win32com.shell import shell
203
 
            has_win32com_shell = True
204
 
        except ImportError:
205
 
            has_win32com_shell = False
206
 
    if has_win32com_shell:
207
 
        # still need to bind the name locally, but this is fast.
208
 
        from win32com.shell import shell
209
 
        try:
210
 
            return shell.SHGetSpecialFolderPath(0, csidl, 0)
211
 
        except shell.error:
212
 
            # possibly E_NOTIMPL meaning we can't load the function pointer,
213
 
            # or E_FAIL meaning the function failed - regardless, just ignore it
214
 
            pass
215
169
    return None
216
170
 
217
171
 
271
225
    if windir:
272
226
        return os.path.splitdrive(windir)[0] + '/'
273
227
    # otherwise C:\ is good enough for 98% users
274
 
    return unicode('C:/')
 
228
    return u'C:/'
275
229
 
276
230
 
277
231
def get_user_name():
278
232
    """Return user name as login name.
279
233
    If name cannot be obtained return None.
280
234
    """
281
 
    if has_ctypes:
 
235
    if has_ctypes_win32:
282
236
        try:
283
237
            advapi32 = ctypes.windll.advapi32
284
 
            GetUserName = getattr(advapi32, 'GetUserName'+suffix)
 
238
            GetUserName = getattr(advapi32, 'GetUserNameW')
285
239
        except AttributeError:
286
240
            pass
287
241
        else:
288
 
            buf = create_buffer(UNLEN+1)
289
 
            n = ctypes.c_int(UNLEN+1)
 
242
            buf = ctypes.create_unicode_buffer(UNLEN + 1)
 
243
            n = ctypes.c_int(UNLEN + 1)
290
244
            if GetUserName(buf, ctypes.byref(n)):
291
 
                return extract_buffer(buf)
 
245
                return buf.value
292
246
    # otherwise try env variables
293
247
    return get_environ_unicode('USERNAME')
294
248
 
297
251
# computer or the cluster associated with the local computer."
298
252
_WIN32_ComputerNameDnsHostname = 1
299
253
 
 
254
 
300
255
def get_host_name():
301
256
    """Return host machine name.
302
257
    If name cannot be obtained return None.
303
258
 
304
259
    :return: A unicode string representing the host name.
305
260
    """
306
 
    if has_win32api:
307
 
        try:
308
 
            return win32api.GetComputerNameEx(_WIN32_ComputerNameDnsHostname)
309
 
        except (NotImplementedError, win32api.error):
310
 
            # NotImplemented will happen on win9x...
311
 
            pass
312
 
    if has_ctypes:
 
261
    if has_ctypes_win32:
313
262
        try:
314
263
            kernel32 = ctypes.windll.kernel32
315
264
        except AttributeError:
316
 
            pass # Missing the module we need
 
265
            pass  # Missing the module we need
317
266
        else:
318
 
            buf = create_buffer(MAX_COMPUTERNAME_LENGTH+1)
319
 
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
 
267
            buf = ctypes.create_unicode_buffer(MAX_COMPUTERNAME_LENGTH + 1)
 
268
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH + 1)
320
269
 
321
270
            # Try GetComputerNameEx which gives a proper Unicode hostname
322
 
            GetComputerNameEx = getattr(kernel32, 'GetComputerNameEx'+suffix,
323
 
                                        None)
 
271
            GetComputerNameEx = getattr(kernel32, 'GetComputerNameExW', None)
324
272
            if (GetComputerNameEx is not None
325
273
                and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
326
274
                                      buf, ctypes.byref(n))):
327
 
                return extract_buffer(buf)
328
 
 
329
 
            # Try GetComputerName in case GetComputerNameEx wasn't found
330
 
            # It returns the NETBIOS name, which isn't as good, but still ok.
331
 
            # The first GetComputerNameEx might have changed 'n', so reset it
332
 
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
333
 
            GetComputerName = getattr(kernel32, 'GetComputerName'+suffix,
334
 
                                      None)
335
 
            if (GetComputerName is not None
336
 
                and GetComputerName(buf, ctypes.byref(n))):
337
 
                return extract_buffer(buf)
 
275
                return buf.value
338
276
    return get_environ_unicode('COMPUTERNAME')
339
277
 
340
278
 
341
279
def _ensure_with_dir(path):
342
280
    if (not os.path.split(path)[0] or path.startswith(u'*')
343
 
        or path.startswith(u'?')):
 
281
            or path.startswith(u'?')):
344
282
        return u'./' + path, True
345
283
    else:
346
284
        return path, False
347
285
 
 
286
 
348
287
def _undo_ensure_with_dir(path, corrected):
349
288
    if corrected:
350
289
        return path[2:]
409
348
 
410
349
    try:
411
350
        hkey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
412
 
            'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\' +
413
 
            basename)
 
351
                               'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\' +
 
352
                               basename)
414
353
    except EnvironmentError:
415
354
        return appname
416
355
 
434
373
 
435
374
def set_file_attr_hidden(path):
436
375
    """Set file attributes to hidden if possible"""
437
 
    if has_win32file:
438
 
        SetFileAttributes = win32file.SetFileAttributesW
439
 
        try:
440
 
            SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)
441
 
        except pywintypes.error as e:
442
 
            from . import trace
443
 
            trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
 
376
    if not has_ctypes_win32:
 
377
        return
 
378
    from ctypes.wintypes import BOOL, DWORD, LPCWSTR
 
379
    _kernel32 = ctypes.windll.kernel32
 
380
    # <https://docs.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-setfileattributesw>
 
381
    _SetFileAttributesW = ctypes.WINFUNCTYPE(BOOL, LPCWSTR, DWORD)(
 
382
        ("SetFileAttributesW", _kernel32))
 
383
    FILE_ATTRIBUTE_HIDDEN = 2
 
384
    if not SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN):
 
385
        e = ctypes.WinError()
 
386
        from . import trace
 
387
        trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
444
388
 
445
389
 
446
390
def _command_line_to_argv(command_line, argv, single_quotes_allowed=False):
457
401
    :return: A list of unicode strings.
458
402
    """
459
403
    # First, split the command line
460
 
    s = cmdline.Splitter(command_line, single_quotes_allowed=single_quotes_allowed)
461
 
    
462
 
    # Bug #587868 Now make sure that the length of s agrees with sys.argv 
463
 
    # we do this by simply counting the number of arguments in each. The counts should 
464
 
    # agree no matter what encoding sys.argv is in (AFAIK) 
465
 
    # len(arguments) < len(sys.argv) should be an impossibility since python gets 
 
404
    s = cmdline.Splitter(
 
405
        command_line, single_quotes_allowed=single_quotes_allowed)
 
406
 
 
407
    # Bug #587868 Now make sure that the length of s agrees with sys.argv
 
408
    # we do this by simply counting the number of arguments in each. The counts should
 
409
    # agree no matter what encoding sys.argv is in (AFAIK)
 
410
    # len(arguments) < len(sys.argv) should be an impossibility since python gets
466
411
    # args from the very same PEB as does GetCommandLineW
467
412
    arguments = list(s)
468
 
    
 
413
 
469
414
    # Now shorten the command line we get from GetCommandLineW to match sys.argv
470
415
    if len(arguments) < len(argv):
471
416
        raise AssertionError("Split command line can't be shorter than argv")
472
417
    arguments = arguments[len(arguments) - len(argv):]
473
 
    
 
418
 
474
419
    # Carry on to process globs (metachars) in the command line
475
420
    # expand globs if necessary
476
421
    # TODO: Use 'globbing' instead of 'glob.glob', this gives us stuff like
484
429
    return args
485
430
 
486
431
 
487
 
if has_ctypes:
 
432
if has_ctypes_win32:
488
433
    def get_unicode_argv():
489
434
        prototype = ctypes.WINFUNCTYPE(ctypes.c_wchar_p)
490
435
        GetCommandLineW = prototype(("GetCommandLineW",
496
441
        argv = _command_line_to_argv(command_line, sys.argv)[1:]
497
442
        return argv
498
443
 
499
 
 
500
444
    def get_environ_unicode(key, default=None):
501
445
        """Get `key` from environment as unicode or `default` if unset
502
446
 
507
451
 
508
452
        A large enough buffer will be allocated to retrieve the value, though
509
453
        it may take two calls to the underlying library function.
510
 
 
511
 
        This needs ctypes because pywin32 does not expose the wide version.
512
454
        """
513
455
        cfunc = getattr(get_environ_unicode, "_c_function", None)
514
456
        if cfunc is None:
516
458
            cfunc = ctypes.WINFUNCTYPE(DWORD, LPCWSTR, LPWSTR, DWORD)(
517
459
                ("GetEnvironmentVariableW", ctypes.windll.kernel32))
518
460
            get_environ_unicode._c_function = cfunc
519
 
        buffer_size = 256 # heuristic, 256 characters often enough
 
461
        buffer_size = 256  # heuristic, 256 characters often enough
520
462
        while True:
521
 
            buffer = ctypes.create_unicode_buffer(buffer_size)
522
 
            length = cfunc(key, buffer, buffer_size)
 
463
            buf = ctypes.create_unicode_buffer(buffer_size)
 
464
            length = cfunc(key, buf, buffer_size)
523
465
            if not length:
524
466
                code = ctypes.GetLastError()
525
 
                if code == 203: # ERROR_ENVVAR_NOT_FOUND
 
467
                if code == 203:  # ERROR_ENVVAR_NOT_FOUND
526
468
                    return default
527
469
                raise ctypes.WinError(code)
528
470
            if buffer_size > length:
529
 
                return buffer[:length]
 
471
                return buf[:length]
530
472
            buffer_size = length
531
473
 
532
474
 
533
 
if has_win32api:
534
 
    def _pywin32_is_local_pid_dead(pid):
535
 
        """True if pid doesn't correspond to live process on this machine"""
536
 
        try:
537
 
            handle = win32api.OpenProcess(1, False, pid) # PROCESS_TERMINATE
538
 
        except pywintypes.error as e:
539
 
            if e[0] == 5: # ERROR_ACCESS_DENIED
540
 
                # Probably something alive we're not allowed to kill
541
 
                return False
542
 
            elif e[0] == 87: # ERROR_INVALID_PARAMETER
543
 
                return True
544
 
            raise
545
 
        handle.close()
546
 
        return False
547
 
    is_local_pid_dead = _pywin32_is_local_pid_dead
548
 
elif has_ctypes and sys.platform == 'win32':
 
475
if has_ctypes_win32:
549
476
    from ctypes.wintypes import BOOL, DWORD, HANDLE
550
477
    _kernel32 = ctypes.windll.kernel32
551
478
    _CloseHandle = ctypes.WINFUNCTYPE(BOOL, HANDLE)(
552
479
        ("CloseHandle", _kernel32))
553
480
    _OpenProcess = ctypes.WINFUNCTYPE(HANDLE, DWORD, BOOL, DWORD)(
554
481
        ("OpenProcess", _kernel32))
 
482
 
555
483
    def _ctypes_is_local_pid_dead(pid):
556
484
        """True if pid doesn't correspond to live process on this machine"""
557
 
        handle = _OpenProcess(1, False, pid) # PROCESS_TERMINATE
 
485
        handle = _OpenProcess(1, False, pid)  # PROCESS_TERMINATE
558
486
        if not handle:
559
487
            errorcode = ctypes.GetLastError()
560
 
            if errorcode == 5: # ERROR_ACCESS_DENIED
 
488
            if errorcode == 5:  # ERROR_ACCESS_DENIED
561
489
                # Probably something alive we're not allowed to kill
562
490
                return False
563
 
            elif errorcode == 87: # ERROR_INVALID_PARAMETER
 
491
            elif errorcode == 87:  # ERROR_INVALID_PARAMETER
564
492
                return True
565
493
            raise ctypes.WinError(errorcode)
566
494
        _CloseHandle(handle)
567
495
        return False
 
496
 
568
497
    is_local_pid_dead = _ctypes_is_local_pid_dead
569
 
 
570
 
 
571
 
def _is_pywintypes_error(evalue):
572
 
    """True if exception instance is an error from pywin32"""
573
 
    if has_pywintypes and isinstance(evalue, pywintypes.error):
574
 
        return True
575
 
    return False