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