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