~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
78
79
# Special Win32 API constants
80
# Handles of std streams
1704.2.3 by Martin Pool
(win32) Detect terminal width using GetConsoleScreenBufferInfo (Alexander)
81
WIN32_STDIN_HANDLE = -10
82
WIN32_STDOUT_HANDLE = -11
83
WIN32_STDERR_HANDLE = -12
84
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
85
# CSIDL constants (from MSDN 2003)
86
CSIDL_APPDATA = 0x001A      # Application Data folder
87
CSIDL_PERSONAL = 0x0005     # My Documents folder
88
89
# from winapi C headers
90
MAX_PATH = 260
91
UNLEN = 256
92
MAX_COMPUTERNAME_LENGTH = 31
93
1704.2.3 by Martin Pool
(win32) Detect terminal width using GetConsoleScreenBufferInfo (Alexander)
94
1185.16.86 by mbp at sourcefrog
- win32 get_console_size from Alexander
95
def get_console_size(defaultx=80, defaulty=25):
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
96
    """Return size of current console.
97
98
    This function try to determine actual size of current working
99
    console window and return tuple (sizex, sizey) if success,
100
    or default size (defaultx, defaulty) otherwise.
101
    """
102
    if not has_ctypes:
103
        # no ctypes is found
104
        return (defaultx, defaulty)
105
106
    # To avoid problem with redirecting output via pipe
107
    # need to use stderr instead of stdout
108
    h = ctypes.windll.kernel32.GetStdHandle(WIN32_STDERR_HANDLE)
109
    csbi = ctypes.create_string_buffer(22)
110
    res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
111
112
    if res:
