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