data1 cleanup
[idzebra-moved-to-github.git] / recctrl / recgrs.c
1 /*
2  * Copyright (C) 1994-2001, Index Data
3  * All rights reserved.
4  *
5  * $Log: recgrs.c,v $
6  * Revision 1.47  2002-05-03 13:50:25  adam
7  * data1 cleanup
8  *
9  * Revision 1.46  2002/04/13 18:16:43  adam
10  * More XPATH work; common sequence numbers for extract keys
11  *
12  * Revision 1.45  2002/04/12 14:40:42  adam
13  * Work on XPATH
14  *
15  * Revision 1.44  2002/04/11 20:09:47  adam
16  * work on string tag indexing
17  *
18  * Revision 1.43  2002/03/21 23:06:36  adam
19  * Source 'tag' in abs-file
20  *
21  * Revision 1.42  2002/02/20 17:30:01  adam
22  * Work on new API. Locking system re-implemented
23  *
24  * Revision 1.41  2001/05/22 21:01:47  adam
25  * Removed print of data1 tree on stdout so that inetd works again.
26  *
27  * Revision 1.40  2001/03/29 21:31:31  adam
28  * Fixed "record begin" for Tcl filter.
29  *
30  * Revision 1.39  2000/12/05 19:09:15  adam
31  * Fixed problem where indexer could crash if abstract syntax was undefined.
32  *
33  * Revision 1.38  2000/12/05 14:44:58  adam
34  * Fixed minor bug that could cause zmbol to break it data were emitted
35  * with not parent tags.
36  *
37  * Revision 1.37  2000/12/05 12:22:53  adam
38  * Termlist source implemented (so that we can index values of XML/SGML
39  * attributes).
40  *
41  * Revision 1.36  2000/12/05 10:01:44  adam
42  * Fixed bug regarding user-defined attribute sets.
43  *
44  * Revision 1.35  2000/11/29 15:21:31  adam
45  * Fixed problem with passwd db.
46  *
47  * Revision 1.34  2000/02/25 13:24:49  adam
48  * Fixed bug regarding pointer conversion that showed up on OSF V5.
49  *
50  * Revision 1.33  1999/11/30 13:48:04  adam
51  * Improved installation. Updated for inclusion of YAZ header files.
52  *
53  * Revision 1.32  1999/09/07 07:19:21  adam
54  * Work on character mapping. Implemented replace rules.
55  *
56  * Revision 1.31  1999/07/14 10:56:43  adam
57  * Fixed potential memory leak.
58  *
59  * Revision 1.30  1999/07/06 12:26:41  adam
60  * Retrieval handler obeys schema and handles XML transfer syntax.
61  *
62  * Revision 1.29  1999/05/26 07:49:14  adam
63  * C++ compilation.
64  *
65  * Revision 1.28  1999/05/21 12:00:17  adam
66  * Better diagnostics for extraction process.
67  *
68  * Revision 1.27  1999/05/20 12:57:18  adam
69  * Implemented TCL filter. Updated recctrl system.
70  *
71  * Revision 1.26  1999/03/02 16:15:44  quinn
72  * Added "tagsysno" and "tagrank" directives to zebra.cfg.
73  *
74  * Revision 1.25  1999/02/18 15:01:26  adam
75  * Minor changes.
76  *
77  * Revision 1.24  1999/02/02 14:51:28  adam
78  * Updated WIN32 code specific sections. Changed header.
79  *
80  * Revision 1.23  1998/10/18 07:51:10  adam
81  * Changed one logf call.
82  *
83  * Revision 1.22  1998/10/16 08:14:37  adam
84  * Updated record control system.
85  *
86  * Revision 1.21  1998/07/01 09:16:10  adam
87  * Element localno only added when it's greater than 0.
88  *
89  * Revision 1.20  1998/05/20 10:12:26  adam
90  * Implemented automatic EXPLAIN database maintenance.
91  * Modified Zebra to work with ASN.1 compiled version of YAZ.
92  *
93  * Revision 1.19  1998/03/11 11:19:05  adam
94  * Changed the way sequence numbers are generated.
95  *
96  * Revision 1.18  1998/03/05 08:41:31  adam
97  * Minor changes.
98  *
99  * Revision 1.17  1998/02/10 12:03:06  adam
100  * Implemented Sort.
101  *
102  * Revision 1.16  1998/01/29 13:38:17  adam
103  * Fixed problem with mapping to record with unknown schema.
104  *
105  * Revision 1.15  1998/01/26 10:37:57  adam
106  * Better diagnostics.
107  *
108  * Revision 1.14  1997/11/06 11:41:01  adam
109  * Implemented "begin variant" for the sgml.regx filter.
110  *
111  * Revision 1.13  1997/10/31 12:35:44  adam
112  * Added a few log statements.
113  *
114  * Revision 1.12  1997/10/29 12:02:22  adam
115  * Using oid_ent_to_oid used instead of the non thread-safe oid_getoidbyent.
116  *
117  * Revision 1.11  1997/10/27 14:34:00  adam
118  * Work on generic character mapping depending on "structure" field
119  * in abstract syntax file.
120  *
121  * Revision 1.10  1997/09/18 08:59:21  adam
122  * Extra generic handle for the character mapping routines.
123  *
124  * Revision 1.9  1997/09/17 12:19:21  adam
125  * Zebra version corresponds to YAZ version 1.4.
126  * Changed Zebra server so that it doesn't depend on global common_resource.
127  *
128  * Revision 1.8  1997/09/09 13:38:14  adam
129  * Partial port to WIN95/NT.
130  *
131  * Revision 1.7  1997/09/05 15:30:10  adam
132  * Changed prototype for chr_map_input - added const.
133  * Added support for C++, headers uses extern "C" for public definitions.
134  *
135  * Revision 1.6  1997/09/04 13:54:40  adam
136  * Added MARC filter - type grs.marc.<syntax> where syntax refers
137  * to abstract syntax. New method tellf in retrieve/extract method.
138  *
139  * Revision 1.5  1997/07/15 16:29:03  adam
140  * Initialized dummy variable to keep checker gcc happy.
141  *
142  * Revision 1.4  1997/04/30 08:56:08  quinn
143  * null
144  *
145  * Revision 1.2  1996/10/11  16:06:43  quinn
146  * Revision 1.3  1997/02/24 10:41:50  adam
147  * Cleanup of code and commented out the "end element-end-record" code.
148  *
149  * Revision 1.2  1996/10/11 16:06:43  quinn
150  * Fixed arguments to nodetogr
151  *
152  * Revision 1.1  1996/10/11  10:57:25  adam
153  * New module recctrl. Used to manage records (extract/retrieval).
154  *
155  * Revision 1.29  1996/10/08 10:30:21  quinn
156  * Fixed type mismatch
157  *
158  * Revision 1.28  1996/10/07  16:06:40  quinn
159  * Added SOIF support
160  *
161  * Revision 1.27  1996/06/11  10:54:12  quinn
162  * Relevance work
163  *
164  * Revision 1.26  1996/06/06  12:08:45  quinn
165  * Added showRecord function
166  *
167  * Revision 1.25  1996/06/04  14:18:53  quinn
168  * Charmap work
169  *
170  * Revision 1.24  1996/06/04  13:27:54  quinn
171  * More work on charmapping
172  *
173  * Revision 1.23  1996/06/04  10:19:01  adam
174  * Minor changes - removed include of ctype.h.
175  *
176  * Revision 1.22  1996/06/03  10:15:27  quinn
177  * Various character-mapping.
178  *
179  * Revision 1.21  1996/05/31  13:27:24  quinn
180  * Character-conversion in phrases, too.
181  *
182  * Revision 1.19  1996/05/16  15:31:14  quinn
183  * a7
184  *
185  * Revision 1.18  1996/05/09  07:28:56  quinn
186  * Work towards phrases and multiple registers
187  *
188  * Revision 1.17  1996/05/01  13:46:37  adam
189  * First work on multiple records in one file.
190  * New option, -offset, to the "unread" command in the filter module.
191  *
192  * Revision 1.16  1996/01/17  14:57:54  adam
193  * Prototype changed for reader functions in extract/retrieve. File
194  *  is identified by 'void *' instead of 'int.
195  *
196  * Revision 1.15  1996/01/08  19:15:47  adam
197  * New input filter that works!
198  *
199  * Revision 1.14  1995/12/15  12:36:11  adam
200  * Retrieval calls data1_read_regx when subType is specified.
201  *
202  * Revision 1.13  1995/12/15  12:24:43  quinn
203  * *** empty log message ***
204  *
205  * Revision 1.12  1995/12/15  12:20:28  quinn
206  * *** empty log message ***
207  *
208  * Revision 1.11  1995/12/15  12:07:57  quinn
209  * Changed extraction strategy.
210  *
211  * Revision 1.10  1995/12/14  11:10:48  quinn
212  * Explain work
213  *
214  * Revision 1.9  1995/12/13  17:14:05  quinn
215  * *** empty log message ***
216  *
217  * Revision 1.8  1995/12/13  15:33:18  quinn
218  * *** empty log message ***
219  *
220  * Revision 1.7  1995/12/13  13:45:39  quinn
221  * Changed data1 to use nmem.
222  *
223  * Revision 1.6  1995/12/04  14:22:30  adam
224  * Extra arg to recType_byName.
225  * Started work on new regular expression parsed input to
226  * structured records.
227  *
228  * Revision 1.5  1995/11/28  14:18:37  quinn
229  * Set output_format.
230  *
231  * Revision 1.4  1995/11/21  13:14:49  quinn
232  * Fixed end-of-data-field problem (maybe).
233  *
234  * Revision 1.3  1995/11/15  19:13:09  adam
235  * Work on record management.
236  *
237  */
238
239 #include <stdio.h>
240 #include <assert.h>
241 #include <sys/types.h>
242 #ifndef WIN32
243 #include <unistd.h>
244 #endif
245
246 #include <yaz/log.h>
247 #include <yaz/oid.h>
248
249 #include <recctrl.h>
250 #include "grsread.h"
251
252 #define GRS_MAX_WORD 512
253
254 struct grs_handler {
255     RecTypeGrs type;
256     void *clientData;
257     int initFlag;
258     struct grs_handler *next;
259 };
260
261 struct grs_handlers {
262     struct grs_handler *handlers;
263 };
264
265 static int read_grs_type (struct grs_handlers *h,
266                           struct grs_read_info *p, const char *type,
267                           data1_node **root)
268 {
269     struct grs_handler *gh = h->handlers;
270     const char *cp = strchr (type, '.');
271
272     if (cp == NULL || cp == type)
273     {
274         cp = strlen(type) + type;
275         *p->type = 0;
276     }
277     else
278         strcpy (p->type, cp+1);
279     for (gh = h->handlers; gh; gh = gh->next)
280     {
281         if (!memcmp (type, gh->type->type, cp-type))
282         {
283             if (!gh->initFlag)
284             {
285                 gh->initFlag = 1;
286                 gh->clientData = (*gh->type->init)();
287             }
288             p->clientData = gh->clientData;
289             *root = (gh->type->read)(p);
290             gh->clientData = p->clientData;
291             return 0;
292         }
293     }
294     return 1;
295 }
296
297 static void grs_add_handler (struct grs_handlers *h, RecTypeGrs t)
298 {
299     struct grs_handler *gh = (struct grs_handler *) malloc (sizeof(*gh));
300     gh->next = h->handlers;
301     h->handlers = gh;
302     gh->initFlag = 0;
303     gh->clientData = 0;
304     gh->type = t;
305 }
306
307 static void *grs_init(RecType recType)
308 {
309     struct grs_handlers *h = (struct grs_handlers *) malloc (sizeof(*h));
310     h->handlers = 0;
311
312     grs_add_handler (h, recTypeGrs_sgml);
313     grs_add_handler (h, recTypeGrs_regx);
314 #if HAVE_TCL_H
315     grs_add_handler (h, recTypeGrs_tcl);
316 #endif
317     grs_add_handler (h, recTypeGrs_marc);
318     return h;
319 }
320
321 static void grs_destroy(void *clientData)
322 {
323     struct grs_handlers *h = (struct grs_handlers *) clientData;
324     struct grs_handler *gh = h->handlers, *gh_next;
325     while (gh)
326     {
327         gh_next = gh->next;
328         if (gh->initFlag)
329             (*gh->type->destroy)(gh->clientData);
330         free (gh);
331         gh = gh_next;
332     }
333     free (h);
334 }
335
336 static void index_xpath (data1_node *n, struct recExtractCtrl *p,
337                          int level, RecWord *wrd, int use)
338 {
339     int i;
340     char tag_path_full[1024];
341     size_t flen = 0;
342     data1_node *nn;
343
344     switch (n->which)
345     {
346     case DATA1N_data:
347         wrd->reg_type = 'w';
348         wrd->string = n->u.data.data;
349         wrd->length = n->u.data.len;
350         wrd->attrSet = VAL_IDXPATH,
351         wrd->attrUse = use;
352         if (p->flagShowRecords)
353         {
354             printf("%*s data=", (level + 1) * 4, "");
355             for (i = 0; i<wrd->length && i < 8; i++)
356                 fputc (wrd->string[i], stdout);
357             printf("\n");
358         }
359         else
360         {
361             (*p->tokenAdd)(wrd);
362         }
363         break;
364     case DATA1N_tag:
365         for (nn = n; nn; nn = nn->parent)
366         {
367             if (n->which == DATA1N_tag)
368             {
369                 size_t tlen = strlen(nn->u.tag.tag);
370                 if (tlen + flen > (sizeof(tag_path_full)-2))
371                     return;
372                 memcpy (tag_path_full + flen, nn->u.tag.tag, tlen);
373                 flen += tlen;
374                 tag_path_full[flen++] = '/';
375             }
376             else if (n->which == DATA1N_root)
377             {
378                 size_t tlen = strlen(nn->u.root.type);
379                 if (tlen + flen > (sizeof(tag_path_full)-2))
380                     return;
381                 memcpy (tag_path_full + flen, nn->u.root.type, tlen);
382                 flen += tlen;
383                 tag_path_full[flen++] = '/';
384                 break;
385             }
386         }
387         wrd->reg_type = '0';
388         wrd->string = tag_path_full;
389         wrd->length = flen;
390         wrd->attrSet = VAL_IDXPATH,
391         wrd->attrUse = use;
392         if (p->flagShowRecords)
393         {
394             printf("%*s tag=", (level + 1) * 4, "");
395             for (i = 0; i<wrd->length && i < 40; i++)
396                 fputc (wrd->string[i], stdout);
397             if (i == 40)
398                 printf (" ..");
399             printf("\n");
400         }
401         else
402         {
403             (*p->tokenAdd)(wrd);
404         }
405         break;
406     }
407 }
408
409 static void index_termlist (data1_node *par, data1_node *n,
410                             struct recExtractCtrl *p, int level, RecWord *wrd)
411 {
412     data1_termlist *tlist = 0;
413     data1_datatype dtype = DATA1K_string;
414     /*
415      * cycle up towards the root until we find a tag with an att..
416      * this has the effect of indexing locally defined tags with
417      * the attribute of their ancestor in the record.
418      */
419     
420     while (!par->u.tag.element)
421         if (!par->parent || !(par=get_parent_tag(p->dh, par->parent)))
422             break;
423     if (!par || !(tlist = par->u.tag.element->termlists))
424         return;
425     if (par->u.tag.element->tag)
426         dtype = par->u.tag.element->tag->kind;
427     
428     for (; tlist; tlist = tlist->next)
429     {
430         char xattr[512];
431         /* consider source */
432         wrd->string = 0;
433         
434         if (!strcmp (tlist->source, "data") && n->which == DATA1N_data)
435         {
436             wrd->string = n->u.data.data;
437             wrd->length = n->u.data.len;
438         }
439         else if (!strcmp (tlist->source, "tag") && n->which == DATA1N_tag)
440         {
441             wrd->string = n->u.tag.tag;
442             wrd->length = strlen(n->u.tag.tag);
443         }
444         else if (sscanf (tlist->source, "attr(%511[^)])", xattr) == 1 &&
445             n->which == DATA1N_tag)
446         {
447             data1_xattr *p = n->u.tag.attributes;
448             while (p && strcmp (p->name, xattr))
449                 p = p->next;
450             if (p)
451             {
452                 wrd->string = p->value;
453                 wrd->length = strlen(p->value);
454             }
455         }
456         if (wrd->string)
457         {
458             if (p->flagShowRecords)
459             {
460                 int i;
461                 printf("%*sIdx: [%s]", (level + 1) * 4, "",
462                        tlist->structure);
463                 printf("%s:%s [%d] %s",
464                        tlist->att->parent->name,
465                        tlist->att->name, tlist->att->value,
466                        tlist->source);
467                 printf (" data=\"");
468                 for (i = 0; i<wrd->length && i < 8; i++)
469                     fputc (wrd->string[i], stdout);
470                 fputc ('"', stdout);
471                 if (wrd->length > 8)
472                     printf (" ...");
473                 fputc ('\n', stdout);
474             }
475             else
476             {
477                 wrd->reg_type = *tlist->structure;
478                 wrd->attrSet = (int) (tlist->att->parent->reference);
479                 wrd->attrUse = tlist->att->locals->local;
480                 (*p->tokenAdd)(wrd);
481             }
482         }
483     }
484 }
485
486 static int dumpkeys(data1_node *n, struct recExtractCtrl *p, int level,
487                     RecWord *wrd)
488 {
489     for (; n; n = n->next)
490     {
491         if (p->flagShowRecords) /* display element description to user */
492         {
493             if (n->which == DATA1N_root)
494             {
495                 printf("%*s", level * 4, "");
496                 printf("Record type: '%s'\n", n->u.root.type);
497             }
498             else if (n->which == DATA1N_tag)
499             {
500                 data1_element *e;
501
502                 printf("%*s", level * 4, "");
503                 if (!(e = n->u.tag.element))
504                     printf("Local tag: '%s'\n", n->u.tag.tag);
505                 else
506                 {
507                     printf("Elm: '%s' ", e->name);
508                     if (e->tag)
509                     {
510                         data1_tag *t = e->tag;
511
512                         printf("TagNam: '%s' ", t->names->name);
513                         printf("(");
514                         if (t->tagset)
515                             printf("%s[%d],", t->tagset->name, t->tagset->type);
516                         else
517                             printf("?,");
518                         if (t->which == DATA1T_numeric)
519                             printf("%d)", t->value.numeric);
520                         else
521                             printf("'%s')", t->value.string);
522                     }
523                     printf("\n");
524                 }
525             }
526         }
527
528         if (n->which == DATA1N_tag)
529         {
530             index_termlist (n, n, p, level, wrd);
531             /* index start tag */
532             if (!n->root->u.root.absyn)
533                 index_xpath (n, p, level, wrd, 1);
534         }
535
536         if (n->child)
537             if (dumpkeys(n->child, p, level + 1, wrd) < 0)
538                 return -1;
539
540
541         if (n->which == DATA1N_data)
542         {
543             data1_node *par = get_parent_tag(p->dh, n);
544
545             if (p->flagShowRecords)
546             {
547                 printf("%*s", level * 4, "");
548                 printf("Data: ");
549                 if (n->u.data.len > 32)
550                     printf("'%.24s ... %.6s'\n", n->u.data.data,
551                            n->u.data.data + n->u.data.len-6);
552                 else if (n->u.data.len > 0)
553                     printf("'%.*s'\n", n->u.data.len, n->u.data.data);
554                 else
555                     printf("NULL\n");
556             }
557
558             if (par)
559                 index_termlist (par, n, p, level, wrd);
560             if (!n->root->u.root.absyn)
561                 index_xpath (n, p, level, wrd, 1016);
562
563         }
564
565         if (n->which == DATA1N_tag)
566         {
567             /* index end tag */
568             if (!n->root->u.root.absyn)
569                 index_xpath (n, p, level, wrd, 2);
570         }
571
572
573         if (p->flagShowRecords && n->which == DATA1N_root)
574         {
575             printf("%*s-------------\n\n", level * 4, "");
576         }
577     }
578     return 0;
579 }
580
581 int grs_extract_tree(struct recExtractCtrl *p, data1_node *n)
582 {
583     oident oe;
584     int oidtmp[OID_SIZE];
585     RecWord wrd;
586
587     oe.proto = PROTO_Z3950;
588     oe.oclass = CLASS_SCHEMA;
589     if (n->u.root.absyn)
590     {
591         oe.value = n->u.root.absyn->reference;
592         
593         if ((oid_ent_to_oid (&oe, oidtmp)))
594             (*p->schemaAdd)(p, oidtmp);
595     }
596     (*p->init)(p, &wrd);
597     return dumpkeys(n, p, 0, &wrd);
598 }
599
600 static int grs_extract_sub(struct grs_handlers *h, struct recExtractCtrl *p,
601                            NMEM mem)
602 {
603     data1_node *n;
604     struct grs_read_info gri;
605     oident oe;
606     int oidtmp[OID_SIZE];
607     RecWord wrd;
608
609     gri.readf = p->readf;
610     gri.seekf = p->seekf;
611     gri.tellf = p->tellf;
612     gri.endf = p->endf;
613     gri.fh = p->fh;
614     gri.offset = p->offset;
615     gri.mem = mem;
616     gri.dh = p->dh;
617
618     if (read_grs_type (h, &gri, p->subType, &n))
619         return RECCTRL_EXTRACT_ERROR;
620     if (!n)
621         return RECCTRL_EXTRACT_EOF;
622     oe.proto = PROTO_Z3950;
623     oe.oclass = CLASS_SCHEMA;
624 #if 0
625     if (!n->u.root.absyn)
626         return RECCTRL_EXTRACT_ERROR;
627 #endif
628     if (n->u.root.absyn)
629     {
630         oe.value = n->u.root.absyn->reference;
631         if ((oid_ent_to_oid (&oe, oidtmp)))
632             (*p->schemaAdd)(p, oidtmp);
633     }
634 #if 0
635     data1_pr_tree (p->dh, n, stdout);
636 #endif
637     (*p->init)(p, &wrd);
638     if (dumpkeys(n, p, 0, &wrd) < 0)
639     {
640         data1_free_tree(p->dh, n);
641         return RECCTRL_EXTRACT_ERROR;
642     }
643     data1_free_tree(p->dh, n);
644     return RECCTRL_EXTRACT_OK;
645 }
646
647 static int grs_extract(void *clientData, struct recExtractCtrl *p)
648 {
649     int ret;
650     NMEM mem = nmem_create ();
651     struct grs_handlers *h = (struct grs_handlers *) clientData;
652
653     ret = grs_extract_sub(h, p, mem);
654     nmem_destroy(mem);
655     return ret;
656 }
657
658 /*
659  * Return: -1: Nothing done. 0: Ok. >0: Bib-1 diagnostic.
660  */
661 static int process_comp(data1_handle dh, data1_node *n, Z_RecordComposition *c)
662 {
663     data1_esetname *eset;
664     Z_Espec1 *espec = 0;
665     Z_ElementSpec *p;
666
667     switch (c->which)
668     {
669     case Z_RecordComp_simple:
670         if (c->u.simple->which != Z_ElementSetNames_generic)
671             return 26; /* only generic form supported. Fix this later */
672         if (!(eset = data1_getesetbyname(dh, n->u.root.absyn,
673                                          c->u.simple->u.generic)))
674         {
675             logf(LOG_LOG, "Unknown esetname '%s'", c->u.simple->u.generic);
676             return 25; /* invalid esetname */
677         }
678         logf(LOG_DEBUG, "Esetname '%s' in simple compspec",
679              c->u.simple->u.generic);
680         espec = eset->spec;
681         break;
682     case Z_RecordComp_complex:
683         if (c->u.complex->generic)
684         {
685             /* insert check for schema */
686             if ((p = c->u.complex->generic->elementSpec))
687             {
688                 switch (p->which)
689                 {
690                 case Z_ElementSpec_elementSetName:
691                     if (!(eset =
692                           data1_getesetbyname(dh, n->u.root.absyn,
693                                               p->u.elementSetName)))
694                     {
695                         logf(LOG_LOG, "Unknown esetname '%s'",
696                              p->u.elementSetName);
697                         return 25; /* invalid esetname */
698                     }
699                     logf(LOG_DEBUG, "Esetname '%s' in complex compspec",
700                          p->u.elementSetName);
701                     espec = eset->spec;
702                     break;
703                 case Z_ElementSpec_externalSpec:
704                     if (p->u.externalSpec->which == Z_External_espec1)
705                     {
706                         logf(LOG_DEBUG, "Got Espec-1");
707                         espec = p->u.externalSpec-> u.espec1;
708                     }
709                     else
710                     {
711                         logf(LOG_LOG, "Unknown external espec.");
712                         return 25; /* bad. what is proper diagnostic? */
713                     }
714                     break;
715                 }
716             }
717         }
718         else
719             return 26; /* fix */
720     }
721     if (espec)
722     {
723         logf (LOG_DEBUG, "Element: Espec-1 match");
724         return data1_doespec1(dh, n, espec);
725     }
726     else
727     {
728         logf (LOG_DEBUG, "Element: all match");
729         return -1;
730     }
731 }
732
733 static int grs_retrieve(void *clientData, struct recRetrieveCtrl *p)
734 {
735     data1_node *node = 0, *onode = 0;
736     data1_node *dnew;
737     data1_maptab *map;
738     int res, selected = 0;
739     NMEM mem;
740     struct grs_read_info gri;
741     char *tagname;
742     struct grs_handlers *h = (struct grs_handlers *) clientData;
743     int requested_schema = VAL_NONE;
744     
745     mem = nmem_create();
746     gri.readf = p->readf;
747     gri.seekf = p->seekf;
748     gri.tellf = p->tellf;
749     gri.endf = NULL;
750     gri.fh = p->fh;
751     gri.offset = 0;
752     gri.mem = mem;
753     gri.dh = p->dh;
754
755     logf (LOG_DEBUG, "grs_retrieve");
756     if (read_grs_type (h, &gri, p->subType, &node))
757     {
758         p->diagnostic = 14;
759         nmem_destroy (mem);
760         return 0;
761     }
762     if (!node)
763     {
764         p->diagnostic = 14;
765         nmem_destroy (mem);
766         return 0;
767     }
768 #if 0
769     data1_pr_tree (p->dh, node, stdout);
770 #endif
771     logf (LOG_DEBUG, "grs_retrieve: size");
772     if ((dnew = data1_mk_tag_data_wd(p->dh, node, node,"size", mem)))
773     {
774         dnew->u.data.what = DATA1I_text;
775         dnew->u.data.data = dnew->lbuf;
776         sprintf(dnew->u.data.data, "%d", p->recordSize);
777         dnew->u.data.len = strlen(dnew->u.data.data);
778     }
779
780     tagname = res_get_def(p->res, "tagrank", "rank");
781     if (strcmp(tagname, "0") && p->score >= 0 &&
782         (dnew = data1_mk_tag_data_wd(p->dh, node, node, tagname, mem)))
783     {
784         logf (LOG_DEBUG, "grs_retrieve: %s", tagname);
785         dnew->u.data.what = DATA1I_num;
786         dnew->u.data.data = dnew->lbuf;
787         sprintf(dnew->u.data.data, "%d", p->score);
788         dnew->u.data.len = strlen(dnew->u.data.data);
789     }
790
791     tagname = res_get_def(p->res, "tagsysno", "localControlNumber");
792     if (strcmp(tagname, "0") && p->localno > 0 &&
793          (dnew = data1_mk_tag_data_wd(p->dh, node, node, tagname, mem)))
794     {
795         logf (LOG_DEBUG, "grs_retrieve: %s", tagname);
796         dnew->u.data.what = DATA1I_text;
797         dnew->u.data.data = dnew->lbuf;
798         sprintf(dnew->u.data.data, "%d", p->localno);
799         dnew->u.data.len = strlen(dnew->u.data.data);
800     }
801
802     data1_pr_tree (p->dh, node, stdout);
803
804     if (p->comp && p->comp->which == Z_RecordComp_complex &&
805         p->comp->u.complex->generic &&
806         p->comp->u.complex->generic->schema)
807     {
808         oident *oe = oid_getentbyoid (p->comp->u.complex->generic->schema);
809         if (oe)
810             requested_schema = oe->value;
811     }
812
813     /* If schema has been specified, map if possible, then check that
814      * we got the right one 
815      */
816     if (requested_schema != VAL_NONE)
817     {
818         logf (LOG_DEBUG, "grs_retrieve: schema mapping");
819         for (map = node->u.root.absyn->maptabs; map; map = map->next)
820         {
821             if (map->target_absyn_ref == requested_schema)
822             {
823                 onode = node;
824                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
825                 {
826                     p->diagnostic = 14;
827                     nmem_destroy (mem);
828                     return 0;
829                 }
830                 break;
831             }
832         }
833         if (node->u.root.absyn &&
834             requested_schema != node->u.root.absyn->reference)
835         {
836             p->diagnostic = 238;
837             nmem_destroy (mem);
838             return 0;
839         }
840     }
841     /*
842      * Does the requested format match a known syntax-mapping? (this reflects
843      * the overlap of schema and formatting which is inherent in the MARC
844      * family)
845      */
846     logf (LOG_DEBUG, "grs_retrieve: syntax mapping");
847     if (node->u.root.absyn)
848         for (map = node->u.root.absyn->maptabs; map; map = map->next)
849         {
850             if (map->target_absyn_ref == p->input_format)
851             {
852                 onode = node;
853                 if (!(node = data1_map_record(p->dh, onode, map, mem)))
854                 {
855                     p->diagnostic = 14;
856                     nmem_destroy (mem);
857                     return 0;
858                 }
859                 break;
860             }
861         }
862     logf (LOG_DEBUG, "grs_retrieve: schemaIdentifier");
863     if (node->u.root.absyn &&
864         node->u.root.absyn->reference != VAL_NONE &&
865         p->input_format == VAL_GRS1)
866     {
867         oident oe;
868         Odr_oid *oid;
869         int oidtmp[OID_SIZE];
870         
871         oe.proto = PROTO_Z3950;
872         oe.oclass = CLASS_SCHEMA;
873         oe.value = node->u.root.absyn->reference;
874         
875         if ((oid = oid_ent_to_oid (&oe, oidtmp)))
876         {
877             char tmp[128];
878             data1_handle dh = p->dh;
879             char *p = tmp;
880             int *ii;
881             
882             for (ii = oid; *ii >= 0; ii++)
883             {
884                 if (p != tmp)
885                         *(p++) = '.';
886                 sprintf(p, "%d", *ii);
887                 p += strlen(p);
888             }
889             *(p++) = '\0';
890                 
891             if ((dnew = data1_mk_tag_data_wd(dh, node, node,
892                                              "schemaIdentifier", mem)))
893             {
894                 dnew->u.data.what = DATA1I_oid;
895                 dnew->u.data.data = (char *) nmem_malloc(mem, p - tmp);
896                 memcpy(dnew->u.data.data, tmp, p - tmp);
897                 dnew->u.data.len = p - tmp;
898             }
899         }
900     }
901
902     logf (LOG_DEBUG, "grs_retrieve: element spec");
903     if (p->comp && (res = process_comp(p->dh, node, p->comp)) > 0)
904     {
905         p->diagnostic = res;
906         if (onode)
907             data1_free_tree(p->dh, onode);
908         data1_free_tree(p->dh, node);
909         nmem_destroy(mem);
910         return 0;
911     }
912     else if (p->comp && !res)
913         selected = 1;
914
915 #if 0
916     data1_pr_tree (p->dh, node, stdout);
917 #endif
918     logf (LOG_DEBUG, "grs_retrieve: transfer syntax mapping");
919     switch (p->output_format = (p->input_format != VAL_NONE ?
920                                 p->input_format : VAL_SUTRS))
921     {
922         data1_marctab *marctab;
923         int dummy;
924         
925     case VAL_TEXT_XML:
926         if (!(p->rec_buf = data1_nodetoidsgml(p->dh, node, selected,
927                                               &p->rec_len)))
928             p->diagnostic = 238;
929         else
930         {
931             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
932             memcpy (new_buf, p->rec_buf, p->rec_len);
933             p->rec_buf = new_buf;
934         }
935         break;
936     case VAL_GRS1:
937         dummy = 0;
938         if (!(p->rec_buf = data1_nodetogr(p->dh, node, selected,
939                                           p->odr, &dummy)))
940             p->diagnostic = 238; /* not available in requested syntax */
941         else
942             p->rec_len = (size_t) (-1);
943         break;
944     case VAL_EXPLAIN:
945         if (!(p->rec_buf = data1_nodetoexplain(p->dh, node, selected,
946                                                p->odr)))
947             p->diagnostic = 238;
948         else
949             p->rec_len = (size_t) (-1);
950         break;
951     case VAL_SUMMARY:
952         if (!(p->rec_buf = data1_nodetosummary(p->dh, node, selected,
953                                                p->odr)))
954             p->diagnostic = 238;
955         else
956             p->rec_len = (size_t) (-1);
957         break;
958     case VAL_SUTRS:
959         if (!(p->rec_buf = data1_nodetobuf(p->dh, node, selected,
960                                            &p->rec_len)))
961             p->diagnostic = 238;
962         else
963         {
964             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
965             memcpy (new_buf, p->rec_buf, p->rec_len);
966             p->rec_buf = new_buf;
967         }
968         break;
969     case VAL_SOIF:
970         if (!(p->rec_buf = data1_nodetosoif(p->dh, node, selected,
971                                             &p->rec_len)))
972             p->diagnostic = 238;
973         else
974         {
975             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
976             memcpy (new_buf, p->rec_buf, p->rec_len);
977             p->rec_buf = new_buf;
978         }
979         break;
980     default:
981         if (!node->u.root.absyn)
982         {
983             p->diagnostic = 238;
984             break;
985         }
986         for (marctab = node->u.root.absyn->marc; marctab;
987              marctab = marctab->next)
988             if (marctab->reference == p->input_format)
989                 break;
990         if (!marctab)
991         {
992             p->diagnostic = 238;
993             break;
994         }
995         if (!(p->rec_buf = data1_nodetomarc(p->dh, marctab, node,
996                                         selected, &p->rec_len)))
997             p->diagnostic = 238;
998         else
999         {
1000             char *new_buf = (char*) odr_malloc (p->odr, p->rec_len);
1001             memcpy (new_buf, p->rec_buf, p->rec_len);
1002                 p->rec_buf = new_buf;
1003         }
1004     }
1005     if (node)
1006         data1_free_tree(p->dh, node);
1007     if (onode)
1008         data1_free_tree(p->dh, onode);
1009     nmem_destroy(mem);
1010     return 0;
1011 }
1012
1013 static struct recType grs_type =
1014 {
1015     "grs",
1016     grs_init,
1017     grs_destroy,
1018     grs_extract,
1019     grs_retrieve
1020 };
1021
1022 RecType recTypeGrs = &grs_type;