113
        (bufx, bufy, curx, cury, wattr,
1185.16.86 by mbp at sourcefrog
- win32 get_console_size from Alexander
114
        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
115
        sizex = right - left + 1
116
        sizey = bottom - top + 1
117
        return (sizex, sizey)
118
    else:
119
        return (defaultx, defaulty)
120
121
122
def get_appdata_location():
123
    """Return Application Data location.
124
    Return None if we cannot obtain location.
125
126
    Returned value can be unicode or plain sring.
127
    To convert plain string to unicode use
128
    s.decode(bzrlib.user_encoding)
129
    """
130
    if has_ctypes:
131
        try:
132
            SHGetSpecialFolderPath = \
133
                ctypes.windll.shell32.SHGetSpecialFolderPathW
134
        except AttributeError:
135
            pass
136
        else:
137
            buf = ctypes.create_unicode_buffer(MAX_PATH)
138
            if SHGetSpecialFolderPath(None,buf,CSIDL_APPDATA,0):
139
                return buf.value
140
    # from env variable
141
    appdata = os.environ.get('APPDATA')
142
    if appdata:
143
        return appdata
144
    # if we fall to this point we on win98
145
    # at least try C:/WINDOWS/Application Data
146
    windir = os.environ.get('windir')
147
    if windir:
148
        appdata = os.path.join(windir, 'Application Data')
149
        if os.path.isdir(appdata):
150
            return appdata
151
    # did not find anything
152
    return None
153
154
155
def get_home_location():
156
    """Return user's home location.
157
    Assume on win32 it's the <My Documents> folder.
158
    If location cannot be obtained return system drive root,
159
    i.e. C:\
160
161
    Returned value can be unicode or plain sring.
162
    To convert plain string to unicode use
163
    s.decode(bzrlib.user_encoding)
164
    """
165
    if has_ctypes:
166
        try:
167
            SHGetSpecialFolderPath = \
168
                ctypes.windll.shell32.SHGetSpecialFolderPathW
169
        except AttributeError:
170
            pass
171
        else:
172
            buf = ctypes.create_unicode_buffer(MAX_PATH)
173
            if SHGetSpecialFolderPath(None,buf,CSIDL_PERSONAL,0):
174
                return buf.value
175
    # try for HOME env variable
176
    home = os.path.expanduser('~')
177
    if home != '~':
178
        return home
179
    # at least return windows root directory
180
    windir = os.environ.get('windir')
181
    if windir:
2610.1.1 by Martin Pool
Fix get_home_location on Win98 (gzlist,r=john,r=alexander)
182
        return os.path.splitdrive(windir)[0] + '/'
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
183
    # otherwise C:\ is good enough for 98% users
184
    return 'C:/'
185
186
187
def get_user_name():
188
    """Return user name as login name.
189
    If name cannot be obtained return None.
190
191
    Returned value can be unicode or plain sring.
192
    To convert plain string to unicode use
193
    s.decode(bzrlib.user_encoding)
194
    """
195
    if has_ctypes:
196
        try:
197
            advapi32 = ctypes.windll.advapi32
198
            GetUserName = getattr(advapi32, 'GetUserName'+suffix)
199
        except AttributeError:
200
            pass
201
        else:
202
            buf = create_buffer(UNLEN+1)
203
            n = ctypes.c_int(UNLEN+1)
204
            if GetUserName(buf, ctypes.byref(n)):
205
                return buf.value
206
    # otherwise try env variables
207
    return os.environ.get('USERNAME', None)
208
209
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
210
# 1 == ComputerNameDnsHostname, which returns "The DNS host name of the local
211
# computer or the cluster associated with the local computer."
212
_WIN32_ComputerNameDnsHostname = 1
213
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
214
def get_host_name():
215
    """Return host machine name.
216
    If name cannot be obtained return None.
217
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
218
    :return: A unicode string representing the host name. On win98, this may be
219
        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
220
    """
3626.1.2 by skip
win32utils.get_host_name() uses 'mbcs' encoding when decoding env vars
221
    if has_win32api:
222
        try:
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
223
            return win32api.GetComputerNameEx(_WIN32_ComputerNameDnsHostname)
3626.1.2 by skip
win32utils.get_host_name() uses 'mbcs' encoding when decoding env vars
224
        except (NotImplementedError, win32api.error):
225
            # NotImplemented will happen on win9x...
226
            pass
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
227
    if has_ctypes:
228
        try:
229
            kernel32 = ctypes.windll.kernel32
230
        except AttributeError:
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
231
            pass # Missing the module we need
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
232
        else:
233
            buf = create_buffer(MAX_COMPUTERNAME_LENGTH+1)
234
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
235
236
            # Try GetComputerNameEx which gives a proper Unicode hostname
237
            GetComputerNameEx = getattr(kernel32, 'GetComputerNameEx'+suffix,
238
                                        None)
239
            if (GetComputerNameEx is not None
240
                and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
241
                                      buf, ctypes.byref(n))):
242
                return buf.value
243
244
            # Try GetComputerName in case GetComputerNameEx wasn't found
245
            # It returns the NETBIOS name, which isn't as good, but still ok.
246
            # The first GetComputerNameEx might have changed 'n', so reset it
247
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
248
            GetComputerName = getattr(kernel32, 'GetComputerName'+suffix,
249
                                      None)
250
            if (GetComputerName is not None
251
                and GetComputerName(buf, ctypes.byref(n))):
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
252
                return buf.value
3626.1.2 by skip
win32utils.get_host_name() uses 'mbcs' encoding when decoding env vars
253
    # otherwise try env variables, which will be 'mbcs' encoded
254
    # on Windows (Python doesn't expose the native win32 unicode environment)
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
255
    # According to this:
256
    # http://msdn.microsoft.com/en-us/library/aa246807.aspx
257
    # environment variables should always be encoded in 'mbcs'.
3626.1.2 by skip
win32utils.get_host_name() uses 'mbcs' encoding when decoding env vars
258
    try:
259
        return os.environ['COMPUTERNAME'].decode("mbcs")
260
    except KeyError:
261
        return None
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
262
263
264
def _ensure_unicode(s):
265
    if s and type(s) != unicode:
266
        import bzrlib
267
        s = s.decode(bzrlib.user_encoding)
268
    return s
3626.1.3 by John Arbash Meinel
Use GetComputerNameEx from ctypes when available.
269
2245.4.1 by Alexander Belchenko
win32utils: Windows-specific functions that use Win32 API via ctypes
270
271
def get_appdata_location_unicode():
272
    return _ensure_unicode(get_appdata_location())
