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