Extra root tag node for data1
[yaz-moved-to-github.git] / retrieval / d1_read.c
1 /*
2  * Copyright (c) 1995-2002, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Id: d1_read.c,v 1.44 2002-07-03 10:04:04 adam Exp $
7  */
8
9 #include <assert.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12
13 #include <yaz/xmalloc.h>
14 #include <yaz/log.h>
15 #include <yaz/data1.h>
16
17 data1_node *data1_get_root_tag (data1_handle dh, data1_node *n)
18 {
19     if (!n)
20         return 0;
21     n = n->child;
22     while (n && n->which != DATA1N_tag)
23         n = n->next;
24     return n;
25 }
26         
27 /*
28  * get the tag which is the immediate parent of this node (this may mean
29  * traversing intermediate things like variants and stuff.
30  */
31 data1_node *get_parent_tag (data1_handle dh, data1_node *n)
32 {
33     for (; n && n->which != DATA1N_root; n = n->parent)
34         if (n->which == DATA1N_tag)
35             return n;
36     return 0;
37 }
38
39 data1_node *data1_mk_node (data1_handle dh, NMEM m)
40 {
41     return data1_mk_node2 (dh, m, DATA1N_root, 0);
42 }
43
44 data1_node *data1_mk_node_type (data1_handle dh, NMEM m, int type)
45 {
46     return data1_mk_node2 (dh, m, type, 0);
47 }
48
49 data1_node *data1_mk_node2 (data1_handle dh, NMEM m, int type,
50                             data1_node *parent)
51 {
52     data1_node *r;
53     
54     r = (data1_node *)nmem_malloc(m, sizeof(*r));
55     r->next = r->child = r->last_child = 0;
56     r->destroy = 0;
57
58     if (!parent)
59     {
60         r->root = r;
61     }
62     else
63     {
64         r->root = parent->root;
65         r->parent = parent;
66         if (!parent->child)
67             parent->child = parent->last_child = r;
68         else
69             parent->last_child->next = r;
70         parent->last_child = r;
71     }
72     r->which = type;
73     switch(type)
74     {
75     case DATA1N_tag:
76         r->u.tag.tag = 0;
77         r->u.tag.element = 0;
78         r->u.tag.no_data_requested = 0;
79         r->u.tag.node_selected = 0;
80         r->u.tag.make_variantlist = 0;
81         r->u.tag.get_bytes = -1;
82         r->u.tag.attributes = 0;
83         break;
84     case DATA1N_root:
85         r->u.root.type = 0;
86         r->u.root.absyn = 0;
87         break;
88     case DATA1N_data:
89         r->u.data.data = 0;
90         r->u.data.len = 0;
91         r->u.data.what = 0;
92         r->u.data.formatted_text = 0;
93         break;
94     case DATA1N_comment:
95         r->u.data.data = 0;
96         r->u.data.len = 0;
97         r->u.data.what = 0;
98         r->u.data.formatted_text = 1;
99         break;
100     case DATA1N_variant:
101         r->u.variant.type = 0;
102         r->u.variant.value = 0;
103         break;
104     case DATA1N_preprocess:
105         r->u.preprocess.target = 0;
106         r->u.preprocess.attributes = 0;
107         break;
108     default:
109         logf (LOG_WARN, "data_mk_node_type. bad type = %d\n", type);
110     }
111     return r;
112 }
113
114 void data1_free_tree (data1_handle dh, data1_node *t)
115 {
116     data1_node *p = t->child, *pn;
117
118     while (p)
119     {
120         pn = p->next;
121         data1_free_tree (dh, p);
122         p = pn;
123     }
124     if (t->destroy)
125         (*t->destroy)(t);
126 }
127
128 data1_node *data1_mk_root (data1_handle dh, NMEM nmem, const char *name)
129 {
130     data1_absyn *absyn = data1_get_absyn (dh, name);
131     data1_node *res;
132     if (!absyn)
133     {
134         yaz_log(LOG_WARN, "Unable to acquire abstract syntax " "for '%s'",
135                 name); 
136         /* It's now OK for a record not to have an absyn */
137     }
138     res = data1_mk_node2 (dh, nmem, DATA1N_root, 0);
139     res->u.root.type = data1_insert_string (dh, res, nmem, name);
140     res->u.root.absyn = absyn;
141     return res;
142 }
143
144 void data1_set_root(data1_handle dh, data1_node *res,
145                     NMEM nmem, const char *name)
146 {
147     data1_absyn *absyn = data1_get_absyn (dh, name);
148
149     res->u.root.type = data1_insert_string (dh, res, nmem, name);
150     res->u.root.absyn = absyn;
151 }
152
153 data1_node *data1_mk_preprocess (data1_handle dh, NMEM nmem,
154                                  const char *target, const char **attr,
155                                  data1_node *at)
156 {
157     data1_xattr **p;
158     data1_node *res = data1_mk_node2 (dh, nmem, DATA1N_preprocess, at);
159     res->u.preprocess.target = data1_insert_string (dh, res, nmem, target);
160
161     p = &res->u.preprocess.attributes;
162     while (attr && *attr)
163     {
164         *p = (data1_xattr*) nmem_malloc (nmem, sizeof(**p));
165         (*p)->name = nmem_strdup (nmem, *attr++);
166         (*p)->value = nmem_strdup (nmem, *attr++);
167         p = &(*p)->next;
168     }
169     *p = 0;
170     return res;
171 }
172
173 data1_node *data1_mk_tag_n (data1_handle dh, NMEM nmem, 
174                             const char *tag, size_t len, const char **attr,
175                             data1_node *at)
176 {
177     data1_node *partag = get_parent_tag(dh, at);
178     data1_node *res = data1_mk_node2 (dh, nmem, DATA1N_tag, at);
179     data1_element *e = NULL;
180     data1_xattr **p;
181     
182     res->u.tag.tag = data1_insert_string_n (dh, res, nmem, tag, len);
183     
184     if (partag)
185         e = partag->u.tag.element;
186     res->u.tag.element =
187         data1_getelementbytagname (dh, at->root->u.root.absyn,
188                                    e, res->u.tag.tag);
189     p = &res->u.tag.attributes;
190     while (attr && *attr)
191     {
192         *p = (data1_xattr*) nmem_malloc (nmem, sizeof(**p));
193         (*p)->name = nmem_strdup (nmem, *attr++);
194         (*p)->value = nmem_strdup (nmem, *attr++);
195         p = &(*p)->next;
196     }
197     *p = 0;
198     return res;
199 }
200
201 void data1_tag_add_attr (data1_handle dh, NMEM nmem,
202                          data1_node *res, const char **attr)
203 {
204     data1_xattr **p;
205
206     if (res->which != DATA1N_tag)
207         return;
208
209     p = &res->u.tag.attributes;
210     while (*p)
211         p = &(*p)->next;
212
213     while (attr && *attr)
214     {
215         *p = (data1_xattr*) nmem_malloc (nmem, sizeof(**p));
216         (*p)->name = nmem_strdup (nmem, *attr++);
217         (*p)->value = nmem_strdup (nmem, *attr++);
218         p = &(*p)->next;
219     }
220     *p = 0;
221 }
222
223 data1_node *data1_mk_tag (data1_handle dh, NMEM nmem,
224                           const char *tag, const char **attr, data1_node *at) 
225 {
226     return data1_mk_tag_n (dh, nmem, tag, strlen(tag), attr, at);
227 }
228
229 data1_node *data1_search_tag (data1_handle dh, data1_node *n,
230                               const char *tag)
231 {
232     if (*tag == '/')
233     {
234         n = data1_get_root_tag (dh, n);
235         if (n)
236             n = n->child;
237         tag++;
238     }
239     for (; n; n = n->next)
240         if (n->which == DATA1N_tag && n->u.tag.tag &&
241             !yaz_matchstr (tag, n->u.tag.tag))
242         {
243             return n;
244         }
245     return 0;
246 }
247
248 data1_node *data1_mk_tag_uni (data1_handle dh, NMEM nmem, 
249                               const char *tag, data1_node *at)
250 {
251     data1_node *node = data1_search_tag (dh, at->child, tag);
252     if (!node)
253         node = data1_mk_tag (dh, nmem, tag, 0 /* attr */, at);
254     else
255         node->child = node->last_child = 0;
256     return node;
257 }
258
259 data1_node *data1_mk_text_n (data1_handle dh, NMEM mem,
260                              const char *buf, size_t len, data1_node *parent)
261 {
262     data1_node *res = data1_mk_node2 (dh, mem, DATA1N_data, parent);
263     res->u.data.what = DATA1I_text;
264     res->u.data.len = len;
265     
266     res->u.data.data = data1_insert_string_n (dh, res, mem, buf, len);
267     return res;
268 }
269
270 data1_node *data1_mk_text_nf (data1_handle dh, NMEM mem,
271                               const char *buf, size_t len, data1_node *parent)
272 {
273     data1_node *res = data1_mk_text_n (dh, mem, buf, len, parent);
274     res->u.data.formatted_text = 1;
275     return res;
276 }
277
278 data1_node *data1_mk_text (data1_handle dh, NMEM mem,
279                            const char *buf, data1_node *parent)
280 {
281     return data1_mk_text_n (dh, mem, buf, strlen(buf), parent);
282 }
283
284 data1_node *data1_mk_comment_n (data1_handle dh, NMEM mem,
285                                 const char *buf, size_t len,
286                                 data1_node *parent)
287 {
288     data1_node *res = data1_mk_node2 (dh, mem, DATA1N_comment, parent);
289     res->u.data.what = DATA1I_text;
290     res->u.data.len = len;
291     
292     res->u.data.data = data1_insert_string_n (dh, res, mem, buf, len);
293     return res;
294 }
295
296 data1_node *data1_mk_comment (data1_handle dh, NMEM mem,
297                               const char *buf, data1_node *parent)
298 {
299     return data1_mk_comment_n (dh, mem, buf, strlen(buf), parent);
300 }
301
302 char *data1_insert_string_n (data1_handle dh, data1_node *res,
303                              NMEM m, const char *str, size_t len)
304 {
305     char *b;
306     if (len >= DATA1_LOCALDATA)
307         b = nmem_malloc (m, len+1);
308     else
309         b = res->lbuf;
310     memcpy (b, str, len);
311     b[len] = 0;
312     return b;
313 }
314
315 char *data1_insert_string (data1_handle dh, data1_node *res,
316                            NMEM m, const char *str)
317 {
318     return data1_insert_string_n (dh, res, m, str, strlen(str));
319 }
320
321 static data1_node *data1_add_insert_taggeddata(data1_handle dh,
322                                                data1_node *at,
323                                                const char *tagname, NMEM m,
324                                                int local_allowed)
325 {
326     data1_node *root = at->root;
327     data1_node *partag = get_parent_tag (dh, at);
328     data1_element *e = NULL;
329     data1_node *datn = 0;
330     data1_node *tagn = 0;
331
332     if (partag)
333         e = partag->u.tag.element;
334     e = data1_getelementbytagname (dh, root->u.root.absyn, e, tagname);
335     if (local_allowed || e)
336     {
337         tagn = data1_mk_node2 (dh, m, DATA1N_tag, at);
338         tagn->u.tag.tag = data1_insert_string (dh, tagn, m, tagname);
339         tagn->u.tag.element = e;
340         datn = data1_mk_node2 (dh, m, DATA1N_data, tagn);
341     }
342     return datn;
343 }
344
345 data1_node *data1_mk_tag_data(data1_handle dh, data1_node *at,
346                               const char *tagname, NMEM m)
347 {
348     return data1_add_insert_taggeddata (dh, at, tagname, m, 1);
349 }
350
351
352 /*
353  * Insert a tagged node into the record root as first child of the node at
354  * which should be root or tag itself). Returns pointer to the data node,
355  * which can then be modified.
356  */
357 data1_node *data1_mk_tag_data_wd(data1_handle dh, data1_node *at,
358                                  const char *tagname, NMEM m)
359 {
360     return data1_add_insert_taggeddata (dh, at, tagname, m, 0);
361 }
362
363 data1_node *data1_insert_taggeddata (data1_handle dh, data1_node *root,
364                                      data1_node *at, const char *tagname,
365                                      NMEM m)
366 {
367     return data1_add_insert_taggeddata (dh, at, tagname, m, 0);
368 }
369
370 data1_node *data1_add_taggeddata (data1_handle dh, data1_node *root,
371                                   data1_node *at, const char *tagname,
372                                   NMEM m)
373 {
374     return data1_add_insert_taggeddata (dh, at, tagname, m, 1);
375 }
376
377 data1_node *data1_mk_tag_data_int (data1_handle dh, data1_node *at,
378                                    const char *tag, int num,
379                                    NMEM nmem)
380 {
381     data1_node *node_data;
382     
383     node_data = data1_mk_tag_data (dh, at, tag, nmem);
384     if (!node_data)
385         return 0;
386     node_data->u.data.what = DATA1I_num;
387     node_data->u.data.data = node_data->lbuf;
388     sprintf (node_data->u.data.data, "%d", num);
389     node_data->u.data.len = strlen (node_data->u.data.data);
390     return node_data;
391 }
392
393 data1_node *data1_mk_tag_data_oid (data1_handle dh, data1_node *at,
394                                    const char *tag, Odr_oid *oid,
395                                    NMEM nmem)
396 {
397     data1_node *node_data;
398     char str[128], *p = str;
399     Odr_oid *ii;
400     
401     node_data = data1_mk_tag_data (dh, at, tag, nmem);
402     if (!node_data)
403         return 0;
404     
405     for (ii = oid; *ii >= 0; ii++)
406     {
407         if (ii != oid)
408             *p++ = '.';
409         sprintf (p, "%d", *ii);
410         p += strlen (p);
411     }
412     node_data->u.data.what = DATA1I_oid;
413     node_data->u.data.len = strlen (str);
414     node_data->u.data.data = data1_insert_string (dh, node_data, nmem, str);
415     return node_data;
416 }
417
418
419 data1_node *data1_mk_tag_data_text (data1_handle dh, data1_node *at,
420                                     const char *tag, const char *str,
421                                     NMEM nmem)
422 {
423     data1_node *node_data;
424     
425     node_data = data1_mk_tag_data (dh, at, tag, nmem);
426     if (!node_data)
427         return 0;
428     node_data->u.data.what = DATA1I_text;
429     node_data->u.data.len = strlen (str);
430     node_data->u.data.data = data1_insert_string (dh, node_data, nmem, str);
431     return node_data;
432 }
433
434
435 data1_node *data1_mk_tag_data_text_uni (data1_handle dh, data1_node *at,
436                                         const char *tag, const char *str,
437                                         NMEM nmem)
438 {
439     data1_node *node = data1_search_tag (dh, at->child, tag);
440     if (!node)
441         return data1_mk_tag_data_text (dh, at, tag, str, nmem);
442     else
443     {
444         data1_node *node_data = node->child;
445         node_data->u.data.what = DATA1I_text;
446         node_data->u.data.len = strlen (str);
447         node_data->u.data.data = data1_insert_string (dh, node_data,
448                                                       nmem, str);
449         node_data->child = node_data->last_child = 0;
450         return node_data;
451     }
452 }
453
454
455 data1_xattr *data1_read_xattr (data1_handle dh, NMEM m,
456                                int (*get_byte)(void *fh), void *fh,
457                                WRBUF wrbuf, int *ch)
458 {
459     data1_xattr *p_first = 0;
460     data1_xattr **pp = &p_first;
461     int c = *ch;
462     for (;;)
463     {
464         data1_xattr *p;
465         int len;
466         while (c && d1_isspace(c))
467             c = (*get_byte)(fh);
468         if (!c  || c == '>' || c == '/')
469             break;
470         *pp = p = (data1_xattr *) nmem_malloc (m, sizeof(*p));
471         p->next = 0;
472         pp = &p->next;
473         p->value = 0;
474         
475         wrbuf_rewind(wrbuf);
476         while (c && c != '=' && c != '>' && c != '/' && !d1_isspace(c))
477         {
478             wrbuf_putc (wrbuf, c);
479             c = (*get_byte)(fh);
480         }
481         wrbuf_putc (wrbuf, '\0');
482         len = wrbuf_len(wrbuf);
483         p->name = (char*) nmem_malloc (m, len);
484         strcpy (p->name, wrbuf_buf(wrbuf));
485         if (c == '=')
486         {
487             c = (*get_byte)(fh);
488             if (c == '"')
489             {
490                 c = (*get_byte)(fh);    
491                 wrbuf_rewind(wrbuf);
492                 while (c && c != '"')
493                 {
494                     wrbuf_putc (wrbuf, c);
495                     c = (*get_byte)(fh);
496                 }
497                 if (c)
498                     c = (*get_byte)(fh);        
499             }
500             else
501             {
502                 wrbuf_rewind(wrbuf);
503                 while (c && c != '>' && c != '/')
504                 {
505                     wrbuf_putc (wrbuf, c);
506                     c = (*get_byte)(fh);
507                 }
508             }
509             wrbuf_putc (wrbuf, '\0');
510             len = wrbuf_len(wrbuf);
511             p->value = (char*) nmem_malloc (m, len);
512             strcpy (p->value, wrbuf_buf(wrbuf));
513         }
514     }
515     *ch = c;
516     return p_first;
517 }
518
519 /*
520  * Ugh. Sometimes functions just grow and grow on you. This one reads a
521  * 'node' and its children.
522  */
523 data1_node *data1_read_nodex (data1_handle dh, NMEM m,
524                               int (*get_byte)(void *fh), void *fh, WRBUF wrbuf)
525 {
526     data1_node *d1_stack[256];
527     data1_node *res;
528     int c;
529     int level = 0;
530     int line = 1;
531
532     d1_stack[level] = 0;
533     c = (*get_byte)(fh);
534     while (1)
535     {
536         data1_node *parent = level ? d1_stack[level-1] : 0;
537         while (c != '\0' && d1_isspace(c))
538         {
539             if (c == '\n')
540                 line++;
541             c = (*get_byte)(fh);
542         }
543         if (c == '\0')
544             break;
545         
546         if (c == '<') /* beginning of tag */
547         {
548             data1_xattr *xattr;
549
550             char tag[64];
551             char args[256];
552             int null_tag = 0;
553             int end_tag = 0;
554             size_t i = 0;
555
556             c = (*get_byte)(fh);
557             if (c == '/')
558             {
559                 end_tag = 1;
560                 c = (*get_byte)(fh);
561             }
562             else if (c == '!')  /* tags/comments that we don't deal with yet */
563             {
564                 while (c && c != '>')
565                     c = (*get_byte)(fh);
566                 if (c)
567                     c = (*get_byte)(fh);
568                 continue;
569             }
570             while (c && c != '>' && c != '/' && !d1_isspace(c))
571             {
572                 if (i < (sizeof(tag)-1))
573                     tag[i++] = c;
574                 c = (*get_byte)(fh);
575             }
576             tag[i] = '\0';
577             xattr = data1_read_xattr (dh, m, get_byte, fh, wrbuf, &c);
578             args[0] = '\0';
579             if (c == '/')
580             {    /* <tag attrs/> or <tag/> */
581                 null_tag = 1;
582                 c = (*get_byte)(fh);
583             }
584             if (c != '>')
585             {
586                 yaz_log(LOG_WARN, "d1: %d: Malformed tag", line);
587                 return 0;
588             }
589             else
590                 c = (*get_byte)(fh);
591
592             /* End tag? */
593             if (end_tag)       
594             {
595                 if (*tag == '\0')
596                     --level;        /* </> */
597                 else
598                 {                   /* </tag> */
599                     int i = level;
600                     while (i > 0)
601                     {
602                         parent = d1_stack[--i];
603                         if ((parent->which == DATA1N_root &&
604                              !strcmp(tag, parent->u.root.type)) ||
605                             (parent->which == DATA1N_tag &&
606                              !strcmp(tag, parent->u.tag.tag)))
607                         {
608                             level = i;
609                             break;
610                         }
611                     }
612                     if (i != level)
613                     {
614                         yaz_log (LOG_WARN, "%d: no begin tag for %s",
615                                  line, tag);
616                         break;
617                     }
618                 }
619                 if (level <= 1)
620                     return d1_stack[0];
621                 continue;
622             }   
623             else if (!strcmp(tag, "var"))
624             {
625                 char tclass[DATA1_MAX_SYMBOL], type[DATA1_MAX_SYMBOL];
626                 data1_vartype *tp;
627                 int val_offset;
628                 
629                 if (sscanf(args, "%s %s %n", tclass, type, &val_offset) != 2)
630                 {
631                     yaz_log(LOG_WARN, "Malformed variant triple at '%s'", tag);
632                     continue;
633                 }
634                 if (!(tp =
635                       data1_getvartypebyct(dh,
636                                            parent->root->u.root.absyn->varset,
637                                            tclass, type)))
638                     continue;
639                 /*
640                  * If we're the first variant in this group, create a parent 
641                  * variant, and insert it before the current variant.
642                  */
643                 if (parent->which != DATA1N_variant)
644                 {
645                     res = data1_mk_node2 (dh, m, DATA1N_variant, parent);
646                 }
647                 else
648                 {
649                     /*
650                      * now determine if one of our ancestor triples is of
651                      * same type. If so, we break here.
652                      */
653                     int i;
654                     for (i = level-1; d1_stack[i]->which==DATA1N_variant; --i)
655                         if (d1_stack[i]->u.variant.type == tp)
656                         {
657                             level = i;
658                             break;
659                         }
660                     res = data1_mk_node2 (dh, m, DATA1N_variant, parent);
661                     res->u.variant.type = tp;
662                     res->u.variant.value =
663                         data1_insert_string (dh, res, m, args + val_offset);
664                 }
665             }
666             else 
667             {
668                 
669                 /* tag .. acquire our element in the abstract syntax */
670                 if (level == 0)
671                 {
672                     parent = data1_mk_root (dh, m, tag);
673                     d1_stack[level++] = parent;
674                 }
675                 res = data1_mk_tag (dh, m, tag, 0 /* attr */, parent);
676                 res->u.tag.attributes = xattr;
677             }
678             d1_stack[level] = res;
679             d1_stack[level+1] = 0;
680             if (level < 250 && !null_tag)
681                 ++level;
682         }
683         else /* != '<'... this is a body of text */
684         {
685             const char *src;
686             char *dst;
687             int len, prev_char = 0;
688             
689             if (level == 0)
690             {
691                 c = (*get_byte)(fh);
692                 continue;
693             }
694             res = data1_mk_node2 (dh, m, DATA1N_data, parent);
695             res->u.data.what = DATA1I_text;
696             res->u.data.formatted_text = 0;
697             d1_stack[level] = res;
698             
699             wrbuf_rewind(wrbuf);
700
701             while (c && c != '<')
702             {
703                 wrbuf_putc (wrbuf, c);
704                 c = (*get_byte)(fh);
705             }
706             len = wrbuf_len(wrbuf);
707
708             /* use local buffer of nmem if too large */
709             if (len >= DATA1_LOCALDATA)
710                 res->u.data.data = (char*) nmem_malloc (m, len);
711             else
712                 res->u.data.data = res->lbuf;
713             
714             /* read "data" and transfer while removing white space */
715             dst = res->u.data.data;
716             for (src = wrbuf_buf(wrbuf); --len >= 0; src++)
717             {
718                 if (*src == '\n')
719                     line++;
720                 if (d1_isspace (*src))
721                     prev_char = ' ';
722                 else
723                 {
724                     if (prev_char)
725                     {
726                         *dst++ = prev_char;
727                         prev_char = 0;
728                     }
729                     *dst++ = *src;
730                 }
731             }
732             res->u.data.len = dst - res->u.data.data;
733         }
734     }
735     return 0;
736 }
737
738 int getc_mem (void *fh)
739 {
740     const char **p = (const char **) fh;
741     if (**p)
742         return *(*p)++;
743     return 0;
744 }
745
746 data1_node *data1_read_node (data1_handle dh, const char **buf, NMEM m)
747 {
748     WRBUF wrbuf = wrbuf_alloc();
749     data1_node *node;
750
751     node = data1_read_nodex(dh, m, getc_mem, (void *) (buf), wrbuf);
752     wrbuf_free (wrbuf, 1);
753     return node;
754 }
755
756 /*
757  * Read a record in the native syntax.
758  */
759 data1_node *data1_read_record(data1_handle dh,
760                               int (*rf)(void *, char *, size_t), void *fh,
761                               NMEM m)
762 {
763     int *size;
764     char **buf = data1_get_read_buf (dh, &size);
765     const char *bp;
766     int rd = 0, res;
767     
768     if (!*buf)
769         *buf = (char *)xmalloc(*size = 4096);
770     
771     for (;;)
772     {
773         if (rd + 2048 >= *size && !(*buf =(char *)xrealloc(*buf, *size *= 2)))
774             abort();
775         if ((res = (*rf)(fh, *buf + rd, 2048)) <= 0)
776         {
777             if (!res)
778             {
779                 bp = *buf;
780                 (*buf)[rd] = '\0';
781                 return data1_read_node(dh, &bp, m);
782             }
783             else
784                 return 0;
785         }
786         rd += res;
787     }
788 }
789
790 data1_node *data1_read_sgml (data1_handle dh, NMEM m, const char *buf)
791 {
792     const char *bp = buf;
793     return data1_read_node (dh, &bp, m);
794 }
795