Implemented sub-trees feature for schemas - including forward
[yaz-moved-to-github.git] / retrieval / d1_absyn.c
1 /*
2  * Copyright (c) 1995-1997, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: d1_absyn.c,v $
7  * Revision 1.16  1997-12-18 10:51:30  adam
8  * Implemented sub-trees feature for schemas - including forward
9  * references.
10  *
11  * Revision 1.15  1997/12/09 16:18:16  adam
12  * Work on EXPLAIN schema. First implementation of sub-schema facility
13  * in the *.abs files.
14  *
15  * Revision 1.14  1997/10/31 12:20:09  adam
16  * Improved memory debugging for xmalloc/nmem.c. References to NMEM
17  * instead of ODR in n ESPEC-1 handling in source d1_espec.c.
18  * Bug fix: missing fclose in data1_read_espec1.
19  *
20  * Revision 1.13  1997/10/27 13:54:18  adam
21  * Changed structure field in data1 node to be simple string which
22  * is "unknown" to the retrieval system itself.
23  *
24  * Revision 1.12  1997/09/17 12:10:34  adam
25  * YAZ version 1.4.
26  *
27  * Revision 1.11  1997/09/05 09:50:55  adam
28  * Removed global data1_tabpath - uses data1_get_tabpath() instead.
29  *
30  * Revision 1.10  1997/05/14 06:54:01  adam
31  * C++ support.
32  *
33  * Revision 1.9  1997/02/19 14:46:15  adam
34  * The "all" specifier only affects elements that are indexed (and not
35  * all elements).
36  *
37  * Revision 1.8  1997/01/02 10:47:59  quinn
38  * Added optional, physical ANY
39  *
40  * Revision 1.7  1996/06/10 08:56:01  quinn
41  * Work on Summary.
42  *
43  * Revision 1.6  1996/05/31  13:52:21  quinn
44  * Fixed uninitialized variable for local tags in abstract syntax.
45  *
46  * Revision 1.5  1996/05/09  07:27:43  quinn
47  * Multiple local attributes values supported.
48  *
49  * Revision 1.4  1996/05/01  12:45:28  quinn
50  * Support use of local tag names in abs file.
51  *
52  * Revision 1.3  1995/11/01  16:34:55  quinn
53  * Making data1 look for tables in data1_tabpath
54  *
55  * Revision 1.2  1995/11/01  13:54:44  quinn
56  * Minor adjustments
57  *
58  * Revision 1.1  1995/11/01  11:56:06  quinn
59  * Added Retrieval (data management) functions en masse.
60  *
61  *
62  */
63
64 #include <ctype.h>
65 #include <stdio.h>
66 #include <assert.h>
67 #include <stdlib.h>
68 #include <string.h>
69
70 #include <oid.h>
71 #include <log.h>
72 #include <tpath.h>
73
74 #include <data1.h>
75
76 #define D1_MAX_NESTING  128
77
78 struct data1_absyn_cache_info 
79 {
80     char *name;
81     data1_absyn *absyn;
82     data1_absyn_cache next;
83 };
84
85 data1_absyn *data1_absyn_search (data1_handle dh, const char *name)
86 {
87     data1_absyn_cache p = *data1_absyn_cache_get (dh);
88
89     while (p)
90     {
91         if (!strcmp (name, p->name))
92             return p->absyn;
93         p = p->next;
94     }
95     return NULL;
96 }
97
98 data1_absyn *data1_absyn_add (data1_handle dh, const char *name)
99 {
100     char fname[512];
101     NMEM mem = data1_nmem_get (dh);
102
103     data1_absyn_cache p = nmem_malloc (mem, sizeof(*p));
104     data1_absyn_cache *pp = data1_absyn_cache_get (dh);
105
106     sprintf(fname, "%s.abs", name);
107     p->absyn = data1_read_absyn (dh, fname);
108     p->name = nmem_strdup (mem, name);
109     p->next = *pp;
110     *pp = p;
111     return p->absyn;
112 }
113
114 data1_absyn *data1_get_absyn (data1_handle dh, char *name)
115 {
116     data1_absyn *absyn;
117
118     if (!(absyn = data1_absyn_search (dh, name)))
119         absyn = data1_absyn_add (dh, name);
120     return absyn;
121 }
122
123 data1_esetname *data1_getesetbyname(data1_handle dh, data1_absyn *a,
124                                     char *name)
125 {
126     data1_esetname *r;
127
128     for (r = a->esetnames; r; r = r->next)
129         if (!data1_matchstr(r->name, name))
130             return r;
131     return 0;
132 }
133
134 data1_element *data1_getelementbytagname (data1_handle dh, data1_absyn *abs,
135                                           data1_element *parent,
136                                           char *tagname)
137 {
138     data1_element *r;
139
140     if (!parent)
141         r = abs->main_elements;
142     else
143         r = parent->children;
144     assert (abs->main_elements);
145     for (; r; r = r->next)
146     {
147         data1_name *n;
148
149         for (n = r->tag->names; n; n = n->next)
150             if (!data1_matchstr(tagname, n->name))
151                 return r;
152     }
153     return 0;
154 }
155
156 data1_element *data1_getelementbyname (data1_handle dh, data1_absyn *absyn,
157                                        char *name)
158 {
159     data1_element *r;
160     assert (absyn->main_elements);
161     for (r = absyn->main_elements; r; r = r->next)
162         if (!data1_matchstr(r->name, name))
163             return r;
164     return 0;
165 }
166
167
168 void fix_element_ref (data1_handle dh, data1_absyn *absyn, data1_element *e)
169 {
170     for (; e; e = e->next)
171     {
172         if (!e->sub_name)
173         {
174             if (e->children)
175                 fix_element_ref (dh, absyn, e->children);
176         }
177         else
178         {
179             data1_sub_elements *sub_e = absyn->sub_elements;
180             while (sub_e && strcmp (e->sub_name, sub_e->name))
181                 sub_e = sub_e->next;
182             if (sub_e)
183                 e->children = sub_e->elements;
184             else
185                 logf (LOG_WARN, "Unresolved reference to sub-elements %s",
186                       e->sub_name);
187         }
188     }
189 }
190
191 data1_absyn *data1_read_absyn (data1_handle dh, const char *file)
192 {
193     char line[512], *r, cmd[512], args[512];
194     data1_sub_elements *cur_elements = NULL;
195     data1_absyn *res = 0;
196     FILE *f;
197     data1_element **ppl[D1_MAX_NESTING];
198     data1_esetname **esetpp;
199     data1_maptab **maptabp;
200     data1_marctab **marcp;
201     data1_termlist *all = 0;
202     int level;
203
204     logf (LOG_DEBUG, "begin data1_read_absyn file=%s", file);
205     if (!(f = yaz_path_fopen(data1_get_tabpath (dh), file, "r")))
206     {
207         logf(LOG_WARN|LOG_ERRNO, "Couldn't open %s", file);
208         return 0;
209     }
210
211     res = nmem_malloc(data1_nmem_get(dh), sizeof(*res));
212     res->name = 0;
213     res->reference = VAL_NONE;
214     res->tagset = 0;
215     res->attset = 0;
216     res->varset = 0;
217     res->esetnames = 0;
218     esetpp = &res->esetnames;
219     res->maptabs = 0;
220     maptabp = &res->maptabs;
221     res->marc = 0;
222     marcp = &res->marc;
223
224     res->sub_elements = NULL;
225     res->main_elements = NULL;
226
227     for (;;)
228     {
229         while ((r = fgets(line, 512, f)))
230         {
231             while (*r && isspace(*r))
232                 r++;
233             if (*r && *r != '#')
234                 break;
235         }
236         if (!r)
237             break;
238         if (sscanf(r, "%s %[^\n]", cmd, args) < 2)
239             *args = '\0';
240         if (!strcmp(cmd, "elm"))
241         {
242             data1_element *new_element;
243             int i;
244             char path[512], name[512], termlists[512], *p, *sub_p;
245             int type, value;
246             data1_termlist **tp;
247
248             if (!cur_elements)
249             {
250                 cur_elements = nmem_malloc(data1_nmem_get(dh),
251                                            sizeof(*cur_elements));
252                 cur_elements->next = res->sub_elements;
253                 cur_elements->elements = NULL;
254                 cur_elements->name = "main";
255                 res->sub_elements = cur_elements;
256
257                 level = 0;
258                 ppl[level] = &cur_elements->elements;
259             }
260             if (sscanf(args, "%511s %511s %511s", path, name, termlists) < 3)
261             {
262                 logf(LOG_WARN, "Bad # of args to elm in %s: '%s'", 
263                     file, args);
264                 fclose(f);
265                 return 0;
266             }
267             p = path;
268             for (i = 0;; i++)
269             {
270                 char *e;
271
272                 if ((e = strchr(p, '/')))
273                     p = e+1;
274                 else
275                     break;
276             }
277             if (i > level + 1)
278             {
279                 logf(LOG_WARN, "Bad level inc in %s in '%s'", file, args);
280                 fclose(f);
281                 return 0;
282             }
283             level = i;
284             new_element = *ppl[level] =
285                 nmem_malloc(data1_nmem_get(dh), sizeof(*new_element));
286             new_element->next = new_element->children = 0;
287             new_element->tag = 0;
288             new_element->termlists = 0;
289             new_element->sub_name = 0;
290
291             tp = &new_element->termlists;
292             ppl[level] = &new_element->next;
293             ppl[level+1] = &new_element->children;
294
295             /* consider subtree (if any) ... */
296             if ((sub_p = strchr (p, ':')) && sub_p[1])
297             {
298                 *sub_p++ = '\0';
299                 new_element->sub_name =
300                     nmem_strdup (data1_nmem_get(dh), sub_p);            
301             }
302             /* well-defined tag */
303             if (sscanf(p, "(%d,%d)", &type, &value) == 2)
304             {
305                 if (!res->tagset)
306                 {
307                     logf(LOG_WARN, "No tagset loaded in %s", file);
308                     fclose(f);
309                     return 0;
310                 }
311                 if (!(new_element->tag = data1_gettagbynum (dh, res->tagset,
312                                                             type, value)))
313                 {
314                     logf(LOG_WARN, "Couldn't find tag %s in tagset in %s",
315                         p, file);
316                     fclose(f);
317                     return 0;
318                 }
319             }
320             /* private tag */
321             else if (*p)
322             {
323                 data1_tag *nt =
324                     new_element->tag = nmem_malloc(data1_nmem_get (dh),
325                                                    sizeof(*new_element->tag));
326                 nt->which = DATA1T_string;
327                 nt->value.string = nmem_strdup(data1_nmem_get (dh), p);
328                 nt->names = nmem_malloc(data1_nmem_get(dh), 
329                                         sizeof(*new_element->tag->names));
330                 nt->names->name = nt->value.string;
331                 nt->names->next = 0;
332                 nt->kind = DATA1K_string;
333                 nt->next = 0;
334                 nt->tagset = 0;
335             }
336             else
337             {
338                 logf(LOG_WARN, "Bad element is %s", file);
339                 fclose(f);
340                 return 0;
341             }
342             /* parse termList definitions */
343             p = termlists;
344             if (*p == '-')
345                 new_element->termlists = 0;
346             else
347             {
348                 if (!res->attset)
349                 {
350                     logf(LOG_WARN, "No attset loaded in %s", file);
351                     fclose(f);
352                     return 0;
353                 }
354                 do
355                 {
356                     char attname[512], structure[512];
357                     int r;
358
359                     if (!(r = sscanf(p, "%511[^:,]:%511[^,]", attname,
360                         structure)))
361                     {
362                         logf(LOG_WARN, "Syntax error in termlistspec in %s",
363                             file);
364                         fclose(f);
365                         return 0;
366                     }
367                     if (*attname == '!')
368                         strcpy(attname, name);
369                     *tp = nmem_malloc(data1_nmem_get(dh), sizeof(**tp));
370                     (*tp)->next = 0;
371                     if (!((*tp)->att = data1_getattbyname(dh, res->attset,
372                                                           attname)))
373                     {
374                         logf(LOG_WARN, "Couldn't find att '%s' in attset",
375                              attname);
376                         fclose(f);
377                         return 0;
378                     }
379                     if (r < 2) /* is the structure qualified? */
380                         (*tp)->structure = "w";
381                     else 
382                     {
383                         (*tp)->structure = nmem_malloc (data1_nmem_get (dh),
384                                                         strlen(structure)+1);
385                         strcpy ((*tp)->structure, structure);
386                     }
387                     tp = &(*tp)->next;
388                 }
389                 while ((p = strchr(p, ',')) && *(++p));
390                 *tp = all; /* append any ALL entries to the list */
391             }
392             new_element->name = nmem_strdup(data1_nmem_get (dh), name);
393         }
394         else if (!strcmp(cmd, "section"))
395         {
396             char name[512];
397             if (sscanf(args, "%511s", name) < 1)
398             {
399                 logf(LOG_WARN, "Bad # of args to sub in %s: '%s'",
400                                 file, args);
401                 continue;
402             }
403             cur_elements = nmem_malloc(data1_nmem_get(dh),
404                                            sizeof(*cur_elements));
405             cur_elements->next = res->sub_elements;
406             cur_elements->elements = NULL;
407             cur_elements->name = nmem_strdup (data1_nmem_get(dh), name);
408             res->sub_elements = cur_elements;
409
410             level = 0;
411             ppl[level] = &cur_elements->elements;
412         }
413         else if (!strcmp(cmd, "all"))
414         {
415             char *p;
416             data1_termlist **tp = &all;
417
418             if (all)
419             {
420                 logf(LOG_WARN, "Too many ALL declarations in %s - ignored",
421                     file);
422                 continue;
423             }
424
425             p = args;
426             if (!res->attset)
427             {
428                 logf(LOG_WARN, "No attset loaded in %s", file);
429                 fclose(f);
430                 return 0;
431             }
432             do
433             {
434                 char attname[512], structure[512];
435                 int r;
436
437                 if (!(r = sscanf(p, "%511[^:,]:%511[^,]", attname,
438                     structure)))
439                 {
440                     logf(LOG_WARN, "Syntax error in termlistspec in %s",
441                         file);
442                     fclose(f);
443                     return 0;
444                 }
445                 *tp = nmem_malloc(data1_nmem_get(dh), sizeof(**tp));
446                 if (!((*tp)->att = data1_getattbyname (dh, res->attset,
447                                                        attname)))
448                 {
449                     logf(LOG_WARN, "Couldn't find att '%s' in attset",
450                          attname);
451                     fclose(f);
452                     return 0;
453                 }
454                 if (r < 2) /* is the structure qualified? */
455                     (*tp)->structure = "w";
456                 else 
457                 {
458                     (*tp)->structure = nmem_malloc (data1_nmem_get (dh),
459                                                     strlen(structure)+1);
460                     strcpy ((*tp)->structure, structure);
461                 }
462                 (*tp)->next = 0;
463                 tp = &(*tp)->next;
464             }
465             while ((p = strchr(p, ',')) && *(++p));
466         }
467         else if (!strcmp(cmd, "name"))
468         {
469             char name[512];
470
471             if (!sscanf(args, "%511s", name))
472             {
473                 logf(LOG_WARN, "Malformed name directive in %s", file);
474                 fclose(f);
475                 return 0;
476             }
477             res->name = nmem_strdup(data1_nmem_get(dh), name);
478         }
479         else if (!strcmp(cmd, "reference"))
480         {
481             char name[512];
482
483             if (!sscanf(args, "%s", name))
484             {
485                 logf(LOG_WARN, "Malformed reference in %s", file);
486                 fclose(f);
487                 return 0;
488             }
489             if ((res->reference = oid_getvalbyname(name)) == VAL_NONE)
490             {
491                 logf(LOG_WARN, "Unknown tagset ref '%s' in %s", name, file);
492                 fclose(f);
493                 return 0;
494             }
495         }
496         else if (!strcmp(cmd, "attset"))
497         {
498             char name[512];
499
500             if (!sscanf(args, "%s", name))
501             {
502                 logf(LOG_WARN, "Malformed attset directive in %s", file);
503                 fclose(f);
504                 return 0;
505             }
506             if (!(res->attset = data1_read_attset (dh, name)))
507             {
508                 logf(LOG_WARN, "Attset failed in %s", file);
509                 fclose(f);
510                 return 0;
511             }
512         }
513         else if (!strcmp(cmd, "tagset"))
514         {
515             char name[512];
516
517             if (!sscanf(args, "%s", name))
518             {
519                 logf(LOG_WARN, "Malformed tagset directive in %s", file);
520                 fclose(f);
521                 return 0;
522             }
523             if (!(res->tagset = data1_read_tagset (dh, name)))
524             {
525                 logf(LOG_WARN, "Tagset failed in %s", file);
526                 fclose(f);
527                 return 0;
528             }
529         }
530         else if (!strcmp(cmd, "varset"))
531         {
532             char name[512];
533
534             if (!sscanf(args, "%s", name))
535             {
536                 logf(LOG_WARN, "Malformed varset directive in %s", file);
537                 fclose(f);
538                 return 0;
539             }
540             if (!(res->varset = data1_read_varset (dh, name)))
541             {
542                 logf(LOG_WARN, "Varset failed in %s", file);
543                 fclose(f);
544                 return 0;
545             }
546         }
547         else if (!strcmp(cmd, "esetname"))
548         {
549             char name[512], fname[512];
550
551             if (sscanf(args, "%s %s", name, fname) != 2)
552             {
553                 logf(LOG_WARN, "Two arg's required for esetname in %s",
554                      file);
555                 fclose(f);
556                 return 0;
557             }
558             *esetpp = nmem_malloc(data1_nmem_get(dh), sizeof(**esetpp));
559             (*esetpp)->name = nmem_strdup(data1_nmem_get(dh), name);
560             (*esetpp)->next = 0;
561             if (*fname == '@')
562                 (*esetpp)->spec = 0;
563             else if (!((*esetpp)->spec = data1_read_espec1 (dh, fname)))
564             {
565                 logf(LOG_WARN, "%s: Espec-1 read failed", file);
566                 fclose(f);
567                 return 0;
568             }
569             esetpp = &(*esetpp)->next;
570         }
571         else if (!strcmp(cmd, "maptab"))
572         {
573             char name[512];
574
575             if (sscanf(args, "%s", name) != 1)
576             {
577                 logf(LOG_WARN, "One argument for maptab directive in %s",
578                      file);
579                 continue;
580             }
581             if (!(*maptabp = data1_read_maptab (dh, name)))
582             {
583                 logf(LOG_WARN, "Failed to read maptab %s in %s",
584                      name, file);
585                 continue;
586             }
587             maptabp = &(*maptabp)->next;
588         }
589         else if (!strcmp(cmd, "marc"))
590         {
591             char name[512];
592
593             if (sscanf(args, "%s", name) != 1)
594             {
595                 logf(LOG_WARN, "One argument for marc directive in %s",
596                     file);
597                 continue;
598             }
599             if (!(*marcp = data1_read_marctab (dh, name)))
600             {
601                 logf(LOG_WARN, "%Failed to read marctab %s in %s",
602                      name, file);
603                 continue;
604             }
605             marcp = &(*marcp)->next;
606         }
607         else
608         {
609             logf(LOG_WARN, "Unknown directive '%s' in %s", cmd, file);
610             fclose(f);
611             return 0;
612         }
613     }
614     fclose(f);
615     
616     for (cur_elements = res->sub_elements; cur_elements;
617          cur_elements = cur_elements->next)
618     {
619         if (!strcmp (cur_elements->name, "main"))
620             res->main_elements = cur_elements->elements;
621         fix_element_ref (dh, res, cur_elements->elements);
622     }
623     logf (LOG_DEBUG, "end data1_read_absyn file=%s", file);
624     return res;
625 }