Added snprintf/vsnprintf wrappers for systems that don't have
[yaz-moved-to-github.git] / src / marcdisp.c
1 /*
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: marcdisp.c,v 1.47 2007-02-23 10:15:01 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 #include <yaz/snprintf.h>
30
31 #if YAZ_HAVE_XML2
32 #include <libxml/parser.h>
33 #include <libxml/tree.h>
34 #endif
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     int write_using_libxml2;
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->write_using_libxml2 = 0;
103     mt->m_wr = wrbuf_alloc();
104     mt->iconv_cd = 0;
105     mt->leader_spec = 0;
106     strcpy(mt->subfield_str, " $");
107     strcpy(mt->endline_str, "\n");
108
109     mt->nmem = nmem_create();
110     yaz_marc_reset(mt);
111     return mt;
112 }
113
114 void yaz_marc_destroy(yaz_marc_t mt)
115 {
116     if (!mt)
117         return ;
118     nmem_destroy(mt->nmem);
119     wrbuf_free(mt->m_wr, 1);
120     xfree(mt->leader_spec);
121     xfree(mt);
122 }
123
124 NMEM yaz_marc_get_nmem(yaz_marc_t mt)
125 {
126     return mt->nmem;
127 }
128
129 static void marc_iconv_reset(yaz_marc_t mt, WRBUF wr)
130 {
131     if (mt->iconv_cd)
132     {
133         char outbuf[12];
134         size_t outbytesleft = sizeof(outbuf);
135         char *outp = outbuf;
136         size_t r = yaz_iconv(mt->iconv_cd, 0, 0, &outp, &outbytesleft);
137         if (r != (size_t) (-1))
138             wrbuf_write(wr, outbuf, outp - outbuf);
139     }
140 }
141
142 static int marc_exec_leader(const char *leader_spec, char *leader,
143                             size_t size);
144
145
146 static struct yaz_marc_node *yaz_marc_add_node(yaz_marc_t mt)
147 {
148     struct yaz_marc_node *n = nmem_malloc(mt->nmem, sizeof(*n));
149     n->next = 0;
150     *mt->nodes_pp = n;
151     mt->nodes_pp = &n->next;
152     return n;
153 }
154
155 #if YAZ_HAVE_XML2
156 void yaz_marc_add_controlfield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
157                                    const xmlNode *ptr_data)
158 {
159     struct yaz_marc_node *n = yaz_marc_add_node(mt);
160     n->which = YAZ_MARC_CONTROLFIELD;
161     n->u.controlfield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
162     n->u.controlfield.data = nmem_text_node_cdata(ptr_data, mt->nmem);
163 }
164 #endif
165
166
167 void yaz_marc_add_comment(yaz_marc_t mt, char *comment)
168 {
169     struct yaz_marc_node *n = yaz_marc_add_node(mt);
170     n->which = YAZ_MARC_COMMENT;
171     n->u.comment = nmem_strdup(mt->nmem, comment);
172 }
173
174 void yaz_marc_cprintf(yaz_marc_t mt, const char *fmt, ...)
175 {
176     va_list ap;
177     char buf[200];
178
179     va_start(ap, fmt);
180     yaz_vsnprintf(buf, sizeof(buf)-1, fmt, ap);
181     yaz_marc_add_comment(mt, buf);
182     va_end (ap);
183 }
184
185 int yaz_marc_get_debug(yaz_marc_t mt)
186 {
187     return mt->debug;
188 }
189
190 void yaz_marc_add_leader(yaz_marc_t mt, const char *leader, size_t leader_len)
191 {
192     struct yaz_marc_node *n = yaz_marc_add_node(mt);
193     n->which = YAZ_MARC_LEADER;
194     n->u.leader = nmem_strdupn(mt->nmem, leader, leader_len);
195     marc_exec_leader(mt->leader_spec, n->u.leader, leader_len);
196 }
197
198 void yaz_marc_add_controlfield(yaz_marc_t mt, const char *tag,
199                                const char *data, size_t data_len)
200 {
201     struct yaz_marc_node *n = yaz_marc_add_node(mt);
202     n->which = YAZ_MARC_CONTROLFIELD;
203     n->u.controlfield.tag = nmem_strdup(mt->nmem, tag);
204     n->u.controlfield.data = nmem_strdupn(mt->nmem, data, data_len);
205     if (mt->debug)
206     {
207         size_t i;
208         char msg[80];
209
210         sprintf(msg, "controlfield:");
211         for (i = 0; i < 16 && i < data_len; i++)
212             sprintf(msg + strlen(msg), " %02X", data[i] & 0xff);
213         if (i < data_len)
214             sprintf(msg + strlen(msg), " ..");
215         yaz_marc_add_comment(mt, msg);
216     }
217 }
218
219 void yaz_marc_add_datafield(yaz_marc_t mt, const char *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_strdup(mt->nmem, tag);
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
233 #if YAZ_HAVE_XML2
234 void yaz_marc_add_datafield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
235                                 const char *indicator, size_t indicator_len)
236 {
237     struct yaz_marc_node *n = yaz_marc_add_node(mt);
238     n->which = YAZ_MARC_DATAFIELD;
239     n->u.datafield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
240     n->u.datafield.indicator =
241         nmem_strdupn(mt->nmem, indicator, indicator_len);
242     n->u.datafield.subfields = 0;
243
244     /* make subfield_pp the current (last one) */
245     mt->subfield_pp = &n->u.datafield.subfields;
246 }
247 #endif
248
249 void yaz_marc_add_subfield(yaz_marc_t mt,
250                            const char *code_data, size_t code_data_len)
251 {
252     if (mt->debug)
253     {
254         size_t i;
255         char msg[80];
256
257         sprintf(msg, "subfield:");
258         for (i = 0; i < 16 && i < code_data_len; i++)
259             sprintf(msg + strlen(msg), " %02X", code_data[i] & 0xff);
260         if (i < code_data_len)
261             sprintf(msg + strlen(msg), " ..");
262         yaz_marc_add_comment(mt, msg);
263     }
264
265     if (mt->subfield_pp)
266     {
267         struct yaz_marc_subfield *n = nmem_malloc(mt->nmem, sizeof(*n));
268         n->code_data = nmem_strdupn(mt->nmem, code_data, code_data_len);
269         n->next = 0;
270         /* mark subfield_pp to point to this one, so we append here next */
271         *mt->subfield_pp = n;
272         mt->subfield_pp = &n->next;
273     }
274 }
275
276 int atoi_n_check(const char *buf, int size, int *val)
277 {
278     int i;
279     for (i = 0; i < size; i++)
280         if (!isdigit(i[(const unsigned char *) buf]))
281             return 0;
282     *val = atoi_n(buf, size);
283     return 1;
284 }
285
286 void yaz_marc_set_leader(yaz_marc_t mt, const char *leader_c,
287                          int *indicator_length,
288                          int *identifier_length,
289                          int *base_address,
290                          int *length_data_entry,
291                          int *length_starting,
292                          int *length_implementation)
293 {
294     char leader[24];
295
296     memcpy(leader, leader_c, 24);
297
298     if (!atoi_n_check(leader+10, 1, indicator_length))
299     {
300         yaz_marc_cprintf(mt, 
301                          "Indicator length at offset 10 should hold a digit."
302                          " Assuming 2");
303         leader[10] = '2';
304         *indicator_length = 2;
305     }
306     if (!atoi_n_check(leader+11, 1, identifier_length))
307     {
308         yaz_marc_cprintf(mt, 
309                          "Identifier length at offset 11 should hold a digit."
310                          " Assuming 2");
311         leader[11] = '2';
312         *identifier_length = 2;
313     }
314     if (!atoi_n_check(leader+12, 5, base_address))
315     {
316         yaz_marc_cprintf(mt, 
317                          "Base address at offsets 12..16 should hold a number."
318                          " Assuming 0");
319         *base_address = 0;
320     }
321     if (!atoi_n_check(leader+20, 1, length_data_entry))
322     {
323         yaz_marc_cprintf(mt, 
324                          "Length data entry at offset 20 should hold a digit."
325                          " Assuming 4");
326         *length_data_entry = 4;
327         leader[20] = '4';
328     }
329     if (!atoi_n_check(leader+21, 1, length_starting))
330     {
331         yaz_marc_cprintf(mt,
332                          "Length starting at offset 21 should hold a digit."
333                          " Assuming 5");
334         *length_starting = 5;
335         leader[21] = '5';
336     }
337     if (!atoi_n_check(leader+22, 1, length_implementation))
338     {
339         yaz_marc_cprintf(mt, 
340                          "Length implementation at offset 22 should hold a digit."
341                          " Assuming 0");
342         *length_implementation = 0;
343         leader[22] = '0';
344     }
345
346     if (mt->debug)
347     {
348         yaz_marc_cprintf(mt, "Indicator length      %5d", *indicator_length);
349         yaz_marc_cprintf(mt, "Identifier length     %5d", *identifier_length);
350         yaz_marc_cprintf(mt, "Base address          %5d", *base_address);
351         yaz_marc_cprintf(mt, "Length data entry     %5d", *length_data_entry);
352         yaz_marc_cprintf(mt, "Length starting       %5d", *length_starting);
353         yaz_marc_cprintf(mt, "Length implementation %5d", *length_implementation);
354     }
355     yaz_marc_add_leader(mt, leader, 24);
356 }
357
358 void yaz_marc_subfield_str(yaz_marc_t mt, const char *s)
359 {
360     strncpy(mt->subfield_str, s, sizeof(mt->subfield_str)-1);
361     mt->subfield_str[sizeof(mt->subfield_str)-1] = '\0';
362 }
363
364 void yaz_marc_endline_str(yaz_marc_t mt, const char *s)
365 {
366     strncpy(mt->endline_str, s, sizeof(mt->endline_str)-1);
367     mt->endline_str[sizeof(mt->endline_str)-1] = '\0';
368 }
369
370 /* try to guess how many bytes the identifier really is! */
371 static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
372 {
373     if (mt->iconv_cd)
374     {
375         size_t i;
376         for (i = 1; i<5; i++)
377         {
378             char outbuf[12];
379             size_t outbytesleft = sizeof(outbuf);
380             char *outp = outbuf;
381             const char *inp = buf;
382
383             size_t inbytesleft = i;
384             size_t r = yaz_iconv(mt->iconv_cd, (char**) &inp, &inbytesleft,
385                                  &outp, &outbytesleft);
386             if (r != (size_t) (-1))
387                 return i;  /* got a complete sequence */
388         }
389         return 1; /* giving up */
390     }
391     return 1; /* we don't know */
392 }
393                               
394 void yaz_marc_reset(yaz_marc_t mt)
395 {
396     nmem_reset(mt->nmem);
397     mt->nodes = 0;
398     mt->nodes_pp = &mt->nodes;
399     mt->subfield_pp = 0;
400 }
401
402 int yaz_marc_write_check(yaz_marc_t mt, WRBUF wr)
403 {
404     struct yaz_marc_node *n;
405     int identifier_length;
406     const char *leader = 0;
407
408     for (n = mt->nodes; n; n = n->next)
409         if (n->which == YAZ_MARC_LEADER)
410         {
411             leader = n->u.leader;
412             break;
413         }
414     
415     if (!leader)
416         return -1;
417     if (!atoi_n_check(leader+11, 1, &identifier_length))
418         return -1;
419
420     for (n = mt->nodes; n; n = n->next)
421     {
422         switch(n->which)
423         {
424         case YAZ_MARC_COMMENT:
425             wrbuf_iconv_write(wr, mt->iconv_cd, 
426                               n->u.comment, strlen(n->u.comment));
427             wrbuf_puts(wr, ")\n");
428             break;
429         default:
430             break;
431         }
432     }
433     return 0;
434 }
435
436
437 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
438 {
439     struct yaz_marc_node *n;
440     int identifier_length;
441     const char *leader = 0;
442
443     for (n = mt->nodes; n; n = n->next)
444         if (n->which == YAZ_MARC_LEADER)
445         {
446             leader = n->u.leader;
447             break;
448         }
449     
450     if (!leader)
451         return -1;
452     if (!atoi_n_check(leader+11, 1, &identifier_length))
453         return -1;
454
455     for (n = mt->nodes; n; n = n->next)
456     {
457         struct yaz_marc_subfield *s;
458         switch(n->which)
459         {
460         case YAZ_MARC_DATAFIELD:
461             wrbuf_printf(wr, "%s %s", n->u.datafield.tag,
462                          n->u.datafield.indicator);
463             for (s = n->u.datafield.subfields; s; s = s->next)
464             {
465                 /* if identifier length is 2 (most MARCs),
466                    the code is a single character .. However we've
467                    seen multibyte codes, so see how big it really is */
468                 size_t using_code_len = 
469                     (identifier_length != 2) ? identifier_length - 1
470                     :
471                     cdata_one_character(mt, s->code_data);
472                 
473                 wrbuf_puts (wr, mt->subfield_str); 
474                 wrbuf_iconv_write(wr, mt->iconv_cd, s->code_data, 
475                                   using_code_len);
476                 wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
477                 wrbuf_iconv_puts(wr, mt->iconv_cd, 
478                                  s->code_data + using_code_len);
479                 marc_iconv_reset(mt, wr);
480             }
481             wrbuf_puts (wr, mt->endline_str);
482             break;
483         case YAZ_MARC_CONTROLFIELD:
484             wrbuf_printf(wr, "%s", n->u.controlfield.tag);
485             wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
486             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
487             marc_iconv_reset(mt, wr);
488             wrbuf_puts (wr, mt->endline_str);
489             break;
490         case YAZ_MARC_COMMENT:
491             wrbuf_puts(wr, "(");
492             wrbuf_iconv_write(wr, mt->iconv_cd, 
493                               n->u.comment, strlen(n->u.comment));
494             wrbuf_puts(wr, ")\n");
495             break;
496         case YAZ_MARC_LEADER:
497             wrbuf_printf(wr, "%s\n", n->u.leader);
498         }
499     }
500     wrbuf_puts(wr, "\n");
501     return 0;
502 }
503
504 int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
505 {
506     switch(mt->xml)
507     {
508     case YAZ_MARC_LINE:
509         return yaz_marc_write_line(mt, wr);
510     case YAZ_MARC_MARCXML:
511         return yaz_marc_write_marcxml(mt, wr);
512     case YAZ_MARC_XCHANGE:
513         return yaz_marc_write_marcxchange(mt, wr, 0, 0); /* no format, type */
514     case YAZ_MARC_ISO2709:
515         return yaz_marc_write_iso2709(mt, wr);
516     case YAZ_MARC_CHECK:
517         return yaz_marc_write_check(mt, wr);
518     }
519     return -1;
520 }
521
522 /** \brief common MARC XML/Xchange writer
523     \param mt handle
524     \param wr WRBUF output
525     \param ns XMLNS for the elements
526     \param format record format (e.g. "MARC21")
527     \param type record type (e.g. "Bibliographic")
528 */
529 static int yaz_marc_write_marcxml_ns1(yaz_marc_t mt, WRBUF wr,
530                                       const char *ns, 
531                                       const char *format,
532                                       const char *type)
533 {
534     struct yaz_marc_node *n;
535     int identifier_length;
536     const char *leader = 0;
537
538     for (n = mt->nodes; n; n = n->next)
539         if (n->which == YAZ_MARC_LEADER)
540         {
541             leader = n->u.leader;
542             break;
543         }
544     
545     if (!leader)
546         return -1;
547     if (!atoi_n_check(leader+11, 1, &identifier_length))
548         return -1;
549
550     wrbuf_printf(wr, "<record xmlns=\"%s\"", ns);
551     if (format)
552         wrbuf_printf(wr, " format=\"%.80s\"", format);
553     if (type)
554         wrbuf_printf(wr, " type=\"%.80s\"", type);
555     wrbuf_printf(wr, ">\n");
556     for (n = mt->nodes; n; n = n->next)
557     {
558         struct yaz_marc_subfield *s;
559
560         switch(n->which)
561         {
562         case YAZ_MARC_DATAFIELD:
563             wrbuf_printf(wr, "  <datafield tag=\"");
564             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
565                                     strlen(n->u.datafield.tag));
566             wrbuf_printf(wr, "\"");
567             if (n->u.datafield.indicator)
568             {
569                 int i;
570                 for (i = 0; n->u.datafield.indicator[i]; i++)
571                 {
572                     wrbuf_printf(wr, " ind%d=\"", i+1);
573                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
574                                           n->u.datafield.indicator+i, 1);
575                     wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
576                 }
577             }
578             wrbuf_printf(wr, ">\n");
579             for (s = n->u.datafield.subfields; s; s = s->next)
580             {
581                 /* if identifier length is 2 (most MARCs),
582                    the code is a single character .. However we've
583                    seen multibyte codes, so see how big it really is */
584                 size_t using_code_len = 
585                     (identifier_length != 2) ? identifier_length - 1
586                     :
587                     cdata_one_character(mt, s->code_data);
588                 
589                 wrbuf_iconv_puts(wr, mt->iconv_cd, "    <subfield code=\"");
590                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
591                                         s->code_data, using_code_len);
592                 wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
593                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
594                                         s->code_data + using_code_len,
595                                         strlen(s->code_data + using_code_len));
596                 marc_iconv_reset(mt, wr);
597                 wrbuf_iconv_puts(wr, mt->iconv_cd, "</subfield>");
598                 wrbuf_puts(wr, "\n");
599             }
600             wrbuf_printf(wr, "  </datafield>\n");
601             break;
602         case YAZ_MARC_CONTROLFIELD:
603             wrbuf_printf(wr, "  <controlfield tag=\"");
604             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
605                                     strlen(n->u.controlfield.tag));
606             wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
607             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
608
609             marc_iconv_reset(mt, wr);
610             wrbuf_iconv_puts(wr, mt->iconv_cd, "</controlfield>");
611             wrbuf_puts(wr, "\n");
612             break;
613         case YAZ_MARC_COMMENT:
614             wrbuf_printf(wr, "<!-- ");
615             wrbuf_puts(wr, n->u.comment);
616             wrbuf_printf(wr, " -->\n");
617             break;
618         case YAZ_MARC_LEADER:
619             wrbuf_printf(wr, "  <leader>");
620             wrbuf_iconv_write_cdata(wr, 
621                                     0 /* no charset conversion for leader */,
622                                     n->u.leader, strlen(n->u.leader));
623             wrbuf_printf(wr, "</leader>\n");
624         }
625     }
626     wrbuf_puts(wr, "</record>\n");
627     return 0;
628 }
629
630 static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
631                                      const char *ns, 
632                                      const char *format,
633                                      const char *type)
634 {
635     if (mt->write_using_libxml2)
636     {
637 #if YAZ_HAVE_XML2
638         int ret;
639         xmlNode *root_ptr;
640
641         ret = yaz_marc_write_xml(mt, &root_ptr, ns, format, type);
642         if (ret == 0)
643         {
644             xmlChar *buf_out;
645             xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
646             int len_out;
647
648             xmlDocSetRootElement(doc, root_ptr);
649             xmlDocDumpMemory(doc, &buf_out, &len_out);
650
651             wrbuf_write(wr, (const char *) buf_out, len_out);
652             wrbuf_puts(wr, "");
653             xmlFree(buf_out);
654             xmlFreeDoc(doc);
655         }
656         return ret;
657 #else
658         return -1;
659 #endif
660     }
661     else
662         return yaz_marc_write_marcxml_ns1(mt, wr, ns, format, type);
663 }
664
665 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
666 {
667     if (!mt->leader_spec)
668         yaz_marc_modify_leader(mt, 9, "a");
669     return yaz_marc_write_marcxml_ns(mt, wr, "http://www.loc.gov/MARC21/slim",
670                                      0, 0);
671 }
672
673 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
674                                const char *format,
675                                const char *type)
676 {
677     return yaz_marc_write_marcxml_ns(mt, wr,
678                                      "http://www.bs.dk/standards/MarcXchange",
679                                      0, 0);
680 }
681
682
683 int yaz_marc_write_xml(yaz_marc_t mt, xmlNode **root_ptr,
684                        const char *ns, 
685                        const char *format,
686                        const char *type)
687 {
688 #if YAZ_HAVE_XML2
689     struct yaz_marc_node *n;
690     int identifier_length;
691     const char *leader = 0;
692     xmlNode *record_ptr;
693     xmlNsPtr ns_record;
694     WRBUF wr_cdata = 0;
695
696     for (n = mt->nodes; n; n = n->next)
697         if (n->which == YAZ_MARC_LEADER)
698         {
699             leader = n->u.leader;
700             break;
701         }
702     
703     if (!leader)
704         return -1;
705     if (!atoi_n_check(leader+11, 1, &identifier_length))
706         return -1;
707
708     wr_cdata = wrbuf_alloc();
709
710     record_ptr = xmlNewNode(0, BAD_CAST "record");
711     *root_ptr = record_ptr;
712
713     ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
714     xmlSetNs(record_ptr, ns_record);
715
716     if (format)
717         xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
718     if (type)
719         xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
720     for (n = mt->nodes; n; n = n->next)
721     {
722         struct yaz_marc_subfield *s;
723         xmlNode *ptr;
724
725         switch(n->which)
726         {
727         case YAZ_MARC_DATAFIELD:
728             ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST "datafield", 0);
729             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.datafield.tag);
730             if (n->u.datafield.indicator)
731             {
732                 int i;
733                 for (i = 0; n->u.datafield.indicator[i]; i++)
734                 {
735                     char ind_str[6];
736                     char ind_val[2];
737
738                     sprintf(ind_str, "ind%d", i+1);
739                     ind_val[0] = n->u.datafield.indicator[i];
740                     ind_val[1] = '\0';
741                     xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
742                 }
743             }
744             for (s = n->u.datafield.subfields; s; s = s->next)
745             {
746                 xmlNode *ptr_subfield;
747                 /* if identifier length is 2 (most MARCs),
748                    the code is a single character .. However we've
749                    seen multibyte codes, so see how big it really is */
750                 size_t using_code_len = 
751                     (identifier_length != 2) ? identifier_length - 1
752                     :
753                     cdata_one_character(mt, s->code_data);
754
755                 wrbuf_rewind(wr_cdata);
756                 wrbuf_iconv_puts(wr_cdata, mt->iconv_cd,
757                                  s->code_data + using_code_len);
758                 marc_iconv_reset(mt, wr_cdata);
759                 ptr_subfield = xmlNewTextChild(
760                     ptr, ns_record, 
761                     BAD_CAST "subfield",  BAD_CAST wrbuf_cstr(wr_cdata));
762
763                 wrbuf_rewind(wr_cdata);
764                 wrbuf_iconv_write(wr_cdata, mt->iconv_cd,
765                                   s->code_data, using_code_len);
766                 xmlNewProp(ptr_subfield, BAD_CAST "code",
767                            BAD_CAST wrbuf_cstr(wr_cdata));
768             }
769             break;
770         case YAZ_MARC_CONTROLFIELD:
771             wrbuf_rewind(wr_cdata);
772             wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
773             marc_iconv_reset(mt, wr_cdata);
774             
775             ptr = xmlNewTextChild(record_ptr, ns_record,
776                                   BAD_CAST "controlfield",
777                                   BAD_CAST wrbuf_cstr(wr_cdata));
778             
779             xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.controlfield.tag);
780             break;
781         case YAZ_MARC_COMMENT:
782             ptr = xmlNewComment(BAD_CAST n->u.comment);
783             xmlAddChild(record_ptr, ptr);
784             break;
785         case YAZ_MARC_LEADER:
786             xmlNewTextChild(record_ptr, ns_record, BAD_CAST "leader",
787                             BAD_CAST n->u.leader);
788             break;
789         }
790     }
791     wrbuf_destroy(wr_cdata);
792     return 0;
793 #else
794     return -1;
795 #endif
796 }
797
798 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
799 {
800     struct yaz_marc_node *n;
801     int indicator_length;
802     int identifier_length;
803     int length_data_entry;
804     int length_starting;
805     int length_implementation;
806     int data_offset = 0;
807     const char *leader = 0;
808     WRBUF wr_dir, wr_head, wr_data_tmp;
809     int base_address;
810     
811     for (n = mt->nodes; n; n = n->next)
812         if (n->which == YAZ_MARC_LEADER)
813             leader = n->u.leader;
814     
815     if (!leader)
816         return -1;
817     if (!atoi_n_check(leader+10, 1, &indicator_length))
818         return -1;
819     if (!atoi_n_check(leader+11, 1, &identifier_length))
820         return -1;
821     if (!atoi_n_check(leader+20, 1, &length_data_entry))
822         return -1;
823     if (!atoi_n_check(leader+21, 1, &length_starting))
824         return -1;
825     if (!atoi_n_check(leader+22, 1, &length_implementation))
826         return -1;
827
828     wr_data_tmp = wrbuf_alloc();
829     wr_dir = wrbuf_alloc();
830     for (n = mt->nodes; n; n = n->next)
831     {
832         int data_length = 0;
833         struct yaz_marc_subfield *s;
834
835         switch(n->which)
836         {
837         case YAZ_MARC_DATAFIELD:
838             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
839             data_length += indicator_length;
840             wrbuf_rewind(wr_data_tmp);
841             for (s = n->u.datafield.subfields; s; s = s->next)
842             {
843                 /* write dummy IDFS + content */
844                 wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
845                 wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
846                 marc_iconv_reset(mt, wr_data_tmp);
847             }
848             /* write dummy FS (makes MARC-8 to become ASCII) */
849             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
850             data_length += wrbuf_len(wr_data_tmp);
851             break;
852         case YAZ_MARC_CONTROLFIELD:
853             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
854
855             wrbuf_rewind(wr_data_tmp);
856             wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, 
857                              n->u.controlfield.data);
858             marc_iconv_reset(mt, wr_data_tmp);
859             wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
860             data_length += wrbuf_len(wr_data_tmp);
861             break;
862         case YAZ_MARC_COMMENT:
863             break;
864         case YAZ_MARC_LEADER:
865             break;
866         }
867         if (data_length)
868         {
869             wrbuf_printf(wr_dir, "%0*d", length_data_entry, data_length);
870             wrbuf_printf(wr_dir, "%0*d", length_starting, data_offset);
871             data_offset += data_length;
872         }
873     }
874     /* mark end of directory */
875     wrbuf_putc(wr_dir, ISO2709_FS);
876
877     /* base address of data (comes after leader+directory) */
878     base_address = 24 + wrbuf_len(wr_dir);
879
880     wr_head = wrbuf_alloc();
881
882     /* write record length */
883     wrbuf_printf(wr_head, "%05d", base_address + data_offset + 1);
884     /* from "original" leader */
885     wrbuf_write(wr_head, leader+5, 7);
886     /* base address of data */
887     wrbuf_printf(wr_head, "%05d", base_address);
888     /* from "original" leader */
889     wrbuf_write(wr_head, leader+17, 7);
890     
891     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
892     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
893     wrbuf_free(wr_head, 1);
894     wrbuf_free(wr_dir, 1);
895     wrbuf_free(wr_data_tmp, 1);
896
897     for (n = mt->nodes; n; n = n->next)
898     {
899         struct yaz_marc_subfield *s;
900
901         switch(n->which)
902         {
903         case YAZ_MARC_DATAFIELD:
904             wrbuf_printf(wr, "%.*s", indicator_length,
905                          n->u.datafield.indicator);
906             for (s = n->u.datafield.subfields; s; s = s->next)
907             {
908                 wrbuf_putc(wr, ISO2709_IDFS);
909                 wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
910                 marc_iconv_reset(mt, wr);
911             }
912             wrbuf_putc(wr, ISO2709_FS);
913             break;
914         case YAZ_MARC_CONTROLFIELD:
915             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
916             marc_iconv_reset(mt, wr);
917             wrbuf_putc(wr, ISO2709_FS);
918             break;
919         case YAZ_MARC_COMMENT:
920             break;
921         case YAZ_MARC_LEADER:
922             break;
923         }
924     }
925     wrbuf_printf(wr, "%c", ISO2709_RS);
926     return 0;
927 }
928
929
930 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
931 {
932     int s, r = yaz_marc_read_iso2709(mt, buf, bsize);
933     if (r <= 0)
934         return r;
935     s = yaz_marc_write_mode(mt, wr); /* returns 0 for OK, -1 otherwise */
936     if (s != 0)
937         return -1; /* error */
938     return r; /* OK, return length > 0 */
939 }
940
941 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
942                          char **result, int *rsize)
943 {
944     int r;
945
946     wrbuf_rewind(mt->m_wr);
947     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
948     if (result)
949         *result = wrbuf_buf(mt->m_wr);
950     if (rsize)
951         *rsize = wrbuf_len(mt->m_wr);
952     return r;
953 }
954
955 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
956 {
957     if (mt)
958         mt->xml = xmlmode;
959 }
960
961 void yaz_marc_debug(yaz_marc_t mt, int level)
962 {
963     if (mt)
964         mt->debug = level;
965 }
966
967 void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
968 {
969     mt->iconv_cd = cd;
970 }
971
972 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
973 {
974     struct yaz_marc_node *n;
975     char *leader = 0;
976     for (n = mt->nodes; n; n = n->next)
977         if (n->which == YAZ_MARC_LEADER)
978         {
979             leader = n->u.leader;
980             memcpy(leader+off, str, strlen(str));
981             break;
982         }
983 }
984
985 /* deprecated */
986 int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
987 {
988     yaz_marc_t mt = yaz_marc_create();
989     int r;
990
991     mt->debug = debug;
992     mt->xml = xml;
993     r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
994     yaz_marc_destroy(mt);
995     return r;
996 }
997
998 /* deprecated */
999 int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
1000 {
1001     return yaz_marc_decode(buf, wr, debug, bsize, 0);
1002 }
1003
1004 /* deprecated */
1005 int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
1006 {
1007     yaz_marc_t mt = yaz_marc_create();
1008     int r;
1009
1010     mt->debug = debug;
1011     r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
1012     if (!outf)
1013         outf = stdout;
1014     if (r > 0)
1015         fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
1016     yaz_marc_destroy(mt);
1017     return r;
1018 }
1019
1020 /* deprecated */
1021 int marc_display_ex (const char *buf, FILE *outf, int debug)
1022 {
1023     return marc_display_exl (buf, outf, debug, -1);
1024 }
1025
1026 /* deprecated */
1027 int marc_display (const char *buf, FILE *outf)
1028 {
1029     return marc_display_ex (buf, outf, 0);
1030 }
1031
1032 int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
1033 {
1034     xfree(mt->leader_spec);
1035     mt->leader_spec = 0;
1036     if (leader_spec)
1037     {
1038         char dummy_leader[24];
1039         if (marc_exec_leader(leader_spec, dummy_leader, 24))
1040             return -1;
1041         mt->leader_spec = xstrdup(leader_spec);
1042     }
1043     return 0;
1044 }
1045
1046 static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
1047 {
1048     const char *cp = leader_spec;
1049     while (cp)
1050     {
1051         char val[21];
1052         int pos;
1053         int no_read = 0, no = 0;
1054
1055         no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
1056         if (no < 2 || no_read < 3)
1057             return -1;
1058         if (pos < 0 || pos >= size)
1059             return -1;
1060
1061         if (*val == '\'')
1062         {
1063             const char *vp = strchr(val+1, '\'');
1064             size_t len;
1065             
1066             if (!vp)
1067                 return -1;
1068             len = vp-val-1;
1069             if (len + pos > size)
1070                 return -1;
1071             memcpy(leader + pos, val+1, len);
1072         }
1073         else if (*val >= '0' && *val <= '9')
1074         {
1075             int ch = atoi(val);
1076             leader[pos] = ch;
1077         }
1078         else
1079             return -1;
1080         cp += no_read;
1081         if (*cp != ',')
1082             break;
1083
1084         cp++;
1085     }
1086     return 0;
1087 }
1088
1089 int yaz_marc_decode_formatstr(const char *arg)
1090 {
1091     int mode = -1; 
1092     if (!strcmp(arg, "marc"))
1093         mode = YAZ_MARC_ISO2709;
1094     if (!strcmp(arg, "marcxml"))
1095         mode = YAZ_MARC_MARCXML;
1096     if (!strcmp(arg, "marcxchange"))
1097         mode = YAZ_MARC_XCHANGE;
1098     if (!strcmp(arg, "line"))
1099         mode = YAZ_MARC_LINE;
1100     return mode;
1101 }
1102
1103 void yaz_marc_write_using_libxml2(yaz_marc_t mt, int enable)
1104 {
1105     mt->write_using_libxml2 = enable;
1106 }
1107
1108 /*
1109  * Local variables:
1110  * c-basic-offset: 4
1111  * indent-tabs-mode: nil
1112  * End:
1113  * vim: shiftwidth=4 tabstop=8 expandtab
1114  */
1115