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