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