~bzr-pqm/bzr/bzr.dev

2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
1
# Copyright (C) 2006, 2007 Canonical Ltd
2
#
2052.3.1 by John Arbash Meinel
Add tests to cleanup the copyright of all source files
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
17
"""Win32-specific helper functions
18
19
Only one dependency: ctypes should be installed.
20
"""
21
22
import os
1185.16.86 by mbp at sourcefrog
- win32 get_console_size from Alexander
23
import struct
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
24
import sys
25
26
27
# Windows version
28
if sys.platform == 'win32':
29
    _major,_minor,_build,_platform,_text = sys.getwindowsversion()
2245.4.11 by Alexander Belchenko
Small fixes after John's review; added NEWS entry
30
    # from MSDN:
31
    # dwPlatformId
32
    #   The operating system platform.
33
    #   This member can be one of the following values.
34
    #   ==========================  ======================================
35
    #   Value                       Meaning
36
    #   --------------------------  --------------------------------------
37
    #   VER_PLATFORM_WIN32_NT       The operating system is Windows Vista,
38
    #   2                           Windows Server "Longhorn",
39
    #                               Windows Server 2003, Windows XP,
40
    #                               Windows 2000, or Windows NT.
41
    #
42
    #   VER_PLATFORM_WIN32_WINDOWS  The operating system is Windows Me,
43
    #   1                           Windows 98, or Windows 95.
44
    #   ==========================  ======================================
45
    if _platform == 2:
46
        winver = 'Windows NT'
47
    else:
48
        # don't care about real Windows name, just to force safe operations
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
49
        winver = 'Windows 98'
50
else:
51
    winver = None
52
1185.16.86 by mbp at sourcefrog
- win32 get_console_size from Alexander
53
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
54
# We can cope without it; use a separate variable to help pyflakes
1185.16.86 by mbp at sourcefrog
- win32 get_console_size from Alexander
55
try:
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
56
    import ctypes
57
    has_ctypes = True
1185.16.86 by mbp at sourcefrog
- win32 get_console_size from Alexander
58
except ImportError:
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
59
    has_ctypes = False
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
60
else:
61
    if winver == 'Windows 98':
62
        create_buffer = ctypes.create_string_buffer
63
        suffix = 'A'
64
    else:
65
        create_buffer = ctypes.create_unicode_buffer
66
        suffix = 'W'
3023.1.2 by Alexander Belchenko
Martin's review.
67
try:
68
    import win32file
69
    has_win32file = True
70
except ImportError:
71
    has_win32file = False
3626.1.2 by skip
win32utils.get_host_name() uses 'mbcs' encoding when decoding env vars
72
try:
73
    import win32api
74
    has_win32api = True
75
except ImportError:
76
    has_win32api = False
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
77
3638.4.1 by Mark Hammond
Add win32utils.get_local_appdata_location() so bzr and plugins can
78
# pulling in win32com.shell is a bit of overhead, and normally we don't need
79
# it as ctypes is preferred and common.  lazy_imports and "optional"
80
# modules don't work well, so we do our own lazy thing...
81
has_win32com_shell = None # Set to True or False once we know for sure...
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
82
83
# Special Win32 API constants
84
# Handles of std streams
1704.2.3 by Martin Pool
(win32) Detect terminal width using GetConsoleScreenBufferInfo (Alexander)
85
WIN32_STDIN_HANDLE = -10
86
WIN32_STDOUT_HANDLE = -11
87
WIN32_STDERR_HANDLE = -12
88
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
89
# CSIDL constants (from MSDN 2003)
90
CSIDL_APPDATA = 0x001A      # Application Data folder
3638.4.10 by Aaron Bentley
Correct spelling of 'Application Data'
91
CSIDL_LOCAL_APPDATA = 0x001c# <user name>\Local Settings\Application Data (non roaming)
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
92
CSIDL_PERSONAL = 0x0005     # My Documents folder
93
94
# from winapi C headers
95
MAX_PATH = 260
96
UNLEN = 256
97
MAX_COMPUTERNAME_LENGTH = 31
98
1704.2.3 by Martin Pool
(win32) Detect terminal width using GetConsoleScreenBufferInfo (Alexander)
99
1185.16.86 by mbp at sourcefrog
- win32 get_console_size from Alexander
100
def get_console_size(defaultx=80, defaulty=25):
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
101
    """Return size of current console.
102
103
    This function try to determine actual size of current working
104
    console window and return tuple (sizex, sizey) if success,
105
    or default size (defaultx, defaulty) otherwise.
106
    """
