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