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