107
    if not has_ctypes:
108
        # no ctypes is found
109
        return (defaultx, defaulty)
110
111
    # To avoid problem with redirecting output via pipe
112
    # need to use stderr instead of stdout
113
    h = ctypes.windll.kernel32.GetStdHandle(WIN32_STDERR_HANDLE)
114
    csbi = ctypes.create_string_buffer(22)
115
    res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
116
117
    if res:
118
        (bufx, bufy, curx, cury, wattr,
1185.16.86 by mbp at sourcefrog
- win32 get_console_size from Alexander
119
        left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
120
        sizex = right - left + 1
121
        sizey = bottom - top + 1
122
        return (sizex, sizey)
123
    else:
124
        return (defaultx, defaulty)
125
126
3638.4.1 by Mark Hammond
Add win32utils.get_local_appdata_location() so bzr and plugins can
127
def _get_sh_special_folder_path(csidl):
128
    """Call SHGetSpecialFolderPathW if available, or return None.
129
    
130
    Result is always unicode (or None).
131
    """
132
    if has_ctypes:
133
        try:
134
            SHGetSpecialFolderPath = \
135
                ctypes.windll.shell32.SHGetSpecialFolderPathW
136
        except AttributeError:
137
            pass
138
        else:
139
            buf = ctypes.create_unicode_buffer(MAX_PATH)
140
            if SHGetSpecialFolderPath(None,buf,csidl,0):
141
                return buf.value
142
143
    global has_win32com_shell
144
    if has_win32com_shell is None:
145
        try:
146
            from win32com.shell import shell
147
            has_win32com_shell = True
148
        except ImportError:
149
            has_win32com_shell = False
150
    if has_win32com_shell:
151
        # still need to bind the name locally, but this is fast.
152
        from win32com.shell import shell
153
        try:
154
            return shell.SHGetSpecialFolderPath(0, csidl, 0)
155
        except shell.error:
156
            # possibly E_NOTIMPL meaning we can't load the function pointer,
157
            # or E_FAIL meaning the function failed - regardless, just ignore it
158
            pass
159
    return None
160
161
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
162
def get_appdata_location():
163
    """Return Application Data location.
164
    Return None if we cannot obtain location.
165
3638.4.1 by Mark Hammond
Add win32utils.get_local_appdata_location() so bzr and plugins can
166
    Windows defines two 'Application Data' folders per user - a 'roaming'
167
    one that moves with the user as they logon to different machines, and
168
    a 'local' one that stays local to the machine.  This returns the 'roaming'
169
    directory, and thus is suitable for storing user-preferences, etc.
170
171
    Returned value can be unicode or plain string.
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
172
    To convert plain string to unicode use
3224.5.4 by Andrew Bennetts
Fix test suite, mainly weeding out uses of bzrlib.user_encoding.
173
    s.decode(osutils.get_user_encoding())
3638.4.2 by Mark Hammond
Add a reference to bug 262874 noting 'mbcs' may be the correct encoding.
174
    (XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
175
    """
3638.4.1 by Mark Hammond
Add win32utils.get_local_appdata_location() so bzr and plugins can
176
    appdata = _get_sh_special_folder_path(CSIDL_APPDATA)
177
    if appdata:
178
        return appdata
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
179
    # from env variable
180
    appdata = os.environ.get('APPDATA')
181
    if appdata:
182
        return appdata
183
    # if we fall to this point we on win98
184
    # at least try C:/WINDOWS/Application Data
185
    windir = os.environ.get('windir')
186
    if windir:
187
        appdata = os.path.join(windir, 'Application Data')
188
        if os.path.isdir(appdata):
189
            return appdata
190
    # did not find anything
191
    return None
192
193
3638.4.1 by Mark Hammond
Add win32utils.get_local_appdata_location() so bzr and plugins can
194
def get_local_appdata_location():
195
    """Return Local Application Data location.
196
    Return the same as get_appdata_location() if we cannot obtain location.
197
198
    Windows defines two 'Application Data' folders per user - a 'roaming'
199
    one that moves with the user as they logon to different machines, and
200
    a 'local' one that stays local to the machine.  This returns the 'local'
201
    directory, and thus is suitable for caches, temp files and other things
202
    which don't need to move with the user.
203
204
    Returned value can be unicode or plain string.
205
    To convert plain string to unicode use
206
    s.decode(bzrlib.user_encoding)
3638.4.2 by Mark Hammond
Add a reference to bug 262874 noting 'mbcs' may be the correct encoding.
207
    (XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
3638.4.1 by Mark Hammond
Add win32utils.get_local_appdata_location() so bzr and plugins can
208
    """
209
    local = _get_sh_special_folder_path(CSIDL_LOCAL_APPDATA)
210
    if local:
211
        return local
212
    # Vista supplies LOCALAPPDATA, but XP and earlier do not.
213
    local = os.environ.get('LOCALAPPDATA')
214
    if local:
215
        return local
216
    return get_appdata_location()
217
218
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
219
def get_home_location():
220
    """Return user's home location.
221
    Assume on win32 it's the <My Documents> folder.
222
    If location cannot be obtained return system drive root,
223
    i.e. C:\
224
225
    Returned value can be unicode or plain sring.
226
    To convert plain string to unicode use
3224.5.4 by Andrew Bennetts
Fix test suite, mainly weeding out uses of bzrlib.user_encoding.
227
    s.decode(osutils.get_user_encoding())
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
228
    """
3638.4.1 by Mark Hammond
Add win32utils.get_local_appdata_location() so bzr and plugins can
229
    home = _get_sh_special_folder_path(CSIDL_PERSONAL)
230
    if home:
231
        return home
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
232
    # try for HOME env variable
233
    home = os.path.expanduser('~')
234
    if home != '~':
235
        return home
236
    # at least return windows root directory
237
    windir = os.environ.get('windir')
238
    if windir:
2610.1.1 by Martin Pool
Fix get_home_location on Win98 (gzlist,r=john,r=alexander)
239
        return os.path.splitdrive(windir)[0] + '/'
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
240
    # otherwise C:\ is good enough for 98% users
241
    return 'C:/'
242
243
244
def get_user_name():
245
    """Return user name as login name.
246
    If name cannot be obtained return None.
247
248
    Returned value can be unicode or plain sring.
249
    To convert plain string to unicode use
3224.5.4 by Andrew Bennetts
Fix test suite, mainly weeding out uses of bzrlib.user_encoding.
250
    s.decode(osutils.get_user_encoding())
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
251
    """
252
    if has_ctypes:
253
        try:
254
            advapi32 = ctypes.windll.advapi32
255
            GetUserName = getattr(advapi32, 'GetUserName'+suffix)
256
        except AttributeError:
257
            pass
258
        else:
259
            buf = create_buffer(UNLEN+1)
260
            n = ctypes.c_int(UNLEN+1)
261
            if GetUserName(buf, ctypes.byref(n)):
262
                return buf.value
263
    # otherwise try env variables
264
    return os.environ.get('USERNAME', None)
265
266
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
267
# 1 == ComputerNameDnsHostname, which returns "The DNS host name of the local
268
# computer or the cluster associated with the local computer."
269
_WIN32_ComputerNameDnsHostname = 1
270
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
271
def get_host_name():
272
    """Return host machine name.
273
    If name cannot be obtained return None.
274
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
275
    :return: A unicode string representing the host name. On win98, this may be
276
        a plain string as win32 api doesn't support unicode.
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
277
    """
3626.1.2 by skip
win32utils.get_host_name() uses 'mbcs' encoding when decoding env vars
278
    if has_win32api:
279
        try:
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
280
            return win32api.GetComputerNameEx(_WIN32_ComputerNameDnsHostname)
3626.1.2 by skip
win32utils.get_host_name() uses 'mbcs' encoding when decoding env vars
281
        except (NotImplementedError, win32api.error):
282
            # NotImplemented will happen on win9x...
283
            pass
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
284
    if has_ctypes:
285
        try:
286
            kernel32 = ctypes.windll.kernel32
287
        except AttributeError:
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
288
            pass # Missing the module we need
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
289
        else:
290
            buf = create_buffer(MAX_COMPUTERNAME_LENGTH+1)
291
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
292
293
            # Try GetComputerNameEx which gives a proper Unicode hostname
294
            GetComputerNameEx = getattr(kernel32, 'GetComputerNameEx'+suffix,
295
                                        None)
296
            if (GetComputerNameEx is not None
297
                and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
298
                                      buf, ctypes.byref(n))):
