/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: Andrew Bennetts
  • Date: 2009-11-19 06:28:13 UTC
  • mfrom: (4811 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4812.
  • Revision ID: andrew.bennetts@canonical.com-20091119062813-t6sd6gwbot8nfyze
MergeĀ lp:bzr.

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
 
422
426
 
423
427
 
424
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
 
425
449
def glob_expand(file_list):
426
450
    """Replacement for glob expansion by the shell.
427
451
 
435
459
    """
436
460
    if not file_list:
437
461
        return []
438
 
    import glob
439
462
    expanded_file_list = []
440
463
    for possible_glob in file_list:
441
 
        # work around bugs in glob.glob()
442
 
        # - Python bug #1001604 ("glob doesn't return unicode with ...")
443
 
        # - failing expansion for */* with non-iso-8859-* chars
444
 
        possible_glob, corrected = _ensure_with_dir(possible_glob)
445
 
        glob_files = glob.glob(possible_glob)
446
 
 
447
 
        if glob_files == []:
448
 
            # special case to let the normal code path handle
449
 
            # files that do not exists
450
 
            expanded_file_list.append(
451
 
                _undo_ensure_with_dir(possible_glob, corrected))
452
 
        else:
453
 
            glob_files = [_undo_ensure_with_dir(elem, corrected) for elem in glob_files]
454
 
            expanded_file_list += glob_files
455
 
 
456
 
    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
457
466
 
458
467
 
459
468
def get_app_path(appname):
511
520
            trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
512
521
 
513
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.replace(u'\\', u'/'))
 
635
        else:
 
636
            args.extend(glob_one(arg))
 
637
    return args
 
638
 
 
639
 
514
640
if has_ctypes and winver != 'Windows 98':
515
641
    def get_unicode_argv():
516
642
        LPCWSTR = ctypes.c_wchar_p
520
646
        GetCommandLine = prototype(("GetCommandLineW",
521
647
                                    ctypes.windll.kernel32))
522
648
        prototype = ctypes.WINFUNCTYPE(POINTER(LPCWSTR), LPCWSTR, POINTER(INT))
523
 
        CommandLineToArgv = prototype(("CommandLineToArgvW",
524
 
                                       ctypes.windll.shell32))
525
 
        c = INT(0)
526
 
        pargv = CommandLineToArgv(GetCommandLine(), ctypes.byref(c))
 
649
        command_line = GetCommandLine()
527
650
        # Skip the first argument, since we only care about parameters
528
 
        argv = [pargv[i] for i in range(1, c.value)]
 
651
        argv = _command_line_to_argv(GetCommandLine())[1:]
529
652
        if getattr(sys, 'frozen', None) is None:
530
653
            # Invoked via 'python.exe' which takes the form:
531
654
            #   python.exe [PYTHON_OPTIONS] C:\Path\bzr [BZR_OPTIONS]
532
655
            # we need to get only BZR_OPTIONS part,
533
 
            # so let's using sys.argv[1:] as reference to get the tail
534
 
            # of unicode argv
535
 
            tail_len = len(sys.argv[1:])
536
 
            ix = len(argv) - tail_len
537
 
            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:]
538
662
        return argv
539
663
else:
540
664
    get_unicode_argv = None