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