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