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