a9a5ed78732405d916835370284a3a4ddb817998
[idzebra-moved-to-github.git] / data1 / d1_marc.c
1 /* $Id: d1_marc.c,v 1.17 2007-01-15 15:10:14 adam Exp $
2    Copyright (C) 1995-2007
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22
23 /* converts data1 tree to ISO2709/MARC record */
24
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <yaz/log.h>
30 #include <yaz/oid.h>
31 #include <yaz/marcdisp.h>
32 #include <yaz/readconf.h>
33 #include <yaz/xmalloc.h>
34 #include <yaz/tpath.h>
35 #include <idzebra/data1.h>
36
37 data1_marctab *data1_read_marctab (data1_handle dh, const char *file)
38 {
39     FILE *f;
40     NMEM mem = data1_nmem_get (dh);
41     data1_marctab *res = (data1_marctab *)nmem_malloc(mem, sizeof(*res));
42     char line[512], *argv[50];
43     int lineno = 0;
44     int argc;
45     
46     if (!(f = data1_path_fopen(dh, file, "r")))
47         return 0;
48
49     res->name = 0;
50     res->reference = VAL_NONE;
51     res->next = 0;
52     res->length_data_entry = 4;
53     res->length_starting = 5;
54     res->length_implementation = 0;
55     strcpy(res->future_use, "4");
56
57     strcpy(res->record_status, "n");
58     strcpy(res->implementation_codes, "    ");
59     res->indicator_length = 2;
60     res->identifier_length = 2;
61     res->force_indicator_length = -1;
62     res->force_identifier_length = -1;
63     strcpy(res->user_systems, "z  ");
64     
65     while ((argc = readconf_line(f, &lineno, line, 512, argv, 50)))
66         if (!strcmp(*argv, "name"))
67         {
68             if (argc != 2)
69             {
70                 yaz_log(YLOG_WARN, "%s:%d:Missing arg for %s", file, lineno,
71                         *argv);
72                 continue;
73             }
74             res->name = nmem_strdup(mem, argv[1]);
75         }
76         else if (!strcmp(*argv, "reference"))
77         {
78             if (argc != 2)
79             {
80                 yaz_log(YLOG_WARN, "%s:%d: Missing arg for %s", file, lineno,
81                         *argv);
82                 continue;
83             }
84             if ((res->reference = oid_getvalbyname(argv[1])) == VAL_NONE)
85             {
86                 yaz_log(YLOG_WARN, "%s:%d: Unknown tagset reference '%s'",
87                         file, lineno, argv[1]);
88                 continue;
89             }
90         }
91         else if (!strcmp(*argv, "length-data-entry"))
92         {
93             if (argc != 2)
94             {
95                 yaz_log(YLOG_WARN, "%s:%d: Missing arg for %s", file, lineno,
96                         *argv);
97                 continue;
98             }
99             res->length_data_entry = atoi(argv[1]);
100         }
101         else if (!strcmp(*argv, "length-starting"))
102         {
103             if (argc != 2)
104             {
105                 yaz_log(YLOG_WARN, "%s:%d: Missing arg for %s", file, lineno,
106                         *argv);
107                 continue;
108             }
109             res->length_starting = atoi(argv[1]);
110         }
111         else if (!strcmp(*argv, "length-implementation"))
112         {
113             if (argc != 2)
114             {
115                 yaz_log(YLOG_WARN, "%s:%d: Missing arg for %s", file, lineno,
116                         *argv);
117                 continue;
118             }
119             res->length_implementation = atoi(argv[1]);
120         }
121         else if (!strcmp(*argv, "future-use"))
122         {
123             if (argc != 2)
124             {
125                 yaz_log(YLOG_WARN, "%s:%d: Missing arg for %s", file, lineno,
126                         *argv);
127                 continue;
128             }
129             strncpy(res->future_use, argv[1], 2);
130         }
131         else if (!strcmp(*argv, "force-indicator-length"))
132         {
133             if (argc != 2)
134             {
135                 yaz_log(YLOG_WARN, "%s:%d: Missing arg for %s", file, lineno,
136                         *argv);
137                 continue;
138             }
139             res->force_indicator_length = atoi(argv[1]);
140         }
141         else if (!strcmp(*argv, "force-identifier-length"))
142         {
143             if (argc != 2)
144             {
145                 yaz_log(YLOG_WARN, "%s:%d: Missing arg for %s", file, lineno,
146                         *argv);
147                 continue;
148             }
149             res->force_identifier_length = atoi(argv[1]);
150         }
151         else
152             yaz_log(YLOG_WARN, "%s:%d: Unknown directive '%s'", file, lineno,
153                     *argv);
154
155     fclose(f);
156     return res;
157 }
158
159
160 /*
161  * Locate some data under this node. This routine should handle variants
162  * prettily.
163  */
164 static char *get_data(data1_node *n, int *len, int chop)
165 {
166     char *r;
167
168     while (n)
169     {
170         if (n->which == DATA1N_data)
171         {
172             int i;
173             *len = n->u.data.len;
174             
175             if (chop)
176             {
177                 for (i = 0; i<*len; i++)
178                     if (!d1_isspace(n->u.data.data[i]))
179                         break;
180                 while (*len && d1_isspace(n->u.data.data[*len - 1]))
181                     (*len)--;
182                 *len = *len - i;
183                 if (*len > 0)
184                     return n->u.data.data + i;
185             }
186             else
187                 if (*len > 0)
188                     return n->u.data.data;
189         }
190         if (n->which == DATA1N_tag)
191             n = n->child;
192         else if (n->which == DATA1N_data)
193             n = n->next;
194         else
195             break;      
196     }
197     r = "";
198     *len = strlen(r);
199     return r;
200 }
201
202 static void memint (char *p, int val, int len)
203 {
204     char buf[10];
205
206     if (len == 1)
207         *p = val + '0';
208     else
209     {
210         sprintf (buf, "%08d", val);
211         memcpy (p, buf+8-len, len);
212     }
213 }
214
215 /* check for indicator. non MARCXML only */
216 static int is_indicator (data1_marctab *p, data1_node *subf)
217 {
218     if (p->indicator_length != 2 ||
219         (subf && subf->which == DATA1N_tag && strlen(subf->u.tag.tag) == 2))
220         return 1;
221     return 0;
222 }
223
224 static int nodetomarc(data1_handle dh,
225                       data1_marctab *p, data1_node *n, int selected,
226                       char **buf, int *size)
227 {
228     char leader[24];
229
230     int len = 26;
231     int dlen;
232     int base_address = 25;
233     int entry_p, data_p;
234     char *op;
235     data1_node *field, *subf;
236
237 #if 0
238     data1_pr_tree(dh, n, stdout);
239 #endif
240     yaz_log (YLOG_DEBUG, "nodetomarc");
241
242     memcpy (leader+5, p->record_status, 1);
243     memcpy (leader+6, p->implementation_codes, 4);
244     memint (leader+10, p->indicator_length, 1);
245     memint (leader+11, p->identifier_length, 1);
246     memcpy (leader+17, p->user_systems, 3);
247     memint (leader+20, p->length_data_entry, 1);
248     memint (leader+21, p->length_starting, 1);
249     memint (leader+22, p->length_implementation, 1);
250     memcpy (leader+23, p->future_use, 1);
251
252     for (field = n->child; field; field = field->next)
253     {
254         int control_field = 0;  /* 00X fields - usually! */
255         int marc_xml = 0;
256
257         if (field->which != DATA1N_tag)
258             continue;
259         if (selected && !field->u.tag.node_selected)
260             continue;
261             
262         subf = field->child;
263         if (!subf)
264             continue;
265         
266         if (!yaz_matchstr(field->u.tag.tag, "mc?"))
267             continue;
268         else if (!strcmp(field->u.tag.tag, "leader"))
269         {
270             int dlen = 0;
271             char *dbuf = get_data(subf, &dlen, 0);
272             if (dlen > 24)
273                 dlen = 24;
274             if (dbuf && dlen > 0)
275                 memcpy (leader, dbuf, dlen);
276             continue;
277         }
278         else if (!strcmp(field->u.tag.tag, "controlfield"))
279         {
280             control_field = 1;
281             marc_xml = 1;
282         }
283         else if (!strcmp(field->u.tag.tag, "datafield"))
284         {
285             control_field = 0;
286             marc_xml = 1;
287         }
288         else if (subf->which == DATA1N_data)
289         {
290             control_field = 1;
291             marc_xml = 0;
292         }
293         else
294         {
295             control_field = 0;
296             marc_xml = 0;
297         }
298
299         len += 4 + p->length_data_entry + p->length_starting
300             + p->length_implementation;
301         base_address += 3 + p->length_data_entry + p->length_starting
302             + p->length_implementation;
303
304         if (!control_field)
305             len += p->indicator_length;  
306
307         /* we'll allow no indicator if length is not 2 */
308         /* select when old XML format, since indicator is an element */
309         if (marc_xml == 0 && is_indicator (p, subf))
310             subf = subf->child;
311         
312         for (; subf; subf = subf->next)
313         {
314             if (!control_field)
315             {
316                 if (marc_xml && subf->which != DATA1N_tag)
317                     continue; /* we skip comments, cdata .. */
318                 len += p->identifier_length;
319             }
320             get_data(subf, &dlen, control_field ? 0 : 1);
321             len += dlen;
322         }
323     }
324
325     if (!*buf)
326         *buf = (char *)xmalloc(*size = len);
327     else if (*size <= len)
328         *buf = (char *)xrealloc(*buf, *size = len);
329         
330     op = *buf;
331
332     /* we know the base address now */
333     memint (leader+12, base_address, 5);
334
335     /* copy temp leader to real output buf op */
336     memcpy (op, leader, 24);
337     memint (op, len, 5);
338     
339     entry_p = 24;
340     data_p = base_address;
341
342     for (field = n->child; field; field = field->next)
343     {
344         int control_field = 0;
345         int marc_xml = 0;
346         const char *tag = 0;
347
348         int data_0 = data_p;
349         char indicator_data[6];
350
351         memset (indicator_data, ' ', sizeof(indicator_data)-1);
352         indicator_data[sizeof(indicator_data)-1] = '\0';
353
354         if (field->which != DATA1N_tag)
355             continue;
356
357         if (selected && !field->u.tag.node_selected)
358             continue;
359
360         subf = field->child;
361         if (!subf)
362             continue;
363         
364         if (!yaz_matchstr(field->u.tag.tag, "mc?"))
365             continue;
366         else if (!strcmp(field->u.tag.tag, "leader"))
367             continue;
368         else if (!strcmp(field->u.tag.tag, "controlfield"))
369         {
370             control_field = 1;
371             marc_xml = 1;
372         }
373         else if (!strcmp(field->u.tag.tag, "datafield"))
374         {
375             control_field = 0;
376             marc_xml = 1;
377         }
378         else if (subf->which == DATA1N_data)
379         {
380             control_field = 1;
381             marc_xml = 0;
382         }
383         else
384         {
385             control_field = 0;
386             marc_xml = 0;
387         }
388         if (marc_xml == 0 && is_indicator (p, subf))
389         {
390             strncpy(indicator_data, subf->u.tag.tag, sizeof(indicator_data)-1);
391             subf = subf->child;
392         }
393         else if (marc_xml == 1 && !control_field)
394         {
395             data1_xattr *xa;
396             for (xa = field->u.tag.attributes; xa; xa = xa->next)
397             {
398                 if (!strcmp(xa->name, "ind1"))
399                     indicator_data[0] = xa->value[0];
400                 if (!strcmp(xa->name, "ind2"))
401                     indicator_data[1] = xa->value[0];
402                 if (!strcmp(xa->name, "ind3"))
403                     indicator_data[2] = xa->value[0];
404             }
405         }
406         if (!control_field)
407         {
408             memcpy (op + data_p, indicator_data, p->indicator_length);
409             data_p += p->indicator_length;
410         }
411         for (; subf; subf = subf->next)
412         {
413             char *data;
414
415             if (!control_field)
416             {
417                 const char *identifier = "a";
418                 if (marc_xml)
419                 {
420                     data1_xattr *xa;
421                     if (subf->which != DATA1N_tag)
422                         continue;
423                     if (strcmp(subf->u.tag.tag, "subfield"))
424                         yaz_log(YLOG_WARN, "Unhandled tag %s",
425                                 subf->u.tag.tag);
426                     
427                     for (xa = subf->u.tag.attributes; xa; xa = xa->next)
428                         if (!strcmp(xa->name, "code"))
429                             identifier = xa->value;
430                 }
431                 else if (subf->which != DATA1N_tag)
432                     yaz_log(YLOG_WARN, "Malformed fields for marc output.");
433                 else
434                     identifier = subf->u.tag.tag;
435                 op[data_p] = ISO2709_IDFS;
436                 memcpy (op + data_p+1, identifier, p->identifier_length-1);
437                 data_p += p->identifier_length;
438             }
439             data = get_data(subf, &dlen, control_field ? 0 : 1);
440             memcpy (op + data_p, data, dlen);
441             data_p += dlen;
442         }
443         op[data_p++] = ISO2709_FS;
444
445         if (marc_xml)
446         {
447             data1_xattr *xa;
448             for (xa = field->u.tag.attributes; xa; xa = xa->next)
449                 if (!strcmp(xa->name, "tag"))
450                     tag = xa->value;
451         }
452         else
453             tag = field->u.tag.tag;
454
455         if (!tag || strlen(tag) != 3)
456             tag = "000";
457         memcpy (op + entry_p, tag, 3);
458         
459         entry_p += 3;
460         memint (op + entry_p, data_p - data_0, p->length_data_entry);
461         entry_p += p->length_data_entry;
462         memint (op + entry_p, data_0 - base_address, p->length_starting);
463         entry_p += p->length_starting;
464         entry_p += p->length_implementation;
465     }
466     op[entry_p++] = ISO2709_FS;
467     assert (entry_p == base_address);
468     op[data_p++] = ISO2709_RS;
469     assert (data_p == len);
470     return len;
471 }
472
473 char *data1_nodetomarc(data1_handle dh, data1_marctab *p, data1_node *n,
474                        int selected, int *len)
475 {
476     int *size;
477     char **buf = data1_get_map_buf (dh, &size);
478
479     n = data1_get_root_tag (dh, n);
480     if (!n)
481         return 0;
482     *len = nodetomarc(dh, p, n, selected, buf, size);
483     return *buf;
484 }
485 /*
486  * Local variables:
487  * c-basic-offset: 4
488  * indent-tabs-mode: nil
489  * End:
490  * vim: shiftwidth=4 tabstop=8 expandtab
491  */
492