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