d71664a9c2bebe376e4a9688b3b2eed475e54683
[idzebra-moved-to-github.git] / recctrl / recgrs.c
1 /* $Id: recgrs.c,v 1.86.2.7 2006-02-06 13:34:00 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 Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23 #include <stdio.h>
24 #include <assert.h>
25 #include <sys/types.h>
26 #ifndef WIN32
27 #include <unistd.h>
28 #endif
29
30 #include <yaz/log.h>
31 #include <yaz/oid.h>
32
33 #include <recctrl.h>
34 #include "grsread.h"
35
36 #define GRS_MAX_WORD 512
37
38 struct 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                 c = *p++;
410                 for (t = s->trans, i = s->tran_no; --i >= 0; t++)
411                     if (c >= t->ch[0] && c <= t->ch[1])
412                         break;
413             }
414         }
415     return 0;
416 }
417
418
419 /* *ostrich*
420    
421 New function, looking for xpath "element" definitions in abs, by
422 tagpath, using a kind of ugly regxp search.The DFA was built while
423 parsing abs, so here we just go trough them and try to match
424 against the given tagpath. The first matching entry is returned.
425
426 pop, 2002-12-13
427
428 Added support for enhanced xelm. Now [] predicates are considered
429 as well, when selecting indexing rules... (why the hell it's called
430 termlist???)
431
432 pop, 2003-01-17
433
434 */
435
436 data1_termlist *xpath_termlist_by_tagpath(char *tagpath, data1_node *n)
437 {
438     data1_absyn *abs = n->root->u.root.absyn;
439     data1_xpelement *xpe = abs->xp_elements;
440     data1_node *nn;
441 #ifdef ENHANCED_XELM 
442     struct xpath_location_step *xp;
443
444 #endif
445     char *pexpr = xmalloc(strlen(tagpath)+5);
446     int ok = 0;
447
448     sprintf (pexpr, "/%s\n", tagpath);
449     yaz_log(LOG_DEBUG,"Checking tagpath %s",tagpath);
450     while (xpe) 
451     {
452         int i;
453         ok = dfa_match_first(xpe->dfa->states, pexpr);
454         if (ok)
455             yaz_log(LOG_DEBUG, " xpath got match %s",xpe->xpath_expr);
456         else
457             yaz_log(LOG_DEBUG, " xpath no match %s",xpe->xpath_expr);
458
459         if (ok) {
460 #ifdef ENHANCED_XELM 
461             /* we have to check the perdicates up to the root node */
462             xp = xpe->xpath;
463             
464             /* find the first tag up in the node structure */
465             nn = n; while (nn && nn->which != DATA1N_tag) {
466                 nn = nn->parent;
467             }
468             
469             /* go from inside out in the node structure, while going
470                backwards trough xpath location steps ... */
471             for (i=xpe->xpath_len - 1; i>0; i--) {
472                 
473                 yaz_log(LOG_DEBUG,"Checking step %d: %s on tag %s",
474                      i,xp[i].part,nn->u.tag.tag);
475                 
476                 if (!d1_check_xpath_predicate(nn, xp[i].predicate)) {
477                     yaz_log(LOG_DEBUG,"  Predicates didn't match");
478                     ok = 0;
479                     break;
480                 }
481                 
482                 if (nn->which == DATA1N_tag) {
483                     nn = nn->parent;
484                 }
485             }
486 #endif
487             if (ok) {
488                 break;
489             }
490         }
491         xpe = xpe->next;
492     } 
493     
494     xfree(pexpr);
495     
496     if (ok) {
497       yaz_log(LOG_DEBUG,"Got it");
498         return xpe->termlists;
499     } else {
500         return NULL;
501     }
502 }
503
504 /* use
505      1   start element (tag)
506      2   end element
507      3   start attr (and attr-exact)
508      4   end attr
509
510   1016   cdata
511   1015   attr data
512
513   *ostrich*
514
515   Now, if there is a matching xelm described in abs, for the
516   indexed element or the attribute,  then the data is handled according 
517   to those definitions...
518
519   modified by pop, 2002-12-13
520 */
521
522 /* add xpath index for an attribute */
523 static void index_xpath_attr (char *tag_path, char *name, char *value,
524                               char *structure, struct recExtractCtrl *p,
525                               RecWord *wrd)
526 {
527     wrd->attrSet = VAL_IDXPATH;
528     wrd->attrUse = 1;
529     wrd->reg_type = '0';
530     wrd->string = tag_path;
531     wrd->length = strlen(tag_path);
532     (*p->tokenAdd)(wrd);
533     
534     if (value) {
535         wrd->attrUse = 1015;
536         wrd->reg_type = 'w';
537         wrd->string = value;
538         wrd->length = strlen(value);
539         (*p->tokenAdd)(wrd);
540     }
541     
542     wrd->attrUse = 2;
543     wrd->reg_type = '0';
544     wrd->string = tag_path;
545     wrd->length = strlen(tag_path);
546     (*p->tokenAdd)(wrd);
547 }
548
549
550
551 static void index_xpath (data1_node *n, struct recExtractCtrl *p,
552                          int level, RecWord *wrd, int use,
553                          struct RecWord_list *wl)
554 {
555     int i;
556     char tag_path_full[1024];
557     size_t flen = 0;
558     data1_node *nn;
559     int termlist_only = 1;
560     data1_termlist *tl;
561     int xpdone = 0;
562
563     yaz_log(LOG_DEBUG, "index_xpath level=%d use=%d", level, use);
564     if ((!n->root->u.root.absyn) ||
565         (n->root->u.root.absyn->enable_xpath_indexing)) {
566       termlist_only = 0;
567     }
568
569     switch (n->which)
570     {
571     case DATA1N_data:
572         wrd->string = n->u.data.data;
573         wrd->length = n->u.data.len;
574         xpdone = 0;
575         flen = 0;
576             
577         /* we have to fetch the whole path to the data tag */
578         for (nn = n; nn; nn = nn->parent) {
579             if (nn->which == DATA1N_tag) {
580                 size_t tlen = strlen(nn->u.tag.tag);
581                 if (tlen + flen > (sizeof(tag_path_full)-2)) return;
582                 memcpy (tag_path_full + flen, nn->u.tag.tag, tlen);
583                 flen += tlen;
584                 tag_path_full[flen++] = '/';
585             }
586             else if (nn->which == DATA1N_root)  break;
587         }
588         
589         tag_path_full[flen] = 0;
590         
591         /* If we have a matching termlist... */
592         if (n->root->u.root.absyn && 
593             (tl = xpath_termlist_by_tagpath(tag_path_full, n)))
594         {
595             for (; tl; tl = tl->next)
596             {
597                 /* need to copy recword because it may be changed */
598                 RecWord wrd_tl;
599                 wrd->reg_type = *tl->structure;
600                 /* this is the ! case, so structure is for the xpath index */
601                 memcpy (&wrd_tl, wrd, sizeof(*wrd));
602                 if (tl->source)
603                     sp_parse(n, &wrd_tl, tl->source);
604                 if (!tl->att) {
605                     wrd_tl.attrSet = VAL_IDXPATH;
606                     wrd_tl.attrUse = use;
607                     if (p->flagShowRecords)
608                     {
609                         int i;
610                         printf("%*sXPath index", (level + 1) * 4, "");
611                         printf (" XData:\"");
612                         for (i = 0; i<wrd_tl.length && i < 40; i++)
613                             fputc (wrd_tl.string[i], stdout);
614                         fputc ('"', stdout);
615                         if (wrd_tl.length > 40)
616                             printf (" ...");
617                         fputc ('\n', stdout);
618                     }
619                     else
620                         (*p->tokenAdd)(&wrd_tl);
621                         
622                     xpdone = 1;
623                 } else {
624                     /* this is just the old fashioned attribute based index */
625                     wrd_tl.attrSet = (int) (tl->att->parent->reference);
626                     wrd_tl.attrUse = tl->att->locals->local;
627                     if (p->flagShowRecords)
628                     {
629                         int i;
630                         printf("%*sIdx: [%s]", (level + 1) * 4, "",
631                                tl->structure);
632                         printf("%s:%s [%d] %s",
633                                tl->att->parent->name,
634                                tl->att->name, tl->att->value,
635                                tl->source);
636                         printf (" XData:\"");
637                         for (i = 0; i<wrd_tl.length && i < 40; i++)
638                             fputc (wrd_tl.string[i], stdout);
639                         fputc ('"', stdout);
640                         if (wrd_tl.length > 40)
641                             printf (" ...");
642                         fputc ('\n', stdout);
643                     }
644                     else
645                         (*p->tokenAdd)(&wrd_tl);
646                 }
647             }
648         }
649         /* xpath indexing is done, if there was no termlist given, 
650            or no ! in the termlist, and default indexing is enabled... */
651         if (!p->flagShowRecords && !xpdone && !termlist_only)
652         {
653             wrd->attrSet = VAL_IDXPATH;
654             wrd->attrUse = use;
655             wrd->reg_type = 'w';
656             (*p->tokenAdd)(wrd);
657         }
658         else
659             wrd->seqno++;
660         break;
661     case DATA1N_tag:
662         flen = 0;
663         for (nn = n; nn; nn = nn->parent)
664         {
665             if (nn->which == DATA1N_tag)
666             {
667                 size_t tlen = strlen(nn->u.tag.tag);
668                 if (tlen + flen > (sizeof(tag_path_full)-2))
669                     return;
670                 memcpy (tag_path_full + flen, nn->u.tag.tag, tlen);
671                 flen += tlen;
672                 tag_path_full[flen++] = '/';
673             }
674             else if (nn->which == DATA1N_root)
675                 break;
676         }
677
678
679         wrd->reg_type = '0';
680         wrd->string = tag_path_full;
681         wrd->length = flen;
682         wrd->attrSet = VAL_IDXPATH;
683         wrd->attrUse = use;
684         if (p->flagShowRecords)
685         {
686             printf("%*s tag=", (level + 1) * 4, "");
687             for (i = 0; i<wrd->length && i < 40; i++)
688                 fputc (wrd->string[i], stdout);
689             if (i == 40)
690                 printf (" ..");
691             printf("\n");
692         }
693         else
694         {
695             data1_xattr *xp;
696             data1_termlist *tl;
697             int do_xpindex;
698             
699             tag_path_full[flen] = 0;
700             
701             /* Add tag start/end xpath index, only when there is a ! in the apropriate xelm
702                directive, or default xpath indexing is enabled */
703             if (!(do_xpindex = 1 - termlist_only)) {
704                 if ((tl = xpath_termlist_by_tagpath(tag_path_full, n))) {
705                     for (; tl; tl = tl->next)
706                     {
707                         if (!tl->att)
708                             do_xpindex = 1;
709                     }
710                 }
711             }
712             if (do_xpindex) {
713                 (*p->tokenAdd)(wrd);   /* index element pag (AKA tag path) */
714             }
715             
716             if (use == 1) /* only for the starting tag... */
717             {
718 #define MAX_ATTR_COUNT 50
719                 data1_termlist *tll[MAX_ATTR_COUNT];
720                 
721                 int i = 0;
722                 
723                 /* get termlists for attributes, and find out, if we have to do xpath indexing */
724                 for (xp = n->u.tag.attributes; xp; xp = xp->next) {
725                     i++;
726                 }
727                 
728                 i = 0;
729                 for (xp = n->u.tag.attributes; xp; xp = xp->next) {
730                     char comb[512];
731                     int do_xpindex = 1 - termlist_only;
732                     data1_termlist *tl;
733                     char attr_tag_path_full[1024]; 
734                     int int_len = flen;
735                     
736                     /* this could be cached as well */
737                     sprintf (attr_tag_path_full, "@%s/%.*s",
738                              xp->name, int_len, tag_path_full);
739                     
740                     tll[i] = xpath_termlist_by_tagpath(attr_tag_path_full,n);
741                     
742                     /* if there is a ! in the xelm termlist, or default indexing is on, 
743                        proceed with xpath idx */
744                     if ((tl = tll[i]))
745                     {
746                         for (; tl; tl = tl->next)
747                         { 
748                             if (!tl->att)
749                                 do_xpindex = 1;
750                         }
751                     }
752                     
753                     if (do_xpindex) {
754                         
755                         /* attribute  (no value) */
756                         wrd->reg_type = '0';
757                         wrd->attrUse = 3;
758                         wrd->string = xp->name;
759                         wrd->length = strlen(xp->name);
760                         
761                         wrd->seqno--;
762                         (*p->tokenAdd)(wrd);
763                         
764                         if (xp->value &&
765                             strlen(xp->name) + strlen(xp->value) < sizeof(comb)-2) {
766                             
767                             /* attribute value exact */
768                             strcpy (comb, xp->name);
769                             strcat (comb, "=");
770                             strcat (comb, xp->value);
771                             
772                             wrd->attrUse = 3;
773                             wrd->reg_type = '0';
774                             wrd->string = comb;
775                             wrd->length = strlen(comb);
776                             wrd->seqno--;
777                             
778                             if (RecWord_list_lookadd(wl, wrd))
779                                 (*p->tokenAdd)(wrd);
780                         }
781                     }                
782                     i++;
783                 }
784                 
785                 i = 0;
786                 for (xp = n->u.tag.attributes; xp; xp = xp->next) {
787                     data1_termlist *tl;
788                     char attr_tag_path_full[1024];
789                     int int_len = flen;
790                     int xpdone = 0;
791                     
792                     sprintf (attr_tag_path_full, "@%s/%.*s",
793                              xp->name, int_len, tag_path_full);
794                     
795                     if ((tl = tll[i]))
796                     {
797                         /* If there is a termlist given (=xelm directive) */
798                         for (; tl; tl = tl->next)
799                         {
800                             if (!tl->att) {
801                                 /* add xpath index for the attribute */
802                                 index_xpath_attr (attr_tag_path_full, xp->name,
803                                                   xp->value, tl->structure,
804                                                   p, wrd);
805                                 xpdone = 1;
806                             } 
807                             else 
808                             {
809                                 /* if this fragment is enabled, we index
810                                    attribute values as well. See bug #460 */
811                                 if (0 && xp->value) {
812                                     wrd->attrSet = (int) 
813                                         (tl->att->parent->reference);
814                                     wrd->attrUse = tl->att->locals->local;
815                                     wrd->reg_type = *tl->structure;
816                                     wrd->string = xp->value;
817                                     wrd->length = strlen(xp->value);
818                                     if (RecWord_list_lookadd(wl, wrd))
819                                         (*p->tokenAdd)(wrd);
820                                 }
821                             }
822                         }
823                     }
824                     /* if there was no termlist for the given path, 
825                        or the termlist didn't have a ! element, index 
826                        the attribute as "w" */
827                     if ((!xpdone) && (!termlist_only))
828                     {
829                         index_xpath_attr (attr_tag_path_full, xp->name,
830                                           xp->value,  "w", p, wrd);
831                     }
832                     i++;
833                 }
834             }
835         }
836     }
837 }
838
839 static void index_termlist (data1_node *par, data1_node *n,
840                             struct recExtractCtrl *p, int level, RecWord *wrd)
841 {
842     data1_termlist *tlist = 0;
843     data1_datatype dtype = DATA1K_string;
844
845     /*
846      * cycle up towards the root until we find a tag with an att..
847      * this has the effect of indexing locally defined tags with
848      * the attribute of their ancestor in the record.
849      */
850     
851     while (!par->u.tag.element)
852         if (!par->parent || !(par=get_parent_tag(p->dh, par->parent)))
853             break;
854     if (!par || !(tlist = par->u.tag.element->termlists))
855         return;
856     if (par->u.tag.element->tag)
857         dtype = par->u.tag.element->tag->kind;
858     
859     for (; tlist; tlist = tlist->next)
860     {
861         /* consider source */
862         wrd->string = 0;
863         assert(tlist->source);
864         sp_parse(n, wrd, tlist->source);
865
866         if (wrd->string)
867         {
868             if (p->flagShowRecords)
869             {
870                 int i;
871                 printf("%*sIdx: [%s]", (level + 1) * 4, "",
872                        tlist->structure);
873                 printf("%s:%s [%d] %s",
874                        tlist->att->parent->name,
875                        tlist->att->name, tlist->att->value,
876                        tlist->source);
877                 printf (" XData:\"");
878                 for (i = 0; i<wrd->length && i < 40; i++)
879                     fputc (wrd->string[i], stdout);
880                 fputc ('"', stdout);
881                 if (wrd->length > 40)
882                     printf (" ...");
883                 fputc ('\n', stdout);
884             }
885             else
886             {
887                 wrd->reg_type = *tlist->structure;
888                 wrd->attrSet = (int) (tlist->att->parent->reference);
889                 wrd->attrUse = tlist->att->locals->local;
890                 (*p->tokenAdd)(wrd);
891             }
892         }
893     }
894 }
895
896 static int dumpkeys(data1_node *n, struct recExtractCtrl *p, int level,
897                     RecWord *wrd, struct RecWord_list *wl)
898 {
899     for (; n; n = n->next)
900     {
901         if (p->flagShowRecords) /* display element description to user */
902         {
903             if (n->which == DATA1N_root)
904             {
905                 printf("%*s", level * 4, "");
906                 printf("Record type: '%s'\n", n->u.root.type);
907             }
908             else if (n->which == DATA1N_tag)
909             {
910                 data1_element *e;
911
912                 printf("%*s", level * 4, "");
913                 if (!(e = n->u.tag.element))
914                     printf("Local tag: '%s'\n", n->u.tag.tag);
915                 else
916                 {
917                     printf("Elm: '%s' ", e->name);
918                     if (e->tag)
919                     {
920                         data1_tag *t = e->tag;
921
922                         printf("TagNam: '%s' ", t->names->name);
923                         printf("(");
924                         if (t->tagset)
925                             printf("%s[%d],", t->tagset->name, t->tagset->type);
926                         else
927                             printf("?,");
928                         if (t->which == DATA1T_numeric)
929                             printf("%d)", t->value.numeric);
930                         else
931                             printf("'%s')", t->value.string);
932                     }
933                     printf("\n");
934                 }
935             }
936         }
937
938         if (n->which == DATA1N_tag)
939         {
940             index_termlist (n, n, p, level, wrd);
941             /* index start tag */
942             if (n->root->u.root.absyn)
943                 index_xpath (n, p, level, wrd, 1, wl);
944         }
945
946         if (n->child)
947             if (dumpkeys(n->child, p, level + 1, wrd, wl) < 0)
948                 return -1;
949
950
951         if (n->which == DATA1N_data)
952         {
953             data1_node *par = get_parent_tag(p->dh, n);
954
955             if (p->flagShowRecords)
956             {
957                 printf("%*s", level * 4, "");
958                 printf("Data: ");
959                 if (n->u.data.len > 256)
960                     printf("'%.170s ... %.70s'\n", n->u.data.data,
961                            n->u.data.data + n->u.data.len-70);
962                 else if (n->u.data.len > 0)
963                     printf("'%.*s'\n", n->u.data.len, n->u.data.data);
964                 else
965                     printf("NULL\n");
966             }
967
968             if (par)
969                 index_termlist (par, n, p, level, wrd);
970
971             index_xpath (n, p, level, wrd, 1016, wl);
972         }
973
974         if (n->which == DATA1N_tag)
975         {
976             /* index end tag */
977             index_xpath (n, p, level, wrd, 2, wl);
978         }
979
980         if (p->flagShowRecords && n->which == DATA1N_root)
981         {
982             printf("%*s-------------\n\n", level * 4, "");
983         }
984     }
985     return 0;
986 }
987
988 int grs_extract_tree(struct recExtractCtrl *p, data1_node *n)
989 {
990     oident oe;
991     int oidtmp[OID_SIZE];
992     RecWord wrd;
993     int r;
994     struct RecWord_list *wl = 0;
995
996     oe.proto = PROTO_Z3950;
997     oe.oclass = CLASS_SCHEMA;
998     if (n->u.root.absyn)
999     {
1000         oe.value = n->u.root.absyn->reference;
1001         
1002         if ((oid_ent_to_oid (&oe, oidtmp)))
1003             (*p->schemaAdd)(p, oidtmp);
1004     }
1005     (*p->init)(p, &wrd);
1006
1007     wl = RecWord_list_create("grs_extract_tree");
1008     r = dumpkeys(n, p, 0, &wrd, wl);
1009     RecWord_list_destroy(wl);
1010     return r;
1011 }
1012
1013 static int grs_extract_sub(struct grs_handlers *h, struct recExtractCtrl *p,
1014                            NMEM mem)
1015 {
1016     data1_node *n;
1017     struct grs_read_info gri;
1018     oident oe;
1019     int oidtmp[OID_SIZE];
1020     RecWord wrd;
1021     struct RecWord_list *wl = 0;
1022     int ret_val;
1023
1024     gri.readf = p->readf;
1025     gri.seekf = p->seekf;
1026     gri.tellf = p->tellf;
1027     gri.endf = p->endf;
1028     gri.fh = p->fh;
1029     gri.offset = p->offset;
1030     gri.mem = mem;
1031     gri.dh = p->dh;
1032
1033     if (read_grs_type (h, &gri, p->subType, &n))
1034         return RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER;
1035     if (!n)
1036         return RECCTRL_EXTRACT_EOF;
1037     oe.proto = PROTO_Z3950;
1038     oe.oclass = CLASS_SCHEMA;
1039 #if 0
1040     if (!n->u.root.absyn)
1041         return RECCTRL_EXTRACT_ERROR;
1042 #endif
1043     if (n->u.root.absyn)
1044     {
1045         oe.value = n->u.root.absyn->reference;
1046         if ((oid_ent_to_oid (&oe, oidtmp)))
1047             (*p->schemaAdd)(p, oidtmp);
1048     }
1049     data1_concat_text(p->dh, mem, n);
1050
1051     /* ensure our data1 tree is UTF-8 */
1052     data1_iconv (p->dh, mem, n, "UTF-8", data1_get_encoding(p->dh, n));
1053
1054 #if 0
1055     data1_pr_tree (p->dh, n, stdout);
1056 #endif
1057
1058     wl = RecWord_list_create("grs.sgml");
1059
1060     (*p->init)(p, &wrd);
1061     if (dumpkeys(n, p, 0, &wrd, wl) < 0)
1062         ret_val = RECCTRL_EXTRACT_ERROR_GENERIC;
1063     else
1064         ret_val = RECCTRL_EXTRACT_OK;
1065     data1_free_tree(p->dh, n);
1066     RecWord_list_destroy(wl);
1067
1068     return ret_val;
1069 }
1070
1071 static int grs_extract(void *clientData, struct recExtractCtrl *p)
1072 {
1073     int ret;
1074     NMEM mem = nmem_create ();
1075     struct grs_handlers *h = (struct grs_handlers *) clientData;
1076
1077     ret = grs_extract_sub(h, p, mem);
1078     nmem_destroy(mem);
1079     return ret;
1080 }
1081
1082 /*
1083  * Return: -1: Nothing done. 0: Ok. >0: Bib-1 diagnostic.
1084  */
1085 static int process_comp(data1_handle dh, data1_node *n, Z_RecordComposition *c)
1086 {
1087     data1_esetname *eset;
1088     Z_Espec1 *espec = 0;
1089     Z_ElementSpec *p;
1090
1091     switch (c->which)
1092     {
1093     case Z_RecordComp_simple:
1094         if (c->u.simple->which != Z_ElementSetNames_generic)
1095             return 26; /* only generic form supported. Fix this later */
1096         if (!(eset = data1_getesetbyname(dh, n->u.root.absyn,
1097                                          c->u.simple->u.generic)))
1098         {
1099             yaz_log(LOG_LOG, "Unknown esetname '%s'", c->u.simple->u.generic);
1100             return 25; /* invalid esetname */
1101         }
1102         yaz_log(LOG_DEBUG, "Esetname '%s' in simple compspec",
1103              c->u.simple->u.generic);
1104         espec = eset->spec;
1105         break;
1106     case Z_RecordComp_complex:
1107         if (c->u.complex->generic)
1108         {
1109             /* insert check for schema */
1110             if ((p = c->u.complex->generic->elementSpec))
1111             {
1112                 switch (p->which)
1113                 {
1114                 case Z_ElementSpec_elementSetName:
1115                     if (!(eset =
1116                           data1_getesetbyname(dh, n->u.root.absyn,
1117                                               p->u.elementSetName)))
1118                     {
1119                         yaz_log(LOG_LOG, "Unknown esetname '%s'",
1120                              p->u.elementSetName);
1121                         return 25; /* invalid esetname */
1122                     }
1123                     yaz_log(LOG_DEBUG, "Esetname '%s' in complex compspec",
1124                          p->u.elementSetName);
1125                     espec = eset->spec;
1126                     break;
1127                 case Z_ElementSpec_externalSpec:
1128                     if (p->u.externalSpec->which == Z_External_espec1)
1129                     {
1130                         yaz_log(LOG_DEBUG, "Got Espec-1");
1131                         espec = p->u.externalSpec-> u.espec1;
1132                     }
1133                     else
1134                     {
1135                         yaz_log(LOG_LOG, "Unknown external espec.");
1136                         return 25; /* bad. what is proper diagnostic? */
1137                     }
1138                     break;
1139                 }
1140             }
1141         }
1142         else
1143             return 26; /* fix */
1144     }
1145     if (espec)
1146     {
1147         yaz_log(LOG_DEBUG, "Element: Espec-1 match");
1148         return data1_doespec1(dh, n, espec);
1149     }
1150     else
1151     {
1152         yaz_log(LOG_DEBUG, "Element: all match");
1153         return -1;
1154     }
1155 }
1156
1157 /* Add Zebra info in separate namespace ...
1158         <root 
1159          ...
1160          <metadata xmlns="http://www.indexdata.dk/zebra/">
1161           <size>359</size>
1162           <localnumber>447</localnumber>
1163           <filename>records/genera.xml</filename>
1164          </metadata>
1165         </root>
1166 */
1167
1168 static void zebra_xml_metadata (struct recRetrieveCtrl *p, data1_node *top,
1169                                 NMEM mem)
1170 {
1171     const char *idzebra_ns[3];
1172     const char *i2 = "\n  ";
1173     const char *i4 = "\n    ";
1174     data1_node *n;
1175
1176     idzebra_ns[0] = "xmlns";
1177     idzebra_ns[1] = "http://www.indexdata.dk/zebra/";
1178     idzebra_ns[2] = 0;
1179
1180     data1_mk_text (p->dh, mem, i2, top);
1181
1182     n = data1_mk_tag (p->dh, mem, "idzebra", idzebra_ns, top);
1183
1184     data1_mk_text (p->dh, mem, "\n", top);
1185
1186     data1_mk_text (p->dh, mem, i4, n);
1187     
1188     data1_mk_tag_data_int (p->dh, n, "size", p->recordSize, mem);
1189
1190     if (p->score != -1)
1191     {
1192         data1_mk_text (p->dh, mem, i4, n);
1193         data1_mk_tag_data_int (p->dh, n, "score", p->score, mem);
1194     }
1195     data1_mk_text (p->dh, mem, i4, n);
1196     data1_mk_tag_data_int (p->dh, n, "localnumber", p->localno, mem);
1197     if (p->fname)
1198     {
1199         data1_mk_text (p->dh, mem, i4, n);
1200         data1_mk_tag_data_text(p->dh, n, "filename", p->fname, mem);
1201     }
1202     data1_mk_text (p->dh, mem, i2, n);
1203 }
1204
1205 static int grs_retrieve(void *clientData, struct recRetrieveCtrl *p)
1206 {
1207     data1_node *node = 0, *onode = 0, *top;
1208     data1_node *dnew;
1209     data1_maptab *map;
1210     int res, selected = 0;
1211     NMEM mem;
1212     struct grs_read_info gri;
1213     const char *tagname;
1214     struct grs_handlers *h = (struct grs_handlers *) clientData;
1215     int requested_schema = VAL_NONE;
1216     data1_marctab *marctab;
1217     int dummy;
1218     
1219     mem = nmem_create();
1220     gri.readf = p->readf;
1221     gri.seekf = p->seekf;
1222     gri.tellf = p->tellf;
1223     gri.endf = NULL;
1224     gri.fh = p->fh;
1225     gri.offset = 0;
1226     gri.mem = mem;
1227     gri.dh = p->dh;
1228
1229     yaz_log(LOG_DEBUG, "grs_retrieve");
1230     if (read_grs_type (h, &gri, p->subType, &node))
1231     {
1232         p->diagnostic = 14;
1233         nmem_destroy (mem);
1234         return 0;
1235     }
1236     if (!node)
1237     {
1238         p->diagnostic = 14;
1239         nmem_destroy (mem);
1240         return 0;
1241     }
1242     data1_concat_text(p->dh, mem, node);
1243
1244     /* ensure our data1 tree is UTF-8 */
1245     data1_iconv (p->dh, mem, node, "UTF-8", data1_get_encoding(p->dh, node));
1246
1247 #if 0
1248     data1_pr_tree (p->dh, node, stdout);
1249 #endif
1250     top = data1_get_root_tag (p->dh, node);
1251
1252     yaz_log(LOG_DEBUG, "grs_retrieve: size");
1253     tagname = data1_systag_lookup(node->u.root.absyn, "size", "size");
1254     if (tagname &&
1255         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1256     {
1257         dnew->u.data.what = DATA1I_text;
1258         dnew->u.data.data = dnew->lbuf;
1259         sprintf(dnew->u.data.data, "%d", p->recordSize);
1260         dnew->u.data.len = strlen(dnew->u.data.data);
1261     }
1262     
1263     tagname = data1_systag_lookup(node->u.root.absyn, "rank", "rank");
1264
1265     if (tagname && p->score >= 0 &&
1266         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1267     {
1268         yaz_log(LOG_DEBUG, "grs_retrieve: %s", tagname);
1269         dnew->u.data.what = DATA1I_num;
1270         dnew->u.data.data = dnew->lbuf;
1271         sprintf(dnew->u.data.data, "%d", p->score);
1272         dnew->u.data.len = strlen(dnew->u.data.data);
1273     }
1274
1275     tagname = data1_systag_lookup(node->u.root.absyn, "sysno",
1276                                   "localControlNumber");
1277     if (tagname && p->localno > 0 &&
1278         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1279     {
1280         yaz_log(LOG_DEBUG, "grs_retrieve: %s", tagname);
1281         dnew->u.data.what = DATA1I_text;
1282         dnew->u.data.data = dnew->lbuf;
1283         
1284         sprintf(dnew->u.data.data, "%d", p->localno);
1285         dnew->u.data.len = strlen(dnew->u.data.data);
1286     }
1287
1288     if (p->input_format == VAL_TEXT_XML)
1289         zebra_xml_metadata (p, top, mem);
1290
1291 #if 0
1292     data1_pr_tree (p->dh, node, stdout);
1293 #endif
1294 #if YAZ_VERSIONL >= 0x010903L
1295     if (p->comp && p->comp->which == Z_RecordComp_complex &&
1296         p->comp->u.complex->generic &&
1297         p->comp->u.complex->generic->which == Z_Schema_oid &&
1298         p->comp->u.complex->generic->schema.oid)
1299     {
1300         oident *oe = oid_getentbyoid (p->comp->u.complex->generic->schema.oid);
1301         if (oe)
1302             requested_schema = oe->value;
1303     }
1304 #else
1305     if (p->comp && p->comp->which == Z_RecordComp_complex &&
1306         p->comp->u.complex->generic && p->comp->u.complex->generic->schema)
1307     {
1308         oident *oe = oid_getentbyoid (p->comp->u.complex->generic->schema);
1309         if (oe)
1310             requested_schema = oe->value;
1311     }
1312 #endif
1313
1314     /* If schema has been specified, map if possible, then check that
1315      * we got the right one 
1316      */
1317     if (requested_schema != VAL_NONE)
1318     {
1319         yaz_log(LOG_DEBUG, "grs_retrieve: schema mapping");
1320         for (map = node->u.root.absyn->maptabs; map; map = map->next)
1321         {
1322             if (map->target_absyn_ref == requested_schema)
1323             {
1324                 onode = node;
1325                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
1326                 {
1327                     p->diagnostic = 14;
1328                     nmem_destroy (mem);
1329                     return 0;
1330                 }
1331                 break;
1332             }
1333         }
1334         if (node->u.root.absyn &&
1335             requested_schema != node->u.root.absyn->reference)
1336         {
1337             p->diagnostic = 238;
1338             nmem_destroy (mem);
1339             return 0;
1340         }
1341     }
1342     /*
1343      * Does the requested format match a known syntax-mapping? (this reflects
1344      * the overlap of schema and formatting which is inherent in the MARC
1345      * family)
1346      */
1347     yaz_log(LOG_DEBUG, "grs_retrieve: syntax mapping");
1348     if (node->u.root.absyn)
1349         for (map = node->u.root.absyn->maptabs; map; map = map->next)
1350         {
1351             if (map->target_absyn_ref == p->input_format)
1352             {
1353                 onode = node;
1354                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
1355                 {
1356                     p->diagnostic = 14;
1357                     nmem_destroy (mem);
1358                     return 0;
1359                 }
1360                 break;
1361             }
1362         }
1363     yaz_log(LOG_DEBUG, "grs_retrieve: schemaIdentifier");
1364     if (node->u.root.absyn &&
1365         node->u.root.absyn->reference != VAL_NONE &&
1366         p->input_format == VAL_GRS1)
1367     {
1368         oident oe;
1369         Odr_oid *oid;
1370         int oidtmp[OID_SIZE];
1371         
1372         oe.proto = PROTO_Z3950;
1373         oe.oclass = CLASS_SCHEMA;
1374         oe.value = node->u.root.absyn->reference;
1375         
1376         if ((oid = oid_ent_to_oid (&oe, oidtmp)))
1377         {
1378             char tmp[128];
1379             data1_handle dh = p->dh;
1380             char *p = tmp;
1381             int *ii;
1382             
1383             for (ii = oid; *ii >= 0; ii++)
1384             {
1385                 if (p != tmp)
1386                         *(p++) = '.';
1387                 sprintf(p, "%d", *ii);
1388                 p += strlen(p);
1389             }
1390             if ((dnew = data1_mk_tag_data_wd(dh, top, 
1391                                              "schemaIdentifier", mem)))
1392             {
1393                 dnew->u.data.what = DATA1I_oid;
1394                 dnew->u.data.data = (char *) nmem_malloc(mem, p - tmp);
1395                 memcpy(dnew->u.data.data, tmp, p - tmp);
1396                 dnew->u.data.len = p - tmp;
1397             }
1398         }
1399     }
1400
1401     yaz_log(LOG_DEBUG, "grs_retrieve: element spec");
1402     if (p->comp && (res = process_comp(p->dh, node, p->comp)) > 0)
1403     {
1404         p->diagnostic = res;
1405         if (onode)
1406             data1_free_tree(p->dh, onode);
1407         data1_free_tree(p->dh, node);
1408         nmem_destroy(mem);
1409         return 0;
1410     }
1411     else if (p->comp && !res)
1412         selected = 1;
1413
1414 #if 0
1415     data1_pr_tree (p->dh, node, stdout);
1416 #endif
1417     yaz_log(LOG_DEBUG, "grs_retrieve: transfer syntax mapping");
1418     switch (p->output_format = (p->input_format != VAL_NONE ?
1419                                 p->input_format : VAL_SUTRS))
1420     {
1421     case VAL_TEXT_XML:
1422
1423 #if 0
1424         data1_pr_tree (p->dh, node, stdout);
1425 #endif
1426
1427         if (p->encoding)
1428             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
1429
1430         if (!(p->rec_buf = data1_nodetoidsgml(p->dh, node, selected,
1431                                               &p->rec_len)))
1432             p->diagnostic = 238;
1433         else
1434         {
1435             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1436             memcpy (new_buf, p->rec_buf, p->rec_len);
1437             p->rec_buf = new_buf;
1438         }
1439         break;
1440     case VAL_GRS1:
1441         dummy = 0;
1442         if (!(p->rec_buf = data1_nodetogr(p->dh, node, selected,
1443                                           p->odr, &dummy)))
1444             p->diagnostic = 238; /* not available in requested syntax */
1445         else
1446             p->rec_len = (size_t) (-1);
1447         break;
1448     case VAL_EXPLAIN:
1449         if (!(p->rec_buf = data1_nodetoexplain(p->dh, node, selected,
1450                                                p->odr)))
1451             p->diagnostic = 238;
1452         else
1453             p->rec_len = (size_t) (-1);
1454         break;
1455     case VAL_SUMMARY:
1456         if (!(p->rec_buf = data1_nodetosummary(p->dh, node, selected,
1457                                                p->odr)))
1458             p->diagnostic = 238;
1459         else
1460             p->rec_len = (size_t) (-1);
1461         break;
1462     case VAL_SUTRS:
1463         if (p->encoding)
1464             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
1465         if (!(p->rec_buf = data1_nodetobuf(p->dh, node, selected,
1466                                            &p->rec_len)))
1467             p->diagnostic = 238;
1468         else
1469         {
1470             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1471             memcpy (new_buf, p->rec_buf, p->rec_len);
1472             p->rec_buf = new_buf;
1473         }
1474         break;
1475     case VAL_SOIF:
1476         if (!(p->rec_buf = data1_nodetosoif(p->dh, node, selected,
1477                                             &p->rec_len)))
1478             p->diagnostic = 238;
1479         else
1480         {
1481             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1482             memcpy (new_buf, p->rec_buf, p->rec_len);
1483             p->rec_buf = new_buf;
1484         }
1485         break;
1486     default:
1487         if (!node->u.root.absyn)
1488         {
1489             p->diagnostic = 238;
1490             break;
1491         }
1492         for (marctab = node->u.root.absyn->marc; marctab;
1493              marctab = marctab->next)
1494             if (marctab->reference == p->input_format)
1495                 break;
1496         if (!marctab)
1497         {
1498             p->diagnostic = 238;
1499             break;
1500         }
1501         if (p->encoding)
1502             data1_iconv (p->dh, mem, node, p->encoding, "UTF-8");
1503         if (!(p->rec_buf = data1_nodetomarc(p->dh, marctab, node,
1504                                         selected, &p->rec_len)))
1505             p->diagnostic = 238;
1506         else
1507         {
1508             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1509             memcpy (new_buf, p->rec_buf, p->rec_len);
1510                 p->rec_buf = new_buf;
1511         }
1512     }
1513     if (node)
1514         data1_free_tree(p->dh, node);
1515     if (onode)
1516         data1_free_tree(p->dh, onode);
1517     nmem_destroy(mem);
1518     return 0;
1519 }
1520
1521 static struct recType grs_type =
1522 {
1523     "grs",
1524     grs_init,
1525     grs_destroy,
1526     grs_extract,
1527     grs_retrieve
1528 };
1529
1530 RecType recTypeGrs = &grs_type;