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