~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/glob_matcher.py

  • Committer: John Arbash Meinel
  • Date: 2006-03-08 14:38:24 UTC
  • mto: (1685.1.1 bzr-encoding)
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060308143824-d84504389354bfc1
Removing glob_matcher for the future ignore pattern upgrade.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 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
 
import re
18
 
 
19
 
 
20
 
def glob_to_re(pat):
21
 
    """Convert a glob pattern into a regular expression.
22
 
 
23
 
    We handle the following patterns:
24
 
        **      Match a string of characters (including dir separators)
25
 
        *       Match a string of characters (not directory separator)
26
 
        ?       Match a single character (not directory separator)
27
 
        [seq]   Matches a single character, but any of 'seq'
28
 
        [!seq]  Match any single character not in 'seq'
29
 
 
30
 
    This was adapted from fnmatch.translate()
31
 
 
32
 
    :param pat: The pattern to transform
33
 
    :return: A regular expression
34
 
    """
35
 
 
36
 
    i, n = 0, len(pat)
37
 
    res = ''
38
 
    while i < n:
39
 
        c = pat[i]
40
 
        i += 1
41
 
        if c == '*':
42
 
            if pat[i:i+1] == '*': # pattern '**'
43
 
                res = res + '.*'
44
 
                i += 1
45
 
            else: # pattern '*'
46
 
                res = res + r'[^/\\]*'
47
 
        elif c == '?':
48
 
            res = res + r'[^/\\]'
49
 
        elif c == '[':
50
 
            j = i
51
 
            if j < n and pat[j] == '!':
52
 
                j = j+1
53
 
            if j < n and pat[j] == ']':
54
 
                j = j+1
55
 
            while j < n and pat[j] != ']':
56
 
                j = j+1
57
 
            if j >= n:
58
 
                res = res + '\\['
59
 
            else:
60
 
                stuff = pat[i:j].replace('\\','\\\\')
61
 
                i = j+1
62
 
                if stuff[0] == '!':
63
 
                    stuff = '^' + stuff[1:] + r'/\\'
64
 
                elif stuff[0] == '^':
65
 
                    stuff = '\\' + stuff
66
 
                res = '%s[%s]' % (res, stuff)
67
 
        else:
68
 
            res = res + re.escape(c)
69
 
    # Without a final $, re.match() will match if just the beginning
70
 
    # matches. I did not expect that. I thought re.match() had to match
71
 
    # the entire string.
72
 
    return res + "$"
73
 
 
74
 
 
75
 
class _GlobMatcher(object):
76
 
    """A class which handles matching filenames to glob expressions"""
77
 
    
78
 
    def __init__(self, glob_re):
79
 
        """Create a matcher from a regular expression."""
80
 
        self._compiled_re = re.compile(glob_re, re.UNICODE)
81
 
 
82
 
    def __call__(self, fname):
83
 
        """See if fname matches the internal glob.
84
 
 
85
 
        :param fname: A filename to check.
86
 
        :return: Boolean, does/doesn't match
87
 
        """
88
 
        return self._compiled_re.match(fname) is not None
89
 
 
90
 
 
91
 
def glob_to_matcher(glob):
92
 
    """Return a callable which will match filenames versus the glob."""
93
 
    return _GlobMatcher(glob_to_re(glob))
94
 
 
95
 
 
96
 
def globs_to_re(patterns):
97
 
    """Convert a set of patterns into a single regular expression.
98
 
 
99
 
    :param patterns: A list of patterns to transform
100
 
    :return: A regular expression combining all patterns
101
 
    """
102
 
    final_re = []
103
 
    for pat in patterns:
104
 
        pat_re = glob_to_re(pat)
105
 
        assert pat_re[-1] == '$'
106
 
        # TODO: jam 20060107 It seems to be enough to do:
107
 
        #       (pat1|pat2|pat3|pat4)$
108
 
        #       Is there a circumstance where we need to do
109
 
        #       ((pat1)|(pat2)|(pat3))$
110
 
        
111
 
        # TODO: jam 20060107 Is it more efficient to do:
112
 
        #       (pat1|pat2|pat3)$
113
 
        #       Or to do:
114
 
        #       (pat1$)|(pat2$)|(pat3$)
115
 
        # I thought it would be more efficent to only have to
116
 
        # match the end of the pattern once
117
 
 
118
 
        #final_re.append('(' + pat_re[:-1] + ')')
119
 
        final_re.append(pat_re[:-1])
120
 
    # All patterns end in $, we don't need to specify it
121
 
    # for every pattern.
122
 
    # Just put one at the end
123
 
    return '(' + '|'.join(final_re) + ')$'
124
 
 
125
 
 
126
 
def globs_to_matcher(patterns):
127
 
    """Return a callable which will match filenames versus the globs."""
128
 
    return _GlobMatcher(globs_to_re(patterns))
129
 
 
130