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