f1f41791c3ef891764776b85ba8fc9a0ff78df31
[idzebra-moved-to-github.git] / recctrl / recgrs.c
1 /*
2  * Copyright (C) 1994-1998, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: recgrs.c,v $
7  * Revision 1.23  1998-10-18 07:51:10  adam
8  * Changed one logf call.
9  *
10  * Revision 1.22  1998/10/16 08:14:37  adam
11  * Updated record control system.
12  *
13  * Revision 1.21  1998/07/01 09:16:10  adam
14  * Element localno only added when it's greater than 0.
15  *
16  * Revision 1.20  1998/05/20 10:12:26  adam
17  * Implemented automatic EXPLAIN database maintenance.
18  * Modified Zebra to work with ASN.1 compiled version of YAZ.
19  *
20  * Revision 1.19  1998/03/11 11:19:05  adam
21  * Changed the way sequence numbers are generated.
22  *
23  * Revision 1.18  1998/03/05 08:41:31  adam
24  * Minor changes.
25  *
26  * Revision 1.17  1998/02/10 12:03:06  adam
27  * Implemented Sort.
28  *
29  * Revision 1.16  1998/01/29 13:38:17  adam
30  * Fixed problem with mapping to record with unknown schema.
31  *
32  * Revision 1.15  1998/01/26 10:37:57  adam
33  * Better diagnostics.
34  *
35  * Revision 1.14  1997/11/06 11:41:01  adam
36  * Implemented "begin variant" for the sgml.regx filter.
37  *
38  * Revision 1.13  1997/10/31 12:35:44  adam
39  * Added a few log statements.
40  *
41  * Revision 1.12  1997/10/29 12:02:22  adam
42  * Using oid_ent_to_oid used instead of the non thread-safe oid_getoidbyent.
43  *
44  * Revision 1.11  1997/10/27 14:34:00  adam
45  * Work on generic character mapping depending on "structure" field
46  * in abstract syntax file.
47  *
48  * Revision 1.10  1997/09/18 08:59:21  adam
49  * Extra generic handle for the character mapping routines.
50  *
51  * Revision 1.9  1997/09/17 12:19:21  adam
52  * Zebra version corresponds to YAZ version 1.4.
53  * Changed Zebra server so that it doesn't depend on global common_resource.
54  *
55  * Revision 1.8  1997/09/09 13:38:14  adam
56  * Partial port to WIN95/NT.
57  *
58  * Revision 1.7  1997/09/05 15:30:10  adam
59  * Changed prototype for chr_map_input - added const.
60  * Added support for C++, headers uses extern "C" for public definitions.
61  *
62  * Revision 1.6  1997/09/04 13:54:40  adam
63  * Added MARC filter - type grs.marc.<syntax> where syntax refers
64  * to abstract syntax. New method tellf in retrieve/extract method.
65  *
66  * Revision 1.5  1997/07/15 16:29:03  adam
67  * Initialized dummy variable to keep checker gcc happy.
68  *
69  * Revision 1.4  1997/04/30 08:56:08  quinn
70  * null
71  *
72  * Revision 1.2  1996/10/11  16:06:43  quinn
73  * Revision 1.3  1997/02/24 10:41:50  adam
74  * Cleanup of code and commented out the "end element-end-record" code.
75  *
76  * Revision 1.2  1996/10/11 16:06:43  quinn
77  * Fixed arguments to nodetogr
78  *
79  * Revision 1.1  1996/10/11  10:57:25  adam
80  * New module recctrl. Used to manage records (extract/retrieval).
81  *
82  * Revision 1.29  1996/10/08 10:30:21  quinn
83  * Fixed type mismatch
84  *
85  * Revision 1.28  1996/10/07  16:06:40  quinn
86  * Added SOIF support
87  *
88  * Revision 1.27  1996/06/11  10:54:12  quinn
89  * Relevance work
90  *
91  * Revision 1.26  1996/06/06  12:08:45  quinn
92  * Added showRecord function
93  *
94  * Revision 1.25  1996/06/04  14:18:53  quinn
95  * Charmap work
96  *
97  * Revision 1.24  1996/06/04  13:27:54  quinn
98  * More work on charmapping
99  *
100  * Revision 1.23  1996/06/04  10:19:01  adam
101  * Minor changes - removed include of ctype.h.
102  *
103  * Revision 1.22  1996/06/03  10:15:27  quinn
104  * Various character-mapping.
105  *
106  * Revision 1.21  1996/05/31  13:27:24  quinn
107  * Character-conversion in phrases, too.
108  *
109  * Revision 1.19  1996/05/16  15:31:14  quinn
110  * a7
111  *
112  * Revision 1.18  1996/05/09  07:28:56  quinn
113  * Work towards phrases and multiple registers
114  *
115  * Revision 1.17  1996/05/01  13:46:37  adam
116  * First work on multiple records in one file.
117  * New option, -offset, to the "unread" command in the filter module.
118  *
119  * Revision 1.16  1996/01/17  14:57:54  adam
120  * Prototype changed for reader functions in extract/retrieve. File
121  *  is identified by 'void *' instead of 'int.
122  *
123  * Revision 1.15  1996/01/08  19:15:47  adam
124  * New input filter that works!
125  *
126  * Revision 1.14  1995/12/15  12:36:11  adam
127  * Retrieval calls data1_read_regx when subType is specified.
128  *
129  * Revision 1.13  1995/12/15  12:24:43  quinn
130  * *** empty log message ***
131  *
132  * Revision 1.12  1995/12/15  12:20:28  quinn
133  * *** empty log message ***
134  *
135  * Revision 1.11  1995/12/15  12:07:57  quinn
136  * Changed extraction strategy.
137  *
138  * Revision 1.10  1995/12/14  11:10:48  quinn
139  * Explain work
140  *
141  * Revision 1.9  1995/12/13  17:14:05  quinn
142  * *** empty log message ***
143  *
144  * Revision 1.8  1995/12/13  15:33:18  quinn
145  * *** empty log message ***
146  *
147  * Revision 1.7  1995/12/13  13:45:39  quinn
148  * Changed data1 to use nmem.
149  *
150  * Revision 1.6  1995/12/04  14:22:30  adam
151  * Extra arg to recType_byName.
152  * Started work on new regular expression parsed input to
153  * structured records.
154  *
155  * Revision 1.5  1995/11/28  14:18:37  quinn
156  * Set output_format.
157  *
158  * Revision 1.4  1995/11/21  13:14:49  quinn
159  * Fixed end-of-data-field problem (maybe).
160  *
161  * Revision 1.3  1995/11/15  19:13:09  adam
162  * Work on record management.
163  *
164  */
165
166 #include <stdio.h>
167 #include <assert.h>
168 #include <sys/types.h>
169 #ifndef WINDOWS
170 #include <unistd.h>
171 #endif
172
173 #include <log.h>
174 #include <oid.h>
175
176 #include <recctrl.h>
177 #include "grsread.h"
178
179 #define GRS_MAX_WORD 512
180
181 static data1_node *read_grs_type (struct grs_read_info *p, const char *type)
182 {
183     static struct {
184         char *type;
185         data1_node *(*func)(struct grs_read_info *p);
186     } tab[] = {
187         { "sgml",  grs_read_sgml },
188         { "regx",  grs_read_regx },
189         { "marc",  grs_read_marc },
190         { NULL, NULL }
191     };
192     const char *cp = strchr (type, '.');
193     int i;
194
195     if (cp == NULL || cp == type)
196     {
197         cp = strlen(type) + type;
198         *p->type = 0;
199     }
200     else
201         strcpy (p->type, cp+1);
202     for (i=0; tab[i].type; i++)
203     {
204         if (!memcmp (type, tab[i].type, cp-type))
205             return (tab[i].func)(p);
206     }
207     return NULL;
208 }
209
210 static void grs_init(RecType recType)
211 {
212 }
213
214 static void grs_destroy(RecType recType)
215 {
216 }
217
218 static int dumpkeys(data1_node *n, struct recExtractCtrl *p, int level)
219 {
220     RecWord wrd;
221     (*p->init)(p, &wrd);      /* set defaults */
222     for (; n; n = n->next)
223     {
224         if (p->flagShowRecords) /* display element description to user */
225         {
226             if (n->which == DATA1N_root)
227             {
228                 printf("%*s", level * 4, "");
229                 printf("Record type: '%s'\n", n->u.root.absyn->name);
230             }
231             else if (n->which == DATA1N_tag)
232             {
233                 data1_element *e;
234
235                 printf("%*s", level * 4, "");
236                 if (!(e = n->u.tag.element))
237                     printf("Local tag: '%s'\n", n->u.tag.tag);
238                 else
239                 {
240                     printf("Elm: '%s' ", e->name);
241                     if (e->tag)
242                     {
243                         data1_tag *t = e->tag;
244
245                         printf("TagNam: '%s' ", t->names->name);
246                         printf("(");
247                         if (t->tagset)
248                             printf("%s[%d],", t->tagset->name, t->tagset->type);
249                         else
250                             printf("?,");
251                         if (t->which == DATA1T_numeric)
252                             printf("%d)", t->value.numeric);
253                         else
254                             printf("'%s')", t->value.string);
255                     }
256                     printf("\n");
257                 }
258             }
259         }
260
261         if (n->child)
262             if (dumpkeys(n->child, p, level + 1) < 0)
263                 return -1;
264
265         if (n->which == DATA1N_data)
266         {
267             data1_node *par = get_parent_tag(p->dh, n);
268             data1_termlist *tlist = 0;
269             data1_datatype dtype = DATA1K_string;
270
271             if (p->flagShowRecords)
272             {
273                 printf("%*s", level * 4, "");
274                 printf("Data: ");
275                 if (n->u.data.len > 32)
276                     printf("'%.24s ... %.6s'\n", n->u.data.data,
277                            n->u.data.data + n->u.data.len-6);
278                 else if (n->u.data.len > 0)
279                     printf("'%.*s'\n", n->u.data.len, n->u.data.data);
280                 else
281                     printf("NULL\n");
282             }
283
284             assert(par);
285
286             /*
287              * cycle up towards the root until we find a tag with an att..
288              * this has the effect of indexing locally defined tags with
289              * the attribute of their ancestor in the record.
290              */
291
292             while (!par->u.tag.element)
293                 if (!par->parent || !(par=get_parent_tag(p->dh, par->parent)))
294                     break;
295             if (!par || !(tlist = par->u.tag.element->termlists))
296                 continue;
297             if (par->u.tag.element->tag)
298                 dtype = par->u.tag.element->tag->kind;
299             for (; tlist; tlist = tlist->next)
300             {
301                 if (p->flagShowRecords)
302                 {
303                     printf("%*sIdx: [%s]", (level + 1) * 4, "",
304                            tlist->structure);
305                     printf("%s:%s [%d]\n",
306                            tlist->att->parent->name,
307                            tlist->att->name, tlist->att->value);
308                 }
309                 else
310                 {
311                     wrd.reg_type = *tlist->structure;
312                     wrd.string = n->u.data.data;
313                     wrd.length = n->u.data.len;
314                     wrd.attrSet = (int) (tlist->att->parent->reference);
315                     wrd.attrUse = tlist->att->locals->local;
316                     (*p->addWord)(&wrd);
317                 }
318             }
319         }
320         if (p->flagShowRecords && n->which == DATA1N_root)
321         {
322             printf("%*s-------------\n\n", level * 4, "");
323         }
324     }
325     return 0;
326 }
327
328 int grs_extract_tree(struct recExtractCtrl *p, data1_node *n)
329 {
330     oident oe;
331     int oidtmp[OID_SIZE];
332
333     oe.proto = PROTO_Z3950;
334     oe.oclass = CLASS_SCHEMA;
335     oe.value = n->u.root.absyn->reference;
336
337     if ((oid_ent_to_oid (&oe, oidtmp)))
338         (*p->addSchema)(p, oidtmp);
339
340     return dumpkeys(n, p, 0);
341 }
342
343 static int grs_extract(struct recExtractCtrl *p)
344 {
345     data1_node *n;
346     NMEM mem;
347     struct grs_read_info gri;
348     oident oe;
349     int oidtmp[OID_SIZE];
350
351     mem = nmem_create (); 
352     gri.readf = p->readf;
353     gri.seekf = p->seekf;
354     gri.tellf = p->tellf;
355     gri.endf = p->endf;
356     gri.fh = p->fh;
357     gri.offset = p->offset;
358     gri.mem = mem;
359     gri.dh = p->dh;
360
361     n = read_grs_type (&gri, p->subType);
362     if (!n)
363         return -1;
364
365     oe.proto = PROTO_Z3950;
366     oe.oclass = CLASS_SCHEMA;
367     oe.value = n->u.root.absyn->reference;
368     if ((oid_ent_to_oid (&oe, oidtmp)))
369         (*p->addSchema)(p, oidtmp);
370
371     if (dumpkeys(n, p, 0) < 0)
372     {
373         data1_free_tree(p->dh, n);
374         return -2;
375     }
376     data1_free_tree(p->dh, n);
377     nmem_destroy(mem);
378     return 0;
379 }
380
381 /*
382  * Return: -1: Nothing done. 0: Ok. >0: Bib-1 diagnostic.
383  */
384 static int process_comp(data1_handle dh, data1_node *n, Z_RecordComposition *c)
385 {
386     data1_esetname *eset;
387     Z_Espec1 *espec = 0;
388     Z_ElementSpec *p;
389
390     switch (c->which)
391     {
392         case Z_RecordComp_simple:
393             if (c->u.simple->which != Z_ElementSetNames_generic)
394                 return 26; /* only generic form supported. Fix this later */
395             if (!(eset = data1_getesetbyname(dh, n->u.root.absyn,
396                 c->u.simple->u.generic)))
397             {
398                 logf(LOG_LOG, "Unknown esetname '%s'", c->u.simple->u.generic);
399                 return 25; /* invalid esetname */
400             }
401             logf(LOG_DEBUG, "Esetname '%s' in simple compspec",
402                 c->u.simple->u.generic);
403             espec = eset->spec;
404             break;
405         case Z_RecordComp_complex:
406             if (c->u.complex->generic)
407             {
408                 /* insert check for schema */
409                 if ((p = c->u.complex->generic->elementSpec))
410                     switch (p->which)
411                     {
412                         case Z_ElementSpec_elementSetName:
413                             if (!(eset =
414                                   data1_getesetbyname(dh,
415                                                       n->u.root.absyn,
416                                                       p->u.elementSetName)))
417                             {
418                                 logf(LOG_LOG, "Unknown esetname '%s'",
419                                     p->u.elementSetName);
420                                 return 25; /* invalid esetname */
421                             }
422                             logf(LOG_DEBUG, "Esetname '%s' in complex compspec",
423                                 p->u.elementSetName);
424                             espec = eset->spec;
425                             break;
426                         case Z_ElementSpec_externalSpec:
427                             if (p->u.externalSpec->which == Z_External_espec1)
428                             {
429                                 logf(LOG_DEBUG, "Got Espec-1");
430                                 espec = p->u.externalSpec-> u.espec1;
431                             }
432                             else
433                             {
434                                 logf(LOG_LOG, "Unknown external espec.");
435                                 return 25; /* bad. what is proper diagnostic? */
436                             }
437                             break;
438                     }
439             }
440             else
441                 return 26; /* fix */
442     }
443     if (espec)
444     {
445         logf (LOG_DEBUG, "Element: Espec-1 match");
446         return data1_doespec1(dh, n, espec);
447     }
448     else
449     {
450         logf (LOG_DEBUG, "Element: all match");
451         return -1;
452     }
453 }
454
455 static int grs_retrieve(struct recRetrieveCtrl *p)
456 {
457     data1_node *node = 0, *onode = 0;
458     data1_node *dnew;
459     data1_maptab *map;
460     int res, selected = 0;
461     NMEM mem;
462     struct grs_read_info gri;
463     
464     mem = nmem_create();
465     gri.readf = p->readf;
466     gri.seekf = p->seekf;
467     gri.tellf = p->tellf;
468     gri.endf = NULL;
469     gri.fh = p->fh;
470     gri.offset = 0;
471     gri.mem = mem;
472     gri.dh = p->dh;
473
474     logf (LOG_DEBUG, "grs_retrieve");
475     node = read_grs_type (&gri, p->subType);
476     if (!node)
477     {
478         p->diagnostic = 14;
479         nmem_destroy (mem);
480         return 0;
481     }
482     logf (LOG_DEBUG, "grs_retrieve: size");
483     if ((dnew = data1_insert_taggeddata(p->dh, node, node,
484                                        "size", mem)))
485     {
486         dnew->u.data.what = DATA1I_text;
487         dnew->u.data.data = dnew->lbuf;
488         sprintf(dnew->u.data.data, "%d", p->recordSize);
489         dnew->u.data.len = strlen(dnew->u.data.data);
490     }
491
492     logf (LOG_DEBUG, "grs_retrieve: score");
493     if (p->score >= 0 && (dnew =
494                           data1_insert_taggeddata(p->dh, node,
495                                                   node, "rank",
496                                                   mem)))
497     {
498         dnew->u.data.what = DATA1I_num;
499         dnew->u.data.data = dnew->lbuf;
500         sprintf(dnew->u.data.data, "%d", p->score);
501         dnew->u.data.len = strlen(dnew->u.data.data);
502     }
503
504     logf (LOG_DEBUG, "grs_retrieve: localControlNumber");
505     if (p->localno > 0 && (dnew = data1_insert_taggeddata(p->dh, node, node,
506                                        "localControlNumber", mem)))
507     {
508         dnew->u.data.what = DATA1I_text;
509         dnew->u.data.data = dnew->lbuf;
510         sprintf(dnew->u.data.data, "%d", p->localno);
511         dnew->u.data.len = strlen(dnew->u.data.data);
512     }
513
514     logf (LOG_DEBUG, "grs_retrieve: schemaIdentifier");
515     if (p->input_format == VAL_GRS1 && node->u.root.absyn &&
516         node->u.root.absyn->reference != VAL_NONE)
517     {
518         oident oe;
519         Odr_oid *oid;
520         int oidtmp[OID_SIZE];
521
522         oe.proto = PROTO_Z3950;
523         oe.oclass = CLASS_SCHEMA;
524         oe.value = node->u.root.absyn->reference;
525
526         if ((oid = oid_ent_to_oid (&oe, oidtmp)))
527         {
528             char tmp[128];
529             data1_handle dh = p->dh;
530             char *p = tmp;
531             int *ii;
532
533             for (ii = oid; *ii >= 0; ii++)
534             {
535                 if (p != tmp)
536                     *(p++) = '.';
537                 sprintf(p, "%d", *ii);
538                 p += strlen(p);
539             }
540             *(p++) = '\0';
541
542             if ((dnew = data1_insert_taggeddata(dh, node, node,
543                                                "schemaIdentifier", mem)))
544             {
545                 dnew->u.data.what = DATA1I_oid;
546                 dnew->u.data.data = nmem_malloc(mem, p - tmp);
547                 memcpy(dnew->u.data.data, tmp, p - tmp);
548                 dnew->u.data.len = p - tmp;
549             }
550         }
551     }
552
553     logf (LOG_DEBUG, "grs_retrieve: schema mapping");
554     /*
555      * Does the requested format match a known schema-mapping? (this reflects
556      * the overlap of schema and formatting which is inherent in the MARC
557      * family)
558      * NOTE: This should look at the schema-specification in the compspec
559      * as well.
560      */
561     for (map = node->u.root.absyn->maptabs; map; map = map->next)
562         if (map->target_absyn_ref == p->input_format)
563         {
564             onode = node;
565             if (!(node = data1_map_record(p->dh, onode, map, mem)))
566             {
567                 p->diagnostic = 14;
568                 nmem_destroy (mem);
569                 return 0;
570             }
571             break;
572         }
573
574     logf (LOG_DEBUG, "grs_retrieve: element spec");
575     if (p->comp && (res = process_comp(p->dh, node, p->comp)) > 0)
576     {
577         p->diagnostic = res;
578         if (onode)
579             data1_free_tree(p->dh, onode);
580         data1_free_tree(p->dh, node);
581         nmem_destroy(mem);
582         return 0;
583     }
584     else if (p->comp && !res)
585         selected = 1;
586
587     logf (LOG_DEBUG, "grs_retrieve: transfer syntax mapping");
588     switch (p->output_format = (p->input_format != VAL_NONE ?
589         p->input_format : VAL_SUTRS))
590     {
591         data1_marctab *marctab;
592         int dummy;
593
594         case VAL_GRS1:
595             dummy = 0;
596             if (!(p->rec_buf = data1_nodetogr(p->dh, node, selected,
597                                               p->odr, &dummy)))
598                 p->diagnostic = 238; /* not available in requested syntax */
599             else
600                 p->rec_len = -1;
601             break;
602         case VAL_EXPLAIN:
603             if (!(p->rec_buf = data1_nodetoexplain(p->dh, node, selected,
604                                                    p->odr)))
605                 p->diagnostic = 238;
606             else
607                 p->rec_len = -1;
608             break;
609         case VAL_SUMMARY:
610             if (!(p->rec_buf = data1_nodetosummary(p->dh, node, selected,
611                                                    p->odr)))
612                 p->diagnostic = 238;
613             else
614                 p->rec_len = -1;
615             break;
616         case VAL_SUTRS:
617             if (!(p->rec_buf = data1_nodetobuf(p->dh, node, selected,
618                 (int*)&p->rec_len)))
619                 p->diagnostic = 238;
620             break;
621         case VAL_SOIF:
622             if (!(p->rec_buf = data1_nodetosoif(p->dh, node, selected,
623                                                 (int*)&p->rec_len)))
624                 p->diagnostic = 238;
625             break;
626         default:
627             if (!node->u.root.absyn)
628             {
629                 p->diagnostic = 238;
630                 break;
631             }
632             for (marctab = node->u.root.absyn->marc; marctab;
633                 marctab = marctab->next)
634                 if (marctab->reference == p->input_format)
635                     break;
636             if (!marctab)
637             {
638                 p->diagnostic = 238;
639                 break;
640             }
641             if (!(p->rec_buf = data1_nodetomarc(p->dh, marctab, node,
642                                                 selected,
643                                                 (int*)&p->rec_len)))
644             {
645                 p->diagnostic = 238;
646                 break;
647             }
648     }
649     if (node)
650         data1_free_tree(p->dh, node);
651     if (onode)
652         data1_free_tree(p->dh, onode);
653     nmem_destroy(mem);
654     return 0;
655 }
656
657 static struct recType grs_type =
658 {
659     "grs",
660     grs_init,
661     grs_destroy,
662     grs_extract,
663     grs_retrieve
664 };
665
666 RecType recTypeGrs = &grs_type;