RPM: store libs in %{_libdir}
[idzebra-moved-to-github.git] / util / zebramap.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 1994-2011 Index Data
3
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26
27 #include <charmap.h>
28 #include <attrfind.h>
29 #include <yaz/yaz-util.h>
30
31 #if YAZ_HAVE_ICU
32 #include <yaz/icu.h>
33 #endif
34 #include <zebramap.h>
35
36 #define ZEBRA_MAP_TYPE_SORT  1
37 #define ZEBRA_MAP_TYPE_INDEX 2
38 #define ZEBRA_MAP_TYPE_STATICRANK 3
39
40 #define ZEBRA_REPLACE_ANY  300
41
42 struct zebra_map {
43     const char *id;
44     int completeness;
45     int positioned;
46     int alwaysmatches;
47     int first_in_field;
48     int type;
49     int use_chain;
50     int debug;
51     union {
52         struct {
53             int entry_size;
54         } sort;
55     } u;
56     chrmaptab maptab;
57     const char *maptab_name;
58     zebra_maps_t zebra_maps;
59 #if YAZ_HAVE_XML2
60     xmlDocPtr doc;
61 #endif
62 #if YAZ_HAVE_ICU
63     struct icu_chain *icu_chain;
64 #endif
65     WRBUF input_str;
66     WRBUF print_str;
67     size_t simple_off;
68     struct zebra_map *next;
69 };
70
71 struct zebra_maps_s {
72     char *tabpath;
73     char *tabroot;
74     NMEM nmem;
75     char temp_map_str[2];
76     const char *temp_map_ptr[2];
77     WRBUF wrbuf_1;
78     int no_files_read;
79     zebra_map_t map_list;
80     zebra_map_t last_map;
81 };
82
83 void zebra_maps_close(zebra_maps_t zms)
84 {
85     struct zebra_map *zm = zms->map_list;
86     while (zm)
87     {
88         if (zm->maptab)
89             chrmaptab_destroy(zm->maptab);
90 #if YAZ_HAVE_ICU
91         if (zm->icu_chain)
92             icu_chain_destroy(zm->icu_chain);
93 #endif
94 #if YAZ_HAVE_XML2
95         xmlFreeDoc(zm->doc);
96 #endif
97         wrbuf_destroy(zm->input_str);
98         wrbuf_destroy(zm->print_str);
99         zm = zm->next;
100     }
101     wrbuf_destroy(zms->wrbuf_1);
102     nmem_destroy(zms->nmem);
103     xfree(zms);
104 }
105
106 zebra_map_t zebra_add_map(zebra_maps_t zms, const char *index_type,
107                           int map_type)
108 {
109     zebra_map_t zm = (zebra_map_t) nmem_malloc(zms->nmem, sizeof(*zm));
110
111     zm->zebra_maps = zms;
112     zm->id = nmem_strdup(zms->nmem, index_type);
113     zm->maptab_name = 0;
114     zm->use_chain = 0;
115     zm->debug = 0;
116     zm->maptab = 0;
117     zm->type = map_type;
118     zm->completeness = 0;
119     zm->positioned = 0;
120     zm->alwaysmatches = 0;
121     zm->first_in_field = 0;
122
123     if (zms->last_map)
124         zms->last_map->next = zm;
125     else
126         zms->map_list = zm;
127     zms->last_map = zm;
128     zm->next = 0;
129 #if YAZ_HAVE_ICU
130     zm->icu_chain = 0;
131 #endif
132 #if YAZ_HAVE_XML2
133     zm->doc = 0;
134 #endif
135     zm->input_str = wrbuf_alloc();
136     zm->print_str = wrbuf_alloc();
137     return zm;
138 }
139
140 static int parse_command(zebra_maps_t zms, int argc, char **argv,
141                          const char *fname, int lineno)
142 {
143     zebra_map_t zm = zms->last_map;
144     if (argc == 1)
145     {
146         yaz_log(YLOG_WARN, "%s:%d: Missing arguments for '%s'",
147                 fname, lineno, argv[0]);
148         return -1;
149     }
150     if (argc > 2)
151     {
152         yaz_log(YLOG_WARN, "%s:%d: Too many arguments for '%s'",
153                 fname, lineno, argv[0]);
154         return -1;
155     }
156     if (!yaz_matchstr(argv[0], "index"))
157     {
158         zm = zebra_add_map(zms, argv[1], ZEBRA_MAP_TYPE_INDEX);
159         zm->positioned = 1;
160     }
161     else if (!yaz_matchstr(argv[0], "sort"))
162     {
163         zm = zebra_add_map(zms, argv[1], ZEBRA_MAP_TYPE_SORT);
164         zm->u.sort.entry_size = 80;
165     }
166     else if (!yaz_matchstr(argv[0], "staticrank"))
167     {
168         zm = zebra_add_map(zms, argv[1], ZEBRA_MAP_TYPE_STATICRANK);
169         zm->completeness = 1;
170     }
171     else if (!zm)
172     {
173         yaz_log(YLOG_WARN, "%s:%d: Missing sort/index before '%s'",  
174                 fname, lineno, argv[0]);
175         return -1;
176     }
177     else if (!yaz_matchstr(argv[0], "charmap") && argc == 2)
178     {
179         if (zm->type != ZEBRA_MAP_TYPE_STATICRANK)
180             zm->maptab_name = nmem_strdup(zms->nmem, argv[1]);
181         else
182         {
183             yaz_log(YLOG_WARN|YLOG_FATAL, "%s:%d: charmap for "
184                     "staticrank is invalid", fname, lineno);
185             yaz_log(YLOG_LOG, "Type is %d", zm->type);
186             return -1;
187         }
188     }
189     else if (!yaz_matchstr(argv[0], "completeness") && argc == 2)
190     {
191         zm->completeness = atoi(argv[1]);
192     }
193     else if (!yaz_matchstr(argv[0], "position") && argc == 2)
194     {
195         zm->positioned = atoi(argv[1]);
196     }
197     else if (!yaz_matchstr(argv[0], "alwaysmatches") && argc == 2)
198     {
199         if (zm->type != ZEBRA_MAP_TYPE_STATICRANK)
200             zm->alwaysmatches = atoi(argv[1]);
201         else
202         {
203             yaz_log(YLOG_WARN|YLOG_FATAL, "%s:%d: alwaysmatches for "
204                     "staticrank is invalid", fname, lineno);
205             return -1;
206         }
207     }
208     else if (!yaz_matchstr(argv[0], "firstinfield") && argc == 2)
209     {
210         zm->first_in_field = atoi(argv[1]);
211     }
212     else if (!yaz_matchstr(argv[0], "entrysize") && argc == 2)
213     {
214         if (zm->type == ZEBRA_MAP_TYPE_SORT)
215             zm->u.sort.entry_size = atoi(argv[1]);
216         else
217         {
218             yaz_log(YLOG_WARN, 
219                     "%s:%d: entrysize only valid in sort section",  
220                     fname, lineno);
221             return -1;
222         }
223     }
224     else if (!yaz_matchstr(argv[0], "simplechain"))
225     {
226         zm->use_chain = 1;
227 #if YAZ_HAVE_ICU
228         zm->icu_chain = 0;
229 #endif
230     }
231     else if (!yaz_matchstr(argv[0], "icuchain"))
232     {
233         char full_path[1024];
234         if (!yaz_filepath_resolve(argv[1], zms->tabpath, zms->tabroot,
235                                   full_path))
236         {
237             yaz_log(YLOG_WARN, "%s:%d: Could not locate icuchain config '%s'",
238                     fname, lineno, argv[1]);
239             return -1;
240         }
241 #if YAZ_HAVE_XML2
242         zm->doc = xmlParseFile(full_path);
243         if (!zm->doc)
244         {
245             yaz_log(YLOG_WARN, "%s:%d: Could not load icuchain config '%s'",
246                     fname, lineno, argv[1]);
247             return -1;
248         }
249         else
250         {
251 #if YAZ_HAVE_ICU
252             UErrorCode status;
253             xmlNode *xml_node = xmlDocGetRootElement(zm->doc);
254             zm->icu_chain = 
255                 icu_chain_xml_config(xml_node,
256 /* not sure about sort for this function yet.. */
257 #if 1
258                                      1,
259 #else
260                                      zm->type == ZEBRA_MAP_TYPE_SORT,
261 #endif                                    
262                                      &status);
263             if (!zm->icu_chain)
264             {
265                 yaz_log(YLOG_WARN, "%s:%d: Failed to load ICU chain %s",
266                         fname, lineno, argv[1]);
267             }
268             zm->use_chain = 1;
269 #else
270             yaz_log(YLOG_WARN, "%s:%d: ICU support unavailable",
271                     fname, lineno);
272             return -1;
273 #endif
274         }
275 #else
276         yaz_log(YLOG_WARN, "%s:%d: XML support unavailable",
277                 fname, lineno);
278         return -1;
279 #endif
280     }
281     else if (!yaz_matchstr(argv[0], "debug") && argc == 2)
282     {
283         zm->debug = atoi(argv[1]);
284     }
285     else
286     {
287         yaz_log(YLOG_WARN, "%s:%d: Unrecognized directive '%s'",  
288                 fname, lineno, argv[0]);
289         return -1;
290     }
291     return 0;
292 }
293
294 ZEBRA_RES zebra_maps_read_file(zebra_maps_t zms, const char *fname)
295 {
296     FILE *f;
297     char line[512];
298     char *argv[10];
299     int argc;
300     int lineno = 0;
301     int failures = 0;
302
303     if (!(f = yaz_fopen(zms->tabpath, fname, "r", zms->tabroot)))
304     {
305         yaz_log(YLOG_ERRNO|YLOG_FATAL, "%s", fname);
306         return ZEBRA_FAIL;
307     }
308     while ((argc = readconf_line(f, &lineno, line, 512, argv, 10)))
309     {
310         int r = parse_command(zms, argc, argv, fname, lineno);
311         if (r)
312             failures++;
313     }
314     yaz_fclose(f);
315
316     if (failures)
317         return ZEBRA_FAIL;
318
319     (zms->no_files_read)++;
320     return ZEBRA_OK;
321 }
322
323 zebra_maps_t zebra_maps_open(Res res, const char *base_path,
324                              const char *profile_path)
325 {
326     zebra_maps_t zms = (zebra_maps_t) xmalloc(sizeof(*zms));
327
328     zms->nmem = nmem_create();
329     zms->tabpath = profile_path ? nmem_strdup(zms->nmem, profile_path) : 0;
330     zms->tabroot = 0;
331     if (base_path)
332         zms->tabroot = nmem_strdup(zms->nmem, base_path);
333     zms->map_list = 0;
334     zms->last_map = 0;
335
336     zms->temp_map_str[0] = '\0';
337     zms->temp_map_str[1] = '\0';
338
339     zms->temp_map_ptr[0] = zms->temp_map_str;
340     zms->temp_map_ptr[1] = NULL;
341
342     zms->wrbuf_1 = wrbuf_alloc();
343
344     zms->no_files_read = 0;
345     return zms;
346 }
347
348 void zebra_maps_define_default_sort(zebra_maps_t zms)
349 {
350     zebra_map_t zm = zebra_add_map(zms, "s", ZEBRA_MAP_TYPE_SORT);
351     zm->u.sort.entry_size = 80;
352 }
353
354 zebra_map_t zebra_map_get(zebra_maps_t zms, const char *id)
355 {
356     zebra_map_t zm;
357     for (zm = zms->map_list; zm; zm = zm->next)
358         if (!strcmp(zm->id, id))
359             break;
360     return zm;
361 }
362
363 zebra_map_t zebra_map_get_or_add(zebra_maps_t zms, const char *id)
364 {
365     struct zebra_map *zm = zebra_map_get(zms, id);
366     if (!zm)
367     {
368         zm = zebra_add_map(zms, id, ZEBRA_MAP_TYPE_INDEX);
369         
370         /* no reason to warn if no maps are read from file */
371         if (zms->no_files_read)
372             yaz_log(YLOG_WARN, "Unknown register type: %s", id);
373
374         zm->maptab_name = nmem_strdup(zms->nmem, "@");
375         zm->completeness = 0;
376         zm->positioned = 1;
377     }
378     return zm;
379 }
380
381 chrmaptab zebra_charmap_get(zebra_map_t zm)
382 {
383     if (!zm->maptab)
384     {
385         if (!zm->maptab_name || !yaz_matchstr(zm->maptab_name, "@"))
386             return NULL;
387         if (!(zm->maptab = chrmaptab_create(zm->zebra_maps->tabpath,
388                                             zm->maptab_name,
389                                             zm->zebra_maps->tabroot)))
390             yaz_log(YLOG_WARN, "Failed to read character table %s",
391                     zm->maptab_name);
392         else
393             yaz_log(YLOG_DEBUG, "Read character table %s", zm->maptab_name);
394     }
395     return zm->maptab;
396 }
397
398 const char **zebra_maps_input(zebra_map_t zm,
399                               const char **from, int len, int first)
400 {
401     chrmaptab maptab = zebra_charmap_get(zm);
402     if (maptab)
403         return chr_map_input(maptab, from, len, first);
404     
405     zm->zebra_maps->temp_map_str[0] = **from;
406
407     (*from)++;
408     return zm->zebra_maps->temp_map_ptr;
409 }
410
411 const char **zebra_maps_search(zebra_map_t zm,
412                                const char **from, int len,  int *q_map_match)
413 {
414     chrmaptab maptab;
415     
416     *q_map_match = 0;
417     maptab = zebra_charmap_get(zm);
418     if (maptab)
419     {
420         const char **map;
421         map = chr_map_q_input(maptab, from, len, 0);
422         if (map && map[0])
423         {
424             *q_map_match = 1;
425             return map;
426         }
427         map = chr_map_input(maptab, from, len, 0);
428         if (map)
429             return map;
430     }
431     zm->zebra_maps->temp_map_str[0] = **from;
432
433     (*from)++;
434     return zm->zebra_maps->temp_map_ptr;
435 }
436
437 const char *zebra_maps_output(zebra_map_t zm,
438                               const char **from)
439 {
440     chrmaptab maptab = zebra_charmap_get(zm);
441     if (!maptab)
442         return 0;
443     return chr_map_output(maptab, from, 1);
444 }
445
446
447 /* ------------------------------------ */
448
449 int zebra_maps_is_complete(zebra_map_t zm)
450
451     if (zm)
452         return zm->completeness;
453     return 0;
454 }
455
456 int zebra_maps_is_positioned(zebra_map_t zm)
457 {
458     if (zm)
459         return zm->positioned;
460     return 0;
461 }
462
463 int zebra_maps_is_index(zebra_map_t zm)
464 {
465     if (zm)
466         return zm->type == ZEBRA_MAP_TYPE_INDEX;
467     return 0;
468 }
469
470 int zebra_maps_is_staticrank(zebra_map_t zm)
471 {
472     if (zm)
473         return zm->type == ZEBRA_MAP_TYPE_STATICRANK;
474     return 0;
475 }
476     
477 int zebra_maps_is_sort(zebra_map_t zm)
478 {
479     if (zm)
480         return zm->type == ZEBRA_MAP_TYPE_SORT;
481     return 0;
482 }
483
484 int zebra_maps_is_alwaysmatches(zebra_map_t zm)
485 {
486     if (zm)
487         return zm->alwaysmatches;
488     return 0;
489 }
490
491 int zebra_maps_is_first_in_field(zebra_map_t zm)
492 {
493     if (zm)
494         return zm->first_in_field;
495     return 0;
496 }
497
498 int zebra_maps_sort(zebra_maps_t zms, Z_SortAttributes *sortAttributes,
499                     int *numerical)
500 {
501     AttrType use;
502     AttrType structure;
503     int structure_value;
504     attr_init_AttrList(&use, sortAttributes->list, 1);
505     attr_init_AttrList(&structure, sortAttributes->list, 4);
506
507     *numerical = 0;
508     structure_value = attr_find(&structure, 0);
509     if (structure_value == 109)
510         *numerical = 1;
511     return attr_find(&use, NULL);
512 }
513
514 int zebra_maps_attr(zebra_maps_t zms, Z_AttributesPlusTerm *zapt,
515                     const char **index_type, char **search_type, char *rank_type,
516                     int *complete_flag, int *sort_flag)
517 {
518     AttrType completeness;
519     AttrType structure;
520     AttrType relation;
521     AttrType sort_relation;
522     AttrType weight;
523     AttrType use;
524     int completeness_value;
525     int structure_value;
526     const char *structure_str = 0;
527     int relation_value;
528     int sort_relation_value;
529     int weight_value;
530     int use_value;
531
532     attr_init_APT(&structure, zapt, 4);
533     attr_init_APT(&completeness, zapt, 6);
534     attr_init_APT(&relation, zapt, 2);
535     attr_init_APT(&sort_relation, zapt, 7);
536     attr_init_APT(&weight, zapt, 9);
537     attr_init_APT(&use, zapt, 1);
538
539     completeness_value = attr_find(&completeness, NULL);
540     structure_value = attr_find_ex(&structure, NULL, &structure_str);
541     relation_value = attr_find(&relation, NULL);
542     sort_relation_value = attr_find(&sort_relation, NULL);
543     weight_value = attr_find(&weight, NULL);
544     use_value = attr_find(&use, NULL);
545
546     if (completeness_value == 2 || completeness_value == 3)
547         *complete_flag = 1;
548     else
549         *complete_flag = 0;
550     *index_type = 0;
551
552     *sort_flag =(sort_relation_value > 0) ? 1 : 0;
553     *search_type = "phrase";
554     strcpy(rank_type, "void");
555     if (relation_value == 102)
556     {
557         if (weight_value == -1)
558             weight_value = 34;
559         sprintf(rank_type, "rank,w=%d,u=%d", weight_value, use_value);
560     }
561     if (*complete_flag)
562         *index_type = "p";
563     else
564         *index_type = "w";
565     switch (structure_value)
566     {
567     case 6:   /* word list */
568         *search_type = "and-list";
569         break;
570     case 105: /* free-form-text */
571         *search_type = "or-list";
572         break;
573     case 106: /* document-text */
574         *search_type = "or-list";
575         break;  
576     case -1:
577     case 1:   /* phrase */
578     case 2:   /* word */
579     case 108: /* string */ 
580         *search_type = "phrase";
581         break;
582     case 107: /* local-number */
583         *search_type = "local";
584         *index_type = 0;
585         break;
586     case 109: /* numeric string */
587         *index_type = "n";
588         *search_type = "numeric";
589         break;
590     case 104: /* urx */
591         *index_type = "u";
592         *search_type = "phrase";
593         break;
594     case 3:   /* key */
595         *index_type = "0";
596         *search_type = "phrase";
597         break;
598     case 4:  /* year */
599         *index_type = "y";
600         *search_type = "phrase";
601         break;
602     case 5:  /* date */
603         *index_type = "d";
604         *search_type = "phrase";
605         break;
606     case -2:
607         if (structure_str && *structure_str)
608             *index_type = structure_str;
609         else
610             return -1;
611         break;
612     default:
613         return -1;
614     }
615     return 0;
616 }
617
618 WRBUF zebra_replace(zebra_map_t zm, const char *ex_list,
619                     const char *input_str, int input_len)
620 {
621     wrbuf_rewind(zm->zebra_maps->wrbuf_1);
622     wrbuf_write(zm->zebra_maps->wrbuf_1, input_str, input_len);
623     return zm->zebra_maps->wrbuf_1;
624 }
625
626 #define SE_CHARS ";,.()-/?<> \r\n\t"
627
628 static int tokenize_simple(zebra_map_t zm,
629                            const char **result_buf, size_t *result_len)
630 {
631     char *buf = wrbuf_buf(zm->input_str);
632     size_t len = wrbuf_len(zm->input_str);
633     size_t i = zm->simple_off;
634     size_t start;
635
636     while (i < len && strchr(SE_CHARS, buf[i]))
637         i++;
638     start = i;
639     while (i < len && !strchr(SE_CHARS, buf[i]))
640     {
641         if (buf[i] > 32 && buf[i] < 127)
642             buf[i] = tolower(buf[i]);
643         i++;
644     }
645
646     zm->simple_off = i;
647     if (start != i)
648     {
649         *result_buf = buf + start;
650         *result_len = i - start;
651         return 1;
652     }
653     return 0;
654  }
655
656
657 int zebra_map_tokenize_next(zebra_map_t zm,
658                             const char **result_buf, size_t *result_len,
659                             const char **display_buf, size_t *display_len)
660 {
661     assert(zm->use_chain);
662
663 #if YAZ_HAVE_ICU
664     if (!zm->icu_chain)
665         return tokenize_simple(zm, result_buf, result_len);
666     else
667     {
668         UErrorCode status;
669         while (icu_chain_next_token(zm->icu_chain, &status))
670         {
671             if (!U_SUCCESS(status))
672                 return 0;
673             *result_buf = icu_chain_token_sortkey(zm->icu_chain);
674             assert(*result_buf);
675
676             *result_len = strlen(*result_buf);
677
678             if (display_buf)
679             {
680                 *display_buf = icu_chain_token_display(zm->icu_chain);
681                 if (display_len)
682                     *display_len = strlen(*display_buf);
683             }
684             if (zm->debug)
685             {
686                 wrbuf_rewind(zm->print_str);
687                 wrbuf_write_escaped(zm->print_str, *result_buf, *result_len);
688                 yaz_log(YLOG_LOG, "output %s", wrbuf_cstr(zm->print_str));
689             }
690
691             if (**result_buf != '\0')
692                 return 1;
693         }
694     }
695     return 0;
696 #else
697     return tokenize_simple(zm, result_buf, result_len);
698 #endif
699 }
700
701 int zebra_map_tokenize_start(zebra_map_t zm,
702                              const char *buf, size_t len)
703 {
704 #if YAZ_HAVE_ICU
705     int ret;
706 #endif
707     assert(zm->use_chain);
708
709     wrbuf_rewind(zm->input_str);
710     wrbuf_write(zm->input_str, buf, len);
711     zm->simple_off = 0;
712 #if YAZ_HAVE_ICU
713     if (zm->icu_chain)
714     {
715         UErrorCode status;
716         if (zm->debug)
717         {
718             wrbuf_rewind(zm->print_str);
719             wrbuf_write_escaped(zm->print_str, wrbuf_buf(zm->input_str),
720                                 wrbuf_len(zm->input_str));
721             
722             yaz_log(YLOG_LOG, "input %s", 
723                     wrbuf_cstr(zm->print_str)); 
724         }
725         ret = icu_chain_assign_cstr(zm->icu_chain,
726                                     wrbuf_cstr(zm->input_str), &status);
727         if (!ret && !U_SUCCESS(status))
728         {
729             if (zm->debug)
730             {
731                 yaz_log(YLOG_WARN, "bad encoding for input");
732             }
733             return -1;
734         }
735     }
736 #endif
737     return 0;
738 }
739
740 int zebra_maps_is_icu(zebra_map_t zm)
741 {
742     assert(zm);
743 #if YAZ_HAVE_ICU
744     return zm->use_chain;
745 #else
746     return 0;
747 #endif
748 }
749
750
751 /*
752  * Local variables:
753  * c-basic-offset: 4
754  * c-file-style: "Stroustrup"
755  * indent-tabs-mode: nil
756  * End:
757  * vim: shiftwidth=4 tabstop=8 expandtab
758  */
759