273
274
def get_home_location_unicode():
275
    return _ensure_unicode(get_home_location())
276
277
def get_user_name_unicode():
278
    return _ensure_unicode(get_user_name())
279
280
def get_host_name_unicode():
281
    return _ensure_unicode(get_host_name())
2568.2.2 by Robert Collins
* New method ``_glob_expand_file_list_if_needed`` on the ``Command`` class
282
283
2617.5.8 by Kuno Meyer
Extended tests for unicode chars outside of the iso-8859-* range
284
def _ensure_with_dir(path):
285
    if not os.path.split(path)[0] or path.startswith(u'*') or path.startswith(u'?'):
286
        return u'./' + path, True
287
    else:
288
        return path, False
289
    
290
def _undo_ensure_with_dir(path, corrected):
291
    if corrected:
292
        return path[2:]
293
    else:
294
        return path
295
296
297
2598.3.1 by Kuno Meyer
fix method rename glob_expand_for_win32 -> win32utils.glob_expand
298
def glob_expand(file_list):
2568.2.2 by Robert Collins
* New method ``_glob_expand_file_list_if_needed`` on the ``Command`` class
299
    """Replacement for glob expansion by the shell.
300
301
    Win32's cmd.exe does not do glob expansion (eg ``*.py``), so we do our own
302
    here.
303
304
    :param file_list: A list of filenames which may include shell globs.
305
    :return: An expanded list of filenames.
306
307
    Introduced in bzrlib 0.18.
308
    """
309
    if not file_list:
310
        return []
311
    import glob
312
    expanded_file_list = []
313
    for possible_glob in file_list:
2617.5.8 by Kuno Meyer
Extended tests for unicode chars outside of the iso-8859-* range
314
        
315
        # work around bugs in glob.glob()
316
        # - Python bug #1001604 ("glob doesn't return unicode with ...")
317
        # - failing expansion for */* with non-iso-8859-* chars
318
        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
319
        glob_files = glob.glob(possible_glob)
320
321
        if glob_files == []:
322
            # special case to let the normal code path handle
323
            # files that do not exists
2617.5.8 by Kuno Meyer
Extended tests for unicode chars outside of the iso-8859-* range
324
            expanded_file_list.append(
325
                _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
326
        else:
2617.5.8 by Kuno Meyer
Extended tests for unicode chars outside of the iso-8859-* range
327
            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
328
            expanded_file_list += glob_files
2617.5.8 by Kuno Meyer
Extended tests for unicode chars outside of the iso-8859-* range
329
            
330
    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
331
332
333
def get_app_path(appname):
334
    """Look up in Windows registry for full path to application executable.
335
    Typicaly, applications create subkey with their basename
336
    in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\
337
338
    :param  appname:    name of application (if no filename extension
339
                        is specified, .exe used)
340
    :return:    full path to aplication executable from registry,
341
                or appname itself if nothing found.
342
    """
2681.4.3 by Alexander Belchenko
move import _winreg into function get_app_path to avoid ImportError on non-win32 platforms
343
    import _winreg
2681.4.1 by Alexander Belchenko
win32: looking for full path of mail client executable in registry
344
    try:
345
        hkey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
346
                               r'SOFTWARE\Microsoft\Windows'
347
                               r'\CurrentVersion\App Paths')
348
    except EnvironmentError:
349
        return appname
350
351
    basename = appname
352
    if not os.path.splitext(basename)[1]:
353
        basename = appname + '.exe'
354
    try:
355
        try:
356
            fullpath = _winreg.QueryValue(hkey, basename)
357
        except WindowsError:
358
            fullpath = appname
359
    finally:
360
        _winreg.CloseKey(hkey)
361
362
    return fullpath
3023.1.2 by Alexander Belchenko
Martin's review.
363
364
365
def set_file_attr_hidden(path):
366
    """Set file attributes to hidden if possible"""
367
    if has_win32file:
368
        win32file.SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)