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 /* Unset TRE_USE_ALLOCA to avoid using the stack to hold all the state
38    info while running */
39 #undef TRE_USE_ALLOCA
40 
41 #ifdef TRE_USE_ALLOCA
42 /* AIX requires this to be the first thing in the file.	 */
43 #ifndef __GNUC__
44 # if HAVE_ALLOCA_H
45 #  include <alloca.h>
46 # else
47 #  ifdef _AIX
48  #pragma alloca
49 #  else
50 #   ifndef alloca /* predefined by HP cc +Olibcalls */
51 char *alloca ();
52 #   endif
53 #  endif
54 # endif
55 #endif
56 #endif /* TRE_USE_ALLOCA */
57 
58 #include <assert.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #ifdef HAVE_WCHAR_H
62 #include <wchar.h>
63 #endif /* HAVE_WCHAR_H */
64 #ifdef HAVE_WCTYPE_H
65 #include <wctype.h>
66 #endif /* HAVE_WCTYPE_H */
67 #ifndef TRE_WCHAR
68 #include <ctype.h>
69 #endif /* !TRE_WCHAR */
70 #ifdef HAVE_MALLOC_H
71 #include <malloc.h>
72 #endif /* HAVE_MALLOC_H */
73 
74 #include "tre-internal.h"
75 #include "tre-mem.h"
76 #include "tre-match-utils.h"
77 #include "tre.h"
78 #include "xmalloc.h"
79 
80 typedef struct {
81   int pos;
82   unsigned int pos_add_next;
83   const char *str_byte;
84 #ifdef TRE_WCHAR
85   const wchar_t *str_wide;
86 #endif /* TRE_WCHAR */
87   tre_tnfa_transition_t *state;
88   int state_id;
89   int next_c;
90   tre_tag_t *tags;
91 #ifdef TRE_MBSTATE
92   mbstate_t mbstate;
93 #endif /* TRE_MBSTATE */
94 } tre_backtrack_item_t;
95 
96 typedef struct tre_backtrack_struct {
97   tre_backtrack_item_t item;
98   struct tre_backtrack_struct *prev;
99   struct tre_backtrack_struct *next;
100 } *tre_backtrack_t;
101 
102 #ifdef TRE_WCHAR
103 #define BT_STACK_WIDE_IN(_str_wide)	stack->item.str_wide = (_str_wide)
104 #define BT_STACK_WIDE_OUT		(str_wide) = stack->item.str_wide
105 #else /* !TRE_WCHAR */
106 #define BT_STACK_WIDE_IN(_str_wide)
107 #define BT_STACK_WIDE_OUT
108 #endif /* !TRE_WCHAR */
109 
110 #ifdef TRE_MBSTATE
111 #define BT_STACK_MBSTATE_IN  stack->item.mbstate = (mbstate)
112 #define BT_STACK_MBSTATE_OUT (mbstate) = stack->item.mbstate
113 #else /* !TRE_MBSTATE */
114 #define BT_STACK_MBSTATE_IN
115 #define BT_STACK_MBSTATE_OUT
116 #endif /* !TRE_MBSTATE */
117 
118 
119 #ifdef TRE_USE_ALLOCA
120 #define tre_bt_mem_new		  tre_mem_newa
121 #define tre_bt_mem_alloc	  tre_mem_alloca
122 #define tre_bt_mem_destroy(obj)	  do { } while (0)
123 #else /* !TRE_USE_ALLOCA */
124 #define tre_bt_mem_new		  tre_mem_new
125 #define tre_bt_mem_alloc	  tre_mem_alloc
126 #define tre_bt_mem_destroy	  tre_mem_destroy
127 #endif /* !TRE_USE_ALLOCA */
128 
129 
130 #define BT_STACK_PUSH(_pos, _pos_add_next, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \
131   do									      \
132     {									      \
133       if (!stack->next)							      \
134 	{								      \
135 	  tre_backtrack_t s;						      \
136 	  s = tre_bt_mem_alloc(mem, sizeof(*s));			      \
137 	  if (!s)							      \
138 	    {								      \
139 	      tre_bt_mem_destroy(mem);					      \
140 	      if (tags)							      \
141 		xfree(tags);						      \
142 	      if (pmatch)						      \
143 		xfree(pmatch);						      \
144 	      if (states_seen)						      \
145 		xfree(states_seen);					      \
146 	      return REG_ESPACE;					      \
147 	    }								      \
148 	  s->prev = stack;						      \
149 	  s->next = NULL;						      \
150 	  s->item.tags = tre_bt_mem_alloc(mem,				      \
151 					  num_tags * sizeof(*tags));          \
152 	  if (!s->item.tags)						      \
153 	    {								      \
154 	      tre_bt_mem_destroy(mem);					      \
155 	      if (tags)							      \
156 		xfree(tags);						      \
157 	      if (pmatch)						      \
158 		xfree(pmatch);						      \
159 	      if (states_seen)						      \
160 		xfree(states_seen);					      \
161 	      return REG_ESPACE;					      \
162 	    }								      \
163 	  stack->next = s;						      \
164 	  stack = s;							      \
165 	}								      \
166       else								      \
167 	stack = stack->next;						      \
168       stack->item.pos = (_pos);						      \
169       stack->item.pos_add_next = (_pos_add_next);			      \
170       stack->item.str_byte = (_str_byte);				      \
171       BT_STACK_WIDE_IN(_str_wide);					      \
172       stack->item.state = (_state);					      \
173       stack->item.state_id = (_state_id);				      \
174       stack->item.next_c = (_next_c);					      \
175       memcpy(stack->item.tags, (_tags), num_tags * sizeof(*(_tags)));         \
176       BT_STACK_MBSTATE_IN;						      \
177     }									      \
178   while (/*CONSTCOND*/0)
179 
180 #define BT_STACK_POP()							      \
181   do									      \
182     {									      \
183       assert(stack->prev);						      \
184       pos = stack->item.pos;						      \
185       pos_add_next = stack->item.pos_add_next;				      \
186       str_byte = stack->item.str_byte;					      \
187       BT_STACK_WIDE_OUT;						      \
188       state = stack->item.state;					      \
189       next_c = stack->item.next_c;					      \
190       memcpy(tags, stack->item.tags, num_tags * sizeof(*tags));               \
191       BT_STACK_MBSTATE_OUT;						      \
192       stack = stack->prev;						      \
193     }									      \
194   while (/*CONSTCOND*/0)
195 
196 #undef MIN
197 #define MIN(a, b) ((a) <= (b) ? (a) : (b))
198 
199 reg_errcode_t
200 tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string,
201 		       int len, tre_str_type_t type, tre_tag_t *match_tags,
202 		       int eflags, int *match_end_ofs)
203 {
204   /* State variables required by GET_NEXT_WCHAR. */
205   tre_char_t prev_c = 0, next_c = 0;
206   const char *str_byte = string;
207   int pos = 0;
208   unsigned int pos_add_next = 1;
209 #ifdef TRE_WCHAR
210   const wchar_t *str_wide = string;
211 #ifdef TRE_MBSTATE
212   mbstate_t mbstate;
213 #endif /* TRE_MBSTATE */
214 #endif /* TRE_WCHAR */
215   int reg_notbol = eflags & REG_NOTBOL;
216   int reg_noteol = eflags & REG_NOTEOL;
217   int reg_newline = tnfa->cflags & REG_NEWLINE;
218   int i;
219 
220   /* These are used to remember the necessary values of the above
221      variables to return to the position where the current search
222      started from. */
223   int next_c_start;
224   const char *str_byte_start;
225   int pos_start = -1;
226 #ifdef TRE_WCHAR
227   const wchar_t *str_wide_start;
228 #endif /* TRE_WCHAR */
229 #ifdef TRE_MBSTATE
230   mbstate_t mbstate_start;
231 #endif /* TRE_MBSTATE */
232 
233   /* End offset of best match so far, or -1 if no match found yet. */
234   int match_eo = -1;
235   /* Tag arrays. */
236   int *next_tags;
237   tre_tag_t *tags = NULL;
238   /* Current TNFA state. */
239   tre_tnfa_transition_t *state;
240   int *states_seen = NULL;
241 
242   /* Memory allocator to for allocating the backtracking stack. */
243   tre_mem_t mem = tre_bt_mem_new();
244 
245   /* The backtracking stack. */
246   tre_backtrack_t stack;
247 
248   tre_tnfa_transition_t *trans_i;
249   regmatch_t *pmatch = NULL;
250   reg_errcode_t ret;
251 
252   int num_tags = tnfa->num_tags;
253   int touch = 1;
254   char *buf;
255   int tbytes;
256 
257 #ifdef TRE_MBSTATE
258   memset(&mbstate, '\0', sizeof(mbstate));
259 #endif /* TRE_MBSTATE */
260 #ifndef TRE_USE_ALLOCA
261   buf = NULL;
262 #endif
263 
264   if (!mem)
265     return REG_ESPACE;
266   stack = tre_bt_mem_alloc(mem, sizeof(*stack));
267   if (!stack)
268     {
269       ret = REG_ESPACE;
270       goto error_exit;
271     }
272   stack->prev = NULL;
273   stack->next = NULL;
274 
275   DPRINT(("tnfa_execute_backtrack, input type %d\n", type));
276   DPRINT(("len = %d\n", len));
277 
278   {
279     int pbytes, sbytes, total_bytes;
280     char *tmp_buf;
281     /* Compute the length of the block we need. */
282     tbytes = sizeof(*tags) * num_tags;
283     pbytes = sizeof(*pmatch) * tnfa->num_submatches;
284     sbytes = sizeof(*states_seen) * tnfa->num_states;
285     total_bytes =
286       (sizeof(long) - 1) * 2 /* for alignment paddings */
287       + tbytes + pbytes + sbytes;
288 
289     DPRINT(("tre_tnfa_run_backtrack, allocate %d bytes\n", total_bytes));
290     /* Allocate the memory. */
291 #ifdef TRE_USE_ALLOCA
292     buf = alloca(total_bytes);
293 #else /* !TRE_USE_ALLOCA */
294     buf = xmalloc((unsigned)total_bytes);
295 #endif /* !TRE_USE_ALLOCA */
296     if (buf == NULL)
297       return REG_ESPACE;
298 
299     /* Get the various pointers within tmp_buf (properly aligned). */
300     tags = (void *)buf;
301     tmp_buf = buf + tbytes;
302     tmp_buf += ALIGN(tmp_buf, long);
303     pmatch = (void *)tmp_buf;
304     tmp_buf += pbytes;
305     tmp_buf += ALIGN(tmp_buf, long);
306     states_seen = (void *)tmp_buf;
307   }
308 
309  retry:
310   {
311     memset(tags, 0, num_tags * sizeof(*tags));
312     if (match_tags)
313       memset(match_tags, 0, num_tags * sizeof(*match_tags));
314     for (i = 0; i < tnfa->num_states; i++)
315       states_seen[i] = 0;
316   }
317 
318   state = NULL;
319   pos = pos_start;
320   GET_NEXT_WCHAR();
321   pos_start = pos;
322   next_c_start = next_c;
323   str_byte_start = str_byte;
324 #ifdef TRE_WCHAR
325   str_wide_start = str_wide;
326 #endif /* TRE_WCHAR */
327 #ifdef TRE_MBSTATE
328   mbstate_start = mbstate;
329 #endif /* TRE_MBSTATE */
330 
331   /* Handle initial states. */
332   next_tags = NULL;
333   for (trans_i = tnfa->initial; trans_i->state; trans_i++)
334     {
335       DPRINT(("> init %p, prev_c %lc\n", trans_i->state, (tre_cint_t)prev_c));
336       if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions))
337 	{
338 	  DPRINT(("assert failed\n"));
339 	  continue;
340 	}
341       if (state == NULL)
342 	{
343 	  /* Start from this state. */
344 	  state = trans_i->state;
345 	  next_tags = trans_i->tags;
346 	}
347       else
348 	{
349 	  /* Backtrack to this state. */
350 	  DPRINT(("saving state %d for backtracking\n", trans_i->state_id));
351 	  BT_STACK_PUSH(pos, pos_add_next, str_byte, str_wide, trans_i->state,
352 			trans_i->state_id, next_c, tags, mbstate);
353 	  {
354 	    int *tmp = trans_i->tags;
355 	    if (tmp)
356 	      {
357 		while (*tmp >= 0)
358 		  tre_tag_set(stack->item.tags, *tmp++, pos, touch);
359 		touch++;
360 	      }
361 	  }
362 	}
363     }
364 
365   if (next_tags)
366     {
367       for (; *next_tags >= 0; next_tags++)
368 	tre_tag_set(tags, *next_tags, pos, touch);
369       touch++;
370     }
371 
372 
373   DPRINT(("entering match loop, pos %d, str_byte %p\n", pos, str_byte));
374   DPRINT(("pos:chr/code | state and tags\n"));
375   DPRINT(("-------------+------------------------------------------------\n"));
376 
377   if (state == NULL)
378     goto backtrack;
379 
380   while (/*CONSTCOND*/1)
381     {
382       tre_tnfa_transition_t *next_state;
383       int empty_br_match;
384 
385       DPRINT(("start loop\n"));
386 
387       if (match_eo >= 0 && tnfa->num_minimals)
388 	{
389 	  int skip = 0;
390 #ifdef TRE_DEBUG
391 	  DPRINT(("Checking minimal conditions: match_eo=%d match_tags=",
392 		  match_eo));
393 	  tre_print_tags(match_tags, tnfa->num_tags);
394 	  DPRINT(("\n"));
395 #endif /* TRE_DEBUG */
396 	  for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2)
397 	    {
398 	      int end = tnfa->minimal_tags[i];
399 	      int start = tnfa->minimal_tags[i + 1];
400 	      DPRINT(("  Minimal start %d, end %d\n", start, end));
401 	      if (tre_minimal_tag_order(start, end, match_tags, tags) > 0)
402 		{
403 		  skip = 1;
404 		  break;
405 		}
406 	    }
407 	  if (!skip)
408 	    {
409 #ifdef TRE_DEBUG
410 	      DPRINT(("	 Keeping tags="));
411 	      tre_print_tags(tags, tnfa->num_tags);
412 	      DPRINT(("\n"));
413 #endif /* TRE_DEBUG */
414 	    }
415 	  else
416 	    {
417 #ifdef TRE_DEBUG
418 	      DPRINT(("	 Throwing out tags="));
419 	      tre_print_tags(tags, tnfa->num_tags);
420 	      DPRINT(("\n"));
421 #endif /* TRE_DEBUG */
422 	      goto backtrack;
423 	    }
424 	}
425 
426       if (state == tnfa->final)
427 	{
428 	  DPRINT(("  match found, match_eo=%d pos=%d\n", match_eo, pos));
429 
430 	  if (match_eo >= 0 && tnfa->num_minimals)
431 	    {
432 	      int compare = 0;
433 #ifdef TRE_DEBUG
434 	      DPRINT(("Checking minimal conditions: match_eo=%d "
435 		      "match_tags=", match_eo));
436 	      tre_print_tags(match_tags, tnfa->num_tags);
437 	      DPRINT(("\n"));
438 #endif /* TRE_DEBUG */
439 	      for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2)
440 		{
441 		  int end = tnfa->minimal_tags[i];
442 		  int start = tnfa->minimal_tags[i + 1];
443 		  DPRINT(("  Minimal start %d, end %d\n", start, end));
444 		  if ((compare = tre_minimal_tag_order(start, end,
445 					   match_tags, tags)) != 0)
446 		    break;
447 		}
448 	      if (compare > 0)
449 		{
450 #ifdef TRE_DEBUG
451 		  DPRINT(("	 Throwing out new match, tags="));
452 		  tre_print_tags(tags, tnfa->num_tags);
453 		  DPRINT(("\n"));
454 #endif /* TRE_DEBUG */
455 		  goto backtrack;
456 		}
457 	      else if (compare < 0)
458 		{
459 #ifdef TRE_DEBUG
460 		  DPRINT(("	 Throwing out old match, tags="));
461 		  tre_print_tags(match_tags, tnfa->num_tags);
462 		  DPRINT(("\n"));
463 #endif /* TRE_DEBUG */
464 		  match_eo = -1;
465 		}
466 	    }
467 
468 	  if (match_eo < pos
469 	      || (match_eo == pos
470 		  && match_tags
471 		  && tre_tag_order(tnfa->num_tags, tnfa->tag_directions,
472 				   tags, match_tags)))
473 	    {
474 	      /* This match wins the previous match. */
475 #ifdef TRE_DEBUG
476 	      DPRINT(("	 win previous tags="));
477 	      tre_print_tags(tags, tnfa->num_tags);
478 	      DPRINT(("\n"));
479 #endif /* TRE_DEBUG */
480 	      match_eo = pos;
481 	      if (match_tags)
482 		memcpy(match_tags, tags, num_tags * sizeof(*tags));
483 	    }
484 	  /* Our TNFAs never have transitions leaving from the final state,
485 	     so we jump right to backtracking. */
486 	  goto backtrack;
487 	}
488 
489 #ifdef TRE_DEBUG
490       DPRINT(("%3d:%2lc/%05d | %p ", pos, (tre_cint_t)next_c, (int)next_c,
491 	      state));
492       tre_print_tags(tags, tnfa->num_tags);
493       DPRINT(("\n"));
494 #endif /* TRE_DEBUG */
495 
496       /* Go to the next character in the input string. */
497       empty_br_match = 0;
498       trans_i = state;
499       if (trans_i->state && trans_i->assertions & ASSERT_BACKREF)
500 	{
501 	  /* This is a back reference state.  All transitions leaving from
502 	     this state have the same back reference "assertion".  Instead
503 	     of reading the next character, we match the back reference. */
504 	  int so, eo, bt = trans_i->u.backref;
505 	  int bt_len;
506 	  int result;
507 
508 	  DPRINT(("  should match back reference %d\n", bt));
509 	  /* Get the substring we need to match against.  Remember to
510 	     turn off REG_NOSUB temporarily. */
511 	  ret = tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & ~REG_NOSUB,
512 			  tnfa, tags, pos);
513 	  if (ret != REG_OK) goto error_exit;
514 	  so = pmatch[bt].rm_so;
515 	  eo = pmatch[bt].rm_eo;
516 	  bt_len = eo - so;
517 
518 #ifdef TRE_DEBUG
519 	  {
520 	    int slen;
521 	    if (len < 0)
522 	      slen = bt_len;
523 	    else
524 	      slen = MIN(bt_len, len - pos);
525 
526 	    if (type == STR_BYTE)
527 	      {
528 		DPRINT(("  substring (len %d) is [%d, %d]: '%.*s'\n",
529 			bt_len, so, eo, bt_len, (char*)string + so));
530 		DPRINT(("  current string is '%.*s'\n", slen, str_byte - 1));
531 	      }
532 #ifdef TRE_WCHAR
533 	    else if (type == STR_WIDE)
534 	      {
535 		DPRINT(("  substring (len %d) is [%d, %d]: '%.*" STRF "'\n",
536 			bt_len, so, eo, bt_len, (wchar_t*)string + so));
537 		DPRINT(("  current string is '%.*" STRF "'\n",
538 			slen, str_wide - 1));
539 	      }
540 #endif /* TRE_WCHAR */
541 	  }
542 #endif
543 
544 	  if (so < 0)
545 	    {
546 	      result = 1; /* Back reference of nomatch doesn't match */
547 	    }
548 	  else if (len < 0)
549 	    {
550 #ifdef TRE_WCHAR
551 	      if (type == STR_WIDE)
552 		result = wcsncmp((const wchar_t*)string + so, str_wide - 1,
553 				 (size_t)bt_len);
554 	      else
555 #endif /* TRE_WCHAR */
556 	      result = strncmp((const char*)string + so, str_byte - 1,
557 				 (size_t)bt_len);
558 	    }
559 	  else if (len - pos < bt_len)
560 	    result = 1;
561 #ifdef TRE_WCHAR
562 	  else if (type == STR_WIDE)
563 	    result = wmemcmp((const wchar_t*)string + so, str_wide - 1,
564 			     (size_t)bt_len);
565 #endif /* TRE_WCHAR */
566 	  else
567 	    result = memcmp((const char*)string + so, str_byte - 1,
568 			    (size_t)bt_len);
569 
570 	  if (result == 0)
571 	    {
572 	      /* Back reference matched.  Check for infinite loop. */
573 	      if (bt_len == 0)
574 		empty_br_match = 1;
575 	      if (empty_br_match && states_seen[trans_i->state_id])
576 		{
577 		  DPRINT(("  avoid loop\n"));
578 		  goto backtrack;
579 		}
580 
581 	      states_seen[trans_i->state_id] = empty_br_match;
582 
583 	      /* Advance in input string and resync `prev_c', `next_c'
584 		 and pos. */
585 	      DPRINT(("	 back reference matched\n"));
586 	      str_byte += bt_len - 1;
587 #ifdef TRE_WCHAR
588 	      str_wide += bt_len - 1;
589 #endif /* TRE_WCHAR */
590 	      pos += bt_len - 1;
591 	      GET_NEXT_WCHAR();
592 	      DPRINT(("	 pos now %d\n", pos));
593 	    }
594 	  else
595 	    {
596 	      DPRINT(("	 back reference did not match\n"));
597 	      goto backtrack;
598 	    }
599 	}
600       else
601 	{
602 	  /* Check for end of string. */
603 	  if (len < 0)
604 	    {
605 	      if (next_c == L'\0')
606 		goto backtrack;
607 	    }
608 	  else
609 	    {
610 	      if (pos >= len)
611 		goto backtrack;
612 	    }
613 
614 	  /* Read the next character. */
615 	  GET_NEXT_WCHAR();
616 	}
617 
618       next_state = NULL;
619       for (trans_i = state; trans_i->state; trans_i++)
620 	{
621 	  DPRINT(("  transition %d-%d (%c-%c) %d to %d\n",
622 		  trans_i->code_min, trans_i->code_max,
623 		  trans_i->code_min, trans_i->code_max,
624 		  trans_i->assertions, trans_i->state_id));
625 	  if (trans_i->code_min <= (tre_cint_t)prev_c
626 	      && trans_i->code_max >= (tre_cint_t)prev_c)
627 	    {
628 	      if (trans_i->assertions
629 		  && (CHECK_ASSERTIONS(trans_i->assertions)
630 		      || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
631 		{
632 		  DPRINT(("  assertion failed\n"));
633 		  continue;
634 		}
635 
636 	      if (next_state == NULL)
637 		{
638 		  /* First matching transition. */
639 		  DPRINT(("  Next state is %d\n", trans_i->state_id));
640 		  next_state = trans_i->state;
641 		  next_tags = trans_i->tags;
642 		}
643 	      else
644 		{
645 		  /* Second matching transition.  We may need to backtrack here
646 		     to take this transition instead of the first one, so we
647 		     push this transition in the backtracking stack so we can
648 		     jump back here if needed. */
649 		  DPRINT(("  saving state %d for backtracking\n",
650 			  trans_i->state_id));
651 		  BT_STACK_PUSH(pos, pos_add_next, str_byte, str_wide,
652 				trans_i->state, trans_i->state_id, next_c,
653 				tags, mbstate);
654 		  {
655 		    int *tmp;
656 		    for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++)
657 		      tre_tag_set(stack->item.tags, *tmp, pos, touch);
658 		    touch++;
659 		  }
660 #if 0 /* XXX - it's important not to look at all transitions here to keep
661 	 the stack small! */
662 		  break;
663 #endif
664 		}
665 	    }
666 	}
667 
668       if (next_state != NULL)
669 	{
670 	  /* Matching transitions were found.  Take the first one. */
671 	  state = next_state;
672 
673 	  /* Update the tag values. */
674 	  if (next_tags)
675 	    {
676 	      while (*next_tags >= 0)
677 		tre_tag_set(tags, *next_tags++, pos, touch);
678 	      touch++;
679 	    }
680 	}
681       else
682 	{
683 	backtrack:
684 	  /* A matching transition was not found.  Try to backtrack. */
685 	  if (stack->prev)
686 	    {
687 	      DPRINT(("	 backtracking\n"));
688 	      if (stack->item.state->assertions & ASSERT_BACKREF)
689 		{
690 		  DPRINT(("  states_seen[%d] = 0\n",
691 			  stack->item.state_id));
692 		  states_seen[stack->item.state_id] = 0;
693 		}
694 
695 	      BT_STACK_POP();
696 	    }
697 	  else if (match_eo < 0)
698 	    {
699 	      /* Try starting from a later position in the input string. */
700 	      /* Check for end of string. */
701 	      if (pos == pos_start)
702 		{
703 		  if (len < 0)
704 		    {
705 		      if (next_c == L'\0')
706 			{
707 			  DPRINT(("end of string.\n"));
708 			  break;
709 			}
710 		    }
711 		  else
712 		    {
713 		      if (pos >= len)
714 			{
715 			  DPRINT(("end of string.\n"));
716 			  break;
717 			}
718 		    }
719 		}
720 	      DPRINT(("restarting from next start position\n"));
721 	      next_c = next_c_start;
722 #ifdef TRE_MBSTATE
723 	      mbstate = mbstate_start;
724 #endif /* TRE_MBSTATE */
725 	      str_byte = str_byte_start;
726 #ifdef TRE_WCHAR
727 	      str_wide = str_wide_start;
728 #endif /* TRE_WCHAR */
729 	      goto retry;
730 	    }
731 	  else
732 	    {
733 	      DPRINT(("finished\n"));
734 	      break;
735 	    }
736 	}
737     }
738 
739   ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
740   *match_end_ofs = match_eo;
741 
742  error_exit:
743   tre_bt_mem_destroy(mem);
744 #ifndef TRE_USE_ALLOCA
745   if (buf)
746     xfree(buf);
747 #endif /* !TRE_USE_ALLOCA */
748 
749   return ret;
750 }
751