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