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