67 return head + res + tail |
67 return head + res + tail |
68 |
68 |
69 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1} |
69 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1} |
70 |
70 |
71 def matcher(cwd, names, inc, exc, head = ''): |
71 def matcher(cwd, names, inc, exc, head = ''): |
72 def patlike(name): |
72 def patkind(name): |
73 for prefix in 're:', 'glob:', 'path:': |
73 for prefix in 're:', 'glob:', 'path:': |
74 if name.startswith(prefix): return True |
74 if name.startswith(prefix): return name.split(':', 1) |
75 for c in name: |
75 for c in name: |
76 if c in _globchars: return True |
76 if c in _globchars: return 'glob', name |
|
77 return 'relpath', name |
|
78 |
|
79 cwdsep = cwd + os.sep |
77 |
80 |
78 def regex(name, tail): |
81 def regex(name, tail): |
79 '''convert a pattern into a regular expression''' |
82 '''convert a pattern into a regular expression''' |
80 if name.startswith('re:'): |
83 kind, name = patkind(name) |
81 return name[3:] |
84 if kind == 're': |
82 elif name.startswith('path:'): |
85 return name |
83 return '^' + re.escape(name[5:]) + '$' |
86 elif kind == 'path': |
84 elif name.startswith('glob:'): |
87 return '^' + re.escape(name) + '$' |
85 return head + globre(name[5:], '', tail) |
88 if cwd: name = os.path.join(cwdsep, name) |
|
89 name = os.path.normpath(name) |
|
90 if name == '.': name = '**' |
86 return head + globre(name, '', tail) |
91 return head + globre(name, '', tail) |
87 |
|
88 cwdsep = cwd + os.sep |
|
89 |
92 |
90 def under(fn): |
93 def under(fn): |
91 """check if fn is under our cwd""" |
94 """check if fn is under our cwd""" |
92 return not cwd or fn.startswith(cwdsep) |
95 return not cwd or fn.startswith(cwdsep) |
93 |
96 |
94 def matchfn(pats, tail): |
97 def matchfn(pats, tail): |
95 """build a matching function from a set of patterns""" |
98 """build a matching function from a set of patterns""" |
96 if pats: |
99 if pats: |
97 pat = '(?:%s)' % '|'.join([regex(p, tail) for p in pats]) |
100 pat = '(?:%s)' % '|'.join([regex(p, tail) for p in pats]) |
98 if cwd: |
|
99 pat = re.escape(cwdsep) + pat |
|
100 return re.compile(pat).match |
101 return re.compile(pat).match |
101 |
102 |
102 pats = filter(patlike, names) |
103 def globprefix(pat): |
103 files = [n for n in names if not patlike(n)] |
104 '''return the non-glob prefix of a path, e.g. foo/* -> foo''' |
104 if pats: plain = [] |
105 root = [] |
105 elif cwd: plain = [cwdsep + f for f in files] |
106 for p in pat.split(os.sep): |
106 else: plain = files |
107 if patkind(p)[0] == 'glob': break |
|
108 root.append(p) |
|
109 return os.sep.join(root) |
|
110 |
|
111 patkinds = map(patkind, names) |
|
112 pats = [name for (kind, name) in patkinds if kind != 'relpath'] |
|
113 files = [name for (kind, name) in patkinds if kind == 'relpath'] |
|
114 roots = filter(None, map(globprefix, pats)) + files |
|
115 if cwd: roots = [cwdsep + r for r in roots] |
107 |
116 |
108 patmatch = matchfn(pats, '$') |
117 patmatch = matchfn(pats, '$') or always |
109 filematch = matchfn(files, '(?:/|$)') |
118 filematch = matchfn(files, '(?:/|$)') or always |
110 incmatch = matchfn(inc, '(?:/|$)') or under |
119 incmatch = matchfn(inc, '(?:/|$)') or always |
111 excmatch = matchfn(exc, '(?:/|$)') or (lambda fn: False) |
120 excmatch = matchfn(exc, '(?:/|$)') or (lambda fn: False) |
112 |
121 |
113 return plain, lambda fn: (incmatch(fn) and not excmatch(fn) and |
122 return roots, lambda fn: (incmatch(fn) and not excmatch(fn) and |
114 (fn.endswith('/') or |
123 (fn.endswith('/') or |
115 (not pats and not files) or |
124 (not pats and not files) or |
116 (pats and patmatch(fn)) or |
125 (pats and patmatch(fn)) or |
117 (files and filematch(fn)))) |
126 (files and filematch(fn)))) |
118 |
127 |