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