home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / FGREP11.ZIP / KWSET.C < prev    next >
C/C++ Source or Header  |  1990-08-27  |  18KB  |  610 lines

  1. /* kwset.c - search for any of a set of keywords.
  2.    Copyright 1989 Free Software Foundation
  3.           Written August 1989 by Mike Haertel.
  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 1, or (at your option)
  8.    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, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  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. #include "std.h"
  23.  
  24. /* The algorithm implemented by these routines bears a startling resemblence
  25.    to one discovered by Beate Commentz-Walter, although it is not identical.
  26.    See "A String Matching Algorithm Fast on the Average," Technical Report,
  27.    IBM-Germany, Scientific Center Heidelberg, Tiergartenstrasse 15, D-6900
  28.    Heidelberg, Germany.  See also Aho, A.V., and M. Corasick, "Efficient
  29.    String Matching:  An Aid to Bibliographic Search," CACM June 1975,
  30.    Vol. 18, No. 6, which describes the failure function used below. */
  31.  
  32. #include "kwset.h"
  33. #include "obstack.h"
  34.  
  35. #define NCHAR (UCHAR_MAX + 1)
  36. #define obstack_chunk_alloc malloc
  37. #define obstack_chunk_free free
  38.  
  39. /* Balanced tree of edges and labels leaving a given trie node. */
  40. struct tree
  41. {
  42.   struct tree *llink;        /* Left link; MUST be first field. */
  43.   struct tree *rlink;        /* Right link (to larger labels). */
  44.   struct trie *trie;        /* Trie node pointed to by this edge. */
  45.   unsigned char label;        /* Label on this edge. */
  46.   char balance;            /* Difference in depths of subtrees. */
  47. };
  48.  
  49. /* Node of a trie representing a set of reversed keywords. */
  50. struct trie
  51. {
  52.   unsigned int accepting;    /* Word index of accepted word, or zero. */
  53.   struct tree *links;        /* Tree of edges leaving this node. */
  54.   struct trie *parent;        /* Parent of this node. */
  55.   struct trie *next;        /* List of all trie nodes in level order. */
  56.   struct trie *fail;        /* Aho-Corasick failure function. */
  57.   int depth;            /* Depth of this node from the root. */
  58.   int shift;            /* Shift function for search failures. */
  59.   int maxshift;            /* Max shift of self and descendents. */
  60. };
  61.  
  62. /* Structure returned opaquely to the caller, containing everything. */
  63. struct kwset
  64. {
  65.   struct obstack obstack;    /* Obstack for node allocation. */
  66.   int words;            /* Number of words in the trie. */
  67.   struct trie *trie;        /* The trie itself. */
  68.   int mind;            /* Minimum depth of an accepting node. */
  69.   int maxd;            /* Maximum depth of any node. */
  70.   int delta[NCHAR];        /* Delta table for rapid search. */
  71.   struct trie *next[NCHAR];    /* Table of children of the root. */
  72.   const char *trans;        /* Character translation table. */
  73. };
  74.  
  75. /* Allocate and initialize a keyword set object, returning an opaque
  76.    pointer to it.  Return NULL if memory is not available. */
  77. kwset_t
  78. DEFUN(kwsalloc, (trans), const char *trans)
  79. {
  80.   struct kwset *kwset;
  81.  
  82.   kwset = (struct kwset *) malloc(sizeof (struct kwset));
  83.   if (!kwset)
  84.     return NULL;
  85.  
  86.   obstack_init(&kwset->obstack);
  87.   kwset->words = 0;
  88.   kwset->trie
  89.     = (struct trie *) obstack_alloc(&kwset->obstack, sizeof (struct trie));
  90.   if (!kwset->trie)
  91.     {
  92.       kwsfree((kwset_t) kwset);
  93.       return NULL;
  94.     }
  95.   kwset->trie->accepting = 0;
  96.   kwset->trie->links = NULL;
  97.   kwset->trie->parent = NULL;
  98.   kwset->trie->next = NULL;
  99.   kwset->trie->fail = NULL;
  100.   kwset->trie->depth = 0;
  101.   kwset->trie->shift = 0;
  102.   kwset->mind = INT_MAX;
  103.   kwset->maxd = -1;
  104.   kwset->trans = trans;
  105.  
  106.   return (kwset_t) kwset;
  107. }
  108.  
  109. /* Add the given string to the contents of the keyword set.  Return NULL
  110.    for success, an error message otherwise. */
  111. const char *
  112. DEFUN(kwsincr, (kws, text, len),
  113.       kwset_t kws AND const char *text AND size_t len)
  114. {
  115.   struct kwset *kwset;
  116.   register struct trie *trie;
  117.   register unsigned char label;
  118.   register struct tree *link;
  119.   register int depth;
  120.   struct tree *links[12];
  121.   enum { L, R } dirs[12];
  122.   struct tree *t, *r, *l, *rl, *lr;
  123.  
  124.   kwset = (struct kwset *) kws;
  125.   trie = kwset->trie;
  126.   text += len;
  127.  
  128.   /* Descend the trie (built of reversed keywords) character-by-character,
  129.      installing new nodes when necessary. */
  130.   while (len--)
  131.     {
  132.       label = kwset->trans ? kwset->trans[(unsigned char) *--text] : *--text;
  133.  
  134.       /* Descend the tree of outgoing links for this trie node,
  135.      looking for the current character and keeping track
  136.      of the path followed. */
  137.       link = trie->links;
  138.       links[0] = (struct tree *) &trie->links;
  139.       dirs[0] = L;
  140.       depth = 1;
  141.  
  142.       while (link && label != link->label)
  143.     {
  144.       links[depth] = link;
  145.       if (label < link->label)
  146.         dirs[depth++] = L, link = link->llink;
  147.       else
  148.         dirs[depth++] = R, link = link->rlink;
  149.     }
  150.  
  151.       /* The current character doesn't have an outgoing link at
  152.      this trie node, so build a new trie node and install
  153.      a link in the current trie node's tree. */
  154.       if (!link)
  155.     {
  156.       link = (struct tree *) obstack_alloc(&kwset->obstack,
  157.                            sizeof (struct tree));
  158.       if (!link)
  159.         return "memory exhausted";
  160.       link->llink = NULL;
  161.       link->rlink = NULL;
  162.       link->trie = (struct trie *) obstack_alloc(&kwset->obstack,
  163.                              sizeof (struct trie));
  164.       if (!link->trie)
  165.         return "memory exhausted";
  166.       link->trie->accepting = 0;
  167.       link->trie->links = NULL;
  168.       link->trie->parent = trie;
  169.       link->trie->next = NULL;
  170.       link->trie->fail = NULL;
  171.       link->trie->depth = trie->depth + 1;
  172.       link->trie->shift = 0;
  173.       link->label = label;
  174.       link->balance = 0;
  175.  
  176.       /* Install the new tree node in its parent. */
  177.       if (dirs[--depth] == L)
  178.         links[depth]->llink = link;
  179.       else
  180.         links[depth]->rlink = link;
  181.  
  182.       /* Back up the tree fixing the balance flags. */
  183.       while (depth && !links[depth]->balance)
  184.         {
  185.           if (dirs[depth] == L)
  186.         --links[depth]->balance;
  187.           else
  188.         ++links[depth]->balance;
  189.           --depth;
  190.         }
  191.  
  192.       /* Rebalance the tree by pointer rotations if necessary. */
  193.       if (depth && (dirs[depth] == L && --links[depth]->balance
  194.             || dirs[depth] == R && ++links[depth]->balance))
  195.         {
  196.           switch (links[depth]->balance)
  197.         {
  198.         case (char) -2:
  199.           switch (dirs[depth + 1])
  200.             {
  201.             case L:
  202.               r = links[depth], t = r->llink, rl = t->rlink;
  203.               t->rlink = r, r->llink = rl;
  204.               t->balance = r->balance = 0;
  205.               break;
  206.             case R:
  207.               r = links[depth], l = r->llink, t = l->rlink;
  208.               rl = t->rlink, lr = t->llink;
  209.               t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
  210.               l->balance = t->balance != 1 ? 0 : -1;
  211.               r->balance = t->balance != (char) -1 ? 0 : 1;
  212.               t->balance = 0;
  213.               break;
  214.             }
  215.           break;
  216.         case 2:
  217.           switch (dirs[depth + 1])
  218.             {
  219.             case R:
  220.               l = links[depth], t = l->rlink, lr = t->llink;
  221.               t->llink = l, l->rlink = lr;
  222.               t->balance = l->balance = 0;
  223.               break;
  224.             case L:
  225.               l = links[depth], r = l->rlink, t = r->llink;
  226.               lr = t->llink, rl = t->rlink;
  227.               t->llink = l, l->rlink = lr, t->rlink = r, r->llink = rl;
  228.               l->balance = t->balance != 1 ? 0 : -1;
  229.               r->balance = t->balance != (char) -1 ? 0 : 1;
  230.               t->balance = 0;
  231.               break;
  232.             }
  233.           break;
  234.         }
  235.  
  236.           if (dirs[depth - 1] == L)
  237.         links[depth - 1]->llink = t;
  238.           else
  239.         links[depth - 1]->rlink = t;
  240.         }
  241.     }
  242.  
  243.       trie = link->trie;
  244.     }
  245.  
  246.   /* Mark the node we finally reached as accepting, encoding the
  247.      index number of this word in the keyword set so far. */
  248.   if (!trie->accepting)
  249.     trie->accepting = 1 + 2 * kwset->words;
  250.   ++kwset->words;
  251.  
  252.   /* Keep track of the longest and shortest string of the keyword set. */
  253.   if (trie->depth < kwset->mind)
  254.     kwset->mind = trie->depth;
  255.   if (trie->depth > kwset->maxd)
  256.     kwset->maxd = trie->depth;
  257.  
  258.   return NULL;
  259. }
  260.  
  261. /* Enqueue the trie nodes referenced from the given tree in the
  262.    given queue. */
  263. static void
  264. DEFUN(enqueue, (tree, last), struct tree *tree AND struct trie **last)
  265. {
  266.   if (!tree)
  267.     return;
  268.   enqueue(tree->llink, last);
  269.   enqueue(tree->rlink, last);
  270.   (*last) = (*last)->next = tree->trie;
  271. }
  272.  
  273. /* Compute the Aho-Corasick failure function for the trie nodes referenced
  274.    from the given tree, given the failure function for their parent as
  275.    well as a last resort failure node. */
  276. static void
  277. DEFUN(treefails, (tree, fail, recourse),
  278.       register struct tree *tree
  279.       AND struct trie *fail AND struct trie *recourse)
  280. {
  281.   register struct tree *link;
  282.  
  283.   if (!tree)
  284.     return;
  285.  
  286.   treefails(tree->llink, fail, recourse);
  287.   treefails(tree->rlink, fail, recourse);
  288.  
  289.   /* Find, in the chain of fails going back to the root, the first
  290.      node that has a descendent on the current label. */
  291.   while (fail)
  292.     {
  293.       link = fail->links;
  294.       while (link && tree->label != link->label)
  295.     if (tree->label < link->label)
  296.       link = link->llink;
  297.     else
  298.       link = link->rlink;
  299.       if (link)
  300.     {
  301.       tree->trie->fail = link->trie;
  302.       return;
  303.     }
  304.       fail = fail->fail;
  305.     }
  306.  
  307.   tree->trie->fail = recourse;
  308. }
  309.  
  310. /* Set delta entries for the links of the given tree such that
  311.    the preexisting delta value is larger than the current depth. */
  312. static void
  313. DEFUN(treedelta, (tree, depth, delta),
  314.       register struct tree *tree AND register int depth AND int delta[])
  315. {
  316.   if (!tree)
  317.     return;
  318.   treedelta(tree->llink, depth, delta);
  319.   treedelta(tree->rlink, depth, delta);
  320.   if (depth < delta[tree->label])
  321.     delta[tree->label] = depth;
  322. }
  323.  
  324. /* Return true if A has every label in B. */
  325. static int
  326. DEFUN(hasevery, (a, b), register struct tree *a AND register struct tree *b)
  327. {
  328.   if (!b)
  329.     return 1;
  330.   if (!hasevery(a, b->llink))
  331.     return 0;
  332.   if (!hasevery(a, b->rlink))
  333.     return 0;
  334.   while (a && b->label != a->label)
  335.     if (b->label < a->label)
  336.       a = a->llink;
  337.     else
  338.       a = a->rlink;
  339.   return !!a;
  340. }
  341.  
  342. /* Compute a vector, indexed by character code, of the trie nodes
  343.    referenced from the given tree. */
  344. static void
  345. DEFUN(treenext, (tree, next), struct tree *tree AND struct trie *next[])
  346. {
  347.   if (!tree)
  348.     return;
  349.   treenext(tree->llink, next);
  350.   treenext(tree->rlink, next);
  351.   next[tree->label] = tree->trie;
  352. }
  353.  
  354. /* Compute the shift for each trie node, as well as the delta
  355.    table and next cache for the given keyword set. */
  356. const char *
  357. DEFUN(kwsprep, (kws), kwset_t kws)
  358. {
  359.   register struct kwset *kwset;
  360.   register int i;
  361.   register struct trie *curr, *fail;
  362.   register const char *trans;
  363.   int delta[NCHAR];
  364.   struct trie *last, *next[NCHAR];
  365.  
  366.   kwset = (struct kwset *) kws;
  367.  
  368.   /* Initial values for the delta table; will be changed later.  The
  369.      delta entry for a given character is the smallest depth of any
  370.      node at which an outgoing edge is labeled by that character. */
  371.   for (i = 0; i < NCHAR; ++i)
  372.     delta[i] = kwset->mind;
  373.  
  374.   /* Traverse the nodes of the trie in level order, simultaneously
  375.      computing the delta table, failure function, and shift function. */
  376.   for (curr = last = kwset->trie; curr; curr = curr->next)
  377.     {
  378.       /* Enqueue the immediate descendents in the level order queue. */
  379.       enqueue(curr->links, &last);
  380.  
  381.       curr->shift = kwset->mind;
  382.       curr->maxshift = kwset->mind;
  383.  
  384.       /* Update the delta table for the descendents of this node. */
  385.       treedelta(curr->links, curr->depth, delta);
  386.  
  387.       /* Compute the failure function for the decendents of this node. */
  388.       treefails(curr->links, curr->fail, kwset->trie);
  389.  
  390.       /* Update the shifts at each node in the current node's chain
  391.      of fails back to the root. */
  392.       for (fail = curr->fail; fail; fail = fail->fail)
  393.     {
  394.       /* If the current node has some outgoing edge that the fail
  395.          doesn't, then the shift at the fail should be no larger
  396.          than the difference of their depths. */
  397.       if (!hasevery(fail->links, curr->links))
  398.         if (curr->depth - fail->depth < fail->shift)
  399.           fail->shift = curr->depth - fail->depth;
  400.  
  401.       /* If the current node is accepting then the shift at the
  402.          fail and its descendents should be no larger than the
  403.          difference of their depths. */
  404.       if (curr->accepting && fail->maxshift > curr->depth - fail->depth)
  405.         fail->maxshift = curr->depth - fail->depth;
  406.     }
  407.     }
  408.  
  409.   /* Traverse the trie in level order again, fixing up all nodes whose
  410.      shift exceeds their inherited maxshift. */
  411.   for (curr = kwset->trie->next; curr; curr = curr->next)
  412.     {
  413.       if (curr->maxshift > curr->parent->maxshift)
  414.     curr->maxshift = curr->parent->maxshift;
  415.       if (curr->shift > curr->maxshift)
  416.     curr->shift = curr->maxshift;
  417.     }
  418.  
  419.   /* Create a vector, indexed by character code, of the outgoing links
  420.      from the root node. */
  421.   for (i = 0; i < NCHAR; ++i)
  422.     next[i] = NULL;
  423.   treenext(kwset->trie->links, next);
  424.  
  425.   /* Fix things up for any translation table. */
  426.   if (trans = kwset->trans)
  427.     for (i = 0; i < NCHAR; ++i)
  428.       {
  429.     kwset->delta[i] = delta[(unsigned char) trans[i]];
  430.     kwset->next[i] = next[(unsigned char) trans[i]];
  431.       }
  432.   else
  433.     for (i = 0; i < NCHAR; ++i)
  434.       {
  435.     kwset->delta[i] = delta[i];
  436.     kwset->next[i] = next[i];
  437.       }
  438.  
  439.   return NULL;
  440. }
  441.  
  442. /* Search through the given text for a match of any member of the
  443.    given keyword set.  Return a pointer to the first character of
  444.    the matching substring, or NULL if no match is found.  If FOUNDLEN
  445.    is non-NULL store in the referenced location the length of the
  446.    matching substring.  Similarly, if FOUNDIDX is non-NULL, store
  447.    in the referenced location the index number of the particular
  448.    keyword matched. */
  449. char *
  450. DEFUN(kwsexec, (kws, text, len, kwsmatch),
  451.       kwset_t kws AND char *text AND size_t len AND struct kwsmatch *kwsmatch)
  452. {
  453.   struct kwset *kwset;
  454.   struct trie **next, *trie, *accept;
  455.   char *beg, *lim, *mch, *lmch;
  456.   register unsigned char c;
  457.   register int *delta, d;
  458.   register char *end, *qlim;
  459.   register struct tree *tree;
  460.   register const char *trans;
  461.  
  462.   /* Initialize register copies and look for easy ways out. */
  463.   kwset = (struct kwset *) kws;
  464.   if (len < kwset->mind)
  465.     return NULL;
  466.   next = kwset->next;
  467.   delta = kwset->delta;
  468.   trans = kwset->trans;
  469.   lim = text + len;
  470.   end = text;
  471.   if (d = kwset->mind)
  472.     mch = NULL;
  473.   else
  474.     {
  475.       mch = text, accept = kwset->trie;
  476.       goto match;
  477.     }
  478.  
  479.   if (len >= 4 * kwset->mind)
  480.     qlim = lim - 4 * kwset->mind;
  481.   else
  482.     qlim = NULL;
  483.  
  484.   while (lim - end >= d)
  485.     {
  486.       if (qlim && end <= qlim)
  487.     {
  488.       end += d - 1;
  489.       while ((d = delta[c = *end]) && end < qlim)
  490.         {
  491.           end += d;
  492.           end += delta[(unsigned char) *end];
  493.           end += delta[(unsigned char) *end];
  494.         }
  495.       ++end;
  496.     }
  497.       else
  498.     d = delta[c = (end += d)[-1]];
  499.       if (d)
  500.     continue;
  501.       beg = end - 1;
  502.       trie = next[c];
  503.       if (trie->accepting)
  504.     {
  505.       mch = beg;
  506.       accept = trie;
  507.     }
  508.       d = trie->shift;
  509.       while (beg > text)
  510.     {
  511.       c = trans ? trans[(unsigned char) *--beg] : *--beg;
  512.       tree = trie->links;
  513.       while (tree && c != tree->label)
  514.         if (c < tree->label)
  515.           tree = tree->llink;
  516.         else
  517.           tree = tree->rlink;
  518.       if (tree)
  519.         {
  520.           trie = tree->trie;
  521.           if (trie->accepting)
  522.         {
  523.           mch = beg;
  524.           accept = trie;
  525.         }
  526.         }
  527.       else
  528.         break;
  529.       d = trie->shift;
  530.     }
  531.       if (mch)
  532.     goto match;
  533.     }
  534.   return NULL;
  535.  
  536.  match:
  537.   /* Given a known match, find the longest possible match anchored
  538.      at or before its starting point.  This is nearly a verbatim
  539.      copy of the preceding main search loops. */
  540.   if (lim - mch > kwset->maxd)
  541.     lim = mch + kwset->maxd;
  542.   lmch = NULL;
  543.   d = 1;
  544.   while (lim - end >= d)
  545.     {
  546.       if (d = delta[c = (end += d)[-1]])
  547.     continue;
  548.       beg = end - 1;
  549.       if (!(trie = next[c]))
  550.     {
  551.       d = 1;
  552.       continue;
  553.     }
  554.       if (trie->accepting && beg <= mch)
  555.     {
  556.       lmch = beg;
  557.       accept = trie;
  558.     }
  559.       d = trie->shift;
  560.       while (beg > text)
  561.     {
  562.       c = trans ? trans[(unsigned char) *--beg] : *--beg;
  563.       tree = trie->links;
  564.       while (tree && c != tree->label)
  565.         if (c < tree->label)
  566.           tree = tree->llink;
  567.         else
  568.           tree = tree->rlink;
  569.       if (tree)
  570.         {
  571.           trie = tree->trie;
  572.           if (trie->accepting && beg <= mch)
  573.         {
  574.           lmch = beg;
  575.           accept = trie;
  576.         }
  577.         }
  578.       else
  579.         break;
  580.       d = trie->shift;
  581.     }
  582.       if (lmch)
  583.     {
  584.       mch = lmch;
  585.       goto match;
  586.     }
  587.       if (!d)
  588.     d = 1;
  589.     }
  590.  
  591.   if (kwsmatch)
  592.     {
  593.       kwsmatch->index = accept->accepting / 2;
  594.       kwsmatch->beg[0] = mch;
  595.       kwsmatch->size[0] = accept->depth;
  596.     }
  597.   return mch;
  598. }
  599.  
  600. /* Free the components of the given keyword set. */
  601. void
  602. DEFUN(kwsfree, (kws), kwset_t kws)
  603. {
  604.   struct kwset *kwset;
  605.  
  606.   kwset = (struct kwset *) kws;
  607.   obstack_free(&kwset->obstack, (PTR) NULL);
  608.   free((PTR) kws);
  609. }
  610.