xref: /netbsd-src/external/bsd/tre/dist/lib/tre-match-approx.c (revision 7cb13d6054cb3cdc330e3f0f9b7035db7b54013b)
163d4abf0Sagc /*
263d4abf0Sagc   tre-match-approx.c - TRE approximate regex matching engine
363d4abf0Sagc 
463d4abf0Sagc   This software is released under a BSD-style license.
563d4abf0Sagc   See the file LICENSE for details and copyright.
663d4abf0Sagc 
763d4abf0Sagc */
863d4abf0Sagc #ifdef HAVE_CONFIG_H
963d4abf0Sagc #include <config.h>
1063d4abf0Sagc #endif /* HAVE_CONFIG_H */
1163d4abf0Sagc 
12f2a3d147Schristos #include "tre-alloca.h"
1363d4abf0Sagc 
1463d4abf0Sagc #define __USE_STRING_INLINES
1563d4abf0Sagc #undef __NO_INLINE__
1663d4abf0Sagc 
1763d4abf0Sagc #include <assert.h>
1863d4abf0Sagc #include <stdlib.h>
1963d4abf0Sagc #include <string.h>
2063d4abf0Sagc #include <limits.h>
2163d4abf0Sagc #ifdef HAVE_WCHAR_H
2263d4abf0Sagc #include <wchar.h>
2363d4abf0Sagc #endif /* HAVE_WCHAR_H */
2463d4abf0Sagc #ifdef HAVE_WCTYPE_H
2563d4abf0Sagc #include <wctype.h>
2663d4abf0Sagc #endif /* HAVE_WCTYPE_H */
2763d4abf0Sagc #ifndef TRE_WCHAR
2863d4abf0Sagc #include <ctype.h>
2963d4abf0Sagc #endif /* !TRE_WCHAR */
3063d4abf0Sagc #ifdef HAVE_MALLOC_H
3163d4abf0Sagc #include <malloc.h>
3263d4abf0Sagc #endif /* HAVE_MALLOC_H */
33*b3178392Srin #include <stdint.h>
3463d4abf0Sagc 
3563d4abf0Sagc #include "tre-internal.h"
3663d4abf0Sagc #include "tre-match-utils.h"
3763d4abf0Sagc #include "tre.h"
3863d4abf0Sagc #include "xmalloc.h"
3963d4abf0Sagc 
4063d4abf0Sagc #define TRE_M_COST	0
4163d4abf0Sagc #define TRE_M_NUM_INS	1
4263d4abf0Sagc #define TRE_M_NUM_DEL	2
4363d4abf0Sagc #define TRE_M_NUM_SUBST 3
4463d4abf0Sagc #define TRE_M_NUM_ERR	4
4563d4abf0Sagc #define TRE_M_LAST	5
4663d4abf0Sagc 
4763d4abf0Sagc #define TRE_M_MAX_DEPTH 3
4863d4abf0Sagc 
4963d4abf0Sagc typedef struct {
5063d4abf0Sagc   /* State in the TNFA transition table. */
5163d4abf0Sagc   tre_tnfa_transition_t *state;
5263d4abf0Sagc   /* Position in input string. */
5363d4abf0Sagc   int pos;
5463d4abf0Sagc   /* Tag values. */
5563d4abf0Sagc   int *tags;
5663d4abf0Sagc   /* Matching parameters. */
5763d4abf0Sagc   regaparams_t params;
5863d4abf0Sagc   /* Nesting depth of parameters.  This is used as an index in
5963d4abf0Sagc      the `costs' array. */
6063d4abf0Sagc   int depth;
6163d4abf0Sagc   /* Costs and counter values for different parameter nesting depths. */
6263d4abf0Sagc   int costs[TRE_M_MAX_DEPTH + 1][TRE_M_LAST];
6363d4abf0Sagc } tre_tnfa_approx_reach_t;
6463d4abf0Sagc 
6563d4abf0Sagc 
6663d4abf0Sagc #ifdef TRE_DEBUG
6763d4abf0Sagc /* Prints the `reach' array in a readable fashion with DPRINT. */
6863d4abf0Sagc static void
tre_print_reach(const tre_tnfa_t * tnfa,tre_tnfa_approx_reach_t * reach,int pos,size_t num_tags)6963d4abf0Sagc tre_print_reach(const tre_tnfa_t *tnfa, tre_tnfa_approx_reach_t *reach,
70f2a3d147Schristos 		int pos, size_t num_tags)
7163d4abf0Sagc {
7263d4abf0Sagc   int id;
7363d4abf0Sagc 
7463d4abf0Sagc   /* Print each state on one line. */
7563d4abf0Sagc   DPRINT(("  reach:\n"));
7663d4abf0Sagc   for (id = 0; id < tnfa->num_states; id++)
7763d4abf0Sagc     {
7863d4abf0Sagc       int i, j;
7963d4abf0Sagc       if (reach[id].pos < pos)
8063d4abf0Sagc 	continue;  /* Not reached. */
8163d4abf0Sagc       DPRINT(("	 %03d, costs ", id));
8263d4abf0Sagc       for (i = 0; i <= reach[id].depth; i++)
8363d4abf0Sagc 	{
8463d4abf0Sagc 	  DPRINT(("["));
8563d4abf0Sagc 	  for (j = 0; j < TRE_M_LAST; j++)
8663d4abf0Sagc 	    {
8763d4abf0Sagc 	      DPRINT(("%2d", reach[id].costs[i][j]));
8863d4abf0Sagc 	      if (j + 1 < TRE_M_LAST)
8963d4abf0Sagc 		DPRINT((","));
9063d4abf0Sagc 	    }
9163d4abf0Sagc 	  DPRINT(("]"));
9263d4abf0Sagc 	  if (i + 1 <= reach[id].depth)
9363d4abf0Sagc 	    DPRINT((", "));
9463d4abf0Sagc 	}
9563d4abf0Sagc       DPRINT(("\n	tags "));
9663d4abf0Sagc       for (i = 0; i < num_tags; i++)
9763d4abf0Sagc 	{
9863d4abf0Sagc 	  DPRINT(("%02d", reach[id].tags[i]));
9963d4abf0Sagc 	  if (i + 1 < num_tags)
10063d4abf0Sagc 	    DPRINT((","));
10163d4abf0Sagc 	}
10263d4abf0Sagc       DPRINT(("\n"));
10363d4abf0Sagc     }
10463d4abf0Sagc   DPRINT(("\n"));
10563d4abf0Sagc }
10663d4abf0Sagc #endif /* TRE_DEBUG */
10763d4abf0Sagc 
10863d4abf0Sagc 
10963d4abf0Sagc /* Sets the matching parameters in `reach' to the ones defined in the `pa'
11063d4abf0Sagc    array.  If `pa' specifies default values, they are taken from
11163d4abf0Sagc    `default_params'. */
11263d4abf0Sagc inline static void
tre_set_params(tre_tnfa_approx_reach_t * reach,int * pa,regaparams_t default_params)11363d4abf0Sagc tre_set_params(tre_tnfa_approx_reach_t *reach,
11463d4abf0Sagc 	       int *pa, regaparams_t default_params)
11563d4abf0Sagc {
11663d4abf0Sagc   int value;
11763d4abf0Sagc 
11863d4abf0Sagc   /* If depth is increased reset costs and counters to zero for the
11963d4abf0Sagc      new levels. */
12063d4abf0Sagc   value = pa[TRE_PARAM_DEPTH];
12163d4abf0Sagc   assert(value <= TRE_M_MAX_DEPTH);
12263d4abf0Sagc   if (value > reach->depth)
12363d4abf0Sagc     {
12463d4abf0Sagc       int i, j;
12563d4abf0Sagc       for (i = reach->depth + 1; i <= value; i++)
12663d4abf0Sagc 	for (j = 0; j < TRE_M_LAST; j++)
12763d4abf0Sagc 	  reach->costs[i][j] = 0;
12863d4abf0Sagc     }
12963d4abf0Sagc   reach->depth = value;
13063d4abf0Sagc 
13163d4abf0Sagc   /* Set insert cost. */
13263d4abf0Sagc   value = pa[TRE_PARAM_COST_INS];
13363d4abf0Sagc   if (value == TRE_PARAM_DEFAULT)
13463d4abf0Sagc     reach->params.cost_ins = default_params.cost_ins;
13563d4abf0Sagc   else if (value != TRE_PARAM_UNSET)
13663d4abf0Sagc     reach->params.cost_ins = value;
13763d4abf0Sagc 
13863d4abf0Sagc   /* Set delete cost. */
13963d4abf0Sagc   value = pa[TRE_PARAM_COST_DEL];
14063d4abf0Sagc   if (value == TRE_PARAM_DEFAULT)
14163d4abf0Sagc     reach->params.cost_del = default_params.cost_del;
14263d4abf0Sagc   else if (value != TRE_PARAM_UNSET)
14363d4abf0Sagc     reach->params.cost_del = value;
14463d4abf0Sagc 
14563d4abf0Sagc   /* Set substitute cost. */
14663d4abf0Sagc   value = pa[TRE_PARAM_COST_SUBST];
14763d4abf0Sagc   if (value == TRE_PARAM_DEFAULT)
14863d4abf0Sagc     reach->params.cost_subst = default_params.cost_subst;
14963d4abf0Sagc   else
15063d4abf0Sagc     reach->params.cost_subst = value;
15163d4abf0Sagc 
15263d4abf0Sagc   /* Set maximum cost. */
15363d4abf0Sagc   value = pa[TRE_PARAM_COST_MAX];
15463d4abf0Sagc   if (value == TRE_PARAM_DEFAULT)
15563d4abf0Sagc     reach->params.max_cost = default_params.max_cost;
15663d4abf0Sagc   else if (value != TRE_PARAM_UNSET)
15763d4abf0Sagc     reach->params.max_cost = value;
15863d4abf0Sagc 
15963d4abf0Sagc   /* Set maximum inserts. */
16063d4abf0Sagc   value = pa[TRE_PARAM_MAX_INS];
16163d4abf0Sagc   if (value == TRE_PARAM_DEFAULT)
16263d4abf0Sagc     reach->params.max_ins = default_params.max_ins;
16363d4abf0Sagc   else if (value != TRE_PARAM_UNSET)
16463d4abf0Sagc     reach->params.max_ins = value;
16563d4abf0Sagc 
16663d4abf0Sagc   /* Set maximum deletes. */
16763d4abf0Sagc   value = pa[TRE_PARAM_MAX_DEL];
16863d4abf0Sagc   if (value == TRE_PARAM_DEFAULT)
16963d4abf0Sagc     reach->params.max_del = default_params.max_del;
17063d4abf0Sagc   else if (value != TRE_PARAM_UNSET)
17163d4abf0Sagc     reach->params.max_del = value;
17263d4abf0Sagc 
17363d4abf0Sagc   /* Set maximum substitutes. */
17463d4abf0Sagc   value = pa[TRE_PARAM_MAX_SUBST];
17563d4abf0Sagc   if (value == TRE_PARAM_DEFAULT)
17663d4abf0Sagc     reach->params.max_subst = default_params.max_subst;
17763d4abf0Sagc   else if (value != TRE_PARAM_UNSET)
17863d4abf0Sagc     reach->params.max_subst = value;
17963d4abf0Sagc 
18063d4abf0Sagc   /* Set maximum number of errors. */
18163d4abf0Sagc   value = pa[TRE_PARAM_MAX_ERR];
18263d4abf0Sagc   if (value == TRE_PARAM_DEFAULT)
18363d4abf0Sagc     reach->params.max_err = default_params.max_err;
18463d4abf0Sagc   else if (value != TRE_PARAM_UNSET)
18563d4abf0Sagc     reach->params.max_err = value;
18663d4abf0Sagc }
18763d4abf0Sagc 
18863d4abf0Sagc reg_errcode_t
tre_tnfa_run_approx(const tre_tnfa_t * tnfa,const void * string,int len,tre_str_type_t type,int * match_tags,regamatch_t * match,regaparams_t default_params,int eflags,int * match_end_ofs)18963d4abf0Sagc tre_tnfa_run_approx(const tre_tnfa_t *tnfa, const void *string, int len,
19063d4abf0Sagc 		    tre_str_type_t type, int *match_tags,
19163d4abf0Sagc 		    regamatch_t *match, regaparams_t default_params,
19263d4abf0Sagc 		    int eflags, int *match_end_ofs)
19363d4abf0Sagc {
19463d4abf0Sagc   /* State variables required by GET_NEXT_WCHAR. */
19563d4abf0Sagc   tre_char_t prev_c = 0, next_c = 0;
19663d4abf0Sagc   const char *str_byte = string;
19763d4abf0Sagc   int pos = -1;
19863d4abf0Sagc   unsigned int pos_add_next = 1;
19963d4abf0Sagc #ifdef TRE_WCHAR
20063d4abf0Sagc   const wchar_t *str_wide = string;
20163d4abf0Sagc #ifdef TRE_MBSTATE
20263d4abf0Sagc   mbstate_t mbstate;
20363d4abf0Sagc #endif /* !TRE_WCHAR */
20463d4abf0Sagc #endif /* TRE_WCHAR */
20563d4abf0Sagc   int reg_notbol = eflags & REG_NOTBOL;
20663d4abf0Sagc   int reg_noteol = eflags & REG_NOTEOL;
20763d4abf0Sagc   int reg_newline = tnfa->cflags & REG_NEWLINE;
208f2a3d147Schristos   size_t str_user_end = 0;
20963d4abf0Sagc 
21063d4abf0Sagc   int prev_pos;
21163d4abf0Sagc 
21263d4abf0Sagc   /* Number of tags. */
213f2a3d147Schristos   size_t num_tags;
21463d4abf0Sagc   /* The reach tables. */
21563d4abf0Sagc   tre_tnfa_approx_reach_t *reach, *reach_next;
21663d4abf0Sagc   /* Tag array for temporary use. */
21763d4abf0Sagc   int *tmp_tags;
21863d4abf0Sagc 
21963d4abf0Sagc   /* End offset of best match so far, or -1 if no match found yet. */
22063d4abf0Sagc   int match_eo = -1;
22163d4abf0Sagc   /* Costs of the match. */
22263d4abf0Sagc   int match_costs[TRE_M_LAST];
22363d4abf0Sagc 
22463d4abf0Sagc   /* Space for temporary data required for matching. */
22563d4abf0Sagc   unsigned char *buf;
22663d4abf0Sagc 
227f2a3d147Schristos   size_t i, id;
22863d4abf0Sagc 
22927d137aeSrin   reg_errcode_t ret;
23027d137aeSrin 
23163d4abf0Sagc   if (!match_tags)
23263d4abf0Sagc     num_tags = 0;
23363d4abf0Sagc   else
23463d4abf0Sagc     num_tags = tnfa->num_tags;
23563d4abf0Sagc 
23663d4abf0Sagc #ifdef TRE_MBSTATE
23763d4abf0Sagc   memset(&mbstate, '\0', sizeof(mbstate));
23863d4abf0Sagc #endif /* TRE_MBSTATE */
23963d4abf0Sagc 
24063d4abf0Sagc   DPRINT(("tre_tnfa_run_approx, input type %d, len %d, eflags %d, "
24163d4abf0Sagc 	  "match_tags %p\n",
24263d4abf0Sagc 	  type, len, eflags,
24363d4abf0Sagc 	  match_tags));
24463d4abf0Sagc   DPRINT(("max cost %d, ins %d, del %d, subst %d\n",
24563d4abf0Sagc 	  default_params.max_cost,
24663d4abf0Sagc 	  default_params.cost_ins,
24763d4abf0Sagc 	  default_params.cost_del,
24863d4abf0Sagc 	  default_params.cost_subst));
24963d4abf0Sagc 
25063d4abf0Sagc   /* Allocate memory for temporary data required for matching.	This needs to
25163d4abf0Sagc      be done for every matching operation to be thread safe.  This allocates
25263d4abf0Sagc      everything in a single large block from the stack frame using alloca()
25363d4abf0Sagc      or with malloc() if alloca is unavailable. */
25463d4abf0Sagc   {
25563d4abf0Sagc     unsigned char *buf_cursor;
25698796d9cSrin 
25798796d9cSrin     /* Ensure that tag_bytes*num_states cannot overflow, and that it don't
25898796d9cSrin      * contribute more than 1/8 of SIZE_MAX to total_bytes. */
25998796d9cSrin     if (num_tags > SIZE_MAX/(8 * sizeof(*tmp_tags) * tnfa->num_states))
26098796d9cSrin       return REG_ESPACE;
26198796d9cSrin 
26298796d9cSrin     /* Likewise check reach_bytes. */
26398796d9cSrin     if (tnfa->num_states > SIZE_MAX/(8 * sizeof(*reach_next)))
26498796d9cSrin       return REG_ESPACE;
26598796d9cSrin 
26663d4abf0Sagc     /* Space needed for one array of tags. */
267f2a3d147Schristos     size_t tag_bytes = sizeof(*tmp_tags) * num_tags;
26863d4abf0Sagc     /* Space needed for one reach table. */
269f2a3d147Schristos     size_t reach_bytes = sizeof(*reach_next) * tnfa->num_states;
27063d4abf0Sagc     /* Total space needed. */
271f2a3d147Schristos     size_t total_bytes = reach_bytes * 2 + (tnfa->num_states * 2 + 1 ) * tag_bytes;
27263d4abf0Sagc     /* Add some extra to make sure we can align the pointers.  The multiplier
27363d4abf0Sagc        used here must be equal to the number of ALIGN calls below. */
27463d4abf0Sagc     total_bytes += (sizeof(long) - 1) * 3;
27563d4abf0Sagc 
27663d4abf0Sagc     /* Allocate the memory. */
27763d4abf0Sagc #ifdef TRE_USE_ALLOCA
27863d4abf0Sagc     buf = alloca(total_bytes);
27963d4abf0Sagc #else /* !TRE_USE_ALLOCA */
28063d4abf0Sagc     buf = xmalloc((unsigned)total_bytes);
28163d4abf0Sagc #endif /* !TRE_USE_ALLOCA */
28263d4abf0Sagc     if (!buf)
28363d4abf0Sagc       return REG_ESPACE;
28463d4abf0Sagc     memset(buf, 0, (size_t)total_bytes);
28563d4abf0Sagc 
28663d4abf0Sagc     /* Allocate `tmp_tags' from `buf'. */
28763d4abf0Sagc     tmp_tags = (void *)buf;
28863d4abf0Sagc     buf_cursor = buf + tag_bytes;
28963d4abf0Sagc     buf_cursor += ALIGN(buf_cursor, long);
29063d4abf0Sagc 
29163d4abf0Sagc     /* Allocate `reach' from `buf'. */
29263d4abf0Sagc     reach = (void *)buf_cursor;
29363d4abf0Sagc     buf_cursor += reach_bytes;
29463d4abf0Sagc     buf_cursor += ALIGN(buf_cursor, long);
29563d4abf0Sagc 
29663d4abf0Sagc     /* Allocate `reach_next' from `buf'. */
29763d4abf0Sagc     reach_next = (void *)buf_cursor;
29863d4abf0Sagc     buf_cursor += reach_bytes;
29963d4abf0Sagc     buf_cursor += ALIGN(buf_cursor, long);
30063d4abf0Sagc 
30163d4abf0Sagc     /* Allocate tag arrays for `reach' and `reach_next' from `buf'. */
30263d4abf0Sagc     for (i = 0; i < tnfa->num_states; i++)
30363d4abf0Sagc       {
30463d4abf0Sagc 	reach[i].tags = (void *)buf_cursor;
30563d4abf0Sagc 	buf_cursor += tag_bytes;
30663d4abf0Sagc 	reach_next[i].tags = (void *)buf_cursor;
30763d4abf0Sagc 	buf_cursor += tag_bytes;
30863d4abf0Sagc       }
30963d4abf0Sagc     assert(buf_cursor <= buf + total_bytes);
31063d4abf0Sagc   }
31163d4abf0Sagc 
31263d4abf0Sagc   for (i = 0; i < TRE_M_LAST; i++)
31363d4abf0Sagc     match_costs[i] = INT_MAX;
31463d4abf0Sagc 
31563d4abf0Sagc   /* Mark the reach arrays empty. */
31663d4abf0Sagc   for (i = 0; i < tnfa->num_states; i++)
31763d4abf0Sagc     reach[i].pos = reach_next[i].pos = -2;
31863d4abf0Sagc 
31963d4abf0Sagc   prev_pos = pos;
32063d4abf0Sagc   GET_NEXT_WCHAR();
32163d4abf0Sagc   pos = 0;
32263d4abf0Sagc 
32313498f30Srin   while (/*CONSTCOND*/(void)1,1)
32463d4abf0Sagc     {
32563d4abf0Sagc       DPRINT(("%03d:%2lc/%05d\n", pos, (tre_cint_t)next_c, (int)next_c));
32663d4abf0Sagc 
32763d4abf0Sagc       /* Add initial states to `reach_next' if an exact match has not yet
32863d4abf0Sagc 	 been found. */
32963d4abf0Sagc       if (match_costs[TRE_M_COST] > 0)
33063d4abf0Sagc 	{
33163d4abf0Sagc 	  tre_tnfa_transition_t *trans;
33263d4abf0Sagc 	  DPRINT(("  init"));
33363d4abf0Sagc 	  for (trans = tnfa->initial; trans->state; trans++)
33463d4abf0Sagc 	    {
33563d4abf0Sagc 	      int stateid = trans->state_id;
33663d4abf0Sagc 
33763d4abf0Sagc 	      /* If this state is not currently in `reach_next', add it
33863d4abf0Sagc 		 there. */
33963d4abf0Sagc 	      if (reach_next[stateid].pos < pos)
34063d4abf0Sagc 		{
34163d4abf0Sagc 		  if (trans->assertions && CHECK_ASSERTIONS(trans->assertions))
34263d4abf0Sagc 		    {
34363d4abf0Sagc 		      /* Assertions failed, don't add this state. */
34463d4abf0Sagc 		      DPRINT((" !%d (assert)", stateid));
34563d4abf0Sagc 		      continue;
34663d4abf0Sagc 		    }
34763d4abf0Sagc 		  DPRINT((" %d", stateid));
34863d4abf0Sagc 		  reach_next[stateid].state = trans->state;
34963d4abf0Sagc 		  reach_next[stateid].pos = pos;
35063d4abf0Sagc 
35163d4abf0Sagc 		  /* Compute tag values after this transition. */
35263d4abf0Sagc 		  for (i = 0; i < num_tags; i++)
35363d4abf0Sagc 		    reach_next[stateid].tags[i] = -1;
35463d4abf0Sagc 
35563d4abf0Sagc 		  if (trans->tags)
35663d4abf0Sagc 		    for (i = 0; trans->tags[i] >= 0; i++)
357f2a3d147Schristos 		      if ((size_t)trans->tags[i] < num_tags)
35863d4abf0Sagc 			reach_next[stateid].tags[trans->tags[i]] = pos;
35963d4abf0Sagc 
36063d4abf0Sagc 		  /* Set the parameters, depth, and costs. */
36163d4abf0Sagc 		  reach_next[stateid].params = default_params;
36263d4abf0Sagc 		  reach_next[stateid].depth = 0;
36363d4abf0Sagc 		  for (i = 0; i < TRE_M_LAST; i++)
36463d4abf0Sagc 		    reach_next[stateid].costs[0][i] = 0;
36563d4abf0Sagc 		  if (trans->params)
36663d4abf0Sagc 		    tre_set_params(&reach_next[stateid], trans->params,
36763d4abf0Sagc 				   default_params);
36863d4abf0Sagc 
36963d4abf0Sagc 		  /* If this is the final state, mark the exact match. */
37063d4abf0Sagc 		  if (trans->state == tnfa->final)
37163d4abf0Sagc 		    {
37263d4abf0Sagc 		      match_eo = pos;
37363d4abf0Sagc 		      for (i = 0; i < num_tags; i++)
37463d4abf0Sagc 			match_tags[i] = reach_next[stateid].tags[i];
37563d4abf0Sagc 		      for (i = 0; i < TRE_M_LAST; i++)
37663d4abf0Sagc 			match_costs[i] = 0;
37763d4abf0Sagc 		    }
37863d4abf0Sagc 		}
37963d4abf0Sagc 	    }
38063d4abf0Sagc 	    DPRINT(("\n"));
38163d4abf0Sagc 	}
38263d4abf0Sagc 
38363d4abf0Sagc 
38463d4abf0Sagc       /* Handle inserts.  This is done by pretending there's an epsilon
38563d4abf0Sagc 	 transition from each state in `reach' back to the same state.
38663d4abf0Sagc 	 We don't need to worry about the final state here; this will never
38763d4abf0Sagc 	 give a better match than what we already have. */
38863d4abf0Sagc       for (id = 0; id < tnfa->num_states; id++)
38963d4abf0Sagc 	{
39063d4abf0Sagc 	  int depth;
39163d4abf0Sagc 	  int cost, cost0;
39263d4abf0Sagc 
39363d4abf0Sagc 	  if (reach[id].pos != prev_pos)
39463d4abf0Sagc 	    {
39563d4abf0Sagc 	      DPRINT(("	 insert: %d not reached\n", id));
39663d4abf0Sagc 	      continue;	 /* Not reached. */
39763d4abf0Sagc 	    }
39863d4abf0Sagc 
39963d4abf0Sagc 	  depth = reach[id].depth;
40063d4abf0Sagc 
40163d4abf0Sagc 	  /* Compute and check cost at current depth. */
40263d4abf0Sagc 	  cost = reach[id].costs[depth][TRE_M_COST];
40363d4abf0Sagc 	  if (reach[id].params.cost_ins != TRE_PARAM_UNSET)
40463d4abf0Sagc 	    cost += reach[id].params.cost_ins;
40563d4abf0Sagc 	  if (cost > reach[id].params.max_cost)
40663d4abf0Sagc 	    continue;  /* Cost too large. */
40763d4abf0Sagc 
40863d4abf0Sagc 	  /* Check number of inserts at current depth. */
40963d4abf0Sagc 	  if (reach[id].costs[depth][TRE_M_NUM_INS] + 1
41063d4abf0Sagc 	      > reach[id].params.max_ins)
41163d4abf0Sagc 	    continue;  /* Too many inserts. */
41263d4abf0Sagc 
41363d4abf0Sagc 	  /* Check total number of errors at current depth. */
41463d4abf0Sagc 	  if (reach[id].costs[depth][TRE_M_NUM_ERR] + 1
41563d4abf0Sagc 	      > reach[id].params.max_err)
41663d4abf0Sagc 	    continue;  /* Too many errors. */
41763d4abf0Sagc 
41863d4abf0Sagc 	  /* Compute overall cost. */
41963d4abf0Sagc 	  cost0 = cost;
42063d4abf0Sagc 	  if (depth > 0)
42163d4abf0Sagc 	    {
42263d4abf0Sagc 	      cost0 = reach[id].costs[0][TRE_M_COST];
42363d4abf0Sagc 	      if (reach[id].params.cost_ins != TRE_PARAM_UNSET)
42463d4abf0Sagc 		cost0 += reach[id].params.cost_ins;
42563d4abf0Sagc 	      else
42663d4abf0Sagc 		cost0 += default_params.cost_ins;
42763d4abf0Sagc 	    }
42863d4abf0Sagc 
42963d4abf0Sagc 	  DPRINT(("  insert: from %d to %d, cost %d: ", id, id,
43063d4abf0Sagc 		  reach[id].costs[depth][TRE_M_COST]));
43163d4abf0Sagc 	  if (reach_next[id].pos == pos
43263d4abf0Sagc 	      && (cost0 >= reach_next[id].costs[0][TRE_M_COST]))
43363d4abf0Sagc 	    {
43463d4abf0Sagc 	      DPRINT(("lose\n"));
43563d4abf0Sagc 	      continue;
43663d4abf0Sagc 	    }
43763d4abf0Sagc 	  DPRINT(("win\n"));
43863d4abf0Sagc 
43963d4abf0Sagc 	  /* Copy state, position, tags, parameters, and depth. */
44063d4abf0Sagc 	  reach_next[id].state = reach[id].state;
44163d4abf0Sagc 	  reach_next[id].pos = pos;
44263d4abf0Sagc 	  for (i = 0; i < num_tags; i++)
44363d4abf0Sagc 	    reach_next[id].tags[i] = reach[id].tags[i];
44463d4abf0Sagc 	  reach_next[id].params = reach[id].params;
44563d4abf0Sagc 	  reach_next[id].depth = reach[id].depth;
44663d4abf0Sagc 
44763d4abf0Sagc 	  /* Set the costs after this transition. */
44863d4abf0Sagc 	  memcpy(reach_next[id].costs, reach[id].costs,
44963d4abf0Sagc 		 sizeof(reach_next[id].costs[0][0])
45063d4abf0Sagc 		 * TRE_M_LAST * (depth + 1));
45163d4abf0Sagc 	  reach_next[id].costs[depth][TRE_M_COST] = cost;
45263d4abf0Sagc 	  reach_next[id].costs[depth][TRE_M_NUM_INS]++;
45363d4abf0Sagc 	  reach_next[id].costs[depth][TRE_M_NUM_ERR]++;
45463d4abf0Sagc 	  if (depth > 0)
45563d4abf0Sagc 	    {
45663d4abf0Sagc 	      reach_next[id].costs[0][TRE_M_COST] = cost0;
45763d4abf0Sagc 	      reach_next[id].costs[0][TRE_M_NUM_INS]++;
45863d4abf0Sagc 	      reach_next[id].costs[0][TRE_M_NUM_ERR]++;
45963d4abf0Sagc 	    }
46063d4abf0Sagc 
46163d4abf0Sagc 	}
46263d4abf0Sagc 
46363d4abf0Sagc 
46463d4abf0Sagc       /* Handle deletes.  This is done by traversing through the whole TNFA
46563d4abf0Sagc 	 pretending that all transitions are epsilon transitions, until
46663d4abf0Sagc 	 no more states can be reached with better costs. */
46763d4abf0Sagc       {
46863d4abf0Sagc 	/* XXX - dynamic ringbuffer size */
46963d4abf0Sagc 	tre_tnfa_approx_reach_t *ringbuffer[512];
47063d4abf0Sagc 	tre_tnfa_approx_reach_t **deque_start, **deque_end;
47163d4abf0Sagc 
47263d4abf0Sagc 	deque_start = deque_end = ringbuffer;
47363d4abf0Sagc 
47463d4abf0Sagc 	/* Add all states in `reach_next' to the deque. */
47563d4abf0Sagc 	for (id = 0; id < tnfa->num_states; id++)
47663d4abf0Sagc 	  {
47763d4abf0Sagc 	    if (reach_next[id].pos != pos)
47863d4abf0Sagc 	      continue;
47963d4abf0Sagc 	    *deque_end = &reach_next[id];
48063d4abf0Sagc 	    deque_end++;
48163d4abf0Sagc 	    assert(deque_end != deque_start);
48263d4abf0Sagc 	  }
48363d4abf0Sagc 
48463d4abf0Sagc 	/* Repeat until the deque is empty. */
48563d4abf0Sagc 	while (deque_end != deque_start)
48663d4abf0Sagc 	  {
48763d4abf0Sagc 	    tre_tnfa_approx_reach_t *reach_p;
48863d4abf0Sagc 	    int depth;
48963d4abf0Sagc 	    int cost, cost0;
49063d4abf0Sagc 	    tre_tnfa_transition_t *trans;
49163d4abf0Sagc 
49263d4abf0Sagc 	    /* Pop the first item off the deque. */
49363d4abf0Sagc 	    reach_p = *deque_start;
49463d4abf0Sagc 	    id = reach_p - reach_next;
49563d4abf0Sagc 	    depth = reach_p->depth;
49663d4abf0Sagc 
49763d4abf0Sagc 	    /* Compute cost at current depth. */
49863d4abf0Sagc 	    cost = reach_p->costs[depth][TRE_M_COST];
49963d4abf0Sagc 	    if (reach_p->params.cost_del != TRE_PARAM_UNSET)
50063d4abf0Sagc 	      cost += reach_p->params.cost_del;
50163d4abf0Sagc 
50263d4abf0Sagc 	    /* Check cost, number of deletes, and total number of errors
50363d4abf0Sagc 	       at current depth. */
50463d4abf0Sagc 	    if (cost > reach_p->params.max_cost
50563d4abf0Sagc 		|| (reach_p->costs[depth][TRE_M_NUM_DEL] + 1
50663d4abf0Sagc 		    > reach_p->params.max_del)
50763d4abf0Sagc 		|| (reach_p->costs[depth][TRE_M_NUM_ERR] + 1
50863d4abf0Sagc 		    > reach_p->params.max_err))
50963d4abf0Sagc 	      {
51063d4abf0Sagc 		/* Too many errors or cost too large. */
51163d4abf0Sagc 		DPRINT(("  delete: from %03d: cost too large\n", id));
51263d4abf0Sagc 		deque_start++;
51363d4abf0Sagc 		if (deque_start >= (ringbuffer + 512))
51463d4abf0Sagc 		  deque_start = ringbuffer;
51563d4abf0Sagc 		continue;
51663d4abf0Sagc 	      }
51763d4abf0Sagc 
51863d4abf0Sagc 	    /* Compute overall cost. */
51963d4abf0Sagc 	    cost0 = cost;
52063d4abf0Sagc 	    if (depth > 0)
52163d4abf0Sagc 	      {
52263d4abf0Sagc 		cost0 = reach_p->costs[0][TRE_M_COST];
52363d4abf0Sagc 		if (reach_p->params.cost_del != TRE_PARAM_UNSET)
52463d4abf0Sagc 		  cost0 += reach_p->params.cost_del;
52563d4abf0Sagc 		else
52663d4abf0Sagc 		  cost0 += default_params.cost_del;
52763d4abf0Sagc 	      }
52863d4abf0Sagc 
52963d4abf0Sagc 	    for (trans = reach_p->state; trans->state; trans++)
53063d4abf0Sagc 	      {
53163d4abf0Sagc 		int dest_id = trans->state_id;
53263d4abf0Sagc 		DPRINT(("  delete: from %03d to %03d, cost %d (%d): ",
53363d4abf0Sagc 			id, dest_id, cost0, reach_p->params.max_cost));
53463d4abf0Sagc 
53563d4abf0Sagc 		if (trans->assertions && CHECK_ASSERTIONS(trans->assertions))
53663d4abf0Sagc 		  {
53763d4abf0Sagc 		    DPRINT(("assertion failed\n"));
53863d4abf0Sagc 		    continue;
53963d4abf0Sagc 		  }
54063d4abf0Sagc 
54163d4abf0Sagc 		/* Compute tag values after this transition. */
54263d4abf0Sagc 		for (i = 0; i < num_tags; i++)
54363d4abf0Sagc 		  tmp_tags[i] = reach_p->tags[i];
54463d4abf0Sagc 		if (trans->tags)
54563d4abf0Sagc 		  for (i = 0; trans->tags[i] >= 0; i++)
546f2a3d147Schristos 		    if ((size_t)trans->tags[i] < num_tags)
54763d4abf0Sagc 		      tmp_tags[trans->tags[i]] = pos;
54863d4abf0Sagc 
54963d4abf0Sagc 		/* If another path has also reached this state, choose the one
55063d4abf0Sagc 		   with the smallest cost or best tags if costs are equal. */
55163d4abf0Sagc 		if (reach_next[dest_id].pos == pos
55263d4abf0Sagc 		    && (cost0 > reach_next[dest_id].costs[0][TRE_M_COST]
55363d4abf0Sagc 			|| (cost0 == reach_next[dest_id].costs[0][TRE_M_COST]
55463d4abf0Sagc 			    && (!match_tags
55563d4abf0Sagc 				|| !tre_tag_order(num_tags,
55663d4abf0Sagc 						  tnfa->tag_directions,
55763d4abf0Sagc 						  tmp_tags,
55863d4abf0Sagc 						  reach_next[dest_id].tags)))))
55963d4abf0Sagc 		  {
56063d4abf0Sagc 		    DPRINT(("lose, cost0 %d, have %d\n",
56163d4abf0Sagc 			    cost0, reach_next[dest_id].costs[0][TRE_M_COST]));
56263d4abf0Sagc 		    continue;
56363d4abf0Sagc 		  }
56463d4abf0Sagc 		DPRINT(("win\n"));
56563d4abf0Sagc 
56663d4abf0Sagc 		/* Set state, position, tags, parameters, depth, and costs. */
56763d4abf0Sagc 		reach_next[dest_id].state = trans->state;
56863d4abf0Sagc 		reach_next[dest_id].pos = pos;
56963d4abf0Sagc 		for (i = 0; i < num_tags; i++)
57063d4abf0Sagc 		  reach_next[dest_id].tags[i] = tmp_tags[i];
57163d4abf0Sagc 
57263d4abf0Sagc 		reach_next[dest_id].params = reach_p->params;
57363d4abf0Sagc 		if (trans->params)
57463d4abf0Sagc 		  tre_set_params(&reach_next[dest_id], trans->params,
57563d4abf0Sagc 				 default_params);
57663d4abf0Sagc 
57763d4abf0Sagc 		reach_next[dest_id].depth = reach_p->depth;
57863d4abf0Sagc 		memcpy(&reach_next[dest_id].costs,
57963d4abf0Sagc 		       reach_p->costs,
58063d4abf0Sagc 		       sizeof(reach_p->costs[0][0])
58163d4abf0Sagc 		       * TRE_M_LAST * (depth + 1));
58263d4abf0Sagc 		reach_next[dest_id].costs[depth][TRE_M_COST] = cost;
58363d4abf0Sagc 		reach_next[dest_id].costs[depth][TRE_M_NUM_DEL]++;
58463d4abf0Sagc 		reach_next[dest_id].costs[depth][TRE_M_NUM_ERR]++;
58563d4abf0Sagc 		if (depth > 0)
58663d4abf0Sagc 		  {
58763d4abf0Sagc 		    reach_next[dest_id].costs[0][TRE_M_COST] = cost0;
58863d4abf0Sagc 		    reach_next[dest_id].costs[0][TRE_M_NUM_DEL]++;
58963d4abf0Sagc 		    reach_next[dest_id].costs[0][TRE_M_NUM_ERR]++;
59063d4abf0Sagc 		  }
59163d4abf0Sagc 
59263d4abf0Sagc 		if (trans->state == tnfa->final
59363d4abf0Sagc 		    && (match_eo < 0
59463d4abf0Sagc 			|| match_costs[TRE_M_COST] > cost0
59563d4abf0Sagc 			|| (match_costs[TRE_M_COST] == cost0
59663d4abf0Sagc 			    && (num_tags > 0
59763d4abf0Sagc 				&& tmp_tags[0] <= match_tags[0]))))
59863d4abf0Sagc 		  {
59963d4abf0Sagc 		    DPRINT(("	 setting new match at %d, cost %d\n",
60063d4abf0Sagc 			    pos, cost0));
60163d4abf0Sagc 		    match_eo = pos;
60263d4abf0Sagc 		    memcpy(match_costs, reach_next[dest_id].costs[0],
60363d4abf0Sagc 			   sizeof(match_costs[0]) * TRE_M_LAST);
60463d4abf0Sagc 		    for (i = 0; i < num_tags; i++)
60563d4abf0Sagc 		      match_tags[i] = tmp_tags[i];
60663d4abf0Sagc 		  }
60763d4abf0Sagc 
60863d4abf0Sagc 		/* Add to the end of the deque. */
60963d4abf0Sagc 		*deque_end = &reach_next[dest_id];
61063d4abf0Sagc 		deque_end++;
61163d4abf0Sagc 		if (deque_end >= (ringbuffer + 512))
61263d4abf0Sagc 		  deque_end = ringbuffer;
61363d4abf0Sagc 		assert(deque_end != deque_start);
61463d4abf0Sagc 	      }
61563d4abf0Sagc 	    deque_start++;
61663d4abf0Sagc 	    if (deque_start >= (ringbuffer + 512))
61763d4abf0Sagc 	      deque_start = ringbuffer;
61863d4abf0Sagc 	  }
61963d4abf0Sagc 
62063d4abf0Sagc       }
62163d4abf0Sagc 
62263d4abf0Sagc #ifdef TRE_DEBUG
62363d4abf0Sagc       tre_print_reach(tnfa, reach_next, pos, num_tags);
62463d4abf0Sagc #endif /* TRE_DEBUG */
62563d4abf0Sagc 
62663d4abf0Sagc       /* Check for end of string. */
62763d4abf0Sagc       if (len < 0)
62863d4abf0Sagc 	{
62963d4abf0Sagc 	  if (type == STR_USER)
63063d4abf0Sagc 	    {
63163d4abf0Sagc 	      if (str_user_end)
63263d4abf0Sagc 		break;
63363d4abf0Sagc 	    }
63463d4abf0Sagc 	  else if (next_c == L'\0')
63563d4abf0Sagc 	    break;
63663d4abf0Sagc 	}
63763d4abf0Sagc       else
63863d4abf0Sagc 	{
63963d4abf0Sagc 	  if (pos >= len)
64063d4abf0Sagc 	    break;
64163d4abf0Sagc 	}
64263d4abf0Sagc 
64363d4abf0Sagc       prev_pos = pos;
64463d4abf0Sagc       GET_NEXT_WCHAR();
64563d4abf0Sagc 
64663d4abf0Sagc       /* Swap `reach' and `reach_next'. */
64763d4abf0Sagc       {
64863d4abf0Sagc 	tre_tnfa_approx_reach_t *tmp;
64963d4abf0Sagc 	tmp = reach;
65063d4abf0Sagc 	reach = reach_next;
65163d4abf0Sagc 	reach_next = tmp;
65263d4abf0Sagc       }
65363d4abf0Sagc 
65463d4abf0Sagc       /* Handle exact matches and substitutions. */
65563d4abf0Sagc       for (id = 0; id < tnfa->num_states; id++)
65663d4abf0Sagc 	{
65763d4abf0Sagc 	  tre_tnfa_transition_t *trans;
65863d4abf0Sagc 
65963d4abf0Sagc 	  if (reach[id].pos < prev_pos)
66063d4abf0Sagc 	    continue;  /* Not reached. */
66163d4abf0Sagc 	  for (trans = reach[id].state; trans->state; trans++)
66263d4abf0Sagc 	    {
66363d4abf0Sagc 	      int dest_id;
66463d4abf0Sagc 	      int depth;
66563d4abf0Sagc 	      int cost, cost0, err;
66663d4abf0Sagc 
66763d4abf0Sagc 	      if (trans->assertions
66863d4abf0Sagc 		  && (CHECK_ASSERTIONS(trans->assertions)
66963d4abf0Sagc 		      || CHECK_CHAR_CLASSES(trans, tnfa, eflags)))
67063d4abf0Sagc 		{
67163d4abf0Sagc 		  DPRINT(("  exact,  from %d: assert failed\n", id));
67263d4abf0Sagc 		  continue;
67363d4abf0Sagc 		}
67463d4abf0Sagc 
67563d4abf0Sagc 	      depth = reach[id].depth;
67663d4abf0Sagc 	      dest_id = trans->state_id;
67763d4abf0Sagc 
67863d4abf0Sagc 	      cost = reach[id].costs[depth][TRE_M_COST];
67963d4abf0Sagc 	      cost0 = reach[id].costs[0][TRE_M_COST];
68063d4abf0Sagc 	      err = 0;
68163d4abf0Sagc 
68263d4abf0Sagc 	      if (trans->code_min > (tre_cint_t)prev_c
68363d4abf0Sagc 		  || trans->code_max < (tre_cint_t)prev_c)
68463d4abf0Sagc 		{
68563d4abf0Sagc 		  /* Handle substitutes.  The required character was not in
68663d4abf0Sagc 		     the string, so match it in place of whatever was supposed
68763d4abf0Sagc 		     to be there and increase costs accordingly. */
68863d4abf0Sagc 		  err = 1;
68963d4abf0Sagc 
69063d4abf0Sagc 		  /* Compute and check cost at current depth. */
69163d4abf0Sagc 		  cost = reach[id].costs[depth][TRE_M_COST];
69263d4abf0Sagc 		  if (reach[id].params.cost_subst != TRE_PARAM_UNSET)
69363d4abf0Sagc 		    cost += reach[id].params.cost_subst;
69463d4abf0Sagc 		  if (cost > reach[id].params.max_cost)
69563d4abf0Sagc 		    continue; /* Cost too large. */
69663d4abf0Sagc 
69763d4abf0Sagc 		  /* Check number of substitutes at current depth. */
69863d4abf0Sagc 		  if (reach[id].costs[depth][TRE_M_NUM_SUBST] + 1
69963d4abf0Sagc 		      > reach[id].params.max_subst)
70063d4abf0Sagc 		    continue; /* Too many substitutes. */
70163d4abf0Sagc 
70263d4abf0Sagc 		  /* Check total number of errors at current depth. */
70363d4abf0Sagc 		  if (reach[id].costs[depth][TRE_M_NUM_ERR] + 1
70463d4abf0Sagc 		      > reach[id].params.max_err)
70563d4abf0Sagc 		    continue; /* Too many errors. */
70663d4abf0Sagc 
70763d4abf0Sagc 		  /* Compute overall cost. */
70863d4abf0Sagc 		  cost0 = cost;
70963d4abf0Sagc 		  if (depth > 0)
71063d4abf0Sagc 		    {
71163d4abf0Sagc 		      cost0 = reach[id].costs[0][TRE_M_COST];
71263d4abf0Sagc 		      if (reach[id].params.cost_subst != TRE_PARAM_UNSET)
71363d4abf0Sagc 			cost0 += reach[id].params.cost_subst;
71463d4abf0Sagc 		      else
71563d4abf0Sagc 			cost0 += default_params.cost_subst;
71663d4abf0Sagc 		    }
71763d4abf0Sagc 		  DPRINT(("  subst,  from %03d to %03d, cost %d: ",
71863d4abf0Sagc 			  id, dest_id, cost0));
71963d4abf0Sagc 		}
72063d4abf0Sagc 	      else
72163d4abf0Sagc 		DPRINT(("  exact,  from %03d to %03d, cost %d: ",
72263d4abf0Sagc 			id, dest_id, cost0));
72363d4abf0Sagc 
72463d4abf0Sagc 	      /* Compute tag values after this transition. */
72563d4abf0Sagc 	      for (i = 0; i < num_tags; i++)
72663d4abf0Sagc 		tmp_tags[i] = reach[id].tags[i];
72763d4abf0Sagc 	      if (trans->tags)
72863d4abf0Sagc 		for (i = 0; trans->tags[i] >= 0; i++)
729f2a3d147Schristos 		  if ((size_t)trans->tags[i] < num_tags)
73063d4abf0Sagc 		    tmp_tags[trans->tags[i]] = pos;
73163d4abf0Sagc 
73263d4abf0Sagc 	      /* If another path has also reached this state, choose the
73363d4abf0Sagc 		 one with the smallest cost or best tags if costs are equal. */
73463d4abf0Sagc 	      if (reach_next[dest_id].pos == pos
73563d4abf0Sagc 		  && (cost0 > reach_next[dest_id].costs[0][TRE_M_COST]
73663d4abf0Sagc 		      || (cost0 == reach_next[dest_id].costs[0][TRE_M_COST]
73763d4abf0Sagc 			  && !tre_tag_order(num_tags, tnfa->tag_directions,
73863d4abf0Sagc 					    tmp_tags,
73963d4abf0Sagc 					    reach_next[dest_id].tags))))
74063d4abf0Sagc 		{
74163d4abf0Sagc 		  DPRINT(("lose\n"));
74263d4abf0Sagc 		  continue;
74363d4abf0Sagc 		}
74463d4abf0Sagc 	      DPRINT(("win %d %d\n",
74563d4abf0Sagc 		      reach_next[dest_id].pos,
74663d4abf0Sagc 		      reach_next[dest_id].costs[0][TRE_M_COST]));
74763d4abf0Sagc 
74863d4abf0Sagc 	      /* Set state, position, tags, and depth. */
74963d4abf0Sagc 	      reach_next[dest_id].state = trans->state;
75063d4abf0Sagc 	      reach_next[dest_id].pos = pos;
75163d4abf0Sagc 	      for (i = 0; i < num_tags; i++)
75263d4abf0Sagc 		reach_next[dest_id].tags[i] = tmp_tags[i];
75363d4abf0Sagc 	      reach_next[dest_id].depth = reach[id].depth;
75463d4abf0Sagc 
75563d4abf0Sagc 	      /* Set parameters. */
75663d4abf0Sagc 	      reach_next[dest_id].params = reach[id].params;
75763d4abf0Sagc 	      if (trans->params)
75863d4abf0Sagc 		tre_set_params(&reach_next[dest_id], trans->params,
75963d4abf0Sagc 			       default_params);
76063d4abf0Sagc 
76163d4abf0Sagc 	      /* Set the costs after this transition. */
76263d4abf0Sagc 	      memcpy(&reach_next[dest_id].costs,
76363d4abf0Sagc 		     reach[id].costs,
76463d4abf0Sagc 		     sizeof(reach[id].costs[0][0])
76563d4abf0Sagc 		     * TRE_M_LAST * (depth + 1));
76663d4abf0Sagc 	      reach_next[dest_id].costs[depth][TRE_M_COST] = cost;
76763d4abf0Sagc 	      reach_next[dest_id].costs[depth][TRE_M_NUM_SUBST] += err;
76863d4abf0Sagc 	      reach_next[dest_id].costs[depth][TRE_M_NUM_ERR] += err;
76963d4abf0Sagc 	      if (depth > 0)
77063d4abf0Sagc 		{
77163d4abf0Sagc 		  reach_next[dest_id].costs[0][TRE_M_COST] = cost0;
77263d4abf0Sagc 		  reach_next[dest_id].costs[0][TRE_M_NUM_SUBST] += err;
77363d4abf0Sagc 		  reach_next[dest_id].costs[0][TRE_M_NUM_ERR] += err;
77463d4abf0Sagc 		}
77563d4abf0Sagc 
77663d4abf0Sagc 	      if (trans->state == tnfa->final
77763d4abf0Sagc 		  && (match_eo < 0
77863d4abf0Sagc 		      || cost0 < match_costs[TRE_M_COST]
77963d4abf0Sagc 		      || (cost0 == match_costs[TRE_M_COST]
78063d4abf0Sagc 			  && num_tags > 0 && tmp_tags[0] <= match_tags[0])))
78163d4abf0Sagc 		{
78263d4abf0Sagc 		  DPRINT(("    setting new match at %d, cost %d\n",
78363d4abf0Sagc 			  pos, cost0));
78463d4abf0Sagc 		  match_eo = pos;
78563d4abf0Sagc 		  for (i = 0; i < TRE_M_LAST; i++)
78663d4abf0Sagc 		    match_costs[i] = reach_next[dest_id].costs[0][i];
78763d4abf0Sagc 		  for (i = 0; i < num_tags; i++)
78863d4abf0Sagc 		    match_tags[i] = tmp_tags[i];
78963d4abf0Sagc 		}
79063d4abf0Sagc 	    }
79163d4abf0Sagc 	}
79263d4abf0Sagc     }
79363d4abf0Sagc 
79463d4abf0Sagc   DPRINT(("match end offset = %d, match cost = %d\n", match_eo,
79563d4abf0Sagc 	  match_costs[TRE_M_COST]));
79663d4abf0Sagc 
79763d4abf0Sagc   match->cost = match_costs[TRE_M_COST];
79863d4abf0Sagc   match->num_ins = match_costs[TRE_M_NUM_INS];
79963d4abf0Sagc   match->num_del = match_costs[TRE_M_NUM_DEL];
80063d4abf0Sagc   match->num_subst = match_costs[TRE_M_NUM_SUBST];
80163d4abf0Sagc   *match_end_ofs = match_eo;
80263d4abf0Sagc 
80327d137aeSrin   ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
80427d137aeSrin error_exit:
80527d137aeSrin #ifndef TRE_USE_ALLOCA
80627d137aeSrin   if (buf)
80727d137aeSrin     xfree(buf);
80827d137aeSrin #endif /* !TRE_USE_ALLOCA */
80927d137aeSrin   return ret;
81063d4abf0Sagc }
811