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