/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: Gustav Hartvigsson
  • Date: 2021-01-09 21:36:27 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20210109213627-h1xwcutzy9m7a99b
Added 'Case Preserving Working Tree Use Cases' from Canonical Wiki

* Addod a page from the Canonical Bazaar wiki
  with information on the scmeatics of case
  perserving filesystems an a case insensitive
  filesystem works.
  
  * Needs re-work, but this will do as it is the
    same inforamoton as what was on the linked
    page in the currint documentation.

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
61
 
 
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
32
 
67
33
# Special Win32 API constants
68
34
# Handles of std streams
88
54
 
89
55
def debug_memory_win32api(message='', short=True):
90
56
    """Use trace.note() to dump the running memory info."""
 
57
    import ctypes
91
58
    from breezy import trace
92
 
    if has_ctypes:
93
 
        class PROCESS_MEMORY_COUNTERS_EX(ctypes.Structure):
94
 
            """Used by GetProcessMemoryInfo"""
95
 
            _fields_ = [('cb', ctypes.c_ulong),
96
 
                        ('PageFaultCount', ctypes.c_ulong),
97
 
                        ('PeakWorkingSetSize', ctypes.c_size_t),
98
 
                        ('WorkingSetSize', ctypes.c_size_t),
99
 
                        ('QuotaPeakPagedPoolUsage', ctypes.c_size_t),
100
 
                        ('QuotaPagedPoolUsage', ctypes.c_size_t),
101
 
                        ('QuotaPeakNonPagedPoolUsage', ctypes.c_size_t),
102
 
                        ('QuotaNonPagedPoolUsage', ctypes.c_size_t),
103
 
                        ('PagefileUsage', ctypes.c_size_t),
104
 
                        ('PeakPagefileUsage', ctypes.c_size_t),
105
 
                        ('PrivateUsage', ctypes.c_size_t),
106
 
                        ]
107
 
        cur_process = ctypes.windll.kernel32.GetCurrentProcess()
108
 
        mem_struct = PROCESS_MEMORY_COUNTERS_EX()
109
 
        ret = ctypes.windll.psapi.GetProcessMemoryInfo(
110
 
            cur_process, ctypes.byref(mem_struct), ctypes.sizeof(mem_struct))
111
 
        if not ret:
112
 
            trace.note(gettext('Failed to GetProcessMemoryInfo()'))
113
 
            return
