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