Fixed bug with termlists (introduced by previous commit).
[yaz-moved-to-github.git] / retrieval / d1_read.c
1 /*
2  * Copyright (c) 1995-2000, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: d1_read.c,v $
7  * Revision 1.34  2000-12-05 10:06:23  adam
8  * Added support for null-data rules like <tag/>.
9  *
10  * Revision 1.33  2000/11/29 14:22:47  adam
11  * Implemented XML/SGML attributes for data1 so that d1_read reads them
12  * and d1_write generates proper attributes for XML/SGML records. Added
13  * register locking for threaded version.
14  *
15  * Revision 1.32  2000/01/06 11:25:59  adam
16  * Added case to prevent warning.
17  *
18  * Revision 1.31  1999/12/21 14:16:20  ian
19  * Changed retrieval module to allow data1 trees with no associated absyn.
20  * Also added a simple interface for extracting values from data1 trees using
21  * a string based tagpath.
22  *
23  * Revision 1.30  1999/11/30 13:47:12  adam
24  * Improved installation. Moved header files to include/yaz.
25  *
26  * Revision 1.29  1999/10/21 12:06:29  adam
27  * Retrieval module no longer uses ctype.h - functions.
28  *
29  * Revision 1.28  1999/10/21 09:50:33  adam
30  * SGML reader uses own isspace - it doesn't do 8-bit on WIN32!
31  *
32  * Revision 1.27  1999/08/27 09:40:32  adam
33  * Renamed logf function to yaz_log. Removed VC++ project files.
34  *
35  * Revision 1.26  1999/07/13 13:23:48  adam
36  * Non-recursive version of data1_read_node. data1_read_nodex reads
37  * stream of bytes (instead of buffer in memory).
38  *
39  * Revision 1.25  1999/04/20 09:56:48  adam
40  * Added 'name' paramter to encoder/decoder routines (typedef Odr_fun).
41  * Modified all encoders/decoders to reflect this change.
42  *
43  * Revision 1.24  1998/10/28 15:10:09  adam
44  * Added --with-yc option to configure. For the data1_node in data1.h:
45  * decreased size of localdata and removed member "line" which wasn't useful.
46  *
47  * Revision 1.23  1998/03/12 11:28:45  adam
48  * Fix: didn't set root member of tagged node in function.
49  * data1_add_insert_taggeddata.
50  *
51  * Revision 1.22  1998/03/05 08:15:32  adam
52  * Implemented data1_add_insert_taggeddata utility which is more flexible
53  * than data1_insert_taggeddata.
54  *
55  * Revision 1.21  1998/02/27 14:08:05  adam
56  * Added const to some char pointer arguments.
57  * Reworked data1_read_node so that it doesn't create a tree with
58  * pointers to original "SGML"-buffer.
59  *
60  * Revision 1.20  1998/02/11 11:53:35  adam
61  * Changed code so that it compiles as C++.
62  *
63  * Revision 1.19  1997/12/09 16:17:09  adam
64  * Fix bug regarding variants. Tags with prefix "var" was incorrectly
65  * interpreted as "start of variants". Now, only "var" indicates such
66  * start.
67  * Cleaned up data1_read_node so tag names and variant names are
68  * copied and not pointed to by the generated data1 tree. Data nodes
69  * still point to old buffer.
70  *
71  * Revision 1.18  1997/11/18 09:51:09  adam
72  * Removed element num_children from data1_node. Minor changes in
73  * data1 to Explain.
74  *
75  * Revision 1.17  1997/11/05 09:20:51  adam
76  * Minor change.
77  *
78  * Revision 1.16  1997/09/17 12:10:37  adam
79  * YAZ version 1.4.
80  *
81  * Revision 1.15  1997/09/05 09:50:57  adam
82  * Removed global data1_tabpath - uses data1_get_tabpath() instead.
83  *
84  * Revision 1.14  1997/05/14 06:54:04  adam
85  * C++ support.
86  *
87  * Revision 1.13  1996/10/29 13:35:38  adam
88  * Implemented data1_set_tabpath and data1_get_tabpath.
89  *
90  * Revision 1.12  1996/10/11 10:35:38  adam
91  * Fixed a bug that caused data1_read_node to core dump when no abstract
92  * syntax was defined in a "sgml"-record.
93  *
94  * Revision 1.11  1996/07/06 19:58:35  quinn
95  * System headerfiles gathered in yconfig
96  *
97  * Revision 1.10  1996/01/19  15:41:47  quinn
98  * Fixed uninitialized boolean.
99  *
100  * Revision 1.9  1996/01/17  14:52:47  adam
101  * Changed prototype for reader function parsed to data1_read_record.
102  *
103  * Revision 1.8  1995/12/15  16:20:41  quinn
104  * Added formatted text.
105  *
106  * Revision 1.7  1995/12/13  13:44:32  quinn
107  * Modified Data1-system to use nmem
108  *
109  * Revision 1.6  1995/12/12  16:37:08  quinn
110  * Added destroy element to data1_node.
111  *
112  * Revision 1.5  1995/12/11  15:22:37  quinn
113  * Added last_child field to the node.
114  * Rewrote schema-mapping.
115  *
116  * Revision 1.4  1995/11/13  09:27:36  quinn
117  * Fiddling with the variant stuff.
118  *
119  * Revision 1.3  1995/11/01  16:34:57  quinn
120  * Making data1 look for tables in data1_tabpath
121  *
122  * Revision 1.2  1995/11/01  13:54:48  quinn
123  * Minor adjustments
124  *
125  * Revision 1.1  1995/11/01  11:56:09  quinn
126  * Added Retrieval (data management) functions en masse.
127  *
128  * Revision 1.14  1995/10/30  12:40:55  quinn
129  * Fixed a couple of bugs.
130  *
131  * Revision 1.13  1995/10/25  16:00:47  quinn
132  * USMARC support is now almost operational
133  *
134  * Revision 1.12  1995/10/16  14:02:55  quinn
135  * Changes to support element set names and espec1
136  *
137  * Revision 1.11  1995/10/13  16:05:08  quinn
138  * Adding Espec1-processing
139  *
140  * Revision 1.10  1995/10/11  14:53:44  quinn
141  * Work on variants.
142  *
143  * Revision 1.9  1995/10/06  16:56:50  quinn
144  * Fixed ranked result.
145  *
146  * Revision 1.8  1995/10/06  16:44:13  quinn
147  * Work on attribute set mapping, etc.
148  *
149  * Revision 1.7  1995/10/06  12:58:35  quinn
150  * SUTRS support
151  *
152  * Revision 1.6  1995/10/04  09:29:49  quinn
153  * Adjustments to support USGS test data
154  *
155  * Revision 1.5  1995/10/03  17:56:43  quinn
156  * Fixing GRS code.
157  *
158  * Revision 1.4  1995/10/02  15:53:19  quinn
159  * Work
160  *
161  * Revision 1.3  1995/10/02  14:55:21  quinn
162  * *** empty log message ***
163  *
164  * Revision 1.2  1995/09/14  15:18:13  quinn
165  * Work
166  *
167  * Revision 1.1  1995/09/12  11:24:30  quinn
168  * Beginning to add code for structured records.
169  *
170  *
171  */
172
173 #include <assert.h>
174 #include <stdio.h>
175 #include <stdlib.h>
176
177 #include <yaz/xmalloc.h>
178 #include <yaz/log.h>
179 #include <yaz/data1.h>
180
181 /*
182  * get the tag which is the immediate parent of this node (this may mean
183  * traversing intermediate things like variants and stuff.
184  */
185 data1_node *get_parent_tag (data1_handle dh, data1_node *n)
186 {
187     for (; n && n->which != DATA1N_root; n = n->parent)
188         if (n->which == DATA1N_tag)
189             return n;
190     return 0;
191 }
192
193 data1_node *data1_mk_node (data1_handle dh, NMEM m)
194 {
195     data1_node *r;
196
197     r = (data1_node *)nmem_malloc(m, sizeof(*r));
198     r->next = r->child = r->last_child = r->parent = 0;
199     r->destroy = 0;
200     return r;
201 }
202
203 data1_node *data1_mk_node_type (data1_handle dh, NMEM m, int type)
204 {
205     data1_node *r;
206
207     r = data1_mk_node(dh, m);
208     r->which = type;
209     switch(type)
210     {
211     case DATA1N_tag:
212         r->u.tag.tag = 0;
213         r->u.tag.element = 0;
214         r->u.tag.no_data_requested = 0;
215         r->u.tag.node_selected = 0;
216         r->u.tag.make_variantlist = 0;
217         r->u.tag.get_bytes = -1;
218 #if DATA1_USING_XATTR
219         r->u.tag.attributes = 0;
220 #endif
221         break;
222     case DATA1N_root:
223         r->u.root.type = 0;
224         r->u.root.absyn = 0;
225         break;
226     case DATA1N_data:
227         r->u.data.data = 0;
228         r->u.data.len = 0;
229         r->u.data.what = 0;
230         r->u.data.formatted_text = 0;
231         break;
232     default:
233         logf (LOG_WARN, "data_mk_node_type. bad type = %d\n", type);
234     }
235     return r;
236 }
237
238 void data1_free_tree (data1_handle dh, data1_node *t)
239 {
240     data1_node *p = t->child, *pn;
241
242     while (p)
243     {
244         pn = p->next;
245         data1_free_tree (dh, p);
246         p = pn;
247     }
248     if (t->destroy)
249         (*t->destroy)(t);
250 }
251
252 char *data1_insert_string (data1_handle dh, data1_node *res,
253                            NMEM m, const char *str)
254 {
255     int len = strlen(str);
256
257     if (len >= DATA1_LOCALDATA)
258         return nmem_strdup (m, str);
259     else
260     {
261         strcpy (res->lbuf, str);
262         return res->lbuf;
263     }
264 }
265
266 data1_node *data1_add_insert_taggeddata(data1_handle dh, data1_node *root,
267                                         data1_node *at,
268                                         const char *tagname, NMEM m,
269                                         int first_flag, int local_allowed)
270 {
271     data1_node *partag = get_parent_tag (dh, at);
272     data1_node *tagn = data1_mk_node_type (dh, m, DATA1N_tag);
273     data1_element *e = NULL;
274     data1_node *datn;
275
276     tagn->u.tag.tag = data1_insert_string (dh, tagn, m, tagname);
277
278     if (partag)
279         e = partag->u.tag.element;
280     tagn->u.tag.element =
281         data1_getelementbytagname (dh, root->u.root.absyn, e, tagname);
282     if (!local_allowed && !tagn->u.tag.element)
283         return NULL;
284     tagn->last_child = tagn->child = datn = data1_mk_node_type (dh, m, DATA1N_data);
285     tagn->root = root;
286     datn->parent = tagn;
287     datn->root = root;
288     datn->u.data.formatted_text = 0;
289     tagn->parent = at;
290
291     if (first_flag)
292     {
293         tagn->next = at->child;
294         if (!tagn->next)
295             at->last_child = tagn;
296         at->child = tagn;
297     }
298     else
299     {
300         if (!at->child)
301             at->child = tagn;
302         else
303         {
304             assert (at->last_child);
305             at->last_child->next = tagn;
306         }
307         at->last_child = tagn;
308     }
309     return datn;
310 }
311
312 data1_node *data1_add_taggeddata(data1_handle dh, data1_node *root,
313                                  data1_node *at,
314                                  const char *tagname, NMEM m)
315 {
316     return data1_add_insert_taggeddata (dh, root, at, tagname, m, 0, 1);
317 }
318
319
320 /*
321  * Insert a tagged node into the record root as first child of the node at
322  * which should be root or tag itself). Returns pointer to the data node,
323  * which can then be modified.
324  */
325 data1_node *data1_insert_taggeddata(data1_handle dh, data1_node *root,
326                                     data1_node *at,
327                                     const char *tagname, NMEM m)
328 {
329     return data1_add_insert_taggeddata (dh, root, at, tagname, m, 1, 0);
330 }
331
332 #if DATA1_USING_XATTR
333 data1_xattr *data1_read_xattr (data1_handle dh, NMEM m,
334                                int (*get_byte)(void *fh), void *fh,
335                                WRBUF wrbuf, int *ch)
336 {
337     data1_xattr *p_first = 0;
338     data1_xattr **pp = &p_first;
339     int c = *ch;
340     for (;;)
341     {
342         data1_xattr *p;
343         int len;
344         while (c && d1_isspace(c))
345             c = (*get_byte)(fh);
346         if (!c  || c == '>' || c == '/')
347             break;
348         *pp = p = nmem_malloc (m, sizeof(*p));
349         p->next = 0;
350         pp = &p->next;
351         p->value = 0;
352         
353         wrbuf_rewind(wrbuf);
354         while (c && c != '=' && c != '>' && c != '/' && !d1_isspace(c))
355         {
356             wrbuf_putc (wrbuf, c);
357             c = (*get_byte)(fh);
358         }
359         wrbuf_putc (wrbuf, '\0');
360         len = wrbuf_len(wrbuf);
361         p->name = nmem_malloc (m, len);
362         strcpy (p->name, wrbuf_buf(wrbuf));
363         if (c == '=')
364         {
365             c = (*get_byte)(fh);
366             if (c == '"')
367                 c = (*get_byte)(fh);    
368             wrbuf_rewind(wrbuf);
369             while (c && c != '"' && c != '>' && c != '/')
370             {
371                 wrbuf_putc (wrbuf, c);
372                 c = (*get_byte)(fh);
373             }
374             wrbuf_putc (wrbuf, '\0');
375             len = wrbuf_len(wrbuf);
376             p->value = nmem_malloc (m, len);
377             strcpy (p->value, wrbuf_buf(wrbuf));
378             if (c == '"')
379                 c = (*get_byte)(fh);    
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_absyn *absyn = 0;
395     data1_node *d1_stack[256];
396     data1_node *res;
397     int c;
398     int level = 0;
399     int line = 1;
400
401     d1_stack[level] = 0;
402     c = (*get_byte)(fh);
403     while (1)
404     {
405         data1_node *parent = level ? d1_stack[level-1] : 0;
406         while (c != '\0' && d1_isspace(c))
407         {
408             if (c == '\n')
409                 line++;
410             c = (*get_byte)(fh);
411         }
412         if (c == '\0')
413             break;
414         
415         if (c == '<') /* beginning of tag */
416         {
417 #if DATA1_USING_XATTR
418             data1_xattr *xattr;
419 #endif
420             char tag[64];
421             char args[256];
422             int null_tag = 0;
423             int end_tag = 0;
424             size_t i = 0;
425
426             c = (*get_byte)(fh);
427             if (c == '/')
428             {
429                 end_tag = 1;
430                 c = (*get_byte)(fh);
431             }
432             while (c && c != '>' && c != '/' && !d1_isspace(c))
433             {
434                 if (i < (sizeof(tag)-1))
435                     tag[i++] = c;
436                 c = (*get_byte)(fh);
437             }
438             tag[i] = '\0';
439 #if DATA1_USING_XATTR
440             xattr = data1_read_xattr (dh, m, get_byte, fh, wrbuf, &c);
441             args[0] = '\0';
442 #else
443             while (d1_isspace(c))
444                 c = (*get_byte)(fh);
445             for (i = 0; c && c != '>' && c != '/'; c = (*get_byte)(fh))
446                 if (i < (sizeof(args)-1))
447                     args[i++] = c;
448             args[i] = '\0';
449 #endif
450             if (c == '/')
451             {    /* <tag attrs/> or <tag/> */
452                 null_tag = 1;
453                 c = (*get_byte)(fh);
454             }
455             if (c != '>')
456             {
457                 yaz_log(LOG_WARN, "d1: %d: Malformed tag", line);
458                 return 0;
459             }
460             else
461                 c = (*get_byte)(fh);
462
463             /* End tag? */
464             if (end_tag)       
465             {
466                 if (*tag == '\0')
467                     --level;        /* </> */
468                 else
469                 {                   /* </tag> */
470                     int i = level;
471                     while (i > 0)
472                     {
473                         parent = d1_stack[--i];
474                         if ((parent->which == DATA1N_root &&
475                              !strcmp(tag, parent->u.root.type)) ||
476                             (parent->which == DATA1N_tag &&
477                              !strcmp(tag, parent->u.tag.tag)))
478                         {
479                             level = i;
480                             break;
481                         }
482                     }
483                     if (i != level)
484                     {
485                         yaz_log (LOG_WARN, "%d: no begin tag for %s",
486                                  line, tag);
487                         break;
488                     }
489                 }
490                 if (level == 0)
491                     return d1_stack[0];
492                 continue;
493             }   
494             if (level == 0) /* root ? */
495             {
496                 if (!(absyn = data1_get_absyn (dh, tag)))
497                 {
498                     yaz_log(LOG_WARN, "Unable to acquire abstract syntax " "for '%s'", tag); 
499                     /* It's now OK for a record not to have an absyn */
500                 }
501                 res = data1_mk_node_type (dh, m, DATA1N_root);
502                 res->u.root.type = data1_insert_string (dh, res, m, tag);
503                 res->u.root.absyn = absyn;
504                 res->root = res;
505             }
506             else if (!strcmp(tag, "var"))
507             {
508                 char tclass[DATA1_MAX_SYMBOL], type[DATA1_MAX_SYMBOL];
509                 data1_vartype *tp;
510                 int val_offset;
511                 
512                 if (sscanf(args, "%s %s %n", tclass, type, &val_offset) != 2)
513                 {
514                     yaz_log(LOG_WARN, "Malformed variant triple at '%s'", tag);
515                     continue;
516                 }
517                 if (!(tp =
518                       data1_getvartypebyct(dh,
519                                            parent->root->u.root.absyn->varset,
520                                            tclass, type)))
521                     continue;
522                 /*
523                  * If we're the first variant in this group, create a parent 
524                  * variant, and insert it before the current variant.
525                  */
526                 if (parent->which != DATA1N_variant)
527                 {
528                     res = data1_mk_node (dh, m);
529                     res->which = DATA1N_variant;
530                     res->u.variant.type = 0;
531                     res->u.variant.value = 0;
532                 }
533                 else
534                 {
535                     /*
536                      * now determine if one of our ancestor triples is of
537                      * same type. If so, we break here.
538                      */
539                     int i;
540                     for (i = level-1; d1_stack[i]->which==DATA1N_variant; --i)
541                         if (d1_stack[i]->u.variant.type == tp)
542                         {
543                             level = i;
544                             break;
545                         }
546                     res = data1_mk_node (dh, m);
547                     res->which = DATA1N_variant;
548                     res->u.variant.type = tp;
549                     res->u.variant.value =
550                         data1_insert_string (dh, res, m, args + val_offset);
551                 }
552             }
553             else /* tag.. acquire our element in the abstract syntax */
554             {
555                 data1_node *partag = get_parent_tag (dh, parent);
556                 data1_element *elem, *e = 0;
557                 int localtag = 0;
558                 
559                 if (parent->which == DATA1N_variant)
560                     return 0;
561                 if (partag)
562                     if (!(e = partag->u.tag.element))
563                         localtag = 1; /* our parent is a local tag */
564                 
565                 elem = data1_getelementbytagname(dh, absyn, e, tag);
566                 res = data1_mk_node_type (dh, m, DATA1N_tag);
567                 res->u.tag.tag = data1_insert_string (dh, res, m, tag);
568                 res->u.tag.element = elem;
569 #if DATA1_USING_XATTR
570                 res->u.tag.attributes = xattr;
571 #endif
572             }
573             if (parent)
574             {
575                 parent->last_child = res;
576                 res->root = parent->root;
577             }
578             res->parent = parent;
579             if (d1_stack[level])
580                 d1_stack[level]->next = res;
581             else if (parent)
582                 parent->child = res;
583             d1_stack[level] = res;
584             d1_stack[level+1] = 0;
585             if (!null_tag)
586                 ++level;
587         }
588         else /* != '<'... this is a body of text */
589         {
590             const char *src;
591             char *dst;
592             int len, prev_char = 0;
593             
594             if (level == 0)
595             {
596                 c = (*get_byte)(fh);
597                 continue;
598             }
599             res = data1_mk_node_type (dh, m, DATA1N_data);
600             res->parent = parent;
601             res->u.data.what = DATA1I_text;
602             res->u.data.formatted_text = 0;
603             res->root = parent->root;
604             parent->last_child = res;
605             if (d1_stack[level])
606                 d1_stack[level]->next = res;
607             else
608                 parent->child = res;
609             d1_stack[level] = res;
610             
611             wrbuf_rewind(wrbuf);
612
613             while (c && c != '<')
614             {
615                 wrbuf_putc (wrbuf, c);
616                 c = (*get_byte)(fh);
617             }
618             len = wrbuf_len(wrbuf);
619
620             /* use local buffer of nmem if too large */
621             if (len >= DATA1_LOCALDATA)
622                 res->u.data.data = (char*) nmem_malloc (m, len);
623             else
624                 res->u.data.data = res->lbuf;
625             
626             /* read "data" and transfer while removing white space */
627             dst = res->u.data.data;
628             for (src = wrbuf_buf(wrbuf); --len >= 0; src++)
629             {
630                 if (*src == '\n')
631                     line++;
632                 if (d1_isspace (*src))
633                     prev_char = ' ';
634                 else
635                 {
636                     if (prev_char)
637                     {
638                         *dst++ = prev_char;
639                         prev_char = 0;
640                     }
641                     *dst++ = *src;
642                 }
643             }
644             res->u.data.len = dst - res->u.data.data;
645         }
646     }
647     return 0;
648 }
649
650 int getc_mem (void *fh)
651 {
652     const char **p = (const char **) fh;
653     if (**p)
654         return *(*p)++;
655     return 0;
656 }
657
658 data1_node *data1_read_node (data1_handle dh, const char **buf, NMEM m)
659 {
660     WRBUF wrbuf = wrbuf_alloc();
661     data1_node *node;
662
663     node = data1_read_nodex(dh, m, getc_mem, (void *) (buf), wrbuf);
664     wrbuf_free (wrbuf, 1);
665     return node;
666 }
667
668 /*
669  * Read a record in the native syntax.
670  */
671 data1_node *data1_read_record(data1_handle dh,
672                               int (*rf)(void *, char *, size_t), void *fh,
673                               NMEM m)
674 {
675     int *size;
676     char **buf = data1_get_read_buf (dh, &size);
677     const char *bp;
678     int rd = 0, res;
679     
680     if (!*buf)
681         *buf = (char *)xmalloc(*size = 4096);
682     
683     for (;;)
684     {
685         if (rd + 2048 >= *size && !(*buf =(char *)xrealloc(*buf, *size *= 2)))
686             abort();
687         if ((res = (*rf)(fh, *buf + rd, 2048)) <= 0)
688         {
689             if (!res)
690             {
691                 bp = *buf;
692                 (*buf)[rd] = '\0';
693                 return data1_read_node(dh, &bp, m);
694             }
695             else
696                 return 0;
697         }
698         rd += res;
699     }
700 }
701
702 data1_node *data1_read_sgml (data1_handle dh, NMEM m, const char *buf)
703 {
704     const char *bp = buf;
705     return data1_read_node (dh, &bp, m);
706 }
707