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