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