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