/src/r-source/src/extra/tre/tre-match-backtrack.c
Line | Count | Source |
1 | | /* |
2 | | tre-match-backtrack.c - TRE backtracking regex matching engine |
3 | | |
4 | | This software is released under a BSD-style license. |
5 | | See the file LICENSE for details and copyright. |
6 | | |
7 | | */ |
8 | | |
9 | | /* |
10 | | This matcher is for regexps that use back referencing. Regexp matching |
11 | | with back referencing is an NP-complete problem on the number of back |
12 | | references. The easiest way to match them is to use a backtracking |
13 | | routine which basically goes through all possible paths in the TNFA |
14 | | and chooses the one which results in the best (leftmost and longest) |
15 | | match. This can be spectacularly expensive and may run out of stack |
16 | | space, but there really is no better known generic algorithm. Quoting |
17 | | Henry Spencer from comp.compilers: |
18 | | <URL: http://compilers.iecc.com/comparch/article/93-03-102> |
19 | | |
20 | | POSIX.2 REs require longest match, which is really exciting to |
21 | | implement since the obsolete ("basic") variant also includes |
22 | | \<digit>. I haven't found a better way of tackling this than doing |
23 | | a preliminary match using a DFA (or simulation) on a modified RE |
24 | | that just replicates subREs for \<digit>, and then doing a |
25 | | backtracking match to determine whether the subRE matches were |
26 | | right. This can be rather slow, but I console myself with the |
27 | | thought that people who use \<digit> deserve very slow execution. |
28 | | (Pun unintentional but very appropriate.) |
29 | | |
30 | | */ |
31 | | |
32 | | |
33 | | #ifdef HAVE_CONFIG_H |
34 | | #include <config.h> |
35 | | #endif /* HAVE_CONFIG_H */ |
36 | | |
37 | | #ifdef TRE_USE_ALLOCA |
38 | | /* AIX requires this to be the first thing in the file. */ |
39 | | #ifndef __GNUC__ |
40 | | # if HAVE_ALLOCA_H |
41 | | # include <alloca.h> |
42 | | # else |
43 | | # ifdef _AIX |
44 | | #pragma alloca |
45 | | # else |
46 | | # ifndef alloca /* predefined by HP cc +Olibcalls */ |
47 | | char *alloca (); |
48 | | # endif |
49 | | # endif |
50 | | # endif |
51 | | #endif |
52 | | #endif /* TRE_USE_ALLOCA */ |
53 | | |
54 | | //#include <assert.h> |
55 | | #include <stdlib.h> |
56 | | #include <string.h> |
57 | | #ifdef HAVE_WCHAR_H |
58 | | #include <wchar.h> |
59 | | #endif /* HAVE_WCHAR_H */ |
60 | | #ifdef HAVE_WCTYPE_H |
61 | | #include <wctype.h> |
62 | | #endif /* HAVE_WCTYPE_H */ |
63 | | #ifndef TRE_WCHAR |
64 | | #include <ctype.h> |
65 | | #endif /* !TRE_WCHAR */ |
66 | | #ifdef HAVE_MALLOC_H |
67 | | #include <malloc.h> |
68 | | #endif /* HAVE_MALLOC_H */ |
69 | | |
70 | | #include "tre-internal.h" |
71 | | #include "tre-mem.h" |
72 | | #include "tre-match-utils.h" |
73 | | #include "tre.h" |
74 | | #include "xmalloc.h" |
75 | | |
76 | 0 | #define assert(a) R_assert(a) |
77 | | |
78 | | typedef struct { |
79 | | int pos; |
80 | | const char *str_byte; |
81 | | #ifdef TRE_WCHAR |
82 | | const wchar_t *str_wide; |
83 | | #endif /* TRE_WCHAR */ |
84 | | tre_tnfa_transition_t *state; |
85 | | int state_id; |
86 | | int next_c; |
87 | | int *tags; |
88 | | #ifdef TRE_MBSTATE |
89 | | mbstate_t mbstate; |
90 | | #endif /* TRE_MBSTATE */ |
91 | | } tre_backtrack_item_t; |
92 | | |
93 | | typedef struct tre_backtrack_struct { |
94 | | tre_backtrack_item_t item; |
95 | | struct tre_backtrack_struct *prev; |
96 | | struct tre_backtrack_struct *next; |
97 | | } *tre_backtrack_t; |
98 | | |
99 | | #ifdef TRE_WCHAR |
100 | 0 | #define BT_STACK_WIDE_IN(_str_wide) stack->item.str_wide = (_str_wide) |
101 | 0 | #define BT_STACK_WIDE_OUT (str_wide) = stack->item.str_wide |
102 | | #else /* !TRE_WCHAR */ |
103 | | #define BT_STACK_WIDE_IN(_str_wide) |
104 | | #define BT_STACK_WIDE_OUT |
105 | | #endif /* !TRE_WCHAR */ |
106 | | |
107 | | #ifdef TRE_MBSTATE |
108 | 0 | #define BT_STACK_MBSTATE_IN stack->item.mbstate = (mbstate) |
109 | 0 | #define BT_STACK_MBSTATE_OUT (mbstate) = stack->item.mbstate |
110 | | #else /* !TRE_MBSTATE */ |
111 | | #define BT_STACK_MBSTATE_IN |
112 | | #define BT_STACK_MBSTATE_OUT |
113 | | #endif /* !TRE_MBSTATE */ |
114 | | |
115 | | |
116 | | #ifdef TRE_USE_ALLOCA |
117 | | #define tre_bt_mem_new tre_mem_newa |
118 | | #define tre_bt_mem_alloc tre_mem_alloca |
119 | | #define tre_bt_mem_destroy(obj) do { } while (0,0) |
120 | | #else /* !TRE_USE_ALLOCA */ |
121 | 0 | #define tre_bt_mem_new tre_mem_new |
122 | 0 | #define tre_bt_mem_alloc tre_mem_alloc |
123 | 0 | #define tre_bt_mem_destroy tre_mem_destroy |
124 | | #endif /* !TRE_USE_ALLOCA */ |
125 | | |
126 | | |
127 | | #define BT_STACK_PUSH(_pos, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \ |
128 | 0 | do \ |
129 | 0 | { \ |
130 | 0 | int i; \ |
131 | 0 | if (!stack->next) \ |
132 | 0 | { \ |
133 | 0 | tre_backtrack_t s; \ |
134 | 0 | s = tre_bt_mem_alloc(mem, sizeof(*s)); \ |
135 | 0 | if (!s) \ |
136 | 0 | { \ |
137 | 0 | tre_bt_mem_destroy(mem); \ |
138 | 0 | if (tags) \ |
139 | 0 | xfree(tags); \ |
140 | 0 | if (pmatch) \ |
141 | 0 | xfree(pmatch); \ |
142 | 0 | if (states_seen) \ |
143 | 0 | xfree(states_seen); \ |
144 | 0 | return REG_ESPACE; \ |
145 | 0 | } \ |
146 | 0 | s->prev = stack; \ |
147 | 0 | s->next = NULL; \ |
148 | 0 | s->item.tags = tre_bt_mem_alloc(mem, \ |
149 | 0 | sizeof(*tags) * tnfa->num_tags); \ |
150 | 0 | if (!s->item.tags) \ |
151 | 0 | { \ |
152 | 0 | tre_bt_mem_destroy(mem); \ |
153 | 0 | if (tags) \ |
154 | 0 | xfree(tags); \ |
155 | 0 | if (pmatch) \ |
156 | 0 | xfree(pmatch); \ |
157 | 0 | if (states_seen) \ |
158 | 0 | xfree(states_seen); \ |
159 | 0 | return REG_ESPACE; \ |
160 | 0 | } \ |
161 | 0 | stack->next = s; \ |
162 | 0 | stack = s; \ |
163 | 0 | } \ |
164 | 0 | else \ |
165 | 0 | stack = stack->next; \ |
166 | 0 | stack->item.pos = (_pos); \ |
167 | 0 | stack->item.str_byte = (_str_byte); \ |
168 | 0 | BT_STACK_WIDE_IN(_str_wide); \ |
169 | 0 | stack->item.state = (_state); \ |
170 | 0 | stack->item.state_id = (_state_id); \ |
171 | 0 | stack->item.next_c = (_next_c); \ |
172 | 0 | for (i = 0; i < tnfa->num_tags; i++) \ |
173 | 0 | stack->item.tags[i] = (_tags)[i]; \ |
174 | 0 | BT_STACK_MBSTATE_IN; \ |
175 | 0 | } \ |
176 | 0 | while (/*CONSTCOND*/(void)0,0) |
177 | | |
178 | | #define BT_STACK_POP() \ |
179 | 0 | do \ |
180 | 0 | { \ |
181 | 0 | int i; \ |
182 | 0 | assert(stack->prev); \ |
183 | 0 | pos = stack->item.pos; \ |
184 | 0 | if (type == STR_USER) \ |
185 | 0 | str_source->rewind(pos + pos_add_next, str_source->context); \ |
186 | 0 | str_byte = stack->item.str_byte; \ |
187 | 0 | BT_STACK_WIDE_OUT; \ |
188 | 0 | state = stack->item.state; \ |
189 | 0 | next_c = (tre_char_t) stack->item.next_c; \ |
190 | 0 | for (i = 0; i < tnfa->num_tags; i++) \ |
191 | 0 | tags[i] = stack->item.tags[i]; \ |
192 | 0 | BT_STACK_MBSTATE_OUT; \ |
193 | 0 | stack = stack->prev; \ |
194 | 0 | } \ |
195 | 0 | while (/*CONSTCOND*/(void)0,0) |
196 | | |
197 | | #undef MIN |
198 | | #define MIN(a, b) ((a) <= (b) ? (a) : (b)) |
199 | | |
200 | | reg_errcode_t |
201 | | tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string, |
202 | | int len, tre_str_type_t type, int *match_tags, |
203 | | int eflags, int *match_end_ofs) |
204 | 0 | { |
205 | | /* State variables required by GET_NEXT_WCHAR. */ |
206 | 0 | tre_char_t prev_c = 0, next_c = 0; |
207 | 0 | const char *str_byte = string; |
208 | 0 | int pos = 0; |
209 | 0 | unsigned int pos_add_next = 1; |
210 | 0 | #ifdef TRE_WCHAR |
211 | 0 | const wchar_t *str_wide = string; |
212 | 0 | #ifdef TRE_MBSTATE |
213 | 0 | mbstate_t mbstate; |
214 | 0 | #endif /* TRE_MBSTATE */ |
215 | 0 | #endif /* TRE_WCHAR */ |
216 | 0 | int reg_notbol = eflags & REG_NOTBOL; |
217 | 0 | int reg_noteol = eflags & REG_NOTEOL; |
218 | 0 | int reg_newline = tnfa->cflags & REG_NEWLINE; |
219 | 0 | int str_user_end = 0; |
220 | | |
221 | | /* These are used to remember the necessary values of the above |
222 | | variables to return to the position where the current search |
223 | | started from. */ |
224 | 0 | int next_c_start; |
225 | 0 | const char *str_byte_start; |
226 | 0 | int pos_start = -1; |
227 | 0 | #ifdef TRE_WCHAR |
228 | 0 | const wchar_t *str_wide_start; |
229 | 0 | #endif /* TRE_WCHAR */ |
230 | 0 | #ifdef TRE_MBSTATE |
231 | 0 | mbstate_t mbstate_start; |
232 | 0 | #endif /* TRE_MBSTATE */ |
233 | | |
234 | | /* End offset of best match so far, or -1 if no match found yet. */ |
235 | 0 | int match_eo = -1; |
236 | | /* Tag arrays. */ |
237 | 0 | int *next_tags, *tags = NULL; |
238 | | /* Current TNFA state. */ |
239 | 0 | tre_tnfa_transition_t *state; |
240 | 0 | int *states_seen = NULL; |
241 | | |
242 | | /* Memory allocator to for allocating the backtracking stack. */ |
243 | 0 | tre_mem_t mem = tre_bt_mem_new(); |
244 | | |
245 | | /* The backtracking stack. */ |
246 | 0 | tre_backtrack_t stack; |
247 | |
|
248 | 0 | tre_tnfa_transition_t *trans_i; |
249 | 0 | regmatch_t *pmatch = NULL; |
250 | 0 | int ret; |
251 | |
|
252 | 0 | #ifdef TRE_MBSTATE |
253 | 0 | memset(&mbstate, '\0', sizeof(mbstate)); |
254 | 0 | #endif /* TRE_MBSTATE */ |
255 | |
|
256 | 0 | if (!mem) |
257 | 0 | return REG_ESPACE; |
258 | 0 | stack = tre_bt_mem_alloc(mem, sizeof(*stack)); |
259 | 0 | if (!stack) |
260 | 0 | { |
261 | 0 | ret = REG_ESPACE; |
262 | 0 | goto error_exit; |
263 | 0 | } |
264 | 0 | stack->prev = NULL; |
265 | 0 | stack->next = NULL; |
266 | |
|
267 | 0 | DPRINT(("tnfa_execute_backtrack, input type %d\n", type)); |
268 | 0 | DPRINT(("len = %d\n", len)); |
269 | |
|
270 | | #ifdef TRE_USE_ALLOCA |
271 | | tags = alloca(sizeof(*tags) * tnfa->num_tags); |
272 | | pmatch = alloca(sizeof(*pmatch) * tnfa->num_submatches); |
273 | | states_seen = alloca(sizeof(*states_seen) * tnfa->num_states); |
274 | | #else /* !TRE_USE_ALLOCA */ |
275 | 0 | if (tnfa->num_tags) |
276 | 0 | { |
277 | 0 | tags = xmalloc(sizeof(*tags) * tnfa->num_tags); |
278 | 0 | if (!tags) |
279 | 0 | { |
280 | 0 | ret = REG_ESPACE; |
281 | 0 | goto error_exit; |
282 | 0 | } |
283 | 0 | } |
284 | 0 | if (tnfa->num_submatches) |
285 | 0 | { |
286 | 0 | pmatch = xmalloc(sizeof(*pmatch) * tnfa->num_submatches); |
287 | 0 | if (!pmatch) |
288 | 0 | { |
289 | 0 | ret = REG_ESPACE; |
290 | 0 | goto error_exit; |
291 | 0 | } |
292 | 0 | } |
293 | 0 | if (tnfa->num_states) |
294 | 0 | { |
295 | 0 | states_seen = xmalloc(sizeof(*states_seen) * tnfa->num_states); |
296 | 0 | if (!states_seen) |
297 | 0 | { |
298 | 0 | ret = REG_ESPACE; |
299 | 0 | goto error_exit; |
300 | 0 | } |
301 | 0 | } |
302 | 0 | #endif /* !TRE_USE_ALLOCA */ |
303 | | |
304 | 0 | retry: |
305 | 0 | { |
306 | 0 | int i; |
307 | 0 | for (i = 0; i < tnfa->num_tags; i++) |
308 | 0 | { |
309 | 0 | tags[i] = -1; |
310 | 0 | if (match_tags) |
311 | 0 | match_tags[i] = -1; |
312 | 0 | } |
313 | 0 | for (i = 0; i < tnfa->num_states; i++) |
314 | 0 | states_seen[i] = 0; |
315 | 0 | } |
316 | |
|
317 | 0 | state = NULL; |
318 | 0 | pos = pos_start; |
319 | 0 | if (type == STR_USER) |
320 | 0 | str_source->rewind(pos + pos_add_next, str_source->context); |
321 | 0 | GET_NEXT_WCHAR(); |
322 | 0 | pos_start = pos; |
323 | 0 | next_c_start = next_c; |
324 | 0 | str_byte_start = str_byte; |
325 | 0 | #ifdef TRE_WCHAR |
326 | 0 | str_wide_start = str_wide; |
327 | 0 | #endif /* TRE_WCHAR */ |
328 | 0 | #ifdef TRE_MBSTATE |
329 | 0 | mbstate_start = mbstate; |
330 | 0 | #endif /* TRE_MBSTATE */ |
331 | | |
332 | | /* Handle initial states. */ |
333 | 0 | next_tags = NULL; |
334 | 0 | for (trans_i = tnfa->initial; trans_i->state; trans_i++) |
335 | 0 | { |
336 | 0 | DPRINT(("> init %p, prev_c %lc\n", trans_i->state, (tre_cint_t)prev_c)); |
337 | 0 | if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions)) |
338 | 0 | { |
339 | 0 | DPRINT(("assert failed\n")); |
340 | 0 | continue; |
341 | 0 | } |
342 | 0 | if (state == NULL) |
343 | 0 | { |
344 | | /* Start from this state. */ |
345 | 0 | state = trans_i->state; |
346 | 0 | next_tags = trans_i->tags; |
347 | 0 | } |
348 | 0 | else |
349 | 0 | { |
350 | | /* Backtrack to this state. */ |
351 | 0 | DPRINT(("saving state %d for backtracking\n", trans_i->state_id)); |
352 | 0 | BT_STACK_PUSH(pos, str_byte, str_wide, trans_i->state, |
353 | 0 | trans_i->state_id, next_c, tags, mbstate); |
354 | 0 | { |
355 | 0 | int *tmp = trans_i->tags; |
356 | 0 | if (tmp) |
357 | 0 | while (*tmp >= 0) |
358 | 0 | stack->item.tags[*tmp++] = pos; |
359 | 0 | } |
360 | 0 | } |
361 | 0 | } |
362 | | |
363 | 0 | if (next_tags) |
364 | 0 | for (; *next_tags >= 0; next_tags++) |
365 | 0 | tags[*next_tags] = pos; |
366 | | |
367 | |
|
368 | 0 | DPRINT(("entering match loop, pos %d, str_byte %p\n", pos, str_byte)); |
369 | 0 | DPRINT(("pos:chr/code | state and tags\n")); |
370 | 0 | DPRINT(("-------------+------------------------------------------------\n")); |
371 | |
|
372 | 0 | if (state == NULL) |
373 | 0 | goto backtrack; |
374 | | |
375 | 0 | while (/*CONSTCOND*/(void)1,1) |
376 | 0 | { |
377 | 0 | tre_tnfa_transition_t *next_state; |
378 | 0 | int empty_br_match; |
379 | |
|
380 | 0 | DPRINT(("start loop\n")); |
381 | 0 | if (state == tnfa->final) |
382 | 0 | { |
383 | 0 | DPRINT((" match found, %d %d\n", match_eo, pos)); |
384 | 0 | if (match_eo < pos |
385 | 0 | || (match_eo == pos |
386 | 0 | && match_tags |
387 | 0 | && tre_tag_order(tnfa->num_tags, tnfa->tag_directions, |
388 | 0 | tags, match_tags))) |
389 | 0 | { |
390 | 0 | int i; |
391 | | /* This match wins the previous match. */ |
392 | 0 | DPRINT((" win previous\n")); |
393 | 0 | match_eo = pos; |
394 | 0 | if (match_tags) |
395 | 0 | for (i = 0; i < tnfa->num_tags; i++) |
396 | 0 | match_tags[i] = tags[i]; |
397 | 0 | } |
398 | | /* Our TNFAs never have transitions leaving from the final state, |
399 | | so we jump right to backtracking. */ |
400 | 0 | goto backtrack; |
401 | 0 | } |
402 | | |
403 | | #ifdef TRE_DEBUG |
404 | | DPRINT(("%3d:%2lc/%05d | %p ", pos, (tre_cint_t)next_c, (int)next_c, |
405 | | state)); |
406 | | { |
407 | | int i; |
408 | | for (i = 0; i < tnfa->num_tags; i++) |
409 | | DPRINT(("%d%s", tags[i], i < tnfa->num_tags - 1 ? ", " : "")); |
410 | | DPRINT(("\n")); |
411 | | } |
412 | | #endif /* TRE_DEBUG */ |
413 | | |
414 | | /* Go to the next character in the input string. */ |
415 | 0 | empty_br_match = 0; |
416 | 0 | trans_i = state; |
417 | 0 | if (trans_i->state && trans_i->assertions & ASSERT_BACKREF) |
418 | 0 | { |
419 | | /* This is a back reference state. All transitions leaving from |
420 | | this state have the same back reference "assertion". Instead |
421 | | of reading the next character, we match the back reference. */ |
422 | 0 | int so, eo, bt = trans_i->u.backref; |
423 | 0 | int bt_len; |
424 | 0 | int result; |
425 | |
|
426 | 0 | DPRINT((" should match back reference %d\n", bt)); |
427 | | /* Get the substring we need to match against. Remember to |
428 | | turn off REG_NOSUB temporarily. */ |
429 | 0 | tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & ~REG_NOSUB, |
430 | 0 | tnfa, tags, pos); |
431 | 0 | so = pmatch[bt].rm_so; |
432 | 0 | eo = pmatch[bt].rm_eo; |
433 | 0 | bt_len = eo - so; |
434 | |
|
435 | | #ifdef TRE_DEBUG |
436 | | { |
437 | | int slen; |
438 | | if (len < 0) |
439 | | slen = bt_len; |
440 | | else |
441 | | slen = MIN(bt_len, len - pos); |
442 | | |
443 | | if (type == STR_BYTE) |
444 | | { |
445 | | DPRINT((" substring (len %d) is [%d, %d[: '%.*s'\n", |
446 | | bt_len, so, eo, bt_len, (char*)string + so)); |
447 | | DPRINT((" current string is '%.*s'\n", slen, str_byte - 1)); |
448 | | } |
449 | | #ifdef TRE_WCHAR |
450 | | else if (type == STR_WIDE) |
451 | | { |
452 | | DPRINT((" substring (len %d) is [%d, %d[: '%.*" STRF "'\n", |
453 | | bt_len, so, eo, bt_len, (wchar_t*)string + so)); |
454 | | DPRINT((" current string is '%.*" STRF "'\n", |
455 | | slen, str_wide - 1)); |
456 | | } |
457 | | #endif /* TRE_WCHAR */ |
458 | | } |
459 | | #endif |
460 | |
|
461 | 0 | if (len < 0) |
462 | 0 | { |
463 | 0 | if (type == STR_USER) |
464 | 0 | result = str_source->compare((unsigned)so, (unsigned)pos, |
465 | 0 | (unsigned)bt_len, |
466 | 0 | str_source->context); |
467 | 0 | #ifdef TRE_WCHAR |
468 | 0 | else if (type == STR_WIDE) |
469 | 0 | result = wcsncmp((const wchar_t*)string + so, str_wide - 1, |
470 | 0 | (size_t)bt_len); |
471 | 0 | #endif /* TRE_WCHAR */ |
472 | 0 | else |
473 | 0 | result = strncmp((const char*)string + so, str_byte - 1, |
474 | 0 | (size_t)bt_len); |
475 | 0 | } |
476 | 0 | else if (len - pos < bt_len) |
477 | 0 | result = 1; |
478 | 0 | #ifdef TRE_WCHAR |
479 | 0 | else if (type == STR_WIDE) |
480 | 0 | result = wmemcmp((const wchar_t*)string + so, str_wide - 1, |
481 | 0 | (size_t)bt_len); |
482 | 0 | #endif /* TRE_WCHAR */ |
483 | 0 | else |
484 | 0 | result = memcmp((const char*)string + so, str_byte - 1, |
485 | 0 | (size_t)bt_len); |
486 | |
|
487 | 0 | if (result == 0) |
488 | 0 | { |
489 | | /* Back reference matched. Check for infinite loop. */ |
490 | 0 | if (bt_len == 0) |
491 | 0 | empty_br_match = 1; |
492 | 0 | if (empty_br_match && states_seen[trans_i->state_id]) |
493 | 0 | { |
494 | 0 | DPRINT((" avoid loop\n")); |
495 | 0 | goto backtrack; |
496 | 0 | } |
497 | | |
498 | 0 | states_seen[trans_i->state_id] = empty_br_match; |
499 | | |
500 | | /* Advance in input string and resync `prev_c', `next_c' |
501 | | and pos. */ |
502 | 0 | DPRINT((" back reference matched\n")); |
503 | 0 | str_byte += bt_len - 1; |
504 | 0 | #ifdef TRE_WCHAR |
505 | 0 | str_wide += bt_len - 1; |
506 | 0 | #endif /* TRE_WCHAR */ |
507 | 0 | pos += bt_len - 1; |
508 | 0 | GET_NEXT_WCHAR(); |
509 | 0 | DPRINT((" pos now %d\n", pos)); |
510 | 0 | } |
511 | 0 | else |
512 | 0 | { |
513 | 0 | DPRINT((" back reference did not match\n")); |
514 | 0 | goto backtrack; |
515 | 0 | } |
516 | 0 | } |
517 | 0 | else |
518 | 0 | { |
519 | | /* Check for end of string. */ |
520 | 0 | if (len < 0) |
521 | 0 | { |
522 | 0 | if (type == STR_USER) |
523 | 0 | { |
524 | 0 | if (str_user_end) |
525 | 0 | goto backtrack; |
526 | 0 | } |
527 | 0 | else if (next_c == L'\0') |
528 | 0 | goto backtrack; |
529 | 0 | } |
530 | 0 | else |
531 | 0 | { |
532 | 0 | if (pos >= len) |
533 | 0 | goto backtrack; |
534 | 0 | } |
535 | | |
536 | | /* Read the next character. */ |
537 | 0 | GET_NEXT_WCHAR(); |
538 | 0 | } |
539 | | |
540 | 0 | next_state = NULL; |
541 | 0 | for (trans_i = state; trans_i->state; trans_i++) |
542 | 0 | { |
543 | 0 | DPRINT((" transition %d-%d (%c-%c) %d to %d\n", |
544 | 0 | trans_i->code_min, trans_i->code_max, |
545 | 0 | trans_i->code_min, trans_i->code_max, |
546 | 0 | trans_i->assertions, trans_i->state_id)); |
547 | 0 | if (trans_i->code_min <= (tre_cint_t)prev_c |
548 | 0 | && trans_i->code_max >= (tre_cint_t)prev_c) |
549 | 0 | { |
550 | 0 | if (trans_i->assertions |
551 | 0 | && (CHECK_ASSERTIONS(trans_i->assertions) |
552 | 0 | || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags))) |
553 | 0 | { |
554 | 0 | DPRINT((" assertion failed\n")); |
555 | 0 | continue; |
556 | 0 | } |
557 | | |
558 | 0 | if (next_state == NULL) |
559 | 0 | { |
560 | | /* First matching transition. */ |
561 | 0 | DPRINT((" Next state is %d\n", trans_i->state_id)); |
562 | 0 | next_state = trans_i->state; |
563 | 0 | next_tags = trans_i->tags; |
564 | 0 | } |
565 | 0 | else |
566 | 0 | { |
567 | | /* Second matching transition. We may need to backtrack here |
568 | | to take this transition instead of the first one, so we |
569 | | push this transition in the backtracking stack so we can |
570 | | jump back here if needed. */ |
571 | 0 | DPRINT((" saving state %d for backtracking\n", |
572 | 0 | trans_i->state_id)); |
573 | 0 | BT_STACK_PUSH(pos, str_byte, str_wide, trans_i->state, |
574 | 0 | trans_i->state_id, next_c, tags, mbstate); |
575 | 0 | { |
576 | 0 | int *tmp; |
577 | 0 | for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++) |
578 | 0 | stack->item.tags[*tmp] = pos; |
579 | 0 | } |
580 | | #if 0 /* XXX - it's important not to look at all transitions here to keep |
581 | | the stack small! */ |
582 | | break; |
583 | | #endif |
584 | 0 | } |
585 | 0 | } |
586 | 0 | } |
587 | | |
588 | 0 | if (next_state != NULL) |
589 | 0 | { |
590 | | /* Matching transitions were found. Take the first one. */ |
591 | 0 | state = next_state; |
592 | | |
593 | | /* Update the tag values. */ |
594 | 0 | if (next_tags) |
595 | 0 | while (*next_tags >= 0) |
596 | 0 | tags[*next_tags++] = pos; |
597 | 0 | } |
598 | 0 | else |
599 | 0 | { |
600 | 0 | backtrack: |
601 | | /* A matching transition was not found. Try to backtrack. */ |
602 | 0 | if (stack->prev) |
603 | 0 | { |
604 | 0 | DPRINT((" backtracking\n")); |
605 | 0 | if (stack->item.state->assertions & ASSERT_BACKREF) |
606 | 0 | { |
607 | 0 | DPRINT((" states_seen[%d] = 0\n", |
608 | 0 | stack->item.state_id)); |
609 | 0 | states_seen[stack->item.state_id] = 0; |
610 | 0 | } |
611 | |
|
612 | 0 | BT_STACK_POP(); |
613 | 0 | } |
614 | 0 | else if (match_eo < 0) |
615 | 0 | { |
616 | | /* Try starting from a later position in the input string. */ |
617 | | /* Check for end of string. */ |
618 | 0 | if (len < 0) |
619 | 0 | { |
620 | 0 | if (next_c == L'\0') |
621 | 0 | { |
622 | 0 | DPRINT(("end of string.\n")); |
623 | 0 | break; |
624 | 0 | } |
625 | 0 | } |
626 | 0 | else |
627 | 0 | { |
628 | 0 | if (pos >= len) |
629 | 0 | { |
630 | 0 | DPRINT(("end of string.\n")); |
631 | 0 | break; |
632 | 0 | } |
633 | 0 | } |
634 | 0 | DPRINT(("restarting from next start position\n")); |
635 | 0 | next_c = (tre_char_t) next_c_start; |
636 | 0 | #ifdef TRE_MBSTATE |
637 | 0 | mbstate = mbstate_start; |
638 | 0 | #endif /* TRE_MBSTATE */ |
639 | 0 | str_byte = str_byte_start; |
640 | 0 | #ifdef TRE_WCHAR |
641 | 0 | str_wide = str_wide_start; |
642 | 0 | #endif /* TRE_WCHAR */ |
643 | 0 | goto retry; |
644 | 0 | } |
645 | 0 | else |
646 | 0 | { |
647 | 0 | DPRINT(("finished\n")); |
648 | 0 | break; |
649 | 0 | } |
650 | 0 | } |
651 | 0 | } |
652 | | |
653 | 0 | ret = match_eo >= 0 ? REG_OK : REG_NOMATCH; |
654 | 0 | *match_end_ofs = match_eo; |
655 | |
|
656 | 0 | error_exit: |
657 | 0 | tre_bt_mem_destroy(mem); |
658 | 0 | #ifndef TRE_USE_ALLOCA |
659 | 0 | if (tags) |
660 | 0 | xfree(tags); |
661 | 0 | if (pmatch) |
662 | 0 | xfree(pmatch); |
663 | 0 | if (states_seen) |
664 | 0 | xfree(states_seen); |
665 | 0 | #endif /* !TRE_USE_ALLOCA */ |
666 | |
|
667 | 0 | return ret; |
668 | 0 | } |