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