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