More optimizations of the melm matching.
[idzebra-moved-to-github.git] / recctrl / recgrs.c
1 /* $Id: recgrs.c,v 1.86.2.12 2006-09-29 10:02:44 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             for (; tl; tl = tl->next)
610             {
611                 /* need to copy recword because it may be changed */
612                 RecWord wrd_tl;
613                 wrd->reg_type = *tl->structure;
614                 /* this is the ! case, so structure is for the xpath index */
615                 memcpy (&wrd_tl, wrd, sizeof(*wrd));
616                 if (tl->source)
617                     sp_parse(n, &wrd_tl, tl->source);
618                 if (!tl->att) {
619                     wrd_tl.attrSet = VAL_IDXPATH;
620                     wrd_tl.attrUse = use;
621                     if (p->flagShowRecords)
622                     {
623                         int i;
624                         printf("%*sXPath index", (level + 1) * 4, "");
625                         printf (" XData:\"");
626                         for (i = 0; i<wrd_tl.length && i < 40; i++)
627                             fputc (wrd_tl.string[i], stdout);
628                         fputc ('"', stdout);
629                         if (wrd_tl.length > 40)
630                             printf (" ...");
631                         fputc ('\n', stdout);
632                     }
633                     else
634                         (*p->tokenAdd)(&wrd_tl);
635                         
636                     xpdone = 1;
637                 } else {
638                     /* this is just the old fashioned attribute based index */
639                     wrd_tl.attrSet = (int) (tl->att->parent->reference);
640                     wrd_tl.attrUse = tl->att->locals->local;
641                     if (p->flagShowRecords)
642                     {
643                         int i;
644                         printf("%*sIdx: [%s]", (level + 1) * 4, "",
645                                tl->structure);
646                         printf("%s:%s [%d] %s",
647                                tl->att->parent->name,
648                                tl->att->name, tl->att->value,
649                                tl->source);
650                         printf (" XData:\"");
651                         for (i = 0; i<wrd_tl.length && i < 40; i++)
652                             fputc (wrd_tl.string[i], stdout);
653                         fputc ('"', stdout);
654                         if (wrd_tl.length > 40)
655                             printf (" ...");
656                         fputc ('\n', stdout);
657                     }
658                     else
659                         (*p->tokenAdd)(&wrd_tl);
660                 }
661             }
662         }
663         /* xpath indexing is done, if there was no termlist given, 
664            or no ! in the termlist, and default indexing is enabled... */
665         if (!p->flagShowRecords && !xpdone && !termlist_only)
666         {
667             wrd->attrSet = VAL_IDXPATH;
668             wrd->attrUse = use;
669             wrd->reg_type = 'w';
670             (*p->tokenAdd)(wrd);
671         }
672         else
673             wrd->seqno++;
674         break;
675     case DATA1N_tag:
676         flen = 0;
677         for (nn = n; nn; nn = nn->parent)
678         {
679             if (nn->which == DATA1N_tag)
680             {
681                 size_t tlen = strlen(nn->u.tag.tag);
682                 if (tlen + flen > (sizeof(tag_path_full)-2))
683                     return;
684                 memcpy (tag_path_full + flen, nn->u.tag.tag, tlen);
685                 flen += tlen;
686                 tag_path_full[flen++] = '/';
687             }
688             else if (nn->which == DATA1N_root)
689                 break;
690         }
691
692
693         wrd->reg_type = '0';
694         wrd->string = tag_path_full;
695         wrd->length = flen;
696         wrd->attrSet = VAL_IDXPATH;
697         wrd->attrUse = use;
698         if (p->flagShowRecords)
699         {
700             printf("%*s tag=", (level + 1) * 4, "");
701             for (i = 0; i<wrd->length && i < 40; i++)
702                 fputc (wrd->string[i], stdout);
703             if (i == 40)
704                 printf (" ..");
705             printf("\n");
706         }
707         else
708         {
709             data1_xattr *xp;
710             data1_termlist *tl;
711             int do_xpindex;
712             
713             tag_path_full[flen] = 0;
714             
715             /* Add tag start/end xpath index, only when there is a ! in the apropriate xelm
716                directive, or default xpath indexing is enabled */
717             if (!(do_xpindex = 1 - termlist_only)) {
718                 if ((tl = xpath_termlist_by_tagpath(tag_path_full, n))) {
719                     for (; tl; tl = tl->next)
720                     {
721                         if (!tl->att)
722                             do_xpindex = 1;
723                     }
724                 }
725             }
726             if (do_xpindex) {
727                 (*p->tokenAdd)(wrd);   /* index element pag (AKA tag path) */
728             }
729             
730             if (use == 1) /* only for the starting tag... */
731             {
732 #define MAX_ATTR_COUNT 50
733                 data1_termlist *tll[MAX_ATTR_COUNT];
734                 
735                 int i = 0;
736                 
737                 /* get termlists for attributes, and find out, if we have to do xpath indexing */
738                 for (xp = n->u.tag.attributes; xp; xp = xp->next) {
739                     i++;
740                 }
741                 
742                 i = 0;
743                 for (xp = n->u.tag.attributes; xp; xp = xp->next) {
744                     char comb[512];
745                     int do_xpindex = 1 - termlist_only;
746                     data1_termlist *tl;
747                     char attr_tag_path_full[1024]; 
748                     int int_len = flen;
749                     
750                     /* this could be cached as well */
751                     sprintf (attr_tag_path_full, "@%s/%.*s",
752                              xp->name, int_len, tag_path_full);
753                     
754                     tll[i] = xpath_termlist_by_tagpath(attr_tag_path_full,n);
755                     
756                     /* if there is a ! in the xelm termlist, or default indexing is on, 
757                        proceed with xpath idx */
758                     if ((tl = tll[i]))
759                     {
760                         for (; tl; tl = tl->next)
761                         { 
762                             if (!tl->att)
763                                 do_xpindex = 1;
764                         }
765                     }
766                     
767                     if (do_xpindex) {
768                         
769                         /* attribute  (no value) */
770                         wrd->reg_type = '0';
771                         wrd->attrUse = 3;
772                         wrd->string = xp->name;
773                         wrd->length = strlen(xp->name);
774                         
775                         wrd->seqno--;
776                         (*p->tokenAdd)(wrd);
777                         
778                         if (xp->value &&
779                             strlen(xp->name) + strlen(xp->value) < sizeof(comb)-2) {
780                             
781                             /* attribute value exact */
782                             strcpy (comb, xp->name);
783                             strcat (comb, "=");
784                             strcat (comb, xp->value);
785                             
786                             wrd->attrUse = 3;
787                             wrd->reg_type = '0';
788                             wrd->string = comb;
789                             wrd->length = strlen(comb);
790                             wrd->seqno--;
791                             
792                             if (RecWord_list_lookadd(wl, wrd))
793                                 (*p->tokenAdd)(wrd);
794                         }
795                     }                
796                     i++;
797                 }
798                 
799                 i = 0;
800                 for (xp = n->u.tag.attributes; xp; xp = xp->next) {
801                     data1_termlist *tl;
802                     char attr_tag_path_full[1024];
803                     int int_len = flen;
804                     int xpdone = 0;
805                     
806                     sprintf (attr_tag_path_full, "@%s/%.*s",
807                              xp->name, int_len, tag_path_full);
808                     
809                     if ((tl = tll[i]))
810                     {
811                         /* If there is a termlist given (=xelm directive) */
812                         for (; tl; tl = tl->next)
813                         {
814                             if (!tl->att) {
815                                 /* add xpath index for the attribute */
816                                 index_xpath_attr (attr_tag_path_full, xp->name,
817                                                   xp->value, tl->structure,
818                                                   p, wrd);
819                                 xpdone = 1;
820                             } 
821                             else 
822                             {
823                                 /* index attribute value (only path/@attr) */
824                                 if (xp->value)
825                                 {
826                                     wrd->attrSet = (int) 
827                                         (tl->att->parent->reference);
828                                     wrd->attrUse = tl->att->locals->local;
829                                     wrd->reg_type = *tl->structure;
830                                     wrd->string = xp->value;
831                                     wrd->length = strlen(xp->value);
832                                     if (RecWord_list_lookadd(wl, wrd))
833                                         (*p->tokenAdd)(wrd);
834                                 }
835                             }
836                         }
837                     }
838                     /* if there was no termlist for the given path, 
839                        or the termlist didn't have a ! element, index 
840                        the attribute as "w" */
841                     if ((!xpdone) && (!termlist_only))
842                     {
843                         index_xpath_attr (attr_tag_path_full, xp->name,
844                                           xp->value,  "w", p, wrd);
845                     }
846                     i++;
847                 }
848             }
849         }
850     }
851 }
852
853 static void index_termlist (data1_node *par, data1_node *n,
854                             struct recExtractCtrl *p, int level, RecWord *wrd)
855 {
856     data1_termlist *tlist = 0;
857     data1_datatype dtype = DATA1K_string;
858
859     /*
860      * cycle up towards the root until we find a tag with an att..
861      * this has the effect of indexing locally defined tags with
862      * the attribute of their ancestor in the record.
863      */
864     
865     while (!par->u.tag.element)
866         if (!par->parent || !(par=get_parent_tag(p->dh, par->parent)))
867             break;
868     if (!par || !(tlist = par->u.tag.element->termlists))
869         return;
870     if (par->u.tag.element->tag)
871         dtype = par->u.tag.element->tag->kind;
872     
873     for (; tlist; tlist = tlist->next)
874     {
875         /* consider source */
876         wrd->string = 0;
877         assert(tlist->source);
878         sp_parse(n, wrd, tlist->source);
879
880         if (wrd->string)
881         {
882             if (p->flagShowRecords)
883             {
884                 int i;
885                 printf("%*sIdx: [%s]", (level + 1) * 4, "",
886                        tlist->structure);
887                 printf("%s:%s [%d] %s",
888                        tlist->att->parent->name,
889                        tlist->att->name, tlist->att->value,
890                        tlist->source);
891                 printf (" XData:\"");
892                 for (i = 0; i<wrd->length && i < 40; i++)
893                     fputc (wrd->string[i], stdout);
894                 fputc ('"', stdout);
895                 if (wrd->length > 40)
896                     printf (" ...");
897                 fputc ('\n', stdout);
898             }
899             else
900             {
901                 wrd->reg_type = *tlist->structure;
902                 wrd->attrSet = (int) (tlist->att->parent->reference);
903                 wrd->attrUse = tlist->att->locals->local;
904                 (*p->tokenAdd)(wrd);
905             }
906         }
907     }
908 }
909
910 static int dumpkeys(data1_node *n, struct recExtractCtrl *p, int level,
911                     RecWord *wrd, struct RecWord_list *wl)
912 {
913     for (; n; n = n->next)
914     {
915         if (p->flagShowRecords) /* display element description to user */
916         {
917             if (n->which == DATA1N_root)
918             {
919                 printf("%*s", level * 4, "");
920                 printf("Record type: '%s'\n", n->u.root.type);
921             }
922             else if (n->which == DATA1N_tag)
923             {
924                 data1_element *e;
925
926                 printf("%*s", level * 4, "");
927                 if (!(e = n->u.tag.element))
928                     printf("Local tag: '%s'\n", n->u.tag.tag);
929                 else
930                 {
931                     printf("Elm: '%s' ", e->name);
932                     if (e->tag)
933                     {
934                         data1_tag *t = e->tag;
935
936                         printf("TagNam: '%s' ", t->names->name);
937                         printf("(");
938                         if (t->tagset)
939                             printf("%s[%d],", t->tagset->name, t->tagset->type);
940                         else
941                             printf("?,");
942                         if (t->which == DATA1T_numeric)
943                             printf("%d)", t->value.numeric);
944                         else
945                             printf("'%s')", t->value.string);
946                     }
947                     printf("\n");
948                 }
949             }
950         }
951
952         if (n->which == DATA1N_tag)
953         {
954             index_termlist (n, n, p, level, wrd);
955             /* index start tag */
956             if (n->root->u.root.absyn)
957                 index_xpath (n, p, level, wrd, 1, wl);
958         }
959
960         if (n->child)
961             if (dumpkeys(n->child, p, level + 1, wrd, wl) < 0)
962                 return -1;
963
964
965         if (n->which == DATA1N_data)
966         {
967             data1_node *par = get_parent_tag(p->dh, n);
968
969             if (p->flagShowRecords)
970             {
971                 printf("%*s", level * 4, "");
972                 printf("Data: ");
973                 if (n->u.data.len > 256)
974                     printf("'%.170s ... %.70s'\n", n->u.data.data,
975                            n->u.data.data + n->u.data.len-70);
976                 else if (n->u.data.len > 0)
977                     printf("'%.*s'\n", n->u.data.len, n->u.data.data);
978                 else
979                     printf("NULL\n");
980             }
981
982             if (par)
983                 index_termlist (par, n, p, level, wrd);
984
985             index_xpath (n, p, level, wrd, 1016, wl);
986         }
987
988         if (n->which == DATA1N_tag)
989         {
990             /* index end tag */
991             index_xpath (n, p, level, wrd, 2, wl);
992         }
993
994         if (p->flagShowRecords && n->which == DATA1N_root)
995         {
996             printf("%*s-------------\n\n", level * 4, "");
997         }
998     }
999     return 0;
1000 }
1001
1002 int grs_extract_tree(struct recExtractCtrl *p, data1_node *n)
1003 {
1004     oident oe;
1005     int oidtmp[OID_SIZE];
1006     RecWord wrd;
1007     int r;
1008     struct RecWord_list *wl = 0;
1009
1010     oe.proto = PROTO_Z3950;
1011     oe.oclass = CLASS_SCHEMA;
1012     if (n->u.root.absyn)
1013     {
1014         oe.value = n->u.root.absyn->reference;
1015         
1016         if ((oid_ent_to_oid (&oe, oidtmp)))
1017             (*p->schemaAdd)(p, oidtmp);
1018     }
1019     (*p->init)(p, &wrd);
1020
1021     wl = RecWord_list_create("grs_extract_tree");
1022     r = dumpkeys(n, p, 0, &wrd, wl);
1023     RecWord_list_destroy(wl);
1024     return r;
1025 }
1026
1027 static int grs_extract_sub(struct grs_handlers *h, struct recExtractCtrl *p,
1028                            NMEM mem)
1029 {
1030     data1_node *n;
1031     struct grs_read_info gri;
1032     oident oe;
1033     int oidtmp[OID_SIZE];
1034     RecWord wrd;
1035     struct RecWord_list *wl = 0;
1036     int ret_val;
1037
1038     gri.readf = p->readf;
1039     gri.seekf = p->seekf;
1040     gri.tellf = p->tellf;
1041     gri.endf = p->endf;
1042     gri.fh = p->fh;
1043     gri.offset = p->offset;
1044     gri.mem = mem;
1045     gri.dh = p->dh;
1046
1047     if (read_grs_type (h, &gri, p->subType, &n))
1048         return RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER;
1049     if (!n)
1050         return RECCTRL_EXTRACT_EOF;
1051     oe.proto = PROTO_Z3950;
1052     oe.oclass = CLASS_SCHEMA;
1053 #if 0
1054     if (!n->u.root.absyn)
1055         return RECCTRL_EXTRACT_ERROR;
1056 #endif
1057     if (n->u.root.absyn)
1058     {
1059         oe.value = n->u.root.absyn->reference;
1060         if ((oid_ent_to_oid (&oe, oidtmp)))
1061             (*p->schemaAdd)(p, oidtmp);
1062     }
1063     data1_concat_text(p->dh, mem, n);
1064
1065     /* ensure our data1 tree is UTF-8 */
1066     data1_iconv (p->dh, mem, n, "UTF-8", data1_get_encoding(p->dh, n));
1067
1068 #if 0
1069     data1_pr_tree (p->dh, n, stdout);
1070 #endif
1071
1072     wl = RecWord_list_create("grs.sgml");
1073
1074     (*p->init)(p, &wrd);
1075     if (dumpkeys(n, p, 0, &wrd, wl) < 0)
1076         ret_val = RECCTRL_EXTRACT_ERROR_GENERIC;
1077     else
1078         ret_val = RECCTRL_EXTRACT_OK;
1079     data1_free_tree(p->dh, n);
1080     RecWord_list_destroy(wl);
1081
1082     return ret_val;
1083 }
1084
1085 static int grs_extract(void *clientData, struct recExtractCtrl *p)
1086 {
1087     int ret;
1088     NMEM mem = nmem_create ();
1089     struct grs_handlers *h = (struct grs_handlers *) clientData;
1090
1091     ret = grs_extract_sub(h, p, mem);
1092     nmem_destroy(mem);
1093     return ret;
1094 }
1095
1096 /*
1097  * Return: -1: Nothing done. 0: Ok. >0: Bib-1 diagnostic.
1098  */
1099 static int process_comp(data1_handle dh, data1_node *n, Z_RecordComposition *c)
1100 {
1101     data1_esetname *eset;
1102     Z_Espec1 *espec = 0;
1103     Z_ElementSpec *p;
1104
1105     switch (c->which)
1106     {
1107     case Z_RecordComp_simple:
1108         if (c->u.simple->which != Z_ElementSetNames_generic)
1109             return 26; /* only generic form supported. Fix this later */
1110         if (!(eset = data1_getesetbyname(dh, n->u.root.absyn,
1111                                          c->u.simple->u.generic)))
1112         {
1113             yaz_log(LOG_LOG, "Unknown esetname '%s'", c->u.simple->u.generic);
1114             return 25; /* invalid esetname */
1115         }
1116         yaz_log(LOG_DEBUG, "Esetname '%s' in simple compspec",
1117              c->u.simple->u.generic);
1118         espec = eset->spec;
1119         break;
1120     case Z_RecordComp_complex:
1121         if (c->u.complex->generic)
1122         {
1123             /* insert check for schema */
1124             if ((p = c->u.complex->generic->elementSpec))
1125             {
1126                 switch (p->which)
1127                 {
1128                 case Z_ElementSpec_elementSetName:
1129                     if (!(eset =
1130                           data1_getesetbyname(dh, n->u.root.absyn,
1131                                               p->u.elementSetName)))
1132                     {
1133                         yaz_log(LOG_LOG, "Unknown esetname '%s'",
1134                              p->u.elementSetName);
1135                         return 25; /* invalid esetname */
1136                     }
1137                     yaz_log(LOG_DEBUG, "Esetname '%s' in complex compspec",
1138                          p->u.elementSetName);
1139                     espec = eset->spec;
1140                     break;
1141                 case Z_ElementSpec_externalSpec:
1142                     if (p->u.externalSpec->which == Z_External_espec1)
1143                     {
1144                         yaz_log(LOG_DEBUG, "Got Espec-1");
1145                         espec = p->u.externalSpec-> u.espec1;
1146                     }
1147                     else
1148                     {
1149                         yaz_log(LOG_LOG, "Unknown external espec.");
1150                         return 25; /* bad. what is proper diagnostic? */
1151                     }
1152                     break;
1153                 }
1154             }
1155         }
1156         else
1157             return 26; /* fix */
1158     }
1159     if (espec)
1160     {
1161         yaz_log(LOG_DEBUG, "Element: Espec-1 match");
1162         return data1_doespec1(dh, n, espec);
1163     }
1164     else
1165     {
1166         yaz_log(LOG_DEBUG, "Element: all match");
1167         return -1;
1168     }
1169 }
1170
1171 /* Add Zebra info in separate namespace ...
1172         <root 
1173          ...
1174          <metadata xmlns="http://www.indexdata.dk/zebra/">
1175           <size>359</size>
1176           <localnumber>447</localnumber>
1177           <filename>records/genera.xml</filename>
1178          </metadata>
1179         </root>
1180 */
1181
1182 static void zebra_xml_metadata (struct recRetrieveCtrl *p, data1_node *top,
1183                                 NMEM mem)
1184 {
1185     const char *idzebra_ns[3];
1186     const char *i2 = "\n  ";
1187     const char *i4 = "\n    ";
1188     data1_node *n;
1189
1190     idzebra_ns[0] = "xmlns";
1191     idzebra_ns[1] = "http://www.indexdata.dk/zebra/";
1192     idzebra_ns[2] = 0;
1193
1194     data1_mk_text (p->dh, mem, i2, top);
1195
1196     n = data1_mk_tag (p->dh, mem, "idzebra", idzebra_ns, top);
1197
1198     data1_mk_text (p->dh, mem, "\n", top);
1199
1200     data1_mk_text (p->dh, mem, i4, n);
1201     
1202     data1_mk_tag_data_int (p->dh, n, "size", p->recordSize, mem);
1203
1204     if (p->score != -1)
1205     {
1206         data1_mk_text (p->dh, mem, i4, n);
1207         data1_mk_tag_data_int (p->dh, n, "score", p->score, mem);
1208     }
1209     data1_mk_text (p->dh, mem, i4, n);
1210     data1_mk_tag_data_int (p->dh, n, "localnumber", p->localno, mem);
1211     if (p->fname)
1212     {
1213         data1_mk_text (p->dh, mem, i4, n);
1214         data1_mk_tag_data_text(p->dh, n, "filename", p->fname, mem);
1215     }
1216     data1_mk_text (p->dh, mem, i2, n);
1217 }
1218
1219 static int grs_retrieve(void *clientData, struct recRetrieveCtrl *p)
1220 {
1221     data1_node *node = 0, *onode = 0, *top;
1222     data1_node *dnew;
1223     data1_maptab *map;
1224     int res, selected = 0;
1225     NMEM mem;
1226     struct grs_read_info gri;
1227     const char *tagname;
1228     struct grs_handlers *h = (struct grs_handlers *) clientData;
1229     int requested_schema = VAL_NONE;
1230     data1_marctab *marctab;
1231     int dummy;
1232     
1233     mem = nmem_create();
1234     gri.readf = p->readf;
1235     gri.seekf = p->seekf;
1236     gri.tellf = p->tellf;
1237     gri.endf = NULL;
1238     gri.fh = p->fh;
1239     gri.offset = 0;
1240     gri.mem = mem;
1241     gri.dh = p->dh;
1242
1243     yaz_log(LOG_DEBUG, "grs_retrieve");
1244     if (read_grs_type (h, &gri, p->subType, &node))
1245     {
1246         p->diagnostic = 14;
1247         nmem_destroy (mem);
1248         return 0;
1249     }
1250     if (!node)
1251     {
1252         p->diagnostic = 14;
1253         nmem_destroy (mem);
1254         return 0;
1255     }
1256     data1_concat_text(p->dh, mem, node);
1257
1258     /* ensure our data1 tree is UTF-8 */
1259     data1_iconv (p->dh, mem, node, "UTF-8", data1_get_encoding(p->dh, node));
1260
1261 #if 0
1262     data1_pr_tree (p->dh, node, stdout);
1263 #endif
1264     top = data1_get_root_tag (p->dh, node);
1265
1266     yaz_log(LOG_DEBUG, "grs_retrieve: size");
1267     tagname = data1_systag_lookup(node->u.root.absyn, "size", "size");
1268     if (tagname &&
1269         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1270     {
1271         dnew->u.data.what = DATA1I_text;
1272         dnew->u.data.data = dnew->lbuf;
1273         sprintf(dnew->u.data.data, "%d", p->recordSize);
1274         dnew->u.data.len = strlen(dnew->u.data.data);
1275     }
1276     
1277     tagname = data1_systag_lookup(node->u.root.absyn, "rank", "rank");
1278
1279     if (tagname && p->score >= 0 &&
1280         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1281     {
1282         yaz_log(LOG_DEBUG, "grs_retrieve: %s", tagname);
1283         dnew->u.data.what = DATA1I_num;
1284         dnew->u.data.data = dnew->lbuf;
1285         sprintf(dnew->u.data.data, "%d", p->score);
1286         dnew->u.data.len = strlen(dnew->u.data.data);
1287     }
1288
1289     tagname = data1_systag_lookup(node->u.root.absyn, "sysno",
1290                                   "localControlNumber");
1291     if (tagname && p->localno > 0 &&
1292         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1293     {
1294         yaz_log(LOG_DEBUG, "grs_retrieve: %s", tagname);
1295         dnew->u.data.what = DATA1I_text;
1296         dnew->u.data.data = dnew->lbuf;
1297         
1298         sprintf(dnew->u.data.data, "%d", p->localno);
1299         dnew->u.data.len = strlen(dnew->u.data.data);
1300     }
1301
1302     if (p->input_format == VAL_TEXT_XML)
1303         zebra_xml_metadata (p, top, mem);
1304
1305 #if 0
1306     data1_pr_tree (p->dh, node, stdout);
1307 #endif
1308 #if YAZ_VERSIONL >= 0x010903L
1309     if (p->comp && p->comp->which == Z_RecordComp_complex &&
1310         p->comp->u.complex->generic &&
1311         p->comp->u.complex->generic->which == Z_Schema_oid &&
1312         p->comp->u.complex->generic->schema.oid)
1313     {
1314         oident *oe = oid_getentbyoid (p->comp->u.complex->generic->schema.oid);
1315         if (oe)
1316             requested_schema = oe->value;
1317     }
1318 #else
1319     if (p->comp && p->comp->which == Z_RecordComp_complex &&
1320         p->comp->u.complex->generic && p->comp->u.complex->generic->schema)
1321     {
1322         oident *oe = oid_getentbyoid (p->comp->u.complex->generic->schema);
1323         if (oe)
1324             requested_schema = oe->value;
1325     }
1326 #endif
1327
1328     /* If schema has been specified, map if possible, then check that
1329      * we got the right one 
1330      */
1331     if (requested_schema != VAL_NONE)
1332     {
1333         yaz_log(LOG_DEBUG, "grs_retrieve: schema mapping");
1334         for (map = node->u.root.absyn->maptabs; map; map = map->next)
1335         {
1336             if (map->target_absyn_ref == requested_schema)
1337             {
1338                 onode = node;
1339                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
1340                 {
1341                     p->diagnostic = 14;
1342                     nmem_destroy (mem);
1343                     return 0;
1344                 }
1345                 break;
1346             }
1347         }
1348         if (node->u.root.absyn &&
1349             requested_schema != node->u.root.absyn->reference)
1350         {
1351             p->diagnostic = 238;
1352             nmem_destroy (mem);
1353             return 0;
1354         }
1355     }
1356     /*
1357      * Does the requested format match a known syntax-mapping? (this reflects
1358      * the overlap of schema and formatting which is inherent in the MARC
1359      * family)
1360      */
1361     yaz_log(LOG_DEBUG, "grs_retrieve: syntax mapping");
1362     if (node->u.root.absyn)
1363         for (map = node->u.root.absyn->maptabs; map; map = map->next)
1364         {
1365             if (map->target_absyn_ref == p->input_format)
1366             {
1367                 onode = node;
1368                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
1369                 {
1370                     p->diagnostic = 14;
1371                     nmem_destroy (mem);
1372                     return 0;
1373                 }
1374                 break;
1375             }
1376         }
1377     yaz_log(LOG_DEBUG, "grs_retrieve: schemaIdentifier");
1378     if (node->u.root.absyn &&
1379         node->u.root.absyn->reference != VAL_NONE &&
1380         p->input_format == VAL_GRS1)
1381     {
1382         oident oe;
1383         Odr_oid *oid;
1384         int oidtmp[OID_SIZE];
1385         
1386         oe.proto = PROTO_Z3950;
1387         oe.oclass = CLASS_SCHEMA;
1388         oe.value = node->u.root.absyn->reference;
1389         
1390         if ((oid = oid_ent_to_oid (&oe, oidtmp)))
1391         {
1392             char tmp[128];
1393             data1_handle dh = p->dh;
1394             char *p = tmp;
1395             int *ii;
1396             
1397             for (ii = oid; *ii >= 0; ii++)
1398             {
1399                 if (p != tmp)
1400                         *(p++) = '.';
1401                 sprintf(p, "%d", *ii);
1402                 p += strlen(p);
1403             }
1404             if ((dnew = data1_mk_tag_data_wd(dh, top, 
1405                                              "schemaIdentifier", mem)))
1406             {
1407                 dnew->u.data.what = DATA1I_oid;
1408                 dnew->u.data.data = (char *) nmem_malloc(mem, p - tmp);
1409                 memcpy(dnew->u.data.data, tmp, p - tmp);
1410                 dnew->u.data.len = p - tmp;
1411             }
1412         }
1413     }
1414
1415     yaz_log(LOG_DEBUG, "grs_retrieve: element spec");
1416     if (p->comp && (res = process_comp(p->dh, node, p->comp)) > 0)
1417     {
1418         p->diagnostic = res;
1419         if (onode)
1420             data1_free_tree(p->dh, onode);
1421         data1_free_tree(p->dh, node);
1422         nmem_destroy(mem);
1423         return 0;
1424     }
1425     else if (p->comp && !res)
1426         selected = 1;
1427
1428 #if 0
1429     data1_pr_tree (p->dh, node, stdout);
1430 #endif
1431     yaz_log(LOG_DEBUG, "grs_retrieve: transfer syntax mapping");
1432     switch (p->output_format = (p->input_format != VAL_NONE ?
1433                                 p->input_format : VAL_SUTRS))
1434     {
1435     case VAL_TEXT_XML:
1436
1437 #if 0
1438         data1_pr_tree (p->dh, node, stdout);
1439 #endif
1440
1441         if (p->encoding)
1442             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
1443
1444         if (!(p->rec_buf = data1_nodetoidsgml(p->dh, node, selected,
1445                                               &p->rec_len)))
1446             p->diagnostic = 238;
1447         else
1448         {
1449             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1450             memcpy (new_buf, p->rec_buf, p->rec_len);
1451             p->rec_buf = new_buf;
1452         }
1453         break;
1454     case VAL_GRS1:
1455         dummy = 0;
1456         if (!(p->rec_buf = data1_nodetogr(p->dh, node, selected,
1457                                           p->odr, &dummy)))
1458             p->diagnostic = 238; /* not available in requested syntax */
1459         else
1460             p->rec_len = (size_t) (-1);
1461         break;
1462     case VAL_EXPLAIN:
1463         if (!(p->rec_buf = data1_nodetoexplain(p->dh, node, selected,
1464                                                p->odr)))
1465             p->diagnostic = 238;
1466         else
1467             p->rec_len = (size_t) (-1);
1468         break;
1469     case VAL_SUMMARY:
1470         if (!(p->rec_buf = data1_nodetosummary(p->dh, node, selected,
1471                                                p->odr)))
1472             p->diagnostic = 238;
1473         else
1474             p->rec_len = (size_t) (-1);
1475         break;
1476     case VAL_SUTRS:
1477         if (p->encoding)
1478             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
1479         if (!(p->rec_buf = data1_nodetobuf(p->dh, node, selected,
1480                                            &p->rec_len)))
1481             p->diagnostic = 238;
1482         else
1483         {
1484             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1485             memcpy (new_buf, p->rec_buf, p->rec_len);
1486             p->rec_buf = new_buf;
1487         }
1488         break;
1489     case VAL_SOIF:
1490         if (!(p->rec_buf = data1_nodetosoif(p->dh, node, selected,
1491                                             &p->rec_len)))
1492             p->diagnostic = 238;
1493         else
1494         {
1495             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1496             memcpy (new_buf, p->rec_buf, p->rec_len);
1497             p->rec_buf = new_buf;
1498         }
1499         break;
1500     default:
1501         if (!node->u.root.absyn)
1502         {
1503             p->diagnostic = 238;
1504             break;
1505         }
1506         for (marctab = node->u.root.absyn->marc; marctab;
1507              marctab = marctab->next)
1508             if (marctab->reference == p->input_format)
1509                 break;
1510         if (!marctab)
1511         {
1512             p->diagnostic = 238;
1513             break;
1514         }
1515         if (p->encoding)
1516             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
1517         if (!(p->rec_buf = data1_nodetomarc(p->dh, marctab, node,
1518                                         selected, &p->rec_len)))
1519             p->diagnostic = 238;
1520         else
1521         {
1522             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1523             memcpy (new_buf, p->rec_buf, p->rec_len);
1524                 p->rec_buf = new_buf;
1525         }
1526     }
1527     if (node)
1528         data1_free_tree(p->dh, node);
1529     if (onode)
1530         data1_free_tree(p->dh, onode);
1531     nmem_destroy(mem);
1532     return 0;
1533 }
1534
1535 static struct recType grs_type =
1536 {
1537     "grs",
1538     grs_init,
1539     grs_destroy,
1540     grs_extract,
1541     grs_retrieve
1542 };
1543
1544 RecType recTypeGrs = &grs_type;