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