Dont index attributes. Bug #460
[idzebra-moved-to-github.git] / recctrl / recgrs.c
1 /* $Id: recgrs.c,v 1.106 2006-02-06 13:34:13 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                                 /* if this fragment is enabled, we index
817                                    attribute values as well. See bug #460 */
818                                 if (0 && xp->value) 
819                                 {
820 #if NATTR
821                                     wrd->index_name = tl->index_name;
822 #else
823                                     wrd->attrSet = (int) 
824                                         (tl->att->parent->reference);
825                                     wrd->attrUse = tl->att->locals->local;
826 #endif
827                                     wrd->index_type = *tl->structure;
828                                     wrd->term_buf = xp->value;
829                                     wrd->term_len = strlen(xp->value);
830                                     (*p->tokenAdd)(wrd);
831                                 }
832                             }
833                         }
834                     }
835                     /* if there was no termlist for the given path, 
836                        or the termlist didn't have a ! element, index 
837                        the attribute as "w" */
838                     if ((!xpdone) && (!termlist_only))
839                     {
840                         index_xpath_attr (attr_tag_path_full, xp->name,
841                                           xp->value,  "w", p, wrd);
842                     }
843                     i++;
844                 }
845             }
846         }
847     }
848 }
849
850 static void index_termlist (struct source_parser *sp, data1_node *par,
851                             data1_node *n,
852                             struct recExtractCtrl *p, int level, RecWord *wrd)
853 {
854     data1_termlist *tlist = 0;
855     data1_datatype dtype = DATA1K_string;
856
857     /*
858      * cycle up towards the root until we find a tag with an att..
859      * this has the effect of indexing locally defined tags with
860      * the attribute of their ancestor in the record.
861      */
862     
863     while (!par->u.tag.element)
864         if (!par->parent || !(par=get_parent_tag(p->dh, par->parent)))
865             break;
866     if (!par || !(tlist = par->u.tag.element->termlists))
867         return;
868     if (par->u.tag.element->tag)
869         dtype = par->u.tag.element->tag->kind;
870
871     for (; tlist; tlist = tlist->next)
872     {
873         /* consider source */
874         wrd->term_buf = 0;
875         assert(tlist->source);
876         sp_parse(sp, n, wrd, tlist->source);
877
878         if (wrd->term_buf && wrd->term_len)
879         {
880             if (p->flagShowRecords)
881             {
882                 int i;
883                 printf("%*sIdx: [%s]", (level + 1) * 4, "",
884                        tlist->structure);
885 #if NATTR
886                 printf("%s %s", tlist->index_name, tlist->source);
887 #else
888                 printf("%s:%s [%d] %s",
889                        tlist->att->parent->name,
890                        tlist->att->name, tlist->att->value,
891                        tlist->source);
892 #endif
893                 printf (" XData:\"");
894                 for (i = 0; i<wrd->term_len && i < 40; i++)
895                     fputc (wrd->term_buf[i], stdout);
896                 fputc ('"', stdout);
897                 if (wrd->term_len > 40)
898                     printf (" ...");
899                 fputc ('\n', stdout);
900             }
901             else
902             {
903                 wrd->index_type = *tlist->structure;
904 #if NATTR
905                 wrd->index_name = tlist->index_name;
906 #else
907                 wrd->attrSet = (int) (tlist->att->parent->reference);
908                 wrd->attrUse = tlist->att->locals->local;
909 #endif
910                 (*p->tokenAdd)(wrd);
911             }
912         }
913     }
914 }
915
916 static int dumpkeys_r(struct source_parser *sp,
917                       data1_node *n, struct recExtractCtrl *p, int level,
918                       RecWord *wrd)
919 {
920     for (; n; n = n->next)
921     {
922         if (p->flagShowRecords) /* display element description to user */
923         {
924             if (n->which == DATA1N_root)
925             {
926                 printf("%*s", level * 4, "");
927                 printf("Record type: '%s'\n", n->u.root.type);
928             }
929             else if (n->which == DATA1N_tag)
930             {
931                 data1_element *e;
932
933                 printf("%*s", level * 4, "");
934                 if (!(e = n->u.tag.element))
935                     printf("Local tag: '%s'\n", n->u.tag.tag);
936                 else
937                 {
938                     printf("Elm: '%s' ", e->name);
939                     if (e->tag)
940                     {
941                         data1_tag *t = e->tag;
942
943                         printf("TagNam: '%s' ", t->names->name);
944                         printf("(");
945                         if (t->tagset)
946                             printf("%s[%d],", t->tagset->name, t->tagset->type);
947                         else
948                             printf("?,");
949                         if (t->which == DATA1T_numeric)
950                             printf("%d)", t->value.numeric);
951                         else
952                             printf("'%s')", t->value.string);
953                     }
954                     printf("\n");
955                 }
956             }
957         }
958
959         if (n->which == DATA1N_tag)
960         {
961             index_termlist(sp, n, n, p, level, wrd);
962             /* index start tag */
963 #if NATTR
964             if (n->root->u.root.absyn)
965                 index_xpath(sp, n, p, level, wrd, ZEBRA_XPATH_ELM_BEGIN, 
966                             1 /* is start */);
967 #else
968             if (n->root->u.root.absyn)
969                 index_xpath(sp, n, p, level, wrd, 1);
970 #endif
971         }
972
973         if (n->child)
974             if (dumpkeys_r(sp, n->child, p, level + 1, wrd) < 0)
975                 return -1;
976
977
978         if (n->which == DATA1N_data)
979         {
980             data1_node *par = get_parent_tag(p->dh, n);
981
982             if (p->flagShowRecords)
983             {
984                 printf("%*s", level * 4, "");
985                 printf("Data: ");
986                 if (n->u.data.len > 256)
987                     printf("'%.170s ... %.70s'\n", n->u.data.data,
988                            n->u.data.data + n->u.data.len-70);
989                 else if (n->u.data.len > 0)
990                     printf("'%.*s'\n", n->u.data.len, n->u.data.data);
991                 else
992                     printf("NULL\n");
993             }
994
995             if (par)
996                 index_termlist(sp, par, n, p, level, wrd);
997
998 #if NATTR
999             index_xpath(sp, n, p, level, wrd, ZEBRA_XPATH_CDATA, 
1000                         0 /* is start */);
1001 #else
1002             index_xpath(sp, n, p, level, wrd, 1016);
1003 #endif
1004         }
1005
1006         if (n->which == DATA1N_tag)
1007         {
1008             /* index end tag */
1009 #if NATTR
1010             index_xpath(sp, n, p, level, wrd, ZEBRA_XPATH_ELM_END, 
1011                         0 /* is start */);
1012 #else
1013             index_xpath(sp, n, p, level, wrd, 2);
1014 #endif
1015         }
1016
1017         if (p->flagShowRecords && n->which == DATA1N_root)
1018         {
1019             printf("%*s-------------\n\n", level * 4, "");
1020         }
1021     }
1022     return 0;
1023 }
1024
1025 static int dumpkeys(data1_node *n, struct recExtractCtrl *p, RecWord *wrd)
1026 {
1027     struct source_parser *sp = source_parser_create();
1028     int r = dumpkeys_r(sp, n, p, 0, wrd);
1029     source_parser_destroy(sp);
1030     return r;
1031 }
1032
1033 int grs_extract_tree(struct recExtractCtrl *p, data1_node *n)
1034 {
1035     oident oe;
1036     int oidtmp[OID_SIZE];
1037     RecWord wrd;
1038
1039     oe.proto = PROTO_Z3950;
1040     oe.oclass = CLASS_SCHEMA;
1041     if (n->u.root.absyn)
1042     {
1043         oe.value = n->u.root.absyn->reference;
1044         
1045         if ((oid_ent_to_oid (&oe, oidtmp)))
1046             (*p->schemaAdd)(p, oidtmp);
1047     }
1048     (*p->init)(p, &wrd);
1049
1050     return dumpkeys(n, p, &wrd);
1051 }
1052
1053 static int grs_extract_sub(void *clientData, struct recExtractCtrl *p,
1054                            NMEM mem,
1055                            data1_node *(*grs_read)(struct grs_read_info *))
1056 {
1057     data1_node *n;
1058     struct grs_read_info gri;
1059     oident oe;
1060     int oidtmp[OID_SIZE];
1061     RecWord wrd;
1062
1063     gri.readf = p->readf;
1064     gri.seekf = p->seekf;
1065     gri.tellf = p->tellf;
1066     gri.endf = p->endf;
1067     gri.fh = p->fh;
1068     gri.offset = p->offset;
1069     gri.mem = mem;
1070     gri.dh = p->dh;
1071     gri.clientData = clientData;
1072
1073     n = (*grs_read)(&gri);
1074     if (!n)
1075         return RECCTRL_EXTRACT_EOF;
1076     oe.proto = PROTO_Z3950;
1077     oe.oclass = CLASS_SCHEMA;
1078 #if 0
1079     if (!n->u.root.absyn)
1080         return RECCTRL_EXTRACT_ERROR;
1081 #endif
1082     if (n->u.root.absyn)
1083     {
1084         oe.value = n->u.root.absyn->reference;
1085         if ((oid_ent_to_oid (&oe, oidtmp)))
1086             (*p->schemaAdd)(p, oidtmp);
1087     }
1088     data1_concat_text(p->dh, mem, n);
1089
1090     /* ensure our data1 tree is UTF-8 */
1091     data1_iconv (p->dh, mem, n, "UTF-8", data1_get_encoding(p->dh, n));
1092
1093 #if 0
1094     data1_pr_tree (p->dh, n, stdout);
1095 #endif
1096
1097     (*p->init)(p, &wrd);
1098     if (dumpkeys(n, p, &wrd) < 0)
1099     {
1100         data1_free_tree(p->dh, n);
1101         return RECCTRL_EXTRACT_ERROR_GENERIC;
1102     }
1103     data1_free_tree(p->dh, n);
1104     return RECCTRL_EXTRACT_OK;
1105 }
1106
1107 int zebra_grs_extract(void *clientData, struct recExtractCtrl *p,
1108                       data1_node *(*grs_read)(struct grs_read_info *))
1109 {
1110     int ret;
1111     NMEM mem = nmem_create ();
1112     ret = grs_extract_sub(clientData, p, mem, grs_read);
1113     nmem_destroy(mem);
1114     return ret;
1115 }
1116
1117 /*
1118  * Return: -1: Nothing done. 0: Ok. >0: Bib-1 diagnostic.
1119  */
1120 static int process_comp(data1_handle dh, data1_node *n, Z_RecordComposition *c,
1121                         char **addinfo, ODR o)
1122 {
1123     data1_esetname *eset;
1124     Z_Espec1 *espec = 0;
1125     Z_ElementSpec *p;
1126
1127     switch (c->which)
1128     {
1129     case Z_RecordComp_simple:
1130         if (c->u.simple->which != Z_ElementSetNames_generic)
1131             return 26; /* only generic form supported. Fix this later */
1132         if (!(eset = data1_getesetbyname(dh, n->u.root.absyn,
1133                                          c->u.simple->u.generic)))
1134         {
1135             yaz_log(YLOG_LOG, "Unknown esetname '%s'", c->u.simple->u.generic);
1136             *addinfo = odr_strdup(o, c->u.simple->u.generic);
1137             return 25; /* invalid esetname */
1138         }
1139         yaz_log(YLOG_DEBUG, "Esetname '%s' in simple compspec",
1140              c->u.simple->u.generic);
1141         espec = eset->spec;
1142         break;
1143     case Z_RecordComp_complex:
1144         if (c->u.complex->generic)
1145         {
1146             /* insert check for schema */
1147             if ((p = c->u.complex->generic->elementSpec))
1148             {
1149                 switch (p->which)
1150                 {
1151                 case Z_ElementSpec_elementSetName:
1152                     if (!(eset =
1153                           data1_getesetbyname(dh, n->u.root.absyn,
1154                                               p->u.elementSetName)))
1155                     {
1156                         yaz_log(YLOG_DEBUG, "Unknown esetname '%s'",
1157                              p->u.elementSetName);
1158                         *addinfo = odr_strdup(o, p->u.elementSetName);
1159                         return 25; /* invalid esetname */
1160                     }
1161                     yaz_log(YLOG_DEBUG, "Esetname '%s' in complex compspec",
1162                          p->u.elementSetName);
1163                     espec = eset->spec;
1164                     break;
1165                 case Z_ElementSpec_externalSpec:
1166                     if (p->u.externalSpec->which == Z_External_espec1)
1167                     {
1168                         yaz_log(YLOG_DEBUG, "Got Espec-1");
1169                         espec = p->u.externalSpec-> u.espec1;
1170                     }
1171                     else
1172                     {
1173                         yaz_log(YLOG_LOG, "Unknown external espec.");
1174                         return 25; /* bad. what is proper diagnostic? */
1175                     }
1176                     break;
1177                 }
1178             }
1179         }
1180         else
1181             return 26; /* fix */
1182     }
1183     if (espec)
1184     {
1185         yaz_log(YLOG_DEBUG, "Element: Espec-1 match");
1186         return data1_doespec1(dh, n, espec);
1187     }
1188     else
1189     {
1190         yaz_log(YLOG_DEBUG, "Element: all match");
1191         return -1;
1192     }
1193 }
1194
1195 /* Add Zebra info in separate namespace ...
1196         <root 
1197          ...
1198          <metadata xmlns="http://www.indexdata.dk/zebra/">
1199           <size>359</size>
1200           <localnumber>447</localnumber>
1201           <filename>records/genera.xml</filename>
1202          </metadata>
1203         </root>
1204 */
1205
1206 static void zebra_xml_metadata (struct recRetrieveCtrl *p, data1_node *top,
1207                                 NMEM mem)
1208 {
1209     const char *idzebra_ns[3];
1210     const char *i2 = "\n  ";
1211     const char *i4 = "\n    ";
1212     data1_node *n;
1213
1214     idzebra_ns[0] = "xmlns";
1215     idzebra_ns[1] = "http://www.indexdata.dk/zebra/";
1216     idzebra_ns[2] = 0;
1217
1218     data1_mk_text (p->dh, mem, i2, top);
1219
1220     n = data1_mk_tag (p->dh, mem, "idzebra", idzebra_ns, top);
1221
1222     data1_mk_text (p->dh, mem, "\n", top);
1223
1224     data1_mk_text (p->dh, mem, i4, n);
1225     
1226     data1_mk_tag_data_int (p->dh, n, "size", p->recordSize, mem);
1227
1228     if (p->score != -1)
1229     {
1230         data1_mk_text (p->dh, mem, i4, n);
1231         data1_mk_tag_data_int (p->dh, n, "score", p->score, mem);
1232     }
1233     data1_mk_text (p->dh, mem, i4, n);
1234     data1_mk_tag_data_zint (p->dh, n, "localnumber", p->localno, mem);
1235     if (p->fname)
1236     {
1237         data1_mk_text (p->dh, mem, i4, n);
1238         data1_mk_tag_data_text(p->dh, n, "filename", p->fname, mem);
1239     }
1240     data1_mk_text (p->dh, mem, i2, n);
1241 }
1242
1243 int zebra_grs_retrieve(void *clientData, struct recRetrieveCtrl *p,
1244                        data1_node *(*grs_read)(struct grs_read_info *))
1245 {
1246     data1_node *node = 0, *onode = 0, *top;
1247     data1_node *dnew;
1248     data1_maptab *map;
1249     int res, selected = 0;
1250     NMEM mem;
1251     struct grs_read_info gri;
1252     const char *tagname;
1253
1254     int requested_schema = VAL_NONE;
1255     data1_marctab *marctab;
1256     int dummy;
1257     
1258     mem = nmem_create();
1259     gri.readf = p->readf;
1260     gri.seekf = p->seekf;
1261     gri.tellf = p->tellf;
1262     gri.endf = NULL;
1263     gri.fh = p->fh;
1264     gri.offset = 0;
1265     gri.mem = mem;
1266     gri.dh = p->dh;
1267     gri.clientData = clientData;
1268
1269     yaz_log(YLOG_DEBUG, "grs_retrieve");
1270     node = (*grs_read)(&gri);
1271     if (!node)
1272     {
1273         p->diagnostic = 14;
1274         nmem_destroy (mem);
1275         return 0;
1276     }
1277     data1_concat_text(p->dh, mem, node);
1278
1279 #if 0
1280     data1_pr_tree (p->dh, node, stdout);
1281 #endif
1282     top = data1_get_root_tag (p->dh, node);
1283
1284     yaz_log(YLOG_DEBUG, "grs_retrieve: size");
1285     tagname = data1_systag_lookup(node->u.root.absyn, "size", "size");
1286     if (tagname &&
1287         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1288     {
1289         dnew->u.data.what = DATA1I_text;
1290         dnew->u.data.data = dnew->lbuf;
1291         sprintf(dnew->u.data.data, "%d", p->recordSize);
1292         dnew->u.data.len = strlen(dnew->u.data.data);
1293     }
1294     
1295     tagname = data1_systag_lookup(node->u.root.absyn, "rank", "rank");
1296     if (tagname && p->score >= 0 &&
1297         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1298     {
1299         yaz_log(YLOG_DEBUG, "grs_retrieve: %s", tagname);
1300         dnew->u.data.what = DATA1I_num;
1301         dnew->u.data.data = dnew->lbuf;
1302         sprintf(dnew->u.data.data, "%d", p->score);
1303         dnew->u.data.len = strlen(dnew->u.data.data);
1304     }
1305
1306     tagname = data1_systag_lookup(node->u.root.absyn, "sysno",
1307                                   "localControlNumber");
1308     if (tagname && p->localno > 0 &&
1309         (dnew = data1_mk_tag_data_wd(p->dh, top, tagname, mem)))
1310     {
1311         yaz_log(YLOG_DEBUG, "grs_retrieve: %s", tagname);
1312         dnew->u.data.what = DATA1I_text;
1313         dnew->u.data.data = dnew->lbuf;
1314         
1315         sprintf(dnew->u.data.data, ZINT_FORMAT, p->localno);
1316         dnew->u.data.len = strlen(dnew->u.data.data);
1317     }
1318
1319     if (p->input_format == VAL_TEXT_XML)
1320        zebra_xml_metadata (p, top, mem);
1321
1322 #if 0
1323     data1_pr_tree (p->dh, node, stdout);
1324 #endif
1325     if (p->comp && p->comp->which == Z_RecordComp_complex &&
1326         p->comp->u.complex->generic &&
1327         p->comp->u.complex->generic->which == Z_Schema_oid &&
1328         p->comp->u.complex->generic->schema.oid)
1329     {
1330         oident *oe = oid_getentbyoid (p->comp->u.complex->generic->schema.oid);
1331         if (oe)
1332             requested_schema = oe->value;
1333     }
1334     /* If schema has been specified, map if possible, then check that
1335      * we got the right one 
1336      */
1337     if (requested_schema != VAL_NONE)
1338     {
1339         yaz_log(YLOG_DEBUG, "grs_retrieve: schema mapping");
1340         for (map = node->u.root.absyn->maptabs; map; map = map->next)
1341         {
1342             if (map->target_absyn_ref == requested_schema)
1343             {
1344                 onode = node;
1345                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
1346                 {
1347                     p->diagnostic = 14;
1348                     nmem_destroy (mem);
1349                     return 0;
1350                 }
1351                 break;
1352             }
1353         }
1354         if (node->u.root.absyn &&
1355             requested_schema != node->u.root.absyn->reference)
1356         {
1357             p->diagnostic = 238;
1358             nmem_destroy (mem);
1359             return 0;
1360         }
1361     }
1362     /*
1363      * Does the requested format match a known syntax-mapping? (this reflects
1364      * the overlap of schema and formatting which is inherent in the MARC
1365      * family)
1366      */
1367     yaz_log(YLOG_DEBUG, "grs_retrieve: syntax mapping");
1368     if (node->u.root.absyn)
1369         for (map = node->u.root.absyn->maptabs; map; map = map->next)
1370         {
1371             if (map->target_absyn_ref == p->input_format)
1372             {
1373                 onode = node;
1374                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
1375                 {
1376                     p->diagnostic = 14;
1377                     nmem_destroy (mem);
1378                     return 0;
1379                 }
1380                 break;
1381             }
1382         }
1383     yaz_log(YLOG_DEBUG, "grs_retrieve: schemaIdentifier");
1384     if (node->u.root.absyn &&
1385         node->u.root.absyn->reference != VAL_NONE &&
1386         p->input_format == VAL_GRS1)
1387     {
1388         oident oe;
1389         Odr_oid *oid;
1390         int oidtmp[OID_SIZE];
1391         
1392         oe.proto = PROTO_Z3950;
1393         oe.oclass = CLASS_SCHEMA;
1394         oe.value = node->u.root.absyn->reference;
1395         
1396         if ((oid = oid_ent_to_oid (&oe, oidtmp)))
1397         {
1398             char tmp[128];
1399             data1_handle dh = p->dh;
1400             char *p = tmp;
1401             int *ii;
1402             
1403             for (ii = oid; *ii >= 0; ii++)
1404             {
1405                 if (p != tmp)
1406                         *(p++) = '.';
1407                 sprintf(p, "%d", *ii);
1408                 p += strlen(p);
1409             }
1410             if ((dnew = data1_mk_tag_data_wd(dh, top, 
1411                                              "schemaIdentifier", mem)))
1412             {
1413                 dnew->u.data.what = DATA1I_oid;
1414                 dnew->u.data.data = (char *) nmem_malloc(mem, p - tmp);
1415                 memcpy(dnew->u.data.data, tmp, p - tmp);
1416                 dnew->u.data.len = p - tmp;
1417             }
1418         }
1419     }
1420
1421     yaz_log(YLOG_DEBUG, "grs_retrieve: element spec");
1422     if (p->comp && (res = process_comp(p->dh, node, p->comp, &p->addinfo,
1423                                        p->odr)) > 0)
1424     {
1425         p->diagnostic = res;
1426         if (onode)
1427             data1_free_tree(p->dh, onode);
1428         data1_free_tree(p->dh, node);
1429         nmem_destroy(mem);
1430         return 0;
1431     }
1432     else if (p->comp && !res)
1433         selected = 1;
1434
1435 #if 0
1436     data1_pr_tree (p->dh, node, stdout);
1437 #endif
1438     yaz_log(YLOG_DEBUG, "grs_retrieve: transfer syntax mapping");
1439     switch (p->output_format = (p->input_format != VAL_NONE ?
1440                                 p->input_format : VAL_SUTRS))
1441     {
1442     case VAL_TEXT_XML:
1443 #if 0
1444         data1_pr_tree (p->dh, node, stdout);
1445 #endif
1446         /* default output encoding for XML is UTF-8 */
1447         data1_iconv (p->dh, mem, node,
1448                      p->encoding ? p->encoding : "UTF-8",
1449                      data1_get_encoding(p->dh, node));
1450
1451         if (!(p->rec_buf = data1_nodetoidsgml(p->dh, node, selected,
1452                                               &p->rec_len)))
1453             p->diagnostic = 238;
1454         else
1455         {
1456             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1457             memcpy (new_buf, p->rec_buf, p->rec_len);
1458             p->rec_buf = new_buf;
1459         }
1460         break;
1461     case VAL_GRS1:
1462         data1_iconv (p->dh, mem, node, "UTF-8", data1_get_encoding(p->dh, node));
1463         dummy = 0;
1464         if (!(p->rec_buf = data1_nodetogr(p->dh, node, selected,
1465                                           p->odr, &dummy)))
1466             p->diagnostic = 238; /* not available in requested syntax */
1467         else
1468             p->rec_len = (size_t) (-1);
1469         break;
1470     case VAL_EXPLAIN:
1471         /* ensure our data1 tree is UTF-8 */
1472         data1_iconv (p->dh, mem, node, "UTF-8", data1_get_encoding(p->dh, node));
1473         
1474         if (!(p->rec_buf = data1_nodetoexplain(p->dh, node, selected,
1475                                                p->odr)))
1476             p->diagnostic = 238;
1477         else
1478             p->rec_len = (size_t) (-1);
1479         break;
1480     case VAL_SUMMARY:
1481         /* ensure our data1 tree is UTF-8 */
1482         data1_iconv (p->dh, mem, node, "UTF-8", data1_get_encoding(p->dh, node));
1483         if (!(p->rec_buf = data1_nodetosummary(p->dh, node, selected,
1484                                                p->odr)))
1485             p->diagnostic = 238;
1486         else
1487             p->rec_len = (size_t) (-1);
1488         break;
1489     case VAL_SUTRS:
1490         if (p->encoding)
1491             data1_iconv (p->dh, mem, node, p->encoding,
1492                          data1_get_encoding(p->dh, node));
1493         if (!(p->rec_buf = data1_nodetobuf(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     case VAL_SOIF:
1504         if (p->encoding)
1505             data1_iconv (p->dh, mem, node, p->encoding,
1506                          data1_get_encoding(p->dh, node));
1507         if (!(p->rec_buf = data1_nodetosoif(p->dh, node, selected,
1508                                             &p->rec_len)))
1509             p->diagnostic = 238;
1510         else
1511         {
1512             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1513             memcpy (new_buf, p->rec_buf, p->rec_len);
1514             p->rec_buf = new_buf;
1515         }
1516         break;
1517     default:
1518         if (!node->u.root.absyn)
1519         {
1520             p->diagnostic = 238;
1521             break;
1522         }
1523         for (marctab = node->u.root.absyn->marc; marctab;
1524              marctab = marctab->next)
1525             if (marctab->reference == p->input_format)
1526                 break;
1527         if (!marctab)
1528         {
1529             p->diagnostic = 238;
1530             break;
1531         }
1532         if (p->encoding)
1533             data1_iconv (p->dh, mem, node, p->encoding,
1534                          data1_get_encoding(p->dh, node));
1535         if (!(p->rec_buf = data1_nodetomarc(p->dh, marctab, node,
1536                                         selected, &p->rec_len)))
1537             p->diagnostic = 238;
1538         else
1539         {
1540             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1541             memcpy (new_buf, p->rec_buf, p->rec_len);
1542                 p->rec_buf = new_buf;
1543         }
1544     }
1545     if (node)
1546         data1_free_tree(p->dh, node);
1547     if (onode)
1548         data1_free_tree(p->dh, onode);
1549     nmem_destroy(mem);
1550     return 0;
1551 }
1552