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