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