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