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