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