6c8a603f21ad107f2090283cf1e738bd66ba5283
[yaz-moved-to-github.git] / src / marcdisp.c
1 /*
2  * Copyright (C) 1995-2006, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: marcdisp.c,v 1.36 2006-12-07 11:08:05 adam Exp $
6  */
7
8 /**
9  * \file marcdisp.c
10  * \brief Implements MARC conversion utilities
11  */
12
13 #if HAVE_CONFIG_H
14 #include <config.h>
15 #endif
16
17 #ifdef WIN32
18 #include <windows.h>
19 #endif
20
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <yaz/marcdisp.h>
26 #include <yaz/wrbuf.h>
27 #include <yaz/yaz-util.h>
28 #include <yaz/nmem_xml.h>
29
30 #if YAZ_HAVE_XML2
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33 #endif
34
35 static void yaz_marc_reset(yaz_marc_t mt);
36
37 /** \brief node types for yaz_marc_node */
38 enum YAZ_MARC_NODE_TYPE
39
40     YAZ_MARC_DATAFIELD,
41     YAZ_MARC_CONTROLFIELD,
42     YAZ_MARC_COMMENT,
43     YAZ_MARC_LEADER
44 };
45
46 /** \brief represets a data field */
47 struct yaz_marc_datafield {
48     char *tag;
49     char *indicator;
50     struct yaz_marc_subfield *subfields;
51 };
52
53 /** \brief represents a control field */
54 struct yaz_marc_controlfield {
55     char *tag;
56     char *data;
57 };
58
59 /** \brief a comment node */
60 struct yaz_marc_comment {
61     char *comment;
62 };
63
64 /** \brief MARC node */
65 struct yaz_marc_node {
66     enum YAZ_MARC_NODE_TYPE which;
67     union {
68         struct yaz_marc_datafield datafield;
69         struct yaz_marc_controlfield controlfield;
70         char *comment;
71         char *leader;
72     } u;
73     struct yaz_marc_node *next;
74 };
75
76 /** \brief represents a subfield */
77 struct yaz_marc_subfield {
78     char *code_data;
79     struct yaz_marc_subfield *next;
80 };
81
82 /** \brief the internals of a yaz_marc_t handle */
83 struct yaz_marc_t_ {
84     WRBUF m_wr;
85     NMEM nmem;
86     int xml;
87     int debug;
88     yaz_iconv_t iconv_cd;
89     char subfield_str[8];
90     char endline_str[8];
91     char *leader_spec;
92     struct yaz_marc_node *nodes;
93     struct yaz_marc_node **nodes_pp;
94     struct yaz_marc_subfield **subfield_pp;
95 };
96
97 yaz_marc_t yaz_marc_create(void)
98 {
99     yaz_marc_t mt = (yaz_marc_t) xmalloc(sizeof(*mt));
100     mt->xml = YAZ_MARC_LINE;
101     mt->debug = 0;
102     mt->m_wr = wrbuf_alloc();
103     mt->iconv_cd = 0;
104     mt->leader_spec = 0;
105     strcpy(mt->subfield_str, " $");
106     strcpy(mt->endline_str, "\n");
107
108     mt->nmem = nmem_create();
109     yaz_marc_reset(mt);
110     return mt;
111 }
112
113 void yaz_marc_destroy(yaz_marc_t mt)
114 {
115     if (!mt)
116         return ;
117     nmem_destroy(mt->nmem);
118     wrbuf_free(mt->m_wr, 1);
119     xfree(mt->leader_spec);
120     xfree(mt);
121 }
122
123 static int marc_exec_leader(const char *leader_spec, char *leader,
124                             size_t size);
125
126
127 struct yaz_marc_node *yaz_marc_add_node(yaz_marc_t mt)
128 {
129     struct yaz_marc_node *n = nmem_malloc(mt->nmem, sizeof(*n));
130     n->next = 0;
131     *mt->nodes_pp = n;
132     mt->nodes_pp = &n->next;
133     return n;
134 }
135
136 void yaz_marc_add_comment(yaz_marc_t mt, char *comment)
137 {
138     struct yaz_marc_node *n = yaz_marc_add_node(mt);
139     n->which = YAZ_MARC_COMMENT;
140     n->u.comment = nmem_strdup(mt->nmem, comment);
141 }
142
143 void yaz_marc_cprintf(yaz_marc_t mt, const char *fmt, ...)
144 {
145     va_list ap;
146     char buf[200];
147     va_start(ap, fmt);
148
149 #ifdef WIN32
150     _vsnprintf(buf, sizeof(buf)-1, fmt, ap);
151 #else
152 /* !WIN32 */
153 #if HAVE_VSNPRINTF
154     vsnprintf(buf, sizeof(buf), fmt, ap);
155 #else
156     vsprintf(buf, fmt, ap);
157 #endif
158 #endif
159 /* WIN32 */
160     yaz_marc_add_comment(mt, buf);
161     va_end (ap);
162 }
163
164 void yaz_marc_add_leader(yaz_marc_t mt, const char *leader, size_t leader_len)
165 {
166     struct yaz_marc_node *n = yaz_marc_add_node(mt);
167     n->which = YAZ_MARC_LEADER;
168     n->u.leader = nmem_strdupn(mt->nmem, leader, leader_len);
169     marc_exec_leader(mt->leader_spec, n->u.leader, leader_len);
170 }
171
172 void yaz_marc_add_controlfield(yaz_marc_t mt, const char *tag,
173                                const char *data, size_t data_len)
174 {
175     struct yaz_marc_node *n = yaz_marc_add_node(mt);
176     n->which = YAZ_MARC_CONTROLFIELD;
177     n->u.controlfield.tag = nmem_strdup(mt->nmem, tag);
178     n->u.controlfield.data = nmem_strdupn(mt->nmem, data, data_len);
179     if (mt->debug)
180     {
181         size_t i;
182         char msg[80];
183
184         sprintf(msg, "controlfield:");
185         for (i = 0; i < 16 && i < data_len; i++)
186             sprintf(msg + strlen(msg), " %02X", data[i] & 0xff);
187         if (i < data_len)
188             sprintf(msg + strlen(msg), " ..");
189         yaz_marc_add_comment(mt, msg);
190     }
191 }
192
193 #if YAZ_HAVE_XML2
194 void yaz_marc_add_controlfield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
195                                    const xmlNode *ptr_data)
196 {
197     struct yaz_marc_node *n = yaz_marc_add_node(mt);
198     n->which = YAZ_MARC_CONTROLFIELD;
199     n->u.controlfield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
200     n->u.controlfield.data = nmem_text_node_cdata(ptr_data, mt->nmem);
201 }
202 #endif
203
204 void yaz_marc_add_datafield(yaz_marc_t mt, const char *tag,
205                             const char *indicator, size_t indicator_len)
206 {
207     struct yaz_marc_node *n = yaz_marc_add_node(mt);
208     n->which = YAZ_MARC_DATAFIELD;
209     n->u.datafield.tag = nmem_strdup(mt->nmem, tag);
210     n->u.datafield.indicator =
211         nmem_strdupn(mt->nmem, indicator, indicator_len);
212     n->u.datafield.subfields = 0;
213
214     /* make subfield_pp the current (last one) */
215     mt->subfield_pp = &n->u.datafield.subfields;
216 }
217
218 #if YAZ_HAVE_XML2
219 void yaz_marc_add_datafield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
220                                 const char *indicator, size_t indicator_len)
221 {
222     struct yaz_marc_node *n = yaz_marc_add_node(mt);
223     n->which = YAZ_MARC_DATAFIELD;
224     n->u.datafield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
225     n->u.datafield.indicator =
226         nmem_strdupn(mt->nmem, indicator, indicator_len);
227     n->u.datafield.subfields = 0;
228
229     /* make subfield_pp the current (last one) */
230     mt->subfield_pp = &n->u.datafield.subfields;
231 }
232 #endif
233
234 void yaz_marc_add_subfield(yaz_marc_t mt,
235                            const char *code_data, size_t code_data_len)
236 {
237     if (mt->debug)
238     {
239         size_t i;
240         char msg[80];
241
242         sprintf(msg, "subfield:");
243         for (i = 0; i < 16 && i < code_data_len; i++)
244             sprintf(msg + strlen(msg), " %02X", code_data[i] & 0xff);
245         if (i < code_data_len)
246             sprintf(msg + strlen(msg), " ..");
247         yaz_marc_add_comment(mt, msg);
248     }
249
250     if (mt->subfield_pp)
251     {
252         struct yaz_marc_subfield *n = nmem_malloc(mt->nmem, sizeof(*n));
253         n->code_data = nmem_strdupn(mt->nmem, code_data, code_data_len);
254         n->next = 0;
255         /* mark subfield_pp to point to this one, so we append here next */
256         *mt->subfield_pp = n;
257         mt->subfield_pp = &n->next;
258     }
259 }
260
261 static int atoi_n_check(const char *buf, int size, int *val)
262 {
263     if (!isdigit(*(const unsigned char *) buf))
264         return 0;
265     *val = atoi_n(buf, size);
266     return 1;
267 }
268
269 /** \brief reads the MARC 24 bytes leader and checks content
270     \param mt handle
271     \param leader of the 24 byte leader
272     \param indicator_length indicator length
273     \param identifier_length identifier length
274     \param base_address base address
275     \param length_data_entry length of data entry
276     \param length_starting length of starting 
277     \param length_implementation length of implementation defined data
278 */
279 static void yaz_marc_read_leader(yaz_marc_t mt, const char *leader_c,
280                                  int *indicator_length,
281                                  int *identifier_length,
282                                  int *base_address,
283                                  int *length_data_entry,
284                                  int *length_starting,
285                                  int *length_implementation)
286 {
287     char leader[24];
288
289     memcpy(leader, leader_c, 24);
290
291     if (!atoi_n_check(leader+10, 1, indicator_length))
292     {
293         yaz_marc_cprintf(mt, 
294                          "Indicator length at offset 10 should hold a digit."
295                          " Assuming 2");
296         leader[10] = '2';
297         *indicator_length = 2;
298     }
299     if (!atoi_n_check(leader+11, 1, identifier_length))
300     {
301         yaz_marc_cprintf(mt, 
302                          "Identifier length at offset 11 should hold a digit."
303                          " Assuming 2");
304         leader[11] = '2';
305         *identifier_length = 2;
306     }
307     if (!atoi_n_check(leader+12, 5, base_address))
308     {
309         yaz_marc_cprintf(mt, 
310                          "Base address at offsets 12..16 should hold a number."
311                          " Assuming 0");
312         *base_address = 0;
313     }
314     if (!atoi_n_check(leader+20, 1, length_data_entry))
315     {
316         yaz_marc_cprintf(mt, 
317                          "Length data entry at offset 20 should hold a digit."
318                          " Assuming 4");
319         *length_data_entry = 4;
320         leader[20] = '4';
321     }
322     if (!atoi_n_check(leader+21, 1, length_starting))
323     {
324         yaz_marc_cprintf(mt,
325                          "Length starting at offset 21 should hold a digit."
326                          " Assuming 5");
327         *length_starting = 5;
328         leader[21] = '5';
329     }
330     if (!atoi_n_check(leader+22, 1, length_implementation))
331     {
332         yaz_marc_cprintf(mt, 
333                          "Length implementation at offset 22 should hold a digit."
334                          " Assuming 0");
335         *length_implementation = 0;
336         leader[22] = '0';
337     }
338
339     if (mt->debug)
340     {
341         yaz_marc_cprintf(mt, "Indicator length      %5d", *indicator_length);
342         yaz_marc_cprintf(mt, "Identifier length     %5d", *identifier_length);
343         yaz_marc_cprintf(mt, "Base address          %5d", *base_address);
344         yaz_marc_cprintf(mt, "Length data entry     %5d", *length_data_entry);
345         yaz_marc_cprintf(mt, "Length starting       %5d", *length_starting);
346         yaz_marc_cprintf(mt, "Length implementation %5d", *length_implementation);
347     }
348     yaz_marc_add_leader(mt, leader, 24);
349 }
350
351 void yaz_marc_subfield_str(yaz_marc_t mt, const char *s)
352 {
353     strncpy(mt->subfield_str, s, sizeof(mt->subfield_str)-1);
354     mt->subfield_str[sizeof(mt->subfield_str)-1] = '\0';
355 }
356
357 void yaz_marc_endline_str(yaz_marc_t mt, const char *s)
358 {
359     strncpy(mt->endline_str, s, sizeof(mt->endline_str)-1);
360     mt->endline_str[sizeof(mt->endline_str)-1] = '\0';
361 }
362
363 /* try to guess how many bytes the identifier really is! */
364 static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
365 {
366     if (mt->iconv_cd)
367     {
368         size_t i;
369         for (i = 1; i<5; i++)
370         {
371             char outbuf[12];
372             size_t outbytesleft = sizeof(outbuf);
373             char *outp = outbuf;
374             const char *inp = buf;
375
376             size_t inbytesleft = i;
377             size_t r = yaz_iconv(mt->iconv_cd, (char**) &inp, &inbytesleft,
378                                  &outp, &outbytesleft);
379             if (r != (size_t) (-1))
380                 return i;  /* got a complete sequence */
381         }
382         return 1; /* giving up */
383     }
384     return 1; /* we don't know */
385 }
386                               
387 static void yaz_marc_reset(yaz_marc_t mt)
388 {
389     nmem_reset(mt->nmem);
390     mt->nodes = 0;
391     mt->nodes_pp = &mt->nodes;
392     mt->subfield_pp = 0;
393 }
394
395 int yaz_marc_write_check(yaz_marc_t mt, WRBUF wr)
396 {
397     struct yaz_marc_node *n;
398     int identifier_length;
399     const char *leader = 0;
400
401     for (n = mt->nodes; n; n = n->next)
402         if (n->which == YAZ_MARC_LEADER)
403         {
404             leader = n->u.leader;
405             break;
406         }
407     
408     if (!leader)
409         return -1;
410     if (!atoi_n_check(leader+11, 1, &identifier_length))
411         return -1;
412
413     for (n = mt->nodes; n; n = n->next)
414     {
415         switch(n->which)
416         {
417         case YAZ_MARC_COMMENT:
418             wrbuf_iconv_write(wr, mt->iconv_cd, 
419                               n->u.comment, strlen(n->u.comment));
420             wrbuf_puts(wr, ")\n");
421             break;
422         default:
423             break;
424         }
425     }
426     return 0;
427 }
428
429
430 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
431 {
432     struct yaz_marc_node *n;
433     int identifier_length;
434     const char *leader = 0;
435
436     for (n = mt->nodes; n; n = n->next)
437         if (n->which == YAZ_MARC_LEADER)
438         {
439             leader = n->u.leader;
440             break;
441         }
442     
443     if (!leader)
444         return -1;
445     if (!atoi_n_check(leader+11, 1, &identifier_length))
446         return -1;
447
448     for (n = mt->nodes; n; n = n->next)
449     {
450         struct yaz_marc_subfield *s;
451         switch(n->which)
452         {
453         case YAZ_MARC_DATAFIELD:
454             wrbuf_printf(wr, "%s %s", n->u.datafield.tag,
455                          n->u.datafield.indicator);
456             for (s = n->u.datafield.subfields; s; s = s->next)
457             {
458                 /* if identifier length is 2 (most MARCs),
459                    the code is a single character .. However we've
460                    seen multibyte codes, so see how big it really is */
461                 size_t using_code_len = 
462                     (identifier_length != 2) ? identifier_length - 1
463                     :
464                     cdata_one_character(mt, s->code_data);
465                 
466                 wrbuf_puts (wr, mt->subfield_str); 
467                 wrbuf_iconv_write(wr, mt->iconv_cd, s->code_data, 
468                                   using_code_len);
469                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
470                 wrbuf_iconv_puts(wr, mt->iconv_cd, 
471                                  s->code_data + using_code_len);
472                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
473                 wr->pos--;
474             }
475             wrbuf_puts (wr, mt->endline_str);
476             break;
477         case YAZ_MARC_CONTROLFIELD:
478             wrbuf_printf(wr, "%s", n->u.controlfield.tag);
479             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
480             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
481             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
482             wr->pos--;
483             wrbuf_puts (wr, mt->endline_str);
484             break;
485         case YAZ_MARC_COMMENT:
486             wrbuf_puts(wr, "(");
487             wrbuf_iconv_write(wr, mt->iconv_cd, 
488                               n->u.comment, strlen(n->u.comment));
489             wrbuf_puts(wr, ")\n");
490             break;
491         case YAZ_MARC_LEADER:
492             wrbuf_printf(wr, "%s\n", n->u.leader);
493         }
494     }
495     return 0;
496 }
497
498 int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
499 {
500     switch(mt->xml)
501     {
502     case YAZ_MARC_LINE:
503         return yaz_marc_write_line(mt, wr);
504     case YAZ_MARC_MARCXML:
505         return yaz_marc_write_marcxml(mt, wr);
506     case YAZ_MARC_XCHANGE:
507         return yaz_marc_write_marcxchange(mt, wr, 0, 0); /* no format, type */
508     case YAZ_MARC_ISO2709:
509         return yaz_marc_write_iso2709(mt, wr);
510     case YAZ_MARC_CHECK:
511         return yaz_marc_write_check(mt, wr);
512     }
513     return -1;
514 }
515
516 /** \brief common MARC XML/Xchange writer
517     \param mt handle
518     \param wr WRBUF output
519     \param ns XMLNS for the elements
520     \param format record format (e.g. "MARC21")
521     \param type record type (e.g. "Bibliographic")
522 */
523 static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
524                                      const char *ns, 
525                                      const char *format,
526                                      const char *type)
527 {
528     struct yaz_marc_node *n;
529     int identifier_length;
530     const char *leader = 0;
531
532     for (n = mt->nodes; n; n = n->next)
533         if (n->which == YAZ_MARC_LEADER)
534         {
535             leader = n->u.leader;
536             break;
537         }
538     
539     if (!leader)
540         return -1;
541     if (!atoi_n_check(leader+11, 1, &identifier_length))
542         return -1;
543
544     wrbuf_printf(wr, "<record xmlns=\"%s\"", ns);
545     if (format)
546         wrbuf_printf(wr, " format=\"%.80s\"", format);
547     if (type)
548         wrbuf_printf(wr, " type=\"%.80s\"", type);
549     wrbuf_printf(wr, ">\n");
550     for (n = mt->nodes; n; n = n->next)
551     {
552         struct yaz_marc_subfield *s;
553
554         switch(n->which)
555         {
556         case YAZ_MARC_DATAFIELD:
557             wrbuf_printf(wr, "  <datafield tag=\"");
558             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
559                                     strlen(n->u.datafield.tag));
560             wrbuf_printf(wr, "\"");
561             if (n->u.datafield.indicator)
562             {
563                 int i;
564                 for (i = 0; n->u.datafield.indicator[i]; i++)
565                 {
566                     wrbuf_printf(wr, " ind%d=\"", i+1);
567                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
568                                           n->u.datafield.indicator+i, 1);
569                     wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
570                 }
571             }
572             wrbuf_printf(wr, ">\n");
573             for (s = n->u.datafield.subfields; s; s = s->next)
574             {
575                 /* if identifier length is 2 (most MARCs),
576                    the code is a single character .. However we've
577                    seen multibyte codes, so see how big it really is */
578                 size_t using_code_len = 
579                     (identifier_length != 2) ? identifier_length - 1
580                     :
581                     cdata_one_character(mt, s->code_data);
582                 
583                 wrbuf_iconv_puts(wr, mt->iconv_cd, "    <subfield code=\"");
584                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
585                                         s->code_data, using_code_len);
586                 wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
587                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
588                                         s->code_data + using_code_len,
589                                         strlen(s->code_data + using_code_len));
590                 wrbuf_iconv_puts(wr, mt->iconv_cd, "</subfield>");
591                 wrbuf_puts(wr, "\n");
592             }
593             wrbuf_printf(wr, "  </datafield>\n");
594             break;
595         case YAZ_MARC_CONTROLFIELD:
596             wrbuf_printf(wr, "  <controlfield tag=\"");
597             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
598                                     strlen(n->u.controlfield.tag));
599             wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
600             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
601             wrbuf_iconv_puts(wr, mt->iconv_cd, "</controlfield>");
602             wrbuf_puts(wr, "\n");
603             break;
604         case YAZ_MARC_COMMENT:
605             wrbuf_printf(wr, "<!-- ");
606             wrbuf_puts(wr, n->u.comment);
607             wrbuf_printf(wr, " -->\n");
608             break;
609         case YAZ_MARC_LEADER:
610             wrbuf_printf(wr, "  <leader>");
611             wrbuf_iconv_write_cdata(wr, 
612                                     0 /* no charset conversion for leader */,
613                                     n->u.leader, strlen(n->u.leader));
614             wrbuf_printf(wr, "</leader>\n");
615         }
616     }
617     wrbuf_puts(wr, "</record>\n");
618     return 0;
619 }
620
621 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
622 {
623     if (!mt->leader_spec)
624         yaz_marc_modify_leader(mt, 9, "a");
625     return yaz_marc_write_marcxml_ns(mt, wr, "http://www.loc.gov/MARC21/slim",
626                                      0, 0);
627 }
628
629 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
630                                const char *format,
631                                const char *type)
632 {
633     return yaz_marc_write_marcxml_ns(mt, wr,
634                                      "http://www.bs.dk/standards/MarcXchange",
635                                      0, 0);
636 }
637
638 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
639 {
640     struct yaz_marc_node *n;
641     int indicator_length;
642     int identifier_length;
643     int length_data_entry;
644     int length_starting;
645     int length_implementation;
646     int data_offset = 0;
647     const char *leader = 0;
648     WRBUF wr_dir, wr_head, wr_data_tmp;
649     int base_address;
650     
651     for (n = mt->nodes; n; n = n->next)
652         if (n->which == YAZ_MARC_LEADER)
653             leader = n->u.leader;
654     
655     if (!leader)
656         return -1;
657     if (!atoi_n_check(leader+10, 1, &indicator_length))
658         return -1;
659     if (!atoi_n_check(leader+11, 1, &identifier_length))
660         return -1;
661     if (!atoi_n_check(leader+20, 1, &length_data_entry))
662         return -1;
663     if (!atoi_n_check(leader+21, 1, &length_starting))
664         return -1;
665     if (!atoi_n_check(leader+22, 1, &length_implementation))
666         return -1;
667
668     wr_data_tmp = wrbuf_alloc();
669     wr_dir = wrbuf_alloc();
670     for (n = mt->nodes; n; n = n->next)
671     {
672         int data_length = 0;
673         struct yaz_marc_subfield *s;
674
675         switch(n->which)
676         {
677         case YAZ_MARC_DATAFIELD:
678             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
679             data_length += indicator_length;
680             wrbuf_rewind(wr_data_tmp);
681             for (s = n->u.datafield.subfields; s; s = s->next)
682             {
683                 /* write dummy IDFS + content */
684                 wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
685                 wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
686             }
687             /* write dummy FS (makes MARC-8 to become ASCII) */
688             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
689             data_length += wrbuf_len(wr_data_tmp);
690             break;
691         case YAZ_MARC_CONTROLFIELD:
692             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
693
694             wrbuf_rewind(wr_data_tmp);
695             wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, 
696                              n->u.controlfield.data);
697             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
698             data_length += wrbuf_len(wr_data_tmp);
699             break;
700         case YAZ_MARC_COMMENT:
701             break;
702         case YAZ_MARC_LEADER:
703             break;
704         }
705         if (data_length)
706         {
707             wrbuf_printf(wr_dir, "%0*d", length_data_entry, data_length);
708             wrbuf_printf(wr_dir, "%0*d", length_starting, data_offset);
709             data_offset += data_length;
710         }
711     }
712     /* mark end of directory */
713     wrbuf_putc(wr_dir, ISO2709_FS);
714
715     /* base address of data (comes after leader+directory) */
716     base_address = 24 + wrbuf_len(wr_dir);
717
718     wr_head = wrbuf_alloc();
719
720     /* write record length */
721     wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
722     /* from "original" leader */
723     wrbuf_write(wr_head, leader+5, 7);
724     /* base address of data */
725     wrbuf_printf(wr_head, "%05d", base_address);
726     /* from "original" leader */
727     wrbuf_write(wr_head, leader+17, 7);
728     
729     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
730     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
731     wrbuf_free(wr_head, 1);
732     wrbuf_free(wr_dir, 1);
733     wrbuf_free(wr_data_tmp, 1);
734
735     for (n = mt->nodes; n; n = n->next)
736     {
737         struct yaz_marc_subfield *s;
738
739         switch(n->which)
740         {
741         case YAZ_MARC_DATAFIELD:
742             wrbuf_printf(wr, "%.*s", indicator_length,
743                          n->u.datafield.indicator);
744             for (s = n->u.datafield.subfields; s; s = s->next)
745             {
746                 wrbuf_putc(wr, ISO2709_IDFS);
747                 wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
748                 /* write dummy blank - makes MARC-8 to become ASCII */
749                 wrbuf_iconv_putchar(wr, mt->iconv_cd, ' ');
750                 wr->pos--;
751             }
752             wrbuf_putc(wr, ISO2709_FS);
753             break;
754         case YAZ_MARC_CONTROLFIELD:
755             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
756             /* write dummy blank - makes MARC-8 to become ASCII */
757             wrbuf_iconv_putchar(wr, mt->iconv_cd, ' ');
758             wr->pos--;
759             wrbuf_putc(wr, ISO2709_FS);
760             break;
761         case YAZ_MARC_COMMENT:
762             break;
763         case YAZ_MARC_LEADER:
764             break;
765         }
766     }
767     wrbuf_printf(wr, "%c", ISO2709_RS);
768     return 0;
769 }
770
771 #if YAZ_HAVE_XML2
772 int yaz_marc_read_xml_subfields(yaz_marc_t mt, const xmlNode *ptr)
773 {
774     for (; ptr; ptr = ptr->next)
775     {
776         if (ptr->type == XML_ELEMENT_NODE)
777         {
778             if (!strcmp((const char *) ptr->name, "subfield"))
779             {
780                 size_t ctrl_data_len = 0;
781                 char *ctrl_data_buf = 0;
782                 const xmlNode *p = 0, *ptr_code = 0;
783                 struct _xmlAttr *attr;
784                 for (attr = ptr->properties; attr; attr = attr->next)
785                     if (!strcmp((const char *)attr->name, "code"))
786                         ptr_code = attr->children;
787                     else
788                     {
789                         yaz_marc_cprintf(
790                             mt, "Bad attribute '%.80s' for 'subfield'",
791                             attr->name);
792                         return -1;
793                     }
794                 if (!ptr_code)
795                 {
796                     yaz_marc_cprintf(
797                         mt, "Missing attribute 'code' for 'subfield'" );
798                     return -1;
799                 }
800                 if (ptr_code->type == XML_TEXT_NODE)
801                 {
802                     ctrl_data_len = 
803                         strlen((const char *)ptr_code->content);
804                 }
805                 else
806                 {
807                     yaz_marc_cprintf(
808                         mt, "Missing value for 'code' in 'subfield'" );
809                     return -1;
810                 }
811                 for (p = ptr->children; p ; p = p->next)
812                     if (p->type == XML_TEXT_NODE)
813                         ctrl_data_len += strlen((const char *)p->content);
814                 ctrl_data_buf = nmem_malloc(mt->nmem, ctrl_data_len+1);
815                 strcpy(ctrl_data_buf, (const char *)ptr_code->content);
816                 for (p = ptr->children; p ; p = p->next)
817                     if (p->type == XML_TEXT_NODE)
818                         strcat(ctrl_data_buf, (const char *)p->content);
819                 yaz_marc_add_subfield(mt, ctrl_data_buf, ctrl_data_len);
820             }
821             else
822             {
823                 yaz_marc_cprintf(
824                     mt, "Expected element 'subfield', got '%.80s'", ptr->name);
825                 return -1;
826             }
827         }
828     }
829     return 0;
830 }
831
832 static int yaz_marc_read_xml_leader(yaz_marc_t mt, const xmlNode **ptr_p)
833 {
834     int indicator_length;
835     int identifier_length;
836     int base_address;
837     int length_data_entry;
838     int length_starting;
839     int length_implementation;
840     const char *leader = 0;
841     const xmlNode *ptr = *ptr_p;
842
843     for(; ptr; ptr = ptr->next)
844         if (ptr->type == XML_ELEMENT_NODE)
845         {
846             if (!strcmp((const char *) ptr->name, "leader"))
847             {
848                 xmlNode *p = ptr->children;
849                 for(; p; p = p->next)
850                     if (p->type == XML_TEXT_NODE)
851                         leader = (const char *) p->content;
852                 break;
853             }
854             else
855             {
856                 yaz_marc_cprintf(
857                     mt, "Expected element 'leader', got '%.80s'", ptr->name);
858                 return -1;
859             }
860         }
861     if (!leader)
862     {
863         yaz_marc_cprintf(mt, "Missing element 'leader'");
864         return -1;
865     }
866     if (strlen(leader) != 24)
867     {
868         yaz_marc_cprintf(mt, "Bad length %d of leader data."
869                          " Must have length of 24 characters", strlen(leader));
870         return -1;
871     }
872     yaz_marc_read_leader(mt, leader,
873                          &indicator_length,
874                          &identifier_length,
875                          &base_address,
876                          &length_data_entry,
877                          &length_starting,
878                          &length_implementation);
879     *ptr_p = ptr;
880     return 0;
881 }
882
883 static int yaz_marc_read_xml_fields(yaz_marc_t mt, const xmlNode *ptr)
884 {
885     for(; ptr; ptr = ptr->next)
886         if (ptr->type == XML_ELEMENT_NODE)
887         {
888             if (!strcmp((const char *) ptr->name, "controlfield"))
889             {
890                 const xmlNode *ptr_tag = 0;
891                 struct _xmlAttr *attr;
892                 for (attr = ptr->properties; attr; attr = attr->next)
893                     if (!strcmp((const char *)attr->name, "tag"))
894                         ptr_tag = attr->children;
895                     else
896                     {
897                         yaz_marc_cprintf(
898                             mt, "Bad attribute '%.80s' for 'controlfield'",
899                             attr->name);
900                         return -1;
901                     }
902                 if (!ptr_tag)
903                 {
904                     yaz_marc_cprintf(
905                         mt, "Missing attribute 'tag' for 'controlfield'" );
906                     return -1;
907                 }
908                 yaz_marc_add_controlfield_xml(mt, ptr_tag, ptr->children);
909             }
910             else if (!strcmp((const char *) ptr->name, "datafield"))
911             {
912                 char indstr[11]; /* 0(unused), 1,....9, + zero term */
913                 const xmlNode *ptr_tag = 0;
914                 struct _xmlAttr *attr;
915                 int i;
916                 for (i = 0; i<11; i++)
917                     indstr[i] = '\0';
918                 for (attr = ptr->properties; attr; attr = attr->next)
919                     if (!strcmp((const char *)attr->name, "tag"))
920                         ptr_tag = attr->children;
921                     else if (strlen((const char *)attr->name) == 4 &&
922                              !memcmp(attr->name, "ind", 3))
923                     {
924                         int no = atoi((const char *)attr->name+3);
925                         if (attr->children
926                             && attr->children->type == XML_TEXT_NODE)
927                             indstr[no] = attr->children->content[0];
928                     }
929                     else
930                     {
931                         yaz_marc_cprintf(
932                             mt, "Bad attribute '%.80s' for 'datafield'",
933                             attr->name);
934                         return -1;
935                     }
936                 if (!ptr_tag)
937                 {
938                     yaz_marc_cprintf(
939                         mt, "Missing attribute 'tag' for 'datafield'" );
940                     return -1;
941                 }
942                 /* note that indstr[0] is unused so we use indstr[1..] */
943                 yaz_marc_add_datafield_xml(mt, ptr_tag,
944                                            indstr+1, strlen(indstr+1));
945                 
946                 if (yaz_marc_read_xml_subfields(mt, ptr->children))
947                     return -1;
948             }
949             else
950             {
951                 yaz_marc_cprintf(mt,
952                                  "Expected element controlfield or datafield,"
953                                  " got %.80s", ptr->name);
954                 return -1;
955             }
956         }
957     return 0;
958 }
959
960 int yaz_marc_read_xml(yaz_marc_t mt, const void *xmlnode)
961 {
962     const xmlNode *ptr = xmlnode;
963     for(; ptr; ptr = ptr->next)
964         if (ptr->type == XML_ELEMENT_NODE)
965         {
966             if (!strcmp((const char *) ptr->name, "record"))
967                 break;
968             else
969             {
970                 yaz_marc_cprintf(
971                     mt, "Unknown element '%.80s' in MARC XML reader",
972                     ptr->name);
973                 return -1;
974             }
975         }
976     if (!ptr)
977     {
978         yaz_marc_cprintf(mt, "Missing element 'record' in MARC XML record");
979         return -1;
980     }
981     /* ptr points to record node now */
982     ptr = ptr->children;
983     if (yaz_marc_read_xml_leader(mt, &ptr))
984         return -1;
985     return yaz_marc_read_xml_fields(mt, ptr->next);
986 }
987 #else
988 int yaz_marc_read_xml(yaz_marc_t mt, const void *xmlnode)
989 {
990     return -1;
991 }
992 #endif
993
994 int yaz_marc_read_iso2709(yaz_marc_t mt, const char *buf, int bsize)
995 {
996     int entry_p;
997     int record_length;
998     int indicator_length;
999     int identifier_length;
1000     int end_of_directory;
1001     int base_address;
1002     int length_data_entry;
1003     int length_starting;
1004     int length_implementation;
1005
1006     yaz_marc_reset(mt);
1007
1008     record_length = atoi_n (buf, 5);
1009     if (record_length < 25)
1010     {
1011         yaz_marc_cprintf(mt, "Record length %d < 24", record_length);
1012         return -1;
1013     }
1014     /* ballout if bsize is known and record_length is less than that */
1015     if (bsize != -1 && record_length > bsize)
1016     {
1017         yaz_marc_cprintf(mt, "Record appears to be larger than buffer %d < %d",
1018                          record_length, bsize);
1019         return -1;
1020     }
1021     if (mt->debug)
1022         yaz_marc_cprintf(mt, "Record length         %5d", record_length);
1023
1024     yaz_marc_read_leader(mt, buf,
1025                          &indicator_length,
1026                          &identifier_length,
1027                          &base_address,
1028                          &length_data_entry,
1029                          &length_starting,
1030                          &length_implementation);
1031
1032     /* First pass. determine length of directory & base of data */
1033     for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
1034     {
1035         /* length of directory entry */
1036         int l = 3 + length_data_entry + length_starting;
1037         if (entry_p + l >= record_length)
1038         {
1039             yaz_marc_cprintf(mt, "Directory offset %d: end of record."
1040                              " Missing FS char", entry_p);
1041             return -1;
1042         }
1043         if (mt->debug)
1044         {
1045             yaz_marc_cprintf(mt, "Directory offset %d: Tag %.3s",
1046                              entry_p, buf+entry_p);
1047         }
1048         /* Check for digits in length info */
1049         while (--l >= 3)
1050             if (!isdigit(*(const unsigned char *) (buf + entry_p+l)))
1051                 break;
1052         if (l >= 3)
1053         {
1054             /* Not all digits, so stop directory scan */
1055             yaz_marc_cprintf(mt, "Directory offset %d: Bad value for data"
1056                              " length and/or length starting", entry_p);
1057             break;
1058         }
1059         entry_p += 3 + length_data_entry + length_starting;
1060     }
1061     end_of_directory = entry_p;
1062     if (base_address != entry_p+1)
1063     {
1064         yaz_marc_cprintf(mt, "Base address not at end of directory,"
1065                          " base %d, end %d", base_address, entry_p+1);
1066     }
1067
1068     /* Second pass. parse control - and datafields */
1069     for (entry_p = 24; entry_p != end_of_directory; )
1070     {
1071         int data_length;
1072         int data_offset;
1073         int end_offset;
1074         int i;
1075         char tag[4];
1076         int identifier_flag = 0;
1077         int entry_p0 = entry_p;
1078
1079         memcpy (tag, buf+entry_p, 3);
1080         entry_p += 3;
1081         tag[3] = '\0';
1082         data_length = atoi_n(buf+entry_p, length_data_entry);
1083         entry_p += length_data_entry;
1084         data_offset = atoi_n(buf+entry_p, length_starting);
1085         entry_p += length_starting;
1086         i = data_offset + base_address;
1087         end_offset = i+data_length-1;
1088
1089         if (data_length <= 0 || data_offset < 0)
1090             break;
1091         
1092         if (mt->debug)
1093         {
1094             yaz_marc_cprintf(mt, "Tag: %s. Directory offset %d: data-length %d,"
1095                              " data-offset %d",
1096                              tag, entry_p0, data_length, data_offset);
1097         }
1098         if (end_offset >= record_length)
1099         {
1100             yaz_marc_cprintf(mt, "Directory offset %d: Data out of bounds %d >= %d",
1101                              entry_p0, end_offset, record_length);
1102             break;
1103         }
1104         
1105         if (memcmp (tag, "00", 2))
1106             identifier_flag = 1;  /* if not 00X assume subfields */
1107         else if (indicator_length < 4 && indicator_length > 0)
1108         {
1109             /* Danmarc 00X have subfields */
1110             if (buf[i + indicator_length] == ISO2709_IDFS)
1111                 identifier_flag = 1;
1112             else if (buf[i + indicator_length + 1] == ISO2709_IDFS)
1113                 identifier_flag = 2;
1114         }
1115
1116         if (identifier_flag)
1117         {
1118             /* datafield */
1119             i += identifier_flag-1;
1120             yaz_marc_add_datafield(mt, tag, buf+i, indicator_length);
1121             i += indicator_length;
1122
1123             while (i < end_offset &&
1124                     buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
1125             {
1126                 int code_offset = i+1;
1127
1128                 i ++;
1129                 while (i < end_offset &&
1130                         buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
1131                        buf[i] != ISO2709_FS)
1132                     i++;
1133                 yaz_marc_add_subfield(mt, buf+code_offset, i - code_offset);
1134             }
1135         }
1136         else
1137         {
1138             /* controlfield */
1139             int i0 = i;
1140             while (i < end_offset && 
1141                 buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
1142                 i++;
1143             yaz_marc_add_controlfield(mt, tag, buf+i0, i-i0);
1144         }
1145         if (i < end_offset)
1146         {
1147             yaz_marc_cprintf(mt, "Separator but not at end of field length=%d",
1148                     data_length);
1149         }
1150         if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
1151         {
1152             yaz_marc_cprintf(mt, "No separator at end of field length=%d",
1153                     data_length);
1154         }
1155     }
1156     return record_length;
1157 }
1158
1159 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
1160 {
1161     int s, r = yaz_marc_read_iso2709(mt, buf, bsize);
1162     if (r <= 0)
1163         return r;
1164     s = yaz_marc_write_mode(mt, wr); /* returns 0 for OK, -1 otherwise */
1165     if (s != 0)
1166         return -1; /* error */
1167     return r; /* OK, return length > 0 */
1168 }
1169
1170 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
1171                          char **result, int *rsize)
1172 {
1173     int r;
1174
1175     wrbuf_rewind(mt->m_wr);
1176     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
1177     if (result)
1178         *result = wrbuf_buf(mt->m_wr);
1179     if (rsize)
1180         *rsize = wrbuf_len(mt->m_wr);
1181     return r;
1182 }
1183
1184 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
1185 {
1186     if (mt)
1187         mt->xml = xmlmode;
1188 }
1189
1190 void yaz_marc_debug(yaz_marc_t mt, int level)
1191 {
1192     if (mt)
1193         mt->debug = level;
1194 }
1195
1196 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
1197 {
1198     mt->iconv_cd = cd;
1199 }
1200
1201 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
1202 {
1203     struct yaz_marc_node *n;
1204     char *leader = 0;
1205     for (n = mt->nodes; n; n = n->next)
1206         if (n->which == YAZ_MARC_LEADER)
1207         {
1208             leader = n->u.leader;
1209             memcpy(leader+off, str, strlen(str));
1210             break;
1211         }
1212 }
1213
1214 /* deprecated */
1215 int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
1216 {
1217     yaz_marc_t mt = yaz_marc_create();
1218     int r;
1219
1220     mt->debug = debug;
1221     mt->xml = xml;
1222     r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
1223     yaz_marc_destroy(mt);
1224     return r;
1225 }
1226
1227 /* deprecated */
1228 int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
1229 {
1230     return yaz_marc_decode(buf, wr, debug, bsize, 0);
1231 }
1232
1233 /* deprecated */
1234 int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
1235 {
1236     yaz_marc_t mt = yaz_marc_create();
1237     int r;
1238
1239     mt->debug = debug;
1240     r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
1241     if (!outf)
1242         outf = stdout;
1243     if (r > 0)
1244         fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
1245     yaz_marc_destroy(mt);
1246     return r;
1247 }
1248
1249 /* deprecated */
1250 int marc_display_ex (const char *buf, FILE *outf, int debug)
1251 {
1252     return marc_display_exl (buf, outf, debug, -1);
1253 }
1254
1255 /* deprecated */
1256 int marc_display (const char *buf, FILE *outf)
1257 {
1258     return marc_display_ex (buf, outf, 0);
1259 }
1260
1261 int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
1262 {
1263     xfree(mt->leader_spec);
1264     mt->leader_spec = 0;
1265     if (leader_spec)
1266     {
1267         char dummy_leader[24];
1268         if (marc_exec_leader(leader_spec, dummy_leader, 24))
1269             return -1;
1270         mt->leader_spec = xstrdup(leader_spec);
1271     }
1272     return 0;
1273 }
1274
1275 static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
1276 {
1277     const char *cp = leader_spec;
1278     while (cp)
1279     {
1280         char val[21];
1281         int pos;
1282         int no_read = 0, no = 0;
1283
1284         no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
1285         if (no < 2 || no_read < 3)
1286             return -1;
1287         if (pos < 0 || pos >= size)
1288             return -1;
1289
1290         if (*val == '\'')
1291         {
1292             const char *vp = strchr(val+1, '\'');
1293             size_t len;
1294             
1295             if (!vp)
1296                 return -1;
1297             len = vp-val-1;
1298             if (len + pos > size)
1299                 return -1;
1300             memcpy(leader + pos, val+1, len);
1301         }
1302         else if (*val >= '0' && *val <= '9')
1303         {
1304             int ch = atoi(val);
1305             leader[pos] = ch;
1306         }
1307         else
1308             return -1;
1309         cp += no_read;
1310         if (*cp != ',')
1311             break;
1312
1313         cp++;
1314     }
1315     return 0;
1316 }
1317
1318
1319 /*
1320  * Local variables:
1321  * c-basic-offset: 4
1322  * indent-tabs-mode: nil
1323  * End:
1324  * vim: shiftwidth=4 tabstop=8 expandtab
1325  */
1326