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