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