~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/win32utils.py

  • Committer: Robert Collins
  • Date: 2005-08-23 06:52:09 UTC
  • mto: (974.1.50) (1185.1.10) (1092.3.1)
  • mto: This revision was merged to the branch mainline in revision 1139.
  • Revision ID: robertc@robertcollins.net-20050823065209-81cd5962c401751b
move io redirection into each test case from the global runner

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
23
 
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
 
# 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...
82
 
 
83
 
# Special Win32 API constants
84
 
# Handles of std streams
85
 
WIN32_STDIN_HANDLE = -10
86
 
WIN32_STDOUT_HANDLE = -11
87
 
WIN32_STDERR_HANDLE = -12
88
 
 
89
 
# CSIDL constants (from MSDN 2003)
90
 
CSIDL_APPDATA = 0x001A      # Application Data folder
91
 
CSIDL_LOCAL_APPDATA = 0x001c# <user name>\Local Settings\Application Data (non roaming)
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
 
 
99
 
 
100
 
def get_console_size(defaultx=80, defaulty=25):
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,
119
 
        left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
120
 
        sizex = right - left + 1
121
 
        sizey = bottom - top + 1
122
 
        return (sizex, sizey)
123
 
    else:
124
 
        return (defaultx, defaulty)
125
 
 
126
 
 
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
 
 
162
 
def get_appdata_location():
163
 
    """Return Application Data location.
164
 
    Return None if we cannot obtain location.
165
 
 
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.
172
 
    To convert plain string to unicode use
173
 
    s.decode(osutils.get_user_encoding())
174
 
    (XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
175
 
    """
176
 
    appdata = _get_sh_special_folder_path(CSIDL_APPDATA)
177
 
    if appdata:
178
 
        return appdata
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
 
 
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)
207
 
    (XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
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
 
 
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
227
 
    s.decode(osutils.get_user_encoding())
228
 
    """
229
 
    home = _get_sh_special_folder_path(CSIDL_PERSONAL)
230
 
    if home:
231
 
        return home
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:
239
 
        return os.path.splitdrive(windir)[0] + '/'
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
250
 
    s.decode(osutils.get_user_encoding())
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
 
 
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
 
 
271
 
def get_host_name():
272
 
    """Return host machine name.
273
 
    If name cannot be obtained return None.
274
 
 
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.
277
 
    """
278
 
    if has_win32api:
279
 
        try:
280
 
            return win32api.GetComputerNameEx(_WIN32_ComputerNameDnsHostname)
281
 
        except (NotImplementedError, win32api.error):
282
 
            # NotImplemented will happen on win9x...
283
 
            pass
284
 
    if has_ctypes:
285
 
        try:
286
 
            kernel32 = ctypes.windll.kernel32
287
 
        except AttributeError:
288
 
            pass # Missing the module we need
289
 
        else:
290
 
            buf = create_buffer(MAX_COMPUTERNAME_LENGTH+1)
291
 
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
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))):
309
 
                return buf.value
310
 
    # otherwise try env variables, which will be 'mbcs' encoded
311
 
    # on Windows (Python doesn't expose the native win32 unicode environment)
312
 
    # According to this:
313
 
    # http://msdn.microsoft.com/en-us/library/aa246807.aspx
314
 
    # environment variables should always be encoded in 'mbcs'.
315
 
    try:
316
 
        return os.environ['COMPUTERNAME'].decode("mbcs")
317
 
    except KeyError:
318
 
        return None
319
 
 
320
 
 
321
 
def _ensure_unicode(s):
322
 
    from bzrlib import osutils
323
 
    if s and type(s) != unicode:
324
 
        from bzrlib import osutils
325
 
        s = s.decode(osutils.get_user_encoding())
326
 
    return s
327
 
 
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())
340
 
 
341
 
 
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
 
 
356
 
def glob_expand(file_list):
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:
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)
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
382
 
            expanded_file_list.append(
383
 
                _undo_ensure_with_dir(possible_glob, corrected))
384
 
        else:
385
 
            glob_files = [_undo_ensure_with_dir(elem, corrected) for elem in glob_files]
386
 
            expanded_file_list += glob_files
387
 
            
388
 
    return [elem.replace(u'\\', u'/') for elem in expanded_file_list] 
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
 
    """
401
 
    import _winreg
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
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)