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