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