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