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