Zebra uses own XML reader (was part of YAZ before)
[idzebra-moved-to-github.git] / recctrl / recgrs.c
1 /* $Id: recgrs.c,v 1.62 2002-08-28 12:47:10 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
3    Index Data Aps
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <assert.h>
25 #include <sys/types.h>
26 #ifndef WIN32
27 #include <unistd.h>
28 #endif
29
30 #include <yaz/log.h>
31 #include <yaz/oid.h>
32
33 #include <recctrl.h>
34 #include "grsread.h"
35
36 #define GRS_MAX_WORD 512
37
38 struct grs_handler {
39     RecTypeGrs type;
40     void *clientData;
41     int initFlag;
42     struct grs_handler *next;
43 };
44
45 struct grs_handlers {
46     struct grs_handler *handlers;
47 };
48
49 static int read_grs_type (struct grs_handlers *h,
50                           struct grs_read_info *p, const char *type,
51                           data1_node **root)
52 {
53     struct grs_handler *gh = h->handlers;
54     const char *cp = strchr (type, '.');
55
56     if (cp == NULL || cp == type)
57     {
58         cp = strlen(type) + type;
59         *p->type = 0;
60     }
61     else
62         strcpy (p->type, cp+1);
63     for (gh = h->handlers; gh; gh = gh->next)
64     {
65         if (!memcmp (type, gh->type->type, cp-type))
66         {
67             if (!gh->initFlag)
68             {
69                 gh->initFlag = 1;
70                 gh->clientData = (*gh->type->init)();
71             }
72             p->clientData = gh->clientData;
73             *root = (gh->type->read)(p);
74             gh->clientData = p->clientData;
75             return 0;
76         }
77     }
78     return 1;
79 }
80
81 static void grs_add_handler (struct grs_handlers *h, RecTypeGrs t)
82 {
83     struct grs_handler *gh = (struct grs_handler *) xmalloc (sizeof(*gh));
84     gh->next = h->handlers;
85     h->handlers = gh;
86     gh->initFlag = 0;
87     gh->clientData = 0;
88     gh->type = t;
89 }
90
91 static void *grs_init(RecType recType)
92 {
93     struct grs_handlers *h = (struct grs_handlers *) xmalloc (sizeof(*h));
94     h->handlers = 0;
95
96     grs_add_handler (h, recTypeGrs_sgml);
97     grs_add_handler (h, recTypeGrs_regx);
98 #if HAVE_TCL_H
99     grs_add_handler (h, recTypeGrs_tcl);
100 #endif
101     grs_add_handler (h, recTypeGrs_marc);
102 #if HAVE_EXPAT_H
103     grs_add_handler (h, recTypeGrs_xml);
104 #endif
105     return h;
106 }
107
108 static void grs_destroy(void *clientData)
109 {
110     struct grs_handlers *h = (struct grs_handlers *) clientData;
111     struct grs_handler *gh = h->handlers, *gh_next;
112     while (gh)
113     {
114         gh_next = gh->next;
115         if (gh->initFlag)
116             (*gh->type->destroy)(gh->clientData);
117         xfree (gh);
118         gh = gh_next;
119     }
120     xfree (h);
121 }
122
123 /* use
124      1   start element (tag)
125      2   end element
126      3   start attr (and attr-exact)
127      4   end attr
128
129   1016   cdata
130   1015   attr data
131 */
132
133 static void index_xpath (data1_node *n, struct recExtractCtrl *p,
134                          int level, RecWord *wrd, int use)
135 {
136     int i;
137     char tag_path_full[1024];
138     size_t flen = 0;
139     data1_node *nn;
140
141     switch (n->which)
142     {
143     case DATA1N_data:
144         wrd->reg_type = 'w';
145         wrd->string = n->u.data.data;
146         wrd->length = n->u.data.len;
147         wrd->attrSet = VAL_IDXPATH,
148         wrd->attrUse = use;
149         if (p->flagShowRecords)
150         {
151             printf("%*s data=", (level + 1) * 4, "");
152             for (i = 0; i<wrd->length && i < 8; i++)
153                 fputc (wrd->string[i], stdout);
154             printf("\n");
155         }
156         else
157         {
158             (*p->tokenAdd)(wrd);
159         }
160         break;
161     case DATA1N_tag:
162         for (nn = n; nn; nn = nn->parent)
163         {
164             if (nn->which == DATA1N_tag)
165             {
166                 size_t tlen = strlen(nn->u.tag.tag);
167                 if (tlen + flen > (sizeof(tag_path_full)-2))
168                     return;
169                 memcpy (tag_path_full + flen, nn->u.tag.tag, tlen);
170                 flen += tlen;
171                 tag_path_full[flen++] = '/';
172             }
173             else if (nn->which == DATA1N_root)
174                 break;
175         }
176         wrd->reg_type = '0';
177         wrd->string = tag_path_full;
178         wrd->length = flen;
179         wrd->attrSet = VAL_IDXPATH;
180         wrd->attrUse = use;
181         if (p->flagShowRecords)
182         {
183             printf("%*s tag=", (level + 1) * 4, "");
184             for (i = 0; i<wrd->length && i < 40; i++)
185                 fputc (wrd->string[i], stdout);
186             if (i == 40)
187                 printf (" ..");
188             printf("\n");
189         }
190         else
191         {
192             data1_xattr *xp;
193             (*p->tokenAdd)(wrd);   /* index element pag (AKA tag path) */
194             if (use == 1)
195             {
196                 for (xp = n->u.tag.attributes; xp; xp = xp->next)
197                 {
198                     char comb[512];
199                     /* attribute  (no value) */
200                     wrd->reg_type = '0';
201                     wrd->attrUse = 3;
202                     wrd->string = xp->name;
203                     wrd->length = strlen(xp->name);
204                     
205                     wrd->seqno--;
206                     (*p->tokenAdd)(wrd);
207
208                     if (xp->value &&
209                         strlen(xp->name) + strlen(xp->value) < sizeof(comb)-2)
210                     {
211                         /* attribute value exact */
212                         strcpy (comb, xp->name);
213                         strcat (comb, "=");
214                         strcat (comb, xp->value);
215                         
216                         wrd->attrUse = 3;
217                         wrd->reg_type = '0';
218                         wrd->string = comb;
219                         wrd->length = strlen(comb);
220                         wrd->seqno--;
221                         
222                         (*p->tokenAdd)(wrd);
223                     }
224                 }                
225                 for (xp = n->u.tag.attributes; xp; xp = xp->next)
226                 {
227                     char attr_tag_path_full[1024];
228                     
229                     sprintf (attr_tag_path_full, "@%s/%.*s",
230                              xp->name, flen, tag_path_full);
231
232                     wrd->reg_type = '0';
233                     wrd->attrUse = 1;
234                     wrd->string = attr_tag_path_full;
235                     wrd->length = strlen(attr_tag_path_full);
236                     (*p->tokenAdd)(wrd);
237                     
238                     wrd->attrUse = 1015;
239                     wrd->reg_type = 'w';
240                     wrd->string = xp->value;
241                     wrd->length = strlen(xp->value);
242                     
243                     (*p->tokenAdd)(wrd);
244                     
245                     wrd->reg_type = '0';
246                     wrd->attrUse = 2;
247                     wrd->string = attr_tag_path_full;
248                     wrd->length = strlen(attr_tag_path_full);
249                     (*p->tokenAdd)(wrd);
250                 }
251             }
252         }
253     }
254 }
255
256 static void index_termlist (data1_node *par, data1_node *n,
257                             struct recExtractCtrl *p, int level, RecWord *wrd)
258 {
259     data1_termlist *tlist = 0;
260     data1_datatype dtype = DATA1K_string;
261     /*
262      * cycle up towards the root until we find a tag with an att..
263      * this has the effect of indexing locally defined tags with
264      * the attribute of their ancestor in the record.
265      */
266     
267     while (!par->u.tag.element)
268         if (!par->parent || !(par=get_parent_tag(p->dh, par->parent)))
269             break;
270     if (!par || !(tlist = par->u.tag.element->termlists))
271         return;
272     if (par->u.tag.element->tag)
273         dtype = par->u.tag.element->tag->kind;
274     
275     for (; tlist; tlist = tlist->next)
276     {
277         char xattr[512];
278         /* consider source */
279         wrd->string = 0;
280         
281         if (!strcmp (tlist->source, "data") && n->which == DATA1N_data)
282         {
283             wrd->string = n->u.data.data;
284             wrd->length = n->u.data.len;
285         }
286         else if (!strcmp (tlist->source, "tag") && n->which == DATA1N_tag)
287         {
288             wrd->string = n->u.tag.tag;
289             wrd->length = strlen(n->u.tag.tag);
290         }
291         else if (sscanf (tlist->source, "attr(%511[^)])", xattr) == 1 &&
292             n->which == DATA1N_tag)
293         {
294             data1_xattr *p = n->u.tag.attributes;
295             while (p && strcmp (p->name, xattr))
296                 p = p->next;
297             if (p)
298             {
299                 wrd->string = p->value;
300                 wrd->length = strlen(p->value);
301             }
302         }
303         if (wrd->string)
304         {
305             if (p->flagShowRecords)
306             {
307                 int i;
308                 printf("%*sIdx: [%s]", (level + 1) * 4, "",
309                        tlist->structure);
310                 printf("%s:%s [%d] %s",
311                        tlist->att->parent->name,
312                        tlist->att->name, tlist->att->value,
313                        tlist->source);
314                 printf (" data=\"");
315                 for (i = 0; i<wrd->length && i < 8; i++)
316                     fputc (wrd->string[i], stdout);
317                 fputc ('"', stdout);
318                 if (wrd->length > 8)
319                     printf (" ...");
320                 fputc ('\n', stdout);
321             }
322             else
323             {
324                 wrd->reg_type = *tlist->structure;
325                 wrd->attrSet = (int) (tlist->att->parent->reference);
326                 wrd->attrUse = tlist->att->locals->local;
327                 (*p->tokenAdd)(wrd);
328             }
329         }
330     }
331 }
332
333 static int dumpkeys(data1_node *n, struct recExtractCtrl *p, int level,
334                     RecWord *wrd)
335 {
336     for (; n; n = n->next)
337     {
338         if (p->flagShowRecords) /* display element description to user */
339         {
340             if (n->which == DATA1N_root)
341             {
342                 printf("%*s", level * 4, "");
343                 printf("Record type: '%s'\n", n->u.root.type);
344             }
345             else if (n->which == DATA1N_tag)
346             {
347                 data1_element *e;
348
349                 printf("%*s", level * 4, "");
350                 if (!(e = n->u.tag.element))
351                     printf("Local tag: '%s'\n", n->u.tag.tag);
352                 else
353                 {
354                     printf("Elm: '%s' ", e->name);
355                     if (e->tag)
356                     {
357                         data1_tag *t = e->tag;
358
359                         printf("TagNam: '%s' ", t->names->name);
360                         printf("(");
361                         if (t->tagset)
362                             printf("%s[%d],", t->tagset->name, t->tagset->type);
363                         else
364                             printf("?,");
365                         if (t->which == DATA1T_numeric)
366                             printf("%d)", t->value.numeric);
367                         else
368                             printf("'%s')", t->value.string);
369                     }
370                     printf("\n");
371                 }
372             }
373         }
374
375         if (n->which == DATA1N_tag)
376         {
377             index_termlist (n, n, p, level, wrd);
378             /* index start tag */
379             assert (n->root->u.root.absyn);
380             
381             if (!n->root->u.root.absyn)
382                 index_xpath (n, p, level, wrd, 1);
383             else if (n->root->u.root.absyn->enable_xpath_indexing)
384                 index_xpath (n, p, level, wrd, 1);
385         }
386
387         if (n->child)
388             if (dumpkeys(n->child, p, level + 1, wrd) < 0)
389                 return -1;
390
391
392         if (n->which == DATA1N_data)
393         {
394             data1_node *par = get_parent_tag(p->dh, n);
395
396             if (p->flagShowRecords)
397             {
398                 printf("%*s", level * 4, "");
399                 printf("Data: ");
400                 if (n->u.data.len > 256)
401                     printf("'%.240s ... %.6s'\n", n->u.data.data,
402                            n->u.data.data + n->u.data.len-6);
403                 else if (n->u.data.len > 0)
404                     printf("'%.*s'\n", n->u.data.len, n->u.data.data);
405                 else
406                     printf("NULL\n");
407             }
408
409             if (par)
410                 index_termlist (par, n, p, level, wrd);
411             if (!n->root->u.root.absyn)
412                 index_xpath (n, p, level, wrd, 1016);
413             else if (n->root->u.root.absyn->enable_xpath_indexing)
414                 index_xpath (n, p, level, wrd, 1016);
415         }
416
417         if (n->which == DATA1N_tag)
418         {
419             /* index end tag */
420             if (!n->root->u.root.absyn)
421                 index_xpath (n, p, level, wrd, 2);
422             else if (n->root->u.root.absyn->enable_xpath_indexing)
423                 index_xpath (n, p, level, wrd, 2);
424         }
425
426         if (p->flagShowRecords && n->which == DATA1N_root)
427         {
428             printf("%*s-------------\n\n", level * 4, "");
429         }
430     }
431     return 0;
432 }
433
434 int grs_extract_tree(struct recExtractCtrl *p, data1_node *n)
435 {
436     oident oe;
437     int oidtmp[OID_SIZE];
438     RecWord wrd;
439
440     oe.proto = PROTO_Z3950;
441     oe.oclass = CLASS_SCHEMA;
442     if (n->u.root.absyn)
443     {
444         oe.value = n->u.root.absyn->reference;
445         
446         if ((oid_ent_to_oid (&oe, oidtmp)))
447             (*p->schemaAdd)(p, oidtmp);
448     }
449     (*p->init)(p, &wrd);
450
451     return dumpkeys(n, p, 0, &wrd);
452 }
453
454 static int grs_extract_sub(struct grs_handlers *h, struct recExtractCtrl *p,
455                            NMEM mem)
456 {
457     data1_node *n;
458     struct grs_read_info gri;
459     oident oe;
460     int oidtmp[OID_SIZE];
461     RecWord wrd;
462
463     gri.readf = p->readf;
464     gri.seekf = p->seekf;
465     gri.tellf = p->tellf;
466     gri.endf = p->endf;
467     gri.fh = p->fh;
468     gri.offset = p->offset;
469     gri.mem = mem;
470     gri.dh = p->dh;
471
472     if (read_grs_type (h, &gri, p->subType, &n))
473         return RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER;
474     if (!n)
475         return RECCTRL_EXTRACT_EOF;
476     oe.proto = PROTO_Z3950;
477     oe.oclass = CLASS_SCHEMA;
478 #if 0
479     if (!n->u.root.absyn)
480         return RECCTRL_EXTRACT_ERROR;
481 #endif
482     if (n->u.root.absyn)
483     {
484         oe.value = n->u.root.absyn->reference;
485         if ((oid_ent_to_oid (&oe, oidtmp)))
486             (*p->schemaAdd)(p, oidtmp);
487     }
488
489     /* ensure our data1 tree is UTF-8 */
490     data1_iconv (p->dh, mem, n, "UTF-8", data1_get_encoding(p->dh, n));
491
492 #if 0
493     data1_pr_tree (p->dh, n, stdout);
494 #endif
495
496     (*p->init)(p, &wrd);
497     if (dumpkeys(n, p, 0, &wrd) < 0)
498     {
499         data1_free_tree(p->dh, n);
500         return RECCTRL_EXTRACT_ERROR_GENERIC;
501     }
502     data1_free_tree(p->dh, n);
503     return RECCTRL_EXTRACT_OK;
504 }
505
506 static int grs_extract(void *clientData, struct recExtractCtrl *p)
507 {
508     int ret;
509     NMEM mem = nmem_create ();
510     struct grs_handlers *h = (struct grs_handlers *) clientData;
511
512     ret = grs_extract_sub(h, p, mem);
513     nmem_destroy(mem);
514     return ret;
515 }
516
517 /*
518  * Return: -1: Nothing done. 0: Ok. >0: Bib-1 diagnostic.
519  */
520 static int process_comp(data1_handle dh, data1_node *n, Z_RecordComposition *c)
521 {
522     data1_esetname *eset;
523     Z_Espec1 *espec = 0;
524     Z_ElementSpec *p;
525
526     switch (c->which)
527     {
528     case Z_RecordComp_simple:
529         if (c->u.simple->which != Z_ElementSetNames_generic)
530             return 26; /* only generic form supported. Fix this later */
531         if (!(eset = data1_getesetbyname(dh, n->u.root.absyn,
532                                          c->u.simple->u.generic)))
533         {
534             logf(LOG_LOG, "Unknown esetname '%s'", c->u.simple->u.generic);
535             return 25; /* invalid esetname */
536         }
537         logf(LOG_DEBUG, "Esetname '%s' in simple compspec",
538              c->u.simple->u.generic);
539         espec = eset->spec;
540         break;
541     case Z_RecordComp_complex:
542         if (c->u.complex->generic)
543         {
544             /* insert check for schema */
545             if ((p = c->u.complex->generic->elementSpec))
546             {
547                 switch (p->which)
548                 {
549                 case Z_ElementSpec_elementSetName:
550                     if (!(eset =
551                           data1_getesetbyname(dh, n->u.root.absyn,
552                                               p->u.elementSetName)))
553                     {
554                         logf(LOG_LOG, "Unknown esetname '%s'",
555                              p->u.elementSetName);
556                         return 25; /* invalid esetname */
557                     }
558                     logf(LOG_DEBUG, "Esetname '%s' in complex compspec",
559                          p->u.elementSetName);
560                     espec = eset->spec;
561                     break;
562                 case Z_ElementSpec_externalSpec:
563                     if (p->u.externalSpec->which == Z_External_espec1)
564                     {
565                         logf(LOG_DEBUG, "Got Espec-1");
566                         espec = p->u.externalSpec-> u.espec1;
567                     }
568                     else
569                     {
570                         logf(LOG_LOG, "Unknown external espec.");
571                         return 25; /* bad. what is proper diagnostic? */
572                     }
573                     break;
574                 }
575             }
576         }
577         else
578             return 26; /* fix */
579     }
580     if (espec)
581     {
582         logf (LOG_DEBUG, "Element: Espec-1 match");
583         return data1_doespec1(dh, n, espec);
584     }
585     else
586     {
587         logf (LOG_DEBUG, "Element: all match");
588         return -1;
589     }
590 }
591
592 static void add_idzebra_info (struct recRetrieveCtrl *p, data1_node *top,
593                               NMEM mem)
594 {
595     const char *idzebra_ns[7];
596
597     idzebra_ns[0] = "xmlns:idzebra";
598     idzebra_ns[1] = "http://www.indexdata.dk/zebra/";
599     idzebra_ns[2] = 0;
600
601     data1_tag_add_attr (p->dh, mem, top, idzebra_ns);
602
603     data1_mk_tag_data_int (p->dh, top, "idzebra:size", p->recordSize,
604                            mem);
605     if (p->score != -1)
606         data1_mk_tag_data_int (p->dh, top, "idzebra:score",
607                                p->score, mem);
608     
609     data1_mk_tag_data_int (p->dh, top, "idzebra:localnumber", p->localno,
610                            mem);
611     if (p->fname)
612         data1_mk_tag_data_text(p->dh, top, "idzebra:filename",
613                                p->fname, mem);
614 }
615
616 static int grs_retrieve(void *clientData, struct recRetrieveCtrl *p)
617 {
618     data1_node *node = 0, *onode = 0, *top;
619     data1_node *dnew;
620     data1_maptab *map;
621     int res, selected = 0;
622     NMEM mem;
623     struct grs_read_info gri;
624     char *tagname;
625     struct grs_handlers *h = (struct grs_handlers *) clientData;
626     int requested_schema = VAL_NONE;
627     data1_marctab *marctab;
628     int dummy;
629     
630     mem = nmem_create();
631     gri.readf = p->readf;
632     gri.seekf = p->seekf;
633     gri.tellf = p->tellf;
634     gri.endf = NULL;
635     gri.fh = p->fh;
636     gri.offset = 0;
637     gri.mem = mem;
638     gri.dh = p->dh;
639
640     logf (LOG_DEBUG, "grs_retrieve");
641     if (read_grs_type (h, &gri, p->subType, &node))
642     {
643         p->diagnostic = 14;
644         nmem_destroy (mem);
645         return 0;
646     }
647     if (!node)
648     {
649         p->diagnostic = 14;
650         nmem_destroy (mem);
651         return 0;
652     }
653     /* ensure our data1 tree is UTF-8 */
654     data1_iconv (p->dh, mem, node, "UTF-8", data1_get_encoding(p->dh, node));
655
656 #if 1
657     data1_pr_tree (p->dh, node, stdout);
658 #endif
659     top = data1_get_root_tag (p->dh, node);
660
661     logf (LOG_DEBUG, "grs_retrieve: size");
662     if ((dnew = data1_mk_tag_data_wd(p->dh, top, "size", mem)))
663     {
664         dnew->u.data.what = DATA1I_text;
665         dnew->u.data.data = dnew->lbuf;
666         sprintf(dnew->u.data.data, "%d", p->recordSize);
667         dnew->u.data.len = strlen(dnew->u.data.data);
668     }
669
670     tagname = res_get_def(p->res, "tagrank", "rank");
671     if (strcmp(tagname, "0") && p->score >= 0 &&
672         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
673     {
674         logf (LOG_DEBUG, "grs_retrieve: %s", tagname);
675         dnew->u.data.what = DATA1I_num;
676         dnew->u.data.data = dnew->lbuf;
677         sprintf(dnew->u.data.data, "%d", p->score);
678         dnew->u.data.len = strlen(dnew->u.data.data);
679     }
680
681     tagname = res_get_def(p->res, "tagsysno", "localControlNumber");
682     if (strcmp(tagname, "0") && p->localno > 0 &&
683          (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
684     {
685         logf (LOG_DEBUG, "grs_retrieve: %s", tagname);
686         dnew->u.data.what = DATA1I_text;
687         dnew->u.data.data = dnew->lbuf;
688
689         sprintf(dnew->u.data.data, "%d", p->localno);
690         dnew->u.data.len = strlen(dnew->u.data.data);
691     }
692 #if 0
693     data1_pr_tree (p->dh, node, stdout);
694 #endif
695     if (p->comp && p->comp->which == Z_RecordComp_complex &&
696         p->comp->u.complex->generic &&
697         p->comp->u.complex->generic->schema)
698     {
699         oident *oe = oid_getentbyoid (p->comp->u.complex->generic->schema);
700         if (oe)
701             requested_schema = oe->value;
702     }
703
704     /* If schema has been specified, map if possible, then check that
705      * we got the right one 
706      */
707     if (requested_schema != VAL_NONE)
708     {
709         logf (LOG_DEBUG, "grs_retrieve: schema mapping");
710         for (map = node->u.root.absyn->maptabs; map; map = map->next)
711         {
712             if (map->target_absyn_ref == requested_schema)
713             {
714                 onode = node;
715                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
716                 {
717                     p->diagnostic = 14;
718                     nmem_destroy (mem);
719                     return 0;
720                 }
721                 break;
722             }
723         }
724         if (node->u.root.absyn &&
725             requested_schema != node->u.root.absyn->reference)
726         {
727             p->diagnostic = 238;
728             nmem_destroy (mem);
729             return 0;
730         }
731     }
732     /*
733      * Does the requested format match a known syntax-mapping? (this reflects
734      * the overlap of schema and formatting which is inherent in the MARC
735      * family)
736      */
737     yaz_log (LOG_DEBUG, "grs_retrieve: syntax mapping");
738     if (node->u.root.absyn)
739         for (map = node->u.root.absyn->maptabs; map; map = map->next)
740         {
741             if (map->target_absyn_ref == p->input_format)
742             {
743                 onode = node;
744                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
745                 {
746                     p->diagnostic = 14;
747                     nmem_destroy (mem);
748                     return 0;
749                 }
750                 break;
751             }
752         }
753     yaz_log (LOG_DEBUG, "grs_retrieve: schemaIdentifier");
754     if (node->u.root.absyn &&
755         node->u.root.absyn->reference != VAL_NONE &&
756         p->input_format == VAL_GRS1)
757     {
758         oident oe;
759         Odr_oid *oid;
760         int oidtmp[OID_SIZE];
761         
762         oe.proto = PROTO_Z3950;
763         oe.oclass = CLASS_SCHEMA;
764         oe.value = node->u.root.absyn->reference;
765         
766         if ((oid = oid_ent_to_oid (&oe, oidtmp)))
767         {
768             char tmp[128];
769             data1_handle dh = p->dh;
770             char *p = tmp;
771             int *ii;
772             
773             for (ii = oid; *ii >= 0; ii++)
774             {
775                 if (p != tmp)
776                         *(p++) = '.';
777                 sprintf(p, "%d", *ii);
778                 p += strlen(p);
779             }
780             *(p++) = '\0';
781                 
782             if ((dnew = data1_mk_tag_data_wd(dh, node, 
783                                              "schemaIdentifier", mem)))
784             {
785                 dnew->u.data.what = DATA1I_oid;
786                 dnew->u.data.data = (char *) nmem_malloc(mem, p - tmp);
787                 memcpy(dnew->u.data.data, tmp, p - tmp);
788                 dnew->u.data.len = p - tmp;
789             }
790         }
791     }
792
793     logf (LOG_DEBUG, "grs_retrieve: element spec");
794     if (p->comp && (res = process_comp(p->dh, node, p->comp)) > 0)
795     {
796         p->diagnostic = res;
797         if (onode)
798             data1_free_tree(p->dh, onode);
799         data1_free_tree(p->dh, node);
800         nmem_destroy(mem);
801         return 0;
802     }
803     else if (p->comp && !res)
804         selected = 1;
805
806 #if 0
807     data1_pr_tree (p->dh, node, stdout);
808 #endif
809     logf (LOG_DEBUG, "grs_retrieve: transfer syntax mapping");
810     switch (p->output_format = (p->input_format != VAL_NONE ?
811                                 p->input_format : VAL_SUTRS))
812     {
813     case VAL_TEXT_XML:
814         add_idzebra_info (p, top, mem);
815
816         if (p->encoding)
817             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
818
819         if (!(p->rec_buf = data1_nodetoidsgml(p->dh, node, selected,
820                                               &p->rec_len)))
821             p->diagnostic = 238;
822         else
823         {
824             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
825             memcpy (new_buf, p->rec_buf, p->rec_len);
826             p->rec_buf = new_buf;
827         }
828         break;
829     case VAL_GRS1:
830         dummy = 0;
831         if (!(p->rec_buf = data1_nodetogr(p->dh, node, selected,
832                                           p->odr, &dummy)))
833             p->diagnostic = 238; /* not available in requested syntax */
834         else
835             p->rec_len = (size_t) (-1);
836         break;
837     case VAL_EXPLAIN:
838         if (!(p->rec_buf = data1_nodetoexplain(p->dh, node, selected,
839                                                p->odr)))
840             p->diagnostic = 238;
841         else
842             p->rec_len = (size_t) (-1);
843         break;
844     case VAL_SUMMARY:
845         if (!(p->rec_buf = data1_nodetosummary(p->dh, node, selected,
846                                                p->odr)))
847             p->diagnostic = 238;
848         else
849             p->rec_len = (size_t) (-1);
850         break;
851     case VAL_SUTRS:
852         if (p->encoding)
853             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
854         if (!(p->rec_buf = data1_nodetobuf(p->dh, node, selected,
855                                            &p->rec_len)))
856             p->diagnostic = 238;
857         else
858         {
859             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
860             memcpy (new_buf, p->rec_buf, p->rec_len);
861             p->rec_buf = new_buf;
862         }
863         break;
864     case VAL_SOIF:
865         if (!(p->rec_buf = data1_nodetosoif(p->dh, node, selected,
866                                             &p->rec_len)))
867             p->diagnostic = 238;
868         else
869         {
870             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
871             memcpy (new_buf, p->rec_buf, p->rec_len);
872             p->rec_buf = new_buf;
873         }
874         break;
875     default:
876         if (!node->u.root.absyn)
877         {
878             p->diagnostic = 238;
879             break;
880         }
881         for (marctab = node->u.root.absyn->marc; marctab;
882              marctab = marctab->next)
883             if (marctab->reference == p->input_format)
884                 break;
885         if (!marctab)
886         {
887             p->diagnostic = 238;
888             break;
889         }
890         if (p->encoding)
891             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
892         if (!(p->rec_buf = data1_nodetomarc(p->dh, marctab, node,
893                                         selected, &p->rec_len)))
894             p->diagnostic = 238;
895         else
896         {
897             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
898             memcpy (new_buf, p->rec_buf, p->rec_len);
899                 p->rec_buf = new_buf;
900         }
901     }
902     if (node)
903         data1_free_tree(p->dh, node);
904     if (onode)
905         data1_free_tree(p->dh, onode);
906     nmem_destroy(mem);
907     return 0;
908 }
909
910 static struct recType grs_type =
911 {
912     "grs",
913     grs_init,
914     grs_destroy,
915     grs_extract,
916     grs_retrieve
917 };
918
919 RecType recTypeGrs = &grs_type;