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