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