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