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