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