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