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