Directive systag.
[idzebra-moved-to-github.git] / data1 / d1_absyn.c
1 /* $Id: d1_absyn.c,v 1.3 2002-12-02 16:55:14 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
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 <stdio.h>
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <yaz/oid.h>
29 #include <yaz/log.h>
30 #include <data1.h>
31
32 #define D1_MAX_NESTING  128
33
34 struct data1_systag {
35     char *name;
36     char *value;
37     struct data1_systag *next;
38 };
39
40 struct data1_absyn_cache_info 
41 {
42     char *name;
43     data1_absyn *absyn;
44     data1_absyn_cache next;
45 };
46
47 struct data1_attset_cache_info 
48 {
49     char *name;
50     data1_attset *attset;
51     data1_attset_cache next;
52 };
53
54 data1_absyn *data1_absyn_search (data1_handle dh, const char *name)
55 {
56     data1_absyn_cache p = *data1_absyn_cache_get (dh);
57
58     while (p)
59     {
60         if (!strcmp (name, p->name))
61             return p->absyn;
62         p = p->next;
63     }
64     return NULL;
65 }
66
67 void data1_absyn_trav (data1_handle dh, void *handle,
68                        void (*fh)(data1_handle dh, void *h, data1_absyn *a))
69 {
70     data1_absyn_cache p = *data1_absyn_cache_get (dh);
71
72     while (p)
73     {
74         (*fh)(dh, handle, p->absyn);
75         p = p->next;
76     }
77 }
78
79 data1_absyn *data1_absyn_add (data1_handle dh, const char *name)
80 {
81     char fname[512];
82     NMEM mem = data1_nmem_get (dh);
83
84     data1_absyn_cache p = (data1_absyn_cache)nmem_malloc (mem, sizeof(*p));
85     data1_absyn_cache *pp = data1_absyn_cache_get (dh);
86
87     sprintf(fname, "%s.abs", name);
88     p->absyn = data1_read_absyn (dh, fname, 0);
89     p->name = nmem_strdup (mem, name);
90     p->next = *pp;
91     *pp = p;
92     return p->absyn;
93 }
94
95 data1_absyn *data1_get_absyn (data1_handle dh, const char *name)
96 {
97     data1_absyn *absyn;
98
99     if (!(absyn = data1_absyn_search (dh, name)))
100         absyn = data1_absyn_add (dh, name);
101     return absyn;
102 }
103
104 data1_attset *data1_attset_search_name (data1_handle dh, const char *name)
105 {
106     data1_attset_cache p = *data1_attset_cache_get (dh);
107
108     while (p)
109     {
110         if (!strcmp (name, p->name))
111             return p->attset;
112         p = p->next;
113     }
114     return NULL;
115 }
116
117 data1_attset *data1_attset_search_id (data1_handle dh, int id)
118 {
119     data1_attset_cache p = *data1_attset_cache_get (dh);
120
121     while (p)
122     {
123         if (id == p->attset->reference)
124             return p->attset;
125         p = p->next;
126     }
127     return NULL;
128 }
129
130 data1_attset *data1_attset_add (data1_handle dh, const char *name)
131 {
132     char fname[512], aname[512];
133     NMEM mem = data1_nmem_get (dh);
134     data1_attset *attset;
135
136     strcpy (aname, name);
137     sprintf(fname, "%s.att", name);
138     attset = data1_read_attset (dh, fname);
139     if (!attset)
140     {
141         char *cp;
142         attset = data1_read_attset (dh, name);
143         if (attset && (cp = strrchr (aname, '.')))
144             *cp = '\0';
145     }
146     if (!attset)
147         yaz_log (LOG_WARN|LOG_ERRNO, "Couldn't load attribute set %s", name);
148     else
149     {
150         data1_attset_cache p = (data1_attset_cache)
151             nmem_malloc (mem, sizeof(*p));
152         data1_attset_cache *pp = data1_attset_cache_get (dh);
153         
154         attset->name = p->name = nmem_strdup (mem, aname);
155         p->attset = attset;
156         p->next = *pp;
157         *pp = p;
158     }
159     return attset;
160 }
161
162 data1_attset *data1_get_attset (data1_handle dh, const char *name)
163 {
164     data1_attset *attset;
165
166     if (!(attset = data1_attset_search_name (dh, name)))
167         attset = data1_attset_add (dh, name);
168     return attset;
169 }
170
171 data1_esetname *data1_getesetbyname(data1_handle dh, data1_absyn *a,
172                                     const char *name)
173 {
174     data1_esetname *r;
175
176     for (r = a->esetnames; r; r = r->next)
177         if (!data1_matchstr(r->name, name))
178             return r;
179     return 0;
180 }
181
182 data1_element *data1_getelementbytagname (data1_handle dh, data1_absyn *abs,
183                                           data1_element *parent,
184                                           const char *tagname)
185 {
186     data1_element *r;
187
188     /* It's now possible to have a data1 tree with no abstract syntax */
189     if ( !abs )
190         return 0;
191
192     if (!parent)
193         r = abs->main_elements;
194     else
195         r = parent->children;
196
197     for (; r; r = r->next)
198     {
199         data1_name *n;
200
201         for (n = r->tag->names; n; n = n->next)
202             if (!data1_matchstr(tagname, n->name))
203                 return r;
204     }
205     return 0;
206 }
207
208 data1_element *data1_getelementbyname (data1_handle dh, data1_absyn *absyn,
209                                        const char *name)
210 {
211     data1_element *r;
212
213     /* It's now possible to have a data1 tree with no abstract syntax */
214     if ( !absyn )
215         return 0;
216     for (r = absyn->main_elements; r; r = r->next)
217         if (!data1_matchstr(r->name, name))
218             return r;
219     return 0;
220 }
221
222
223 void fix_element_ref (data1_handle dh, data1_absyn *absyn, data1_element *e)
224 {
225     /* It's now possible to have a data1 tree with no abstract syntax */
226     if ( !absyn )
227         return;
228
229     for (; e; e = e->next)
230     {
231         if (!e->sub_name)
232         {
233             if (e->children)
234                 fix_element_ref (dh, absyn, e->children);
235         }
236         else
237         {
238             data1_sub_elements *sub_e = absyn->sub_elements;
239             while (sub_e && strcmp (e->sub_name, sub_e->name))
240                 sub_e = sub_e->next;
241             if (sub_e)
242                 e->children = sub_e->elements;
243             else
244                 yaz_log (LOG_WARN, "Unresolved reference to sub-elements %s",
245                       e->sub_name);
246         }
247     }
248 }
249
250
251 static int parse_termlists (data1_handle dh, data1_termlist ***tpp,
252                             char *p, const char *file, int lineno,
253                             const char *element_name, data1_absyn *res)
254 {
255     data1_termlist **tp = *tpp;
256     do
257     {
258         char attname[512], structure[512];
259         char *source;
260         int r;
261         
262         if (!(r = sscanf(p, "%511[^:,]:%511[^,]", attname,
263                          structure)))
264         {
265             yaz_log(LOG_WARN,
266                     "%s:%d: Syntax error in termlistspec '%s'",
267                     file, lineno, p);
268             return -1;
269         }
270         if (*attname == '!')
271             strcpy(attname, element_name);
272         *tp = (data1_termlist *)
273             nmem_malloc(data1_nmem_get(dh), sizeof(**tp));
274         (*tp)->next = 0;
275         if (!((*tp)->att = data1_getattbyname(dh, res->attset,
276                                               attname)))
277         {
278             yaz_log(LOG_WARN,
279                     "%s:%d: Couldn't find att '%s' in attset",
280                     file, lineno, attname);
281             return -1;
282         }
283         if (r == 2 && (source = strchr(structure, ':')))
284             *source++ = '\0';   /* cut off structure .. */
285         else
286             source = "data";    /* ok: default is leaf data */
287         (*tp)->source = (char *)
288             nmem_strdup (data1_nmem_get (dh), source);
289         
290         if (r < 2) /* is the structure qualified? */
291             (*tp)->structure = "w";
292         else 
293             (*tp)->structure = (char *)
294                 nmem_strdup (data1_nmem_get (dh), structure);
295         tp = &(*tp)->next;
296     }
297     while ((p = strchr(p, ',')) && *(++p));
298     *tpp = tp;
299     return 0;
300 }
301
302 const char *data1_systag_lookup(data1_absyn *absyn, const char *tag,
303                                 const char *default_value)
304 {
305     struct data1_systag *p = absyn->systags;
306     for (; p; p = p->next)
307         if (!strcmp(p->name, tag))
308             return p->value;
309     return default_value;
310 }
311
312 data1_absyn *data1_read_absyn (data1_handle dh, const char *file,
313                                int file_must_exist)
314 {
315     data1_sub_elements *cur_elements = NULL;
316     data1_absyn *res = 0;
317     FILE *f;
318     data1_element **ppl[D1_MAX_NESTING];
319     data1_esetname **esetpp;
320     data1_maptab **maptabp;
321     data1_marctab **marcp;
322     data1_termlist *all = 0;
323     data1_attset_child **attset_childp;
324     data1_tagset **tagset_childp;
325     struct data1_systag **systagsp;
326     int level = 0;
327     int lineno = 0;
328     int argc;
329     char *argv[50], line[512];
330
331     if (!(f = data1_path_fopen(dh, file, "r")))
332     {
333         yaz_log(LOG_WARN|LOG_ERRNO, "Couldn't open %s", file);
334         if (file_must_exist)
335             return 0;
336     }
337     
338     res = (data1_absyn *) nmem_malloc(data1_nmem_get(dh), sizeof(*res));
339     res->name = 0;
340     res->reference = VAL_NONE;
341     res->tagset = 0;
342     res->encoding = 0;
343     res->enable_xpath_indexing = (f ? 0 : 1);
344     res->systags = 0;
345     systagsp = &res->systags;
346     tagset_childp = &res->tagset;
347
348     res->attset = data1_empty_attset (dh);
349     attset_childp =  &res->attset->children;
350
351     res->varset = 0;
352     res->esetnames = 0;
353     esetpp = &res->esetnames;
354     res->maptabs = 0;
355     maptabp = &res->maptabs;
356     res->marc = 0;
357     marcp = &res->marc;
358     res->sub_elements = NULL;
359     res->main_elements = NULL;
360     
361     while (f && (argc = readconf_line(f, &lineno, line, 512, argv, 50)))
362     {
363         char *cmd = *argv;
364         if (!strcmp(cmd, "elm") || !strcmp(cmd, "element"))
365         {
366             data1_element *new_element;
367             int i;
368             char *p, *sub_p, *path, *name, *termlists;
369             int type, value;
370             data1_termlist **tp;
371
372             if (argc < 4)
373             {
374                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to elm", file, lineno);
375                 continue;
376             }
377             path = argv[1];
378             name = argv[2];
379             termlists = argv[3];
380
381             if (!cur_elements)
382             {
383                 cur_elements = (data1_sub_elements *)
384                     nmem_malloc(data1_nmem_get(dh), sizeof(*cur_elements));
385                 cur_elements->next = res->sub_elements;
386                 cur_elements->elements = NULL;
387                 cur_elements->name = "main";
388                 res->sub_elements = cur_elements;
389                 
390                 level = 0;
391                 ppl[level] = &cur_elements->elements;
392             }
393             p = path;
394             for (i = 1;; i++)
395             {
396                 char *e;
397
398                 if ((e = strchr(p, '/')))
399                     p = e+1;
400                 else
401                     break;
402             }
403             if (i > level+1)
404             {
405                 yaz_log(LOG_WARN, "%s:%d: Bad level increase", file, lineno);
406                 fclose(f);
407                 return 0;
408             }
409             level = i;
410             new_element = *ppl[level-1] = (data1_element *)
411                 nmem_malloc(data1_nmem_get(dh), sizeof(*new_element));
412             new_element->next = new_element->children = 0;
413             new_element->tag = 0;
414             new_element->termlists = 0;
415             new_element->sub_name = 0;
416             
417             tp = &new_element->termlists;
418             ppl[level-1] = &new_element->next;
419             ppl[level] = &new_element->children;
420             
421             /* consider subtree (if any) ... */
422             if ((sub_p = strchr (p, ':')) && sub_p[1])
423             {
424                 *sub_p++ = '\0';
425                 new_element->sub_name =
426                     nmem_strdup (data1_nmem_get(dh), sub_p);            
427             }
428             /* well-defined tag */
429             if (sscanf(p, "(%d,%d)", &type, &value) == 2)
430             {
431                 if (!res->tagset)
432                 {
433                     yaz_log(LOG_WARN, "%s:%d: No tagset loaded", file, lineno);
434                     fclose(f);
435                     return 0;
436                 }
437                 if (!(new_element->tag = data1_gettagbynum (dh, res->tagset,
438                                                             type, value)))
439                 {
440                     yaz_log(LOG_WARN, "%s:%d: Couldn't find tag %s in tagset",
441                          file, lineno, p);
442                     fclose(f);
443                     return 0;
444                 }
445             }
446             /* private tag */
447             else if (*p)
448             {
449                 data1_tag *nt =
450                     new_element->tag = (data1_tag *)
451                     nmem_malloc(data1_nmem_get (dh),
452                                 sizeof(*new_element->tag));
453                 nt->which = DATA1T_string;
454                 nt->value.string = nmem_strdup(data1_nmem_get (dh), p);
455                 nt->names = (data1_name *)
456                     nmem_malloc(data1_nmem_get(dh), 
457                                 sizeof(*new_element->tag->names));
458                 nt->names->name = nt->value.string;
459                 nt->names->next = 0;
460                 nt->kind = DATA1K_string;
461                 nt->next = 0;
462                 nt->tagset = 0;
463             }
464             else
465             {
466                 yaz_log(LOG_WARN, "%s:%d: Bad element", file, lineno);
467                 fclose(f);
468                 return 0;
469             }
470             /* parse termList definitions */
471             p = termlists;
472             if (*p != '-')
473             {
474                 assert (res->attset);
475                 
476                 if (parse_termlists (dh, &tp, p, file, lineno, name, res))
477                 {
478                     fclose (f);
479                     return 0;
480                 }
481                 *tp = all; /* append any ALL entries to the list */
482             }
483             new_element->name = nmem_strdup(data1_nmem_get (dh), name);
484         }
485         else if (!strcmp(cmd, "section"))
486         {
487             char *name;
488             
489             if (argc < 2)
490             {
491                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to section",
492                      file, lineno);
493                 continue;
494             }
495             name = argv[1];
496             
497             cur_elements = (data1_sub_elements *)
498                 nmem_malloc(data1_nmem_get(dh), sizeof(*cur_elements));
499             cur_elements->next = res->sub_elements;
500             cur_elements->elements = NULL;
501             cur_elements->name = nmem_strdup (data1_nmem_get(dh), name);
502             res->sub_elements = cur_elements;
503             
504             level = 0;
505             ppl[level] = &cur_elements->elements;
506         }
507         else if (!strcmp(cmd, "xpath"))
508         {
509             if (argc != 2)
510             {
511                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to 'xpath' directive",
512                      file, lineno);
513                 continue;
514             }
515             if (!strcmp(argv[1], "enable"))
516                 res->enable_xpath_indexing = 1;
517             else if (!strcmp (argv[1], "disable"))
518                 res->enable_xpath_indexing = 0;
519             else
520             {
521                 yaz_log(LOG_WARN, "%s:%d: Expecting disable/enable "
522                         "after 'xpath' directive", file, lineno);
523             }
524         }
525         else if (!strcmp(cmd, "all"))
526         {
527             data1_termlist **tp = &all;
528             if (all)
529             {
530                 yaz_log(LOG_WARN, "%s:%d: Too many 'all' directives - ignored",
531                      file, lineno);
532                 continue;
533             }
534             if (argc != 2)
535             {
536                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to 'all' directive",
537                      file, lineno);
538                 continue;
539             }
540             if (parse_termlists (dh, &tp, argv[1], file, lineno, 0, res))
541             {
542                 fclose (f);
543                 return 0;
544             }
545         }
546         else if (!strcmp(cmd, "name"))
547         {
548             if (argc != 2)
549             {
550                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to name directive",
551                      file, lineno);
552                 continue;
553             }
554             res->name = nmem_strdup(data1_nmem_get(dh), argv[1]);
555         }
556         else if (!strcmp(cmd, "reference"))
557         {
558             char *name;
559             
560             if (argc != 2)
561             {
562                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to reference",
563                      file, lineno);
564                 continue;
565             }
566             name = argv[1];
567             if ((res->reference = oid_getvalbyname(name)) == VAL_NONE)
568             {
569                 yaz_log(LOG_WARN, "%s:%d: Unknown tagset ref '%s'", 
570                      file, lineno, name);
571                 continue;
572             }
573         }
574         else if (!strcmp(cmd, "attset"))
575         {
576             char *name;
577             data1_attset *attset;
578             
579             if (argc != 2)
580             {
581                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to attset",
582                      file, lineno);
583                 continue;
584             }
585             name = argv[1];
586             if (!(attset = data1_get_attset (dh, name)))
587             {
588                 yaz_log(LOG_WARN, "%s:%d: Couldn't find attset  %s",
589                      file, lineno, name);
590                 continue;
591             }
592             *attset_childp = (data1_attset_child *)
593                 nmem_malloc (data1_nmem_get(dh), sizeof(**attset_childp));
594             (*attset_childp)->child = attset;
595             (*attset_childp)->next = 0;
596             attset_childp = &(*attset_childp)->next;
597         }
598         else if (!strcmp(cmd, "tagset"))
599         {
600             char *name;
601             int type = 0;
602             if (argc < 2)
603             {
604                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to tagset",
605                      file, lineno);
606                 continue;
607             }
608             name = argv[1];
609             if (argc == 3)
610                 type = atoi(argv[2]);
611             *tagset_childp = data1_read_tagset (dh, name, type);
612             if (!(*tagset_childp))
613             {
614                 yaz_log(LOG_WARN, "%s:%d: Couldn't load tagset %s",
615                      file, lineno, name);
616                 continue;
617             }
618             tagset_childp = &(*tagset_childp)->next;
619         }
620         else if (!strcmp(cmd, "varset"))
621         {
622             char *name;
623
624             if (argc != 2)
625             {
626                 yaz_log(LOG_WARN, "%s:%d: Bad # of args in varset",
627                      file, lineno);
628                 continue;
629             }
630             name = argv[1];
631             if (!(res->varset = data1_read_varset (dh, name)))
632             {
633                 yaz_log(LOG_WARN, "%s:%d: Couldn't load Varset %s",
634                      file, lineno, name);
635                 continue;
636             }
637         }
638         else if (!strcmp(cmd, "esetname"))
639         {
640             char *name, *fname;
641
642             if (argc != 3)
643             {
644                 yaz_log(LOG_WARN, "%s:%d: Bad # of args in esetname",
645                      file, lineno);
646                 continue;
647             }
648             name = argv[1];
649             fname = argv[2];
650             
651             *esetpp = (data1_esetname *)
652                 nmem_malloc(data1_nmem_get(dh), sizeof(**esetpp));
653             (*esetpp)->name = nmem_strdup(data1_nmem_get(dh), name);
654             (*esetpp)->next = 0;
655             if (*fname == '@')
656                 (*esetpp)->spec = 0;
657             else if (!((*esetpp)->spec = data1_read_espec1 (dh, fname)))
658             {
659                 yaz_log(LOG_WARN, "%s:%d: Espec-1 read failed for %s",
660                      file, lineno, fname);
661                 continue;
662             }
663             esetpp = &(*esetpp)->next;
664         }
665         else if (!strcmp(cmd, "maptab"))
666         {
667             char *name;
668             
669             if (argc != 2)
670             {
671                 yaz_log(LOG_WARN, "%s:%d: Bad # of args for maptab",
672                      file, lineno);
673                 continue;
674             }
675             name = argv[1];
676             if (!(*maptabp = data1_read_maptab (dh, name)))
677             {
678                 yaz_log(LOG_WARN, "%s:%d: Couldn't load maptab %s",
679                      file, lineno, name);
680                 continue;
681             }
682             maptabp = &(*maptabp)->next;
683         }
684         else if (!strcmp(cmd, "marc"))
685         {
686             char *name;
687             
688             if (argc != 2)
689             {
690                 yaz_log(LOG_WARN, "%s:%d: Bad # or args for marc",
691                      file, lineno);
692                 continue;
693             }
694             name = argv[1];
695             if (!(*marcp = data1_read_marctab (dh, name)))
696             {
697                 yaz_log(LOG_WARN, "%s:%d: Couldn't read marctab %s",
698                      file, lineno, name);
699                 continue;
700             }
701             marcp = &(*marcp)->next;
702         }
703         else if (!strcmp(cmd, "encoding"))
704         {
705             if (argc != 2)
706             {
707                 yaz_log(LOG_WARN, "%s:%d: Bad # or args for encoding",
708                      file, lineno);
709                 continue;
710             }
711             res->encoding = nmem_strdup (data1_nmem_get(dh), argv[1]);
712         }
713         else if (!strcmp(cmd, "systag"))
714         {
715             struct data1_systag *st;
716             if (argc != 3)
717             {
718                 yaz_log(LOG_WARN, "%s:%d: Bad # or args for systag",
719                      file, lineno);
720                 continue;
721             }
722             *systagsp = nmem_malloc (data1_nmem_get(dh), sizeof(**systagsp));
723
724             (*systagsp)->name = nmem_strdup(data1_nmem_get(dh), argv[1]);
725             (*systagsp)->value = nmem_strdup(data1_nmem_get(dh), argv[2]);
726             systagsp = &(*systagsp)->next;
727         }
728         else
729         {
730             yaz_log(LOG_WARN, "%s:%d: Unknown directive '%s'", file, 
731                     lineno, cmd);
732             continue;
733         }
734     }
735     if (f)
736         fclose(f);
737     
738     for (cur_elements = res->sub_elements; cur_elements;
739          cur_elements = cur_elements->next)
740     {
741         if (!strcmp (cur_elements->name, "main"))
742             res->main_elements = cur_elements->elements;
743         fix_element_ref (dh, res, cur_elements->elements);
744     }
745     *systagsp = 0;
746     yaz_log (LOG_DEBUG, "%s: data1_read_absyn end", file);
747     return res;
748 }