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