(root)/
gettext-0.22.4/
gettext-tools/
libgrep/
kwset.c
       1  /* kwset.c - search for any of a set of keywords.
       2     Copyright 1989, 1998, 2000, 2005-2006, 2010, 2012 Free Software
       3     Foundation, Inc.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation; either version 3 of the License, or
       8     (at your option) any later 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, see <https://www.gnu.org/licenses/>.  */
      17  
      18  /* Written August 1989 by Mike Haertel.
      19     The author may be reached (Email) at the address mike@ai.mit.edu,
      20     or (US mail) as Mike Haertel c/o Free Software Foundation. */
      21  
      22  /* The algorithm implemented by these routines bears a startling resemblance
      23     to one discovered by Beate Commentz-Walter, although it is not identical.
      24     See "A String Matching Algorithm Fast on the Average," Technical Report,
      25     IBM-Germany, Scientific Center Heidelberg, Tiergartenstrasse 15, D-6900
      26     Heidelberg, Germany.  See also Aho, A.V., and M. Corasick, "Efficient
      27     String Matching:  An Aid to Bibliographic Search," CACM June 1975,
      28     Vol. 18, No. 6, which describes the failure function used below. */
      29  
      30  #ifdef HAVE_CONFIG_H
      31  # include <config.h>
      32  #endif
      33  #include <sys/types.h>
      34  #include "kwset.h"
      35  #include <limits.h>
      36  #include <stdlib.h>
      37  #include "obstack.h"
      38  #include "gettext.h"
      39  #define _(str) gettext (str)
      40  
      41  #ifdef GREP
      42  extern char *xmalloc();
      43  # undef malloc
      44  # define malloc xmalloc
      45  #endif
      46  
      47  #define NCHAR (UCHAR_MAX + 1)
      48  #define obstack_chunk_alloc malloc
      49  #define obstack_chunk_free free
      50  
      51  /* Balanced tree of edges and labels leaving a given trie node. */
      52  struct tree
      53  {
      54    struct tree *llink;           /* Left link; MUST be first field. */
      55    struct tree *rlink;           /* Right link (to larger labels). */
      56    struct trie *trie;            /* Trie node pointed to by this edge. */
      57    unsigned char label;          /* Label on this edge. */
      58    char balance;                 /* Difference in depths of subtrees. */
      59  };
      60  
      61  /* Node of a trie representing a set of reversed keywords. */
      62  struct trie
      63  {
      64    unsigned int accepting;       /* Word index of accepted word, or zero. */
      65    struct tree *links;           /* Tree of edges leaving this node. */
      66    struct trie *parent;          /* Parent of this node. */
      67    struct trie *next;            /* List of all trie nodes in level order. */
      68    struct trie *fail;            /* Aho-Corasick failure function. */
      69    int depth;                    /* Depth of this node from the root. */
      70    int shift;                    /* Shift function for search failures. */
      71    int maxshift;                 /* Max shift of self and descendents. */
      72  };
      73  
      74  /* Structure returned opaquely to the caller, containing everything. */
      75  struct kwset
      76  {
      77    struct obstack obstack;       /* Obstack for node allocation. */
      78    int words;                    /* Number of words in the trie. */
      79    struct trie *trie;            /* The trie itself. */
      80    int mind;                     /* Minimum depth of an accepting node. */
      81    int maxd;                     /* Maximum depth of any node. */
      82    unsigned char delta[NCHAR];   /* Delta table for rapid search. */
      83    struct trie *next[NCHAR];     /* Table of children of the root. */
      84    char *target;                 /* Target string if there's only one. */
      85    int mind2;                    /* Used in Boyer-Moore search for one string. */
      86    char const *trans;            /* Character translation table. */
      87  };
      88  
      89  /* Allocate and initialize a keyword set object, returning an opaque
      90     pointer to it.  Return NULL if memory is not available. */
      91  kwset_t
      92  kwsalloc (char const *trans)
      93  {
      94    struct kwset *kwset;
      95  
      96    kwset = (struct kwset *) malloc (sizeof (struct kwset));
      97    if (!kwset)
      98      return NULL;
      99  
     100    obstack_init (&kwset->obstack);
     101    kwset->words = 0;
     102    kwset->trie
     103      = (struct trie *) obstack_alloc (&kwset->obstack, sizeof (struct trie));
     104    if (!kwset->trie)
     105      {
     106        kwsfree (kwset);
     107        return NULL;
     108      }
     109    kwset->trie->accepting = 0;
     110    kwset->trie->links = 0;
     111    kwset->trie->parent = 0;
     112    kwset->trie->next = 0;
     113    kwset->trie->fail = 0;
     114    kwset->trie->depth = 0;
     115    kwset->trie->shift = 0;
     116    kwset->mind = INT_MAX;
     117    kwset->maxd = -1;
     118    kwset->target = 0;
     119    kwset->trans = trans;
     120  
     121    return kwset;
     122  }
     123  
     124  /* Add the given string to the contents of the keyword set.  Return NULL
     125     for success, an error message otherwise. */
     126  const char *
     127  kwsincr (kwset_t kws, char const *text, size_t len)
     128  {
     129    struct kwset *kwset;
     130    register struct trie *trie;
     131  
     132    kwset = (struct kwset *) kws;
     133    trie = kwset->trie;
     134    text += len;
     135  
     136    /* Descend the trie (built of reversed keywords) character-by-character,
     137       installing new nodes when necessary. */
     138    while (len--)
     139      {
     140        register unsigned char label;
     141        register struct tree *link;
     142        register int depth;
     143        struct tree *links[12];
     144        enum { L, R } dirs[12];
     145  
     146        label = kwset->trans ? kwset->trans[(unsigned char) *--text] : *--text;
     147  
     148        /* Descend the tree of outgoing links for this trie node,
     149           looking for the current character and keeping track
     150           of the path followed. */
     151        link = trie->links;
     152        links[0] = (struct tree *) &trie->links;
     153        dirs[0] = L;
     154        depth = 1;
     155  
     156        while (link && label != link->label)
     157          {
     158            links[depth] = link;
     159            if (label < link->label)
     160              dirs[depth++] = L, link = link->llink;
     161            else
     162              dirs[depth++] = R, link = link->rlink;
     163          }
     164  
     165        /* The current character doesn't have an outgoing link at
     166           this trie node, so build a new trie node and install
     167           a link in the current trie node's tree. */
     168        if (!link)
     169          {
     170            link = (struct tree *) obstack_alloc (&kwset->obstack,
     171                                                  sizeof (struct tree));
     172            if (!link)
     173              return _("memory exhausted");
     174            link->llink = 0;
     175            link->rlink = 0;
     176            link->trie = (struct trie *) obstack_alloc (&kwset->obstack,
     177                                                        sizeof (struct trie));
     178            if (!link->trie)
     179              return _("memory exhausted");
     180            link->trie->accepting = 0;
     181            link->trie->links = 0;
     182            link->trie->parent = trie;
     183            link->trie->next = 0;
     184            link->trie->fail = 0;
     185            link->trie->depth = trie->depth + 1;
     186            link->trie->shift = 0;
     187            link->label = label;
     188            link->balance = 0;
     189  
     190            /* Install the new tree node in its parent. */
     191            if (dirs[--depth] == L)
     192              links[depth]->llink = link;
     193            else
     194              links[depth]->rlink = link;
     195  
     196            /* Back up the tree fixing the balance flags. */
     197            while (depth && !links[depth]->balance)
     198              {
     199                if (dirs[depth] == L)
     200                  --links[depth]->balance;
     201                else
     202                  ++links[depth]->balance;
     203                --depth;
     204              }
     205  
     206            /* Rebalance the tree by pointer rotations if necessary. */
     207            if (depth && ((dirs[depth] == L && --links[depth]->balance)
     208                          || (dirs[depth] == R && ++links[depth]->balance)))
     209              {
     210                struct tree *t;
     211  
     212                switch (links[depth]->balance)
     213                  {
     214                    struct tree *r, *l, *rl, *lr;
     215                  case (char) -2:
     216                    switch (dirs[depth + 1])
     217                      {
     218                      case L:
     219                        r = links[depth], t = r->llink, rl = t->rlink;
     220                        t->rlink = r, r->llink = rl;
     221                        t->balance = r->balance = 0;
     222                        break;
     223                      case R:
     224                        r = links[depth], l = r->llink, t = l->rlink;
     225                        rl = t->rlink, lr = t->llink;
     226                        t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
     227                        l->balance = t->balance != 1 ? 0 : -1;
     228                        r->balance = t->balance != (char) -1 ? 0 : 1;
     229                        t->balance = 0;
     230                        break;
     231                      default:
     232                        abort ();
     233                      }
     234                    break;
     235                  case 2:
     236                    switch (dirs[depth + 1])
     237                      {
     238                      case R:
     239                        l = links[depth], t = l->rlink, lr = t->llink;
     240                        t->llink = l, l->rlink = lr;
     241                        t->balance = l->balance = 0;
     242                        break;
     243                      case L:
     244                        l = links[depth], r = l->rlink, t = r->llink;
     245                        lr = t->llink, rl = t->rlink;
     246                        t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
     247                        l->balance = t->balance != 1 ? 0 : -1;
     248                        r->balance = t->balance != (char) -1 ? 0 : 1;
     249                        t->balance = 0;
     250                        break;
     251                      default:
     252                        abort ();
     253                      }
     254                    break;
     255                  default:
     256                    abort ();
     257                  }
     258  
     259                if (dirs[depth - 1] == L)
     260                  links[depth - 1]->llink = t;
     261                else
     262                  links[depth - 1]->rlink = t;
     263              }
     264          }
     265  
     266        trie = link->trie;
     267      }
     268  
     269    /* Mark the node we finally reached as accepting, encoding the
     270       index number of this word in the keyword set so far. */
     271    if (!trie->accepting)
     272      trie->accepting = 1 + 2 * kwset->words;
     273    ++kwset->words;
     274  
     275    /* Keep track of the longest and shortest string of the keyword set. */
     276    if (trie->depth < kwset->mind)
     277      kwset->mind = trie->depth;
     278    if (trie->depth > kwset->maxd)
     279      kwset->maxd = trie->depth;
     280  
     281    return 0;
     282  }
     283  
     284  /* Enqueue the trie nodes referenced from the given tree in the
     285     given queue. */
     286  static void
     287  enqueue (struct tree *tree, struct trie **last)
     288  {
     289    if (!tree)
     290      return;
     291    enqueue (tree->llink, last);
     292    enqueue (tree->rlink, last);
     293    (*last) = (*last)->next = tree->trie;
     294  }
     295  
     296  /* Compute the Aho-Corasick failure function for the trie nodes referenced
     297     from the given tree, given the failure function for their parent as
     298     well as a last resort failure node. */
     299  static void
     300  treefails (register struct tree const *tree, struct trie const *fail,
     301             struct trie *recourse)
     302  {
     303    if (!tree)
     304      return;
     305  
     306    treefails (tree->llink, fail, recourse);
     307    treefails (tree->rlink, fail, recourse);
     308  
     309    /* Find, in the chain of fails going back to the root, the first
     310       node that has a descendent on the current label. */
     311    while (fail)
     312      {
     313        register struct tree *link;
     314  
     315        link = fail->links;
     316        while (link && tree->label != link->label)
     317          if (tree->label < link->label)
     318            link = link->llink;
     319          else
     320            link = link->rlink;
     321        if (link)
     322          {
     323            tree->trie->fail = link->trie;
     324            return;
     325          }
     326        fail = fail->fail;
     327      }
     328  
     329    tree->trie->fail = recourse;
     330  }
     331  
     332  /* Set delta entries for the links of the given tree such that
     333     the preexisting delta value is larger than the current depth. */
     334  static void
     335  treedelta (register struct tree const *tree,
     336             register unsigned int depth,
     337             unsigned char delta[])
     338  {
     339    if (!tree)
     340      return;
     341    treedelta (tree->llink, depth, delta);
     342    treedelta (tree->rlink, depth, delta);
     343    if (depth < delta[tree->label])
     344      delta[tree->label] = depth;
     345  }
     346  
     347  /* Return true if A has every label in B. */
     348  static int
     349  hasevery (register struct tree const *a, register struct tree const *b)
     350  {
     351    if (!b)
     352      return 1;
     353    if (!hasevery (a, b->llink))
     354      return 0;
     355    if (!hasevery (a, b->rlink))
     356      return 0;
     357    while (a && b->label != a->label)
     358      if (b->label < a->label)
     359        a = a->llink;
     360      else
     361        a = a->rlink;
     362    return !!a;
     363  }
     364  
     365  /* Compute a vector, indexed by character code, of the trie nodes
     366     referenced from the given tree. */
     367  static void
     368  treenext (struct tree const *tree, struct trie *next[])
     369  {
     370    if (!tree)
     371      return;
     372    treenext (tree->llink, next);
     373    treenext (tree->rlink, next);
     374    next[tree->label] = tree->trie;
     375  }
     376  
     377  /* Compute the shift for each trie node, as well as the delta
     378     table and next cache for the given keyword set. */
     379  const char *
     380  kwsprep (kwset_t kwset)
     381  {
     382    unsigned char delta[NCHAR];
     383  
     384    /* Initial values for the delta table; will be changed later.  The
     385       delta entry for a given character is the smallest depth of any
     386       node at which an outgoing edge is labeled by that character. */
     387    {
     388      register int i;
     389  
     390      if (kwset->mind < 256)
     391        for (i = 0; i < NCHAR; ++i)
     392          delta[i] = kwset->mind;
     393      else
     394        for (i = 0; i < NCHAR; ++i)
     395          delta[i] = 255;
     396    }
     397  
     398    /* Check if we can use the simple boyer-moore algorithm, instead
     399       of the hairy commentz-walter algorithm. */
     400    if (kwset->words == 1 && kwset->trans == 0)
     401      {
     402        register int i;
     403        register struct trie *curr;
     404  
     405        /* Looking for just one string.  Extract it from the trie. */
     406        kwset->target = (char *) obstack_alloc (&kwset->obstack, kwset->mind);
     407        for (i = kwset->mind - 1, curr = kwset->trie; i >= 0; --i)
     408          {
     409            kwset->target[i] = curr->links->label;
     410            curr = curr->links->trie;
     411          }
     412        /* Build the Boyer Moore delta.  Boy that's easy compared to CW. */
     413        for (i = 0; i < kwset->mind; ++i)
     414          delta[(unsigned char) kwset->target[i]] = kwset->mind - (i + 1);
     415        kwset->mind2 = kwset->mind;
     416        /* Find the minimal delta2 shift that we might make after
     417           a backwards match has failed. */
     418        for (i = 0; i < kwset->mind - 1; ++i)
     419          if (kwset->target[i] == kwset->target[kwset->mind - 1])
     420            kwset->mind2 = kwset->mind - (i + 1);
     421      }
     422    else
     423      {
     424        register struct trie *curr;
     425        struct trie *last;
     426  
     427        /* Traverse the nodes of the trie in level order, simultaneously
     428           computing the delta table, failure function, and shift function. */
     429        for (curr = last = kwset->trie; curr; curr = curr->next)
     430          {
     431            register struct trie *fail;
     432  
     433            /* Enqueue the immediate descendents in the level order queue. */
     434            enqueue (curr->links, &last);
     435  
     436            curr->shift = kwset->mind;
     437            curr->maxshift = kwset->mind;
     438  
     439            /* Update the delta table for the descendents of this node. */
     440            treedelta (curr->links, curr->depth, delta);
     441  
     442            /* Compute the failure function for the descendants of this node. */
     443            treefails (curr->links, curr->fail, kwset->trie);
     444  
     445            /* Update the shifts at each node in the current node's chain
     446               of fails back to the root. */
     447            for (fail = curr->fail; fail; fail = fail->fail)
     448              {
     449                /* If the current node has some outgoing edge that the fail
     450                   doesn't, then the shift at the fail should be no larger
     451                   than the difference of their depths. */
     452                if (!hasevery (fail->links, curr->links))
     453                  if (curr->depth - fail->depth < fail->shift)
     454                    fail->shift = curr->depth - fail->depth;
     455  
     456                /* If the current node is accepting then the shift at the
     457                   fail and its descendents should be no larger than the
     458                   difference of their depths. */
     459                if (curr->accepting && fail->maxshift > curr->depth - fail->depth)
     460                  fail->maxshift = curr->depth - fail->depth;
     461              }
     462          }
     463  
     464        /* Traverse the trie in level order again, fixing up all nodes whose
     465           shift exceeds their inherited maxshift. */
     466        for (curr = kwset->trie->next; curr; curr = curr->next)
     467          {
     468            if (curr->maxshift > curr->parent->maxshift)
     469              curr->maxshift = curr->parent->maxshift;
     470            if (curr->shift > curr->maxshift)
     471              curr->shift = curr->maxshift;
     472          }
     473  
     474        /* Create a vector, indexed by character code, of the outgoing links
     475           from the root node. */
     476        {
     477          struct trie *next[NCHAR];
     478          register int i;
     479  
     480          for (i = 0; i < NCHAR; ++i)
     481            next[i] = 0;
     482          treenext (kwset->trie->links, next);
     483  
     484          {
     485            register char const *trans;
     486  
     487            if ((trans = kwset->trans) != 0)
     488              for (i = 0; i < NCHAR; ++i)
     489                kwset->next[i] = next[(unsigned char) trans[i]];
     490            else
     491              for (i = 0; i < NCHAR; ++i)
     492                kwset->next[i] = next[i];
     493          }
     494        }
     495      }
     496  
     497    /* Fix things up for any translation table. */
     498    {
     499      register char const *trans;
     500      register int i;
     501  
     502      if ((trans = kwset->trans) != 0)
     503        for (i = 0; i < NCHAR; ++i)
     504          kwset->delta[i] = delta[(unsigned char) trans[i]];
     505      else
     506        for (i = 0; i < NCHAR; ++i)
     507          kwset->delta[i] = delta[i];
     508    }
     509  
     510    return 0;
     511  }
     512  
     513  #define U(C) ((unsigned char) (C))
     514  
     515  /* Fast boyer-moore search. */
     516  static size_t
     517  bmexec (kwset_t kws, char const *text, size_t size)
     518  {
     519    struct kwset const *kwset;
     520    register int len;
     521  
     522    kwset = (struct kwset const *) kws;
     523    len = kwset->mind;
     524  
     525    if (len == 0)
     526      return 0;
     527    if (len > size)
     528      return -1;
     529    if (len == 1)
     530      {
     531        register char const *tp;
     532  
     533        tp = (const char *) memchr (text, kwset->target[0], size);
     534        return tp ? tp - text : -1;
     535      }
     536  
     537    {
     538      register unsigned char const *d1;
     539      register char const *sp;
     540      register int gc;
     541      register int md2;
     542      register char const *tp;
     543  
     544      d1 = kwset->delta;
     545      sp = kwset->target + len;
     546      gc = U(sp[-2]);
     547      md2 = kwset->mind2;
     548      tp = text + len;
     549  
     550      /* Significance of 12: 1 (initial offset) + 10 (skip loop) + 1 (md2). */
     551      if (size > 12 * len)
     552        {
     553          register char const *ep;
     554          register int d;
     555  
     556          /* 11 is not a bug, the initial offset happens only once. */
     557          for (ep = text + size - 11 * len;;)
     558            {
     559              while (tp <= ep)
     560                {
     561                  d = d1[U(tp[-1])], tp += d;
     562                  d = d1[U(tp[-1])], tp += d;
     563                  if (d == 0)
     564                    goto found;
     565                  d = d1[U(tp[-1])], tp += d;
     566                  d = d1[U(tp[-1])], tp += d;
     567                  d = d1[U(tp[-1])], tp += d;
     568                  if (d == 0)
     569                    goto found;
     570                  d = d1[U(tp[-1])], tp += d;
     571                  d = d1[U(tp[-1])], tp += d;
     572                  d = d1[U(tp[-1])], tp += d;
     573                  if (d == 0)
     574                    goto found;
     575                  d = d1[U(tp[-1])], tp += d;
     576                  d = d1[U(tp[-1])], tp += d;
     577                }
     578              break;
     579            found:
     580              if (U(tp[-2]) == gc)
     581                {
     582                  register int i;
     583                  for (i = 3; i <= len && U(tp[-i]) == U(sp[-i]); ++i)
     584                    ;
     585                  if (i > len)
     586                    return tp - len - text;
     587                }
     588              tp += md2;
     589            }
     590        }
     591  
     592      {
     593        /* Now we have only a few characters left to search.  We
     594           carefully avoid ever producing an out-of-bounds pointer. */
     595        register char const *ep;
     596        register int d;
     597  
     598        ep = text + size;
     599        d = d1[U(tp[-1])];
     600        while (d <= ep - tp)
     601          {
     602            d = d1[U((tp += d)[-1])];
     603            if (d != 0)
     604              continue;
     605            if (U(tp[-2]) == gc)
     606              {
     607                register int i;
     608                for (i = 3; i <= len && U(tp[-i]) == U(sp[-i]); ++i)
     609                  ;
     610                if (i > len)
     611                  return tp - len - text;
     612              }
     613            d = md2;
     614          }
     615      }
     616    }
     617  
     618    return -1;
     619  }
     620  
     621  /* Hairy multiple string search. */
     622  static size_t
     623  cwexec (kwset_t kws, char const *text, size_t len, struct kwsmatch *kwsmatch)
     624  {
     625    struct kwset const *kwset;
     626  
     627    /* Initialize register copies and look for easy ways out. */
     628    kwset = (struct kwset *) kws;
     629    if (len < kwset->mind)
     630      return -1;
     631    {
     632      struct trie const *accept;
     633      struct trie * const *next;
     634      register unsigned char const *delta;
     635      register char const *trans;
     636      char const *lim;
     637      register char const *end;
     638      register int d;
     639      char const *mch;
     640      register char const *qlim;
     641  
     642      accept = NULL;
     643  
     644      next = kwset->next;
     645      delta = kwset->delta;
     646      trans = kwset->trans;
     647      lim = text + len;
     648      end = text;
     649      if ((d = kwset->mind) != 0)
     650        mch = 0;
     651      else
     652        {
     653          mch = text, accept = kwset->trie;
     654          goto match;
     655        }
     656  
     657      if (len >= 4 * kwset->mind)
     658        qlim = lim - 4 * kwset->mind;
     659      else
     660        qlim = 0;
     661  
     662      while (lim - end >= d)
     663        {
     664          char const *beg;
     665          struct trie const *trie;
     666  
     667          {
     668            register unsigned char c;
     669  
     670            if (qlim && end <= qlim)
     671              {
     672                end += d - 1;
     673                while ((d = delta[c = *end]) && end < qlim)
     674                  {
     675                    end += d;
     676                    end += delta[(unsigned char) *end];
     677                    end += delta[(unsigned char) *end];
     678                  }
     679                ++end;
     680              }
     681            else
     682              d = delta[c = (end += d)[-1]];
     683            if (d)
     684              continue;
     685            beg = end - 1;
     686            trie = next[c];
     687          }
     688          if (trie->accepting)
     689            {
     690              mch = beg;
     691              accept = trie;
     692            }
     693          d = trie->shift;
     694          while (beg > text)
     695            {
     696              register unsigned char c;
     697              register struct tree const *tree;
     698  
     699              c = trans ? trans[(unsigned char) *--beg] : *--beg;
     700              tree = trie->links;
     701              while (tree && c != tree->label)
     702                if (c < tree->label)
     703                  tree = tree->llink;
     704                else
     705                  tree = tree->rlink;
     706              if (tree)
     707                {
     708                  trie = tree->trie;
     709                  if (trie->accepting)
     710                    {
     711                      mch = beg;
     712                      accept = trie;
     713                    }
     714                }
     715              else
     716                break;
     717              d = trie->shift;
     718            }
     719          if (mch)
     720            goto match;
     721        }
     722      return -1;
     723  
     724     match:
     725      /* Given a known match, find the longest possible match anchored
     726         at or before its starting point.  This is nearly a verbatim
     727         copy of the preceding main search loops. */
     728      {
     729        char const *lmch;
     730  
     731        if (lim - mch > kwset->maxd)
     732          lim = mch + kwset->maxd;
     733        lmch = 0;
     734        d = 1;
     735        while (lim - end >= d)
     736          {
     737            char const *beg;
     738            struct trie const *trie;
     739  
     740            {
     741              register unsigned char c;
     742  
     743              if ((d = delta[c = (end += d)[-1]]) != 0)
     744                continue;
     745              beg = end - 1;
     746              if (!(trie = next[c]))
     747                {
     748                  d = 1;
     749                  continue;
     750                }
     751            }
     752            if (trie->accepting && beg <= mch)
     753              {
     754                lmch = beg;
     755                accept = trie;
     756              }
     757            d = trie->shift;
     758            while (beg > text)
     759              {
     760                register unsigned char c;
     761                register struct tree const *tree;
     762  
     763                c = trans ? trans[(unsigned char) *--beg] : *--beg;
     764                tree = trie->links;
     765                while (tree && c != tree->label)
     766                  if (c < tree->label)
     767                    tree = tree->llink;
     768                  else
     769                    tree = tree->rlink;
     770                if (tree)
     771                  {
     772                    trie = tree->trie;
     773                    if (trie->accepting && beg <= mch)
     774                      {
     775                        lmch = beg;
     776                        accept = trie;
     777                      }
     778                  }
     779                else
     780                  break;
     781                d = trie->shift;
     782              }
     783            if (lmch)
     784              {
     785                mch = lmch;
     786                goto match;
     787              }
     788            if (!d)
     789              d = 1;
     790          }
     791      }
     792  
     793      if (kwsmatch)
     794        {
     795          kwsmatch->index = accept->accepting / 2;
     796          kwsmatch->offset[0] = mch - text;
     797          kwsmatch->size[0] = accept->depth;
     798        }
     799      return mch - text;
     800    }
     801  }
     802  
     803  /* Search through the given text for a match of any member of the
     804     given keyword set.  Return a pointer to the first character of
     805     the matching substring, or NULL if no match is found.  If FOUNDLEN
     806     is non-NULL store in the referenced location the length of the
     807     matching substring.  Similarly, if FOUNDIDX is non-NULL, store
     808     in the referenced location the index number of the particular
     809     keyword matched. */
     810  size_t
     811  kwsexec (kwset_t kws, char const *text, size_t size,
     812           struct kwsmatch *kwsmatch)
     813  {
     814    struct kwset const *kwset = (struct kwset *) kws;
     815    if (kwset->words == 1 && kwset->trans == 0)
     816      {
     817        size_t ret = bmexec (kws, text, size);
     818        if (kwsmatch != 0 && ret != (size_t) -1)
     819          {
     820            kwsmatch->index = 0;
     821            kwsmatch->offset[0] = ret;
     822            kwsmatch->size[0] = kwset->mind;
     823          }
     824        return ret;
     825      }
     826    else
     827      return cwexec (kws, text, size, kwsmatch);
     828  }
     829  
     830  /* Free the components of the given keyword set. */
     831  void
     832  kwsfree (kwset_t kwset)
     833  {
     834    obstack_free (&kwset->obstack, 0);
     835    free (kwset);
     836  }