~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/win32console.py

MergeĀ inĀ upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 Canonical Ltd
2
 
#
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
 
 
17
 
"""Win32-specific helper functions
18
 
 
19
 
Only one dependency: ctypes should be installed.
20
 
"""
21
 
 
22
 
import os
 
1
 
 
2
"""
 
3
Set of functions to work with console on Windows.
 
4
Author: Alexander Belchenko (e-mail: bialix AT ukr.net)
 
5
License: Public domain
 
6
"""
 
7
 
23
8
import struct
24
 
import sys
25
 
 
26
 
 
27
 
# Windows version
28
 
if sys.platform == 'win32':
29
 
    _major,_minor,_build,_platform,_text = sys.getwindowsversion()
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
49
 
        winver = 'Windows 98'
50
 
else:
51
 
    winver = None
52
 
 
53
 
 
54
 
# We can cope without it; use a separate variable to help pyflakes
55
 
try:
56
 
    import ctypes
57
 
    has_ctypes = True
58
 
except ImportError:
59
 
    has_ctypes = False
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
 
try:
68
 
    import win32file
69
 
    has_win32file = True
70
 
except ImportError:
71
 
    has_win32file = False
72
 
try:
73
 
    import win32api
74
 
    has_win32api = True
75
 
except ImportError:
76
 
    has_win32api = False
77
 
 
78
 
 
79
 
# Special Win32 API constants
80
 
# Handles of std streams
81
 
WIN32_STDIN_HANDLE = -10
82
 
WIN32_STDOUT_HANDLE = -11
83
 
WIN32_STDERR_HANDLE = -12
84
 
 
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
 
9
 
 
10
try:
 
11
   import ctypes
 
12
except ImportError:
 
13
   ctypes = None
93
14
 
94
15
 
95
16
def get_console_size(defaultx=80, defaulty=25):
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,
 
17
   """ Return size of current console.
 
18
 
 
19
   This function try to determine actual size of current working
 
20
   console window and return tuple (sizex, sizey) if success,
 
21
   or default size (defaultx, defaulty) otherwise.
 
22
 
 
23
   Dependencies: ctypes should be installed.
 
24
   """
 
25
   if ctypes is None:
 
26
       # no ctypes is found
 
27
       return (defaultx, defaulty)
 
28
 
 
29
   h = ctypes.windll.kernel32.GetStdHandle(-11)
 
30
   csbi = ctypes.create_string_buffer(22)
 
31
   res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
 
32
 
 
33
   if res:
 
34
       (bufx, bufy, curx, cury, wattr,
114
35
        left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
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:
182
 
        return os.path.splitdrive(windir)[0] + '/'
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
 
 
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
 
 
214
 
def get_host_name():
215
 
    """Return host machine name.
216
 
    If name cannot be obtained return None.
217
 
 
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.
220
 
    """
221
 
    if has_win32api:
222
 
        try:
223
 
            return win32api.GetComputerNameEx(_WIN32_ComputerNameDnsHostname)
224
 
        except (NotImplementedError, win32api.error):
225
 
            # NotImplemented will happen on win9x...
226
 
            pass
227
 
    if has_ctypes:
228
 
        try:
229
 
            kernel32 = ctypes.windll.kernel32
230
 
        except AttributeError:
231
 
            pass # Missing the module we need
232
 
        else:
233
 
            buf = create_buffer(MAX_COMPUTERNAME_LENGTH+1)
234
 
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
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))):
252
 
                return buf.value
253
 
    # otherwise try env variables, which will be 'mbcs' encoded
254
 
    # on Windows (Python doesn't expose the native win32 unicode environment)
255
 
    # According to this:
256
 
    # http://msdn.microsoft.com/en-us/library/aa246807.aspx
257
 
    # environment variables should always be encoded in 'mbcs'.
258
 
    try:
259
 
        return os.environ['COMPUTERNAME'].decode("mbcs")
260
 
    except KeyError:
261
 
        return None
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
269
 
 
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())
282
 
 
283
 
 
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
 
 
298
 
def glob_expand(file_list):
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:
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)
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
324
 
            expanded_file_list.append(
325
 
                _undo_ensure_with_dir(possible_glob, corrected))
326
 
        else:
327
 
            glob_files = [_undo_ensure_with_dir(elem, corrected) for elem in glob_files]
328
 
            expanded_file_list += glob_files
329
 
            
330
 
    return [elem.replace(u'\\', u'/') for elem in expanded_file_list] 
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
 
    """
343
 
    import _winreg
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
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)
 
36
       sizex = right - left + 1
 
37
       sizey = bottom - top + 1
 
38
       return (sizex, sizey)
 
39
   else:
 
40
       return (defaultx, defaulty)