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