/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/win32utils.py

  • Committer: Jonathan Lange
  • Date: 2009-12-09 09:20:42 UTC
  • mfrom: (4881 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4907.
  • Revision ID: jml@canonical.com-20091209092042-s2zgqcf8f39yzxpj
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
Only one dependency: ctypes should be installed.
20
20
"""
21
21
 
 
22
import glob
22
23
import os
 
24
import re
 
25
import shlex
23
26
import struct
 
27
import StringIO
24
28
import sys
25
29
 
26
30
 
66
70
        suffix = 'W'
67
71
try:
68
72
    import win32file
 
73
    import pywintypes
69
74
    has_win32file = True
70
75
except ImportError:
71
76
    has_win32file = False
177
182
        return (defaultx, defaulty)
178
183
 
179
184
    # To avoid problem with redirecting output via pipe
180
 
    # need to use stderr instead of stdout
 
185
    # we need to use stderr instead of stdout
181
186
    h = ctypes.windll.kernel32.GetStdHandle(WIN32_STDERR_HANDLE)
182
187
    csbi = ctypes.create_string_buffer(22)
183
188
    res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
421
426
 
422
427
 
423
428
 
 
429
def glob_one(possible_glob):
 
430
    """Same as glob.glob().
 
431
 
 
432
    work around bugs in glob.glob()
 
433
    - Python bug #1001604 ("glob doesn't return unicode with ...")
 
434
    - failing expansion for */* with non-iso-8859-* chars
 
435
    """
 
436
    corrected_glob, corrected = _ensure_with_dir(possible_glob)
 
437
    glob_files = glob.glob(corrected_glob)
 
438
 
 
439
    if not glob_files:
 
440
        # special case to let the normal code path handle
 
441
        # files that do not exist, etc.
 
442
        glob_files = [possible_glob]
 
443
    elif corrected:
 
444
        glob_files = [_undo_ensure_with_dir(elem, corrected)
 
445
                      for elem in glob_files]
 
446
    return [elem.replace(u'\\', u'/') for elem in glob_files]
 
447
 
 
448
 
424
449
def glob_expand(file_list):
425
450
    """Replacement for glob expansion by the shell.
426
451
 
434
459
    """
435
460
    if not file_list:
436
461
        return []
437
 
    import glob
438
462
    expanded_file_list = []
439
463
    for possible_glob in file_list:
440
 
        # work around bugs in glob.glob()
441
 
        # - Python bug #1001604 ("glob doesn't return unicode with ...")
442
 
        # - failing expansion for */* with non-iso-8859-* chars
443
 
        possible_glob, corrected = _ensure_with_dir(possible_glob)
444
 
        glob_files = glob.glob(possible_glob)
445
 
 
446
 
        if glob_files == []:
447
 
            # special case to let the normal code path handle
448
 
            # files that do not exists
449
 
            expanded_file_list.append(
450
 
                _undo_ensure_with_dir(possible_glob, corrected))
451
 
        else:
452
 
            glob_files = [_undo_ensure_with_dir(elem, corrected) for elem in glob_files]
453
 
            expanded_file_list += glob_files
454
 
 
455
 
    return [elem.replace(u'\\', u'/') for elem in expanded_file_list]
 
464
        expanded_file_list.extend(glob_one(possible_glob))
 
465
    return expanded_file_list
456
466
 
457
467
 
458
468
def get_app_path(appname):
499
509
def set_file_attr_hidden(path):
500
510
    """Set file attributes to hidden if possible"""
501
511
    if has_win32file:
502
 
        win32file.SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)
 
512
        if winver != 'Windows 98':
 
513
            SetFileAttributes = win32file.SetFileAttributesW
 
514
        else:
 
515
            SetFileAttributes = win32file.SetFileAttributes
 
516
        try:
 
517
            SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)
 
518
        except pywintypes.error, e:
 
519
            from bzrlib import trace
 
520
            trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
 
521
 
 
522
 
 
523
 
 
524
class UnicodeShlex(object):
 
525
    """This is a very simplified version of shlex.shlex.
 
526
 
 
527
    The main change is that it supports non-ascii input streams. The internal
 
528
    structure is quite simplified relative to shlex.shlex, since we aren't
 
529
    trying to handle multiple input streams, etc. In fact, we don't use a
 
530
    file-like api either.
 
531
    """
 
532
 
 
533
    def __init__(self, uni_string):
 
534
        self._input = uni_string
 
535
        self._input_iter = iter(self._input)
 
536
        self._whitespace_match = re.compile(u'\s').match
 
537
        self._word_match = re.compile(u'\S').match
 
538
        self._quote_chars = u'"'
 
539
        # self._quote_match = re.compile(u'[\'"]').match
 
540
        self._escape_match = lambda x: None # Never matches
 
541
        self._escape = '\\'
 
542
        # State can be
 
543
        #   ' ' - after whitespace, starting a new token
 
544
        #   'a' - after text, currently working on a token
 
545
        #   '"' - after ", currently in a "-delimited quoted section
 
546
        #   "\" - after '\', checking the next char
 
547
        self._state = ' '
 
548
        self._token = [] # Current token being parsed
 
549
 
 
550
    def _get_token(self):
 
