~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/win32utils.py

  • Committer: Tarmac
  • Author(s): Vincent Ladeuil
  • Date: 2017-01-30 14:42:05 UTC
  • mfrom: (6620.1.1 trunk)
  • Revision ID: tarmac-20170130144205-r8fh2xpmiuxyozpv
Merge  2.7 into trunk including fix for bug #1657238 [r=vila]

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
 
22
24
import glob
 
25
import operator
23
26
import os
24
27
import struct
25
28
import sys
26
29
 
27
 
from bzrlib import cmdline
 
30
from bzrlib import (
 
31
    cmdline,
 
32
    symbol_versioning,
 
33
    )
 
34
from bzrlib.i18n import gettext
28
35
 
29
36
# Windows version
30
37
if sys.platform == 'win32':
62
69
else:
63
70
    if winver == 'Windows 98':
64
71
        create_buffer = ctypes.create_string_buffer
 
72
        def extract_buffer(buf):
 
73
            return buf.value.decode("mbcs")
65
74
        suffix = 'A'
66
75
    else:
67
76
        create_buffer = ctypes.create_unicode_buffer
 
77
        extract_buffer = operator.attrgetter("value")
68
78
        suffix = 'W'
69
79
try:
70
 
    import win32file
71
80
    import pywintypes
72
 
    has_win32file = True
73
 
except ImportError:
74
 
    has_win32file = False
75
 
try:
76
 
    import win32api
77
 
    has_win32api = True
78
 
except ImportError:
79
 
    has_win32api = False
 
81
    has_pywintypes = True
 
82
except ImportError:
 
83
    has_pywintypes = has_win32file = has_win32api = False
 
84
else:
 
85
    try:
 
86
        import win32file
 
87
        has_win32file = True
 
88
    except ImportError:
 
89
        has_win32file = False
 
90
    try:
 
91
        import win32api
 
92
        has_win32api = True
 
93
    except ImportError:
 
94
        has_win32api = False
80
95
 
81
96
# pulling in win32com.shell is a bit of overhead, and normally we don't need
82
97
# it as ctypes is preferred and common.  lazy_imports and "optional"
128
143
            ctypes.byref(mem_struct),
129
144
            ctypes.sizeof(mem_struct))
130
145
        if not ret:
131
 
            trace.note('Failed to GetProcessMemoryInfo()')
 
146
            trace.note(gettext('Failed to GetProcessMemoryInfo()'))
132
147
            return
