Let zebra_map_tokenize_start returns -1 in case of error.
[idzebra-moved-to-github.git] / util / zebramap.c
1 /* $Id: zebramap.c,v 1.77 2007-12-20 19:05:12 adam Exp $
2    Copyright (C) 1995-2007
3    Index Data ApS
4
5    This file is part of the Zebra server.
6
7    Zebra is free software; you can redistribute it and/or modify it under
8    the terms of the GNU General Public License as published by the Free
9    Software Foundation; either version 2, or (at your option) any later
10    version.
11
12    Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13    WARRANTY; without even the implied warranty of MERCHANTABILITY or
14    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15    for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Zebra; see the file LICENSE.zebra.  If not, write to the
19    Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.
21 */
22
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 zebra_map_t zebra_map_get(zebra_maps_t zms, const char *id)
349 {
350     zebra_map_t zm;
351     for (zm = zms->map_list; zm; zm = zm->next)
352         if (!strcmp(zm->id, id))
353             break;
354     return zm;
355 }
356
357 zebra_map_t zebra_map_get_or_add(zebra_maps_t zms, const char *id)
358 {
359     struct zebra_map *zm = zebra_map_get(zms, id);
360     if (!zm)
361     {
362         zm = zebra_add_map(zms, id, ZEBRA_MAP_TYPE_INDEX);
363         
364         /* no reason to warn if no maps are read from file */
365         if (zms->no_files_read)
366             yaz_log(YLOG_WARN, "Unknown register type: %s", id);
367
368         zm->maptab_name = nmem_strdup(zms->nmem, "@");
369         zm->completeness = 0;
370         zm->positioned = 1;
371     }
372     return zm;
373 }
374
375 chrmaptab zebra_charmap_get(zebra_map_t zm)
376 {
377     if (!zm->maptab)
378     {
379         if (!zm->maptab_name || !yaz_matchstr(zm->maptab_name, "@"))
380             return NULL;
381         if (!(zm->maptab = chrmaptab_create(zm->zebra_maps->tabpath,
382                                             zm->maptab_name,
383                                             zm->zebra_maps->tabroot)))
384             yaz_log(YLOG_WARN, "Failed to read character table %s",
385                     zm->maptab_name);
386         else
387             yaz_log(YLOG_DEBUG, "Read character table %s", zm->maptab_name);
388     }
389     return zm->maptab;
390 }
391
392 const char **zebra_maps_input(zebra_map_t zm,
393                               const char **from, int len, int first)
394 {
395     chrmaptab maptab = zebra_charmap_get(zm);
396     if (maptab)
397         return chr_map_input(maptab, from, len, first);
398     
399     zm->zebra_maps->temp_map_str[0] = **from;
400
401     (*from)++;
402     return zm->zebra_maps->temp_map_ptr;
403 }
404
405 const char **zebra_maps_search(zebra_map_t zm,
406                                const char **from, int len,  int *q_map_match)
407 {
408     chrmaptab maptab;
409     
410     *q_map_match = 0;
411     maptab = zebra_charmap_get(zm);
412     if (maptab)
413     {
414         const char **map;
415         map = chr_map_q_input(maptab, from, len, 0);
416         if (map && map[0])
417         {
418             *q_map_match = 1;
419             return map;
420         }
421         map = chr_map_input(maptab, from, len, 0);
422         if (map)
423             return map;
424     }
425     zm->zebra_maps->temp_map_str[0] = **from;
426
427     (*from)++;
428     return zm->zebra_maps->temp_map_ptr;
429 }
430
431 const char *zebra_maps_output(zebra_map_t zm,
432                               const char **from)
433 {
434     chrmaptab maptab = zebra_charmap_get(zm);
435     if (!maptab)
436         return 0;
437     return chr_map_output(maptab, from, 1);
438 }
439
440
441 /* ------------------------------------ */
442
443 int zebra_maps_is_complete(zebra_map_t zm)
444
445     if (zm)
446         return zm->completeness;
447     return 0;
448 }
449
450 int zebra_maps_is_positioned(zebra_map_t zm)
451 {
452     if (zm)
453         return zm->positioned;
454     return 0;
455 }
456
457 int zebra_maps_is_index(zebra_map_t zm)
458 {
459     if (zm)
460         return zm->type == ZEBRA_MAP_TYPE_INDEX;
461     return 0;
462 }
463
464 int zebra_maps_is_staticrank(zebra_map_t zm)
465 {
466     if (zm)
467         return zm->type == ZEBRA_MAP_TYPE_STATICRANK;
468     return 0;
469 }
470     
471 int zebra_maps_is_sort(zebra_map_t zm)
472 {
473     if (zm)
474         return zm->type == ZEBRA_MAP_TYPE_SORT;
475     return 0;
476 }
477
478 int zebra_maps_is_alwaysmatches(zebra_map_t zm)
479 {
480     if (zm)
481         return zm->alwaysmatches;
482     return 0;
483 }
484
485 int zebra_maps_is_first_in_field(zebra_map_t zm)
486 {
487     if (zm)
488         return zm->first_in_field;
489     return 0;
490 }
491
492 int zebra_maps_sort(zebra_maps_t zms, Z_SortAttributes *sortAttributes,
493                     int *numerical)
494 {
495     AttrType use;
496     AttrType structure;
497     int structure_value;
498     attr_init_AttrList(&use, sortAttributes->list, 1);
499     attr_init_AttrList(&structure, sortAttributes->list, 4);
500
501     *numerical = 0;
502     structure_value = attr_find(&structure, 0);
503     if (structure_value == 109)
504         *numerical = 1;
505     return attr_find(&use, NULL);
506 }
507
508 int zebra_maps_attr(zebra_maps_t zms, Z_AttributesPlusTerm *zapt,
509                     const char **index_type, char **search_type, char *rank_type,
510                     int *complete_flag, int *sort_flag)
511 {
512     AttrType completeness;
513     AttrType structure;
514     AttrType relation;
515     AttrType sort_relation;
516     AttrType weight;
517     AttrType use;
518     int completeness_value;
519     int structure_value;
520     const char *structure_str = 0;
521     int relation_value;
522     int sort_relation_value;
523     int weight_value;
524     int use_value;
525
526     attr_init_APT(&structure, zapt, 4);
527     attr_init_APT(&completeness, zapt, 6);
528     attr_init_APT(&relation, zapt, 2);
529     attr_init_APT(&sort_relation, zapt, 7);
530     attr_init_APT(&weight, zapt, 9);
531     attr_init_APT(&use, zapt, 1);
532
533     completeness_value = attr_find(&completeness, NULL);
534     structure_value = attr_find_ex(&structure, NULL, &structure_str);
535     relation_value = attr_find(&relation, NULL);
536     sort_relation_value = attr_find(&sort_relation, NULL);
537     weight_value = attr_find(&weight, NULL);
538     use_value = attr_find(&use, NULL);
539
540     if (completeness_value == 2 || completeness_value == 3)
541         *complete_flag = 1;
542     else
543         *complete_flag = 0;
544     *index_type = 0;
545
546     *sort_flag =(sort_relation_value > 0) ? 1 : 0;
547     *search_type = "phrase";
548     strcpy(rank_type, "void");
549     if (relation_value == 102)
550     {
551         if (weight_value == -1)
552             weight_value = 34;
553         sprintf(rank_type, "rank,w=%d,u=%d", weight_value, use_value);
554     }
555     if (*complete_flag)
556         *index_type = "p";
557     else
558         *index_type = "w";
559     switch (structure_value)
560     {
561     case 6:   /* word list */
562         *search_type = "and-list";
563         break;
564     case 105: /* free-form-text */
565         *search_type = "or-list";
566         break;
567     case 106: /* document-text */
568         *search_type = "or-list";
569         break;  
570     case -1:
571     case 1:   /* phrase */
572     case 2:   /* word */
573     case 108: /* string */ 
574         *search_type = "phrase";
575         break;
576     case 107: /* local-number */
577         *search_type = "local";
578         *index_type = 0;
579         break;
580     case 109: /* numeric string */
581         *index_type = "n";
582         *search_type = "numeric";
583         break;
584     case 104: /* urx */
585         *index_type = "u";
586         *search_type = "phrase";
587         break;
588     case 3:   /* key */
589         *index_type = "0";
590         *search_type = "phrase";
591         break;
592     case 4:  /* year */
593         *index_type = "y";
594         *search_type = "phrase";
595         break;
596     case 5:  /* date */
597         *index_type = "d";
598         *search_type = "phrase";
599         break;
600     case -2:
601         if (structure_str && *structure_str)
602             *index_type = structure_str;
603         else
604             return -1;
605         break;
606     default:
607         return -1;
608     }
609     return 0;
610 }
611
612 WRBUF zebra_replace(zebra_map_t zm, const char *ex_list,
613                     const char *input_str, int input_len)
614 {
615     wrbuf_rewind(zm->zebra_maps->wrbuf_1);
616     wrbuf_write(zm->zebra_maps->wrbuf_1, input_str, input_len);
617     return zm->zebra_maps->wrbuf_1;
618 }
619
620 #define SE_CHARS ";,.()-/?<> \r\n\t"
621
622 static int tokenize_simple(zebra_map_t zm,
623                            const char **result_buf, size_t *result_len)
624 {
625     char *buf = wrbuf_buf(zm->input_str);
626     size_t len = wrbuf_len(zm->input_str);
627     size_t i = zm->simple_off;
628     size_t start;
629
630     while (i < len && strchr(SE_CHARS, buf[i]))
631         i++;
632     start = i;
633     while (i < len && !strchr(SE_CHARS, buf[i]))
634     {
635         if (buf[i] > 32 && buf[i] < 127)
636             buf[i] = tolower(buf[i]);
637         i++;
638     }
639
640     zm->simple_off = i;
641     if (start != i)
642     {
643         *result_buf = buf + start;
644         *result_len = i - start;
645         return 1;
646     }
647     return 0;
648  }
649
650
651 int zebra_map_tokenize_next(zebra_map_t zm,
652                             const char **result_buf, size_t *result_len,
653                             const char **display_buf, size_t *display_len)
654 {
655     assert(zm->use_chain);
656
657 #if YAZ_HAVE_ICU
658     if (!zm->icu_chain)
659         return tokenize_simple(zm, result_buf, result_len);
660     else
661     {
662         UErrorCode status;
663         while (icu_chain_next_token(zm->icu_chain, &status))
664         {
665             if (!U_SUCCESS(status))
666                 return 0;
667             *result_buf = icu_chain_token_sortkey(zm->icu_chain);
668             assert(*result_buf);
669
670             *result_len = strlen(*result_buf);
671
672             if (display_buf)
673             {
674                 *display_buf = icu_chain_token_display(zm->icu_chain);
675                 if (display_len)
676                     *display_len = strlen(*display_buf);
677             }
678             if (zm->debug)
679             {
680                 wrbuf_rewind(zm->print_str);
681                 wrbuf_write_escaped(zm->print_str, *result_buf, *result_len);
682                 yaz_log(YLOG_LOG, "output %s", wrbuf_cstr(zm->print_str));
683             }
684
685             if (**result_buf != '\0')
686                 return 1;
687         }
688     }
689     return 0;
690 #else
691     return tokenize_simple(zm, result_buf, result_len);
692 #endif
693 }
694
695 int zebra_map_tokenize_start(zebra_map_t zm,
696                              const char *buf, size_t len)
697 {
698     assert(zm->use_chain);
699
700     wrbuf_rewind(zm->input_str);
701     wrbuf_write(zm->input_str, buf, len);
702     zm->simple_off = 0;
703 #if YAZ_HAVE_ICU
704     if (zm->icu_chain)
705     {
706         UErrorCode status;
707         if (zm->debug)
708         {
709             wrbuf_rewind(zm->print_str);
710             wrbuf_write_escaped(zm->print_str, wrbuf_buf(zm->input_str),
711                                 wrbuf_len(zm->input_str));
712             
713             yaz_log(YLOG_LOG, "input %s", 
714                     wrbuf_cstr(zm->print_str)); 
715         }
716         icu_chain_assign_cstr(zm->icu_chain,
717                               wrbuf_cstr(zm->input_str),
718                               &status);
719         if (!U_SUCCESS(status))
720         {
721             if (zm->debug)
722             {
723                 yaz_log(YLOG_WARN, "bad encoding for input");
724             }
725             return -1;
726         }
727     }
728 #endif
729     return 0;
730 }
731
732 int zebra_maps_is_icu(zebra_map_t zm)
733 {
734 #if YAZ_HAVE_ICU
735     return zm->use_chain;
736 #else
737     return 0;
738 #endif
739 }
740
741
742 /*
743  * Local variables:
744  * c-basic-offset: 4
745  * indent-tabs-mode: nil
746  * End:
747  * vim: shiftwidth=4 tabstop=8 expandtab
748  */
749