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