C++ compilation.
[idzebra-moved-to-github.git] / recctrl / recgrs.c
1 /*
2  * Copyright (C) 1994-1999, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: recgrs.c,v $
7  * Revision 1.29  1999-05-26 07:49:14  adam
8  * C++ compilation.
9  *
10  * Revision 1.28  1999/05/21 12:00:17  adam
11  * Better diagnostics for extraction process.
12  *
13  * Revision 1.27  1999/05/20 12:57:18  adam
14  * Implemented TCL filter. Updated recctrl system.
15  *
16  * Revision 1.26  1999/03/02 16:15:44  quinn
17  * Added "tagsysno" and "tagrank" directives to zebra.cfg.
18  *
19  * Revision 1.25  1999/02/18 15:01:26  adam
20  * Minor changes.
21  *
22  * Revision 1.24  1999/02/02 14:51:28  adam
23  * Updated WIN32 code specific sections. Changed header.
24  *
25  * Revision 1.23  1998/10/18 07:51:10  adam
26  * Changed one logf call.
27  *
28  * Revision 1.22  1998/10/16 08:14:37  adam
29  * Updated record control system.
30  *
31  * Revision 1.21  1998/07/01 09:16:10  adam
32  * Element localno only added when it's greater than 0.
33  *
34  * Revision 1.20  1998/05/20 10:12:26  adam
35  * Implemented automatic EXPLAIN database maintenance.
36  * Modified Zebra to work with ASN.1 compiled version of YAZ.
37  *
38  * Revision 1.19  1998/03/11 11:19:05  adam
39  * Changed the way sequence numbers are generated.
40  *
41  * Revision 1.18  1998/03/05 08:41:31  adam
42  * Minor changes.
43  *
44  * Revision 1.17  1998/02/10 12:03:06  adam
45  * Implemented Sort.
46  *
47  * Revision 1.16  1998/01/29 13:38:17  adam
48  * Fixed problem with mapping to record with unknown schema.
49  *
50  * Revision 1.15  1998/01/26 10:37:57  adam
51  * Better diagnostics.
52  *
53  * Revision 1.14  1997/11/06 11:41:01  adam
54  * Implemented "begin variant" for the sgml.regx filter.
55  *
56  * Revision 1.13  1997/10/31 12:35:44  adam
57  * Added a few log statements.
58  *
59  * Revision 1.12  1997/10/29 12:02:22  adam
60  * Using oid_ent_to_oid used instead of the non thread-safe oid_getoidbyent.
61  *
62  * Revision 1.11  1997/10/27 14:34:00  adam
63  * Work on generic character mapping depending on "structure" field
64  * in abstract syntax file.
65  *
66  * Revision 1.10  1997/09/18 08:59:21  adam
67  * Extra generic handle for the character mapping routines.
68  *
69  * Revision 1.9  1997/09/17 12:19:21  adam
70  * Zebra version corresponds to YAZ version 1.4.
71  * Changed Zebra server so that it doesn't depend on global common_resource.
72  *
73  * Revision 1.8  1997/09/09 13:38:14  adam
74  * Partial port to WIN95/NT.
75  *
76  * Revision 1.7  1997/09/05 15:30:10  adam
77  * Changed prototype for chr_map_input - added const.
78  * Added support for C++, headers uses extern "C" for public definitions.
79  *
80  * Revision 1.6  1997/09/04 13:54:40  adam
81  * Added MARC filter - type grs.marc.<syntax> where syntax refers
82  * to abstract syntax. New method tellf in retrieve/extract method.
83  *
84  * Revision 1.5  1997/07/15 16:29:03  adam
85  * Initialized dummy variable to keep checker gcc happy.
86  *
87  * Revision 1.4  1997/04/30 08:56:08  quinn
88  * null
89  *
90  * Revision 1.2  1996/10/11  16:06:43  quinn
91  * Revision 1.3  1997/02/24 10:41:50  adam
92  * Cleanup of code and commented out the "end element-end-record" code.
93  *
94  * Revision 1.2  1996/10/11 16:06:43  quinn
95  * Fixed arguments to nodetogr
96  *
97  * Revision 1.1  1996/10/11  10:57:25  adam
98  * New module recctrl. Used to manage records (extract/retrieval).
99  *
100  * Revision 1.29  1996/10/08 10:30:21  quinn
101  * Fixed type mismatch
102  *
103  * Revision 1.28  1996/10/07  16:06:40  quinn
104  * Added SOIF support
105  *
106  * Revision 1.27  1996/06/11  10:54:12  quinn
107  * Relevance work
108  *
109  * Revision 1.26  1996/06/06  12:08:45  quinn
110  * Added showRecord function
111  *
112  * Revision 1.25  1996/06/04  14:18:53  quinn
113  * Charmap work
114  *
115  * Revision 1.24  1996/06/04  13:27:54  quinn
116  * More work on charmapping
117  *
118  * Revision 1.23  1996/06/04  10:19:01  adam
119  * Minor changes - removed include of ctype.h.
120  *
121  * Revision 1.22  1996/06/03  10:15:27  quinn
122  * Various character-mapping.
123  *
124  * Revision 1.21  1996/05/31  13:27:24  quinn
125  * Character-conversion in phrases, too.
126  *
127  * Revision 1.19  1996/05/16  15:31:14  quinn
128  * a7
129  *
130  * Revision 1.18  1996/05/09  07:28:56  quinn
131  * Work towards phrases and multiple registers
132  *
133  * Revision 1.17  1996/05/01  13:46:37  adam
134  * First work on multiple records in one file.
135  * New option, -offset, to the "unread" command in the filter module.
136  *
137  * Revision 1.16  1996/01/17  14:57:54  adam
138  * Prototype changed for reader functions in extract/retrieve. File
139  *  is identified by 'void *' instead of 'int.
140  *
141  * Revision 1.15  1996/01/08  19:15:47  adam
142  * New input filter that works!
143  *
144  * Revision 1.14  1995/12/15  12:36:11  adam
145  * Retrieval calls data1_read_regx when subType is specified.
146  *
147  * Revision 1.13  1995/12/15  12:24:43  quinn
148  * *** empty log message ***
149  *
150  * Revision 1.12  1995/12/15  12:20:28  quinn
151  * *** empty log message ***
152  *
153  * Revision 1.11  1995/12/15  12:07:57  quinn
154  * Changed extraction strategy.
155  *
156  * Revision 1.10  1995/12/14  11:10:48  quinn
157  * Explain work
158  *
159  * Revision 1.9  1995/12/13  17:14:05  quinn
160  * *** empty log message ***
161  *
162  * Revision 1.8  1995/12/13  15:33:18  quinn
163  * *** empty log message ***
164  *
165  * Revision 1.7  1995/12/13  13:45:39  quinn
166  * Changed data1 to use nmem.
167  *
168  * Revision 1.6  1995/12/04  14:22:30  adam
169  * Extra arg to recType_byName.
170  * Started work on new regular expression parsed input to
171  * structured records.
172  *
173  * Revision 1.5  1995/11/28  14:18:37  quinn
174  * Set output_format.
175  *
176  * Revision 1.4  1995/11/21  13:14:49  quinn
177  * Fixed end-of-data-field problem (maybe).
178  *
179  * Revision 1.3  1995/11/15  19:13:09  adam
180  * Work on record management.
181  *
182  */
183
184 #include <stdio.h>
185 #include <assert.h>
186 #include <sys/types.h>
187 #ifndef WIN32
188 #include <unistd.h>
189 #endif
190
191 #include <log.h>
192 #include <oid.h>
193
194 #include <recctrl.h>
195 #include "grsread.h"
196
197 #define GRS_MAX_WORD 512
198
199 struct grs_handler {
200     RecTypeGrs type;
201     void *clientData;
202     int initFlag;
203     struct grs_handler *next;
204 };
205
206 struct grs_handlers {
207     struct grs_handler *handlers;
208 };
209
210 static int read_grs_type (struct grs_handlers *h,
211                           struct grs_read_info *p, const char *type,
212                           data1_node **root)
213 {
214     struct grs_handler *gh = h->handlers;
215     const char *cp = strchr (type, '.');
216
217     if (cp == NULL || cp == type)
218     {
219         cp = strlen(type) + type;
220         *p->type = 0;
221     }
222     else
223         strcpy (p->type, cp+1);
224     for (gh = h->handlers; gh; gh = gh->next)
225     {
226         if (!memcmp (type, gh->type->type, cp-type))
227         {
228             if (!gh->initFlag)
229             {
230                 gh->initFlag = 1;
231                 gh->clientData = (*gh->type->init)();
232             }
233             p->clientData = gh->clientData;
234             *root = (gh->type->read)(p);
235             gh->clientData = p->clientData;
236             return 0;
237         }
238     }
239     return 1;
240 }
241
242 static void grs_add_handler (struct grs_handlers *h, RecTypeGrs t)
243 {
244     struct grs_handler *gh = (struct grs_handler *) malloc (sizeof(*gh));
245     gh->next = h->handlers;
246     h->handlers = gh;
247     gh->initFlag = 0;
248     gh->clientData = 0;
249     gh->type = t;
250 }
251
252 static void *grs_init(RecType recType)
253 {
254     struct grs_handlers *h = (struct grs_handlers *) malloc (sizeof(*h));
255     h->handlers = 0;
256
257     grs_add_handler (h, recTypeGrs_sgml);
258     grs_add_handler (h, recTypeGrs_regx);
259 #if HAVE_TCL_H
260     grs_add_handler (h, recTypeGrs_tcl);
261 #endif
262     grs_add_handler (h, recTypeGrs_marc);
263     return h;
264 }
265
266 static void grs_destroy(void *clientData)
267 {
268     struct grs_handlers *h = (struct grs_handlers *) clientData;
269     struct grs_handler *gh = h->handlers, *gh_next;
270     while (gh)
271     {
272         gh_next = gh->next;
273         if (gh->initFlag)
274             (*gh->type->destroy)(gh->clientData);
275         free (gh);
276         gh = gh_next;
277     }
278     free (h);
279 }
280
281 static int dumpkeys(data1_node *n, struct recExtractCtrl *p, int level)
282 {
283     RecWord wrd;
284     (*p->init)(p, &wrd);      /* set defaults */
285     for (; n; n = n->next)
286     {
287         if (p->flagShowRecords) /* display element description to user */
288         {
289             if (n->which == DATA1N_root)
290             {
291                 printf("%*s", level * 4, "");
292                 printf("Record type: '%s'\n", n->u.root.absyn->name);
293             }
294             else if (n->which == DATA1N_tag)
295             {
296                 data1_element *e;
297
298                 printf("%*s", level * 4, "");
299                 if (!(e = n->u.tag.element))
300                     printf("Local tag: '%s'\n", n->u.tag.tag);
301                 else
302                 {
303                     printf("Elm: '%s' ", e->name);
304                     if (e->tag)
305                     {
306                         data1_tag *t = e->tag;
307
308                         printf("TagNam: '%s' ", t->names->name);
309                         printf("(");
310                         if (t->tagset)
311                             printf("%s[%d],", t->tagset->name, t->tagset->type);
312                         else
313                             printf("?,");
314                         if (t->which == DATA1T_numeric)
315                             printf("%d)", t->value.numeric);
316                         else
317                             printf("'%s')", t->value.string);
318                     }
319                     printf("\n");
320                 }
321             }
322         }
323
324         if (n->child)
325             if (dumpkeys(n->child, p, level + 1) < 0)
326                 return -1;
327
328         if (n->which == DATA1N_data)
329         {
330             data1_node *par = get_parent_tag(p->dh, n);
331             data1_termlist *tlist = 0;
332             data1_datatype dtype = DATA1K_string;
333
334             if (p->flagShowRecords)
335             {
336                 printf("%*s", level * 4, "");
337                 printf("Data: ");
338                 if (n->u.data.len > 32)
339                     printf("'%.24s ... %.6s'\n", n->u.data.data,
340                            n->u.data.data + n->u.data.len-6);
341                 else if (n->u.data.len > 0)
342                     printf("'%.*s'\n", n->u.data.len, n->u.data.data);
343                 else
344                     printf("NULL\n");
345             }
346
347             assert(par);
348
349             /*
350              * cycle up towards the root until we find a tag with an att..
351              * this has the effect of indexing locally defined tags with
352              * the attribute of their ancestor in the record.
353              */
354
355             while (!par->u.tag.element)
356                 if (!par->parent || !(par=get_parent_tag(p->dh, par->parent)))
357                     break;
358             if (!par || !(tlist = par->u.tag.element->termlists))
359                 continue;
360             if (par->u.tag.element->tag)
361                 dtype = par->u.tag.element->tag->kind;
362             for (; tlist; tlist = tlist->next)
363             {
364                 if (p->flagShowRecords)
365                 {
366                     printf("%*sIdx: [%s]", (level + 1) * 4, "",
367                            tlist->structure);
368                     printf("%s:%s [%d]\n",
369                            tlist->att->parent->name,
370                            tlist->att->name, tlist->att->value);
371                 }
372                 else
373                 {
374                     wrd.reg_type = *tlist->structure;
375                     wrd.string = n->u.data.data;
376                     wrd.length = n->u.data.len;
377                     wrd.attrSet = (int) (tlist->att->parent->reference);
378                     wrd.attrUse = tlist->att->locals->local;
379                     (*p->addWord)(&wrd);
380                 }
381             }
382         }
383         if (p->flagShowRecords && n->which == DATA1N_root)
384         {
385             printf("%*s-------------\n\n", level * 4, "");
386         }
387     }
388     return 0;
389 }
390
391 int grs_extract_tree(struct recExtractCtrl *p, data1_node *n)
392 {
393     oident oe;
394     int oidtmp[OID_SIZE];
395
396     oe.proto = PROTO_Z3950;
397     oe.oclass = CLASS_SCHEMA;
398     oe.value = n->u.root.absyn->reference;
399
400     if ((oid_ent_to_oid (&oe, oidtmp)))
401         (*p->addSchema)(p, oidtmp);
402
403     return dumpkeys(n, p, 0);
404 }
405
406 static int grs_extract(void *clientData, struct recExtractCtrl *p)
407 {
408     data1_node *n;
409     NMEM mem;
410     struct grs_read_info gri;
411     oident oe;
412     int oidtmp[OID_SIZE];
413     struct grs_handlers *h = (struct grs_handlers *) clientData;
414
415     mem = nmem_create (); 
416     gri.readf = p->readf;
417     gri.seekf = p->seekf;
418     gri.tellf = p->tellf;
419     gri.endf = p->endf;
420     gri.fh = p->fh;
421     gri.offset = p->offset;
422     gri.mem = mem;
423     gri.dh = p->dh;
424
425     if (read_grs_type (h, &gri, p->subType, &n))
426         return RECCTRL_EXTRACT_ERROR;
427     if (!n)
428         return RECCTRL_EXTRACT_EOF;
429
430     oe.proto = PROTO_Z3950;
431     oe.oclass = CLASS_SCHEMA;
432     oe.value = n->u.root.absyn->reference;
433     if ((oid_ent_to_oid (&oe, oidtmp)))
434         (*p->addSchema)(p, oidtmp);
435
436     if (dumpkeys(n, p, 0) < 0)
437     {
438         data1_free_tree(p->dh, n);
439         return RECCTRL_EXTRACT_ERROR;
440     }
441     data1_free_tree(p->dh, n);
442     nmem_destroy(mem);
443     return RECCTRL_EXTRACT_OK;
444 }
445
446 /*
447  * Return: -1: Nothing done. 0: Ok. >0: Bib-1 diagnostic.
448  */
449 static int process_comp(data1_handle dh, data1_node *n, Z_RecordComposition *c)
450 {
451     data1_esetname *eset;
452     Z_Espec1 *espec = 0;
453     Z_ElementSpec *p;
454
455     switch (c->which)
456     {
457         case Z_RecordComp_simple:
458             if (c->u.simple->which != Z_ElementSetNames_generic)
459                 return 26; /* only generic form supported. Fix this later */
460             if (!(eset = data1_getesetbyname(dh, n->u.root.absyn,
461                 c->u.simple->u.generic)))
462             {
463                 logf(LOG_LOG, "Unknown esetname '%s'", c->u.simple->u.generic);
464                 return 25; /* invalid esetname */
465             }
466             logf(LOG_DEBUG, "Esetname '%s' in simple compspec",
467                 c->u.simple->u.generic);
468             espec = eset->spec;
469             break;
470         case Z_RecordComp_complex:
471             if (c->u.complex->generic)
472             {
473                 /* insert check for schema */
474                 if ((p = c->u.complex->generic->elementSpec))
475                     switch (p->which)
476                     {
477                         case Z_ElementSpec_elementSetName:
478                             if (!(eset =
479                                   data1_getesetbyname(dh,
480                                                       n->u.root.absyn,
481                                                       p->u.elementSetName)))
482                             {
483                                 logf(LOG_LOG, "Unknown esetname '%s'",
484                                     p->u.elementSetName);
485                                 return 25; /* invalid esetname */
486                             }
487                             logf(LOG_DEBUG, "Esetname '%s' in complex compspec",
488                                 p->u.elementSetName);
489                             espec = eset->spec;
490                             break;
491                         case Z_ElementSpec_externalSpec:
492                             if (p->u.externalSpec->which == Z_External_espec1)
493                             {
494                                 logf(LOG_DEBUG, "Got Espec-1");
495                                 espec = p->u.externalSpec-> u.espec1;
496                             }
497                             else
498                             {
499                                 logf(LOG_LOG, "Unknown external espec.");
500                                 return 25; /* bad. what is proper diagnostic? */
501                             }
502                             break;
503                     }
504             }
505             else
506                 return 26; /* fix */
507     }
508     if (espec)
509     {
510         logf (LOG_DEBUG, "Element: Espec-1 match");
511         return data1_doespec1(dh, n, espec);
512     }
513     else
514     {
515         logf (LOG_DEBUG, "Element: all match");
516         return -1;
517     }
518 }
519
520 static int grs_retrieve(void *clientData, struct recRetrieveCtrl *p)
521 {
522     data1_node *node = 0, *onode = 0;
523     data1_node *dnew;
524     data1_maptab *map;
525     int res, selected = 0;
526     NMEM mem;
527     struct grs_read_info gri;
528     char *tagname;
529     struct grs_handlers *h = (struct grs_handlers *) clientData;
530     
531     mem = nmem_create();
532     gri.readf = p->readf;
533     gri.seekf = p->seekf;
534     gri.tellf = p->tellf;
535     gri.endf = NULL;
536     gri.fh = p->fh;
537     gri.offset = 0;
538     gri.mem = mem;
539     gri.dh = p->dh;
540
541     logf (LOG_DEBUG, "grs_retrieve");
542     if (read_grs_type (h, &gri, p->subType, &node))
543     {
544         p->diagnostic = 14;
545         nmem_destroy (mem);
546         return 0;
547     }
548     if (!node)
549     {
550         p->diagnostic = 14;
551         nmem_destroy (mem);
552         return 0;
553     }
554     logf (LOG_DEBUG, "grs_retrieve: size");
555     if ((dnew = data1_insert_taggeddata(p->dh, node, node,
556                                        "size", mem)))
557     {
558         dnew->u.data.what = DATA1I_text;
559         dnew->u.data.data = dnew->lbuf;
560         sprintf(dnew->u.data.data, "%d", p->recordSize);
561         dnew->u.data.len = strlen(dnew->u.data.data);
562     }
563
564     tagname = res_get_def(p->res, "tagrank", "rank");
565     if (strcmp(tagname, "0") && p->score >= 0 && (dnew =
566                           data1_insert_taggeddata(p->dh, node,
567                                                   node, tagname,
568                                                   mem)))
569     {
570         logf (LOG_DEBUG, "grs_retrieve: %s", tagname);
571         dnew->u.data.what = DATA1I_num;
572         dnew->u.data.data = dnew->lbuf;
573         sprintf(dnew->u.data.data, "%d", p->score);
574         dnew->u.data.len = strlen(dnew->u.data.data);
575     }
576
577     tagname = res_get_def(p->res, "tagsysno", "localControlNumber");
578     if (strcmp(tagname, "0") && p->localno > 0 &&
579          (dnew = data1_insert_taggeddata(p->dh, node, node, tagname, mem)))
580     {
581         logf (LOG_DEBUG, "grs_retrieve: %s", tagname);
582         dnew->u.data.what = DATA1I_text;
583         dnew->u.data.data = dnew->lbuf;
584         sprintf(dnew->u.data.data, "%d", p->localno);
585         dnew->u.data.len = strlen(dnew->u.data.data);
586     }
587
588     logf (LOG_DEBUG, "grs_retrieve: schemaIdentifier");
589     if (p->input_format == VAL_GRS1 && node->u.root.absyn &&
590         node->u.root.absyn->reference != VAL_NONE)
591     {
592         oident oe;
593         Odr_oid *oid;
594         int oidtmp[OID_SIZE];
595
596         oe.proto = PROTO_Z3950;
597         oe.oclass = CLASS_SCHEMA;
598         oe.value = node->u.root.absyn->reference;
599
600         if ((oid = oid_ent_to_oid (&oe, oidtmp)))
601         {
602             char tmp[128];
603             data1_handle dh = p->dh;
604             char *p = tmp;
605             int *ii;
606
607             for (ii = oid; *ii >= 0; ii++)
608             {
609                 if (p != tmp)
610                     *(p++) = '.';
611                 sprintf(p, "%d", *ii);
612                 p += strlen(p);
613             }
614             *(p++) = '\0';
615
616             if ((dnew = data1_insert_taggeddata(dh, node, node,
617                                                "schemaIdentifier", mem)))
618             {
619                 dnew->u.data.what = DATA1I_oid;
620                 dnew->u.data.data = (char *) nmem_malloc(mem, p - tmp);
621                 memcpy(dnew->u.data.data, tmp, p - tmp);
622                 dnew->u.data.len = p - tmp;
623             }
624         }
625     }
626
627     logf (LOG_DEBUG, "grs_retrieve: schema mapping");
628     /*
629      * Does the requested format match a known schema-mapping? (this reflects
630      * the overlap of schema and formatting which is inherent in the MARC
631      * family)
632      * NOTE: This should look at the schema-specification in the compspec
633      * as well.
634      */
635     for (map = node->u.root.absyn->maptabs; map; map = map->next)
636         if (map->target_absyn_ref == p->input_format)
637         {
638             onode = node;
639             if (!(node = data1_map_record(p->dh, onode, map, mem)))
640             {
641                 p->diagnostic = 14;
642                 nmem_destroy (mem);
643                 return 0;
644             }
645             break;
646         }
647
648     logf (LOG_DEBUG, "grs_retrieve: element spec");
649     if (p->comp && (res = process_comp(p->dh, node, p->comp)) > 0)
650     {
651         p->diagnostic = res;
652         if (onode)
653             data1_free_tree(p->dh, onode);
654         data1_free_tree(p->dh, node);
655         nmem_destroy(mem);
656         return 0;
657     }
658     else if (p->comp && !res)
659         selected = 1;
660
661     logf (LOG_DEBUG, "grs_retrieve: transfer syntax mapping");
662     switch (p->output_format = (p->input_format != VAL_NONE ?
663         p->input_format : VAL_SUTRS))
664     {
665         data1_marctab *marctab;
666         int dummy;
667
668         case VAL_GRS1:
669             dummy = 0;
670             if (!(p->rec_buf = data1_nodetogr(p->dh, node, selected,
671                                               p->odr, &dummy)))
672                 p->diagnostic = 238; /* not available in requested syntax */
673             else
674                 p->rec_len = (size_t) (-1);
675             break;
676         case VAL_EXPLAIN:
677             if (!(p->rec_buf = data1_nodetoexplain(p->dh, node, selected,
678                                                    p->odr)))
679                 p->diagnostic = 238;
680             else
681                 p->rec_len = (size_t) (-1);
682             break;
683         case VAL_SUMMARY:
684             if (!(p->rec_buf = data1_nodetosummary(p->dh, node, selected,
685                                                    p->odr)))
686                 p->diagnostic = 238;
687             else
688                 p->rec_len = (size_t) (-1);
689             break;
690         case VAL_SUTRS:
691             if (!(p->rec_buf = data1_nodetobuf(p->dh, node, selected,
692                 (int*)&p->rec_len)))
693                 p->diagnostic = 238;
694             else
695             {
696                 char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
697                 memcpy (new_buf, p->rec_buf, p->rec_len);
698                 p->rec_buf = new_buf;
699             }
700             break;
701         case VAL_SOIF:
702             if (!(p->rec_buf = data1_nodetosoif(p->dh, node, selected,
703                                                 (int*)&p->rec_len)))
704                 p->diagnostic = 238;
705             else
706             {
707                 char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
708                 memcpy (new_buf, p->rec_buf, p->rec_len);
709                 p->rec_buf = new_buf;
710             }
711             break;
712         default:
713             if (!node->u.root.absyn)
714             {
715                 p->diagnostic = 238;
716                 break;
717             }
718             for (marctab = node->u.root.absyn->marc; marctab;
719                 marctab = marctab->next)
720                 if (marctab->reference == p->input_format)
721                     break;
722             if (!marctab)
723             {
724                 p->diagnostic = 238;
725                 break;
726             }
727             if (!(p->rec_buf = data1_nodetomarc(p->dh, marctab, node,
728                                                 selected,
729                                                 (int*)&p->rec_len)))
730                 p->diagnostic = 238;
731             else
732             {
733                 char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
734                 memcpy (new_buf, p->rec_buf, p->rec_len);
735                 p->rec_buf = new_buf;
736             }
737     }
738     if (node)
739         data1_free_tree(p->dh, node);
740     if (onode)
741         data1_free_tree(p->dh, onode);
742     nmem_destroy(mem);
743     return 0;
744 }
745
746 static struct recType grs_type =
747 {
748     "grs",
749     grs_init,
750     grs_destroy,
751     grs_extract,
752     grs_retrieve
753 };
754
755 RecType recTypeGrs = &grs_type;