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