1#include "regex.h"
2
3#include <stddef.h>
4
5enum atom_type {
6 ATOM_LITERAL,
7 ATOM_ANY,
8 ATOM_CLASS
9};
10
11struct atom {
12 enum atom_type type;
13 unsigned char literal;
14 const char *class_begin;
15 const char *class_end;
16};
17
18static int match_here(const char *pattern, const char *text);
19
20
21
22
23
24static const char *class_end(const char *pattern)
25{
26 const char *cursor = pattern + 1;
27 int first = 1;
28
29 if (*cursor == '^') {
30 ++cursor;
31 }
32
33 while (*cursor != '\0') {
34 if (*cursor == '\\') {
35 if (cursor[1] == '\0') {
36 return NULL;
37 }
38 cursor += 2;
39 first = 0;
40 continue;
41 }
42
43 if (*cursor == ']' && !first) {
44 return cursor;
45 }
46
47 ++cursor;
48 first = 0;
49 }
50
51 return NULL;
52}
53
54
55static int parse_atom(const char *pattern, struct atom *atom,
56 const char **after)
57{
58 const char *end;
59
60 if (*pattern == '\0' || *pattern == '*' || *pattern == '+' ||
61 *pattern == '?') {
62 return 0;
63 }
64
65 if (*pattern == '\\') {
66 if (pattern[1] == '\0') {
67 return 0;
68 }
69 atom->type = ATOM_LITERAL;
70 atom->literal = (unsigned char)pattern[1];
71 *after = pattern + 2;
72 return 1;
73 }
74
75 if (*pattern == '.') {
76 atom->type = ATOM_ANY;
77 *after = pattern + 1;
78 return 1;
79 }
80
81 if (*pattern == '[') {
82 end = class_end(pattern);
83 if (end == NULL) {
84 return 0;
85 }
86 atom->type = ATOM_CLASS;
87 atom->class_begin = pattern + 1;
88 atom->class_end = end;
89 *after = end + 1;
90 return 1;
91 }
92
93 atom->type = ATOM_LITERAL;
94 atom->literal = (unsigned char)*pattern;
95 *after = pattern + 1;
96 return 1;
97}
98
99
100static const char *class_char(const char *cursor, const char *end,
101 unsigned char *value)
102{
103 if (cursor >= end) {
104 return NULL;
105 }
106
107 if (*cursor == '\\') {
108 if (cursor + 1 >= end) {
109 return NULL;
110 }
111 *value = (unsigned char)cursor[1];
112 return cursor + 2;
113 }
114
115 *value = (unsigned char)*cursor;
116 return cursor + 1;
117}
118
119static int class_matches(const struct atom *atom, unsigned char value)
120{
121 const char *cursor = atom->class_begin;
122 const char *after_first;
123 const char *after_last;
124 unsigned char first;
125 unsigned char last;
126 int negate = 0;
127 int found = 0;
128
129 if (cursor < atom->class_end && *cursor == '^') {
130 negate = 1;
131 ++cursor;
132 }
133
134 while (cursor < atom->class_end) {
135 after_first = class_char(cursor, atom->class_end, &first);
136 if (after_first == NULL) {
137 return 0;
138 }
139
140 if (after_first < atom->class_end && *after_first == '-' &&
141 after_first + 1 < atom->class_end) {
142 after_last = class_char(after_first + 1, atom->class_end,
143 &last);
144 if (after_last == NULL) {
145 return 0;
146 }
147 if (first <= value && value <= last) {
148 found = 1;
149 }
150 cursor = after_last;
151 } else {
152 if (first == value) {
153 found = 1;
154 }
155 cursor = after_first;
156 }
157 }
158
159 return negate ? !found : found;
160}
161
162static int valid_class(const struct atom *atom)
163{
164 const char *cursor = atom->class_begin;
165 const char *after_first;
166 const char *after_last;
167 unsigned char first;
168 unsigned char last;
169
170 if (cursor < atom->class_end && *cursor == '^') {
171 ++cursor;
172 }
173
174 while (cursor < atom->class_end) {
175 after_first = class_char(cursor, atom->class_end, &first);
176 if (after_first == NULL) {
177 return 0;
178 }
179
180 if (after_first < atom->class_end && *after_first == '-' &&
181 after_first + 1 < atom->class_end) {
182 after_last = class_char(after_first + 1, atom->class_end,
183 &last);
184 if (after_last == NULL || first > last) {
185 return 0;
186 }
187 cursor = after_last;
188 } else {
189 cursor = after_first;
190 }
191 }
192
193 return 1;
194}
195
196static int atom_matches(const struct atom *atom, unsigned char value)
197{
198 switch (atom->type) {
199 case ATOM_LITERAL:
200 return atom->literal == value;
201 case ATOM_ANY:
202 return 1;
203 case ATOM_CLASS:
204 return class_matches(atom, value);
205 }
206
207 return 0;
208}
209
210
211
212
213
214static int match_many(const struct atom *atom, const char *rest,
215 const char *text, size_t minimum)
216{
217 const char *cursor = text;
218 const char *minimum_end = text + minimum;
219
220 while (*cursor != '\0' &&
221 atom_matches(atom, (unsigned char)*cursor)) {
222 ++cursor;
223 }
224
225 if (cursor < minimum_end) {
226 return 0;
227 }
228
229 for (;;) {
230 if (match_here(rest, cursor)) {
231 return 1;
232 }
233 if (cursor == minimum_end) {
234 break;
235 }
236 --cursor;
237 }
238
239 return 0;
240}
241
242static int match_here(const char *pattern, const char *text)
243{
244 struct atom atom;
245 const char *after;
246 char quantifier;
247
248 if (*pattern == '\0') {
249 return 1;
250 }
251
252 if (*pattern == '$' && pattern[1] == '\0') {
253 return *text == '\0';
254 }
255
256 if (!parse_atom(pattern, &atom, &after)) {
257 return 0;
258 }
259
260 quantifier = *after;
261 if (quantifier == '*' || quantifier == '+') {
262 return match_many(&atom, after + 1, text,
263 quantifier == '+' ? 1U : 0U);
264 }
265
266 if (quantifier == '?') {
267 if (*text != '\0' &&
268 atom_matches(&atom, (unsigned char)*text) &&
269 match_here(after + 1, text + 1)) {
270 return 1;
271 }
272 return match_here(after + 1, text);
273 }
274
275 if (*text != '\0' && atom_matches(&atom, (unsigned char)*text)) {
276 return match_here(after, text + 1);
277 }
278
279 return 0;
280}
281
282
283
284
285
286static int valid_pattern(const char *pattern)
287{
288 const char *cursor = pattern;
289 const char *after;
290 struct atom atom;
291
292 if (*cursor == '^') {
293 ++cursor;
294 }
295
296 while (*cursor != '\0') {
297 if (*cursor == '$' && cursor[1] == '\0') {
298 return 1;
299 }
300
301 if (!parse_atom(cursor, &atom, &after)) {
302 return 0;
303 }
304 if (atom.type == ATOM_CLASS && !valid_class(&atom)) {
305 return 0;
306 }
307
308 cursor = after;
309 if (*cursor == '*' || *cursor == '+' || *cursor == '?') {
310 ++cursor;
311 if (*cursor == '*' || *cursor == '+' || *cursor == '?') {
312 return 0;
313 }
314 }
315 }
316
317 return 1;
318}
319
320int regex_match(const char *pattern, const char *text)
321{
322 const char *cursor;
323
324 if (pattern == NULL || text == NULL || !valid_pattern(pattern)) {
325 return 0;
326 }
327
328 if (*pattern == '^') {
329 return match_here(pattern + 1, text);
330 }
331
332 cursor = text;
333 do {
334 if (match_here(pattern, cursor)) {
335 return 1;
336 }
337 } while (*cursor++ != '\0');
338
339 return 0;
340}
341
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.