42c57e674b7e7e703050d2698bdcf0836a138246
[idzebra-moved-to-github.git] / util / zebramap.c
1 /*
2  * Copyright (C) 1994-1999, Index Data 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: zebramap.c,v $
7  * Revision 1.23  2001-11-15 08:41:24  adam
8  * Fix for weight (bug introduced by previous commit).
9  *
10  * Revision 1.22  2001/11/14 22:06:27  adam
11  * Rank-weight may be controlled via query.
12  *
13  * Revision 1.21  2001/01/22 10:42:56  adam
14  * Added numerical sort.
15  *
16  * Revision 1.20  2000/03/02 14:35:19  adam
17  * Added structure year and date.
18  *
19  * Revision 1.19  1999/11/30 13:48:04  adam
20  * Improved installation. Updated for inclusion of YAZ header files.
21  *
22  * Revision 1.18  1999/10/15 08:27:46  adam
23  * Fixed replace handler. 8-bit fix.
24  *
25  * Revision 1.17  1999/09/08 12:13:21  adam
26  * Fixed minor bug "replace"-mappings. Removed some logging messages.
27  *
28  * Revision 1.16  1999/09/07 07:19:21  adam
29  * Work on character mapping. Implemented replace rules.
30  *
31  * Revision 1.15  1999/05/26 07:49:14  adam
32  * C++ compilation.
33  *
34  * Revision 1.14  1999/02/19 10:37:40  adam
35  * Minor fix.
36  *
37  * Revision 1.13  1999/02/18 15:01:04  adam
38  * Structure=key uses register type 0.
39  *
40  * Revision 1.12  1999/02/12 13:29:25  adam
41  * Implemented position-flag for registers.
42  *
43  * Revision 1.11  1998/10/13 20:09:19  adam
44  * Changed call to readconf_line.
45  *
46  * Revision 1.10  1998/06/23 15:33:37  adam
47  * Added feature to specify sort criteria in query (type 7 specifies
48  * sort flags).
49  *
50  * Revision 1.9  1998/04/02 14:35:30  adam
51  * First version of Zebra that works with compiled ASN.1.
52  *
53  * Revision 1.8  1998/03/05 08:42:44  adam
54  * Minor changes to zebramap data structures. Query mapping rules changed.
55  *
56  * Revision 1.7  1998/02/10 12:03:07  adam
57  * Implemented Sort.
58  *
59  * Revision 1.6  1998/01/29 13:36:01  adam
60  * Structure word-list, free-form-text and document-text all
61  * trigger ranked search.
62  *
63  * Revision 1.5  1997/11/19 10:22:14  adam
64  * Bug fix (introduced by previous commit).
65  *
66  * Revision 1.4  1997/11/18 10:05:08  adam
67  * Changed character map facility so that admin can specify character
68  * mapping files for each register type, w, p, etc.
69  *
70  * Revision 1.3  1997/11/17 15:35:26  adam
71  * Bug fix. Relation=relevance wasn't observed.
72  *
73  * Revision 1.2  1997/10/31 12:39:30  adam
74  * Changed log message.
75  *
76  * Revision 1.1  1997/10/27 14:33:06  adam
77  * Moved towards generic character mapping depending on "structure"
78  * field in abstract syntax file. Fixed a few memory leaks. Fixed
79  * bug with negative integers when doing searches with relational
80  * operators.
81  *
82  */
83
84 #include <assert.h>
85 #include <ctype.h>
86
87 #include <yaz/yaz-util.h>
88 #include <charmap.h>
89 #include <zebramap.h>
90
91 #define ZEBRA_MAP_TYPE_SORT  1
92 #define ZEBRA_MAP_TYPE_INDEX 2
93
94 #define ZEBRA_REPLACE_ANY  300
95
96 struct zm_token {
97     int *token_from;
98     char *token_to;
99     int token_min;
100     struct zm_token *next;
101 };
102
103 struct zebra_map {
104     unsigned reg_id;
105     int completeness;
106     int positioned;
107     int type;
108     union {
109         struct {
110             int dummy;
111         } index;
112         struct {
113             int entry_size;
114         } sort;
115     } u;
116     chrmaptab maptab;
117     const char *maptab_name;
118     struct zebra_map *next;
119     struct zm_token *replace_tokens;
120 };
121
122 struct zebra_maps {
123     char *tabpath;
124     NMEM nmem;
125     struct zebra_map *map_list;
126     char temp_map_str[2];
127     const char *temp_map_ptr[2];
128     struct zebra_map **lookup_array;
129     WRBUF wrbuf_1, wrbuf_2;
130 };
131
132 void zebra_maps_close (ZebraMaps zms)
133 {
134     struct zebra_map *zm = zms->map_list;
135     while (zm)
136     {
137         if (zm->maptab)
138             chrmaptab_destroy (zm->maptab);
139         zm = zm->next;
140     }
141     wrbuf_free (zms->wrbuf_1, 1);
142     wrbuf_free (zms->wrbuf_2, 1);
143     nmem_destroy (zms->nmem);
144     xfree (zms);
145 }
146
147 static void zebra_map_read (ZebraMaps zms, const char *name)
148 {
149     FILE *f;
150     char line[512];
151     char *argv[10];
152     int argc;
153     int lineno = 0;
154     struct zebra_map **zm = 0, *zp;
155
156     if (!(f = yaz_path_fopen(zms->tabpath, name, "r")))
157     {
158         logf(LOG_WARN|LOG_ERRNO, "%s", name);
159         return ;
160     }
161     while ((argc = readconf_line(f, &lineno, line, 512, argv, 10)))
162     {
163         if (!yaz_matchstr (argv[0], "index") && argc == 2)
164         {
165             if (!zm)
166                 zm = &zms->map_list;
167             else
168                 zm = &(*zm)->next;
169             *zm = (struct zebra_map *) nmem_malloc (zms->nmem, sizeof(**zm));
170             (*zm)->reg_id = argv[1][0];
171             (*zm)->maptab_name = NULL;
172             (*zm)->maptab = NULL;
173             (*zm)->type = ZEBRA_MAP_TYPE_INDEX;
174             (*zm)->completeness = 0;
175             (*zm)->positioned = 1;
176             (*zm)->replace_tokens = 0;
177         }
178         else if (!yaz_matchstr (argv[0], "sort") && argc == 2)
179         {
180             if (!zm)
181                 zm = &zms->map_list;
182             else
183                 zm = &(*zm)->next;
184             *zm = (struct zebra_map *) nmem_malloc (zms->nmem, sizeof(**zm));
185             (*zm)->reg_id = argv[1][0];
186             (*zm)->maptab_name = NULL;
187             (*zm)->type = ZEBRA_MAP_TYPE_SORT;
188             (*zm)->u.sort.entry_size = 80;
189             (*zm)->maptab = NULL;
190             (*zm)->completeness = 0;
191             (*zm)->positioned = 0;
192             (*zm)->replace_tokens = 0;
193         }
194         else if (zm && !yaz_matchstr (argv[0], "charmap") && argc == 2)
195         {
196             (*zm)->maptab_name = nmem_strdup (zms->nmem, argv[1]);
197         }
198         else if (zm && !yaz_matchstr (argv[0], "completeness") && argc == 2)
199         {
200             (*zm)->completeness = atoi (argv[1]);
201         }
202         else if (zm && !yaz_matchstr (argv[0], "position") && argc == 2)
203         {
204             (*zm)->positioned = atoi (argv[1]);
205         }
206         else if (zm && !yaz_matchstr (argv[0], "entrysize") && argc == 2)
207         {
208             if ((*zm)->type == ZEBRA_MAP_TYPE_SORT)
209                 (*zm)->u.sort.entry_size = atoi (argv[1]);
210         }
211         else if (zm && !yaz_matchstr (argv[0], "replace") && argc >= 2)
212         {
213             struct zm_token *token = nmem_malloc (zms->nmem, sizeof(*token));
214             token->next = (*zm)->replace_tokens;
215             (*zm)->replace_tokens = token;
216 #if 0
217             logf (LOG_LOG, "replace %s", argv[1]);
218 #endif
219             token->token_from = 0;
220             if (argc >= 2)
221             {
222                 char *cp = argv[1];
223                 int *dp = token->token_from = (int *)
224                     nmem_malloc (zms->nmem, (1+strlen(cp))*sizeof(int));
225                 while (*cp)
226                     if (*cp == '$')
227                     {
228                         *dp++ = ' ';
229                         cp++;
230                     }
231                     else if (*cp == '.')
232                     {
233                         *dp++ = ZEBRA_REPLACE_ANY;
234                         cp++;
235                     }
236                     else
237                     {
238                         *dp++ = zebra_prim(&cp);
239 #if 0
240                         logf (LOG_LOG, "  char %2X %c", dp[-1], dp[-1]);
241 #endif
242                     }
243                 *dp = '\0';
244             }
245             if (argc >= 3)
246             {
247                 char *cp = argv[2];
248                 char *dp = token->token_to =
249                     nmem_malloc (zms->nmem, strlen(cp)+1);
250                 while (*cp)
251                     if (*cp == '$')
252                     {
253                         *dp++ = ' ';
254                         cp++;
255                     }
256                     else
257                         *dp++ = zebra_prim(&cp);
258                 *dp = '\0';
259             }
260             else
261                 token->token_to = 0;
262         }
263     }
264     if (zm)
265         (*zm)->next = NULL;
266     fclose (f);
267
268     for (zp = zms->map_list; zp; zp = zp->next)
269         zms->lookup_array[zp->reg_id] = zp;
270 }
271
272 static void zms_map_handle (void *p, const char *name, const char *value)
273 {
274     ZebraMaps zms = (ZebraMaps) p;
275     
276     zebra_map_read (zms, value);
277 }
278
279 ZebraMaps zebra_maps_open (Res res)
280 {
281     ZebraMaps zms = (ZebraMaps) xmalloc (sizeof(*zms));
282     int i;
283
284     zms->nmem = nmem_create ();
285     zms->tabpath = nmem_strdup (zms->nmem,
286                                 res_get_def (res, "profilePath", "."));
287     zms->map_list = NULL;
288
289     zms->temp_map_str[0] = '\0';
290     zms->temp_map_str[1] = '\0';
291
292     zms->temp_map_ptr[0] = zms->temp_map_str;
293     zms->temp_map_ptr[1] = NULL;
294
295     zms->lookup_array = (struct zebra_map**)
296         nmem_malloc (zms->nmem, sizeof(*zms->lookup_array)*256);
297     for (i = 0; i<256; i++)
298         zms->lookup_array[i] = 0;
299     if (!res || !res_trav (res, "index", zms, zms_map_handle))
300         zebra_map_read (zms, "default.idx");
301
302     zms->wrbuf_1 = wrbuf_alloc();
303     zms->wrbuf_2 = wrbuf_alloc();
304     return zms;
305 }
306
307 struct zebra_map *zebra_map_get (ZebraMaps zms, unsigned reg_id)
308 {
309     return zms->lookup_array[reg_id];
310 }
311
312 chrmaptab zebra_charmap_get (ZebraMaps zms, unsigned reg_id)
313 {
314     struct zebra_map *zm = zebra_map_get (zms, reg_id);
315     if (!zm)
316     {
317         zm = (struct zebra_map *) nmem_malloc (zms->nmem, sizeof(*zm));
318         logf (LOG_WARN, "Unknown register type: %c", reg_id);
319
320         zm->reg_id = reg_id;
321         zm->maptab_name = nmem_strdup (zms->nmem, "@");
322         zm->maptab = NULL;
323         zm->type = ZEBRA_MAP_TYPE_INDEX;
324         zm->completeness = 0;
325         zm->next = zms->map_list;
326         zms->map_list = zm->next;
327
328         zms->lookup_array[zm->reg_id & 255] = zm;
329     }
330     if (!zm->maptab)
331     {
332         if (!zm->maptab_name || !yaz_matchstr (zm->maptab_name, "@"))
333             return NULL;
334         if (!(zm->maptab = chrmaptab_create (zms->tabpath,
335                                              zm->maptab_name, 0)))
336             logf(LOG_WARN, "Failed to read character table %s",
337                  zm->maptab_name);
338         else
339             logf(LOG_DEBUG, "Read character table %s", zm->maptab_name);
340     }
341     return zm->maptab;
342 }
343
344 const char **zebra_maps_input (ZebraMaps zms, unsigned reg_id,
345                                const char **from, int len)
346 {
347     chrmaptab maptab;
348
349     maptab = zebra_charmap_get (zms, reg_id);
350     if (maptab)
351         return chr_map_input(maptab, from, len);
352     
353     zms->temp_map_str[0] = **from;
354
355     (*from)++;
356     return zms->temp_map_ptr;
357 }
358
359 #if 0
360 int zebra_maps_input_tokens (ZebraMaps zms, unsigned reg_id,
361                              const char *input_str, int input_len,
362                              WRBUF wrbuf)
363 {
364     chrmaptab maptab = zebra_charmap_get (zms, reg_id);
365     int len[4];
366     char *str[3];
367     int input_i = 0;
368     int first = 1;
369     const char **out;
370         
371     if (!maptab)
372     {
373         wrbuf_write (wrbuf, input_str, input_len);
374         return -1;
375     }
376     str[0] = " ";
377     len[0] = 1;
378     str[1] = input_str;
379     len[1] = input_len;
380     str[2] = " ";
381     len[2] = 1;
382     len[3] = -1;
383     
384     out = chr_map_input (maptab, str, len);
385     while (len[1] > 0)
386     {
387         while (out && *out && **out == *CHR_SPACE)
388             out = chr_map_input (maptab, str, len);
389     }
390 }
391 #endif
392
393 const char *zebra_maps_output(ZebraMaps zms, unsigned reg_id,
394                               const char **from)
395 {
396     chrmaptab maptab = zebra_charmap_get (zms, reg_id);
397     if (!maptab)
398         return 0;
399     return chr_map_output (maptab, from, 1);
400 }
401
402
403 /* ------------------------------------ */
404
405 typedef struct {
406     int type;
407     int major;
408     int minor;
409     Z_AttributeElement **attributeList;
410     int num_attributes;
411 } AttrType;
412
413 static int attr_find (AttrType *src, oid_value *attributeSetP)
414 {
415     while (src->major < src->num_attributes)
416     {
417         Z_AttributeElement *element;
418
419         element = src->attributeList[src->major];
420         if (src->type == *element->attributeType)
421         {
422             switch (element->which) 
423             {
424             case Z_AttributeValue_numeric:
425                 ++(src->major);
426                 if (element->attributeSet && attributeSetP)
427                 {
428                     oident *attrset;
429
430                     attrset = oid_getentbyoid (element->attributeSet);
431                     *attributeSetP = attrset->value;
432                 }
433                 return *element->value.numeric;
434                 break;
435             case Z_AttributeValue_complex:
436                 if (src->minor >= element->value.complex->num_list ||
437                     element->value.complex->list[src->minor]->which !=  
438                     Z_StringOrNumeric_numeric)
439                     break;
440                 ++(src->minor);
441                 if (element->attributeSet && attributeSetP)
442                 {
443                     oident *attrset;
444
445                     attrset = oid_getentbyoid (element->attributeSet);
446                     *attributeSetP = attrset->value;
447                 }
448                 return *element->value.complex->list[src->minor-1]->u.numeric;
449             default:
450                 assert (0);
451             }
452         }
453         ++(src->major);
454     }
455     return -1;
456 }
457
458 static void attr_init_APT (AttrType *src, Z_AttributesPlusTerm *zapt, int type)
459 {
460 #ifdef ASN_COMPILED
461     src->attributeList = zapt->attributes->attributes;
462     src->num_attributes = zapt->attributes->num_attributes;
463 #else
464     src->attributeList = zapt->attributeList;
465     src->num_attributes = zapt->num_attributes;
466 #endif
467     src->type = type;
468     src->major = 0;
469     src->minor = 0;
470 }
471
472 static void attr_init_AttrList (AttrType *src, Z_AttributeList *list, int type)
473 {
474     src->attributeList = list->attributes;
475     src->num_attributes = list->num_attributes;
476     src->type = type;
477     src->major = 0;
478     src->minor = 0;
479 }
480
481 /* ------------------------------------ */
482
483 int zebra_maps_is_complete (ZebraMaps zms, unsigned reg_id)
484
485     struct zebra_map *zm = zebra_map_get (zms, reg_id);
486     if (zm)
487         return zm->completeness;
488     return 0;
489 }
490
491 int zebra_maps_is_positioned (ZebraMaps zms, unsigned reg_id)
492 {
493     struct zebra_map *zm = zebra_map_get (zms, reg_id);
494     if (zm)
495         return zm->positioned;
496     return 0;
497 }
498     
499 int zebra_maps_is_sort (ZebraMaps zms, unsigned reg_id)
500 {
501     struct zebra_map *zm = zebra_map_get (zms, reg_id);
502     if (zm)
503         return zm->type == ZEBRA_MAP_TYPE_SORT;
504     return 0;
505 }
506
507 int zebra_maps_sort (ZebraMaps zms, Z_SortAttributes *sortAttributes,
508                      int *numerical)
509 {
510     AttrType use;
511     AttrType structure;
512     int structure_value;
513     attr_init_AttrList (&use, sortAttributes->list, 1);
514     attr_init_AttrList (&structure, sortAttributes->list, 4);
515
516     *numerical = 0;
517     structure_value = attr_find (&structure, 0);
518     if (structure_value == 109)
519         *numerical = 1;
520     return attr_find (&use, NULL);
521 }
522
523 int zebra_maps_attr (ZebraMaps zms, Z_AttributesPlusTerm *zapt,
524                      unsigned *reg_id, char **search_type, char *rank_type,
525                      int *complete_flag, int *sort_flag)
526 {
527     AttrType completeness;
528     AttrType structure;
529     AttrType relation;
530     AttrType sort_relation;
531     AttrType weight;
532     int completeness_value;
533     int structure_value;
534     int relation_value;
535     int sort_relation_value;
536     int weight_value;
537
538     attr_init_APT (&structure, zapt, 4);
539     attr_init_APT (&completeness, zapt, 6);
540     attr_init_APT (&relation, zapt, 2);
541     attr_init_APT (&sort_relation, zapt, 7);
542     attr_init_APT (&weight, zapt, 9);
543
544     completeness_value = attr_find (&completeness, NULL);
545     structure_value = attr_find (&structure, NULL);
546     relation_value = attr_find (&relation, NULL);
547     sort_relation_value = attr_find (&sort_relation, NULL);
548     weight_value = attr_find (&weight, NULL);
549
550     if (completeness_value == 2 || completeness_value == 3)
551         *complete_flag = 1;
552     else
553         *complete_flag = 0;
554     *reg_id = 0;
555
556     *sort_flag = (sort_relation_value > 0) ? 1 : 0;
557     *search_type = "phrase";
558     strcpy (rank_type, "void");
559     if (relation_value == 102)
560     {
561         if (weight_value == -1)
562             weight_value = 34;
563         sprintf (rank_type, "rank,%d", weight_value);
564     }    
565     if (*complete_flag)
566         *reg_id = 'p';
567     else
568         *reg_id = 'w';
569     switch (structure_value)
570     {
571     case 6:   /* word list */
572         *search_type = "and-list";
573         break;
574     case 105: /* free-form-text */
575         *search_type = "or-list";
576         break;
577     case 106: /* document-text */
578         *search_type = "or-list";
579         break;  
580     case -1:
581     case 1:   /* phrase */
582     case 2:   /* word */
583     case 108: /* string */ 
584         *search_type = "phrase";
585         break;
586     case 107: /* local-number */
587         *search_type = "local";
588         *reg_id = 0;
589         break;
590     case 109: /* numeric string */
591         *reg_id = 'n';
592         *search_type = "numeric";
593         break;
594     case 104: /* urx */
595         *reg_id = 'u';
596         *search_type = "phrase";
597         break;
598     case 3:   /* key */
599         *reg_id = '0';
600         *search_type = "phrase";
601         break;
602     case 4:  /* year */
603         *reg_id = 'y';
604         *search_type = "phrase";
605         break;
606     case 5:  /* date */
607         *reg_id = 'd';
608         *search_type = "phrase";
609         break;
610     default:
611         return -1;
612     }
613     return 0;
614 }
615
616 int zebra_replace_sub(ZebraMaps zms, unsigned reg_id, const char *ex_list,
617                       const char *input_str, int input_len, WRBUF wrbuf);
618
619 WRBUF zebra_replace(ZebraMaps zms, unsigned reg_id, const char *ex_list,
620                     const char *input_str, int input_len)
621 {
622     struct zebra_map *zm = zebra_map_get (zms, reg_id);
623
624     wrbuf_rewind(zms->wrbuf_1);
625     wrbuf_write(zms->wrbuf_1, input_str, input_len);
626     if (!zm || !zm->replace_tokens)
627         return zms->wrbuf_1;
628   
629 #if 0
630     logf (LOG_LOG, "in:%.*s:", wrbuf_len(zms->wrbuf_1),
631           wrbuf_buf(zms->wrbuf_1));
632 #endif
633     for (;;)
634     {
635         if (!zebra_replace_sub(zms, reg_id, ex_list, wrbuf_buf(zms->wrbuf_1),
636                                wrbuf_len(zms->wrbuf_1), zms->wrbuf_2))
637             return zms->wrbuf_2;
638         if (!zebra_replace_sub(zms, reg_id, ex_list, wrbuf_buf(zms->wrbuf_2),
639                                wrbuf_len(zms->wrbuf_2), zms->wrbuf_1))
640             return zms->wrbuf_1;
641     }
642     return 0;
643 }
644
645 int zebra_replace_sub(ZebraMaps zms, unsigned reg_id, const char *ex_list,
646                       const char *input_str, int input_len, WRBUF wrbuf)
647 {
648     int i = -1;
649     int no_replaces = 0;
650     struct zebra_map *zm = zebra_map_get (zms, reg_id);
651
652     wrbuf_rewind(wrbuf);
653     for (i = -1; i <= input_len; )
654     {
655         struct zm_token *token;
656         char replace_string[128];
657         int replace_out;
658         int replace_in = 0;
659
660         for (token = zm->replace_tokens; !replace_in && token;
661              token = token->next)
662         {
663             int j = 0;
664             int replace_done = 0;
665             replace_out = 0;
666             for (;; j++)
667             {
668                 int c;
669                 if (!token->token_from[j])
670                 {
671                     replace_in = j;
672                     break;
673                 }
674                 if (ex_list && strchr (ex_list, token->token_from[j]))
675                     break;
676                 if (i+j < 0 || j+i >= input_len)
677                     c = ' ';
678                 else
679                     c = input_str[j+i] & 255;
680                 if (token->token_from[j] == ZEBRA_REPLACE_ANY)
681                 {
682                     if (c == ' ')
683                         break;
684                     replace_string[replace_out++] = c;
685                 }
686                 else
687                 {
688                     if (c != token->token_from[j])
689                     {
690                         break;
691                     }
692                     if (!replace_done)
693                     {
694                         const char *cp = token->token_to;
695                         replace_done = 1;
696                         for (; cp && *cp; cp++)
697                             replace_string[replace_out++] = *cp;
698                     }
699                 }
700             }
701         }
702         if (!replace_in)
703         {
704             if (i >= 0 && i < input_len)
705                 wrbuf_putc(wrbuf, input_str[i]);
706             i++;
707         }
708         else
709         {
710             no_replaces++;
711             if (replace_out)
712                 wrbuf_write(wrbuf, replace_string, replace_out);
713             i += replace_in;
714         }
715     }
716 #if 0
717     logf (LOG_LOG, "out:%.*s:", wrbuf_len(wrbuf), wrbuf_buf(wrbuf));
718 #endif
719     return no_replaces;
720 }