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