Name | Executed | Routines | % | Executed | Lines | % | Unexecuted |
/home/matt/eu/rds/include/std/wildcard.e | 3 | 4 | 75.00% | 53 | 58 | 91.38% | 5 |
Routine | Executed | Lines | Unexecuted | |
new() | 0 | 2 | 0.00% | 2 |
is_match() | 32 | 33 | 96.97% | 1 |
qmatch() | 14 | 15 | 93.33% | 1 |
wildcard_file() | 6 | 7 | 85.71% | 1 |
# | Executed | |
1 | -- (c) Copyright - See License.txt | |
2 | -- wildcard.e | |
3 | namespace wildcard | |
4 | ||
5 | --**** | |
6 | -- == Wildcard Matching | |
7 | -- | |
8 | -- < | |
9 | -- | |
10 | ||
11 | --**** | |
12 | -- === Routines | |
13 | ||
14 | include std/text.e as txt -- upper/lower | |
15 | ||
16 | ||
17 | ||
18 | --** | |
19 | -- Return a text pattern | |
20 | -- | |
21 | -- Parameters: | |
22 | -- # ##pattern## : a sequence representing a text string pattern | |
23 | -- | |
24 | -- Returns: | |
25 | -- A the same ##pattern##. | |
26 | -- | |
27 | -- Comments: | |
28 | -- You might wonder why this function even exists. If you use it and ##is_match## you can easily | |
29 | -- change to the routines found in std/regex.e. And if using std/regex.e and you restrict | |
30 | -- yourself to only using | |
31 | -- [[:regex:new]] and [[:regex:is_match]], you can change to using | |
32 | -- std/wildcards.e with very little modification to your source code. | |
33 | -- | |
34 | -- Suppose you work for a hotel and you set up a system for looking up | |
35 | -- guests. | |
36 | -- | |
37 | -- | |
38 | -- -- The user can use regular expressions to find guests at a hotel... | |
39 | -- include std/regex.e as uip -- user input patterns 'uip' | |
40 | -- puts(1,"Enter a person to find. You may use regular expressions:") | |
41 | -- | |
42 | -- sequence person_to_find | |
43 | -- object pattern | |
44 | -- person_to_find = gets(0) | |
45 | -- person_to_find_pattern = uip:new(person_to_find[1..$-1]) | |
46 | -- while sequence(line) do | |
47 | -- line = line[1..$-1] | |
48 | -- if uip:is_match(person_to_find_pattern, line) then | |
49 | -- -- code for telling users the person is there. | |
50 | -- end if | |
51 | -- -- code loads next name into 'line' | |
52 | -- end while | |
53 | -- close(dbfd) | |
54 | -- | |
55 | -- | |
56 | -- Later the hotel manager tells you that the users would rather use wildcard | |
57 | -- matching you need only change the include line and the prompt for the pattern. | |
58 | -- | |
59 | -- -- This will make things simpler... | |
60 | -- include std/wildcard.e as uip -- user input patterns 'uip'. | |
61 | -- puts(1,"Enter a person to find. You may use '*' and '?' wildcards:") | |
62 | -- | |
63 | -- | |
64 | -- See Also: [[:is_match]] | |
65 | 0 | |
66 | 0 | return s |
67 | end function | |
68 | ||
69 | 249 | |
70 | -- find pattern p in string s | |
71 | -- p may have '?' wild cards (but not '*') | |
72 | integer k | |
73 | ||
74 | 249 | if not find('?', p) then |
75 | 248 | return match(p, s) -- fast |
76 | end if | |
77 | -- must allow for '?' wildcard | |
78 | 1 | for i = 1 to length(s) - length(p) + 1 do |
79 | 4 | k = i |
80 | 4 | for j = 1 to length(p) do |
81 | 8 | if p[j] != s[k] and p[j] != '?' then |
82 | 3 | k = 0 |
83 | 3 | exit |
84 | end if | |
85 | 5 | k += 1 |
86 | 5 | end for |
87 | 4 | if k != 0 then |
88 | 1 | return i |
89 | end if | |
90 | 3 | end for |
91 | 0 | return 0 |
92 | end function | |
93 | ||
94 | 101 | constant END_MARKER = -1 |
95 | ||
96 | --** | |
97 | -- Determine whether a string matches a pattern. The pattern may contain * and ? wildcards. | |
98 | -- | |
99 | -- Parameters: | |
100 | -- # ##pattern## : a string, the pattern to match | |
101 | -- # ##string## : the string to be matched against | |
102 | -- | |
103 | -- Returns: | |
104 | -- An **integer**, TRUE if ##string## matches ##pattern##, else FALSE. | |
105 | -- | |
106 | -- Comments: | |
107 | -- | |
108 | -- Character comparisons are case sensitive. | |
109 | -- If you want case insensitive comparisons, pass both ##pattern## and ##string## through [[:upper]](), or both through [[:lower]](), before calling ##is_match##(). | |
110 | -- | |
111 | -- If you want to detect a pattern anywhere within a string, add * to each end of the pattern: | |
112 | -- {{{ | |
113 | -- i = is_match('*' & pattern & '*', string) | |
114 | -- }}} | |
115 | -- | |
116 | -- There is currently no way to treat * or ? literally in a pattern. | |
117 | -- | |
118 | -- Example 1: | |
119 | -- | |
120 | -- i = is_match("A?B*", "AQBXXYY") | |
121 | -- -- i is 1 (TRUE) | |
122 | -- | |
123 | -- | |
124 | -- Example 2: | |
125 | -- | |
126 | -- i = is_match("*xyz*", "AAAbbbxyz") | |
127 | -- -- i is 1 (TRUE) | |
128 | -- | |
129 | -- | |
130 | -- Example 3: | |
131 | -- | |
132 | -- i = is_match("A*B*C", "a111b222c") | |
133 | -- -- i is 0 (FALSE) because upper/lower case doesn't match | |
134 | -- | |
135 | -- | |
136 | -- Example 4: | |
137 | -- ##bin/search.ex## | |
138 | -- | |
139 | -- See Also: | |
140 | -- [[:wildcard_file]], [[:upper]], [[:lower]], [[:Regular Expressions]] | |
141 | ||
142 | 257 | |
143 | integer p, f, t | |
144 | sequence match_string | |
145 | ||
146 | 257 | pattern = pattern & END_MARKER |
147 | 257 | string = string & END_MARKER |
148 | 257 | p = 1 |
149 | 257 | f = 1 |
150 | 257 | while f <= length(string) do |
151 | 522 | if not find(pattern[p], {string[f], '?'}) then |
152 | 496 | if pattern[p] = '*' then |
153 | 489 | while pattern[p] = '*' do |
154 | 489 | p += 1 |
155 | 489 | end while |
156 | 489 | if pattern[p] = END_MARKER then |
157 | 240 | return 1 |
158 | end if | |
159 | 249 | match_string = "" |
160 | 249 | while pattern[p] != '*' do |
161 | 271 | match_string = match_string & pattern[p] |
162 | 271 | if pattern[p] = END_MARKER then |
163 | 10 | exit |
164 | end if | |
165 | 261 | p += 1 |
166 | 261 | end while |
167 | 249 | if pattern[p] = '*' then |
168 | 239 | p -= 1 |
169 | end if | |
170 | 249 | t = qmatch(match_string, string[f..$]) |
171 | 249 | if t = 0 then |
172 | 6 | return 0 |
173 | else | |
174 | 243 | f += t + length(match_string) - 2 |
175 | end if | |
176 | else | |
177 | 7 | return 0 |
178 | end if | |
179 | end if | |
180 | 269 | p += 1 |
181 | 269 | f += 1 |
182 | 269 | if p > length(pattern) then |
183 | 4 | return f > length(string) |
184 | end if | |
185 | 265 | end while |
186 | 0 | return 0 |
187 | end function | |
188 | ||
189 | --** | |
190 | -- Determine whether a file name matches a wildcard pattern. | |
191 | -- | |
192 | -- Parameters: | |
193 | -- # ##pattern## : a string, the pattern to match | |
194 | -- # ##filename## : the string to be matched against | |
195 | -- | |
196 | -- Returns: | |
197 | -- An **integer**, TRUE if ##filename## matches ##pattern##, else FALSE. | |
198 | -- | |
199 | -- Comments: | |
200 | -- | |
201 | -- ~* matches any 0 or more characters, ? matches any single character. On //Unix// the | |
202 | -- character comparisons are case sensitive. On Windows they are not. | |
203 | -- | |
204 | -- You might use this function to check the output of the [[:dir]]() routine for file names that match a pattern supplied by the user of your program. | |
205 | -- | |
206 | -- Example 1: | |
207 | -- | |
208 | -- i = wildcard_file("AB*CD.?", "aB123cD.e") | |
209 | -- -- i is set to 1 on Windows, 0 on Linux or FreeBSD | |
210 | -- | |
211 | -- | |
212 | -- Example 2: | |
213 | -- | |
214 | -- i = wildcard_file("AB*CD.?", "abcd.ex") | |
215 | -- -- i is set to 0 on all systems, | |
216 | -- -- because the file type has 2 letters not 1 | |
217 | -- | |
218 | -- | |
219 | -- Example 3: | |
220 | -- ##bin/search.ex## | |
221 | -- | |
222 | -- See Also: | |
223 | -- [[:is_match]], [[:dir]] | |
224 | ||
225 | 254 | |
226 | 254 | ifdef not UNIX then |
227 | pattern = txt:upper(pattern) | |
228 | filename = txt:upper(filename) | |
229 | end ifdef | |
230 | ||
231 | 254 | if not find('.', pattern) then |
232 | 0 | pattern = pattern & '.' |
233 | end if | |
234 | ||
235 | 254 | if not find('.', filename) then |
236 | 5 | filename = filename & '.' |
237 | end if | |
238 | ||
239 | 254 | return is_match(pattern, filename) |
240 | end function |