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