Fixed problem with element level in reading of abstract syntax.
[yaz-moved-to-github.git] / retrieval / d1_absyn.c
1 /*
2  * Copyright (c) 1995-2000, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: d1_absyn.c,v $
7  * Revision 1.30  2000-12-05 19:07:24  adam
8  * Fixed problem with element level in reading of abstract syntax.
9  *
10  * Revision 1.29  2000/12/05 14:34:49  adam
11  * Fixed bug with termlists (introduced by previous commit).
12  *
13  * Revision 1.28  2000/12/05 12:21:45  adam
14  * Added termlist source for data1 system.
15  *
16  * Revision 1.27  1999/12/21 14:16:19  ian
17  * Changed retrieval module to allow data1 trees with no associated absyn.
18  * Also added a simple interface for extracting values from data1 trees using
19  * a string based tagpath.
20  *
21  * Revision 1.26  1999/11/30 13:47:12  adam
22  * Improved installation. Moved header files to include/yaz.
23  *
24  * Revision 1.25  1999/10/21 12:06:29  adam
25  * Retrieval module no longer uses ctype.h - functions.
26  *
27  * Revision 1.24  1999/08/27 09:40:32  adam
28  * Renamed logf function to yaz_log. Removed VC++ project files.
29  *
30  * Revision 1.23  1998/10/15 08:29:16  adam
31  * Tag set type may be specified in reference to it using "tagset"
32  * directive in .abs-files and "include" directive in .tag-files.
33  *
34  * Revision 1.22  1998/10/13 16:09:47  adam
35  * Added support for arbitrary OID's for tagsets, schemas and attribute sets.
36  * Added support for multiple attribute set references and tagset references
37  * from an abstract syntax file.
38  * Fixed many bad logs-calls in routines that read the various
39  * specifications regarding data1 (*.abs,*.att,...) and made the messages
40  * consistent whenever possible.
41  * Added extra 'lineno' argument to function readconf_line.
42  *
43  * Revision 1.21  1998/06/09 13:55:07  adam
44  * Minor changes.
45  *
46  * Revision 1.20  1998/05/18 13:07:02  adam
47  * Changed the way attribute sets are handled by the retriaval module.
48  * Extended Explain conversion / schema.
49  * Modified server and client to work with ASN.1 compiled protocol handlers.
50  *
51  * Revision 1.19  1998/03/05 08:15:32  adam
52  * Implemented data1_add_insert_taggeddata utility which is more flexible
53  * than data1_insert_taggeddata.
54  *
55  * Revision 1.18  1998/02/27 14:08:04  adam
56  * Added const to some char pointer arguments.
57  * Reworked data1_read_node so that it doesn't create a tree with
58  * pointers to original "SGML"-buffer.
59  *
60  * Revision 1.17  1998/02/11 11:53:34  adam
61  * Changed code so that it compiles as C++.
62  *
63  * Revision 1.16  1997/12/18 10:51:30  adam
64  * Implemented sub-trees feature for schemas - including forward
65  * references.
66  *
67  * Revision 1.15  1997/12/09 16:18:16  adam
68  * Work on EXPLAIN schema. First implementation of sub-schema facility
69  * in the *.abs files.
70  *
71  * Revision 1.14  1997/10/31 12:20:09  adam
72  * Improved memory debugging for xmalloc/nmem.c. References to NMEM
73  * instead of ODR in n ESPEC-1 handling in source d1_espec.c.
74  * Bug fix: missing fclose in data1_read_espec1.
75  *
76  * Revision 1.13  1997/10/27 13:54:18  adam
77  * Changed structure field in data1 node to be simple string which
78  * is "unknown" to the retrieval system itself.
79  *
80  * Revision 1.12  1997/09/17 12:10:34  adam
81  * YAZ version 1.4.
82  *
83  * Revision 1.11  1997/09/05 09:50:55  adam
84  * Removed global data1_tabpath - uses data1_get_tabpath() instead.
85  *
86  * Revision 1.10  1997/05/14 06:54:01  adam
87  * C++ support.
88  *
89  * Revision 1.9  1997/02/19 14:46:15  adam
90  * The "all" specifier only affects elements that are indexed (and not
91  * all elements).
92  *
93  * Revision 1.8  1997/01/02 10:47:59  quinn
94  * Added optional, physical ANY
95  *
96  * Revision 1.7  1996/06/10 08:56:01  quinn
97  * Work on Summary.
98  *
99  * Revision 1.6  1996/05/31  13:52:21  quinn
100  * Fixed uninitialized variable for local tags in abstract syntax.
101  *
102  * Revision 1.5  1996/05/09  07:27:43  quinn
103  * Multiple local attributes values supported.
104  *
105  * Revision 1.4  1996/05/01  12:45:28  quinn
106  * Support use of local tag names in abs file.
107  *
108  * Revision 1.3  1995/11/01  16:34:55  quinn
109  * Making data1 look for tables in data1_tabpath
110  *
111  * Revision 1.2  1995/11/01  13:54:44  quinn
112  * Minor adjustments
113  *
114  * Revision 1.1  1995/11/01  11:56:06  quinn
115  * Added Retrieval (data management) functions en masse.
116  *
117  */
118
119 #include <stdio.h>
120 #include <assert.h>
121 #include <stdlib.h>
122 #include <string.h>
123
124 #include <yaz/oid.h>
125 #include <yaz/log.h>
126 #include <yaz/data1.h>
127
128 #define D1_MAX_NESTING  128
129
130 struct data1_absyn_cache_info 
131 {
132     char *name;
133     data1_absyn *absyn;
134     data1_absyn_cache next;
135 };
136
137 struct data1_attset_cache_info 
138 {
139     char *name;
140     data1_attset *attset;
141     data1_attset_cache next;
142 };
143
144 data1_absyn *data1_absyn_search (data1_handle dh, const char *name)
145 {
146     data1_absyn_cache p = *data1_absyn_cache_get (dh);
147
148     while (p)
149     {
150         if (!strcmp (name, p->name))
151             return p->absyn;
152         p = p->next;
153     }
154     return NULL;
155 }
156
157 void data1_absyn_trav (data1_handle dh, void *handle,
158                        void (*fh)(data1_handle dh, void *h, data1_absyn *a))
159 {
160     data1_absyn_cache p = *data1_absyn_cache_get (dh);
161
162     while (p)
163     {
164         (*fh)(dh, handle, p->absyn);
165         p = p->next;
166     }
167 }
168
169 data1_absyn *data1_absyn_add (data1_handle dh, const char *name)
170 {
171     char fname[512];
172     NMEM mem = data1_nmem_get (dh);
173
174     data1_absyn_cache p = (data1_absyn_cache)nmem_malloc (mem, sizeof(*p));
175     data1_absyn_cache *pp = data1_absyn_cache_get (dh);
176
177     sprintf(fname, "%s.abs", name);
178     p->absyn = data1_read_absyn (dh, fname);
179     p->name = nmem_strdup (mem, name);
180     p->next = *pp;
181     *pp = p;
182     return p->absyn;
183 }
184
185 data1_absyn *data1_get_absyn (data1_handle dh, const char *name)
186 {
187     data1_absyn *absyn;
188
189     if (!(absyn = data1_absyn_search (dh, name)))
190         absyn = data1_absyn_add (dh, name);
191     return absyn;
192 }
193
194 data1_attset *data1_attset_search_name (data1_handle dh, const char *name)
195 {
196     data1_attset_cache p = *data1_attset_cache_get (dh);
197
198     while (p)
199     {
200         if (!strcmp (name, p->name))
201             return p->attset;
202         p = p->next;
203     }
204     return NULL;
205 }
206
207 data1_attset *data1_attset_search_id (data1_handle dh, int id)
208 {
209     data1_attset_cache p = *data1_attset_cache_get (dh);
210
211     while (p)
212     {
213         if (id == p->attset->reference)
214             return p->attset;
215         p = p->next;
216     }
217     return NULL;
218 }
219
220 data1_attset *data1_attset_add (data1_handle dh, const char *name)
221 {
222     char fname[512], aname[512];
223     NMEM mem = data1_nmem_get (dh);
224     data1_attset *attset;
225
226     strcpy (aname, name);
227     sprintf(fname, "%s.att", name);
228     attset = data1_read_attset (dh, fname);
229     if (!attset)
230     {
231         char *cp;
232         attset = data1_read_attset (dh, name);
233         if (attset && (cp = strrchr (aname, '.')))
234             *cp = '\0';
235     }
236     if (!attset)
237         yaz_log (LOG_WARN|LOG_ERRNO, "Couldn't load attribute set %s", name);
238     else
239     {
240         data1_attset_cache p = (data1_attset_cache)
241             nmem_malloc (mem, sizeof(*p));
242         data1_attset_cache *pp = data1_attset_cache_get (dh);
243         
244         attset->name = p->name = nmem_strdup (mem, aname);
245         p->attset = attset;
246         p->next = *pp;
247         *pp = p;
248     }
249     return attset;
250 }
251
252 data1_attset *data1_get_attset (data1_handle dh, const char *name)
253 {
254     data1_attset *attset;
255
256     if (!(attset = data1_attset_search_name (dh, name)))
257         attset = data1_attset_add (dh, name);
258     return attset;
259 }
260
261 data1_esetname *data1_getesetbyname(data1_handle dh, data1_absyn *a,
262                                     const char *name)
263 {
264     data1_esetname *r;
265
266     for (r = a->esetnames; r; r = r->next)
267         if (!data1_matchstr(r->name, name))
268             return r;
269     return 0;
270 }
271
272 data1_element *data1_getelementbytagname (data1_handle dh, data1_absyn *abs,
273                                           data1_element *parent,
274                                           const char *tagname)
275 {
276     data1_element *r;
277
278     /* It's now possible to have a data1 tree with no abstract syntax */
279     if ( !abs )
280         return 0;
281
282     if (!parent)
283         r = abs->main_elements;
284     else
285         r = parent->children;
286     assert (abs->main_elements);
287     for (; r; r = r->next)
288     {
289         data1_name *n;
290
291         for (n = r->tag->names; n; n = n->next)
292             if (!data1_matchstr(tagname, n->name))
293                 return r;
294     }
295     return 0;
296 }
297
298 data1_element *data1_getelementbyname (data1_handle dh, data1_absyn *absyn,
299                                        const char *name)
300 {
301     data1_element *r;
302
303     /* It's now possible to have a data1 tree with no abstract syntax */
304     if ( !absyn )
305         return 0;
306     
307     assert (absyn->main_elements);
308     for (r = absyn->main_elements; r; r = r->next)
309         if (!data1_matchstr(r->name, name))
310             return r;
311     return 0;
312 }
313
314
315 void fix_element_ref (data1_handle dh, data1_absyn *absyn, data1_element *e)
316 {
317     /* It's now possible to have a data1 tree with no abstract syntax */
318     if ( !absyn )
319         return;
320
321     for (; e; e = e->next)
322     {
323         if (!e->sub_name)
324         {
325             if (e->children)
326                 fix_element_ref (dh, absyn, e->children);
327         }
328         else
329         {
330             data1_sub_elements *sub_e = absyn->sub_elements;
331             while (sub_e && strcmp (e->sub_name, sub_e->name))
332                 sub_e = sub_e->next;
333             if (sub_e)
334                 e->children = sub_e->elements;
335             else
336                 yaz_log (LOG_WARN, "Unresolved reference to sub-elements %s",
337                       e->sub_name);
338         }
339     }
340 }
341
342
343 static int parse_termlists (data1_handle dh, data1_termlist ***tpp,
344                             char *p, const char *file, int lineno,
345                             const char *element_name, data1_absyn *res)
346 {
347     data1_termlist **tp = *tpp;
348     do
349     {
350         char attname[512], structure[512];
351         char *source;
352         int r;
353         
354         if (!(r = sscanf(p, "%511[^:,]:%511[^,]", attname,
355                          structure)))
356         {
357             yaz_log(LOG_WARN,
358                     "%s:%d: Syntax error in termlistspec '%s'",
359                     file, lineno, p);
360             return -1;
361 /*
362   fclose(f);
363   return 0;
364 */
365         }
366         if (*attname == '!')
367             strcpy(attname, element_name);
368         *tp = (data1_termlist *)
369             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             yaz_log(LOG_WARN,
375                     "%s:%d: Couldn't find att '%s' in attset",
376                     file, lineno, attname);
377             return -1;
378 /*
379             fclose(f);
380             return 0;
381 */
382         }
383         if (r == 2 && (source = strchr(structure, ':')))
384             *source++ = '\0';   /* cut off structure .. */
385         else
386             source = "data";    /* ok: default is leaf data */
387         (*tp)->source = (char *)
388             nmem_strdup (data1_nmem_get (dh), source);
389         
390         if (r < 2) /* is the structure qualified? */
391             (*tp)->structure = "w";
392         else 
393             (*tp)->structure = (char *)
394                 nmem_strdup (data1_nmem_get (dh), structure);
395         tp = &(*tp)->next;
396     }
397     while ((p = strchr(p, ',')) && *(++p));
398     *tpp = tp;
399     return 0;
400 }
401
402 data1_absyn *data1_read_absyn (data1_handle dh, const char *file)
403 {
404     data1_sub_elements *cur_elements = NULL;
405     data1_absyn *res = 0;
406     FILE *f;
407     data1_element **ppl[D1_MAX_NESTING];
408     data1_esetname **esetpp;
409     data1_maptab **maptabp;
410     data1_marctab **marcp;
411     data1_termlist *all = 0;
412     data1_attset_child **attset_childp;
413     data1_tagset **tagset_childp;
414     int level = 0;
415     int lineno = 0;
416     int argc;
417     char *argv[50], line[512];
418
419     if (!(f = yaz_path_fopen(data1_get_tabpath (dh), file, "r")))
420     {
421         yaz_log(LOG_WARN|LOG_ERRNO, "Couldn't open %s", file);
422         return 0;
423     }
424     
425     res = (data1_absyn *) nmem_malloc(data1_nmem_get(dh), sizeof(*res));
426     res->name = 0;
427     res->reference = VAL_NONE;
428     res->tagset = 0;
429     tagset_childp = &res->tagset;
430
431     res->attset = data1_empty_attset (dh);
432     attset_childp =  &res->attset->children;
433
434     res->varset = 0;
435     res->esetnames = 0;
436     esetpp = &res->esetnames;
437     res->maptabs = 0;
438     maptabp = &res->maptabs;
439     res->marc = 0;
440     marcp = &res->marc;
441
442     res->sub_elements = NULL;
443     res->main_elements = NULL;
444
445     while ((argc = readconf_line(f, &lineno, line, 512, argv, 50)))
446     {
447         char *cmd = *argv;
448         if (!strcmp(cmd, "elm"))
449         {
450             data1_element *new_element;
451             int i;
452             char *p, *sub_p, *path, *name, *termlists;
453             int type, value;
454             data1_termlist **tp;
455
456             if (argc < 4)
457             {
458                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to elm", file, lineno);
459                 continue;
460             }
461             path = argv[1];
462             name = argv[2];
463             termlists = argv[3];
464
465             if (!cur_elements)
466             {
467                 cur_elements = (data1_sub_elements *)
468                     nmem_malloc(data1_nmem_get(dh), sizeof(*cur_elements));
469                 cur_elements->next = res->sub_elements;
470                 cur_elements->elements = NULL;
471                 cur_elements->name = "main";
472                 res->sub_elements = cur_elements;
473                 
474                 level = 0;
475                 ppl[level] = &cur_elements->elements;
476             }
477             p = path;
478             for (i = 1;; i++)
479             {
480                 char *e;
481
482                 if ((e = strchr(p, '/')))
483                     p = e+1;
484                 else
485                     break;
486             }
487             if (i > level+1)
488             {
489                 yaz_log(LOG_WARN, "%s:%d: Bad level increase", file, lineno);
490                 fclose(f);
491                 return 0;
492             }
493             level = i;
494             new_element = *ppl[level-1] = (data1_element *)
495                 nmem_malloc(data1_nmem_get(dh), sizeof(*new_element));
496             new_element->next = new_element->children = 0;
497             new_element->tag = 0;
498             new_element->termlists = 0;
499             new_element->sub_name = 0;
500             
501             tp = &new_element->termlists;
502             ppl[level-1] = &new_element->next;
503             ppl[level] = &new_element->children;
504             
505             /* consider subtree (if any) ... */
506             if ((sub_p = strchr (p, ':')) && sub_p[1])
507             {
508                 *sub_p++ = '\0';
509                 new_element->sub_name =
510                     nmem_strdup (data1_nmem_get(dh), sub_p);            
511             }
512             /* well-defined tag */
513             if (sscanf(p, "(%d,%d)", &type, &value) == 2)
514             {
515                 if (!res->tagset)
516                 {
517                     yaz_log(LOG_WARN, "%s:%d: No tagset loaded", file, lineno);
518                     fclose(f);
519                     return 0;
520                 }
521                 if (!(new_element->tag = data1_gettagbynum (dh, res->tagset,
522                                                             type, value)))
523                 {
524                     yaz_log(LOG_WARN, "%s:%d: Couldn't find tag %s in tagset",
525                          file, lineno, p);
526                     fclose(f);
527                     return 0;
528                 }
529             }
530             /* private tag */
531             else if (*p)
532             {
533                 data1_tag *nt =
534                     new_element->tag = (data1_tag *)
535                     nmem_malloc(data1_nmem_get (dh),
536                                 sizeof(*new_element->tag));
537                 nt->which = DATA1T_string;
538                 nt->value.string = nmem_strdup(data1_nmem_get (dh), p);
539                 nt->names = (data1_name *)
540                     nmem_malloc(data1_nmem_get(dh), 
541                                 sizeof(*new_element->tag->names));
542                 nt->names->name = nt->value.string;
543                 nt->names->next = 0;
544                 nt->kind = DATA1K_string;
545                 nt->next = 0;
546                 nt->tagset = 0;
547             }
548             else
549             {
550                 yaz_log(LOG_WARN, "%s:%d: Bad element", file, lineno);
551                 fclose(f);
552                 return 0;
553             }
554             /* parse termList definitions */
555             p = termlists;
556             if (*p != '-')
557             {
558                 assert (res->attset);
559                 
560                 if (parse_termlists (dh, &tp, p, file, lineno, name, res))
561                 {
562                     fclose (f);
563                     return 0;
564                 }
565                 *tp = all; /* append any ALL entries to the list */
566             }
567             new_element->name = nmem_strdup(data1_nmem_get (dh), name);
568         }
569         else if (!strcmp(cmd, "section"))
570         {
571             char *name;
572             
573             if (argc < 2)
574             {
575                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to section",
576                      file, lineno);
577                 continue;
578             }
579             name = argv[1];
580             
581             cur_elements = (data1_sub_elements *)
582                 nmem_malloc(data1_nmem_get(dh), sizeof(*cur_elements));
583             cur_elements->next = res->sub_elements;
584             cur_elements->elements = NULL;
585             cur_elements->name = nmem_strdup (data1_nmem_get(dh), name);
586             res->sub_elements = cur_elements;
587             
588             level = 0;
589             ppl[level] = &cur_elements->elements;
590         }
591         else if (!strcmp(cmd, "all"))
592         {
593             data1_termlist **tp = &all;
594             if (all)
595             {
596                 yaz_log(LOG_WARN, "%s:%d: Too many 'all' directives - ignored",
597                      file, lineno);
598                 continue;
599             }
600             if (argc != 2)
601             {
602                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to 'all' directive",
603                      file, lineno);
604                 continue;
605             }
606             if (parse_termlists (dh, &tp, argv[1], file, lineno, 0, res))
607             {
608                 fclose (f);
609                 return 0;
610             }
611         }
612         else if (!strcmp(cmd, "name"))
613         {
614             if (argc != 2)
615             {
616                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to name directive",
617                      file, lineno);
618                 continue;
619             }
620             res->name = nmem_strdup(data1_nmem_get(dh), argv[1]);
621         }
622         else if (!strcmp(cmd, "reference"))
623         {
624             char *name;
625             
626             if (argc != 2)
627             {
628                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to reference",
629                      file, lineno);
630                 continue;
631             }
632             name = argv[1];
633             if ((res->reference = oid_getvalbyname(name)) == VAL_NONE)
634             {
635                 yaz_log(LOG_WARN, "%s:%d: Unknown tagset ref '%s'", 
636                      file, lineno, name);
637                 continue;
638             }
639         }
640         else if (!strcmp(cmd, "attset"))
641         {
642             char *name;
643             data1_attset *attset;
644             
645             if (argc != 2)
646             {
647                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to attset",
648                      file, lineno);
649                 continue;
650             }
651             name = argv[1];
652             if (!(attset = data1_get_attset (dh, name)))
653             {
654                 yaz_log(LOG_WARN, "%s:%d: Couldn't find attset  %s",
655                      file, lineno, name);
656                 continue;
657             }
658             *attset_childp = (data1_attset_child *)
659                 nmem_malloc (data1_nmem_get(dh), sizeof(**attset_childp));
660             (*attset_childp)->child = attset;
661             (*attset_childp)->next = 0;
662             attset_childp = &(*attset_childp)->next;
663         }
664         else if (!strcmp(cmd, "tagset"))
665         {
666             char *name;
667             int type = 0;
668             if (argc < 2)
669             {
670                 yaz_log(LOG_WARN, "%s:%d: Bad # of args to tagset",
671                      file, lineno);
672                 continue;
673             }
674             name = argv[1];
675             if (argc == 3)
676                 type = atoi(argv[2]);
677             *tagset_childp = data1_read_tagset (dh, name, type);
678             if (!(*tagset_childp))
679             {
680                 yaz_log(LOG_WARN, "%s:%d: Couldn't load tagset %s",
681                      file, lineno, name);
682                 continue;
683             }
684             tagset_childp = &(*tagset_childp)->next;
685         }
686         else if (!strcmp(cmd, "varset"))
687         {
688             char *name;
689
690             if (argc != 2)
691             {
692                 yaz_log(LOG_WARN, "%s:%d: Bad # of args in varset",
693                      file, lineno);
694                 continue;
695             }
696             name = argv[1];
697             if (!(res->varset = data1_read_varset (dh, name)))
698             {
699                 yaz_log(LOG_WARN, "%s:%d: Couldn't load Varset %s",
700                      file, lineno, name);
701                 continue;
702             }
703         }
704         else if (!strcmp(cmd, "esetname"))
705         {
706             char *name, *fname;
707
708             if (argc != 3)
709             {
710                 yaz_log(LOG_WARN, "%s:%d: Bad # of args in esetname",
711                      file, lineno);
712                 continue;
713             }
714             name = argv[1];
715             fname = argv[2];
716             
717             *esetpp = (data1_esetname *)
718                 nmem_malloc(data1_nmem_get(dh), sizeof(**esetpp));
719             (*esetpp)->name = nmem_strdup(data1_nmem_get(dh), name);
720             (*esetpp)->next = 0;
721             if (*fname == '@')
722                 (*esetpp)->spec = 0;
723             else if (!((*esetpp)->spec = data1_read_espec1 (dh, fname)))
724             {
725                 yaz_log(LOG_WARN, "%s:%d: Espec-1 read failed for %s",
726                      file, lineno, fname);
727                 continue;
728             }
729             esetpp = &(*esetpp)->next;
730         }
731         else if (!strcmp(cmd, "maptab"))
732         {
733             char *name;
734             
735             if (argc != 2)
736             {
737                 yaz_log(LOG_WARN, "%s:%d: Bad # of args for maptab",
738                      file, lineno);
739                 continue;
740             }
741             name = argv[1];
742             if (!(*maptabp = data1_read_maptab (dh, name)))
743             {
744                 yaz_log(LOG_WARN, "%s:%d: Couldn't load maptab %s",
745                      file, lineno, name);
746                 continue;
747             }
748             maptabp = &(*maptabp)->next;
749         }
750         else if (!strcmp(cmd, "marc"))
751         {
752             char *name;
753             
754             if (argc != 2)
755             {
756                 yaz_log(LOG_WARN, "%s:%d: Bad # or args for marc",
757                      file, lineno);
758                 continue;
759             }
760             name = argv[1];
761             if (!(*marcp = data1_read_marctab (dh, name)))
762             {
763                 yaz_log(LOG_WARN, "%s:%d: Couldn't read marctab %s",
764                      file, lineno, name);
765                 continue;
766             }
767             marcp = &(*marcp)->next;
768         }
769         else
770         {
771             yaz_log(LOG_WARN, "%s:%d: Unknown directive '%s'", file, lineno, cmd);
772             continue;
773         }
774     }
775     fclose(f);
776     
777     for (cur_elements = res->sub_elements; cur_elements;
778          cur_elements = cur_elements->next)
779     {
780         if (!strcmp (cur_elements->name, "main"))
781             res->main_elements = cur_elements->elements;
782         fix_element_ref (dh, res, cur_elements->elements);
783     }
784     yaz_log (LOG_DEBUG, "%s: data1_read_absyn end", file);
785     return res;
786 }