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