a4305657383bed57fe69ecba611b717522999df7
[yaz-moved-to-github.git] / retrieval / d1_read.c
1 /*
2  * Copyright (c) 1995, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: d1_read.c,v $
7  * Revision 1.13  1996-10-29 13:35:38  adam
8  * Implemented data1_set_tabpath and data1_get_tabpath.
9  *
10  * Revision 1.12  1996/10/11 10:35:38  adam
11  * Fixed a bug that caused data1_read_node to core dump when no abstract
12  * syntax was defined in a "sgml"-record.
13  *
14  * Revision 1.11  1996/07/06 19:58:35  quinn
15  * System headerfiles gathered in yconfig
16  *
17  * Revision 1.10  1996/01/19  15:41:47  quinn
18  * Fixed uninitialized boolean.
19  *
20  * Revision 1.9  1996/01/17  14:52:47  adam
21  * Changed prototype for reader function parsed to data1_read_record.
22  *
23  * Revision 1.8  1995/12/15  16:20:41  quinn
24  * Added formatted text.
25  *
26  * Revision 1.7  1995/12/13  13:44:32  quinn
27  * Modified Data1-system to use nmem
28  *
29  * Revision 1.6  1995/12/12  16:37:08  quinn
30  * Added destroy element to data1_node.
31  *
32  * Revision 1.5  1995/12/11  15:22:37  quinn
33  * Added last_child field to the node.
34  * Rewrote schema-mapping.
35  *
36  * Revision 1.4  1995/11/13  09:27:36  quinn
37  * Fiddling with the variant stuff.
38  *
39  * Revision 1.3  1995/11/01  16:34:57  quinn
40  * Making data1 look for tables in data1_tabpath
41  *
42  * Revision 1.2  1995/11/01  13:54:48  quinn
43  * Minor adjustments
44  *
45  * Revision 1.1  1995/11/01  11:56:09  quinn
46  * Added Retrieval (data management) functions en masse.
47  *
48  * Revision 1.14  1995/10/30  12:40:55  quinn
49  * Fixed a couple of bugs.
50  *
51  * Revision 1.13  1995/10/25  16:00:47  quinn
52  * USMARC support is now almost operational
53  *
54  * Revision 1.12  1995/10/16  14:02:55  quinn
55  * Changes to support element set names and espec1
56  *
57  * Revision 1.11  1995/10/13  16:05:08  quinn
58  * Adding Espec1-processing
59  *
60  * Revision 1.10  1995/10/11  14:53:44  quinn
61  * Work on variants.
62  *
63  * Revision 1.9  1995/10/06  16:56:50  quinn
64  * Fixed ranked result.
65  *
66  * Revision 1.8  1995/10/06  16:44:13  quinn
67  * Work on attribute set mapping, etc.
68  *
69  * Revision 1.7  1995/10/06  12:58:35  quinn
70  * SUTRS support
71  *
72  * Revision 1.6  1995/10/04  09:29:49  quinn
73  * Adjustments to support USGS test data
74  *
75  * Revision 1.5  1995/10/03  17:56:43  quinn
76  * Fixing GRS code.
77  *
78  * Revision 1.4  1995/10/02  15:53:19  quinn
79  * Work
80  *
81  * Revision 1.3  1995/10/02  14:55:21  quinn
82  * *** empty log message ***
83  *
84  * Revision 1.2  1995/09/14  15:18:13  quinn
85  * Work
86  *
87  * Revision 1.1  1995/09/12  11:24:30  quinn
88  * Beginning to add code for structured records.
89  *
90  *
91  */
92
93 #include <ctype.h>
94 #include <stdio.h>
95 #include <stdlib.h>
96
97 #include <xmalloc.h>
98 #include <log.h>
99 #include <data1.h>
100
101 char *data1_tabpath = 0; /* global path for tables */
102
103 void data1_set_tabpath(const char *p)
104 {
105     if (data1_tabpath)
106     {
107         xfree (data1_tabpath);
108         data1_tabpath = NULL;
109     }
110     if (p)
111     {
112         data1_tabpath = xmalloc (strlen(p)+1);
113         strcpy (data1_tabpath, p);
114     }
115 }
116
117 const char *data1_get_tabpath (void)
118 {
119     return data1_tabpath;
120 }
121
122 #if 0
123 static data1_node *freelist = 0;
124 #endif
125
126 /*
127  * get the tag which is the immediate parent of this node (this may mean
128  * traversing intermediate things like variants and stuff.
129  */
130 data1_node *get_parent_tag(data1_node *n)
131 {
132     for (; n && n->which != DATA1N_root; n = n->parent)
133         if (n->which == DATA1N_tag)
134             return n;
135     return 0;
136 }
137
138 data1_node *data1_mk_node(NMEM m)
139 {
140     data1_node *r;
141
142 #if 0
143     if ((r = freelist))
144         freelist = r->next;
145     else
146         if (!(r = xmalloc(sizeof(*r))))
147             abort();
148 #else
149     r = nmem_malloc(m, sizeof(*r));
150 #endif
151     r->next = r->child = r->last_child = r->parent = 0;
152     r->num_children = 0;
153     r->destroy = 0;
154     return r;
155 }
156
157 #if 0
158 static void fr_node(data1_node *n)
159 {
160     n->next = freelist;
161     freelist = n;
162 }
163 #endif
164
165 void data1_free_tree(data1_node *t)
166 {
167     data1_node *p = t->child, *pn;
168
169     while (p)
170     {
171         pn = p->next;
172         data1_free_tree(p);
173         p = pn;
174     }
175     if (t->destroy)
176         (*t->destroy)(t);
177 #if 0
178     fr_node(t);
179 #endif
180 }
181
182 /*
183  * Insert a tagged node into the record root as first child of the node at
184  * which should be root or tag itself). Returns pointer to the data node,
185  * which can then be modified.
186  */
187 data1_node *data1_insert_taggeddata(data1_node *root, data1_node *at,
188     char *tagname, NMEM m)
189 {
190     data1_node *tagn = data1_mk_node(m);
191     data1_node *datn;
192
193     tagn->which = DATA1N_tag;
194     tagn->line = -1;
195     tagn->u.tag.tag = 0;
196     tagn->u.tag.node_selected = 0;
197     tagn->u.tag.make_variantlist = 0;
198     tagn->u.tag.no_data_requested = 0;
199     tagn->u.tag.get_bytes = -1;
200     if (!(tagn->u.tag.element = data1_getelementbytagname(root->u.root.absyn,
201         0, tagname)))
202         return 0;
203     tagn->child = datn = data1_mk_node(m);
204     tagn->num_children = 1;
205     datn->parent = tagn;
206     datn->root = root;
207     datn->which = DATA1N_data;
208     datn->u.data.formatted_text = 0;
209     tagn->next = at->child;
210     tagn->parent = at;
211     at->child = tagn;
212     at->num_children++;
213     return datn;
214 }
215
216 /*
217  * Ugh. Sometimes functions just grow and grow on you. This one reads a
218  * 'node' and its children.
219  */
220 data1_node *data1_read_node(char **buf, data1_node *parent, int *line,
221     data1_absyn *absyn, NMEM m)
222 {
223     data1_node *res;
224
225     while (**buf && isspace(**buf))
226     {
227         if (**buf == '\n')
228             (*line)++;
229         (*buf)++;
230     }
231     if (!**buf)
232         return 0;
233
234     if (**buf == '<') /* beginning of tag */
235     {
236         char *tag = (*buf) + 1;
237         char *args = 0;
238         char *t = tag;
239         data1_node **pp;
240         data1_element *elem = 0;
241
242         for (; *t && *t != '>' && !isspace(*t); t++);
243         if (*t != '>' && !isspace(*t))
244         {
245             logf(LOG_WARN, "d1: %d: Malformed tag", *line);
246             return 0;
247         }
248         if (isspace(*t)) /* the tag has arguments */
249         {
250             while (isspace(*t))
251                 t++;
252             if (*t != '>')
253             {
254                 args = t;
255                 for (; *t && *t != '>'; t++);
256                 if (*t != '>' && !isspace(*t))
257                 {
258                     logf(LOG_WARN, "d1: %d: Malformed tag", *line);
259                     return 0;
260                 }
261             }
262         }
263
264         /*
265          * if end-tag, see if we terminate parent. If so, consume and return.
266          * Else, return.
267          */
268         *t = '\0';
269         if (*tag == '/')
270         {
271             if (!parent)
272                 return 0;
273             if (!*(tag +1) || (parent->which == DATA1N_root && !strcmp(tag + 1,
274                 parent->u.root.type)) ||
275                 (parent->which == DATA1N_tag && !strcmp(tag + 1,
276                 parent->u.tag.tag)))
277             {
278                 *buf = t + 1;
279                 return 0;
280             }
281             else
282             {
283                 *t = '>';
284                 return 0;
285             }
286         }
287
288         if (!absyn) /* parent node - what are we? */
289         {
290             if (!(absyn = data1_get_absyn(tag)))
291             {
292                 logf(LOG_WARN, "Unable to acquire abstract syntax for '%s'",
293                     tag);
294                 return 0;
295             }
296             res = data1_mk_node(m);
297             res->which = DATA1N_root;
298             res->u.root.type = tag;
299             res->u.root.absyn = absyn;
300             res->root = res;
301             *buf = t + 1;
302         }
303         else if (!strncmp(tag, "var", 3))
304         {
305             char class[DATA1_MAX_SYMBOL], type[DATA1_MAX_SYMBOL];
306             data1_vartype *tp;
307             int val_offset;
308             data1_node *p;
309
310             if (sscanf(args, "%s %s %n", class, type, &val_offset) != 2)
311             {
312                 logf(LOG_WARN, "Malformed variant triple at '%s'", tag);
313                 return 0;
314             }
315             if (!(tp = data1_getvartypebyct(parent->root->u.root.absyn->varset,
316                 class, type)))
317                 return 0;
318             
319             /*
320              * If we're the first variant in this group, create a parent var,
321              * and insert it before the current variant.
322              */
323             if (parent->which != DATA1N_variant)
324             {
325                 res = data1_mk_node(m);
326                 res->which = DATA1N_variant;
327                 res->u.variant.type = 0;
328                 res->u.variant.value = 0;
329                 res->root = parent->root;
330                 *t = '>';
331             }
332             else
333             {
334                 /*
335                  * now determine if one of our ancestor triples is of same type.
336                  * If so, we break here. This will make the parser unwind until
337                  * we become a sibling (alternate variant) to the aforementioned
338                  * triple. It stinks that we re-parse these tags on every
339                  * iteration of this. This is a function in need of a rewrite.
340                  */
341                 for (p = parent; p->which == DATA1N_variant; p = p->parent)
342                     if (p->u.variant.type == tp)
343                     {
344                         *t = '>';
345                         return 0;
346                     }
347
348                 res =  data1_mk_node(m);
349                 res->which = DATA1N_variant;
350                 res->root = parent->root;
351                 res->u.variant.type = tp;
352                 res->u.variant.value = args + val_offset;
353                 *buf = t + 1;
354             }
355         }
356         else /* tag.. acquire our element in the abstract syntax */
357         {
358             data1_node *partag = get_parent_tag(parent);
359             data1_element *e = 0;
360             int localtag = 0;
361
362             if (parent->which == DATA1N_variant)
363             {
364                 *t = '>';
365                 return 0;
366             }
367             if (partag)
368                 if (!(e = partag->u.tag.element))
369                     localtag = 1; /* our parent is a local tag */
370
371 #if 0
372             if (!localtag && !(elem = data1_getelementbytagname(absyn,
373                 e, tag)) && (data1_gettagbyname(absyn->tagset, tag)))
374             {
375                 if (parent->which == DATA1N_root)
376                     logf(LOG_WARN, "Tag '%s' used out of context", tag);
377                 *t = '>';
378                 return 0;
379             }
380 #else
381             elem = data1_getelementbytagname(absyn, e, tag);
382 #endif
383             res = data1_mk_node(m);
384             res->which = DATA1N_tag;
385             res->u.tag.element = elem;
386             res->u.tag.tag = tag;
387             res->u.tag.node_selected = 0;
388             res->u.tag.make_variantlist = 0;
389             res->u.tag.no_data_requested = 0;
390             res->u.tag.get_bytes = -1;
391             res->root = parent->root;
392             *buf = t + 1;
393         }
394
395         res->parent = parent;
396         res->num_children = 0;
397
398         pp = &res->child;
399         /*
400          * Read child nodes.
401          */
402         while ((*pp = data1_read_node(buf, res, line, absyn, m)))
403         {
404             res->last_child = *pp;
405             res->num_children++;
406             pp = &(*pp)->next;
407         }
408     }
409     else /* != '<'... this is a body of text */
410     {
411         int len = 0;
412         char *data = *buf, *pp = *buf;
413 #if 0
414         data1_node *partag = get_parent_tag(parent);
415 #endif
416
417         if (!parent)      /* abort if abstract syntax is undefined */
418             return 0;
419         /* Determine length and remove newlines/extra blanks */
420         while (**buf && **buf != '<')
421         {
422             if (**buf == '\n')
423                 (*line)++;
424             if (isspace(**buf))
425             {
426                 *(pp++) = ' ';
427                 (*buf)++;
428                 while (isspace(**buf))
429                     (*buf)++;
430             }
431             else
432                 *(pp++) = *((*buf)++);
433             len++;
434         }
435         while (isspace(data[len-1]))
436             len--;
437         res = data1_mk_node(m);
438         res->parent = parent;
439         res->which = DATA1N_data;
440         res->u.data.what = DATA1I_text;
441         res->u.data.len = len;
442         res->u.data.data = data;
443         res->u.data.formatted_text = 0;
444         res->root = parent->root;
445     }
446     return res;
447 }
448
449 /*
450  * Read a record in the native syntax.
451  */
452 data1_node *data1_read_record(int (*rf)(void *, char *, size_t), void *fh,
453                               NMEM m)
454 {
455     static char *buf = 0;
456     char *bp;
457     static int size;
458     int rd = 0, res;
459     int line = 0;
460
461     if (!buf && !(buf = xmalloc(size = 4096)))
462         abort();
463
464     for (;;)
465     {
466         if (rd + 4096 > size && !(buf =xrealloc(buf, size *= 2)))
467             abort();
468         if ((res = (*rf)(fh, buf + rd, 4096)) <= 0)
469         {
470             if (!res)
471             {
472                 bp = buf;
473                 return data1_read_node(&bp, 0, &line, 0, m);
474             }
475             else
476                 return 0;
477         }
478         rd += res;
479     }
480 }