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