7f3c4a3265e3664b0d2c64d77884a67c461595ee
[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.48 2002-08-19 21:09:10 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
487 data1_xattr *data1_read_xattr (data1_handle dh, NMEM m,
488                                int (*get_byte)(void *fh), void *fh,
489                                WRBUF wrbuf, int *ch)
490 {
491     data1_xattr *p_first = 0;
492     data1_xattr **pp = &p_first;
493     int c = *ch;
494     for (;;)
495     {
496         data1_xattr *p;
497         int len;
498         while (c && d1_isspace(c))
499             c = (*get_byte)(fh);
500         if (!c  || c == '>' || c == '/')
501             break;
502         *pp = p = (data1_xattr *) nmem_malloc (m, sizeof(*p));
503         p->next = 0;
504         pp = &p->next;
505         p->value = 0;
506         
507         wrbuf_rewind(wrbuf);
508         while (c && c != '=' && c != '>' && c != '/' && !d1_isspace(c))
509         {
510             wrbuf_putc (wrbuf, c);
511             c = (*get_byte)(fh);
512         }
513         wrbuf_putc (wrbuf, '\0');
514         len = wrbuf_len(wrbuf);
515         p->name = (char*) nmem_malloc (m, len);
516         strcpy (p->name, wrbuf_buf(wrbuf));
517         if (c == '=')
518         {
519             c = (*get_byte)(fh);
520             if (c == '"')
521             {
522                 c = (*get_byte)(fh);    
523                 wrbuf_rewind(wrbuf);
524                 while (c && c != '"')
525                 {
526                     wrbuf_putc (wrbuf, c);
527                     c = (*get_byte)(fh);
528                 }
529                 if (c)
530                     c = (*get_byte)(fh);        
531             }
532             else
533             {
534                 wrbuf_rewind(wrbuf);
535                 while (c && c != '>' && c != '/')
536                 {
537                     wrbuf_putc (wrbuf, c);
538                     c = (*get_byte)(fh);
539                 }
540             }
541             wrbuf_putc (wrbuf, '\0');
542             len = wrbuf_len(wrbuf);
543             p->value = (char*) nmem_malloc (m, len);
544             strcpy (p->value, wrbuf_buf(wrbuf));
545         }
546     }
547     *ch = c;
548     return p_first;
549 }
550
551 /*
552  * Ugh. Sometimes functions just grow and grow on you. This one reads a
553  * 'node' and its children.
554  */
555 data1_node *data1_read_nodex (data1_handle dh, NMEM m,
556                               int (*get_byte)(void *fh), void *fh, WRBUF wrbuf)
557 {
558     data1_node *d1_stack[256];
559     data1_node *res;
560     int c;
561     int level = 0;
562     int line = 1;
563
564     d1_stack[level] = 0;
565     c = (*get_byte)(fh);
566     while (1)
567     {
568         data1_node *parent = level ? d1_stack[level-1] : 0;
569         while (c != '\0' && d1_isspace(c))
570         {
571             if (c == '\n')
572                 line++;
573             c = (*get_byte)(fh);
574         }
575         if (c == '\0')
576             break;
577         
578         if (c == '<') /* beginning of tag */
579         {
580             data1_xattr *xattr;
581
582             char tag[64];
583             char args[256];
584             int null_tag = 0;
585             int end_tag = 0;
586             size_t i = 0;
587
588             c = (*get_byte)(fh);
589             if (c == '/')
590             {
591                 end_tag = 1;
592                 c = (*get_byte)(fh);
593             }
594             else if (c == '!')  /* tags/comments that we don't deal with yet */
595             {
596                 while (c && c != '>')
597                     c = (*get_byte)(fh);
598                 if (c)
599                     c = (*get_byte)(fh);
600                 continue;
601             }
602             while (c && c != '>' && c != '/' && !d1_isspace(c))
603             {
604                 if (i < (sizeof(tag)-1))
605                     tag[i++] = c;
606                 c = (*get_byte)(fh);
607             }
608             tag[i] = '\0';
609             xattr = data1_read_xattr (dh, m, get_byte, fh, wrbuf, &c);
610             args[0] = '\0';
611             if (c == '/')
612             {    /* <tag attrs/> or <tag/> */
613                 null_tag = 1;
614                 c = (*get_byte)(fh);
615             }
616             if (c != '>')
617             {
618                 yaz_log(LOG_WARN, "d1: %d: Malformed tag", line);
619                 return 0;
620             }
621             else
622                 c = (*get_byte)(fh);
623
624             /* End tag? */
625             if (end_tag)       
626             {
627                 if (*tag == '\0')
628                     --level;        /* </> */
629                 else
630                 {                   /* </tag> */
631                     int i = level;
632                     while (i > 0)
633                     {
634                         parent = d1_stack[--i];
635                         if ((parent->which == DATA1N_root &&
636                              !strcmp(tag, parent->u.root.type)) ||
637                             (parent->which == DATA1N_tag &&
638                              !strcmp(tag, parent->u.tag.tag)))
639                         {
640                             level = i;
641                             break;
642                         }
643                     }
644                     if (i != level)
645                     {
646                         yaz_log (LOG_WARN, "%d: no begin tag for %s",
647                                  line, tag);
648                         break;
649                     }
650                 }
651                 if (data1_is_xmlmode(dh))
652                 {
653                     if (level <= 1)
654                         return d1_stack[0];
655                 }
656                 else
657                 {
658                     if (level <= 0)
659                         return d1_stack[0];
660                 }
661                 continue;
662             }   
663             else if (!strcmp(tag, "var"))
664             {
665                 char tclass[DATA1_MAX_SYMBOL], type[DATA1_MAX_SYMBOL];
666                 data1_vartype *tp;
667                 int val_offset;
668                 
669                 if (sscanf(args, "%s %s %n", tclass, type, &val_offset) != 2)
670                 {
671                     yaz_log(LOG_WARN, "Malformed variant triple at '%s'", tag);
672                     continue;
673                 }
674                 if (!(tp =
675                       data1_getvartypebyct(dh,
676                                            parent->root->u.root.absyn->varset,
677                                            tclass, type)))
678                     continue;
679                 /*
680                  * If we're the first variant in this group, create a parent 
681                  * variant, and insert it before the current variant.
682                  */
683                 if (parent->which != DATA1N_variant)
684                 {
685                     res = data1_mk_node2 (dh, m, DATA1N_variant, parent);
686                 }
687                 else
688                 {
689                     /*
690                      * now determine if one of our ancestor triples is of
691                      * same type. If so, we break here.
692                      */
693                     int i;
694                     for (i = level-1; d1_stack[i]->which==DATA1N_variant; --i)
695                         if (d1_stack[i]->u.variant.type == tp)
696                         {
697                             level = i;
698                             break;
699                         }
700                     res = data1_mk_node2 (dh, m, DATA1N_variant, parent);
701                     res->u.variant.type = tp;
702                     res->u.variant.value =
703                         data1_insert_string (dh, res, m, args + val_offset);
704                 }
705             }
706             else 
707             {
708                 
709                 /* tag .. acquire our element in the abstract syntax */
710                 if (level == 0)
711                 {
712                     parent = data1_mk_root (dh, m, tag);
713                     res = d1_stack[level] = parent;
714
715                     if (data1_is_xmlmode(dh))
716                     {
717                         level++;
718                         res = data1_mk_tag (dh, m, tag, 0 /* attr */, parent);
719                         res->u.tag.attributes = xattr;
720                     }
721                 }
722                 else
723                 {
724                     res = data1_mk_tag (dh, m, tag, 0 /* attr */, parent);
725                     res->u.tag.attributes = xattr;
726                 }
727             }
728             d1_stack[level] = res;
729             d1_stack[level+1] = 0;
730             if (level < 250 && !null_tag)
731                 ++level;
732         }
733         else /* != '<'... this is a body of text */
734         {
735             const char *src;
736             char *dst;
737             int len, prev_char = 0;
738             
739             if (level == 0)
740             {
741                 c = (*get_byte)(fh);
742                 continue;
743             }
744             res = data1_mk_node2 (dh, m, DATA1N_data, parent);
745             res->u.data.what = DATA1I_text;
746             res->u.data.formatted_text = 0;
747             d1_stack[level] = res;
748             
749             wrbuf_rewind(wrbuf);
750
751             while (c && c != '<')
752             {
753                 wrbuf_putc (wrbuf, c);
754                 c = (*get_byte)(fh);
755             }
756             len = wrbuf_len(wrbuf);
757
758             /* use local buffer of nmem if too large */
759             if (len >= DATA1_LOCALDATA)
760                 res->u.data.data = (char*) nmem_malloc (m, len);
761             else
762                 res->u.data.data = res->lbuf;
763             
764             /* read "data" and transfer while removing white space */
765             dst = res->u.data.data;
766             for (src = wrbuf_buf(wrbuf); --len >= 0; src++)
767             {
768                 if (*src == '\n')
769                     line++;
770                 if (d1_isspace (*src))
771                     prev_char = ' ';
772                 else
773                 {
774                     if (prev_char)
775                     {
776                         *dst++ = prev_char;
777                         prev_char = 0;
778                     }
779                     *dst++ = *src;
780                 }
781             }
782             res->u.data.len = dst - res->u.data.data;
783         }
784     }
785     return 0;
786 }
787
788 int getc_mem (void *fh)
789 {
790     const char **p = (const char **) fh;
791     if (**p)
792         return *(*p)++;
793     return 0;
794 }
795
796 data1_node *data1_read_node (data1_handle dh, const char **buf, NMEM m)
797 {
798     WRBUF wrbuf = wrbuf_alloc();
799     data1_node *node;
800
801     node = data1_read_nodex(dh, m, getc_mem, (void *) (buf), wrbuf);
802     wrbuf_free (wrbuf, 1);
803     return node;
804 }
805
806 /*
807  * Read a record in the native syntax.
808  */
809 data1_node *data1_read_record(data1_handle dh,
810                               int (*rf)(void *, char *, size_t), void *fh,
811                               NMEM m)
812 {
813     int *size;
814     char **buf = data1_get_read_buf (dh, &size);
815     const char *bp;
816     int rd = 0, res;
817     
818     if (!*buf)
819         *buf = (char *)xmalloc(*size = 4096);
820     
821     for (;;)
822     {
823         if (rd + 2048 >= *size && !(*buf =(char *)xrealloc(*buf, *size *= 2)))
824             abort();
825         if ((res = (*rf)(fh, *buf + rd, 2048)) <= 0)
826         {
827             if (!res)
828             {
829                 bp = *buf;
830                 (*buf)[rd] = '\0';
831                 return data1_read_node(dh, &bp, m);
832             }
833             else
834                 return 0;
835         }
836         rd += res;
837     }
838 }
839
840 data1_node *data1_read_sgml (data1_handle dh, NMEM m, const char *buf)
841 {
842     const char *bp = buf;
843     return data1_read_node (dh, &bp, m);
844 }
845
846
847 #if HAVE_ICONV_H
848
849 static int conv_item (NMEM m, iconv_t t, 
850                       WRBUF wrbuf, char *inbuf, size_t inlen)
851 {
852     wrbuf_rewind (wrbuf);
853     if (wrbuf->size < 10)
854         wrbuf_grow (wrbuf, 10);
855     for (;;)
856     {
857         char *outbuf = wrbuf->buf + wrbuf->pos;
858         size_t outlen = wrbuf->size - wrbuf->pos;
859         if (iconv (t, &inbuf, &inlen, &outbuf, &outlen) ==
860             (size_t)(-1) && errno != E2BIG)
861         {
862             /* bad data. stop and skip conversion entirely */
863             return -1;
864         }
865         else if (inlen == 0)
866         {   /* finished converting */
867             wrbuf->pos = wrbuf->size - outlen;
868             break;
869         }
870         else
871         {
872             /* buffer too small: make sure we expand buffer */
873             wrbuf->pos = wrbuf->size - outlen;
874             wrbuf_grow(wrbuf, 20);
875         }
876     }
877     return 0;
878 }
879
880 static void data1_iconv_s (data1_handle dh, NMEM m, data1_node *n,
881                            iconv_t t, WRBUF wrbuf, const char *tocode)
882 {
883     for (; n; n = n->next)
884     {
885         switch (n->which)
886         {
887         case DATA1N_data:
888         case DATA1N_comment:
889             if (conv_item (m, t, wrbuf, n->u.data.data, n->u.data.len) == 0)
890             {
891                 n->u.data.data =
892                     data1_insert_string_n (dh, n, m, wrbuf->buf,
893                                            wrbuf->pos);
894                 n->u.data.len = wrbuf->pos;
895             }
896             break;
897         case DATA1N_tag:
898             if (conv_item (m, t, wrbuf, n->u.tag.tag, strlen(n->u.tag.tag))
899                 == 0)
900             {
901                 n->u.tag.tag =
902                     data1_insert_string_n (dh, n, m, 
903                                            wrbuf->buf, wrbuf->pos);
904             }
905             if (n->u.tag.attributes)
906             {
907                 data1_xattr *p;
908                 for (p = n->u.tag.attributes; p; p = p->next)
909                 {
910                     if (p->value &&
911                         conv_item(m, t, wrbuf, p->value, strlen(p->value))
912                         == 0)
913                     {
914                         wrbuf_puts (wrbuf, "");
915                         p->value = nmem_strdup (m, wrbuf->buf);
916                     }
917                 }
918             }
919             break;
920         case DATA1N_preprocess:
921             if (strcmp(n->u.preprocess.target, "xml") == 0)
922             {
923                 data1_xattr *p = n->u.preprocess.attributes;
924                 for (; p; p = p->next)
925                     if (strcmp (p->name, "encoding") == 0)
926                         p->value = nmem_strdup (m, tocode);
927             }
928             break;
929         }
930         data1_iconv_s (dh, m, n->child, t, wrbuf, tocode);
931     }
932 }
933 #endif
934
935 const char *data1_get_encoding (data1_handle dh, data1_node *n)
936 {
937     /* see if we have an xml header that specifies encoding */
938     if (n && n->child && n->child->which == DATA1N_preprocess &&
939         strcmp (n->child->u.preprocess.target, "xml") == 0)
940     {
941         data1_xattr *xp = n->child->u.preprocess.attributes;
942         for (; xp; xp = xp->next)
943             if (!strcmp (xp->name, "encoding") == 0)
944                 return xp->value;
945     }
946     /* no encoding in header, so see if "encoding" was specified for abs */
947     if (n && n->which == DATA1N_root &&
948         n->u.root.absyn && n->u.root.absyn->encoding)
949         return n->u.root.absyn->encoding;
950     /* none of above, return a hard coded default */
951     return "ISO-8859-1";
952 }
953
954 int data1_iconv (data1_handle dh, NMEM m, data1_node *n,
955                   const char *tocode, 
956                   const char *fromcode)
957 {
958 #if HAVE_ICONV_H
959     if (strcmp (tocode, fromcode))
960     {
961         WRBUF wrbuf = wrbuf_alloc();
962         iconv_t t = iconv_open (tocode, fromcode);
963         if (t == (iconv_t) (-1))
964             return -1;
965         data1_iconv_s (dh, m, n, t, wrbuf, tocode);
966         iconv_close (t);
967         wrbuf_free (wrbuf, 1);
968     }
969     return 0;
970 #else
971     return -2;
972 #endif
973 }