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