Work on EXPLAIN schema. First implementation of sub-schema facility
[yaz-moved-to-github.git] / retrieval / d1_marc.c
1 /*
2  * Copyright (c) 1995-1997, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: d1_marc.c,v $
7  * Revision 1.10  1997-09-30 11:50:04  adam
8  * Added handler data1_get_map_buf that is used by data1_nodetomarc.
9  *
10  * Revision 1.9  1997/09/24 13:35:45  adam
11  * Added two members to data1_marctab to ease reading of weird MARC records.
12  *
13  * Revision 1.8  1997/09/17 12:10:37  adam
14  * YAZ version 1.4.
15  *
16  * Revision 1.7  1997/09/05 09:50:57  adam
17  * Removed global data1_tabpath - uses data1_get_tabpath() instead.
18  *
19  * Revision 1.6  1997/09/04 13:51:58  adam
20  * Added data1 to marc conversion with indicators.
21  *
22  * Revision 1.5  1997/09/04 13:48:04  adam
23  * Added data1 to marc conversion.
24  *
25  * Revision 1.4  1996/03/25 10:18:03  quinn
26  * Removed trailing whitespace from data elements
27  *
28  * Revision 1.3  1995/11/01  16:34:57  quinn
29  * Making data1 look for tables in data1_tabpath
30  *
31  * Revision 1.2  1995/11/01  13:54:48  quinn
32  * Minor adjustments
33  *
34  * Revision 1.1  1995/11/01  11:56:08  quinn
35  * Added Retrieval (data management) functions en masse.
36  *
37  *
38  */
39
40
41 #include <assert.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45
46 #include <oid.h>
47 #include <log.h>
48 #include <marcdisp.h>
49 #include <readconf.h>
50 #include <xmalloc.h>
51 #include <data1.h>
52 #include <tpath.h>
53
54 data1_marctab *data1_read_marctab (data1_handle dh, const char *file)
55 {
56     FILE *f;
57     NMEM mem = data1_nmem_get (dh);
58     data1_marctab *res = nmem_malloc(mem, sizeof(*res));
59     char line[512], *argv[50];
60     int argc;
61     
62     if (!(f = yaz_path_fopen(data1_get_tabpath(dh), file, "r")))
63     {
64         logf(LOG_WARN|LOG_ERRNO, "%s", file);
65         return 0;
66     }
67
68     res->name = 0;
69     res->reference = VAL_NONE;
70     res->next = 0;
71     res->length_data_entry = 4;
72     res->length_starting = 5;
73     res->length_implementation = 0;
74     strcpy(res->future_use, "4");
75
76     strcpy(res->record_status, "n");
77     strcpy(res->implementation_codes, "    ");
78     res->indicator_length = 2;
79     res->identifier_length = 2;
80     res->force_indicator_length = -1;
81     res->force_identifier_length = -1;
82     strcpy(res->user_systems, "z  ");
83
84     while ((argc = readconf_line(f, line, 512, argv, 50)))
85         if (!strcmp(argv[0], "name"))
86         {
87             if (argc != 2)
88             {
89                 logf(LOG_WARN, "%s: Bad name directive");
90                 continue;
91             }
92             res->name = nmem_strdup(mem, argv[1]);
93         }
94         else if (!strcmp(argv[0], "reference"))
95         {
96             if (argc != 2)
97             {
98                 logf(LOG_WARN, "%s: Bad name directive");
99                 continue;
100             }
101             if ((res->reference = oid_getvalbyname(argv[1])) == VAL_NONE)
102             {
103                 logf(LOG_WARN, "%s: Unknown tagset ref '%s' in %s", file,
104                     argv[1]);
105                 continue;
106             }
107         }
108         else if (!strcmp(argv[0], "length-data-entry"))
109         {
110             if (argc != 2)
111             {
112                 logf(LOG_WARN, "%s: Bad data-length-entry");
113                 continue;
114             }
115             res->length_data_entry = atoi(argv[1]);
116         }
117         else if (!strcmp(argv[0], "length-starting"))
118         {
119             if (argc != 2)
120             {
121                 logf(LOG_WARN, "%s: Bad length-starting");
122                 continue;
123             }
124             res->length_starting = atoi(argv[1]);
125         }
126         else if (!strcmp(argv[0], "length-implementation"))
127         {
128             if (argc != 2)
129             {
130                 logf(LOG_WARN, "%s: Bad length-implentation");
131                 continue;
132             }
133             res->length_implementation = atoi(argv[1]);
134         }
135         else if (!strcmp(argv[0], "future-use"))
136         {
137             if (argc != 2)
138             {
139                 logf(LOG_WARN, "%s: Bad future-use");
140                 continue;
141             }
142             strncpy(res->future_use, argv[1], 2);
143         }
144         else if (!strcmp(argv[0], "force-indicator-length"))
145         {
146             if (argc != 2)
147             {
148                 logf(LOG_WARN, "%s: Bad future-use");
149                 continue;
150             }
151             res->force_indicator_length = atoi(argv[1]);
152         }
153         else if (!strcmp(argv[0], "force-identifier-length"))
154         {
155             if (argc != 2)
156             {
157                 logf(LOG_WARN, "%s: Bad future-use");
158                 continue;
159             }
160             res->force_identifier_length = atoi(argv[1]);
161         }
162         else
163             logf(LOG_WARN, "%s: Bad directive '%s'", file, argv[0]);
164
165     fclose(f);
166     return res;
167 }
168
169 /*
170  * Locate some data under this node. This routine should handle variants
171  * prettily.
172  */
173 static char *get_data(data1_node *n, int *len)
174 {
175     char *r;
176
177     while (n->which != DATA1N_data && n->child)
178         n = n->child;
179     if (n->which != DATA1N_data || n->u.data.what != DATA1I_text)
180     {
181         r = "[Structured/included data]";
182         *len = strlen(r);
183         return r;
184     }
185
186     *len = n->u.data.len;
187     while (*len && isspace(n->u.data.data[*len - 1]))
188         (*len)--;
189     return n->u.data.data;
190 }
191
192 static void memint (char *p, int val, int len)
193 {
194     char buf[10];
195
196     if (len == 1)
197         *p = val + '0';
198     else
199     {
200         sprintf (buf, "%08d", val);
201         memcpy (p, buf+8-len, len);
202     }
203 }
204
205 static int is_indicator (data1_marctab *p, data1_node *subf)
206 {
207 #if 1
208     if (p->indicator_length != 2 ||
209         (subf->which == DATA1N_tag && strlen(subf->u.tag.tag) == 2))
210         return 1;
211 #else
212     if (subf->which == DATA1N_tag && subf->child->which == DATA1N_tag)
213         return 1;
214 #endif
215     return 0;
216 }
217
218 static int nodetomarc(data1_marctab *p, data1_node *n, int selected,
219     char **buf, int *size)
220 {
221     int len = 26;
222     int dlen;
223     int base_address = 25;
224     int entry_p, data_p;
225     char *op;
226     data1_node *field, *subf;
227
228     logf (LOG_DEBUG, "nodetomarc");
229     for (field = n->child; field; field = field->next)
230     {
231         if (field->which != DATA1N_tag)
232         {
233             logf(LOG_WARN, "Malformed field composition for marc output.");
234             return -1;
235         }
236         if (selected && !field->u.tag.node_selected)
237             continue;
238         len += 4 + p->length_data_entry + p->length_starting
239             + p->length_implementation;
240         base_address += 3 + p->length_data_entry + p->length_starting
241             + p->length_implementation;
242         if (strncmp(field->u.tag.tag, "00", 2))
243             len += p->indicator_length;      /* this is fairly bogus */
244         subf = field->child;
245         
246         /*  we'll allow no indicator if length is not 2 */
247         if (is_indicator (p, subf))
248             subf = subf->child;
249
250         for (; subf; subf = subf->next)
251         {
252             if (subf->which != DATA1N_tag)
253             {
254                 logf(LOG_WARN,
255                     "Malformed subfield composition for marc output.");
256                 return -1;
257             }
258             if (strncmp(field->u.tag.tag, "00", 2))
259                 len += p->identifier_length;
260             get_data(subf, &dlen);
261             len += dlen;
262         }
263     }
264
265     if (!*buf)
266         *buf = xmalloc(*size = len);
267     else if (*size <= len)
268         *buf = xrealloc(*buf, *size = len);
269         
270     op = *buf;
271     memint (op, len, 5);
272     memcpy (op+5, p->record_status, 1);
273     memcpy (op+6, p->implementation_codes, 4);
274     memint (op+10, p->indicator_length, 1);
275     memint (op+11, p->identifier_length, 1);
276     memint (op+12, base_address, 5);
277     memcpy (op+17, p->user_systems, 3);
278     memint (op+20, p->length_data_entry, 1);
279     memint (op+21, p->length_starting, 1);
280     memint (op+22, p->length_implementation, 1);
281     memcpy (op+23, p->future_use, 1);
282     
283     entry_p = 24;
284     data_p = base_address;
285
286     for (field = n->child; field; field = field->next)
287     {
288         int data_0 = data_p;
289         char *indicator_data = "    ";
290         if (selected && !field->u.tag.node_selected)
291             continue;
292
293         subf = field->child;
294
295         if (is_indicator (p, subf))
296         {
297             indicator_data = subf->u.tag.tag;
298             subf = subf->child;
299         }
300         if (strncmp(field->u.tag.tag, "00", 2))   /* bogus */
301         {
302             memcpy (op + data_p, indicator_data, p->indicator_length);
303             data_p += p->indicator_length;
304         }
305         for (; subf; subf = subf->next)
306         {
307             char *data;
308
309             if (strncmp(field->u.tag.tag, "00", 2))
310             {
311                 op[data_p] = ISO2709_IDFS;
312                 memcpy (op + data_p+1, subf->u.tag.tag, p->identifier_length-1);
313                 data_p += p->identifier_length;
314             }
315             data = get_data(subf, &dlen);
316             memcpy (op + data_p, data, dlen);
317             data_p += dlen;
318         }
319         op[data_p++] = ISO2709_FS;
320
321         memcpy (op + entry_p, field->u.tag.tag, 3);
322         entry_p += 3;
323         memint (op + entry_p, data_p - data_0, p->length_data_entry);
324         entry_p += p->length_data_entry;
325         memint (op + entry_p, data_0 - base_address, p->length_starting);
326         entry_p += p->length_starting;
327         entry_p += p->length_implementation;
328     }
329     op[entry_p++] = ISO2709_FS;
330     assert (entry_p == base_address);
331     op[data_p++] = ISO2709_RS;
332     assert (data_p == len);
333     return len;
334 }
335
336 char *data1_nodetomarc(data1_handle dh, data1_marctab *p, data1_node *n,
337                        int selected, int *len)
338 {
339     int *size;
340     char **buf = data1_get_map_buf (dh, &size);
341
342     *len = nodetomarc(p, n, selected, buf, size);
343     return *buf;
344 }