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