fd62d4aa5a463a8637fc2ac83ebf5a7639d3b88b
[idzebra-moved-to-github.git] / util / zebramap.c
1 /* $Id: zebramap.c,v 1.72 2007-11-15 08:53:26 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 #if YAZ_HAVE_XML2
234         zm->doc = xmlParseFile(argv[1]);
235         if (!zm->doc)
236         {
237             yaz_log(YLOG_WARN, "%s:%d: Could not load icuchain config '%s'",
238                     fname, lineno, argv[1]);
239             return -1;
240         }
241         else
242         {
243 #if YAZ_HAVE_ICU
244             UErrorCode status;
245             xmlNode *xml_node = xmlDocGetRootElement(zm->doc);
246             zm->icu_chain = 
247                 icu_chain_xml_config(xml_node,
248 /* not sure about sort for this function yet.. */
249 #if 1
250                                      1,
251 #else
252                                      zm->type == ZEBRA_MAP_TYPE_SORT,
253 #endif                                    
254                                      &status);
255             if (!zm->icu_chain)
256             {
257                 yaz_log(YLOG_WARN, "%s:%d: Failed to load ICU chain %s",
258                         fname, lineno, argv[1]);
259             }
260             zm->use_chain = 1;
261 #else
262             yaz_log(YLOG_WARN, "%s:%d: ICU support unavailable",
263                     fname, lineno);
264             return -1;
265 #endif
266         }
267 #else
268         yaz_log(YLOG_WARN, "%s:%d: XML support unavailable",
269                 fname, lineno);
270         return -1;
271 #endif
272     }
273     else if (!yaz_matchstr(argv[0], "debug") && argc == 2)
274     {
275         zm->debug = atoi(argv[1]);
276     }
277     else
278     {
279         yaz_log(YLOG_WARN, "%s:%d: Unrecognized directive '%s'",  
280                 fname, lineno, argv[0]);
281         return -1;
282     }
283     return 0;
284 }
285
286 ZEBRA_RES zebra_maps_read_file(zebra_maps_t zms, const char *fname)
287 {
288     FILE *f;
289     char line[512];
290     char *argv[10];
291     int argc;
292     int lineno = 0;
293     int failures = 0;
294
295     if (!(f = yaz_fopen(zms->tabpath, fname, "r", zms->tabroot)))
296     {
297         yaz_log(YLOG_ERRNO|YLOG_FATAL, "%s", fname);
298         return ZEBRA_FAIL;
299     }
300     while ((argc = readconf_line(f, &lineno, line, 512, argv, 10)))
301     {
302         int r = parse_command(zms, argc, argv, fname, lineno);
303         if (r)
304             failures++;
305     }
306     yaz_fclose(f);
307
308     if (failures)
309         return ZEBRA_FAIL;
310
311     (zms->no_files_read)++;
312     return ZEBRA_OK;
313 }
314
315 zebra_maps_t zebra_maps_open(Res res, const char *base_path,
316                              const char *profile_path)
317 {
318     zebra_maps_t zms = (zebra_maps_t) xmalloc(sizeof(*zms));
319
320     zms->nmem = nmem_create();
321     zms->tabpath = profile_path ? nmem_strdup(zms->nmem, profile_path) : 0;
322     zms->tabroot = 0;
323     if (base_path)
324         zms->tabroot = nmem_strdup(zms->nmem, base_path);
325     zms->map_list = 0;
326     zms->last_map = 0;
327
328     zms->temp_map_str[0] = '\0';
329     zms->temp_map_str[1] = '\0';
330
331     zms->temp_map_ptr[0] = zms->temp_map_str;
332     zms->temp_map_ptr[1] = NULL;
333
334     zms->wrbuf_1 = wrbuf_alloc();
335
336     zms->no_files_read = 0;
337     return zms;
338 }
339
340 zebra_map_t zebra_map_get(zebra_maps_t zms, const char *id)
341 {
342     zebra_map_t zm;
343     for (zm = zms->map_list; zm; zm = zm->next)
344         if (!strcmp(zm->id, id))
345             break;
346     return zm;
347 }
348
349 zebra_map_t zebra_map_get_or_add(zebra_maps_t zms, const char *id)
350 {
351     struct zebra_map *zm = zebra_map_get(zms, id);
352     if (!zm)
353     {
354         zm = zebra_add_map(zms, id, ZEBRA_MAP_TYPE_INDEX);
355         
356         /* no reason to warn if no maps are read from file */
357         if (zms->no_files_read)
358             yaz_log(YLOG_WARN, "Unknown register type: %s", id);
359
360         zm->maptab_name = nmem_strdup(zms->nmem, "@");
361         zm->completeness = 0;
362         zm->positioned = 1;
363     }
364     return zm;
365 }
366
367 chrmaptab zebra_charmap_get(zebra_map_t zm)
368 {
369     if (!zm->maptab)
370     {
371         if (!zm->maptab_name || !yaz_matchstr(zm->maptab_name, "@"))
372             return NULL;
373         if (!(zm->maptab = chrmaptab_create(zm->zebra_maps->tabpath,
374                                             zm->maptab_name,
375                                             zm->zebra_maps->tabroot)))
376             yaz_log(YLOG_WARN, "Failed to read character table %s",
377                     zm->maptab_name);
378         else
379             yaz_log(YLOG_DEBUG, "Read character table %s", zm->maptab_name);
380     }
381     return zm->maptab;
382 }
383
384 const char **zebra_maps_input(zebra_map_t zm,
385                               const char **from, int len, int first)
386 {
387     chrmaptab maptab = zebra_charmap_get(zm);
388     if (maptab)
389         return chr_map_input(maptab, from, len, first);
390     
391     zm->zebra_maps->temp_map_str[0] = **from;
392
393     (*from)++;
394     return zm->zebra_maps->temp_map_ptr;
395 }
396
397 const char **zebra_maps_search(zebra_map_t zm,
398                                const char **from, int len,  int *q_map_match)
399 {
400     chrmaptab maptab;
401     
402     *q_map_match = 0;
403     maptab = zebra_charmap_get(zm);
404     if (maptab)
405     {
406         const char **map;
407         map = chr_map_q_input(maptab, from, len, 0);
408         if (map && map[0])
409         {
410             *q_map_match = 1;
411             return map;
412         }
413         map = chr_map_input(maptab, from, len, 0);
414         if (map)
415             return map;
416     }
417     zm->zebra_maps->temp_map_str[0] = **from;
418
419     (*from)++;
420     return zm->zebra_maps->temp_map_ptr;
421 }
422
423 const char *zebra_maps_output(zebra_map_t zm,
424                               const char **from)
425 {
426     chrmaptab maptab = zebra_charmap_get(zm);
427     if (!maptab)
428         return 0;
429     return chr_map_output(maptab, from, 1);
430 }
431
432
433 /* ------------------------------------ */
434
435 int zebra_maps_is_complete(zebra_map_t zm)
436
437     if (zm)
438         return zm->completeness;
439     return 0;
440 }
441
442 int zebra_maps_is_positioned(zebra_map_t zm)
443 {
444     if (zm)
445         return zm->positioned;
446     return 0;
447 }
448
449 int zebra_maps_is_index(zebra_map_t zm)
450 {
451     if (zm)
452         return zm->type == ZEBRA_MAP_TYPE_INDEX;
453     return 0;
454 }
455
456 int zebra_maps_is_staticrank(zebra_map_t zm)
457 {
458     if (zm)
459         return zm->type == ZEBRA_MAP_TYPE_STATICRANK;
460     return 0;
461 }
462     
463 int zebra_maps_is_sort(zebra_map_t zm)
464 {
465     if (zm)
466         return zm->type == ZEBRA_MAP_TYPE_SORT;
467     return 0;
468 }
469
470 int zebra_maps_is_alwaysmatches(zebra_map_t zm)
471 {
472     if (zm)
473         return zm->alwaysmatches;
474     return 0;
475 }
476
477 int zebra_maps_is_first_in_field(zebra_map_t zm)
478 {
479     if (zm)
480         return zm->first_in_field;
481     return 0;
482 }
483
484 int zebra_maps_sort(zebra_maps_t zms, Z_SortAttributes *sortAttributes,
485                     int *numerical)
486 {
487     AttrType use;
488     AttrType structure;
489     int structure_value;
490     attr_init_AttrList(&use, sortAttributes->list, 1);
491     attr_init_AttrList(&structure, sortAttributes->list, 4);
492
493     *numerical = 0;
494     structure_value = attr_find(&structure, 0);
495     if (structure_value == 109)
496         *numerical = 1;
497     return attr_find(&use, NULL);
498 }
499
500 int zebra_maps_attr(zebra_maps_t zms, Z_AttributesPlusTerm *zapt,
501                     const char **index_type, char **search_type, char *rank_type,
502                     int *complete_flag, int *sort_flag)
503 {
504     AttrType completeness;
505     AttrType structure;
506     AttrType relation;
507     AttrType sort_relation;
508     AttrType weight;
509     AttrType use;
510     int completeness_value;
511     int structure_value;
512     const char *structure_str = 0;
513     int relation_value;
514     int sort_relation_value;
515     int weight_value;
516     int use_value;
517
518     attr_init_APT(&structure, zapt, 4);
519     attr_init_APT(&completeness, zapt, 6);
520     attr_init_APT(&relation, zapt, 2);
521     attr_init_APT(&sort_relation, zapt, 7);
522     attr_init_APT(&weight, zapt, 9);
523     attr_init_APT(&use, zapt, 1);
524
525     completeness_value = attr_find(&completeness, NULL);
526     structure_value = attr_find_ex(&structure, NULL, &structure_str);
527     relation_value = attr_find(&relation, NULL);
528     sort_relation_value = attr_find(&sort_relation, NULL);
529     weight_value = attr_find(&weight, NULL);
530     use_value = attr_find(&use, NULL);
531
532     if (completeness_value == 2 || completeness_value == 3)
533         *complete_flag = 1;
534     else
535         *complete_flag = 0;
536     *index_type = 0;
537
538     *sort_flag =(sort_relation_value > 0) ? 1 : 0;
539     *search_type = "phrase";
540     strcpy(rank_type, "void");
541     if (relation_value == 102)
542     {
543         if (weight_value == -1)
544             weight_value = 34;
545         sprintf(rank_type, "rank,w=%d,u=%d", weight_value, use_value);
546     }
547     if (*complete_flag)
548         *index_type = "p";
549     else
550         *index_type = "w";
551     switch (structure_value)
552     {
553     case 6:   /* word list */
554         *search_type = "and-list";
555         break;
556     case 105: /* free-form-text */
557         *search_type = "or-list";
558         break;
559     case 106: /* document-text */
560         *search_type = "or-list";
561         break;  
562     case -1:
563     case 1:   /* phrase */
564     case 2:   /* word */
565     case 108: /* string */ 
566         *search_type = "phrase";
567         break;
568     case 107: /* local-number */
569         *search_type = "local";
570         *index_type = 0;
571         break;
572     case 109: /* numeric string */
573         *index_type = "n";
574         *search_type = "numeric";
575         break;
576     case 104: /* urx */
577         *index_type = "u";
578         *search_type = "phrase";
579         break;
580     case 3:   /* key */
581         *index_type = "0";
582         *search_type = "phrase";
583         break;
584     case 4:  /* year */
585         *index_type = "y";
586         *search_type = "phrase";
587         break;
588     case 5:  /* date */
589         *index_type = "d";
590         *search_type = "phrase";
591         break;
592     case -2:
593         if (structure_str && *structure_str)
594             *index_type = structure_str;
595         else
596             return -1;
597         break;
598     default:
599         return -1;
600     }
601     return 0;
602 }
603
604 WRBUF zebra_replace(zebra_map_t zm, const char *ex_list,
605                     const char *input_str, int input_len)
606 {
607     wrbuf_rewind(zm->zebra_maps->wrbuf_1);
608     wrbuf_write(zm->zebra_maps->wrbuf_1, input_str, input_len);
609     return zm->zebra_maps->wrbuf_1;
610 }
611
612 #define SE_CHARS ";,.()-/?<> \r\n\t"
613
614 static int tokenize_simple(zebra_map_t zm,
615                            const char **result_buf, size_t *result_len)
616 {
617     char *buf = wrbuf_buf(zm->input_str);
618     size_t len = wrbuf_len(zm->input_str);
619     size_t i = zm->simple_off;
620     size_t start;
621
622     while (i < len && strchr(SE_CHARS, buf[i]))
623         i++;
624     start = i;
625     while (i < len && !strchr(SE_CHARS, buf[i]))
626     {
627         if (buf[i] > 32 && buf[i] < 127)
628             buf[i] = tolower(buf[i]);
629         i++;
630     }
631
632     zm->simple_off = i;
633     if (start != i)
634     {
635         *result_buf = buf + start;
636         *result_len = i - start;
637         return 1;
638     }
639     return 0;
640  }
641
642 int zebra_map_tokenize(zebra_map_t zm,
643                        const char *buf, size_t len,
644                        const char **result_buf, size_t *result_len)
645 {
646     assert(zm->use_chain);
647
648     if (buf)
649     {
650         wrbuf_rewind(zm->input_str);
651         wrbuf_write(zm->input_str, buf, len);
652         zm->simple_off = 0;
653     }
654
655 #if YAZ_HAVE_ICU
656     if (!zm->icu_chain)
657         return tokenize_simple(zm, result_buf, result_len);
658     else
659     {
660         UErrorCode status;
661         if (buf)
662         {
663             if (zm->debug)
664             {
665                 wrbuf_rewind(zm->print_str);
666                 wrbuf_write_escaped(zm->print_str, wrbuf_buf(zm->input_str),
667                                     wrbuf_len(zm->input_str));
668                 
669                 yaz_log(YLOG_LOG, "input %s", 
670                         wrbuf_cstr(zm->print_str)); 
671             }
672             icu_chain_assign_cstr(zm->icu_chain,
673                                   wrbuf_cstr(zm->input_str),
674                                   &status);
675             assert(U_SUCCESS(status));
676         }
677         while (icu_chain_next_token(zm->icu_chain, &status))
678         {
679             assert(U_SUCCESS(status));
680             *result_buf = icu_chain_token_sortkey(zm->icu_chain);
681             assert(*result_buf);
682
683             *result_len = strlen(*result_buf);
684
685             if (zm->debug)
686             {
687                 wrbuf_rewind(zm->print_str);
688                 wrbuf_write_escaped(zm->print_str, *result_buf, *result_len);
689                 yaz_log(YLOG_LOG, "output %s", wrbuf_cstr(zm->print_str));
690             }
691
692             if (**result_buf != '\0')
693                 return 1;
694         }
695         assert(U_SUCCESS(status));
696     }
697     return 0;
698 #else
699     return tokenize_simple(zm, result_buf, result_len);
700 #endif
701 }
702
703 int zebra_maps_is_icu(zebra_map_t zm)
704 {
705 #if YAZ_HAVE_ICU
706     return zm->use_chain;
707 #else
708     return 0;
709 #endif
710 }
711
712
713 /*
714  * Local variables:
715  * c-basic-offset: 4
716  * indent-tabs-mode: nil
717  * End:
718  * vim: shiftwidth=4 tabstop=8 expandtab
719  */
720