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