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