114
 
        info = {'PageFaultCount': mem_struct.PageFaultCount,
115
 
                'PeakWorkingSetSize': mem_struct.PeakWorkingSetSize,
116
 
                'WorkingSetSize': mem_struct.WorkingSetSize,
117
 
                'QuotaPeakPagedPoolUsage': mem_struct.QuotaPeakPagedPoolUsage,
118
 
                'QuotaPagedPoolUsage': mem_struct.QuotaPagedPoolUsage,
119
 
                'QuotaPeakNonPagedPoolUsage':
120
 
                    mem_struct.QuotaPeakNonPagedPoolUsage,
121
 
                'QuotaNonPagedPoolUsage': mem_struct.QuotaNonPagedPoolUsage,
122
 
                'PagefileUsage': mem_struct.PagefileUsage,
123
 
                'PeakPagefileUsage': mem_struct.PeakPagefileUsage,
124
 
                '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)
132
 
    else:
133
 
        trace.note(gettext('Cannot debug memory on win32 without ctypes'
134
 
                           ' or win32process'))
 
59
    class PROCESS_MEMORY_COUNTERS_EX(ctypes.Structure):
 
60
        """Used by GetProcessMemoryInfo"""
 
61
        _fields_ = [('cb', ctypes.c_ulong),
 
62
                    ('PageFaultCount', ctypes.c_ulong),
 
63
                    ('PeakWorkingSetSize', ctypes.c_size_t),
 
64
                    ('WorkingSetSize', ctypes.c_size_t),
 
65
                    ('QuotaPeakPagedPoolUsage', ctypes.c_size_t),
 
66
                    ('QuotaPagedPoolUsage', ctypes.c_size_t),
 
67
                    ('QuotaPeakNonPagedPoolUsage', ctypes.c_size_t),
 
68
                    ('QuotaNonPagedPoolUsage', ctypes.c_size_t),
 
69
                    ('PagefileUsage', ctypes.c_size_t),
 
70
                    ('PeakPagefileUsage', ctypes.c_size_t),
 
71
                    ('PrivateUsage', ctypes.c_size_t),
 
72
                    ]
 
73
    cur_process = ctypes.windll.kernel32.GetCurrentProcess()
 
74
    mem_struct = PROCESS_MEMORY_COUNTERS_EX()
 
75
    ret = ctypes.windll.psapi.GetProcessMemoryInfo(
 
76
        cur_process, ctypes.byref(mem_struct), ctypes.sizeof(mem_struct))
 
77
    if not ret:
 
78
        trace.note(gettext('Failed to GetProcessMemoryInfo()'))
135
79
        return
 
80
    info = {'PageFaultCount': mem_struct.PageFaultCount,
 
81
            'PeakWorkingSetSize': mem_struct.PeakWorkingSetSize,
 
82
            'WorkingSetSize': mem_struct.WorkingSetSize,
 
83
            'QuotaPeakPagedPoolUsage': mem_struct.QuotaPeakPagedPoolUsage,
 
84
            'QuotaPagedPoolUsage': mem_struct.QuotaPagedPoolUsage,
 
85
            'QuotaPeakNonPagedPoolUsage':
 
86
                mem_struct.QuotaPeakNonPagedPoolUsage,
 
87
            'QuotaNonPagedPoolUsage': mem_struct.QuotaNonPagedPoolUsage,
 
88
            'PagefileUsage': mem_struct.PagefileUsage,
 
89
            'PeakPagefileUsage': mem_struct.PeakPagefileUsage,
 
90
            'PrivateUsage': mem_struct.PrivateUsage,
 
91
            }
136
92
    if short:
137
93
        # using base-2 units (see HACKING.txt).
138
94
        trace.note(gettext('WorkingSize {0:>7}KiB'
163
119
    console window and return tuple (sizex, sizey) if success,
164
120
    or default size (defaultx, defaulty) otherwise.
165
121
    """
166
 
    if not has_ctypes:
167
 
        # no ctypes is found
168
 
        return (defaultx, defaulty)
169
 
 
 
122
    import ctypes
170
123
    # To avoid problem with redirecting output via pipe
171
124
    # we need to use stderr instead of stdout
172
125
    h = ctypes.windll.kernel32.GetStdHandle(WIN32_STDERR_HANDLE)
189
142
 
190
143
    Result is always unicode (or None).
191
144
    """
192
 
    if has_ctypes:
193
 
        try:
194
 
            SHGetSpecialFolderPath = \
195
 
                ctypes.windll.shell32.SHGetSpecialFolderPathW
196
 
        except AttributeError:
197
 
            pass
198
 
        else:
199
 
            buf = ctypes.create_unicode_buffer(MAX_PATH)
200
 
            if SHGetSpecialFolderPath(None, buf, csidl, 0):
201
 
                return buf.value
202
 
 
203
 
    global has_win32com_shell
204
 
    if has_win32com_shell is None:
205
 
        try:
206
 
            from win32com.shell import shell
207
 
            has_win32com_shell = True
208
 
        except ImportError:
209
 
            has_win32com_shell = False
210
 
    if has_win32com_shell:
211
 
        # still need to bind the name locally, but this is fast.
212
 
        from win32com.shell import shell
213
 
        try:
214
 
            return shell.SHGetSpecialFolderPath(0, csidl, 0)
215
 
        except shell.error:
216
 
            # possibly E_NOTIMPL meaning we can't load the function pointer,
217
 
            # or E_FAIL meaning the function failed - regardless, just ignore it
218
 
            pass
219
 
    return None
 
145
    import ctypes
 
146
    try:
 
147
        SHGetSpecialFolderPath = \
 
148
            ctypes.windll.shell32.SHGetSpecialFolderPathW
 
149
    except AttributeError:
 
150
        pass
 
151
    else:
 
152
        buf = ctypes.create_unicode_buffer(MAX_PATH)
 
153
        if SHGetSpecialFolderPath(None, buf, csidl, 0):
 
154
            return buf.value
220
155
 
221
156
 
222
157
def get_appdata_location():
232
167
    if appdata:
233
168
        return appdata
234
169
    # Use APPDATA if defined, will return None if not
235
 
    return get_environ_unicode('APPDATA')
 
170
    return os.environ.get('APPDATA')
236
171
 
237
172
 
238
173
def get_local_appdata_location():
249
184
    if local:
250
185
        return local
251
186
    # Vista supplies LOCALAPPDATA, but XP and earlier do not.
252
 
    local = get_environ_unicode('LOCALAPPDATA')
 
187
    local = os.environ.get('LOCALAPPDATA')
253
188
    if local:
254
189
        return local
255
190
    return get_appdata_location()
264
199
    home = _get_sh_special_folder_path(CSIDL_PERSONAL)
265
200
    if home:
266
201
        return home
267
 
    home = get_environ_unicode('HOME')
 
202
    home = os.environ.get('HOME')
268
203
    if home is not None:
269
204
        return home
270
 
    homepath = get_environ_unicode('HOMEPATH')
 
205
    homepath = os.environ.get('HOMEPATH')
271
206
    if homepath is not None:
272
 
        return os.path.join(get_environ_unicode('HOMEDIR', ''), home)
 
207
        return os.path.join(os.environ.get('HOMEDIR', ''), home)
273
208
    # at least return windows root directory
274
 
    windir = get_environ_unicode('WINDIR')
 
209
    windir = os.environ.get('WINDIR')
275
210
    if windir:
276
211
        return os.path.splitdrive(windir)[0] + '/'
277
212
    # otherwise C:\ is good enough for 98% users
282
217
    """Return user name as login name.
283
218
    If name cannot be obtained return None.
284
219
    """
285
 
    if has_ctypes:
286
 
        try:
287
 
            advapi32 = ctypes.windll.advapi32
288
 
            GetUserName = getattr(advapi32, 'GetUserName' + suffix)
289
 
        except AttributeError:
290
 
            pass
291
 
        else:
292
 
            buf = create_buffer(UNLEN + 1)
293
 
            n = ctypes.c_int(UNLEN + 1)
294
 
            if GetUserName(buf, ctypes.byref(n)):
295
 
                return extract_buffer(buf)
 
220
    try:
 
221
        advapi32 = ctypes.windll.advapi32
 
222
        GetUserName = getattr(advapi32, 'GetUserNameW')
 
223
    except AttributeError:
 
224
        pass
 
225
    else:
 
226
        buf = ctypes.create_unicode_buffer(UNLEN + 1)
 
227
        n = ctypes.c_int(UNLEN + 1)
 
228
        if GetUserName(buf, ctypes.byref(n)):
 
229
            return buf.value
296
230
    # otherwise try env variables
297
 
    return get_environ_unicode('USERNAME')
 
231
    return os.environ.get('USERNAME')
298
232
 
299
233
 
300
234
# 1 == ComputerNameDnsHostname, which returns "The DNS host name of the local
308
242
 
309
243
    :return: A unicode string representing the host name.
310
244
    """
311
 
    if has_win32api:
312
 
        try:
313
 
            return win32api.GetComputerNameEx(_WIN32_ComputerNameDnsHostname)
314
 
        except (NotImplementedError, win32api.error):
315
 
            # NotImplemented will happen on win9x...
316
 
            pass
317
 
    if has_ctypes:
318
 
        try:
319
 
            kernel32 = ctypes.windll.kernel32
320
 
        except AttributeError:
321
 
            pass  # Missing the module we need
322
 
        else:
323
 
            buf = create_buffer(MAX_COMPUTERNAME_LENGTH + 1)
324
 
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH + 1)
325
 
 
326
 
            # Try GetComputerNameEx which gives a proper Unicode hostname
327
 
            GetComputerNameEx = getattr(kernel32, 'GetComputerNameEx' + suffix,
328
 
                                        None)
329
 
            if (GetComputerNameEx is not None
330
 
                and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
331
 
                                      buf, ctypes.byref(n))):
332
 
                return extract_buffer(buf)
333
 
 
334
 
            # Try GetComputerName in case GetComputerNameEx wasn't found
335
 
            # It returns the NETBIOS name, which isn't as good, but still ok.
336
 
            # The first GetComputerNameEx might have changed 'n', so reset it
337
 
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH + 1)
338
 
            GetComputerName = getattr(kernel32, 'GetComputerName' + suffix,
339
 
                                      None)
340
 
            if (GetComputerName is not None
341
 
                    and GetComputerName(buf, ctypes.byref(n))):
342
 
                return extract_buffer(buf)
343
 
    return get_environ_unicode('COMPUTERNAME')
 
245
    import ctypes
 
246
    buf = ctypes.create_unicode_buffer(MAX_COMPUTERNAME_LENGTH + 1)
 
247
    n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH + 1)
 
248
 
 
249
    # Try GetComputerNameEx which gives a proper Unicode hostname
 
250
    GetComputerNameEx = getattr(
 
251
        ctypes.windll.kernel32, 'GetComputerNameExW', None)
 
252
    if (GetComputerNameEx is not None
 
253
        and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
 
254
                              buf, ctypes.byref(n))):
 
255
        return buf.value
 
256
    return os.environ.get('COMPUTERNAME')
344
257
 
345
258
 
346
259
def _ensure_with_dir(path):
430
343
 
431
344
    if type_id == REG_SZ:
432
345
        return path
433
 
    if type_id == REG_EXPAND_SZ and has_win32api:
434
 
        fullpath = win32api.ExpandEnvironmentStrings(path)
435
 
        if len(fullpath) > 1 and fullpath[0] == '"' and fullpath[-1] == '"':
436
 
            fullpath = fullpath[1:-1]   # remove quotes around value
437
 
        return fullpath
438
346
    return appname
439
347
 
440
348
 
441
349
def set_file_attr_hidden(path):
442
350
    """Set file attributes to hidden if possible"""
443
 
    if has_win32file:
444
 
        SetFileAttributes = win32file.SetFileAttributesW
445
 
        try:
446
 
            SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)
447
 
        except pywintypes.error as e:
448
 
            from . import trace
449
 
            trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
 
351
    from ctypes.wintypes import BOOL, DWORD, LPWSTR
 
352
    import ctypes
 
353
    # <https://docs.microsoft.com/windows/desktop/api/fileapi/nf-fileapi-setfileattributesw>
 
354
    SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW
 
355
    SetFileAttributes.argtypes = LPWSTR, DWORD
 
356
    SetFileAttributes.restype = BOOL
 
357
    FILE_ATTRIBUTE_HIDDEN = 2
 
358
    if not SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN):
 
359
        e = ctypes.WinError()
 
360
        from . import trace
 
361
        trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
450
362
 
451
363
 
452
364
def _command_line_to_argv(command_line, argv, single_quotes_allowed=False):
491
403
    return args
492
404
 
493
405
 
494
 
if has_ctypes:
495
 
    def get_unicode_argv():
496
 
        prototype = ctypes.WINFUNCTYPE(ctypes.c_wchar_p)
497
 
        GetCommandLineW = prototype(("GetCommandLineW",
498
 
                                     ctypes.windll.kernel32))
499
 
        command_line = GetCommandLineW()
500
 
        if command_line is None:
501
 
            raise ctypes.WinError()
502
 
        # Skip the first argument, since we only care about parameters
503
 
        argv = _command_line_to_argv(command_line, sys.argv)[1:]
504
 
        return argv
505
 
 
506
 
    def get_environ_unicode(key, default=None):
507
 
        """Get `key` from environment as unicode or `default` if unset
508
 
 
509
 
        The environment is natively unicode on modern windows versions but
510
 
        Python 2 only accesses it through the legacy bytestring api.
511
 
 
512
 
        Environmental variable names are case insenstive on Windows.
513
 
 
514
 
        A large enough buffer will be allocated to retrieve the value, though
515
 
        it may take two calls to the underlying library function.
516
 
 
517
 
        This needs ctypes because pywin32 does not expose the wide version.
518
 
        """
519
 
        cfunc = getattr(get_environ_unicode, "_c_function", None)
520
 
        if cfunc is None:
521
 
            from ctypes.wintypes import DWORD, LPCWSTR, LPWSTR
522
 
            cfunc = ctypes.WINFUNCTYPE(DWORD, LPCWSTR, LPWSTR, DWORD)(
523
 
                ("GetEnvironmentVariableW", ctypes.windll.kernel32))
524
 
            get_environ_unicode._c_function = cfunc
525
 
        buffer_size = 256  # heuristic, 256 characters often enough
526
 
        while True:
527
 
            buffer = ctypes.create_unicode_buffer(buffer_size)
528
 
            length = cfunc(key, buffer, buffer_size)
529
 
            if not length:
530
 
                code = ctypes.GetLastError()
531
 
                if code == 203:  # ERROR_ENVVAR_NOT_FOUND
532
 
                    return default
533
 
                raise ctypes.WinError(code)
534
 
            if buffer_size > length:
535
 
                return buffer[:length]
536
 
            buffer_size = length
537
 
 
538
 
 
539
 
if has_win32api:
540
 
    def _pywin32_is_local_pid_dead(pid):
541
 
        """True if pid doesn't correspond to live process on this machine"""
542
 
        try:
543
 
            handle = win32api.OpenProcess(1, False, pid)  # PROCESS_TERMINATE
544
 
        except pywintypes.error as e:
545
 
            if e[0] == 5:  # ERROR_ACCESS_DENIED
546
 
                # Probably something alive we're not allowed to kill
547
 
                return False
548
 
            elif e[0] == 87:  # ERROR_INVALID_PARAMETER
549
 
                return True
550
 
            raise
551
 
        handle.close()
552
 
        return False
553
 
    is_local_pid_dead = _pywin32_is_local_pid_dead
554
 
elif has_ctypes and sys.platform == 'win32':
555
 
    from ctypes.wintypes import BOOL, DWORD, HANDLE
556
 
    _kernel32 = ctypes.windll.kernel32
557
 
    _CloseHandle = ctypes.WINFUNCTYPE(BOOL, HANDLE)(
558
 
        ("CloseHandle", _kernel32))
559
 
    _OpenProcess = ctypes.WINFUNCTYPE(HANDLE, DWORD, BOOL, DWORD)(
560
 
        ("OpenProcess", _kernel32))
561
 
 
562
 
    def _ctypes_is_local_pid_dead(pid):
563
 
        """True if pid doesn't correspond to live process on this machine"""
564
 
        handle = _OpenProcess(1, False, pid)  # PROCESS_TERMINATE
565
 
        if not handle:
566
 
            errorcode = ctypes.GetLastError()
567
 
            if errorcode == 5:  # ERROR_ACCESS_DENIED
568
 
                # Probably something alive we're not allowed to kill
569
 
                return False
570
 
            elif errorcode == 87:  # ERROR_INVALID_PARAMETER
571
 
                return True
572
 
            raise ctypes.WinError(errorcode)
573
 
        _CloseHandle(handle)
574
 
        return False
575
 
    is_local_pid_dead = _ctypes_is_local_pid_dead
576
 
 
577
 
 
578
 
def _is_pywintypes_error(evalue):
579
 
    """True if exception instance is an error from pywin32"""
580
 
    if has_pywintypes and isinstance(evalue, pywintypes.error):
581
 
        return True
 
406
def _ctypes_is_local_pid_dead(pid):
 
407
    import ctypes
 
408
    kernel32 = ctypes.wintypes.windll.kernel32
 
409
    """True if pid doesn't correspond to live process on this machine"""
 
410
    handle = kernel32.OpenProcess(1, False, pid)
 
411
    if not handle:
 
412
        errorcode = ctypes.GetLastError()
 
413
        if errorcode == 5:  # ERROR_ACCESS_DENIED
 
414
            # Probably something alive we're not allowed to kill
 
415
            return False
 
416
        elif errorcode == 87:  # ERROR_INVALID_PARAMETER
 
417
            return True
 
418
        raise ctypes.WinError(errorcode)
 
419
    Kernel32.CloseHandle(handle)
582
420
    return False
 
421
 
 
422
is_local_pid_dead = _ctypes_is_local_pid_dead