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