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