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