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