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