551
        # Were there quote chars as part of this token?
 
552
        quoted = False
 
553
        quoted_state = None
 
554
        for nextchar in self._input_iter:
 
555
            if self._state == ' ':
 
556
                if self._whitespace_match(nextchar):
 
557
                    # if self._token: return token
 
558
                    continue
 
559
                elif nextchar in self._quote_chars:
 
560
                    self._state = nextchar # quoted state
 
561
                elif self._word_match(nextchar):
 
562
                    self._token.append(nextchar)
 
563
                    self._state = 'a'
 
564
                else:
 
565
                    raise AssertionError('wtttf?')
 
566
            elif self._state in self._quote_chars:
 
567
                quoted = True
 
568
                if nextchar == self._state: # End of quote
 
569
                    self._state = 'a' # posix allows 'foo'bar to translate to
 
570
                                      # foobar
 
571
                elif self._state == '"' and nextchar == self._escape:
 
572
                    quoted_state = self._state
 
573
                    self._state = nextchar
 
574
                else:
 
575
                    self._token.append(nextchar)
 
576
            elif self._state == self._escape:
 
577
                if nextchar == '\\':
 
578
                    self._token.append('\\')
 
579
                elif nextchar == '"':
 
580
                    self._token.append(nextchar)
 
581
                else:
 
582
                    self._token.append('\\' + nextchar)
 
583
                self._state = quoted_state
 
584
            elif self._state == 'a':
 
585
                if self._whitespace_match(nextchar):
 
586
                    if self._token:
 
587
                        break # emit this token
 
588
                    else:
 
589
                        continue # no token to emit
 
590
                elif nextchar in self._quote_chars:
 
591
                    # Start a new quoted section
 
592
                    self._state = nextchar
 
593
                # escape?
 
594
                elif (self._word_match(nextchar)
 
595
                      or nextchar in self._quote_chars
 
596
                      # or whitespace_split?
 
597
                      ):
 
598
                    self._token.append(nextchar)
 
599
                else:
 
600
                    raise AssertionError('state == "a", char: %r'
 
601
                                         % (nextchar,))
 
602
            else:
 
603
                raise AssertionError('unknown state: %r' % (self._state,))
 
604
        result = ''.join(self._token)
 
605
        self._token = []
 
606
        if not quoted and result == '':
 
607
            result = None
 
608
        return quoted, result
 
609
 
 
610
    def __iter__(self):
 
611
        return self
 
612
 
 
613
    def next(self):
 
614
        quoted, token = self._get_token()
 
615
        if token is None:
 
616
            raise StopIteration
 
617
        return quoted, token
 
618
 
 
619
 
 
620
def _command_line_to_argv(command_line):
 
621
    """Convert a Unicode command line into a set of argv arguments.
 
622
 
 
623
    This does wildcard expansion, etc. It is intended to make wildcards act
 
624
    closer to how they work in posix shells, versus how they work by default on
 
625
    Windows.
 
626
    """
 
627
    s = UnicodeShlex(command_line)
 
628
    # Now that we've split the content, expand globs
 
629
    # TODO: Use 'globbing' instead of 'glob.glob', this gives us stuff like
 
630
    #       '**/' style globs
 
631
    args = []
 
632
    for is_quoted, arg in s:
 
633
        if is_quoted or not glob.has_magic(arg):
 
634
            args.append(arg)
 
635
        else:
 
636
            args.extend(glob_one(arg))
 
637
    return args
503
638
 
504
639
 
505
640
if has_ctypes and winver != 'Windows 98':
511
646
        GetCommandLine = prototype(("GetCommandLineW",
512
647
                                    ctypes.windll.kernel32))
513
648
        prototype = ctypes.WINFUNCTYPE(POINTER(LPCWSTR), LPCWSTR, POINTER(INT))
514
 
        CommandLineToArgv = prototype(("CommandLineToArgvW",
515
 
                                       ctypes.windll.shell32))
516
 
        c = INT(0)
517
 
        pargv = CommandLineToArgv(GetCommandLine(), ctypes.byref(c))
 
649
        command_line = GetCommandLine()
518
650
        # Skip the first argument, since we only care about parameters
519
 
        argv = [pargv[i] for i in range(1, c.value)]
 
651
        argv = _command_line_to_argv(GetCommandLine())[1:]
520
652
        if getattr(sys, 'frozen', None) is None:
521
653
            # Invoked via 'python.exe' which takes the form:
522
654
            #   python.exe [PYTHON_OPTIONS] C:\Path\bzr [BZR_OPTIONS]
523
655
            # we need to get only BZR_OPTIONS part,
524
 
            # so let's using sys.argv[1:] as reference to get the tail
525
 
            # of unicode argv
526
 
            tail_len = len(sys.argv[1:])
527
 
            ix = len(argv) - tail_len
528
 
            argv = argv[ix:]
 
656
            # We already removed 'python.exe' so we remove everything up to and
 
657
            # including the first non-option ('-') argument.
 
658
            for idx in xrange(len(argv)):
 
659
                if argv[idx][:1] != '-':
 
660
                    break
 
661
            argv = argv[idx+1:]
529
662
        return argv
530
663
else:
531
664
    get_unicode_argv = None