4b651dc78fe463ce359e3970427a24e1e83e54a3
[idzebra-moved-to-github.git] / dict / lookgrep.c
1 /*
2  * Copyright (C) 1994-1999, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: lookgrep.c,v $
7  * Revision 1.23  1999-05-15 14:36:37  adam
8  * Updated dictionary. Implemented "compression" of dictionary.
9  *
10  * Revision 1.22  1999/02/02 14:50:23  adam
11  * Updated WIN32 code specific sections. Changed header.
12  *
13  * Revision 1.21  1998/06/24 12:16:12  adam
14  * Support for relations on text operands. Open range support in
15  * DFA module (i.e. [-j], [g-]).
16  *
17  * Revision 1.20  1997/10/27 14:33:03  adam
18  * Moved towards generic character mapping depending on "structure"
19  * field in abstract syntax file. Fixed a few memory leaks. Fixed
20  * bug with negative integers when doing searches with relational
21  * operators.
22  *
23  * Revision 1.19  1997/09/18 08:59:18  adam
24  * Extra generic handle for the character mapping routines.
25  *
26  * Revision 1.18  1997/09/05 15:29:58  adam
27  * Changed prototype for chr_map_input - added const.
28  * Added support for C++, headers uses extern "C" for public definitions.
29  *
30  * Revision 1.17  1996/06/04 10:20:06  adam
31  * Added support for character mapping.
32  *
33  * Revision 1.16  1996/05/24  14:46:04  adam
34  * Added dict_grep_cmap function to define user-mapping in grep lookups.
35  *
36  * Revision 1.15  1996/03/20  09:35:18  adam
37  * Function dict_lookup_grep got extra parameter, init_pos, which marks
38  * from which position in pattern approximate pattern matching should occur.
39  *
40  * Revision 1.14  1996/02/02  13:43:51  adam
41  * The public functions simply use char instead of Dict_char to represent
42  * search strings. Dict_char is used internally only.
43  *
44  * Revision 1.13  1996/01/08  09:09:30  adam
45  * Function dfa_parse got 'const' string argument.
46  *
47  * Revision 1.12  1995/12/11  09:04:48  adam
48  * Bug fix: the lookup/scan/lookgrep didn't handle empty dictionary.
49  *
50  * Revision 1.11  1995/12/06  14:43:02  adam
51  * New function: dict_delete.
52  *
53  * Revision 1.10  1995/11/16  17:00:44  adam
54  * Changed stupid log.
55  *
56  * Revision 1.9  1995/10/27  13:58:09  adam
57  * Makes 'Database unavailable' diagnostic.
58  *
59  * Revision 1.8  1995/10/19  14:57:21  adam
60  * New feature: grep lookup saves length of longest prefix match.
61  *
62  * Revision 1.7  1995/10/17  18:01:22  adam
63  * Userfunc may return non-zero in which case the the grepping stops
64  * immediately.
65  *
66  * Revision 1.6  1995/10/09  16:18:32  adam
67  * Function dict_lookup_grep got extra client data parameter.
68  *
69  * Revision 1.5  1995/09/14  11:52:59  adam
70  * Grep handle function parameter info is const now.
71  *
72  * Revision 1.4  1995/01/24  16:01:02  adam
73  * Added -ansi to CFLAGS.
74  * Use new API of dfa module.
75  *
76  * Revision 1.3  1994/10/05  12:16:50  adam
77  * Pagesize is a resource now.
78  *
79  * Revision 1.2  1994/10/04  12:08:07  adam
80  * Some bug fixes and some optimizations.
81  *
82  * Revision 1.1  1994/10/03  17:23:04  adam
83  * First version of dictionary lookup with regular expressions and errors.
84  *
85  */
86
87 #include <stdlib.h>
88 #include <string.h>
89 #include <stdio.h>
90 #include <assert.h>
91
92 #include <dfa.h>
93 #include <dict.h>
94
95 typedef unsigned MatchWord;
96 #define WORD_BITS 32
97 #define MAX_LENGTH 1024
98
99 typedef struct {
100   int n;                 /* no of MatchWord needed */
101   int range;             /* max no. of errors */
102   int fact;              /* (range+1)*n */
103   MatchWord *match_mask; /* match_mask */
104 } MatchContext;
105
106 #define INLINE 
107
108 static INLINE void set_bit (MatchContext *mc, MatchWord *m, int ch, int state)
109 {
110     int off = state & (WORD_BITS-1);
111     int wno = state / WORD_BITS;
112   
113     m[mc->n * ch + wno] |= 1<<off;
114 }
115
116 static INLINE MatchWord get_bit (MatchContext *mc, MatchWord *m, int ch,
117                                  int state)
118 {
119     int off = state & (WORD_BITS-1);
120     int wno = state / WORD_BITS;
121
122     return m[mc->n * ch + wno] & (1<<off);
123 }
124
125 static MatchContext *mk_MatchContext (struct DFA *dfa, int range)
126 {
127     MatchContext *mc = xmalloc (sizeof(*mc));
128     int s;
129
130     mc->n = (dfa->no_states+WORD_BITS) / WORD_BITS;
131     mc->range = range;
132     mc->fact = (range+1)*mc->n;
133     mc->match_mask = xcalloc (mc->n, sizeof(*mc->match_mask));
134
135     for (s = 0; s<dfa->no_states; s++)
136         if (dfa->states[s]->rule_no)
137             set_bit (mc, mc->match_mask, 0, s);
138     return mc;
139 }
140
141 static void rm_MatchContext (MatchContext **mc)
142 {
143     xfree ((*mc)->match_mask);
144     xfree (*mc);
145     *mc = NULL;
146 }
147
148 static void mask_shift (MatchContext *mc, MatchWord *Rdst, MatchWord *Rsrc,
149                    struct DFA *dfa, int ch)
150 {
151     int j, s = 0;
152     MatchWord *Rsrc_p = Rsrc, mask;
153
154     for (j = 0; j<mc->n; j++)
155         Rdst[j] = 0;
156     while (1)
157     {
158         mask = *Rsrc_p++;
159         for (j = 0; j<WORD_BITS/4; j++)
160         {
161             if (mask & 15)
162             {
163                 if (mask & 1)
164                 {
165                     struct DFA_state *state = dfa->states[s];
166                     int i = state->tran_no;
167                     while (--i >= 0)
168                         if (ch >= state->trans[i].ch[0] &&
169                             ch <= state->trans[i].ch[1])
170                             set_bit (mc, Rdst, 0, state->trans[i].to);
171                 }
172                 if (mask & 2)
173                 {
174                     struct DFA_state *state = dfa->states[s+1];
175                     int i = state->tran_no;
176                     while (--i >= 0)
177                         if (ch >= state->trans[i].ch[0] &&
178                             ch <= state->trans[i].ch[1])
179                             set_bit (mc, Rdst, 0, state->trans[i].to);
180                 }
181                 if (mask & 4)
182                 {
183                     struct DFA_state *state = dfa->states[s+2];
184                     int i = state->tran_no;
185                     while (--i >= 0)
186                         if (ch >= state->trans[i].ch[0] &&
187                             ch <= state->trans[i].ch[1])
188                             set_bit (mc, Rdst, 0, state->trans[i].to);
189                 }
190                 if (mask & 8)
191                 {
192                     struct DFA_state *state = dfa->states[s+3];
193                     int i = state->tran_no;
194                     while (--i >= 0)
195                         if (ch >= state->trans[i].ch[0] &&
196                             ch <= state->trans[i].ch[1])
197                             set_bit (mc, Rdst, 0, state->trans[i].to);
198                 }
199             }
200             s += 4;
201             if (s >= dfa->no_states)
202                 return;
203             mask >>= 4;
204         }
205     }
206 }
207
208 static void shift (MatchContext *mc, MatchWord *Rdst, MatchWord *Rsrc,
209                    struct DFA *dfa)
210 {
211     int j, s = 0;
212     MatchWord *Rsrc_p = Rsrc, mask;
213     for (j = 0; j<mc->n; j++)
214         Rdst[j] = 0;
215     while (1)
216     {
217         mask = *Rsrc_p++;
218         for (j = 0; j<WORD_BITS/4; j++)
219         {
220             if (mask & 15)
221             {
222                 if (mask & 1)
223                 {
224                     struct DFA_state *state = dfa->states[s];
225                     int i = state->tran_no;
226                     while (--i >= 0)
227                         set_bit (mc, Rdst, 0, state->trans[i].to);
228                 }
229                 if (mask & 2)
230                 {
231                     struct DFA_state *state = dfa->states[s+1];
232                     int i = state->tran_no;
233                     while (--i >= 0)
234                         set_bit (mc, Rdst, 0, state->trans[i].to);
235                 }
236                 if (mask & 4)
237                 {
238                     struct DFA_state *state = dfa->states[s+2];
239                     int i = state->tran_no;
240                     while (--i >= 0)
241                         set_bit (mc, Rdst, 0, state->trans[i].to);
242                 }
243                 if (mask & 8)
244                 {
245                     struct DFA_state *state = dfa->states[s+3];
246                     int i = state->tran_no;
247                     while (--i >= 0)
248                         set_bit (mc, Rdst, 0, state->trans[i].to);
249                 }
250             }
251             s += 4;
252             if (s >= dfa->no_states)
253                 return;
254             mask >>= 4;
255         }
256     }
257 }
258
259 static void or (MatchContext *mc, MatchWord *Rdst,
260                 MatchWord *Rsrc1, MatchWord *Rsrc2)
261 {
262     int i;
263     for (i = 0; i<mc->n; i++)
264         Rdst[i] = Rsrc1[i] | Rsrc2[i];
265 }
266
267 static INLINE int move (MatchContext *mc, MatchWord *Rj1, MatchWord *Rj,
268                         Dict_char ch, struct DFA *dfa, MatchWord *Rtmp,
269                         int range)
270 {
271     int d;
272     MatchWord *Rtmp_2 = Rtmp + mc->n;
273
274     mask_shift (mc, Rj1, Rj, dfa, ch);
275     for (d = 1; d <= mc->range; d++)
276     {
277         or (mc, Rtmp, Rj, Rj1);                         /* 2,3 */
278         
279         shift (mc, Rtmp_2, Rtmp, dfa);
280
281         mask_shift (mc, Rtmp, Rj+mc->n, dfa, ch);      /* 1 */
282                 
283         or (mc, Rtmp, Rtmp_2, Rtmp);                    /* 1,2,3*/
284
285         Rj1 += mc->n;
286         
287         or (mc, Rj1, Rtmp, Rj);                         /* 1,2,3,4 */
288
289         Rj += mc->n;
290     }
291     return 1;
292
293 }
294
295
296 static int dict_grep (Dict dict, Dict_ptr ptr, MatchContext *mc,
297                       MatchWord *Rj, int pos, void *client,
298                       int (*userfunc)(char *, const char *, void *),
299                       Dict_char *prefix, struct DFA *dfa,
300                       int *max_pos, int init_pos)
301 {
302     int lo, hi, d;
303     void *p;
304     short *indxp;
305     char *info;
306
307     dict_bf_readp (dict->dbf, ptr, &p);
308     lo = 0;
309     hi = DICT_nodir(p)-1;
310     indxp = (short*) ((char*) p+DICT_bsize(p)-sizeof(short));
311
312     while (lo <= hi)
313     {
314         if (indxp[-lo] > 0)
315         {
316             /* string (Dict_char *) DICT_EOS terminated */
317             /* unsigned char        length of information */
318             /* char *               information */
319             int j;
320             int was_match = 0;
321             info = (char*)p + indxp[-lo];
322             for (j=0; ; j++)
323             {
324                 Dict_char ch;
325                 MatchWord *Rj0 =    Rj + j    *mc->fact;
326                 MatchWord *Rj1 =    Rj + (j+1)*mc->fact;
327                 MatchWord *Rj_tmp = Rj + (j+2)*mc->fact;
328                 int range;
329
330                 memcpy (&ch, info+j*sizeof(Dict_char), sizeof(Dict_char));
331                 prefix[pos+j] = ch;
332                 if (pos+j > *max_pos)
333                     *max_pos = pos+j;
334                 if (ch == DICT_EOS)
335                 {
336                     if (was_match)
337                         if ((*userfunc)((char*) prefix,
338                                         info+(j+1)*sizeof(Dict_char), client))
339                             return 1;
340                     break;
341                 }
342                 if (pos+j >= init_pos)
343                     range = mc->range;
344                 else
345                     range = 0;
346                 move (mc, Rj1, Rj0, ch, dfa, Rj_tmp, range);
347                 for (d = mc->n; --d >= 0; )
348                     if (Rj1[range*mc->n + d])
349                         break;
350                 if (d < 0)
351                     break;
352                 was_match = 0;
353                 for (d = mc->n; --d >= 0; )
354                     if (Rj1[range*mc->n + d] & mc->match_mask[d])
355                     {
356                         was_match = 1;
357                         break;
358                     }
359             }
360         }
361         else
362         {
363             MatchWord *Rj1 =    Rj+  mc->fact;
364             MatchWord *Rj_tmp = Rj+2*mc->fact;
365             Dict_char ch;
366             int range;
367
368             /* Dict_ptr             subptr */
369             /* Dict_char            sub char */
370             /* unsigned char        length of information */
371             /* char *               information */
372             info = (char*)p - indxp[-lo];
373             memcpy (&ch, info+sizeof(Dict_ptr), sizeof(Dict_char));
374             prefix[pos] = ch;
375             
376             if (pos > *max_pos)
377                 *max_pos = pos;
378             if (pos >= init_pos)
379                 range = mc->range;
380             else
381                 range = 0;
382             move (mc, Rj1, Rj, ch, dfa, Rj_tmp, range);
383             for (d = mc->n; --d >= 0; )
384                 if (Rj1[range*mc->n + d])
385                     break;
386             if (d >= 0)
387             {
388                 Dict_ptr subptr;
389                 if (info[sizeof(Dict_ptr)+sizeof(Dict_char)])
390                 {
391                     for (d = mc->n; --d >= 0; )
392                         if (Rj1[range*mc->n + d] & mc->match_mask[d])
393                         {
394                             prefix[pos+1] = DICT_EOS;
395                             if ((*userfunc)((char*) prefix,
396                                             info+sizeof(Dict_ptr)+
397                                             sizeof(Dict_char), client))
398                                 return 1;
399                             break;
400                         }
401                 }
402                 memcpy (&subptr, info, sizeof(Dict_ptr));
403                 if (subptr)
404                 {
405                     if (dict_grep (dict, subptr, mc, Rj1, pos+1,
406                                    client, userfunc, prefix, dfa, max_pos,
407                                    init_pos))
408                         return 1;
409                     dict_bf_readp (dict->dbf, ptr, &p);
410                     indxp = (short*) ((char*) p+DICT_bsize(p)-sizeof(short));
411                 }
412             }
413         }
414         lo++;
415     }
416     return 0;
417 }
418
419 int dict_lookup_grep (Dict dict, const char *pattern, int range, void *client,
420                       int *max_pos, int init_pos,
421                       int (*userfunc)(char *name, const char *info,
422                                       void *client))
423 {
424     MatchWord *Rj;
425     Dict_char prefix[MAX_LENGTH+1];
426     const char *this_pattern = pattern;
427     MatchContext *mc;
428     struct DFA *dfa = dfa_init();
429     int i, d;
430
431 #if 0
432     debug_dfa_trav = 1;
433     debug_dfa_tran = 1;
434     debug_dfa_followpos = 1;
435     dfa_verbose = 1;
436 #endif
437
438     logf (LOG_DEBUG, "dict_lookup_grep range=%d", range);
439     for (i = 0; pattern[i]; i++)
440     {
441         logf (LOG_DEBUG, " %3d  %c", pattern[i],
442               (pattern[i] > ' ' && pattern[i] < 127) ? pattern[i] : '?');
443     }
444    
445     dfa_set_cmap (dfa, dict->grep_cmap_data, dict->grep_cmap);
446
447     i = dfa_parse (dfa, &this_pattern);
448     if (i || *this_pattern)
449     {
450         dfa_delete (&dfa);
451         return -1;
452     }
453     dfa_mkstate (dfa);
454
455     mc = mk_MatchContext (dfa, range);
456
457     Rj = xcalloc ((MAX_LENGTH+1) * mc->n, sizeof(*Rj));
458
459     set_bit (mc, Rj, 0, 0);
460     for (d = 1; d<=mc->range; d++)
461     {
462         int s;
463         memcpy (Rj + mc->n * d, Rj + mc->n * (d-1), mc->n * sizeof(*Rj));
464         for (s = 0; s<dfa->no_states; s++)
465         {
466             if (get_bit (mc, Rj, d-1, s))
467             {
468                 struct DFA_state *state = dfa->states[s];
469                 int i = state->tran_no;
470                 while (--i >= 0)
471                     set_bit (mc, Rj, d, state->trans[i].to);
472             }
473         }
474     }
475     *max_pos = 0;
476     if (dict->head.root)
477         i = dict_grep (dict, dict->head.root, mc, Rj, 0, client,
478                        userfunc, prefix,
479                        dfa, max_pos, init_pos);
480     else
481         i = 0;
482     logf (LOG_DEBUG, "max_pos = %d", *max_pos);
483     dfa_delete (&dfa);
484     xfree (Rj);
485     rm_MatchContext (&mc);
486     return i;
487 }
488
489 void dict_grep_cmap (Dict dict, void *vp,
490                      const char **(*cmap)(void *vp, const char **from, int len))
491 {
492     dict->grep_cmap = cmap;
493     dict->grep_cmap_data = vp;
494 }