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