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