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