Tag set type may be specified in reference to it using "tagset"
[yaz-moved-to-github.git] / retrieval / d1_absyn.c
1 /*
2  * Copyright (c) 1995-1998, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: d1_absyn.c,v $
7  * Revision 1.23  1998-10-15 08:29:16  adam
8  * Tag set type may be specified in reference to it using "tagset"
9  * directive in .abs-files and "include" directive in .tag-files.
10  *
11  * Revision 1.22  1998/10/13 16:09:47  adam
12  * Added support for arbitrary OID's for tagsets, schemas and attribute sets.
13  * Added support for multiple attribute set references and tagset references
14  * from an abstract syntax file.
15  * Fixed many bad logs-calls in routines that read the various
16  * specifications regarding data1 (*.abs,*.att,...) and made the messages
17  * consistent whenever possible.
18  * Added extra 'lineno' argument to function readconf_line.
19  *
20  * Revision 1.21  1998/06/09 13:55:07  adam
21  * Minor changes.
22  *
23  * Revision 1.20  1998/05/18 13:07:02  adam
24  * Changed the way attribute sets are handled by the retriaval module.
25  * Extended Explain conversion / schema.
26  * Modified server and client to work with ASN.1 compiled protocol handlers.
27  *
28  * Revision 1.19  1998/03/05 08:15:32  adam
29  * Implemented data1_add_insert_taggeddata utility which is more flexible
30  * than data1_insert_taggeddata.
31  *
32  * Revision 1.18  1998/02/27 14:08:04  adam
33  * Added const to some char pointer arguments.
34  * Reworked data1_read_node so that it doesn't create a tree with
35  * pointers to original "SGML"-buffer.
36  *
37  * Revision 1.17  1998/02/11 11:53:34  adam
38  * Changed code so that it compiles as C++.
39  *
40  * Revision 1.16  1997/12/18 10:51:30  adam
41  * Implemented sub-trees feature for schemas - including forward
42  * references.
43  *
44  * Revision 1.15  1997/12/09 16:18:16  adam
45  * Work on EXPLAIN schema. First implementation of sub-schema facility
46  * in the *.abs files.
47  *
48  * Revision 1.14  1997/10/31 12:20:09  adam
49  * Improved memory debugging for xmalloc/nmem.c. References to NMEM
50  * instead of ODR in n ESPEC-1 handling in source d1_espec.c.
51  * Bug fix: missing fclose in data1_read_espec1.
52  *
53  * Revision 1.13  1997/10/27 13:54:18  adam
54  * Changed structure field in data1 node to be simple string which
55  * is "unknown" to the retrieval system itself.
56  *
57  * Revision 1.12  1997/09/17 12:10:34  adam
58  * YAZ version 1.4.
59  *
60  * Revision 1.11  1997/09/05 09:50:55  adam
61  * Removed global data1_tabpath - uses data1_get_tabpath() instead.
62  *
63  * Revision 1.10  1997/05/14 06:54:01  adam
64  * C++ support.
65  *
66  * Revision 1.9  1997/02/19 14:46:15  adam
67  * The "all" specifier only affects elements that are indexed (and not
68  * all elements).
69  *
70  * Revision 1.8  1997/01/02 10:47:59  quinn
71  * Added optional, physical ANY
72  *
73  * Revision 1.7  1996/06/10 08:56:01  quinn
74  * Work on Summary.
75  *
76  * Revision 1.6  1996/05/31  13:52:21  quinn
77  * Fixed uninitialized variable for local tags in abstract syntax.
78  *
79  * Revision 1.5  1996/05/09  07:27:43  quinn
80  * Multiple local attributes values supported.
81  *
82  * Revision 1.4  1996/05/01  12:45:28  quinn
83  * Support use of local tag names in abs file.
84  *
85  * Revision 1.3  1995/11/01  16:34:55  quinn
86  * Making data1 look for tables in data1_tabpath
87  *
88  * Revision 1.2  1995/11/01  13:54:44  quinn
89  * Minor adjustments
90  *
91  * Revision 1.1  1995/11/01  11:56:06  quinn
92  * Added Retrieval (data management) functions en masse.
93  *
94  */
95
96 #include <ctype.h>
97 #include <stdio.h>
98 #include <assert.h>
99 #include <stdlib.h>
100 #include <string.h>
101
102 #include <oid.h>
103 #include <log.h>
104 #include <data1.h>
105
106 #define D1_MAX_NESTING  128
107
108 struct data1_absyn_cache_info 
109 {
110     char *name;
111     data1_absyn *absyn;
112     data1_absyn_cache next;
113 };
114
115 struct data1_attset_cache_info 
116 {
117     char *name;
118     data1_attset *attset;
119     data1_attset_cache next;
120 };
121
122 data1_absyn *data1_absyn_search (data1_handle dh, const char *name)
123 {
124     data1_absyn_cache p = *data1_absyn_cache_get (dh);
125
126     while (p)
127     {
128         if (!strcmp (name, p->name))
129             return p->absyn;
130         p = p->next;
131     }
132     return NULL;
133 }
134
135 void data1_absyn_trav (data1_handle dh, void *handle,
136                        void (*fh)(data1_handle dh, void *h, data1_absyn *a))
137 {
138     data1_absyn_cache p = *data1_absyn_cache_get (dh);
139
140     while (p)
141     {
142         (*fh)(dh, handle, p->absyn);
143         p = p->next;
144     }
145 }
146
147 data1_absyn *data1_absyn_add (data1_handle dh, const char *name)
148 {
149     char fname[512];
150     NMEM mem = data1_nmem_get (dh);
151
152     data1_absyn_cache p = (data1_absyn_cache)nmem_malloc (mem, sizeof(*p));
153     data1_absyn_cache *pp = data1_absyn_cache_get (dh);
154
155     sprintf(fname, "%s.abs", name);
156     p->absyn = data1_read_absyn (dh, fname);
157     p->name = nmem_strdup (mem, name);
158     p->next = *pp;
159     *pp = p;
160     return p->absyn;
161 }
162
163 data1_absyn *data1_get_absyn (data1_handle dh, const char *name)
164 {
165     data1_absyn *absyn;
166
167     if (!(absyn = data1_absyn_search (dh, name)))
168         absyn = data1_absyn_add (dh, name);
169     return absyn;
170 }
171
172 data1_attset *data1_attset_search_name (data1_handle dh, const char *name)
173 {
174     data1_attset_cache p = *data1_attset_cache_get (dh);
175
176     while (p)
177     {
178         if (!strcmp (name, p->name))
179             return p->attset;
180         p = p->next;
181     }
182     return NULL;
183 }
184
185 data1_attset *data1_attset_search_id (data1_handle dh, int id)
186 {
187     data1_attset_cache p = *data1_attset_cache_get (dh);
188
189     while (p)
190     {
191         if (id == p->attset->reference)
192             return p->attset;
193         p = p->next;
194     }
195     return NULL;
196 }
197
198 data1_attset *data1_attset_add (data1_handle dh, const char *name)
199 {
200     char fname[512], aname[512];
201     NMEM mem = data1_nmem_get (dh);
202     data1_attset *attset;
203
204     strcpy (aname, name);
205     sprintf(fname, "%s.att", name);
206     attset = data1_read_attset (dh, fname);
207     if (!attset)
208     {
209         char *cp;
210         attset = data1_read_attset (dh, name);
211         if (attset && (cp = strrchr (aname, '.')))
212             *cp = '\0';
213     }
214     if (!attset)
215         logf (LOG_WARN|LOG_ERRNO, "Couldn't load attribute set %s", name);
216     else
217     {
218         data1_attset_cache p = (data1_attset_cache)
219             nmem_malloc (mem, sizeof(*p));
220         data1_attset_cache *pp = data1_attset_cache_get (dh);
221         
222         attset->name = p->name = nmem_strdup (mem, aname);
223         p->attset = attset;
224         p->next = *pp;
225         *pp = p;
226     }
227     return attset;
228 }
229
230 data1_attset *data1_get_attset (data1_handle dh, const char *name)
231 {
232     data1_attset *attset;
233
234     if (!(attset = data1_attset_search_name (dh, name)))
235         attset = data1_attset_add (dh, name);
236     return attset;
237 }
238
239 data1_esetname *data1_getesetbyname(data1_handle dh, data1_absyn *a,
240                                     const char *name)
241 {
242     data1_esetname *r;
243
244     for (r = a->esetnames; r; r = r->next)
245         if (!data1_matchstr(r->name, name))
246             return r;
247     return 0;
248 }
249
250 data1_element *data1_getelementbytagname (data1_handle dh, data1_absyn *abs,
251                                           data1_element *parent,
252                                           const char *tagname)
253 {
254     data1_element *r;
255
256     if (!parent)
257         r = abs->main_elements;
258     else
259         r = parent->children;
260     assert (abs->main_elements);
261     for (; r; r = r->next)
262     {
263         data1_name *n;
264
265         for (n = r->tag->names; n; n = n->next)
266             if (!data1_matchstr(tagname, n->name))
267                 return r;
268     }
269     return 0;
270 }
271
272 data1_element *data1_getelementbyname (data1_handle dh, data1_absyn *absyn,
273                                        const char *name)
274 {
275     data1_element *r;
276     assert (absyn->main_elements);
277     for (r = absyn->main_elements; r; r = r->next)
278         if (!data1_matchstr(r->name, name))
279             return r;
280     return 0;
281 }
282
283
284 void fix_element_ref (data1_handle dh, data1_absyn *absyn, data1_element *e)
285 {
286     for (; e; e = e->next)
287     {
288         if (!e->sub_name)
289         {
290             if (e->children)
291                 fix_element_ref (dh, absyn, e->children);
292         }
293         else
294         {
295             data1_sub_elements *sub_e = absyn->sub_elements;
296             while (sub_e && strcmp (e->sub_name, sub_e->name))
297                 sub_e = sub_e->next;
298             if (sub_e)
299                 e->children = sub_e->elements;
300             else
301                 logf (LOG_WARN, "Unresolved reference to sub-elements %s",
302                       e->sub_name);
303         }
304     }
305 }
306
307 data1_absyn *data1_read_absyn (data1_handle dh, const char *file)
308 {
309     data1_sub_elements *cur_elements = NULL;
310     data1_absyn *res = 0;
311     FILE *f;
312     data1_element **ppl[D1_MAX_NESTING];
313     data1_esetname **esetpp;
314     data1_maptab **maptabp;
315     data1_marctab **marcp;
316     data1_termlist *all = 0;
317     data1_attset_child **attset_childp;
318     data1_tagset **tagset_childp;
319     int level = 0;
320     int lineno = 0;
321     int argc;
322     char *argv[50], line[512];
323
324     if (!(f = yaz_path_fopen(data1_get_tabpath (dh), file, "r")))
325     {
326         logf(LOG_WARN|LOG_ERRNO, "Couldn't open %s", file);
327         return 0;
328     }
329     
330     res = (data1_absyn *) nmem_malloc(data1_nmem_get(dh), sizeof(*res));
331     res->name = 0;
332     res->reference = VAL_NONE;
333     res->tagset = 0;
334     tagset_childp = &res->tagset;
335
336     res->attset = data1_empty_attset (dh);
337     attset_childp =  &res->attset->children;
338
339     res->varset = 0;
340     res->esetnames = 0;
341     esetpp = &res->esetnames;
342     res->maptabs = 0;
343     maptabp = &res->maptabs;
344     res->marc = 0;
345     marcp = &res->marc;
346
347     res->sub_elements = NULL;
348     res->main_elements = NULL;
349
350     while ((argc = readconf_line(f, &lineno, line, 512, argv, 50)))
351     {
352         char *cmd = *argv;
353         if (!strcmp(cmd, "elm"))
354         {
355             data1_element *new_element;
356             int i;
357             char *p, *sub_p, *path, *name, *termlists;
358             int type, value;
359             data1_termlist **tp;
360
361             if (argc < 4)
362             {
363                 logf(LOG_WARN, "%s:%d: Bad # of args to elm", file, lineno);
364                 continue;
365             }
366             path = argv[1];
367             name = argv[2];
368             termlists = argv[3];
369
370             if (!cur_elements)
371             {
372                 cur_elements = (data1_sub_elements *)
373                     nmem_malloc(data1_nmem_get(dh), sizeof(*cur_elements));
374                 cur_elements->next = res->sub_elements;
375                 cur_elements->elements = NULL;
376                 cur_elements->name = "main";
377                 res->sub_elements = cur_elements;
378                 
379                 level = 0;
380                 ppl[level] = &cur_elements->elements;
381             }
382             p = path;
383             for (i = 0;; i++)
384             {
385                 char *e;
386
387                 if ((e = strchr(p, '/')))
388                     p = e+1;
389                 else
390                     break;
391             }
392             if (i > level + 1)
393             {
394                 logf(LOG_WARN, "%s:%d: Bad level increase", file, lineno);
395                 fclose(f);
396                 return 0;
397             }
398             level = i;
399             new_element = *ppl[level] = (data1_element *)
400                 nmem_malloc(data1_nmem_get(dh), sizeof(*new_element));
401             new_element->next = new_element->children = 0;
402             new_element->tag = 0;
403             new_element->termlists = 0;
404             new_element->sub_name = 0;
405             
406             tp = &new_element->termlists;
407             ppl[level] = &new_element->next;
408             ppl[level+1] = &new_element->children;
409             
410             /* consider subtree (if any) ... */
411             if ((sub_p = strchr (p, ':')) && sub_p[1])
412             {
413                 *sub_p++ = '\0';
414                 new_element->sub_name =
415                     nmem_strdup (data1_nmem_get(dh), sub_p);            
416             }
417             /* well-defined tag */
418             if (sscanf(p, "(%d,%d)", &type, &value) == 2)
419             {
420                 if (!res->tagset)
421                 {
422                     logf(LOG_WARN, "%s:%d: No tagset loaded", file, lineno);
423                     fclose(f);
424                     return 0;
425                 }
426                 if (!(new_element->tag = data1_gettagbynum (dh, res->tagset,
427                                                             type, value)))
428                 {
429                     logf(LOG_WARN, "%s:%d: Couldn't find tag %s in tagset",
430                          file, lineno, p);
431                     fclose(f);
432                     return 0;
433                 }
434             }
435             /* private tag */
436             else if (*p)
437             {
438                 data1_tag *nt =
439                     new_element->tag = (data1_tag *)
440                     nmem_malloc(data1_nmem_get (dh),
441                                 sizeof(*new_element->tag));
442                 nt->which = DATA1T_string;
443                 nt->value.string = nmem_strdup(data1_nmem_get (dh), p);
444                 nt->names = (data1_name *)
445                     nmem_malloc(data1_nmem_get(dh), 
446                                 sizeof(*new_element->tag->names));
447                 nt->names->name = nt->value.string;
448                 nt->names->next = 0;
449                 nt->kind = DATA1K_string;
450                 nt->next = 0;
451                 nt->tagset = 0;
452             }
453             else
454             {
455                 logf(LOG_WARN, "%s:%d: Bad element", file, lineno);
456                 fclose(f);
457                 return 0;
458             }
459             /* parse termList definitions */
460             p = termlists;
461             if (*p == '-')
462                 new_element->termlists = 0;
463             else
464             {
465                 assert (res->attset);
466                 do
467                 {
468                     char attname[512], structure[512];
469                     int r;
470                     
471                     if (!(r = sscanf(p, "%511[^:,]:%511[^,]", attname,
472                                      structure)))
473                     {
474                         logf(LOG_WARN,
475                              "%s:%d: Syntax error in termlistspec '%s'",
476                              file, lineno, p);
477                         fclose(f);
478                         return 0;
479                     }
480                     if (*attname == '!')
481                         strcpy(attname, name);
482                     *tp = (data1_termlist *)
483                         nmem_malloc(data1_nmem_get(dh), sizeof(**tp));
484                     (*tp)->next = 0;
485                     if (!((*tp)->att = data1_getattbyname(dh, res->attset,
486                                                           attname)))
487                     {
488                         logf(LOG_WARN,
489                              "%s:%d: Couldn't find att '%s' in attset",
490                              file, lineno, attname);
491                         fclose(f);
492                         return 0;
493                     }
494                     if (r < 2) /* is the structure qualified? */
495                         (*tp)->structure = "w";
496                     else 
497                     {
498                         (*tp)->structure = (char *)
499                             nmem_malloc (data1_nmem_get (dh),
500                                          strlen(structure)+1);
501                         strcpy ((*tp)->structure, structure);
502                     }
503                     tp = &(*tp)->next;
504                 }
505                 while ((p = strchr(p, ',')) && *(++p));
506                 *tp = all; /* append any ALL entries to the list */
507             }
508             new_element->name = nmem_strdup(data1_nmem_get (dh), name);
509         }
510         else if (!strcmp(cmd, "section"))
511         {
512             char *name;
513             
514             if (argc < 2)
515             {
516                 logf(LOG_WARN, "%s:%d: Bad # of args to section",
517                      file, lineno);
518                 continue;
519             }
520             name = argv[1];
521             
522             cur_elements = (data1_sub_elements *)
523                 nmem_malloc(data1_nmem_get(dh), sizeof(*cur_elements));
524             cur_elements->next = res->sub_elements;
525             cur_elements->elements = NULL;
526             cur_elements->name = nmem_strdup (data1_nmem_get(dh), name);
527             res->sub_elements = cur_elements;
528             
529             level = 0;
530             ppl[level] = &cur_elements->elements;
531         }
532         else if (!strcmp(cmd, "all"))
533         {
534             char *p;
535             data1_termlist **tp = &all;
536             
537             if (all)
538             {
539                 logf(LOG_WARN, "%s:%d: Too many 'all' directives - ignored",
540                      file, lineno);
541                 continue;
542             }
543
544             if (argc != 2)
545             {
546                 logf(LOG_WARN, "%s:%d: Bad # of args to 'all' directive",
547                      file, lineno);
548                 continue;
549             }
550             p = argv[1];
551             assert (res->attset);
552             do
553             {
554                 char attname[512], structure[512];
555                 int r;
556                 
557                 if (!(r = sscanf(p, "%511[^:,]:%511[^,]", attname,
558                                  structure)))
559                 {
560                     logf(LOG_WARN, "%s:%d: Syntax error in termlistspec",
561                          file, lineno);
562                     fclose(f);
563                     return 0;
564                 }
565                 *tp = (data1_termlist *)
566                     nmem_malloc(data1_nmem_get(dh), sizeof(**tp));
567                 if (!((*tp)->att =
568                       data1_getattbyname (dh, res->attset, attname)))
569                 {
570                     logf(LOG_WARN, "%s:%d: Couldn't find att '%s' in attset",
571                          file, lineno, attname);
572                     fclose(f);
573                     return 0;
574                 }
575                 if (r < 2) /* is the structure qualified? */
576                     (*tp)->structure = "w";
577                 else 
578                 {
579                     (*tp)->structure =
580                         (char *)nmem_malloc (data1_nmem_get (dh),
581                                              strlen(structure)+1);
582                     strcpy ((*tp)->structure, structure);
583                 }
584                 (*tp)->next = 0;
585                 tp = &(*tp)->next;
586             }
587             while ((p = strchr(p, ',')) && *(++p));
588         }
589         else if (!strcmp(cmd, "name"))
590         {
591             if (argc != 2)
592             {
593                 logf(LOG_WARN, "%s:%d: Bad # of args to name directive",
594                      file, lineno);
595                 continue;
596             }
597             res->name = nmem_strdup(data1_nmem_get(dh), argv[1]);
598         }
599         else if (!strcmp(cmd, "reference"))
600         {
601             char *name;
602             
603             if (argc != 2)
604             {
605                 logf(LOG_WARN, "%s:%d: Bad # of args to reference",
606                      file, lineno);
607                 continue;
608             }
609             name = argv[1];
610             if ((res->reference = oid_getvalbyname(name)) == VAL_NONE)
611             {
612                 logf(LOG_WARN, "%s:%d: Unknown tagset ref '%s'", 
613                      file, lineno, name);
614                 continue;
615             }
616         }
617         else if (!strcmp(cmd, "attset"))
618         {
619             char *name;
620             data1_attset *attset;
621             
622             if (argc != 2)
623             {
624                 logf(LOG_WARN, "%s:%d: Bad # of args to attset",
625                      file, lineno);
626                 continue;
627             }
628             name = argv[1];
629             if (!(attset = data1_get_attset (dh, name)))
630             {
631                 logf(LOG_WARN, "%s:%d: Couldn't find attset  %s",
632                      file, lineno, name);
633                 continue;
634             }
635             *attset_childp = (data1_attset_child *)
636                 nmem_malloc (data1_nmem_get(dh), sizeof(**attset_childp));
637             (*attset_childp)->child = attset;
638             (*attset_childp)->next = 0;
639             attset_childp = &(*attset_childp)->next;
640         }
641         else if (!strcmp(cmd, "tagset"))
642         {
643             char *name;
644             int type = 0;
645             if (argc < 2)
646             {
647                 logf(LOG_WARN, "%s:%d: Bad # of args to tagset",
648                      file, lineno);
649                 continue;
650             }
651             name = argv[1];
652             if (argc == 3)
653                 type = atoi(argv[2]);
654             *tagset_childp = data1_read_tagset (dh, name, type);
655             if (!(*tagset_childp))
656             {
657                 logf(LOG_WARN, "%s:%d: Couldn't load tagset %s",
658                      file, lineno, name);
659                 continue;
660             }
661             tagset_childp = &(*tagset_childp)->next;
662         }
663         else if (!strcmp(cmd, "varset"))
664         {
665             char *name;
666
667             if (argc != 2)
668             {
669                 logf(LOG_WARN, "%s:%d: Bad # of args in varset",
670                      file, lineno);
671                 continue;
672             }
673             name = argv[1];
674             if (!(res->varset = data1_read_varset (dh, name)))
675             {
676                 logf(LOG_WARN, "%s:%d: Couldn't load Varset %s",
677                      file, lineno, name);
678                 continue;
679             }
680         }
681         else if (!strcmp(cmd, "esetname"))
682         {
683             char *name, *fname;
684
685             if (argc != 3)
686             {
687                 logf(LOG_WARN, "%s:%d: Bad # of args in esetname",
688                      file, lineno);
689                 continue;
690             }
691             name = argv[1];
692             fname = argv[2];
693             
694             *esetpp = (data1_esetname *)
695                 nmem_malloc(data1_nmem_get(dh), sizeof(**esetpp));
696             (*esetpp)->name = nmem_strdup(data1_nmem_get(dh), name);
697             (*esetpp)->next = 0;
698             if (*fname == '@')
699                 (*esetpp)->spec = 0;
700             else if (!((*esetpp)->spec = data1_read_espec1 (dh, fname)))
701             {
702                 logf(LOG_WARN, "%s:%d: Espec-1 read failed for %s",
703                      file, lineno, fname);
704                 continue;
705             }
706             esetpp = &(*esetpp)->next;
707         }
708         else if (!strcmp(cmd, "maptab"))
709         {
710             char *name;
711             
712             if (argc != 2)
713             {
714                 logf(LOG_WARN, "%s:%d: Bad # of args for maptab",
715                      file, lineno);
716                 continue;
717             }
718             name = argv[1];
719             if (!(*maptabp = data1_read_maptab (dh, name)))
720             {
721                 logf(LOG_WARN, "%s:%d: Couldn't load maptab %s",
722                      file, lineno, name);
723                 continue;
724             }
725             maptabp = &(*maptabp)->next;
726         }
727         else if (!strcmp(cmd, "marc"))
728         {
729             char *name;
730             
731             if (argc != 2)
732             {
733                 logf(LOG_WARN, "%s:%d: Bad # or args for marc",
734                      file, lineno);
735                 continue;
736             }
737             name = argv[1];
738             if (!(*marcp = data1_read_marctab (dh, name)))
739             {
740                 logf(LOG_WARN, "%s:%d: Couldn't read marctab %s",
741                      file, lineno, name);
742                 continue;
743             }
744             marcp = &(*marcp)->next;
745         }
746         else
747         {
748             logf(LOG_WARN, "%s:%d: Unknown directive '%s'", file, lineno, cmd);
749             continue;
750         }
751     }
752     fclose(f);
753     
754     for (cur_elements = res->sub_elements; cur_elements;
755          cur_elements = cur_elements->next)
756     {
757         if (!strcmp (cur_elements->name, "main"))
758             res->main_elements = cur_elements->elements;
759         fix_element_ref (dh, res, cur_elements->elements);
760     }
761     logf (LOG_DEBUG, "%s: data1_read_absyn end", file);
762     return res;
763 }