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