133
148
        info = {'PageFaultCount': mem_struct.PageFaultCount,
134
149
                'PeakWorkingSetSize': mem_struct.PeakWorkingSetSize,
149
164
        proc = win32process.GetCurrentProcess()
150
165
        info = win32process.GetProcessMemoryInfo(proc)
151
166
    else:
152
 
        trace.note('Cannot debug memory on win32 without ctypes'
153
 
                   ' or win32process')
 
167
        trace.note(gettext('Cannot debug memory on win32 without ctypes'
 
168
                   ' or win32process'))
154
169
        return
155
170
    if short:
156
171
        # using base-2 units (see HACKING.txt).
157
 
        trace.note('WorkingSize %7dKiB'
158
 
                   '\tPeakWorking %7dKiB\t%s',
 
172
        trace.note(gettext('WorkingSize {0:>7}KiB'
 
173
                   '\tPeakWorking {1:>7}KiB\t{2}').format(
159
174
                   info['WorkingSetSize'] / 1024,
160
175
                   info['PeakWorkingSetSize'] / 1024,
161
 
                   message)
 
176
                   message))
162
177
        return
163
178
    if message:
164
179
        trace.note('%s', message)
165
 
    trace.note('WorkingSize       %8d KiB', info['WorkingSetSize'] / 1024)
166
 
    trace.note('PeakWorking       %8d KiB', info['PeakWorkingSetSize'] / 1024)
167
 
    trace.note('PagefileUsage     %8d KiB', info.get('PagefileUsage', 0) / 1024)
168
 
    trace.note('PeakPagefileUsage %8d KiB',
 
180
    trace.note(gettext('WorkingSize       %8d KiB'), info['WorkingSetSize'] / 1024)
 
181
    trace.note(gettext('PeakWorking       %8d KiB'), info['PeakWorkingSetSize'] / 1024)
 
182
    trace.note(gettext('PagefileUsage     %8d KiB'), info.get('PagefileUsage', 0) / 1024)
 
183
    trace.note(gettext('PeakPagefileUsage %8d KiB'),
169
184
               info.get('PeakPagefileUsage', 0) / 1024)
170
 
    trace.note('PrivateUsage      %8d KiB', info.get('PrivateUsage', 0) / 1024)
171
 
    trace.note('PageFaultCount    %8d', info.get('PageFaultCount', 0))
 
185
    trace.note(gettext('PrivateUsage      %8d KiB'), info.get('PrivateUsage', 0) / 1024)
 
186
    trace.note(gettext('PageFaultCount    %8d'), info.get('PageFaultCount', 0))
172
187
 
173
188
 
174
189
def get_console_size(defaultx=80, defaulty=25):
242
257
    one that moves with the user as they logon to different machines, and
243
258
    a 'local' one that stays local to the machine.  This returns the 'roaming'
244
259
    directory, and thus is suitable for storing user-preferences, etc.
245
 
 
246
 
    Returned value can be unicode or plain string.
247
 
    To convert plain string to unicode use
248
 
    s.decode(osutils.get_user_encoding())
249
 
    (XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
250
260
    """
251
261
    appdata = _get_sh_special_folder_path(CSIDL_APPDATA)
252
262
    if appdata:
253
263
        return appdata
254
 
    # from env variable
255
 
    appdata = os.environ.get('APPDATA')
256
 
    if appdata:
257
 
        return appdata
258
 
    # if we fall to this point we on win98
259
 
    # at least try C:/WINDOWS/Application Data
260
 
    windir = os.environ.get('windir')
261
 
    if windir:
262
 
        appdata = os.path.join(windir, 'Application Data')
263
 
        if os.path.isdir(appdata):
264
 
            return appdata
265
 
    # did not find anything
266
 
    return None
 
264
    # Use APPDATA if defined, will return None if not
 
265
    return get_environ_unicode('APPDATA')
267
266
 
268
267
 
269
268
def get_local_appdata_location():
275
274
    a 'local' one that stays local to the machine.  This returns the 'local'
276
275
    directory, and thus is suitable for caches, temp files and other things
277
276
    which don't need to move with the user.
278
 
 
279
 
    Returned value can be unicode or plain string.
280
 
    To convert plain string to unicode use
281
 
    s.decode(osutils.get_user_encoding())
282
 
    (XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
283
277
    """
284
278
    local = _get_sh_special_folder_path(CSIDL_LOCAL_APPDATA)
285
279
    if local:
286
280
        return local
287
281
    # Vista supplies LOCALAPPDATA, but XP and earlier do not.
288
 
    local = os.environ.get('LOCALAPPDATA')
 
282
    local = get_environ_unicode('LOCALAPPDATA')
289
283
    if local:
290
284
        return local
291
285
    return get_appdata_location()
296
290
    Assume on win32 it's the <My Documents> folder.
297
291
    If location cannot be obtained return system drive root,
298
292
    i.e. C:\
299
 
 
300
 
    Returned value can be unicode or plain string.
301
 
    To convert plain string to unicode use
302
 
    s.decode(osutils.get_user_encoding())
303
293
    """
304
294
    home = _get_sh_special_folder_path(CSIDL_PERSONAL)
305
295
    if home:
306
296
        return home
307
 
    # try for HOME env variable
308
 
    home = os.path.expanduser('~')
309
 
    if home != '~':
 
297
    home = get_environ_unicode('HOME')
 
298
    if home is not None:
310
299
        return home
 
300
    homepath = get_environ_unicode('HOMEPATH')
 
301
    if homepath is not None:
 
302
        return os.path.join(get_environ_unicode('HOMEDIR', ''), home)
311
303
    # at least return windows root directory
312
 
    windir = os.environ.get('windir')
 
304
    windir = get_environ_unicode('WINDIR')
313
305
    if windir:
314
306
        return os.path.splitdrive(windir)[0] + '/'
315
307
    # otherwise C:\ is good enough for 98% users
316
 
    return 'C:/'
 
308
    return unicode('C:/')
317
309
 
318
310
 
319
311
def get_user_name():
320
312
    """Return user name as login name.
321
313
    If name cannot be obtained return None.
322
 
 
323
 
    Returned value can be unicode or plain string.
324
 
    To convert plain string to unicode use
325
 
    s.decode(osutils.get_user_encoding())
326
314
    """
327
315
    if has_ctypes:
328
316
        try:
334
322
            buf = create_buffer(UNLEN+1)
335
323
            n = ctypes.c_int(UNLEN+1)
336
324
            if GetUserName(buf, ctypes.byref(n)):
337
 
                return buf.value
 
325
                return extract_buffer(buf)
338
326
    # otherwise try env variables
339
 
    return os.environ.get('USERNAME', None)
 
327
    return get_environ_unicode('USERNAME')
340
328
 
341
329
 
342
330
# 1 == ComputerNameDnsHostname, which returns "The DNS host name of the local
347
335
    """Return host machine name.
348
336
    If name cannot be obtained return None.
349
337
 
350
 
    :return: A unicode string representing the host name. On win98, this may be
351
 
        a plain string as win32 api doesn't support unicode.
 
338
    :return: A unicode string representing the host name.
352
339
    """
353
340
    if has_win32api:
354
341
        try:
371
358
            if (GetComputerNameEx is not None
372
359
                and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
373
360
                                      buf, ctypes.byref(n))):
374
 
                return buf.value
 
361
                return extract_buffer(buf)
375
362
 
376
363
            # Try GetComputerName in case GetComputerNameEx wasn't found
377
364
            # It returns the NETBIOS name, which isn't as good, but still ok.
381
368
                                      None)
382
369
            if (GetComputerName is not None
383
370
                and GetComputerName(buf, ctypes.byref(n))):
384
 
                return buf.value
385
 
    # otherwise try env variables, which will be 'mbcs' encoded
386
 
    # on Windows (Python doesn't expose the native win32 unicode environment)
387
 
    # According to this:
388
 
    # http://msdn.microsoft.com/en-us/library/aa246807.aspx
389
 
    # environment variables should always be encoded in 'mbcs'.
390
 
    try:
391
 
        return os.environ['COMPUTERNAME'].decode("mbcs")
392
 
    except KeyError:
393
 
        return None
394
 
 
395
 
 
 
371
                return extract_buffer(buf)
 
372
    return get_environ_unicode('COMPUTERNAME')
 
373
 
 
374
 
 
375
@symbol_versioning.deprecated_function(
 
376
    symbol_versioning.deprecated_in((2, 5, 0)))
396
377
def _ensure_unicode(s):
397
378
    if s and type(s) != unicode:
398
379
        from bzrlib import osutils
400
381
    return s
401
382
 
402
383
 
403
 
def get_appdata_location_unicode():
404
 
    return _ensure_unicode(get_appdata_location())
405
 
 
406
 
def get_home_location_unicode():
407
 
    return _ensure_unicode(get_home_location())
408
 
 
409
 
def get_user_name_unicode():
410
 
    return _ensure_unicode(get_user_name())
411
 
 
412
 
def get_host_name_unicode():
413
 
    return _ensure_unicode(get_host_name())
 
384
get_appdata_location_unicode = symbol_versioning.deprecated_function(
 
385
    symbol_versioning.deprecated_in((2, 5, 0)))(get_appdata_location)
 
386
 
 
387
get_home_location_unicode = symbol_versioning.deprecated_function(
 
388
    symbol_versioning.deprecated_in((2, 5, 0)))(get_home_location)
 
389
 
 
390
get_user_name_unicode = symbol_versioning.deprecated_function(
 
391
    symbol_versioning.deprecated_in((2, 5, 0)))(get_user_name)
 
392
 
 
393
get_host_name_unicode = symbol_versioning.deprecated_function(
 
394
    symbol_versioning.deprecated_in((2, 5, 0)))(get_host_name)
414
395
 
415
396
 
416
397
def _ensure_with_dir(path):
427
408
        return path
428
409
 
429
410
 
430
 
 
431
411
def glob_one(possible_glob):
432
412
    """Same as glob.glob().
433
413
 
468
448
 
469
449
 
470
450
def get_app_path(appname):
471
 
    """Look up in Windows registry for full path to application executable.
 
451
    r"""Look up in Windows registry for full path to application executable.
472
452
    Typically, applications create subkey with their basename
473
453
    in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\
474
454
 
522
502
            trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
523
503
 
524
504
 
525
 
def _command_line_to_argv(command_line, single_quotes_allowed=False):
 
505
def _command_line_to_argv(command_line, argv, single_quotes_allowed=False):
526
506
    """Convert a Unicode command line into a list of argv arguments.
527
507
 
528
508
    It performs wildcard expansion to make wildcards act closer to how they
535
515
                                  default.
536
516
    :return: A list of unicode strings.
537
517
    """
 
518
    # First, split the command line
538
519
    s = cmdline.Splitter(command_line, single_quotes_allowed=single_quotes_allowed)
539
 
    # Now that we've split the content, expand globs if necessary
 
520
    
 
521
    # Bug #587868 Now make sure that the length of s agrees with sys.argv 
 
522
    # we do this by simply counting the number of arguments in each. The counts should 
 
523
    # agree no matter what encoding sys.argv is in (AFAIK) 
 
524
    # len(arguments) < len(sys.argv) should be an impossibility since python gets 
 
525
    # args from the very same PEB as does GetCommandLineW
 
526
    arguments = list(s)
 
527
    
 
528
    # Now shorten the command line we get from GetCommandLineW to match sys.argv
 
529
    if len(arguments) < len(argv):
 
530
        raise AssertionError("Split command line can't be shorter than argv")
 
531
    arguments = arguments[len(arguments) - len(argv):]
 
532
    
 
533
    # Carry on to process globs (metachars) in the command line
 
534
    # expand globs if necessary
540
535
    # TODO: Use 'globbing' instead of 'glob.glob', this gives us stuff like
541
536
    #       '**/' style globs
542
537
    args = []
543
 
    for is_quoted, arg in s:
 
538
    for is_quoted, arg in arguments:
544
539
        if is_quoted or not glob.has_magic(arg):
545
540
            args.append(arg)
546
541
        else:
548
543
    return args
549
544
 
550
545
 
551
 
if has_ctypes and winver != 'Windows 98':
 
546
if has_ctypes and winver == 'Windows NT':
552
547
    def get_unicode_argv():
553
548
        prototype = ctypes.WINFUNCTYPE(ctypes.c_wchar_p)
554
549
        GetCommandLineW = prototype(("GetCommandLineW",
557
552
        if command_line is None:
558
553
            raise ctypes.WinError()
559
554
        # Skip the first argument, since we only care about parameters
560
 
        argv = _command_line_to_argv(command_line)[1:]
561
 
        if getattr(sys, 'frozen', None) is None:
562
 
            # Invoked via 'python.exe' which takes the form:
563
 
            #   python.exe [PYTHON_OPTIONS] C:\Path\bzr [BZR_OPTIONS]
564
 
            # we need to get only BZR_OPTIONS part,
565
 
            # We already removed 'python.exe' so we remove everything up to and
566
 
            # including the first non-option ('-') argument.
567
 
            for idx in xrange(len(argv)):
568
 
                if argv[idx][:1] != '-':
569
 
                    break
570
 
            argv = argv[idx+1:]
 
555
        argv = _command_line_to_argv(command_line, sys.argv)[1:]
571
556
        return argv
 
557
    
 
558
 
 
559
    def get_environ_unicode(key, default=None):
 
560
        """Get `key` from environment as unicode or `default` if unset
 
561
 
 
562
        The environment is natively unicode on modern windows versions but
 
563
        Python 2 only accesses it through the legacy bytestring api.
 
564
 
 
565
        Environmental variable names are case insenstive on Windows.
 
566
 
 
567
        A large enough buffer will be allocated to retrieve the value, though
 
568
        it may take two calls to the underlying library function.
 
569
 
 
570
        This needs ctypes because pywin32 does not expose the wide version.
 
571
        """
 
572
        cfunc = getattr(get_environ_unicode, "_c_function", None)
 
573
        if cfunc is None:
 
574
            from ctypes.wintypes import DWORD, LPCWSTR, LPWSTR
 
575
            cfunc = ctypes.WINFUNCTYPE(DWORD, LPCWSTR, LPWSTR, DWORD)(
 
576
                ("GetEnvironmentVariableW", ctypes.windll.kernel32))
 
577
            get_environ_unicode._c_function = cfunc
 
578
        buffer_size = 256 # heuristic, 256 characters often enough
 
579
        while True:
 
580
            buffer = ctypes.create_unicode_buffer(buffer_size)
 
581
            length = cfunc(key, buffer, buffer_size)
 
582
            if not length:
 
583
                code = ctypes.GetLastError()
 
584
                if code == 203: # ERROR_ENVVAR_NOT_FOUND
 
585
                    return default
 
586
                raise ctypes.WinError(code)
 
587
            if buffer_size > length:
 
588
                return buffer[:length]
 
589
            buffer_size = length
572
590
else:
573
591
    get_unicode_argv = None
 
592
    def get_environ_unicode(key, default=None):
 
593
        """Get `key` from environment as unicode or `default` if unset
 
594
 
 
595
        Fallback version that should basically never be needed.
 
596
        """
 
597
        from bzrlib import osutils
 
598
        try:
 
599
            return os.environ[key].decode(osutils.get_user_encoding())
 
600
        except KeyError:
 
601
            return default
 
602
 
 
603
 
 
604
if has_win32api:
 
605
    def _pywin32_is_local_pid_dead(pid):
 
606
        """True if pid doesn't correspond to live process on this machine"""
 
607
        try:
 
608
            handle = win32api.OpenProcess(1, False, pid) # PROCESS_TERMINATE
 
609
        except pywintypes.error, e:
 
610
            if e[0] == 5: # ERROR_ACCESS_DENIED
 
611
                # Probably something alive we're not allowed to kill
 
612
                return False
 
613
            elif e[0] == 87: # ERROR_INVALID_PARAMETER
 
614
                return True
 
615
            raise
 
616
        handle.close()
 
617
        return False
 
618
    is_local_pid_dead = _pywin32_is_local_pid_dead
 
619
elif has_ctypes and sys.platform == 'win32':
 
620
    from ctypes.wintypes import BOOL, DWORD, HANDLE
 
621
    _kernel32 = ctypes.windll.kernel32
 
622
    _CloseHandle = ctypes.WINFUNCTYPE(BOOL, HANDLE)(
 
623
        ("CloseHandle", _kernel32))
 
624
    _OpenProcess = ctypes.WINFUNCTYPE(HANDLE, DWORD, BOOL, DWORD)(
 
625
        ("OpenProcess", _kernel32))
 
626
    def _ctypes_is_local_pid_dead(pid):
 
627
        """True if pid doesn't correspond to live process on this machine"""
 
628
        handle = _OpenProcess(1, False, pid) # PROCESS_TERMINATE
 
629
        if not handle:
 
630
            errorcode = ctypes.GetLastError()
 
631
            if errorcode == 5: # ERROR_ACCESS_DENIED
 
632
                # Probably something alive we're not allowed to kill
 
633
                return False
 
634
            elif errorcode == 87: # ERROR_INVALID_PARAMETER
 
635
                return True
 
636
            raise ctypes.WinError(errorcode)
 
637
        _CloseHandle(handle)
 
638
        return False
 
639
    is_local_pid_dead = _ctypes_is_local_pid_dead
 
640
 
 
641
 
 
642
def _is_pywintypes_error(evalue):
 
643
    """True if exception instance is an error from pywin32"""
 
644
    if has_pywintypes and isinstance(evalue, pywintypes.error):
 
645
        return True
 
646
    return False