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