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