1 /*
2 Copyright (c) 2006 Aaron Stone <aaron@serendipity.cx>
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later
8 version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include "dbmail.h"
21
weird_tokenize(char * pat,char * sep)22 static char **weird_tokenize(char *pat, char *sep)
23 {
24 char **parts;
25 char *pat_begin, *pat_end;
26 int t = 0, tokens = 1;
27
28 // Count the number of separators in pat
29 for (pat_end = pat; pat_end[0] != '\0'; pat_end++) {
30 if (strchr(sep, pat_end[0])) {
31 tokens+=2;
32 }
33 }
34
35 // Mask the original pat.
36 pat = g_strdup(pat);
37 parts = g_new0(char *, tokens + 1);
38
39 // Catch the degenerate case.
40 if (tokens == 1) {
41 parts[0] = pat;
42 return parts;
43 }
44
45 // Break pat by sep into parts.
46 for (pat_begin = pat_end = pat; pat_end[0] != '\0'; pat_end++) {
47 if (strchr(sep, pat_end[0])) {
48 if (pat_begin == pat_end) {
49 parts[t] = g_strdup(" ");
50 parts[t][0] = pat_end[0];
51 pat_end[0] = '\0';
52 pat_begin++;
53 t++;
54 } else {
55 parts[t+1] = g_strdup(" ");
56 parts[t+1][0] = pat_end[0];
57 pat_end[0] = '\0';
58 parts[t] = g_strdup(pat_begin);
59 pat_begin = pat_end + 1;
60 t+=2;
61 }
62 }
63 }
64
65 // Don't overwrite existing values
66 // Copy the part if we haven't found a sep char.
67 if (!parts[t] && pat_begin < pat_end) {
68 parts[t] = g_strdup(pat_begin);
69 }
70
71 g_free(pat);
72
73 return parts;
74 }
75
76
77 // See if the needle matches the haystack, given
78 // than '?' means zero or one character and '*'
79 // means zero or any number of characters.
80 //
81 // Returns the candidate argument if it matches, or NULL.
82 // Does not make a copy of candidate. This allows nesting.
match_glob(char * pattern,char * candidate)83 char *match_glob(char *pattern, char *candidate)
84 {
85 char *can = candidate;
86 char **parts;
87 int i, has_star = 0, has_question = 0;
88
89 parts = weird_tokenize(pattern, "?*");
90 if (!parts)
91 return NULL;
92
93 // Each of the parts much be placed on candidate.
94 for (i = 0; parts[i] != NULL; i++) {
95 int len, plen;
96
97 plen = strlen(parts[i]);
98
99 if (parts[i][0] == '\0') {
100 continue;
101 }
102
103 if (parts[i][0] == '*') {
104 // Signals that we should do an anywhere-ahead match.
105 has_star = 1;
106 continue;
107 }
108
109 if (parts[i][0] == '?') {
110 // Signals that we should do a here or next match.
111 // Count up the number of spaces we're allowed to skip.
112 has_question++;
113 continue;
114 }
115
116 len = strlen(can);
117
118 if (has_star) {
119 int j;
120
121 for (j = 0; j < len; j++) {
122 if (strncmp(parts[i], can + j, MIN(plen, len - j)) == 0) {
123 can += CLAMP(plen + j, plen, len);
124 has_star = 0;
125 break;
126 }
127 }
128
129 // If we get here, then we never matched.
130 if (has_star)
131 goto nomatch;
132
133 continue;
134 }
135
136 // If we have a question mark, we're allowed to match one-ahead.
137 if (has_question) {
138 int j;
139
140 for (j = 0; j <= has_question; j++) {
141 if (strncmp(parts[i], can + j, MIN(plen, len-j)) == 0) {
142 can += CLAMP(plen + j, plen, len);
143 has_question = 0;
144 break;
145 }
146 }
147
148 // If we get here, then we never matched.
149 if (has_question)
150 goto nomatch;
151
152 continue;
153 }
154
155 // This is for normal matching.
156 if (strncmp(parts[i], can, MIN(plen, len)) == 0) {
157 can += MIN(plen, len);
158 continue;
159 }
160
161 // If we get here, then we never matched.
162 goto nomatch;
163 }
164
165 // Did we advance all the way to the end?
166 // Or, did we have a star at the end?
167 if (can[0] == '\0' || has_star || (has_question && can[1] == '\0'))
168 goto match;
169
170 nomatch:
171 g_strfreev(parts);
172 return NULL;
173
174 match:
175 g_strfreev(parts);
176 return candidate;
177
178 }
179
180 // Returns a list of elements from the argument list that
181 // match the pattern. DOES make a copy of matching elements.
182 // The resulting GList should be freed with
183 // g_list_destroy(glob_list);
match_glob_list(char * pattern,GList * list)184 GList *match_glob_list(char *pattern, GList *list)
185 {
186 GList *match_list = NULL;
187
188 if (!list || !pattern)
189 return NULL;
190
191 list = g_list_first(list);
192 if (!list)
193 return NULL;
194
195 while (list) {
196 if (match_glob(pattern, (char *)list->data)) {
197 match_list = g_list_prepend(match_list,
198 g_strdup((char *)list->data));
199 }
200 if (! g_list_next(list)) break;
201 list = g_list_next(list);
202 }
203
204 // Return elements in the same relative order.
205 if (match_list)
206 match_list = g_list_reverse(match_list);
207
208 return match_list;
209 }
210
211