299
                return buf.value
300
301
            # Try GetComputerName in case GetComputerNameEx wasn't found
302
            # It returns the NETBIOS name, which isn't as good, but still ok.
303
            # The first GetComputerNameEx might have changed 'n', so reset it
304
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
305
            GetComputerName = getattr(kernel32, 'GetComputerName'+suffix,
306
                                      None)
307
            if (GetComputerName is not None
308
                and GetComputerName(buf, ctypes.byref(n))):
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
309
                return buf.value
3626.1.2 by skip
win32utils.get_host_name() uses 'mbcs' encoding when decoding env vars
310
    # otherwise try env variables, which will be 'mbcs' encoded
311
    # on Windows (Python doesn't expose the native win32 unicode environment)
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
312
    # According to this:
313
    # http://msdn.microsoft.com/en-us/library/aa246807.aspx
314
    # environment variables should always be encoded in 'mbcs'.
3626.1.2 by skip
win32utils.get_host_name() uses 'mbcs' encoding when decoding env vars
315
    try:
316
        return os.environ['COMPUTERNAME'].decode("mbcs")
317
    except KeyError:
318
        return None
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
319
320
321
def _ensure_unicode(s):
3794.1.1 by Martin Pool
Update osutils imports to fix setup.py on Windows
322
    from bzrlib import osutils
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
323
    if s and type(s) != unicode:
3788.1.1 by John Arbash Meinel
Fix a missing import
324
        from bzrlib import osutils
3224.5.4 by Andrew Bennetts
Fix test suite, mainly weeding out uses of bzrlib.user_encoding.
325
        s = s.decode(osutils.get_user_encoding())
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
326
    return s
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
327
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
328
329
def get_appdata_location_unicode():
330
    return _ensure_unicode(get_appdata_location())
331
332
def get_home_location_unicode():
333
    return _ensure_unicode(get_home_location())
334
335
def get_user_name_unicode():
336
    return _ensure_unicode(get_user_name())
337
338
def get_host_name_unicode():
339
    return _ensure_unicode(get_host_name())
2568.2.2 by Robert Collins
* New method ``_glob_expand_file_list_if_needed`` on the ``Command`` class
340
341
2617.5.8 by Kuno Meyer
Extended tests for unicode chars outside of the iso-8859-* range
342
def _ensure_with_dir(path):
343
    if not os.path.split(path)[0] or path.startswith(u'*') or path.startswith(u'?'):
344
        return u'./' + path, True
345
    else:
346
        return path, False
347
    
348
def _undo_ensure_with_dir(path, corrected):
349
    if corrected:
350
        return path[2:]
351
    else:
352
        return path
353
354
355
2598.3.1 by Kuno Meyer
fix method rename glob_expand_for_win32 -> win32utils.glob_expand
356
def glob_expand(file_list):
2568.2.2 by Robert Collins
* New method ``_glob_expand_file_list_if_needed`` on the ``Command`` class
357
    """Replacement for glob expansion by the shell.
358
359
    Win32's cmd.exe does not do glob expansion (eg ``*.py``), so we do our own
360
    here.
361
362
    :param file_list: A list of filenames which may include shell globs.
363
    :return: An expanded list of filenames.
364
365
    Introduced in bzrlib 0.18.
366
    """
