5c09a82e5aeb88397d06e57a0d3911ca7e3d4d47
[idzebra-moved-to-github.git] / recctrl / recgrs.c
1 /* $Id: recgrs.c,v 1.86.2.14 2006-11-03 10:11:37 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003
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 this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
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 RecWord_list {
39     NMEM nmem;
40     struct RecWord_entry **entries;
41     unsigned hash_size;
42     char *name;
43 };
44
45 struct RecWord_entry {
46     RecWord w;
47     struct RecWord_entry *next;
48 };
49
50 struct RecWord_list *RecWord_list_create(const char *name)
51 {
52     NMEM m = nmem_create();
53     struct RecWord_list *p = nmem_malloc(m, sizeof(*p));
54     size_t i;
55
56     p->hash_size = 127;
57     p->nmem = m;
58     p->entries = nmem_malloc(m, p->hash_size * sizeof(*p->entries));
59     for (i = 0; i<p->hash_size; i++)
60         p->entries[i] = 0;
61     p->name = nmem_strdup(m, name);
62     return p;
63 }
64
65 int RecWord_list_lookadd(struct RecWord_list *l, RecWord *wrd)
66 {
67     struct RecWord_entry *e;
68
69     unsigned hash =
70         (wrd->attrSet*15 + wrd->attrSet + wrd->reg_type) % l->hash_size;
71
72     for (e = l->entries[hash]; e; e = e->next)
73         if (e->w.attrSet == wrd->attrSet &&
74             e->w.attrUse == wrd->attrUse &&
75             e->w.reg_type == wrd->reg_type &&
76             e->w.length == wrd->length &&
77             !memcmp(e->w.string, wrd->string, wrd->length))
78         {
79 #if 0
80             fprintf(stderr, "DUP key found in %s\n", l->name);
81             fprintf(stderr, "set=%d use=%d regtype=%c\n",
82                     wrd->attrSet, wrd->attrUse, wrd->reg_type);
83 #endif
84             return 0;
85         }
86     e = nmem_malloc(l->nmem, sizeof(*e));
87     e->next = l->entries[hash];
88     l->entries[hash] = e;
89     memcpy(&e->w, wrd, sizeof(*wrd));
90     e->w.string = nmem_malloc(l->nmem, wrd->length);
91     memcpy(e->w.string, wrd->string, wrd->length);
92     return 1;
93 }
94
95 void RecWord_list_destroy(struct RecWord_list *l)
96 {
97     if (l)
98         nmem_destroy(l->nmem);
99 }
100
101
102 struct grs_handler {
103     RecTypeGrs type;
104     void *clientData;
105     int initFlag;
106     struct grs_handler *next;
107 };
108
109 struct grs_handlers {
110     struct grs_handler *handlers;
111 };
112
113 static int read_grs_type (struct grs_handlers *h,
114                           struct grs_read_info *p, const char *type,
115                           data1_node **root)
116 {
117     struct grs_handler *gh = h->handlers;
118     const char *cp = strchr (type, '.');
119
120     if (cp == NULL || cp == type)
121     {
122         cp = strlen(type) + type;
123         *p->type = 0;
124     }
125     else
126         strcpy (p->type, cp+1);
127     for (gh = h->handlers; gh; gh = gh->next)
128     {
129         if (!memcmp (type, gh->type->type, cp-type) && 
130             gh->type->type[cp-type] == '\0')
131         {
132             if (!gh->initFlag)
133             {
134                 gh->initFlag = 1;
135                 gh->clientData = (*gh->type->init)();
136             }
137             p->clientData = gh->clientData;
138             *root = (gh->type->read)(p);
139             gh->clientData = p->clientData;
140             return 0;
141         }
142     }
143     return 1;
144 }
145
146 static void grs_add_handler (struct grs_handlers *h, RecTypeGrs t)
147 {
148     struct grs_handler *gh = (struct grs_handler *) xmalloc (sizeof(*gh));
149     gh->next = h->handlers;
150     h->handlers = gh;
151     gh->initFlag = 0;
152     gh->clientData = 0;
153     gh->type = t;
154 }
155
156 static void *grs_init(RecType recType)
157 {
158     struct grs_handlers *h = (struct grs_handlers *) xmalloc (sizeof(*h));
159     h->handlers = 0;
160
161     grs_add_handler (h, recTypeGrs_sgml);
162     grs_add_handler (h, recTypeGrs_regx);
163 #if HAVE_TCL_H
164     grs_add_handler (h, recTypeGrs_tcl);
165 #endif
166     grs_add_handler (h, recTypeGrs_marc);
167     grs_add_handler (h, recTypeGrs_marcxml);
168 #if HAVE_EXPAT_H
169     grs_add_handler (h, recTypeGrs_xml);
170 #endif
171 #if HAVE_PERL
172     grs_add_handler (h, recTypeGrs_perl);
173 #endif
174     grs_add_handler (h, recTypeGrs_danbib);
175     return h;
176 }
177
178 static void grs_destroy(void *clientData)
179 {
180     struct grs_handlers *h = (struct grs_handlers *) clientData;
181     struct grs_handler *gh = h->handlers, *gh_next;
182     while (gh)
183     {
184         gh_next = gh->next;
185         if (gh->initFlag)
186             (*gh->type->destroy)(gh->clientData);
187         xfree (gh);
188         gh = gh_next;
189     }
190     xfree (h);
191 }
192
193 struct source_parser {
194     int len;
195     const char *tok;
196     const char *src;
197     int lookahead;
198 };
199
200 static int sp_lex(struct source_parser *sp)
201 {
202     while (*sp->src == ' ')
203         (sp->src)++;
204     sp->tok = sp->src;
205     sp->len = 0;
206     while (*sp->src && !strchr("<>();,-: ", *sp->src))
207     {
208         sp->src++;
209         sp->len++;
210     }
211     if (sp->len)
212         sp->lookahead = 't';
213     else
214     {
215         sp->lookahead = *sp->src;
216         if (*sp->src)
217             sp->src++;
218     }
219     return sp->lookahead;
220 }
221
222
223 static int sp_expr(struct source_parser *sp, data1_node *n, RecWord *wrd)
224 {
225     if (sp->lookahead != 't')
226         return 0;
227     if (sp->len == 4 && !memcmp(sp->tok, "data", sp->len))
228     {
229         if (n->which == DATA1N_data)
230         {
231             wrd->string = n->u.data.data;
232             wrd->length = n->u.data.len;
233         }
234         sp_lex(sp);
235     }
236     else if (sp->len == 3 && !memcmp(sp->tok, "tag", sp->len))
237     {
238         if (n->which == DATA1N_tag)
239         {               
240             wrd->string = n->u.tag.tag;
241             wrd->length = strlen(n->u.tag.tag);
242         }
243         sp_lex(sp);
244     }
245     else if (sp->len == 4 && !memcmp(sp->tok, "attr", sp->len))
246     {
247         sp_lex(sp);
248         if (sp->lookahead != '(')
249             return 0;
250         sp_lex(sp);
251         if (sp->lookahead != 't')
252             return 0;
253         
254         if (n->which == DATA1N_tag)
255         {
256             data1_xattr *p = n->u.tag.attributes;
257             while (p && strlen(p->name) != sp->len && 
258                    memcmp (p->name, sp->tok, sp->len))
259                 p = p->next;
260             if (p)
261             {
262                 wrd->string = p->value;
263                 wrd->length = strlen(p->value);
264             }
265         }
266         sp_lex(sp);
267         if (sp->lookahead != ')')
268             return 0;
269         sp_lex(sp);
270     }
271     else if (sp->len == 5 && !memcmp(sp->tok, "range", sp->len))
272     {
273         int start, len;
274         sp_lex(sp);
275         if (sp->lookahead != '(')
276             return 0;
277         
278         sp_lex(sp);
279         sp_expr(sp, n, wrd);
280         if (sp->lookahead != ',')
281             return 0;
282         
283         sp_lex(sp);
284         if (sp->lookahead != 't')
285             return 0;
286         start = atoi_n(sp->tok, sp->len);
287         
288         sp_lex(sp);
289         if (sp->lookahead != ',')
290             return 0;
291         
292         sp_lex(sp);
293         if (sp->lookahead != 't')
294             return 0;
295         len = atoi_n(sp->tok, sp->len);
296         
297         sp_lex(sp);
298         if (sp->lookahead != ')')
299             return 0;
300         
301         sp_lex(sp);
302         if (wrd->string && wrd->length)
303         {
304             wrd->string += start;
305             wrd->length -= start;
306             if (wrd->length > len)
307                 wrd->length = len;
308         }
309     }
310     return 1;
311 }
312
313 static int sp_parse(data1_node *n, RecWord *wrd, const char *src)
314 {
315     struct source_parser sp;
316     sp.len = 0;
317     sp.tok = 0;
318     sp.src = src;
319     sp.lookahead = 0;
320     sp_lex(&sp);
321
322     return sp_expr(&sp, n, wrd);
323 }
324
325 int d1_check_xpath_predicate(data1_node *n, struct xpath_predicate *p)
326 {
327     int res = 1;
328     char *attname;
329     data1_xattr *attr;
330     
331     if (!p) {
332         return 1;
333     } else {
334         if (p->which == XPATH_PREDICATE_RELATION) {
335             if (p->u.relation.name[0]) {
336                 if (*p->u.relation.name != '@') {
337                     yaz_log(LOG_WARN, 
338                          "  Only attributes (@) are supported in xelm xpath predicates");
339                     yaz_log(LOG_WARN, "predicate %s ignored", p->u.relation.name);
340                     return (1);
341                 }
342                 attname = p->u.relation.name + 1;
343                 res = 0;
344                 /* looking for the attribute with a specified name */
345                 for (attr = n->u.tag.attributes; attr; attr = attr->next) {
346                     yaz_log(LOG_DEBUG,"  - attribute %s <-> %s", attname, attr->name );
347                     
348                     if (!strcmp(attr->name, attname)) {
349                         if (p->u.relation.op[0]) {
350                             if (*p->u.relation.op != '=') {
351                                 yaz_log(LOG_WARN, 
352                                      "Only '=' relation is supported (%s)",p->u.relation.op);
353                                 yaz_log(LOG_WARN, "predicate %s ignored", p->u.relation.name);
354                                 res = 1; break;
355                             } else {
356                                 yaz_log(LOG_DEBUG,"    - value %s <-> %s", 
357                                      p->u.relation.value, attr->value );
358                                 if (!strcmp(attr->value, p->u.relation.value)) {
359                                     res = 1; break;
360                                 } 
361                             }
362                         } else {
363                             /* attribute exists, no value specified */
364                             res = 1; break;
365                         }
366                     }
367                 }
368                 yaz_log(LOG_DEBUG, "return %d", res);
369                 return res;
370             } else {
371                 return 1;
372             }
373         } 
374         else if (p->which == XPATH_PREDICATE_BOOLEAN) {
375             if (!strcmp(p->u.boolean.op,"and")) {
376                 return d1_check_xpath_predicate(n, p->u.boolean.left) 
377                     && d1_check_xpath_predicate(n, p->u.boolean.right); 
378             }
379             else if (!strcmp(p->u.boolean.op,"or")) {
380                 return (d1_check_xpath_predicate(n, p->u.boolean.left) 
381                         || d1_check_xpath_predicate(n, p->u.boolean.right)); 
382             } else {
383                 yaz_log(LOG_WARN, "Unknown boolean relation %s, ignored",p->u.boolean.op);
384                 return 1;
385             }
386         }
387     }
388     return 0;
389 }
390
391 static int dfa_match_first(struct DFA_state **dfaar, const char *text)
392 {
393     struct DFA_state *s = dfaar[0]; /* start state */
394     struct DFA_tran *t;
395     int i;
396     const char *p = text;
397     unsigned char c;
398     
399     for (c = *p++, t = s->trans, i = s->tran_no; --i >= 0; t++)
400         if (c >= t->ch[0] && c <= t->ch[1])
401         {
402             while (i >= 0)
403             {
404                 /* move to next state and return if we get a match */
405                 s = dfaar[t->to];
406                 if (s->rule_no)
407                     return 1;
408                 /* next char */
409                 if (!c)
410                     return 0;
411                 c = *p++;
412                 for (t = s->trans, i = s->tran_no; --i >= 0; t++)
413                     if (c >= t->ch[0] && c <= t->ch[1])
414                         break;
415             }
416         }
417     return 0;
418 }
419
420
421 /* *ostrich*
422    
423 New function, looking for xpath "element" definitions in abs, by
424 tagpath, using a kind of ugly regxp search.The DFA was built while
425 parsing abs, so here we just go trough them and try to match
426 against the given tagpath. The first matching entry is returned.
427
428 pop, 2002-12-13
429
430 Added support for enhanced xelm. Now [] predicates are considered
431 as well, when selecting indexing rules... (why the hell it's called
432 termlist???)
433
434 pop, 2003-01-17
435
436 */
437
438 data1_termlist *xpath_termlist_by_tagpath(char *tagpath, data1_node *n)
439 {
440     data1_absyn *abs = n->root->u.root.absyn;
441     data1_xpelement *xpe = 0;
442     data1_node *nn;
443 #ifdef ENHANCED_XELM 
444     struct xpath_location_step *xp;
445 #endif
446     char *pexpr = xmalloc(strlen(tagpath)+5);
447
448     sprintf (pexpr, "/%s\n", tagpath);
449 #if 0
450     yaz_log(LOG_DEBUG, "Checking tagpath %s", tagpath);
451 #endif
452
453     for (xpe = abs->xp_elements; xpe; xpe = xpe->next)
454         xpe->match_state = -1; /* don't know if it matches yet */
455
456     for (xpe = abs->xp_elements; xpe; xpe = xpe->next)
457     {
458         int i;
459         int ok = xpe->match_state;
460
461         if (ok == -1)
462         {   /* don't know whether there is a match yet */
463             data1_xpelement *xpe1;
464             
465             assert(xpe->dfa);
466             ok = dfa_match_first(xpe->dfa->states, pexpr);
467 #if OPTIMIZE_MELM
468             /* mark this and following ones with same regexp */
469             for (xpe1 = xpe; xpe1; xpe1 = xpe1->match_next)
470                 xpe1->match_state = ok;
471 #endif
472         }
473         assert (ok == 0 || ok == 1);
474         if (ok) {
475 #ifdef ENHANCED_XELM 
476             /* we have to check the perdicates up to the root node */
477             xp = xpe->xpath;
478             
479             /* find the first tag up in the node structure */
480             nn = n; while (nn && nn->which != DATA1N_tag) {
481                 nn = nn->parent;
482             }
483             
484             /* go from inside out in the node structure, while going
485                backwards trough xpath location steps ... */
486             for (i=xpe->xpath_len - 1; i>0; i--) {
487                 
488                 yaz_log(LOG_DEBUG,"Checking step %d: %s on tag %s",
489                      i,xp[i].part,nn->u.tag.tag);
490                 
491                 if (!d1_check_xpath_predicate(nn, xp[i].predicate)) {
492                     yaz_log(LOG_DEBUG,"  Predicates didn't match");
493                     ok = 0;
494                     break;
495                 }
496                 
497                 if (nn->which == DATA1N_tag) {
498                     nn = nn->parent;
499                 }
500             }
501 #endif
502             if (ok) {
503                 break;
504             }
505         }
506     } 
507     
508     xfree(pexpr);
509     
510     if (xpe) {
511         yaz_log(LOG_DEBUG,"Got it");
512         return xpe->termlists;
513     } else {
514         return NULL;
515     }
516 }
517
518 /* use
519      1   start element (tag)
520      2   end element
521      3   start attr (and attr-exact)
522      4   end attr
523
524   1016   cdata
525   1015   attr data
526
527   *ostrich*
528
529   Now, if there is a matching xelm described in abs, for the
530   indexed element or the attribute,  then the data is handled according 
531   to those definitions...
532
533   modified by pop, 2002-12-13
534 */
535
536 /* add xpath index for an attribute */
537 static void index_xpath_attr (char *tag_path, char *name, char *value,
538                               char *structure, struct recExtractCtrl *p,
539                               RecWord *wrd)
540 {
541     wrd->attrSet = VAL_IDXPATH;
542     wrd->attrUse = 1;
543     wrd->reg_type = '0';
544     wrd->string = tag_path;
545     wrd->length = strlen(tag_path);
546     (*p->tokenAdd)(wrd);
547     
548     if (value) {
549         wrd->attrUse = 1015;
550         wrd->reg_type = 'w';
551         wrd->string = value;
552         wrd->length = strlen(value);
553         (*p->tokenAdd)(wrd);
554     }
555     
556     wrd->attrUse = 2;
557     wrd->reg_type = '0';
558     wrd->string = tag_path;
559     wrd->length = strlen(tag_path);
560     (*p->tokenAdd)(wrd);
561 }
562
563
564
565 static void index_xpath (data1_node *n, struct recExtractCtrl *p,
566                          int level, RecWord *wrd, int use,
567                          struct RecWord_list *wl)
568 {
569     int i;
570     char tag_path_full[1024];
571     size_t flen = 0;
572     data1_node *nn;
573     int termlist_only = 1;
574     data1_termlist *tl;
575     int xpdone = 0;
576
577     yaz_log(LOG_DEBUG, "index_xpath level=%d use=%d", level, use);
578     if ((!n->root->u.root.absyn) ||
579         (n->root->u.root.absyn->enable_xpath_indexing)) {
580         termlist_only = 0;
581     }
582
583     switch (n->which)
584     {
585     case DATA1N_data:
586         wrd->string = n->u.data.data;
587         wrd->length = n->u.data.len;
588         xpdone = 0;
589         flen = 0;
590             
591         /* we have to fetch the whole path to the data tag */
592         for (nn = n; nn; nn = nn->parent) {
593             if (nn->which == DATA1N_tag) {
594                 size_t tlen = strlen(nn->u.tag.tag);
595                 if (tlen + flen > (sizeof(tag_path_full)-2)) return;
596                 memcpy (tag_path_full + flen, nn->u.tag.tag, tlen);
597                 flen += tlen;
598                 tag_path_full[flen++] = '/';
599             }
600             else if (nn->which == DATA1N_root)  break;
601         }
602         
603         tag_path_full[flen] = 0;
604         
605         /* If we have a matching termlist... */
606         if (n->root->u.root.absyn && 
607             (tl = xpath_termlist_by_tagpath(tag_path_full, n)))
608         {
609             int max_seqno = 0;
610
611             for (; tl; tl = tl->next)
612             {
613                 /* need to copy recword because it may be changed */
614                 RecWord wrd_tl;
615                 wrd->reg_type = *tl->structure;
616                 /* this is the ! case, so structure is for the xpath index */
617                 memcpy (&wrd_tl, wrd, sizeof(*wrd));
618                 if (tl->source)
619                     sp_parse(n, &wrd_tl, tl->source);
620                 if (!tl->att) {
621                     wrd_tl.attrSet = VAL_IDXPATH;
622                     wrd_tl.attrUse = use;
623                     if (p->flagShowRecords)
624                     {
625                         int i;
626                         printf("%*sXPath index", (level + 1) * 4, "");
627                         printf (" XData:\"");
628                         for (i = 0; i<wrd_tl.length && i < 40; i++)
629                             fputc (wrd_tl.string[i], stdout);
630                         fputc ('"', stdout);
631                         if (wrd_tl.length > 40)
632                             printf (" ...");
633                         fputc ('\n', stdout);
634                     }
635                     else
636                         (*p->tokenAdd)(&wrd_tl);
637                         
638                     xpdone = 1;
639                 } else {
640                     /* this is just the old fashioned attribute based index */
641                     wrd_tl.attrSet = (int) (tl->att->parent->reference);
642                     wrd_tl.attrUse = tl->att->locals->local;
643                     if (p->flagShowRecords)
644                     {
645                         int i;
646                         printf("%*sIdx: [%s]", (level + 1) * 4, "",
647                                tl->structure);
648                         printf("%s:%s [%d] %s",
649                                tl->att->parent->name,
650                                tl->att->name, tl->att->value,
651                                tl->source);
652                         printf (" XData:\"");
653                         for (i = 0; i<wrd_tl.length && i < 40; i++)
654                             fputc (wrd_tl.string[i], stdout);
655                         fputc ('"', stdout);
656                         if (wrd_tl.length > 40)
657                             printf (" ...");
658                         fputc ('\n', stdout);
659                     }
660                     else
661                         (*p->tokenAdd)(&wrd_tl);
662                 }
663                 if (wrd_tl.seqno > max_seqno)
664                      max_seqno = wrd_tl.seqno;
665             }
666             if (max_seqno)
667                 wrd->seqno = max_seqno;
668         }
669         /* xpath indexing is done, if there was no termlist given, 
670            or no ! in the termlist, and default indexing is enabled... */
671         if (!p->flagShowRecords && !xpdone && !termlist_only)
672         {
673             wrd->attrSet = VAL_IDXPATH;
674             wrd->attrUse = use;
675             wrd->reg_type = 'w';
676             (*p->tokenAdd)(wrd);
677         }
678         break;
679     case DATA1N_tag:
680         flen = 0;
681         for (nn = n; nn; nn = nn->parent)
682         {
683             if (nn->which == DATA1N_tag)
684             {
685                 size_t tlen = strlen(nn->u.tag.tag);
686                 if (tlen + flen > (sizeof(tag_path_full)-2))
687                     return;
688                 memcpy (tag_path_full + flen, nn->u.tag.tag, tlen);
689                 flen += tlen;
690                 tag_path_full[flen++] = '/';
691             }
692             else if (nn->which == DATA1N_root)
693                 break;
694         }
695
696
697         wrd->reg_type = '0';
698         wrd->string = tag_path_full;
699         wrd->length = flen;
700         wrd->attrSet = VAL_IDXPATH;
701         wrd->attrUse = use;
702         if (p->flagShowRecords)
703         {
704             printf("%*s tag=", (level + 1) * 4, "");
705             for (i = 0; i<wrd->length && i < 40; i++)
706                 fputc (wrd->string[i], stdout);
707             if (i == 40)
708                 printf (" ..");
709             printf("\n");
710         }
711         else
712         {
713             data1_xattr *xp;
714             data1_termlist *tl;
715             int do_xpindex;
716             
717             tag_path_full[flen] = 0;
718             
719             /* Add tag start/end xpath index, only when there is a ! in the apropriate xelm
720                directive, or default xpath indexing is enabled */
721             if (!(do_xpindex = 1 - termlist_only)) {
722                 if ((tl = xpath_termlist_by_tagpath(tag_path_full, n))) {
723                     for (; tl; tl = tl->next)
724                     {
725                         if (!tl->att)
726                             do_xpindex = 1;
727                     }
728                 }
729             }
730             if (do_xpindex) {
731                 (*p->tokenAdd)(wrd);   /* index element pag (AKA tag path) */
732             }
733             
734             if (use == 1) /* only for the starting tag... */
735             {
736 #define MAX_ATTR_COUNT 50
737                 data1_termlist *tll[MAX_ATTR_COUNT];
738                 
739                 int i = 0;
740                 
741                 /* get termlists for attributes, and find out, if we have to do xpath indexing */
742                 for (xp = n->u.tag.attributes; xp; xp = xp->next) {
743                     i++;
744                 }
745                 
746                 i = 0;
747                 for (xp = n->u.tag.attributes; xp; xp = xp->next) {
748                     char comb[512];
749                     int do_xpindex = 1 - termlist_only;
750                     data1_termlist *tl;
751                     char attr_tag_path_full[1024]; 
752                     int int_len = flen;
753                     
754                     /* this could be cached as well */
755                     sprintf (attr_tag_path_full, "@%s/%.*s",
756                              xp->name, int_len, tag_path_full);
757                     
758                     tll[i] = xpath_termlist_by_tagpath(attr_tag_path_full,n);
759                     
760                     /* if there is a ! in the xelm termlist, or default indexing is on, 
761                        proceed with xpath idx */
762                     if ((tl = tll[i]))
763                     {
764                         for (; tl; tl = tl->next)
765                         { 
766                             if (!tl->att)
767                                 do_xpindex = 1;
768                         }
769                     }
770                     
771                     if (do_xpindex) {
772                         
773                         /* attribute  (no value) */
774                         wrd->reg_type = '0';
775                         wrd->attrUse = 3;
776                         wrd->string = xp->name;
777                         wrd->length = strlen(xp->name);
778                         
779                         wrd->seqno--;
780                         (*p->tokenAdd)(wrd);
781                         
782                         if (xp->value &&
783                             strlen(xp->name) + strlen(xp->value) < sizeof(comb)-2) {
784                             
785                             /* attribute value exact */
786                             strcpy (comb, xp->name);
787                             strcat (comb, "=");
788                             strcat (comb, xp->value);
789                             
790                             wrd->attrUse = 3;
791                             wrd->reg_type = '0';
792                             wrd->string = comb;
793                             wrd->length = strlen(comb);
794                             wrd->seqno--;
795                             
796                             if (RecWord_list_lookadd(wl, wrd))
797                                 (*p->tokenAdd)(wrd);
798                         }
799                     }                
800                     i++;
801                 }
802                 
803                 i = 0;
804                 for (xp = n->u.tag.attributes; xp; xp = xp->next) {
805                     data1_termlist *tl;
806                     char attr_tag_path_full[1024];
807                     int int_len = flen;
808                     int xpdone = 0;
809                     
810                     sprintf (attr_tag_path_full, "@%s/%.*s",
811                              xp->name, int_len, tag_path_full);
812                     
813                     if ((tl = tll[i]))
814                     {
815                         /* If there is a termlist given (=xelm directive) */
816                         for (; tl; tl = tl->next)
817                         {
818                             if (!tl->att) {
819                                 /* add xpath index for the attribute */
820                                 index_xpath_attr (attr_tag_path_full, xp->name,
821                                                   xp->value, tl->structure,
822                                                   p, wrd);
823                                 xpdone = 1;
824                             } 
825                             else 
826                             {
827                                 /* index attribute value (only path/@attr) */
828                                 if (xp->value)
829                                 {
830                                     wrd->attrSet = (int) 
831                                         (tl->att->parent->reference);
832                                     wrd->attrUse = tl->att->locals->local;
833                                     wrd->reg_type = *tl->structure;
834                                     wrd->string = xp->value;
835                                     wrd->length = strlen(xp->value);
836                                     if (RecWord_list_lookadd(wl, wrd))
837                                         (*p->tokenAdd)(wrd);
838                                 }
839                             }
840                         }
841                     }
842                     /* if there was no termlist for the given path, 
843                        or the termlist didn't have a ! element, index 
844                        the attribute as "w" */
845                     if ((!xpdone) && (!termlist_only))
846                     {
847                         index_xpath_attr (attr_tag_path_full, xp->name,
848                                           xp->value,  "w", p, wrd);
849                     }
850                     i++;
851                 }
852             }
853         }
854     }
855 }
856
857 static void index_termlist (data1_node *par, data1_node *n,
858                             struct recExtractCtrl *p, int level, RecWord *wrd)
859 {
860     data1_termlist *tlist = 0;
861     data1_datatype dtype = DATA1K_string;
862
863     /*
864      * cycle up towards the root until we find a tag with an att..
865      * this has the effect of indexing locally defined tags with
866      * the attribute of their ancestor in the record.
867      */
868     
869     while (!par->u.tag.element)
870         if (!par->parent || !(par=get_parent_tag(p->dh, par->parent)))
871             break;
872     if (!par || !(tlist = par->u.tag.element->termlists))
873         return;
874     if (par->u.tag.element->tag)
875         dtype = par->u.tag.element->tag->kind;
876     
877     for (; tlist; tlist = tlist->next)
878     {
879         /* consider source */
880         wrd->string = 0;
881         assert(tlist->source);
882         sp_parse(n, wrd, tlist->source);
883
884         if (wrd->string)
885         {
886             if (p->flagShowRecords)
887             {
888                 int i;
889                 printf("%*sIdx: [%s]", (level + 1) * 4, "",
890                        tlist->structure);
891                 printf("%s:%s [%d] %s",
892                        tlist->att->parent->name,
893                        tlist->att->name, tlist->att->value,
894                        tlist->source);
895                 printf (" XData:\"");
896                 for (i = 0; i<wrd->length && i < 40; i++)
897                     fputc (wrd->string[i], stdout);
898                 fputc ('"', stdout);
899                 if (wrd->length > 40)
900                     printf (" ...");
901                 fputc ('\n', stdout);
902             }
903             else
904             {
905                 wrd->reg_type = *tlist->structure;
906                 wrd->attrSet = (int) (tlist->att->parent->reference);
907                 wrd->attrUse = tlist->att->locals->local;
908                 (*p->tokenAdd)(wrd);
909             }
910         }
911     }
912 }
913
914 static int dumpkeys(data1_node *n, struct recExtractCtrl *p, int level,
915                     RecWord *wrd, struct RecWord_list *wl)
916 {
917     for (; n; n = n->next)
918     {
919         if (p->flagShowRecords) /* display element description to user */
920         {
921             if (n->which == DATA1N_root)
922             {
923                 printf("%*s", level * 4, "");
924                 printf("Record type: '%s'\n", n->u.root.type);
925             }
926             else if (n->which == DATA1N_tag)
927             {
928                 data1_element *e;
929
930                 printf("%*s", level * 4, "");
931                 if (!(e = n->u.tag.element))
932                     printf("Local tag: '%s'\n", n->u.tag.tag);
933                 else
934                 {
935                     printf("Elm: '%s' ", e->name);
936                     if (e->tag)
937                     {
938                         data1_tag *t = e->tag;
939
940                         printf("TagNam: '%s' ", t->names->name);
941                         printf("(");
942                         if (t->tagset)
943                             printf("%s[%d],", t->tagset->name, t->tagset->type);
944                         else
945                             printf("?,");
946                         if (t->which == DATA1T_numeric)
947                             printf("%d)", t->value.numeric);
948                         else
949                             printf("'%s')", t->value.string);
950                     }
951                     printf("\n");
952                 }
953             }
954         }
955
956         if (n->which == DATA1N_tag)
957         {
958             index_termlist (n, n, p, level, wrd);
959             /* index start tag */
960             if (n->root->u.root.absyn)
961                 index_xpath (n, p, level, wrd, 1, wl);
962         }
963
964         if (n->child)
965             if (dumpkeys(n->child, p, level + 1, wrd, wl) < 0)
966                 return -1;
967
968
969         if (n->which == DATA1N_data)
970         {
971             data1_node *par = get_parent_tag(p->dh, n);
972
973             if (p->flagShowRecords)
974             {
975                 printf("%*s", level * 4, "");
976                 printf("Data: ");
977                 if (n->u.data.len > 256)
978                     printf("'%.170s ... %.70s'\n", n->u.data.data,
979                            n->u.data.data + n->u.data.len-70);
980                 else if (n->u.data.len > 0)
981                     printf("'%.*s'\n", n->u.data.len, n->u.data.data);
982                 else
983                     printf("NULL\n");
984             }
985
986             if (par)
987                 index_termlist (par, n, p, level, wrd);
988
989             index_xpath (n, p, level, wrd, 1016, wl);
990         }
991
992         if (n->which == DATA1N_tag)
993         {
994             /* index end tag */
995             index_xpath (n, p, level, wrd, 2, wl);
996         }
997
998         if (p->flagShowRecords && n->which == DATA1N_root)
999         {
1000             printf("%*s-------------\n\n", level * 4, "");
1001         }
1002     }
1003     return 0;
1004 }
1005
1006 int grs_extract_tree(struct recExtractCtrl *p, data1_node *n)
1007 {
1008     oident oe;
1009     int oidtmp[OID_SIZE];
1010     RecWord wrd;
1011     int r;
1012     struct RecWord_list *wl = 0;
1013
1014     oe.proto = PROTO_Z3950;
1015     oe.oclass = CLASS_SCHEMA;
1016     if (n->u.root.absyn)
1017     {
1018         oe.value = n->u.root.absyn->reference;
1019         
1020         if ((oid_ent_to_oid (&oe, oidtmp)))
1021             (*p->schemaAdd)(p, oidtmp);
1022     }
1023     (*p->init)(p, &wrd);
1024
1025     wl = RecWord_list_create("grs_extract_tree");
1026     r = dumpkeys(n, p, 0, &wrd, wl);
1027     RecWord_list_destroy(wl);
1028     return r;
1029 }
1030
1031 static int grs_extract_sub(struct grs_handlers *h, struct recExtractCtrl *p,
1032                            NMEM mem)
1033 {
1034     data1_node *n;
1035     struct grs_read_info gri;
1036     oident oe;
1037     int oidtmp[OID_SIZE];
1038     RecWord wrd;
1039     struct RecWord_list *wl = 0;
1040     int ret_val;
1041
1042     gri.readf = p->readf;
1043     gri.seekf = p->seekf;
1044     gri.tellf = p->tellf;
1045     gri.endf = p->endf;
1046     gri.fh = p->fh;
1047     gri.offset = p->offset;
1048     gri.mem = mem;
1049     gri.dh = p->dh;
1050
1051     if (read_grs_type (h, &gri, p->subType, &n))
1052         return RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER;
1053     if (!n)
1054         return RECCTRL_EXTRACT_EOF;
1055     oe.proto = PROTO_Z3950;
1056     oe.oclass = CLASS_SCHEMA;
1057 #if 0
1058     if (!n->u.root.absyn)
1059         return RECCTRL_EXTRACT_ERROR;
1060 #endif
1061     if (n->u.root.absyn)
1062     {
1063         oe.value = n->u.root.absyn->reference;
1064         if ((oid_ent_to_oid (&oe, oidtmp)))
1065             (*p->schemaAdd)(p, oidtmp);
1066     }
1067     data1_concat_text(p->dh, mem, n);
1068
1069     /* ensure our data1 tree is UTF-8 */
1070     data1_iconv (p->dh, mem, n, "UTF-8", data1_get_encoding(p->dh, n));
1071
1072 #if 0
1073     data1_pr_tree (p->dh, n, stdout);
1074 #endif
1075
1076     wl = RecWord_list_create("grs.sgml");
1077
1078     (*p->init)(p, &wrd);
1079     if (dumpkeys(n, p, 0, &wrd, wl) < 0)
1080         ret_val = RECCTRL_EXTRACT_ERROR_GENERIC;
1081     else
1082         ret_val = RECCTRL_EXTRACT_OK;
1083     data1_free_tree(p->dh, n);
1084     RecWord_list_destroy(wl);
1085
1086     return ret_val;
1087 }
1088
1089 static int grs_extract(void *clientData, struct recExtractCtrl *p)
1090 {
1091     int ret;
1092     NMEM mem = nmem_create ();
1093     struct grs_handlers *h = (struct grs_handlers *) clientData;
1094
1095     ret = grs_extract_sub(h, p, mem);
1096     nmem_destroy(mem);
1097     return ret;
1098 }
1099
1100 /*
1101  * Return: -1: Nothing done. 0: Ok. >0: Bib-1 diagnostic.
1102  */
1103 static int process_comp(data1_handle dh, data1_node *n, Z_RecordComposition *c)
1104 {
1105     data1_esetname *eset;
1106     Z_Espec1 *espec = 0;
1107     Z_ElementSpec *p;
1108
1109     switch (c->which)
1110     {
1111     case Z_RecordComp_simple:
1112         if (c->u.simple->which != Z_ElementSetNames_generic)
1113             return 26; /* only generic form supported. Fix this later */
1114         if (!(eset = data1_getesetbyname(dh, n->u.root.absyn,
1115                                          c->u.simple->u.generic)))
1116         {
1117             yaz_log(LOG_LOG, "Unknown esetname '%s'", c->u.simple->u.generic);
1118             return 25; /* invalid esetname */
1119         }
1120         yaz_log(LOG_DEBUG, "Esetname '%s' in simple compspec",
1121              c->u.simple->u.generic);
1122         espec = eset->spec;
1123         break;
1124     case Z_RecordComp_complex:
1125         if (c->u.complex->generic)
1126         {
1127             /* insert check for schema */
1128             if ((p = c->u.complex->generic->elementSpec))
1129             {
1130                 switch (p->which)
1131                 {
1132                 case Z_ElementSpec_elementSetName:
1133                     if (!(eset =
1134                           data1_getesetbyname(dh, n->u.root.absyn,
1135                                               p->u.elementSetName)))
1136                     {
1137                         yaz_log(LOG_LOG, "Unknown esetname '%s'",
1138                              p->u.elementSetName);
1139                         return 25; /* invalid esetname */
1140                     }
1141                     yaz_log(LOG_DEBUG, "Esetname '%s' in complex compspec",
1142                          p->u.elementSetName);
1143                     espec = eset->spec;
1144                     break;
1145                 case Z_ElementSpec_externalSpec:
1146                     if (p->u.externalSpec->which == Z_External_espec1)
1147                     {
1148                         yaz_log(LOG_DEBUG, "Got Espec-1");
1149                         espec = p->u.externalSpec-> u.espec1;
1150                     }
1151                     else
1152                     {
1153                         yaz_log(LOG_LOG, "Unknown external espec.");
1154                         return 25; /* bad. what is proper diagnostic? */
1155                     }
1156                     break;
1157                 }
1158             }
1159         }
1160         else
1161             return 26; /* fix */
1162     }
1163     if (espec)
1164     {
1165         yaz_log(LOG_DEBUG, "Element: Espec-1 match");
1166         return data1_doespec1(dh, n, espec);
1167     }
1168     else
1169     {
1170         yaz_log(LOG_DEBUG, "Element: all match");
1171         return -1;
1172     }
1173 }
1174
1175 /* Add Zebra info in separate namespace ...
1176         <root 
1177          ...
1178          <metadata xmlns="http://www.indexdata.dk/zebra/">
1179           <size>359</size>
1180           <localnumber>447</localnumber>
1181           <filename>records/genera.xml</filename>
1182          </metadata>
1183         </root>
1184 */
1185
1186 static void zebra_xml_metadata (struct recRetrieveCtrl *p, data1_node *top,
1187                                 NMEM mem)
1188 {
1189     const char *idzebra_ns[3];
1190     const char *i2 = "\n  ";
1191     const char *i4 = "\n    ";
1192     data1_node *n;
1193
1194     idzebra_ns[0] = "xmlns";
1195     idzebra_ns[1] = "http://www.indexdata.dk/zebra/";
1196     idzebra_ns[2] = 0;
1197
1198     data1_mk_text (p->dh, mem, i2, top);
1199
1200     n = data1_mk_tag (p->dh, mem, "idzebra", idzebra_ns, top);
1201
1202     data1_mk_text (p->dh, mem, "\n", top);
1203
1204     data1_mk_text (p->dh, mem, i4, n);
1205     
1206     data1_mk_tag_data_int (p->dh, n, "size", p->recordSize, mem);
1207
1208     if (p->score != -1)
1209     {
1210         data1_mk_text (p->dh, mem, i4, n);
1211         data1_mk_tag_data_int (p->dh, n, "score", p->score, mem);
1212     }
1213     data1_mk_text (p->dh, mem, i4, n);
1214     data1_mk_tag_data_int (p->dh, n, "localnumber", p->localno, mem);
1215     if (p->fname)
1216     {
1217         data1_mk_text (p->dh, mem, i4, n);
1218         data1_mk_tag_data_text(p->dh, n, "filename", p->fname, mem);
1219     }
1220     data1_mk_text (p->dh, mem, i2, n);
1221 }
1222
1223 static int grs_retrieve(void *clientData, struct recRetrieveCtrl *p)
1224 {
1225     data1_node *node = 0, *onode = 0, *top;
1226     data1_node *dnew;
1227     data1_maptab *map;
1228     int res, selected = 0;
1229     NMEM mem;
1230     struct grs_read_info gri;
1231     const char *tagname;
1232     struct grs_handlers *h = (struct grs_handlers *) clientData;
1233     int requested_schema = VAL_NONE;
1234     data1_marctab *marctab;
1235     int dummy;
1236     
1237     mem = nmem_create();
1238     gri.readf = p->readf;
1239     gri.seekf = p->seekf;
1240     gri.tellf = p->tellf;
1241     gri.endf = NULL;
1242     gri.fh = p->fh;
1243     gri.offset = 0;
1244     gri.mem = mem;
1245     gri.dh = p->dh;
1246
1247     yaz_log(LOG_DEBUG, "grs_retrieve");
1248     if (read_grs_type (h, &gri, p->subType, &node))
1249     {
1250         p->diagnostic = 14;
1251         nmem_destroy (mem);
1252         return 0;
1253     }
1254     if (!node)
1255     {
1256         p->diagnostic = 14;
1257         nmem_destroy (mem);
1258         return 0;
1259     }
1260     data1_concat_text(p->dh, mem, node);
1261
1262     /* ensure our data1 tree is UTF-8 */
1263     data1_iconv (p->dh, mem, node, "UTF-8", data1_get_encoding(p->dh, node));
1264
1265 #if 0
1266     data1_pr_tree (p->dh, node, stdout);
1267 #endif
1268     top = data1_get_root_tag (p->dh, node);
1269
1270     yaz_log(LOG_DEBUG, "grs_retrieve: size");
1271     tagname = data1_systag_lookup(node->u.root.absyn, "size", "size");
1272     if (tagname &&
1273         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1274     {
1275         dnew->u.data.what = DATA1I_text;
1276         dnew->u.data.data = dnew->lbuf;
1277         sprintf(dnew->u.data.data, "%d", p->recordSize);
1278         dnew->u.data.len = strlen(dnew->u.data.data);
1279     }
1280     
1281     tagname = data1_systag_lookup(node->u.root.absyn, "rank", "rank");
1282
1283     if (tagname && p->score >= 0 &&
1284         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1285     {
1286         yaz_log(LOG_DEBUG, "grs_retrieve: %s", tagname);
1287         dnew->u.data.what = DATA1I_num;
1288         dnew->u.data.data = dnew->lbuf;
1289         sprintf(dnew->u.data.data, "%d", p->score);
1290         dnew->u.data.len = strlen(dnew->u.data.data);
1291     }
1292
1293     tagname = data1_systag_lookup(node->u.root.absyn, "sysno",
1294                                   "localControlNumber");
1295     if (tagname && p->localno > 0 &&
1296         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1297     {
1298         yaz_log(LOG_DEBUG, "grs_retrieve: %s", tagname);
1299         dnew->u.data.what = DATA1I_text;
1300         dnew->u.data.data = dnew->lbuf;
1301         
1302         sprintf(dnew->u.data.data, "%d", p->localno);
1303         dnew->u.data.len = strlen(dnew->u.data.data);
1304     }
1305
1306     if (p->input_format == VAL_TEXT_XML)
1307         zebra_xml_metadata (p, top, mem);
1308
1309 #if 0
1310     data1_pr_tree (p->dh, node, stdout);
1311 #endif
1312 #if YAZ_VERSIONL >= 0x010903L
1313     if (p->comp && p->comp->which == Z_RecordComp_complex &&
1314         p->comp->u.complex->generic &&
1315         p->comp->u.complex->generic->which == Z_Schema_oid &&
1316         p->comp->u.complex->generic->schema.oid)
1317     {
1318         oident *oe = oid_getentbyoid (p->comp->u.complex->generic->schema.oid);
1319         if (oe)
1320             requested_schema = oe->value;
1321     }
1322 #else
1323     if (p->comp && p->comp->which == Z_RecordComp_complex &&
1324         p->comp->u.complex->generic && p->comp->u.complex->generic->schema)
1325     {
1326         oident *oe = oid_getentbyoid (p->comp->u.complex->generic->schema);
1327         if (oe)
1328             requested_schema = oe->value;
1329     }
1330 #endif
1331
1332     /* If schema has been specified, map if possible, then check that
1333      * we got the right one 
1334      */
1335     if (requested_schema != VAL_NONE)
1336     {
1337         yaz_log(LOG_DEBUG, "grs_retrieve: schema mapping");
1338         for (map = node->u.root.absyn->maptabs; map; map = map->next)
1339         {
1340             if (map->target_absyn_ref == requested_schema)
1341             {
1342                 onode = node;
1343                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
1344                 {
1345                     p->diagnostic = 14;
1346                     nmem_destroy (mem);
1347                     return 0;
1348                 }
1349                 break;
1350             }
1351         }
1352         if (node->u.root.absyn &&
1353             requested_schema != node->u.root.absyn->reference)
1354         {
1355             p->diagnostic = 238;
1356             nmem_destroy (mem);
1357             return 0;
1358         }
1359     }
1360     /*
1361      * Does the requested format match a known syntax-mapping? (this reflects
1362      * the overlap of schema and formatting which is inherent in the MARC
1363      * family)
1364      */
1365     yaz_log(LOG_DEBUG, "grs_retrieve: syntax mapping");
1366     if (node->u.root.absyn)
1367         for (map = node->u.root.absyn->maptabs; map; map = map->next)
1368         {
1369             if (map->target_absyn_ref == p->input_format)
1370             {
1371                 onode = node;
1372                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
1373                 {
1374                     p->diagnostic = 14;
1375                     nmem_destroy (mem);
1376                     return 0;
1377                 }
1378                 break;
1379             }
1380         }
1381     yaz_log(LOG_DEBUG, "grs_retrieve: schemaIdentifier");
1382     if (node->u.root.absyn &&
1383         node->u.root.absyn->reference != VAL_NONE &&
1384         p->input_format == VAL_GRS1)
1385     {
1386         oident oe;
1387         Odr_oid *oid;
1388         int oidtmp[OID_SIZE];
1389         
1390         oe.proto = PROTO_Z3950;
1391         oe.oclass = CLASS_SCHEMA;
1392         oe.value = node->u.root.absyn->reference;
1393         
1394         if ((oid = oid_ent_to_oid (&oe, oidtmp)))
1395         {
1396             char tmp[128];
1397             data1_handle dh = p->dh;
1398             char *p = tmp;
1399             int *ii;
1400             
1401             for (ii = oid; *ii >= 0; ii++)
1402             {
1403                 if (p != tmp)
1404                         *(p++) = '.';
1405                 sprintf(p, "%d", *ii);
1406                 p += strlen(p);
1407             }
1408             if ((dnew = data1_mk_tag_data_wd(dh, top, 
1409                                              "schemaIdentifier", mem)))
1410             {
1411                 dnew->u.data.what = DATA1I_oid;
1412                 dnew->u.data.data = (char *) nmem_malloc(mem, p - tmp);
1413                 memcpy(dnew->u.data.data, tmp, p - tmp);
1414                 dnew->u.data.len = p - tmp;
1415             }
1416         }
1417     }
1418
1419     yaz_log(LOG_DEBUG, "grs_retrieve: element spec");
1420     if (p->comp && (res = process_comp(p->dh, node, p->comp)) > 0)
1421     {
1422         p->diagnostic = res;
1423         if (onode)
1424             data1_free_tree(p->dh, onode);
1425         data1_free_tree(p->dh, node);
1426         nmem_destroy(mem);
1427         return 0;
1428     }
1429     else if (p->comp && !res)
1430         selected = 1;
1431
1432 #if 0
1433     data1_pr_tree (p->dh, node, stdout);
1434 #endif
1435     yaz_log(LOG_DEBUG, "grs_retrieve: transfer syntax mapping");
1436     switch (p->output_format = (p->input_format != VAL_NONE ?
1437                                 p->input_format : VAL_SUTRS))
1438     {
1439     case VAL_TEXT_XML:
1440
1441 #if 0
1442         data1_pr_tree (p->dh, node, stdout);
1443 #endif
1444
1445         if (p->encoding)
1446             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
1447
1448         if (!(p->rec_buf = data1_nodetoidsgml(p->dh, node, selected,
1449                                               &p->rec_len)))
1450             p->diagnostic = 238;
1451         else
1452         {
1453             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1454             memcpy (new_buf, p->rec_buf, p->rec_len);
1455             p->rec_buf = new_buf;
1456         }
1457         break;
1458     case VAL_GRS1:
1459         dummy = 0;
1460         if (!(p->rec_buf = data1_nodetogr(p->dh, node, selected,
1461                                           p->odr, &dummy)))
1462             p->diagnostic = 238; /* not available in requested syntax */
1463         else
1464             p->rec_len = -1;
1465         break;
1466     case VAL_EXPLAIN:
1467         if (!(p->rec_buf = data1_nodetoexplain(p->dh, node, selected,
1468                                                p->odr)))
1469             p->diagnostic = 238;
1470         else
1471             p->rec_len = -1;
1472         break;
1473     case VAL_SUMMARY:
1474         if (!(p->rec_buf = data1_nodetosummary(p->dh, node, selected,
1475                                                p->odr)))
1476             p->diagnostic = 238;
1477         else
1478             p->rec_len = -1;
1479         break;
1480     case VAL_SUTRS:
1481         if (p->encoding)
1482             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
1483         if (!(p->rec_buf = data1_nodetobuf(p->dh, node, selected,
1484                                            &p->rec_len)))
1485             p->diagnostic = 238;
1486         else
1487         {
1488             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1489             memcpy (new_buf, p->rec_buf, p->rec_len);
1490             p->rec_buf = new_buf;
1491         }
1492         break;
1493     case VAL_SOIF:
1494         if (!(p->rec_buf = data1_nodetosoif(p->dh, node, selected,
1495                                             &p->rec_len)))
1496             p->diagnostic = 238;
1497         else
1498         {
1499             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1500             memcpy (new_buf, p->rec_buf, p->rec_len);
1501             p->rec_buf = new_buf;
1502         }
1503         break;
1504     default:
1505         if (!node->u.root.absyn)
1506         {
1507             p->diagnostic = 238;
1508             break;
1509         }
1510         for (marctab = node->u.root.absyn->marc; marctab;
1511              marctab = marctab->next)
1512             if (marctab->reference == p->input_format)
1513                 break;
1514         if (!marctab)
1515         {
1516             p->diagnostic = 238;
1517             break;
1518         }
1519         if (p->encoding)
1520             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
1521         if (!(p->rec_buf = data1_nodetomarc(p->dh, marctab, node,
1522                                         selected, &p->rec_len)))
1523             p->diagnostic = 238;
1524         else
1525         {
1526             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1527             memcpy (new_buf, p->rec_buf, p->rec_len);
1528                 p->rec_buf = new_buf;
1529         }
1530     }
1531     if (node)
1532         data1_free_tree(p->dh, node);
1533     if (onode)
1534         data1_free_tree(p->dh, onode);
1535     nmem_destroy(mem);
1536     return 0;
1537 }
1538
1539 static struct recType grs_type =
1540 {
1541     "grs",
1542     grs_init,
1543     grs_destroy,
1544     grs_extract,
1545     grs_retrieve
1546 };
1547
1548 RecType recTypeGrs = &grs_type;