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