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