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