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