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