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