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