367
    if not file_list:
368
        return []
369
    import glob
370
    expanded_file_list = []
371
    for possible_glob in file_list:
2617.5.8 by Kuno Meyer
Extended tests for unicode chars outside of the iso-8859-* range
372
        
373
        # work around bugs in glob.glob()
374
        # - Python bug #1001604 ("glob doesn't return unicode with ...")
375
        # - failing expansion for */* with non-iso-8859-* chars
376
        possible_glob, corrected = _ensure_with_dir(possible_glob)
2568.2.2 by Robert Collins
* New method ``_glob_expand_file_list_if_needed`` on the ``Command`` class
377
        glob_files = glob.glob(possible_glob)
378
379
        if glob_files == []:
380
            # special case to let the normal code path handle
381
            # files that do not exists
2617.5.8 by Kuno Meyer
Extended tests for unicode chars outside of the iso-8859-* range
382
            expanded_file_list.append(
383
                _undo_ensure_with_dir(possible_glob, corrected))
2568.2.2 by Robert Collins
* New method ``_glob_expand_file_list_if_needed`` on the ``Command`` class
384
        else:
2617.5.8 by Kuno Meyer
Extended tests for unicode chars outside of the iso-8859-* range
385
            glob_files = [_undo_ensure_with_dir(elem, corrected) for elem in glob_files]
2568.2.2 by Robert Collins
* New method ``_glob_expand_file_list_if_needed`` on the ``Command`` class
386
            expanded_file_list += glob_files
2617.5.8 by Kuno Meyer
Extended tests for unicode chars outside of the iso-8859-* range
387
            
388
    return [elem.replace(u'\\', u'/') for elem in expanded_file_list] 
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
389
390
391
def get_app_path(appname):
392
    """Look up in Windows registry for full path to application executable.
393
    Typicaly, applications create subkey with their basename
394
    in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\
395
396
    :param  appname:    name of application (if no filename extension
397
                        is specified, .exe used)
398
    :return:    full path to aplication executable from registry,
399
                or appname itself if nothing found.
400
    """
2681.4.3 by Alexander Belchenko
move import _winreg into function get_app_path to avoid ImportError on non-win32 platforms
401
    import _winreg
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
402
    try:
403
        hkey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
404
                               r'SOFTWARE\Microsoft\Windows'
405
                               r'\CurrentVersion\App Paths')
406
    except EnvironmentError:
407
        return appname
408
409
    basename = appname
410
    if not os.path.splitext(basename)[1]:
411
        basename = appname + '.exe'
412
    try:
413
        try:
414
            fullpath = _winreg.QueryValue(hkey, basename)
415
        except WindowsError:
416
            fullpath = appname
417
    finally:
418
        _winreg.CloseKey(hkey)
419
420
    return fullpath
3023.1.2 by Alexander Belchenko
Martin's review.
421
422
423
def set_file_attr_hidden(path):
424
    """Set file attributes to hidden if possible"""
425
    if has_win32file:
426
        win32file.SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)