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