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