SGML reader parses comments
[yaz-moved-to-github.git] / retrieval / d1_read.c
1 /*
2  * Copyright (c) 1995-2002, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Id: d1_read.c,v 1.52 2002-09-24 07:58:59 adam Exp $
7  */
8
9 #include <assert.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12
13 #include <yaz/xmalloc.h>
14 #include <yaz/yaz-util.h>
15 #include <yaz/log.h>
16 #include <yaz/data1.h>
17
18 data1_node *data1_get_root_tag (data1_handle dh, data1_node *n)
19 {
20     if (!n)
21         return 0;
22     if (data1_is_xmlmode(dh))
23     {
24         n = n->child;
25         while (n && n->which != DATA1N_tag)
26             n = n->next;
27     }
28     return n;
29 }
30         
31 /*
32  * get the tag which is the immediate parent of this node (this may mean
33  * traversing intermediate things like variants and stuff.
34  */
35 data1_node *get_parent_tag (data1_handle dh, data1_node *n)
36 {
37     if (data1_is_xmlmode(dh))
38     {
39         for (; n && n->which != DATA1N_root; n = n->parent)
40             if (n->which == DATA1N_tag && n->parent &&
41                 n->parent->which != DATA1N_root)
42                 return n;
43     }
44     else
45     {
46         for (; n && n->which != DATA1N_root; n = n->parent)
47             if (n->which == DATA1N_tag)
48                 return n;
49     }
50     return 0;
51 }
52
53 data1_node *data1_mk_node (data1_handle dh, NMEM m)
54 {
55     return data1_mk_node2 (dh, m, DATA1N_root, 0);
56 }
57
58 data1_node *data1_mk_node_type (data1_handle dh, NMEM m, int type)
59 {
60     return data1_mk_node2 (dh, m, type, 0);
61 }
62
63 data1_node *data1_mk_node2 (data1_handle dh, NMEM m, int type,
64                             data1_node *parent)
65 {
66     data1_node *r;
67     
68     r = (data1_node *)nmem_malloc(m, sizeof(*r));
69     r->next = r->child = r->last_child = 0;
70     r->destroy = 0;
71
72     if (!parent)
73     {
74         r->root = r;
75     }
76     else
77     {
78         r->root = parent->root;
79         r->parent = parent;
80         if (!parent->child)
81             parent->child = parent->last_child = r;
82         else
83             parent->last_child->next = r;
84         parent->last_child = r;
85     }
86     r->which = type;
87     switch(type)
88     {
89     case DATA1N_tag:
90         r->u.tag.tag = 0;
91         r->u.tag.element = 0;
92         r->u.tag.no_data_requested = 0;
93         r->u.tag.node_selected = 0;
94         r->u.tag.make_variantlist = 0;
95         r->u.tag.get_bytes = -1;
96         r->u.tag.attributes = 0;
97         break;
98     case DATA1N_root:
99         r->u.root.type = 0;
100         r->u.root.absyn = 0;
101         break;
102     case DATA1N_data:
103         r->u.data.data = 0;
104         r->u.data.len = 0;
105         r->u.data.what = 0;
106         r->u.data.formatted_text = 0;
107         break;
108     case DATA1N_comment:
109         r->u.data.data = 0;
110         r->u.data.len = 0;
111         r->u.data.what = 0;
112         r->u.data.formatted_text = 1;
113         break;
114     case DATA1N_variant:
115         r->u.variant.type = 0;
116         r->u.variant.value = 0;
117         break;
118     case DATA1N_preprocess:
119         r->u.preprocess.target = 0;
120         r->u.preprocess.attributes = 0;
121         break;
122     default:
123         logf (LOG_WARN, "data_mk_node_type. bad type = %d\n", type);
124     }
125     return r;
126 }
127
128 void data1_free_tree (data1_handle dh, data1_node *t)
129 {
130     data1_node *p = t->child, *pn;
131
132     while (p)
133     {
134         pn = p->next;
135         data1_free_tree (dh, p);
136         p = pn;
137     }
138     if (t->destroy)
139         (*t->destroy)(t);
140 }
141
142 data1_node *data1_mk_root (data1_handle dh, NMEM nmem, const char *name)
143 {
144     data1_absyn *absyn = data1_get_absyn (dh, name);
145     data1_node *res;
146     if (!absyn)
147     {
148         yaz_log(LOG_WARN, "Unable to acquire abstract syntax " "for '%s'",
149                 name); 
150         /* It's now OK for a record not to have an absyn */
151     }
152     res = data1_mk_node2 (dh, nmem, DATA1N_root, 0);
153     res->u.root.type = data1_insert_string (dh, res, nmem, name);
154     res->u.root.absyn = absyn;
155     return res;
156 }
157
158 void data1_set_root(data1_handle dh, data1_node *res,
159                     NMEM nmem, const char *name)
160 {
161     data1_absyn *absyn = data1_get_absyn (dh, name);
162
163     res->u.root.type = data1_insert_string (dh, res, nmem, name);
164     res->u.root.absyn = absyn;
165 }
166
167 data1_node *data1_mk_preprocess (data1_handle dh, NMEM nmem,
168                                  const char *target,
169                                  const char **attr, data1_node *at)
170 {
171     return data1_mk_preprocess_n (dh, nmem, target, strlen(target),
172                                   attr, at);
173 }
174
175 data1_node *data1_mk_preprocess_n (data1_handle dh, NMEM nmem,
176                                    const char *target, size_t len,
177                                    const char **attr, data1_node *at)
178 {
179     data1_xattr **p;
180     data1_node *res = data1_mk_node2 (dh, nmem, DATA1N_preprocess, at);
181     res->u.preprocess.target = data1_insert_string_n (dh, res, nmem,
182                                                       target, len);
183     
184     p = &res->u.preprocess.attributes;
185     while (attr && *attr)
186     {
187         *p = (data1_xattr*) nmem_malloc (nmem, sizeof(**p));
188         (*p)->name = nmem_strdup (nmem, *attr++);
189         (*p)->value = nmem_strdup (nmem, *attr++);
190         p = &(*p)->next;
191     }
192     *p = 0;
193     return res;
194 }
195
196 data1_node *data1_mk_tag_n (data1_handle dh, NMEM nmem, 
197                             const char *tag, size_t len, const char **attr,
198                             data1_node *at)
199 {
200     data1_node *partag = get_parent_tag(dh, at);
201     data1_node *res = data1_mk_node2 (dh, nmem, DATA1N_tag, at);
202     data1_xattr **p;
203     data1_element *e = 0;
204     
205     res->u.tag.tag = data1_insert_string_n (dh, res, nmem, tag, len);
206     
207     if (!partag)  /* top tag? */
208         e  = data1_getelementbytagname (dh, at->root->u.root.absyn,
209                                         0 /* index as local */,
210                                         res->u.tag.tag);
211     else
212     {
213         /* only set element for known tags */
214         e = partag->u.tag.element;
215         if (e)
216             e = data1_getelementbytagname (dh, at->root->u.root.absyn,
217                                            e, res->u.tag.tag);
218     }
219     res->u.tag.element = e;
220     p = &res->u.tag.attributes;
221     while (attr && *attr)
222     {
223         *p = (data1_xattr*) nmem_malloc (nmem, sizeof(**p));
224         (*p)->name = nmem_strdup (nmem, *attr++);
225         (*p)->value = nmem_strdup (nmem, *attr++);
226         p = &(*p)->next;
227     }
228     *p = 0;
229     return res;
230 }
231
232 void data1_tag_add_attr (data1_handle dh, NMEM nmem,
233                          data1_node *res, const char **attr)
234 {
235     data1_xattr **p;
236
237     if (res->which != DATA1N_tag)
238         return;
239
240     p = &res->u.tag.attributes;
241     while (*p)
242         p = &(*p)->next;
243
244     while (attr && *attr)
245     {
246         *p = (data1_xattr*) nmem_malloc (nmem, sizeof(**p));
247         (*p)->name = nmem_strdup (nmem, *attr++);
248         (*p)->value = nmem_strdup (nmem, *attr++);
249         p = &(*p)->next;
250     }
251     *p = 0;
252 }
253
254 data1_node *data1_mk_tag (data1_handle dh, NMEM nmem,
255                           const char *tag, const char **attr, data1_node *at) 
256 {
257     return data1_mk_tag_n (dh, nmem, tag, strlen(tag), attr, at);
258 }
259
260 data1_node *data1_search_tag (data1_handle dh, data1_node *n,
261                               const char *tag)
262 {
263     if (*tag == '/')
264     {
265         n = data1_get_root_tag (dh, n);
266         if (n)
267             n = n->child;
268         tag++;
269     }
270     for (; n; n = n->next)
271         if (n->which == DATA1N_tag && n->u.tag.tag &&
272             !yaz_matchstr (tag, n->u.tag.tag))
273         {
274             return n;
275         }
276     return 0;
277 }
278
279 data1_node *data1_mk_tag_uni (data1_handle dh, NMEM nmem, 
280                               const char *tag, data1_node *at)
281 {
282     data1_node *node = data1_search_tag (dh, at->child, tag);
283     if (!node)
284         node = data1_mk_tag (dh, nmem, tag, 0 /* attr */, at);
285     else
286         node->child = node->last_child = 0;
287     return node;
288 }
289
290 data1_node *data1_mk_text_n (data1_handle dh, NMEM mem,
291                              const char *buf, size_t len, data1_node *parent)
292 {
293     data1_node *res = data1_mk_node2 (dh, mem, DATA1N_data, parent);
294     res->u.data.what = DATA1I_text;
295     res->u.data.len = len;
296     
297     res->u.data.data = data1_insert_string_n (dh, res, mem, buf, len);
298     return res;
299 }
300
301 data1_node *data1_mk_text_nf (data1_handle dh, NMEM mem,
302                               const char *buf, size_t len, data1_node *parent)
303 {
304     data1_node *res = data1_mk_text_n (dh, mem, buf, len, parent);
305     res->u.data.formatted_text = 1;
306     return res;
307 }
308
309 data1_node *data1_mk_text (data1_handle dh, NMEM mem,
310                            const char *buf, data1_node *parent)
311 {
312     return data1_mk_text_n (dh, mem, buf, strlen(buf), parent);
313 }
314
315 data1_node *data1_mk_comment_n (data1_handle dh, NMEM mem,
316                                 const char *buf, size_t len,
317                                 data1_node *parent)
318 {
319     data1_node *res = data1_mk_node2 (dh, mem, DATA1N_comment, parent);
320     res->u.data.what = DATA1I_text;
321     res->u.data.len = len;
322     
323     res->u.data.data = data1_insert_string_n (dh, res, mem, buf, len);
324     return res;
325 }
326
327 data1_node *data1_mk_comment (data1_handle dh, NMEM mem,
328                               const char *buf, data1_node *parent)
329 {
330     return data1_mk_comment_n (dh, mem, buf, strlen(buf), parent);
331 }
332
333 char *data1_insert_string_n (data1_handle dh, data1_node *res,
334                              NMEM m, const char *str, size_t len)
335 {
336     char *b;
337     if (len >= DATA1_LOCALDATA)
338         b = (char *) nmem_malloc (m, len+1);
339     else
340         b = res->lbuf;
341     memcpy (b, str, len);
342     b[len] = 0;
343     return b;
344 }
345
346 char *data1_insert_string (data1_handle dh, data1_node *res,
347                            NMEM m, const char *str)
348 {
349     return data1_insert_string_n (dh, res, m, str, strlen(str));
350 }
351
352 static data1_node *data1_add_insert_taggeddata(data1_handle dh,
353                                                data1_node *at,
354                                                const char *tagname, NMEM m,
355                                                int local_allowed)
356 {
357     data1_node *root = at->root;
358     data1_node *partag = get_parent_tag (dh, at);
359     data1_element *e = NULL;
360     data1_node *datn = 0;
361     data1_node *tagn = 0;
362
363     if (!partag)
364         e = data1_getelementbytagname (dh, root->u.root.absyn, 0, tagname);
365     else 
366     {
367         e = partag->u.tag.element;
368         if (e)
369             e = data1_getelementbytagname (dh, root->u.root.absyn, e, tagname);
370     }
371     if (local_allowed || e)
372     {
373         tagn = data1_mk_node2 (dh, m, DATA1N_tag, at);
374         tagn->u.tag.tag = data1_insert_string (dh, tagn, m, tagname);
375         tagn->u.tag.element = e;
376         datn = data1_mk_node2 (dh, m, DATA1N_data, tagn);
377     }
378     return datn;
379 }
380
381 data1_node *data1_mk_tag_data(data1_handle dh, data1_node *at,
382                               const char *tagname, NMEM m)
383 {
384     return data1_add_insert_taggeddata (dh, at, tagname, m, 1);
385 }
386
387
388 /*
389  * Insert a tagged node into the record root as first child of the node at
390  * which should be root or tag itself). Returns pointer to the data node,
391  * which can then be modified.
392  */
393 data1_node *data1_mk_tag_data_wd(data1_handle dh, data1_node *at,
394                                  const char *tagname, NMEM m)
395 {
396     return data1_add_insert_taggeddata (dh, at, tagname, m, 0);
397 }
398
399 data1_node *data1_insert_taggeddata (data1_handle dh, data1_node *root,
400                                      data1_node *at, const char *tagname,
401                                      NMEM m)
402 {
403     return data1_add_insert_taggeddata (dh, at, tagname, m, 0);
404 }
405
406 data1_node *data1_add_taggeddata (data1_handle dh, data1_node *root,
407                                   data1_node *at, const char *tagname,
408                                   NMEM m)
409 {
410     return data1_add_insert_taggeddata (dh, at, tagname, m, 1);
411 }
412
413 data1_node *data1_mk_tag_data_int (data1_handle dh, data1_node *at,
414                                    const char *tag, int num,
415                                    NMEM nmem)
416 {
417     data1_node *node_data;
418     
419     node_data = data1_mk_tag_data (dh, at, tag, nmem);
420     if (!node_data)
421         return 0;
422     node_data->u.data.what = DATA1I_num;
423     node_data->u.data.data = node_data->lbuf;
424     sprintf (node_data->u.data.data, "%d", num);
425     node_data->u.data.len = strlen (node_data->u.data.data);
426     return node_data;
427 }
428
429 data1_node *data1_mk_tag_data_oid (data1_handle dh, data1_node *at,
430                                    const char *tag, Odr_oid *oid,
431                                    NMEM nmem)
432 {
433     data1_node *node_data;
434     char str[128], *p = str;
435     Odr_oid *ii;
436     
437     node_data = data1_mk_tag_data (dh, at, tag, nmem);
438     if (!node_data)
439         return 0;
440     
441     for (ii = oid; *ii >= 0; ii++)
442     {
443         if (ii != oid)
444             *p++ = '.';
445         sprintf (p, "%d", *ii);
446         p += strlen (p);
447     }
448     node_data->u.data.what = DATA1I_oid;
449     node_data->u.data.len = strlen (str);
450     node_data->u.data.data = data1_insert_string (dh, node_data, nmem, str);
451     return node_data;
452 }
453
454
455 data1_node *data1_mk_tag_data_text (data1_handle dh, data1_node *at,
456                                     const char *tag, const char *str,
457                                     NMEM nmem)
458 {
459     data1_node *node_data;
460     
461     node_data = data1_mk_tag_data (dh, at, tag, nmem);
462     if (!node_data)
463         return 0;
464     node_data->u.data.what = DATA1I_text;
465     node_data->u.data.len = strlen (str);
466     node_data->u.data.data = data1_insert_string (dh, node_data, nmem, str);
467     return node_data;
468 }
469
470
471 data1_node *data1_mk_tag_data_text_uni (data1_handle dh, data1_node *at,
472                                         const char *tag, const char *str,
473                                         NMEM nmem)
474 {
475     data1_node *node = data1_search_tag (dh, at->child, tag);
476     if (!node)
477         return data1_mk_tag_data_text (dh, at, tag, str, nmem);
478     else
479     {
480         data1_node *node_data = node->child;
481         node_data->u.data.what = DATA1I_text;
482         node_data->u.data.len = strlen (str);
483         node_data->u.data.data = data1_insert_string (dh, node_data,
484                                                       nmem, str);
485         node_data->child = node_data->last_child = 0;
486         return node_data;
487     }
488 }
489
490 static int ampr (int (*get_byte)(void *fh), void *fh, int *amp)
491 {
492     int c = (*get_byte)(fh);
493     *amp = 0;
494     if (c == '&')
495     {
496         char ent[20];
497         int i = 0;
498        
499         while (1)
500         {
501             c = (*get_byte)(fh);
502             if (c == ';')
503             {
504                 ent[i] = 0;
505                 
506                 c = ' ';
507                 if (!strcmp (ent, "quot"))
508                     c = '"';
509                 if (!strcmp (ent, "apos"))
510                     c = '\'';
511                 if (!strcmp (ent, "gt"))
512                     c = '>';
513                 if (!strcmp (ent, "lt"))
514                     c = '<';
515                 if (!strcmp (ent, "amp"))
516                     c = '&';
517                 *amp = 1;
518                 break;
519             }
520             else if (c == 0 || d1_isspace(c))
521                 break;
522             if (i < 19)
523                 ent[i++] = c;
524         }
525     }
526     return c;
527 }
528
529 data1_xattr *data1_read_xattr (data1_handle dh, NMEM m,
530                                int (*get_byte)(void *fh), void *fh,
531                                WRBUF wrbuf, int *ch, int *amp)
532 {
533     data1_xattr *p_first = 0;
534     data1_xattr **pp = &p_first;
535     int c = *ch;
536     for (;;)
537     {
538         data1_xattr *p;
539         int len;
540         while (*amp || (c && d1_isspace(c)))
541             c = ampr (get_byte, fh, amp);
542         if (*amp == 0 && (c == 0 || c == '>' || c == '/'))
543             break;
544         *pp = p = (data1_xattr *) nmem_malloc (m, sizeof(*p));
545         p->next = 0;
546         pp = &p->next;
547         p->value = 0;
548         
549         wrbuf_rewind(wrbuf);
550         while (c && c != '=' && c != '>' && c != '/' && !d1_isspace(c))
551         {
552             wrbuf_putc (wrbuf, c);
553             c = ampr (get_byte, fh, amp);
554         }
555         wrbuf_putc (wrbuf, '\0');
556         len = wrbuf_len(wrbuf);
557         p->name = (char*) nmem_malloc (m, len);
558         strcpy (p->name, wrbuf_buf(wrbuf));
559         if (c == '=')
560         {
561             c = ampr (get_byte, fh, amp);
562             if (*amp == 0 && c == '"')
563             {
564                 c = ampr (get_byte, fh, amp);   
565                 wrbuf_rewind(wrbuf);
566                 while (*amp || (c && c != '"'))
567                 {
568                     wrbuf_putc (wrbuf, c);
569                     c = ampr (get_byte, fh, amp);
570                 }
571                 if (c)
572                     c = ampr (get_byte, fh, amp);
573             }
574             else if (*amp == 0 && c == '\'')
575             {
576                 c = ampr (get_byte, fh, amp);   
577                 wrbuf_rewind(wrbuf);
578                 while (*amp || (c && c != '\''))
579                 {
580                     wrbuf_putc (wrbuf, c);
581                     c = ampr (get_byte, fh, amp);
582                 }
583                 if (c)
584                     c = ampr (get_byte, fh, amp);
585             }
586             else
587             {
588                 wrbuf_rewind(wrbuf);
589                 while (*amp || (c && c != '>' && c != '/'))
590                 {
591                     wrbuf_putc (wrbuf, c);
592                     c = ampr (get_byte, fh, amp);
593                 }
594             }
595             wrbuf_putc (wrbuf, '\0');
596             len = wrbuf_len(wrbuf);
597             p->value = (char*) nmem_malloc (m, len);
598             strcpy (p->value, wrbuf_buf(wrbuf));
599         }
600     }
601     *ch = c;
602     return p_first;
603 }
604
605 /*
606  * Ugh. Sometimes functions just grow and grow on you. This one reads a
607  * 'node' and its children.
608  */
609 data1_node *data1_read_nodex (data1_handle dh, NMEM m,
610                               int (*get_byte)(void *fh), void *fh, WRBUF wrbuf)
611 {
612     data1_node *d1_stack[256];
613     data1_node *res;
614     int c, amp;
615     int level = 0;
616     int line = 1;
617
618     d1_stack[level] = 0;
619     c = ampr (get_byte, fh, &amp);
620     while (c != '\0')
621     {
622         data1_node *parent = level ? d1_stack[level-1] : 0;
623
624         if (amp == 0 && c == '<') /* beginning of tag */
625         {
626             data1_xattr *xattr;
627
628             char tag[64];
629             char args[256];
630             int null_tag = 0;
631             int end_tag = 0;
632             size_t i = 0;
633
634             c = ampr (get_byte, fh, &amp);
635             if (amp == 0 && c == '/')
636             {
637                 end_tag = 1;
638                 c = ampr (get_byte, fh, &amp);
639             }
640             else if (amp == 0 && c == '!')
641             {
642                 int c0, amp0;
643               
644                 wrbuf_rewind(wrbuf);
645                 
646                 c0 = ampr (get_byte, fh, &amp0);
647                 if (amp0 == 0 && c0 == '\0')
648                     break;
649                 c = ampr (get_byte, fh, &amp);
650                 
651                 if (amp0 == 0 && c0 == '-' && amp == 0 && c == '-')
652                 {
653                     /* COMMENT: <!-- ... --> */
654                     int no_dash = 0;
655                     
656                     c = ampr (get_byte, fh, &amp);
657                     while (amp || c)
658                     {
659                         if (amp == 0 && c == '-')
660                             no_dash++;
661                         else if (amp == 0 && c == '>' && no_dash >= 2)
662                         {
663                             if (level > 0)
664                                 d1_stack[level] = 
665                                     data1_mk_comment_n (
666                                         dh, m,
667                                         wrbuf_buf(wrbuf), wrbuf_len(wrbuf)-2,
668                                         d1_stack[level-1]);
669                             c = ampr (get_byte, fh, &amp); /* skip > */
670                             break;
671                         }
672                         else
673                             no_dash = 0;
674                         wrbuf_putc (wrbuf, c);
675                         c = ampr (get_byte, fh, &amp);
676                     }
677                     continue;
678                 }
679                 else
680                 {   /* DIRECTIVE: <! .. > */
681         
682                     int blevel = 0;
683                     while (amp || c)
684                     {
685                         if (amp == 0 && c == '>' && blevel == 0)
686                         {
687                             c = ampr (get_byte, fh, &amp);
688                             break;
689                         }
690                         if (amp == 0 && c == '[')
691                             blevel++;
692                         if (amp == 0 && c == ']' && blevel > 0)
693                             blevel--;
694                         c = ampr (get_byte, fh, &amp);
695                     }
696                     continue;
697                 }
698             }
699             while (amp || (c && c != '>' && c != '/' && !d1_isspace(c)))
700             {
701                 if (i < (sizeof(tag)-1))
702                     tag[i++] = c;
703                 c = ampr (get_byte, fh, &amp);
704             }
705             tag[i] = '\0';
706             xattr = data1_read_xattr (dh, m, get_byte, fh, wrbuf, &c, &amp);
707             args[0] = '\0';
708             if (amp == 0 && c == '/')
709             {    /* <tag attrs/> or <tag/> */
710                 null_tag = 1;
711                 c = ampr (get_byte, fh, &amp);
712             }
713             if (amp || c != '>')
714             {
715                 yaz_log(LOG_WARN, "d1: %d: Malformed tag", line);
716                 return 0;
717             }
718             else
719                 c = ampr (get_byte, fh, &amp);
720
721             /* End tag? */
722             if (end_tag)       
723             {
724                 if (*tag == '\0')
725                     --level;        /* </> */
726                 else
727                 {                   /* </tag> */
728                     int i = level;
729                     while (i > 0)
730                     {
731                         parent = d1_stack[--i];
732                         if ((parent->which == DATA1N_root &&
733                              !strcmp(tag, parent->u.root.type)) ||
734                             (parent->which == DATA1N_tag &&
735                              !strcmp(tag, parent->u.tag.tag)))
736                         {
737                             level = i;
738                             break;
739                         }
740                     }
741                     if (i != level)
742                     {
743                         yaz_log (LOG_WARN, "%d: no begin tag for %s",
744                                  line, tag);
745                         break;
746                     }
747                 }
748                 if (data1_is_xmlmode(dh))
749                 {
750                     if (level <= 1)
751                         return d1_stack[0];
752                 }
753                 else
754                 {
755                     if (level <= 0)
756                         return d1_stack[0];
757                 }
758                 continue;
759             }   
760             else if (!strcmp(tag, "var"))
761             {
762                 char tclass[DATA1_MAX_SYMBOL], type[DATA1_MAX_SYMBOL];
763                 data1_vartype *tp;
764                 int val_offset;
765                 
766                 if (sscanf(args, "%s %s %n", tclass, type, &val_offset) != 2)
767                 {
768                     yaz_log(LOG_WARN, "Malformed variant triple at '%s'", tag);
769                     continue;
770                 }
771                 if (!(tp =
772                       data1_getvartypebyct(dh,
773                                            parent->root->u.root.absyn->varset,
774                                            tclass, type)))
775                     continue;
776                 /*
777                  * If we're the first variant in this group, create a parent 
778                  * variant, and insert it before the current variant.
779                  */
780                 if (parent->which != DATA1N_variant)
781                 {
782                     res = data1_mk_node2 (dh, m, DATA1N_variant, parent);
783                 }
784                 else
785                 {
786                     /*
787                      * now determine if one of our ancestor triples is of
788                      * same type. If so, we break here.
789                      */
790                     int i;
791                     for (i = level-1; d1_stack[i]->which==DATA1N_variant; --i)
792                         if (d1_stack[i]->u.variant.type == tp)
793                         {
794                             level = i;
795                             break;
796                         }
797                     res = data1_mk_node2 (dh, m, DATA1N_variant, parent);
798                     res->u.variant.type = tp;
799                     res->u.variant.value =
800                         data1_insert_string (dh, res, m, args + val_offset);
801                 }
802             }
803             else 
804             {
805                 
806                 /* tag .. acquire our element in the abstract syntax */
807                 if (level == 0)
808                 {
809                     parent = data1_mk_root (dh, m, tag);
810                     res = d1_stack[level] = parent;
811
812                     if (data1_is_xmlmode(dh))
813                     {
814                         level++;
815                         res = data1_mk_tag (dh, m, tag, 0 /* attr */, parent);
816                         res->u.tag.attributes = xattr;
817                     }
818                 }
819                 else
820                 {
821                     res = data1_mk_tag (dh, m, tag, 0 /* attr */, parent);
822                     res->u.tag.attributes = xattr;
823                 }
824             }
825             d1_stack[level] = res;
826             d1_stack[level+1] = 0;
827             if (level < 250 && !null_tag)
828                 ++level;
829         }
830         else /* != '<'... this is a body of text */
831         {
832             int len;
833             
834             if (level == 0)
835             {
836                 c = ampr (get_byte, fh, &amp);
837                 continue;
838             }
839             res = data1_mk_node2 (dh, m, DATA1N_data, parent);
840             res->u.data.what = DATA1I_text;
841             res->u.data.formatted_text = 0;
842             d1_stack[level] = res;
843             
844             wrbuf_rewind(wrbuf);
845
846             while (amp || (c && c != '<'))
847             {
848                 wrbuf_putc (wrbuf, c);
849                 c = ampr (get_byte, fh, &amp);
850             }
851             len = wrbuf_len(wrbuf);
852
853             /* use local buffer of nmem if too large */
854             if (len >= DATA1_LOCALDATA)
855                 res->u.data.data = (char*) nmem_malloc (m, len);
856             else
857                 res->u.data.data = res->lbuf;
858         
859             if (len)
860                 memcpy (res->u.data.data, wrbuf_buf(wrbuf), len);
861             else
862                 res->u.data.data = 0;
863             res->u.data.len = len;
864         }
865     }
866     return 0;
867 }
868
869 int getc_mem (void *fh)
870 {
871     const char **p = (const char **) fh;
872     if (**p)
873         return *(*p)++;
874     return 0;
875 }
876
877 data1_node *data1_read_node (data1_handle dh, const char **buf, NMEM m)
878 {
879     WRBUF wrbuf = wrbuf_alloc();
880     data1_node *node;
881
882     node = data1_read_nodex(dh, m, getc_mem, (void *) (buf), wrbuf);
883     wrbuf_free (wrbuf, 1);
884     return node;
885 }
886
887 /*
888  * Read a record in the native syntax.
889  */
890 data1_node *data1_read_record(data1_handle dh,
891                               int (*rf)(void *, char *, size_t), void *fh,
892                               NMEM m)
893 {
894     int *size;
895     char **buf = data1_get_read_buf (dh, &size);
896     const char *bp;
897     int rd = 0, res;
898     
899     if (!*buf)
900         *buf = (char *)xmalloc(*size = 4096);
901     
902     for (;;)
903     {
904         if (rd + 2048 >= *size && !(*buf =(char *)xrealloc(*buf, *size *= 2)))
905             abort();
906         if ((res = (*rf)(fh, *buf + rd, 2048)) <= 0)
907         {
908             if (!res)
909             {
910                 bp = *buf;
911                 (*buf)[rd] = '\0';
912                 return data1_read_node(dh, &bp, m);
913             }
914             else
915                 return 0;
916         }
917         rd += res;
918     }
919 }
920
921 data1_node *data1_read_sgml (data1_handle dh, NMEM m, const char *buf)
922 {
923     const char *bp = buf;
924     return data1_read_node (dh, &bp, m);
925 }
926
927
928 static int conv_item (NMEM m, yaz_iconv_t t, 
929                       WRBUF wrbuf, char *inbuf, size_t inlen)
930 {
931     wrbuf_rewind (wrbuf);
932     if (wrbuf->size < 10)
933         wrbuf_grow (wrbuf, 10);
934     for (;;)
935     {
936         char *outbuf = wrbuf->buf + wrbuf->pos;
937         size_t outlen = wrbuf->size - wrbuf->pos;
938         if (yaz_iconv (t, &inbuf, &inlen, &outbuf, &outlen) ==
939             (size_t)(-1) && yaz_iconv_error(t) != YAZ_ICONV_E2BIG)
940         {
941             /* bad data. stop and skip conversion entirely */
942             return -1;
943         }
944         else if (inlen == 0)
945         {   /* finished converting */
946             wrbuf->pos = wrbuf->size - outlen;
947             break;
948         }
949         else
950         {
951             /* buffer too small: make sure we expand buffer */
952             wrbuf->pos = wrbuf->size - outlen;
953             wrbuf_grow(wrbuf, 20);
954         }
955     }
956     return 0;
957 }
958
959 static void data1_iconv_s (data1_handle dh, NMEM m, data1_node *n,
960                            yaz_iconv_t t, WRBUF wrbuf, const char *tocode)
961 {
962     for (; n; n = n->next)
963     {
964         switch (n->which)
965         {
966         case DATA1N_data:
967         case DATA1N_comment:
968             if (conv_item (m, t, wrbuf, n->u.data.data, n->u.data.len) == 0)
969             {
970                 n->u.data.data =
971                     data1_insert_string_n (dh, n, m, wrbuf->buf,
972                                            wrbuf->pos);
973                 n->u.data.len = wrbuf->pos;
974             }
975             break;
976         case DATA1N_tag:
977             if (conv_item (m, t, wrbuf, n->u.tag.tag, strlen(n->u.tag.tag))
978                 == 0)
979             {
980                 n->u.tag.tag =
981                     data1_insert_string_n (dh, n, m, 
982                                            wrbuf->buf, wrbuf->pos);
983             }
984             if (n->u.tag.attributes)
985             {
986                 data1_xattr *p;
987                 for (p = n->u.tag.attributes; p; p = p->next)
988                 {
989                     if (p->value &&
990                         conv_item(m, t, wrbuf, p->value, strlen(p->value))
991                         == 0)
992                     {
993                         wrbuf_puts (wrbuf, "");
994                         p->value = nmem_strdup (m, wrbuf->buf);
995                     }
996                 }
997             }
998             break;
999         case DATA1N_preprocess:
1000             if (strcmp(n->u.preprocess.target, "xml") == 0)
1001             {
1002                 data1_xattr *p = n->u.preprocess.attributes;
1003                 for (; p; p = p->next)
1004                     if (strcmp (p->name, "encoding") == 0)
1005                         p->value = nmem_strdup (m, tocode);
1006             }
1007             break;
1008         }
1009         data1_iconv_s (dh, m, n->child, t, wrbuf, tocode);
1010     }
1011 }
1012
1013 const char *data1_get_encoding (data1_handle dh, data1_node *n)
1014 {
1015     /* see if we have an xml header that specifies encoding */
1016     if (n && n->child && n->child->which == DATA1N_preprocess &&
1017         strcmp (n->child->u.preprocess.target, "xml") == 0)
1018     {
1019         data1_xattr *xp = n->child->u.preprocess.attributes;
1020         for (; xp; xp = xp->next)
1021             if (!strcmp (xp->name, "encoding") == 0)
1022                 return xp->value;
1023     }
1024     /* no encoding in header, so see if "encoding" was specified for abs */
1025     if (n && n->which == DATA1N_root &&
1026         n->u.root.absyn && n->u.root.absyn->encoding)
1027         return n->u.root.absyn->encoding;
1028     /* none of above, return a hard coded default */
1029     return "ISO-8859-1";
1030 }
1031
1032 int data1_iconv (data1_handle dh, NMEM m, data1_node *n,
1033                   const char *tocode, 
1034                   const char *fromcode)
1035 {
1036     if (strcmp (tocode, fromcode))
1037     {
1038         WRBUF wrbuf = wrbuf_alloc();
1039         yaz_iconv_t t = yaz_iconv_open (tocode, fromcode);
1040         if (!t)
1041             return -1;
1042         data1_iconv_s (dh, m, n, t, wrbuf, tocode);
1043         yaz_iconv_close (t);
1044         wrbuf_free (wrbuf, 1);
1045     }
1046     return 0;
1047 }