data1 cleanup
[yaz-moved-to-github.git] / retrieval / d1_read.c
1 /*
2  * Copyright (c) 1995-2002, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Id: d1_read.c,v 1.40 2002-05-03 13:48:27 adam Exp $
7  */
8
9 #include <assert.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12
13 #include <yaz/xmalloc.h>
14 #include <yaz/log.h>
15 #include <yaz/data1.h>
16
17 /*
18  * get the tag which is the immediate parent of this node (this may mean
19  * traversing intermediate things like variants and stuff.
20  */
21 data1_node *get_parent_tag (data1_handle dh, data1_node *n)
22 {
23     for (; n && n->which != DATA1N_root; n = n->parent)
24         if (n->which == DATA1N_tag)
25             return n;
26     return 0;
27 }
28
29 data1_node *data1_mk_node (data1_handle dh, NMEM m, int type,
30                            data1_node *parent)
31 {
32     data1_node *r;
33
34     r = (data1_node *)nmem_malloc(m, sizeof(*r));
35     r->next = r->child = r->last_child = 0;
36     r->destroy = 0;
37
38     if (!parent)
39     {
40         r->root = r;
41         assert (type == DATA1N_root);
42     }
43     else
44     {
45         r->root = parent->root;
46         r->parent = parent;
47         if (!parent->child)
48             parent->child = parent->last_child = r;
49         else
50             parent->last_child->next = r;
51         parent->last_child = r;
52     }
53     r->which = type;
54     switch(type)
55     {
56     case DATA1N_tag:
57         r->u.tag.tag = 0;
58         r->u.tag.element = 0;
59         r->u.tag.no_data_requested = 0;
60         r->u.tag.node_selected = 0;
61         r->u.tag.make_variantlist = 0;
62         r->u.tag.get_bytes = -1;
63 #if DATA1_USING_XATTR
64         r->u.tag.attributes = 0;
65 #endif
66         break;
67     case DATA1N_root:
68         r->u.root.type = 0;
69         r->u.root.absyn = 0;
70         break;
71     case DATA1N_data:
72         r->u.data.data = 0;
73         r->u.data.len = 0;
74         r->u.data.what = 0;
75         r->u.data.formatted_text = 0;
76         break;
77     case DATA1N_variant:
78         r->u.variant.type = 0;
79         r->u.variant.value = 0;
80         break;
81     default:
82         logf (LOG_WARN, "data_mk_node_type. bad type = %d\n", type);
83     }
84     return r;
85 }
86
87 void data1_free_tree (data1_handle dh, data1_node *t)
88 {
89     data1_node *p = t->child, *pn;
90
91     while (p)
92     {
93         pn = p->next;
94         data1_free_tree (dh, p);
95         p = pn;
96     }
97     if (t->destroy)
98         (*t->destroy)(t);
99 }
100
101 data1_node *data1_mk_root (data1_handle dh, NMEM nmem,  const char *name)
102 {
103     data1_absyn *absyn = data1_get_absyn (dh, name);
104     data1_node *res;
105     if (!absyn)
106     {
107         yaz_log(LOG_WARN, "Unable to acquire abstract syntax " "for '%s'",
108                 name); 
109         /* It's now OK for a record not to have an absyn */
110     }
111     res = data1_mk_node (dh, nmem, DATA1N_root, 0);
112     res->u.root.type = data1_insert_string (dh, res, nmem, name);
113     res->u.root.absyn = absyn;
114     return res;
115 }
116
117 data1_node *data1_mk_tag_n (data1_handle dh, NMEM nmem, 
118                             const char *tag, size_t len, data1_node *at)
119 {
120     data1_node *partag = get_parent_tag(dh, at);
121     data1_node *res = data1_mk_node (dh, nmem, DATA1N_tag, at);
122     data1_element *e = NULL;
123     
124     res->u.tag.tag = data1_insert_string_n (dh, res, nmem, tag, len);
125     
126     if (partag)
127         e = partag->u.tag.element;
128     res->u.tag.element =
129         data1_getelementbytagname (dh, at->root->u.root.absyn,
130                                    e, res->u.tag.tag);
131     return res;
132 }
133
134 data1_node *data1_mk_tag (data1_handle dh, NMEM nmem,
135                           const char *tag, data1_node *at) 
136 {
137     return data1_mk_tag_n (dh, nmem, tag, strlen(tag), at);
138 }
139
140 data1_node *data1_search_tag (data1_handle dh, data1_node *n,
141                               const char *tag)
142 {
143     for (; n; n = n->next)
144         if (n->which == DATA1N_tag && n->u.tag.tag &&
145             !yaz_matchstr (tag, n->u.tag.tag))
146         {
147             return n;
148         }
149     return 0;
150 }
151
152 data1_node *data1_mk_tag_uni (data1_handle dh, NMEM nmem, 
153                               const char *tag, data1_node *at)
154 {
155     data1_node *node = data1_search_tag (dh, at->child, tag);
156     if (!node)
157         node = data1_mk_tag (dh, nmem, tag, at);
158     return node;
159 }
160
161
162 data1_node *data1_mk_text_n (data1_handle dh, NMEM mem,
163                              const char *buf, size_t len, data1_node *parent)
164 {
165     data1_node *res = data1_mk_node (dh, mem, DATA1N_data, parent);
166     res->u.data.what = DATA1I_text;
167     res->u.data.len = len;
168     
169     res->u.data.data = data1_insert_string_n (dh, res, mem, buf, len);
170     return res;
171 }
172
173
174 data1_node *data1_mk_text (data1_handle dh, NMEM mem,
175                            const char *buf, data1_node *parent)
176 {
177     return data1_mk_text_n (dh, mem, buf, strlen(buf), parent);
178 }
179
180 char *data1_insert_string_n (data1_handle dh, data1_node *res,
181                              NMEM m, const char *str, size_t len)
182 {
183     char *b;
184     if (len >= DATA1_LOCALDATA)
185         b = nmem_malloc (m, len+1);
186     else
187         b = res->lbuf;
188     memcpy (b, str, len);
189     b[len] = 0;
190     return b;
191 }
192
193 char *data1_insert_string (data1_handle dh, data1_node *res,
194                            NMEM m, const char *str)
195 {
196     return data1_insert_string_n (dh, res, m, str, strlen(str));
197 }
198
199 static data1_node *data1_add_insert_taggeddata(data1_handle dh,
200                                                data1_node *root,
201                                                data1_node *at,
202                                                const char *tagname, NMEM m,
203                                                int local_allowed)
204 {
205     data1_node *partag = get_parent_tag (dh, at);
206     data1_element *e = NULL;
207     data1_node *datn = 0;
208     data1_node *tagn = 0;
209
210     if (partag)
211         e = partag->u.tag.element;
212     e = data1_getelementbytagname (dh, root->u.root.absyn, e, tagname);
213     if (local_allowed || e)
214     {
215         tagn = data1_mk_node (dh, m, DATA1N_tag, at);
216         tagn->u.tag.tag = data1_insert_string (dh, tagn, m, tagname);
217         tagn->u.tag.element = e;
218         datn = data1_mk_node (dh, m, DATA1N_data, tagn);
219     }
220     return datn;
221 }
222
223 data1_node *data1_mk_tag_data(data1_handle dh, data1_node *root,
224                               data1_node *at,
225                               const char *tagname, NMEM m)
226 {
227     return data1_add_insert_taggeddata (dh, root, at, tagname, m, 1);
228 }
229
230
231 /*
232  * Insert a tagged node into the record root as first child of the node at
233  * which should be root or tag itself). Returns pointer to the data node,
234  * which can then be modified.
235  */
236 data1_node *data1_mk_tag_data_wd(data1_handle dh, data1_node *root,
237                                  data1_node *at,
238                                  const char *tagname, NMEM m)
239 {
240     return data1_add_insert_taggeddata (dh, root, at, tagname, m, 0);
241 }
242
243
244 data1_node *data1_mk_tag_data_int (data1_handle dh, data1_node *at,
245                                    const char *tag, int num,
246                                    NMEM nmem)
247 {
248     data1_node *node_data;
249     
250     node_data = data1_mk_tag_data (dh, at->root, at, tag, nmem);
251     if (!node_data)
252         return 0;
253     node_data->u.data.what = DATA1I_num;
254     node_data->u.data.data = node_data->lbuf;
255     sprintf (node_data->u.data.data, "%d", num);
256     node_data->u.data.len = strlen (node_data->u.data.data);
257     return node_data;
258 }
259
260 data1_node *data1_mk_tag_data_oid (data1_handle dh, data1_node *at,
261                                    const char *tag, Odr_oid *oid,
262                                    NMEM nmem)
263 {
264     data1_node *node_data;
265     char str[128], *p = str;
266     Odr_oid *ii;
267     
268     node_data = data1_mk_tag_data (dh, at->root, at, tag, nmem);
269     if (!node_data)
270         return 0;
271     
272     for (ii = oid; *ii >= 0; ii++)
273     {
274         if (ii != oid)
275             *p++ = '.';
276         sprintf (p, "%d", *ii);
277         p += strlen (p);
278     }
279     node_data->u.data.what = DATA1I_oid;
280     node_data->u.data.len = strlen (str);
281     node_data->u.data.data = data1_insert_string (dh, node_data, nmem, str);
282     return node_data;
283 }
284
285
286 data1_node *data1_mk_tag_data_text (data1_handle dh, data1_node *at,
287                                     const char *tag, const char *str,
288                                     NMEM nmem)
289 {
290     data1_node *node_data;
291     
292     node_data = data1_mk_tag_data (dh, at->root, at, tag, nmem);
293     if (!node_data)
294         return 0;
295     node_data->u.data.what = DATA1I_text;
296     node_data->u.data.len = strlen (str);
297     node_data->u.data.data = data1_insert_string (dh, node_data, nmem, str);
298     return node_data;
299 }
300
301
302 data1_node *data1_mk_tag_data_text_uni (data1_handle dh, data1_node *at,
303                                         const char *tag, const char *str,
304                                         NMEM nmem)
305 {
306     data1_node *node = data1_search_tag (dh, at->child, tag);
307     if (!node)
308         return data1_mk_tag_data_text (dh, at, tag, str, nmem);
309     else
310     {
311         data1_node *node_data = node->child;
312         node_data->u.data.what = DATA1I_text;
313         node_data->u.data.len = strlen (str);
314         node_data->u.data.data = data1_insert_string (dh, node_data,
315                                                       nmem, str);
316         return node_data;
317     }
318 }
319
320
321 #if DATA1_USING_XATTR
322 data1_xattr *data1_read_xattr (data1_handle dh, NMEM m,
323                                int (*get_byte)(void *fh), void *fh,
324                                WRBUF wrbuf, int *ch)
325 {
326     data1_xattr *p_first = 0;
327     data1_xattr **pp = &p_first;
328     int c = *ch;
329     for (;;)
330     {
331         data1_xattr *p;
332         int len;
333         while (c && d1_isspace(c))
334             c = (*get_byte)(fh);
335         if (!c  || c == '>' || c == '/')
336             break;
337         *pp = p = (data1_xattr *) nmem_malloc (m, sizeof(*p));
338         p->next = 0;
339         pp = &p->next;
340         p->value = 0;
341         
342         wrbuf_rewind(wrbuf);
343         while (c && c != '=' && c != '>' && c != '/' && !d1_isspace(c))
344         {
345             wrbuf_putc (wrbuf, c);
346             c = (*get_byte)(fh);
347         }
348         wrbuf_putc (wrbuf, '\0');
349         len = wrbuf_len(wrbuf);
350         p->name = (char*) nmem_malloc (m, len);
351         strcpy (p->name, wrbuf_buf(wrbuf));
352         if (c == '=')
353         {
354             c = (*get_byte)(fh);
355             if (c == '"')
356             {
357                 c = (*get_byte)(fh);    
358                 wrbuf_rewind(wrbuf);
359                 while (c && c != '"')
360                 {
361                     wrbuf_putc (wrbuf, c);
362                     c = (*get_byte)(fh);
363                 }
364                 if (c)
365                     c = (*get_byte)(fh);        
366             }
367             else
368             {
369                 wrbuf_rewind(wrbuf);
370                 while (c && c != '>' && c != '/')
371                 {
372                     wrbuf_putc (wrbuf, c);
373                     c = (*get_byte)(fh);
374                 }
375             }
376             wrbuf_putc (wrbuf, '\0');
377             len = wrbuf_len(wrbuf);
378             p->value = (char*) nmem_malloc (m, len);
379             strcpy (p->value, wrbuf_buf(wrbuf));
380         }
381     }
382     *ch = c;
383     return p_first;
384 }
385 #endif
386
387 /*
388  * Ugh. Sometimes functions just grow and grow on you. This one reads a
389  * 'node' and its children.
390  */
391 data1_node *data1_read_nodex (data1_handle dh, NMEM m,
392                               int (*get_byte)(void *fh), void *fh, WRBUF wrbuf)
393 {
394     data1_node *d1_stack[256];
395     data1_node *res;
396     int c;
397     int level = 0;
398     int line = 1;
399
400     d1_stack[level] = 0;
401     c = (*get_byte)(fh);
402     while (1)
403     {
404         data1_node *parent = level ? d1_stack[level-1] : 0;
405         while (c != '\0' && d1_isspace(c))
406         {
407             if (c == '\n')
408                 line++;
409             c = (*get_byte)(fh);
410         }
411         if (c == '\0')
412             break;
413         
414         if (c == '<') /* beginning of tag */
415         {
416 #if DATA1_USING_XATTR
417             data1_xattr *xattr;
418 #endif
419             char tag[64];
420             char args[256];
421             int null_tag = 0;
422             int end_tag = 0;
423             size_t i = 0;
424
425             c = (*get_byte)(fh);
426             if (c == '/')
427             {
428                 end_tag = 1;
429                 c = (*get_byte)(fh);
430             }
431             else if (c == '!')  /* tags/comments that we don't deal with yet */
432             {
433                 while (c && c != '>')
434                     c = (*get_byte)(fh);
435                 if (c)
436                     c = (*get_byte)(fh);
437                 continue;
438             }
439             while (c && c != '>' && c != '/' && !d1_isspace(c))
440             {
441                 if (i < (sizeof(tag)-1))
442                     tag[i++] = c;
443                 c = (*get_byte)(fh);
444             }
445             tag[i] = '\0';
446 #if DATA1_USING_XATTR
447             xattr = data1_read_xattr (dh, m, get_byte, fh, wrbuf, &c);
448             args[0] = '\0';
449 #else
450             while (d1_isspace(c))
451                 c = (*get_byte)(fh);
452             for (i = 0; c && c != '>' && c != '/'; c = (*get_byte)(fh))
453                 if (i < (sizeof(args)-1))
454                     args[i++] = c;
455             args[i] = '\0';
456 #endif
457             if (c == '/')
458             {    /* <tag attrs/> or <tag/> */
459                 null_tag = 1;
460                 c = (*get_byte)(fh);
461             }
462             if (c != '>')
463             {
464                 yaz_log(LOG_WARN, "d1: %d: Malformed tag", line);
465                 return 0;
466             }
467             else
468                 c = (*get_byte)(fh);
469
470             /* End tag? */
471             if (end_tag)       
472             {
473                 if (*tag == '\0')
474                     --level;        /* </> */
475                 else
476                 {                   /* </tag> */
477                     int i = level;
478                     while (i > 0)
479                     {
480                         parent = d1_stack[--i];
481                         if ((parent->which == DATA1N_root &&
482                              !strcmp(tag, parent->u.root.type)) ||
483                             (parent->which == DATA1N_tag &&
484                              !strcmp(tag, parent->u.tag.tag)))
485                         {
486                             level = i;
487                             break;
488                         }
489                     }
490                     if (i != level)
491                     {
492                         yaz_log (LOG_WARN, "%d: no begin tag for %s",
493                                  line, tag);
494                         break;
495                     }
496                 }
497                 if (level == 0)
498                     return d1_stack[0];
499                 continue;
500             }   
501             if (level == 0) /* root ? */
502             {
503                 res = data1_mk_root (dh, m, tag);
504             }
505             else if (!strcmp(tag, "var"))
506             {
507                 char tclass[DATA1_MAX_SYMBOL], type[DATA1_MAX_SYMBOL];
508                 data1_vartype *tp;
509                 int val_offset;
510                 
511                 if (sscanf(args, "%s %s %n", tclass, type, &val_offset) != 2)
512                 {
513                     yaz_log(LOG_WARN, "Malformed variant triple at '%s'", tag);
514                     continue;
515                 }
516                 if (!(tp =
517                       data1_getvartypebyct(dh,
518                                            parent->root->u.root.absyn->varset,
519                                            tclass, type)))
520                     continue;
521                 /*
522                  * If we're the first variant in this group, create a parent 
523                  * variant, and insert it before the current variant.
524                  */
525                 if (parent->which != DATA1N_variant)
526                 {
527                     res = data1_mk_node (dh, m, DATA1N_variant, parent);
528                 }
529                 else
530                 {
531                     /*
532                      * now determine if one of our ancestor triples is of
533                      * same type. If so, we break here.
534                      */
535                     int i;
536                     for (i = level-1; d1_stack[i]->which==DATA1N_variant; --i)
537                         if (d1_stack[i]->u.variant.type == tp)
538                         {
539                             level = i;
540                             break;
541                         }
542                     res = data1_mk_node (dh, m, DATA1N_variant, parent);
543                     res->u.variant.type = tp;
544                     res->u.variant.value =
545                         data1_insert_string (dh, res, m, args + val_offset);
546                 }
547             }
548             else /* tag.. acquire our element in the abstract syntax */
549                 res = data1_mk_tag (dh, m, tag, parent);
550             d1_stack[level] = res;
551             d1_stack[level+1] = 0;
552             if (level < 250 && !null_tag)
553                 ++level;
554         }
555         else /* != '<'... this is a body of text */
556         {
557             const char *src;
558             char *dst;
559             int len, prev_char = 0;
560             
561             if (level == 0)
562             {
563                 c = (*get_byte)(fh);
564                 continue;
565             }
566             res = data1_mk_node (dh, m, DATA1N_data, parent);
567             res->u.data.what = DATA1I_text;
568             res->u.data.formatted_text = 0;
569             d1_stack[level] = res;
570             
571             wrbuf_rewind(wrbuf);
572
573             while (c && c != '<')
574             {
575                 wrbuf_putc (wrbuf, c);
576                 c = (*get_byte)(fh);
577             }
578             len = wrbuf_len(wrbuf);
579
580             /* use local buffer of nmem if too large */
581             if (len >= DATA1_LOCALDATA)
582                 res->u.data.data = (char*) nmem_malloc (m, len);
583             else
584                 res->u.data.data = res->lbuf;
585             
586             /* read "data" and transfer while removing white space */
587             dst = res->u.data.data;
588             for (src = wrbuf_buf(wrbuf); --len >= 0; src++)
589             {
590                 if (*src == '\n')
591                     line++;
592                 if (d1_isspace (*src))
593                     prev_char = ' ';
594                 else
595                 {
596                     if (prev_char)
597                     {
598                         *dst++ = prev_char;
599                         prev_char = 0;
600                     }
601                     *dst++ = *src;
602                 }
603             }
604             res->u.data.len = dst - res->u.data.data;
605         }
606     }
607     return 0;
608 }
609
610 int getc_mem (void *fh)
611 {
612     const char **p = (const char **) fh;
613     if (**p)
614         return *(*p)++;
615     return 0;
616 }
617
618 data1_node *data1_read_node (data1_handle dh, const char **buf, NMEM m)
619 {
620     WRBUF wrbuf = wrbuf_alloc();
621     data1_node *node;
622
623     node = data1_read_nodex(dh, m, getc_mem, (void *) (buf), wrbuf);
624     wrbuf_free (wrbuf, 1);
625     return node;
626 }
627
628 /*
629  * Read a record in the native syntax.
630  */
631 data1_node *data1_read_record(data1_handle dh,
632                               int (*rf)(void *, char *, size_t), void *fh,
633                               NMEM m)
634 {
635     int *size;
636     char **buf = data1_get_read_buf (dh, &size);
637     const char *bp;
638     int rd = 0, res;
639     
640     if (!*buf)
641         *buf = (char *)xmalloc(*size = 4096);
642     
643     for (;;)
644     {
645         if (rd + 2048 >= *size && !(*buf =(char *)xrealloc(*buf, *size *= 2)))
646             abort();
647         if ((res = (*rf)(fh, *buf + rd, 2048)) <= 0)
648         {
649             if (!res)
650             {
651                 bp = *buf;
652                 (*buf)[rd] = '\0';
653                 return data1_read_node(dh, &bp, m);
654             }
655             else
656                 return 0;
657         }
658         rd += res;
659     }
660 }
661
662 data1_node *data1_read_sgml (data1_handle dh, NMEM m, const char *buf)
663 {
664     const char *bp = buf;
665     return data1_read_node (dh, &bp, m);
666 }
667