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