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