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