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