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