blob: da0bb34c781af19ed6e09f7a53fa321596ae84a1 [file] [log] [blame]
Guido van Rossum7e4b2de1995-01-27 02:41:45 +00001"""Filename matching with shell patterns.
Guido van Rossum05e52191992-01-12 23:29:29 +00002
Guido van Rossum7e4b2de1995-01-27 02:41:45 +00003fnmatch(FILENAME, PATTERN) matches according to the local convention.
4fnmatchcase(FILENAME, PATTERN) always takes case in account.
Guido van Rossum05e52191992-01-12 23:29:29 +00005
Guido van Rossum7e4b2de1995-01-27 02:41:45 +00006The functions operate by translating the pattern into a regular
7expression. They cache the compiled regular expressions for speed.
8
9The function translate(PATTERN) returns a regular expression
10corresponding to PATTERN. (It does not compile it.)
11"""
12
Guido van Rossum9694fca1997-10-22 21:00:49 +000013import re
14
Skip Montanaroeccd02a2001-01-20 23:34:12 +000015__all__ = ["fnmatch","fnmatchcase","translate"]
16
Guido van Rossum7e4b2de1995-01-27 02:41:45 +000017_cache = {}
Guido van Rossum762c39e1991-01-01 18:11:14 +000018
Guido van Rossum762c39e1991-01-01 18:11:14 +000019def fnmatch(name, pat):
Tim Peters88869f92001-01-14 23:36:06 +000020 """Test whether FILENAME matches PATTERN.
21
22 Patterns are Unix shell style:
23
24 * matches everything
25 ? matches any single character
26 [seq] matches any character in seq
27 [!seq] matches any char not in seq
28
29 An initial period in FILENAME is not special.
30 Both FILENAME and PATTERN are first case-normalized
31 if the operating system requires it.
32 If you don't want this, use fnmatchcase(FILENAME, PATTERN).
33 """
34
35 import os
36 name = os.path.normcase(name)
37 pat = os.path.normcase(pat)
38 return fnmatchcase(name, pat)
Guido van Rossum7e4b2de1995-01-27 02:41:45 +000039
Martin v. Löwisb5d4d2a2001-06-06 06:24:38 +000040def filter(names, pat):
41 """Return the subset of the list NAMES that match PAT"""
42 import os,posixpath
43 result=[]
44 pat=os.path.normcase(pat)
45 if not _cache.has_key(pat):
46 res = translate(pat)
47 _cache[pat] = re.compile(res)
48 match=_cache[pat].match
49 if os.path is posixpath:
50 # normcase on posix is NOP. Optimize it away from the loop.
51 for name in names:
52 if match(name):
53 result.append(name)
54 else:
55 for name in names:
56 if match(os.path.normcase(name)):
57 result.append(name)
58 return result
59
Guido van Rossum7e4b2de1995-01-27 02:41:45 +000060def fnmatchcase(name, pat):
Tim Peters88869f92001-01-14 23:36:06 +000061 """Test whether FILENAME matches PATTERN, including case.
62
63 This is a version of fnmatch() which doesn't case-normalize
64 its arguments.
65 """
66
67 if not _cache.has_key(pat):
68 res = translate(pat)
69 _cache[pat] = re.compile(res)
70 return _cache[pat].match(name) is not None
Guido van Rossum762c39e1991-01-01 18:11:14 +000071
Guido van Rossum05e52191992-01-12 23:29:29 +000072def translate(pat):
Tim Peters88869f92001-01-14 23:36:06 +000073 """Translate a shell PATTERN to a regular expression.
74
75 There is no way to quote meta-characters.
76 """
77
78 i, n = 0, len(pat)
79 res = ''
80 while i < n:
81 c = pat[i]
82 i = i+1
83 if c == '*':
84 res = res + '.*'
85 elif c == '?':
86 res = res + '.'
87 elif c == '[':
88 j = i
89 if j < n and pat[j] == '!':
90 j = j+1
91 if j < n and pat[j] == ']':
92 j = j+1
93 while j < n and pat[j] != ']':
94 j = j+1
95 if j >= n:
96 res = res + '\\['
97 else:
Fred Drake46d9fda2001-03-21 18:05:48 +000098 stuff = pat[i:j].replace('\\','\\\\')
Tim Peters88869f92001-01-14 23:36:06 +000099 i = j+1
100 if stuff[0] == '!':
Fred Drake46d9fda2001-03-21 18:05:48 +0000101 stuff = '^' + stuff[1:]
102 elif stuff[0] == '^':
103 stuff = '\\' + stuff
104 res = '%s[%s]' % (res, stuff)
Tim Peters88869f92001-01-14 23:36:06 +0000105 else:
106 res = res + re.escape(c)
107 return res + "$"