Fix dup'ed identifers for MARC-8 encoding YAZ-650
[yaz-moved-to-github.git] / src / marcdisp.c
index f56f280..ff4c97e 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * Copyright (C) 1995-2006, Index Data ApS
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2013 Index Data
  * See the file LICENSE for details.
  * See the file LICENSE for details.
- *
- * $Id: marcdisp.c,v 1.28 2006-04-20 20:35:02 adam Exp $
  */
 
 /**
  */
 
 /**
 
 #include <stdarg.h>
 #include <stdio.h>
 
 #include <stdarg.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <string.h>
-#include <ctype.h>
 #include <yaz/marcdisp.h>
 #include <yaz/wrbuf.h>
 #include <yaz/yaz-util.h>
 #include <yaz/marcdisp.h>
 #include <yaz/wrbuf.h>
 #include <yaz/yaz-util.h>
+#include <yaz/nmem_xml.h>
+#include <yaz/snprintf.h>
 
 
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 #endif
 
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 #endif
 
-static void yaz_marc_reset(yaz_marc_t mt);
+enum yaz_collection_state {
+    no_collection,
+    collection_first,
+    collection_second
+};
 
 /** \brief node types for yaz_marc_node */
 enum YAZ_MARC_NODE_TYPE
 
 /** \brief node types for yaz_marc_node */
 enum YAZ_MARC_NODE_TYPE
-{ 
+{
     YAZ_MARC_DATAFIELD,
     YAZ_MARC_CONTROLFIELD,
     YAZ_MARC_COMMENT,
     YAZ_MARC_DATAFIELD,
     YAZ_MARC_CONTROLFIELD,
     YAZ_MARC_COMMENT,
@@ -82,11 +86,14 @@ struct yaz_marc_subfield {
 struct yaz_marc_t_ {
     WRBUF m_wr;
     NMEM nmem;
 struct yaz_marc_t_ {
     WRBUF m_wr;
     NMEM nmem;
-    int xml;
+    int output_format;
     int debug;
     int debug;
+    int write_using_libxml2;
+    enum yaz_collection_state enable_collection;
     yaz_iconv_t iconv_cd;
     char subfield_str[8];
     char endline_str[8];
     yaz_iconv_t iconv_cd;
     char subfield_str[8];
     char endline_str[8];
+    char *leader_spec;
     struct yaz_marc_node *nodes;
     struct yaz_marc_node **nodes_pp;
     struct yaz_marc_subfield **subfield_pp;
     struct yaz_marc_node *nodes;
     struct yaz_marc_node **nodes_pp;
     struct yaz_marc_subfield **subfield_pp;
@@ -95,10 +102,13 @@ struct yaz_marc_t_ {
 yaz_marc_t yaz_marc_create(void)
 {
     yaz_marc_t mt = (yaz_marc_t) xmalloc(sizeof(*mt));
 yaz_marc_t yaz_marc_create(void)
 {
     yaz_marc_t mt = (yaz_marc_t) xmalloc(sizeof(*mt));
-    mt->xml = YAZ_MARC_LINE;
+    mt->output_format = YAZ_MARC_LINE;
     mt->debug = 0;
     mt->debug = 0;
+    mt->write_using_libxml2 = 0;
+    mt->enable_collection = no_collection;
     mt->m_wr = wrbuf_alloc();
     mt->iconv_cd = 0;
     mt->m_wr = wrbuf_alloc();
     mt->iconv_cd = 0;
+    mt->leader_spec = 0;
     strcpy(mt->subfield_str, " $");
     strcpy(mt->endline_str, "\n");
 
     strcpy(mt->subfield_str, " $");
     strcpy(mt->endline_str, "\n");
 
@@ -112,71 +122,91 @@ void yaz_marc_destroy(yaz_marc_t mt)
     if (!mt)
         return ;
     nmem_destroy(mt->nmem);
     if (!mt)
         return ;
     nmem_destroy(mt->nmem);
-    wrbuf_free (mt->m_wr, 1);
-    xfree (mt);
+    wrbuf_destroy(mt->m_wr);
+    xfree(mt->leader_spec);
+    xfree(mt);
+}
+
+NMEM yaz_marc_get_nmem(yaz_marc_t mt)
+{
+    return mt->nmem;
 }
 
 }
 
-struct yaz_marc_node *yaz_marc_add_node(yaz_marc_t mt)
+static void marc_iconv_reset(yaz_marc_t mt, WRBUF wr)
 {
 {
-    struct yaz_marc_node *n = nmem_malloc(mt->nmem, sizeof(*n));
+    wrbuf_iconv_reset(wr, mt->iconv_cd);
+}
+
+static int marc_exec_leader(const char *leader_spec, char *leader,
+                            size_t size);
+#if YAZ_HAVE_XML2
+static int yaz_marc_write_xml_turbo_xml(yaz_marc_t mt, xmlNode **root_ptr,
+                                        const char *ns,
+                                        const char *format,
+                                        const char *type);
+#endif
+
+static struct yaz_marc_node *yaz_marc_add_node(yaz_marc_t mt)
+{
+    struct yaz_marc_node *n = (struct yaz_marc_node *)
+        nmem_malloc(mt->nmem, sizeof(*n));
     n->next = 0;
     *mt->nodes_pp = n;
     mt->nodes_pp = &n->next;
     return n;
 }
 
     n->next = 0;
     *mt->nodes_pp = n;
     mt->nodes_pp = &n->next;
     return n;
 }
 
-void yaz_marc_add_comment(yaz_marc_t mt, char *comment)
+#if YAZ_HAVE_XML2
+void yaz_marc_add_controlfield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
+                                   const xmlNode *ptr_data)
 {
     struct yaz_marc_node *n = yaz_marc_add_node(mt);
 {
     struct yaz_marc_node *n = yaz_marc_add_node(mt);
-    n->which = YAZ_MARC_COMMENT;
-    n->u.comment = nmem_strdup(mt->nmem, comment);
+    n->which = YAZ_MARC_CONTROLFIELD;
+    n->u.controlfield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
+    n->u.controlfield.data = nmem_text_node_cdata(ptr_data, mt->nmem);
 }
 
 }
 
-#if HAVE_XML2
-static char *yaz_marc_get_xml_text(const xmlNode *ptr_cdata, NMEM nmem)
+void yaz_marc_add_controlfield_xml2(yaz_marc_t mt, char *tag,
+                                    const xmlNode *ptr_data)
 {
 {
-    char *cdata;
-    int len = 0;
-    const xmlNode *ptr;
-
-    for (ptr = ptr_cdata; ptr; ptr = ptr->next)
-        if (ptr->type == XML_TEXT_NODE)
-            len += xmlStrlen(ptr->content);
-    cdata = (char *) nmem_malloc(nmem, len+1);
-    *cdata = '\0';
-    for (ptr = ptr_cdata; ptr; ptr = ptr->next)
-        if (ptr->type == XML_TEXT_NODE)
-            strcat(cdata, (const char *) ptr->content);
-    return cdata;
+    struct yaz_marc_node *n = yaz_marc_add_node(mt);
+    n->which = YAZ_MARC_CONTROLFIELD;
+    n->u.controlfield.tag = tag;
+    n->u.controlfield.data = nmem_text_node_cdata(ptr_data, mt->nmem);
 }
 }
+
 #endif
 
 #endif
 
+
+void yaz_marc_add_comment(yaz_marc_t mt, char *comment)
+{
+    struct yaz_marc_node *n = yaz_marc_add_node(mt);
+    n->which = YAZ_MARC_COMMENT;
+    n->u.comment = nmem_strdup(mt->nmem, comment);
+}
+
 void yaz_marc_cprintf(yaz_marc_t mt, const char *fmt, ...)
 {
     va_list ap;
     char buf[200];
 void yaz_marc_cprintf(yaz_marc_t mt, const char *fmt, ...)
 {
     va_list ap;
     char buf[200];
-    va_start(ap, fmt);
 
 
-#ifdef WIN32
-    _vsnprintf(buf, sizeof(buf)-1, fmt, ap);
-#else
-/* !WIN32 */
-#if HAVE_VSNPRINTF
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-#else
-    vsprintf(buf, fmt, ap);
-#endif
-#endif
-/* WIN32 */
+    va_start(ap, fmt);
+    yaz_vsnprintf(buf, sizeof(buf)-1, fmt, ap);
     yaz_marc_add_comment(mt, buf);
     va_end (ap);
 }
 
     yaz_marc_add_comment(mt, buf);
     va_end (ap);
 }
 
+int yaz_marc_get_debug(yaz_marc_t mt)
+{
+    return mt->debug;
+}
+
 void yaz_marc_add_leader(yaz_marc_t mt, const char *leader, size_t leader_len)
 {
     struct yaz_marc_node *n = yaz_marc_add_node(mt);
     n->which = YAZ_MARC_LEADER;
     n->u.leader = nmem_strdupn(mt->nmem, leader, leader_len);
 void yaz_marc_add_leader(yaz_marc_t mt, const char *leader, size_t leader_len)
 {
     struct yaz_marc_node *n = yaz_marc_add_node(mt);
     n->which = YAZ_MARC_LEADER;
     n->u.leader = nmem_strdupn(mt->nmem, leader, leader_len);
+    marc_exec_leader(mt->leader_spec, n->u.leader, leader_len);
 }
 
 void yaz_marc_add_controlfield(yaz_marc_t mt, const char *tag,
 }
 
 void yaz_marc_add_controlfield(yaz_marc_t mt, const char *tag,
@@ -200,17 +230,6 @@ void yaz_marc_add_controlfield(yaz_marc_t mt, const char *tag,
     }
 }
 
     }
 }
 
-#if HAVE_XML2
-void yaz_marc_add_controlfield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
-                                   const xmlNode *ptr_data)
-{
-    struct yaz_marc_node *n = yaz_marc_add_node(mt);
-    n->which = YAZ_MARC_CONTROLFIELD;
-    n->u.controlfield.tag = yaz_marc_get_xml_text(ptr_tag, mt->nmem);
-    n->u.controlfield.data = yaz_marc_get_xml_text(ptr_data, mt->nmem);
-}
-#endif
-
 void yaz_marc_add_datafield(yaz_marc_t mt, const char *tag,
                             const char *indicator, size_t indicator_len)
 {
 void yaz_marc_add_datafield(yaz_marc_t mt, const char *tag,
                             const char *indicator, size_t indicator_len)
 {
@@ -225,13 +244,49 @@ void yaz_marc_add_datafield(yaz_marc_t mt, const char *tag,
     mt->subfield_pp = &n->u.datafield.subfields;
 }
 
     mt->subfield_pp = &n->u.datafield.subfields;
 }
 
-#if HAVE_XML2
+/** \brief adds a attribute value to the element name if it is plain chars
+
+    If not, and if the attribute name is not null, it will append a
+    attribute element with the value if attribute name is null it will
+    return a non-zero value meaning it couldnt handle the value.
+*/
+static int element_name_append_attribute_value(
+    yaz_marc_t mt, WRBUF buffer,
+    const char *attribute_name, char *code_data, size_t code_len)
+{
+    /* TODO Map special codes to something possible for XML ELEMENT names */
+
+    int encode = 0;
+    int index = 0;
+    int success = 0;
+    for (index = 0; index < code_len; index++)
+    {
+        if (!((code_data[index] >= '0' && code_data[index] <= '9') ||
+              (code_data[index] >= 'a' && code_data[index] <= 'z') ||
+              (code_data[index] >= 'A' && code_data[index] <= 'Z')))
+            encode = 1;
+    }
+    /* Add as attribute */
+    if (encode && attribute_name)
+        wrbuf_printf(buffer, " %s=\"", attribute_name);
+
+    if (!encode || attribute_name)
+        wrbuf_iconv_write_cdata(buffer, mt->iconv_cd, code_data, code_len);
+    else
+        success = -1;
+
+    if (encode && attribute_name)
+        wrbuf_printf(buffer, "\""); /* return error if we couldn't handle it.*/
+    return success;
+}
+
+#if YAZ_HAVE_XML2
 void yaz_marc_add_datafield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
                                 const char *indicator, size_t indicator_len)
 {
     struct yaz_marc_node *n = yaz_marc_add_node(mt);
     n->which = YAZ_MARC_DATAFIELD;
 void yaz_marc_add_datafield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
                                 const char *indicator, size_t indicator_len)
 {
     struct yaz_marc_node *n = yaz_marc_add_node(mt);
     n->which = YAZ_MARC_DATAFIELD;
-    n->u.datafield.tag = yaz_marc_get_xml_text(ptr_tag, mt->nmem);
+    n->u.datafield.tag = nmem_text_node_cdata(ptr_tag, mt->nmem);
     n->u.datafield.indicator =
         nmem_strdupn(mt->nmem, indicator, indicator_len);
     n->u.datafield.subfields = 0;
     n->u.datafield.indicator =
         nmem_strdupn(mt->nmem, indicator, indicator_len);
     n->u.datafield.subfields = 0;
@@ -239,6 +294,24 @@ void yaz_marc_add_datafield_xml(yaz_marc_t mt, const xmlNode *ptr_tag,
     /* make subfield_pp the current (last one) */
     mt->subfield_pp = &n->u.datafield.subfields;
 }
     /* make subfield_pp the current (last one) */
     mt->subfield_pp = &n->u.datafield.subfields;
 }
+
+void yaz_marc_add_datafield_xml2(yaz_marc_t mt, char *tag_value, char *indicators)
+{
+    struct yaz_marc_node *n = yaz_marc_add_node(mt);
+    n->which = YAZ_MARC_DATAFIELD;
+    n->u.datafield.tag = tag_value;
+    n->u.datafield.indicator = indicators;
+    n->u.datafield.subfields = 0;
+
+    /* make subfield_pp the current (last one) */
+    mt->subfield_pp = &n->u.datafield.subfields;
+}
+
+void yaz_marc_datafield_set_indicators(struct yaz_marc_node *n, char *indicator)
+{
+    n->u.datafield.indicator = indicator;
+}
+
 #endif
 
 void yaz_marc_add_subfield(yaz_marc_t mt,
 #endif
 
 void yaz_marc_add_subfield(yaz_marc_t mt,
@@ -259,7 +332,8 @@ void yaz_marc_add_subfield(yaz_marc_t mt,
 
     if (mt->subfield_pp)
     {
 
     if (mt->subfield_pp)
     {
-        struct yaz_marc_subfield *n = nmem_malloc(mt->nmem, sizeof(*n));
+        struct yaz_marc_subfield *n = (struct yaz_marc_subfield *)
+            nmem_malloc(mt->nmem, sizeof(*n));
         n->code_data = nmem_strdupn(mt->nmem, code_data, code_data_len);
         n->next = 0;
         /* mark subfield_pp to point to this one, so we append here next */
         n->code_data = nmem_strdupn(mt->nmem, code_data, code_data_len);
         n->next = 0;
         /* mark subfield_pp to point to this one, so we append here next */
@@ -268,39 +342,38 @@ void yaz_marc_add_subfield(yaz_marc_t mt,
     }
 }
 
     }
 }
 
-static int atoi_n_check(const char *buf, int size, int *val)
+static void check_ascii(yaz_marc_t mt, char *leader, int offset,
+                        int ch_default)
 {
 {
-    if (!isdigit(*(const unsigned char *) buf))
-        return 0;
-    *val = atoi_n(buf, size);
-    return 1;
+    if (leader[offset] < ' ' || leader[offset] > 127)
+    {
+        yaz_marc_cprintf(mt,
+                         "Leader character at offset %d is non-ASCII. "
+                         "Setting value to '%c'", offset, ch_default);
+        leader[offset] = ch_default;
+    }
 }
 
 }
 
-/** \brief reads the MARC 24 bytes leader and checks content
-    \param mt handle
-    \param leader of the 24 byte leader
-    \param indicator_length indicator length
-    \param identifier_length identifier length
-    \param base_address base address
-    \param length_data_entry length of data entry
-    \param length_starting length of starting 
-    \param length_implementation length of implementation defined data
-*/
-static void yaz_marc_read_leader(yaz_marc_t mt, const char *leader_c,
-                                 int *indicator_length,
-                                 int *identifier_length,
-                                 int *base_address,
-                                 int *length_data_entry,
-                                 int *length_starting,
-                                 int *length_implementation)
+void yaz_marc_set_leader(yaz_marc_t mt, const char *leader_c,
+                         int *indicator_length,
+                         int *identifier_length,
+                         int *base_address,
+                         int *length_data_entry,
+                         int *length_starting,
+                         int *length_implementation)
 {
     char leader[24];
 
     memcpy(leader, leader_c, 24);
 
 {
     char leader[24];
 
     memcpy(leader, leader_c, 24);
 
+    check_ascii(mt, leader, 5, 'a');
+    check_ascii(mt, leader, 6, 'a');
+    check_ascii(mt, leader, 7, 'a');
+    check_ascii(mt, leader, 8, '#');
+    check_ascii(mt, leader, 9, '#');
     if (!atoi_n_check(leader+10, 1, indicator_length))
     {
     if (!atoi_n_check(leader+10, 1, indicator_length))
     {
-        yaz_marc_cprintf(mt, 
+        yaz_marc_cprintf(mt,
                          "Indicator length at offset 10 should hold a digit."
                          " Assuming 2");
         leader[10] = '2';
                          "Indicator length at offset 10 should hold a digit."
                          " Assuming 2");
         leader[10] = '2';
@@ -308,7 +381,7 @@ static void yaz_marc_read_leader(yaz_marc_t mt, const char *leader_c,
     }
     if (!atoi_n_check(leader+11, 1, identifier_length))
     {
     }
     if (!atoi_n_check(leader+11, 1, identifier_length))
     {
-        yaz_marc_cprintf(mt, 
+        yaz_marc_cprintf(mt,
                          "Identifier length at offset 11 should hold a digit."
                          " Assuming 2");
         leader[11] = '2';
                          "Identifier length at offset 11 should hold a digit."
                          " Assuming 2");
         leader[11] = '2';
@@ -316,14 +389,17 @@ static void yaz_marc_read_leader(yaz_marc_t mt, const char *leader_c,
     }
     if (!atoi_n_check(leader+12, 5, base_address))
     {
     }
     if (!atoi_n_check(leader+12, 5, base_address))
     {
-        yaz_marc_cprintf(mt, 
+        yaz_marc_cprintf(mt,
                          "Base address at offsets 12..16 should hold a number."
                          " Assuming 0");
         *base_address = 0;
     }
                          "Base address at offsets 12..16 should hold a number."
                          " Assuming 0");
         *base_address = 0;
     }
+    check_ascii(mt, leader, 17, '#');
+    check_ascii(mt, leader, 18, '#');
+    check_ascii(mt, leader, 19, '#');
     if (!atoi_n_check(leader+20, 1, length_data_entry))
     {
     if (!atoi_n_check(leader+20, 1, length_data_entry))
     {
-        yaz_marc_cprintf(mt, 
+        yaz_marc_cprintf(mt,
                          "Length data entry at offset 20 should hold a digit."
                          " Assuming 4");
         *length_data_entry = 4;
                          "Length data entry at offset 20 should hold a digit."
                          " Assuming 4");
         *length_data_entry = 4;
@@ -339,12 +415,13 @@ static void yaz_marc_read_leader(yaz_marc_t mt, const char *leader_c,
     }
     if (!atoi_n_check(leader+22, 1, length_implementation))
     {
     }
     if (!atoi_n_check(leader+22, 1, length_implementation))
     {
-        yaz_marc_cprintf(mt, 
+        yaz_marc_cprintf(mt,
                          "Length implementation at offset 22 should hold a digit."
                          " Assuming 0");
         *length_implementation = 0;
         leader[22] = '0';
     }
                          "Length implementation at offset 22 should hold a digit."
                          " Assuming 0");
         *length_implementation = 0;
         leader[22] = '0';
     }
+    check_ascii(mt, leader, 23, '0');
 
     if (mt->debug)
     {
 
     if (mt->debug)
     {
@@ -386,6 +463,7 @@ static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
             size_t inbytesleft = i;
             size_t r = yaz_iconv(mt->iconv_cd, (char**) &inp, &inbytesleft,
                                  &outp, &outbytesleft);
             size_t inbytesleft = i;
             size_t r = yaz_iconv(mt->iconv_cd, (char**) &inp, &inbytesleft,
                                  &outp, &outbytesleft);
+            yaz_iconv(mt->iconv_cd, 0, 0, &outp, &outbytesleft);
             if (r != (size_t) (-1))
                 return i;  /* got a complete sequence */
         }
             if (r != (size_t) (-1))
                 return i;  /* got a complete sequence */
         }
@@ -393,8 +471,8 @@ static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
     }
     return 1; /* we don't know */
 }
     }
     return 1; /* we don't know */
 }
-                              
-static void yaz_marc_reset(yaz_marc_t mt)
+
+void yaz_marc_reset(yaz_marc_t mt)
 {
     nmem_reset(mt->nmem);
     mt->nodes = 0;
 {
     nmem_reset(mt->nmem);
     mt->nodes = 0;
@@ -402,6 +480,52 @@ static void yaz_marc_reset(yaz_marc_t mt)
     mt->subfield_pp = 0;
 }
 
     mt->subfield_pp = 0;
 }
 
+int yaz_marc_write_check(yaz_marc_t mt, WRBUF wr)
+{
+    struct yaz_marc_node *n;
+    int identifier_length;
+    const char *leader = 0;
+
+    for (n = mt->nodes; n; n = n->next)
+        if (n->which == YAZ_MARC_LEADER)
+        {
+            leader = n->u.leader;
+            break;
+        }
+
+    if (!leader)
+        return -1;
+    if (!atoi_n_check(leader+11, 1, &identifier_length))
+        return -1;
+
+    for (n = mt->nodes; n; n = n->next)
+    {
+        switch(n->which)
+        {
+        case YAZ_MARC_COMMENT:
+            wrbuf_iconv_write(wr, mt->iconv_cd,
+                              n->u.comment, strlen(n->u.comment));
+            wrbuf_puts(wr, "\n");
+            break;
+        default:
+            break;
+        }
+    }
+    return 0;
+}
+
+static size_t get_subfield_len(yaz_marc_t mt, const char *data,
+                               int identifier_length)
+{
+    /* if identifier length is 2 (most MARCs) or less (probably an error),
+       the code is a single character .. However we've
+       seen multibyte codes, so see how big it really is */
+    if (identifier_length > 2)
+        return identifier_length - 1;
+    else
+        return cdata_one_character(mt, data);
+}
+
 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
 {
     struct yaz_marc_node *n;
 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
 {
     struct yaz_marc_node *n;
@@ -414,7 +538,7 @@ int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
             leader = n->u.leader;
             break;
         }
             leader = n->u.leader;
             break;
         }
-    
+
     if (!leader)
         return -1;
     if (!atoi_n_check(leader+11, 1, &identifier_length))
     if (!leader)
         return -1;
     if (!atoi_n_check(leader+11, 1, &identifier_length))
@@ -430,68 +554,106 @@ int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
                          n->u.datafield.indicator);
             for (s = n->u.datafield.subfields; s; s = s->next)
             {
                          n->u.datafield.indicator);
             for (s = n->u.datafield.subfields; s; s = s->next)
             {
-                /* if identifier length is 2 (most MARCs),
-                   the code is a single character .. However we've
-                   seen multibyte codes, so see how big it really is */
-                size_t using_code_len = 
-                    (identifier_length != 2) ? identifier_length - 1
-                    :
-                    cdata_one_character(mt, s->code_data);
-                
-                wrbuf_puts (wr, mt->subfield_str); 
-                wrbuf_iconv_write(wr, mt->iconv_cd, s->code_data, 
+                size_t using_code_len = get_subfield_len(mt, s->code_data,
+                                                         identifier_length);
+
+                wrbuf_puts (wr, mt->subfield_str);
+                wrbuf_iconv_write(wr, mt->iconv_cd, s->code_data,
                                   using_code_len);
                                   using_code_len);
-                wrbuf_printf(wr, " ");
-                wrbuf_iconv_puts(wr, mt->iconv_cd, 
+                wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
+                wrbuf_iconv_puts(wr, mt->iconv_cd,
                                  s->code_data + using_code_len);
                                  s->code_data + using_code_len);
+                marc_iconv_reset(mt, wr);
             }
             wrbuf_puts (wr, mt->endline_str);
             break;
         case YAZ_MARC_CONTROLFIELD:
             }
             wrbuf_puts (wr, mt->endline_str);
             break;
         case YAZ_MARC_CONTROLFIELD:
-            wrbuf_printf(wr, "%s ", n->u.controlfield.tag);
+            wrbuf_printf(wr, "%s", n->u.controlfield.tag);
+            wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
             wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
+            marc_iconv_reset(mt, wr);
             wrbuf_puts (wr, mt->endline_str);
             break;
         case YAZ_MARC_COMMENT:
             wrbuf_puts(wr, "(");
             wrbuf_puts (wr, mt->endline_str);
             break;
         case YAZ_MARC_COMMENT:
             wrbuf_puts(wr, "(");
-            wrbuf_iconv_write(wr, mt->iconv_cd, 
+            wrbuf_iconv_write(wr, mt->iconv_cd,
                               n->u.comment, strlen(n->u.comment));
                               n->u.comment, strlen(n->u.comment));
+            marc_iconv_reset(mt, wr);
             wrbuf_puts(wr, ")\n");
             break;
         case YAZ_MARC_LEADER:
             wrbuf_printf(wr, "%s\n", n->u.leader);
         }
     }
             wrbuf_puts(wr, ")\n");
             break;
         case YAZ_MARC_LEADER:
             wrbuf_printf(wr, "%s\n", n->u.leader);
         }
     }
+    wrbuf_puts(wr, "\n");
     return 0;
 }
 
     return 0;
 }
 
+int yaz_marc_write_trailer(yaz_marc_t mt, WRBUF wr)
+{
+    if (mt->enable_collection == collection_second)
+    {
+        switch(mt->output_format)
+        {
+        case YAZ_MARC_MARCXML:
+        case YAZ_MARC_TURBOMARC:
+            wrbuf_printf(wr, "</collection>\n");
+            break;
+        case YAZ_MARC_XCHANGE:
+            wrbuf_printf(wr, "</collection>\n");
+            break;
+        }
+    }
+    return 0;
+}
+
+void yaz_marc_enable_collection(yaz_marc_t mt)
+{
+    mt->enable_collection = collection_first;
+}
+
 int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
 {
 int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
 {
-    switch(mt->xml)
+    switch(mt->output_format)
     {
     case YAZ_MARC_LINE:
         return yaz_marc_write_line(mt, wr);
     case YAZ_MARC_MARCXML:
         return yaz_marc_write_marcxml(mt, wr);
     {
     case YAZ_MARC_LINE:
         return yaz_marc_write_line(mt, wr);
     case YAZ_MARC_MARCXML:
         return yaz_marc_write_marcxml(mt, wr);
+    case YAZ_MARC_TURBOMARC:
+        return yaz_marc_write_turbomarc(mt, wr);
     case YAZ_MARC_XCHANGE:
         return yaz_marc_write_marcxchange(mt, wr, 0, 0); /* no format, type */
     case YAZ_MARC_ISO2709:
         return yaz_marc_write_iso2709(mt, wr);
     case YAZ_MARC_XCHANGE:
         return yaz_marc_write_marcxchange(mt, wr, 0, 0); /* no format, type */
     case YAZ_MARC_ISO2709:
         return yaz_marc_write_iso2709(mt, wr);
+    case YAZ_MARC_CHECK:
+        return yaz_marc_write_check(mt, wr);
     }
     return -1;
 }
 
     }
     return -1;
 }
 
-/** \brief common MARC XML/Xchange writer
+static const char *record_name[2]      = { "record", "r"};
+static const char *leader_name[2]      = { "leader", "l"};
+static const char *controlfield_name[2] = { "controlfield", "c"};
+static const char *datafield_name[2]   = { "datafield", "d"};
+static const char *indicator_name[2]   = { "ind", "i"};
+static const char *subfield_name[2]    = { "subfield", "s"};
+
+/** \brief common MARC XML/Xchange/turbomarc writer
     \param mt handle
     \param wr WRBUF output
     \param ns XMLNS for the elements
     \param format record format (e.g. "MARC21")
     \param type record type (e.g. "Bibliographic")
     \param mt handle
     \param wr WRBUF output
     \param ns XMLNS for the elements
     \param format record format (e.g. "MARC21")
     \param type record type (e.g. "Bibliographic")
+    \param turbo =1 for turbomarc
+    \retval 0 OK
+    \retval -1 failure
 */
 */
-static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
-                                     const char *ns, 
-                                     const char *format,
-                                     const char *type)
+static int yaz_marc_write_marcxml_wrbuf(yaz_marc_t mt, WRBUF wr,
+                                        const char *ns,
+                                        const char *format,
+                                        const char *type,
+                                        int turbo)
 {
     struct yaz_marc_node *n;
     int identifier_length;
 {
     struct yaz_marc_node *n;
     int identifier_length;
@@ -503,13 +665,25 @@ static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
             leader = n->u.leader;
             break;
         }
             leader = n->u.leader;
             break;
         }
-    
+
     if (!leader)
         return -1;
     if (!atoi_n_check(leader+11, 1, &identifier_length))
         return -1;
 
     if (!leader)
         return -1;
     if (!atoi_n_check(leader+11, 1, &identifier_length))
         return -1;
 
-    wrbuf_printf(wr, "<record xmlns=\"%s\"", ns);
+    if (mt->enable_collection != no_collection)
+    {
+        if (mt->enable_collection == collection_first)
+        {
+            wrbuf_printf(wr, "<collection xmlns=\"%s\">\n", ns);
+            mt->enable_collection = collection_second;
+        }
+        wrbuf_printf(wr, "<%s", record_name[turbo]);
+    }
+    else
+    {
+        wrbuf_printf(wr, "<%s xmlns=\"%s\"", record_name[turbo], ns);
+    }
     if (format)
         wrbuf_printf(wr, " format=\"%.80s\"", format);
     if (type)
     if (format)
         wrbuf_printf(wr, " format=\"%.80s\"", format);
     if (type)
@@ -518,74 +692,165 @@ static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
     for (n = mt->nodes; n; n = n->next)
     {
         struct yaz_marc_subfield *s;
     for (n = mt->nodes; n; n = n->next)
     {
         struct yaz_marc_subfield *s;
+
         switch(n->which)
         {
         case YAZ_MARC_DATAFIELD:
         switch(n->which)
         {
         case YAZ_MARC_DATAFIELD:
-            wrbuf_printf(wr, "  <datafield tag=\"");
+
+            wrbuf_printf(wr, "  <%s", datafield_name[turbo]);
+            if (!turbo)
+               wrbuf_printf(wr, " tag=\"");
             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
                                     strlen(n->u.datafield.tag));
             wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
                                     strlen(n->u.datafield.tag));
-            wrbuf_printf(wr, "\"");
-            if (n->u.datafield.indicator)
-            {
-                int i;
-                for (i = 0; n->u.datafield.indicator[i]; i++)
-                {
-                    wrbuf_printf(wr, " ind%d=\"", i+1);
+            if (!turbo)
+                wrbuf_printf(wr, "\"");
+           if (n->u.datafield.indicator)
+           {
+               int i;
+               for (i = 0; n->u.datafield.indicator[i]; i++)
+               {
+                    wrbuf_printf(wr, " %s%d=\"", indicator_name[turbo], i+1);
                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
-                                          n->u.datafield.indicator+i, 1);
-                    wrbuf_printf(wr, "\"");
+                                            n->u.datafield.indicator+i, 1);
+                    wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
                 }
             }
             wrbuf_printf(wr, ">\n");
             for (s = n->u.datafield.subfields; s; s = s->next)
             {
                 }
             }
             wrbuf_printf(wr, ">\n");
             for (s = n->u.datafield.subfields; s; s = s->next)
             {
-                /* if identifier length is 2 (most MARCs),
-                   the code is a single character .. However we've
-                   seen multibyte codes, so see how big it really is */
-                size_t using_code_len = 
-                    (identifier_length != 2) ? identifier_length - 1
-                    :
-                    cdata_one_character(mt, s->code_data);
-                
-                wrbuf_puts(wr, "    <subfield code=\"");
-                wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
-                                        s->code_data, using_code_len);
-                wrbuf_puts(wr, "\">");
+                size_t using_code_len = get_subfield_len(mt, s->code_data,
+                                                         identifier_length);
+                wrbuf_printf(wr, "    <%s", subfield_name[turbo]);
+                if (!turbo)
+                {
+                    wrbuf_printf(wr, " code=\"");
+                    wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
+                                            s->code_data, using_code_len);
+                    wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
+                }
+                else
+                {
+                    element_name_append_attribute_value(mt, wr, "code", s->code_data, using_code_len);
+                    wrbuf_puts(wr, ">");
+                }
                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
                                         s->code_data + using_code_len,
                                         strlen(s->code_data + using_code_len));
                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
                                         s->code_data + using_code_len,
                                         strlen(s->code_data + using_code_len));
-                wrbuf_puts(wr, "</subfield>\n");
+                marc_iconv_reset(mt, wr);
+                wrbuf_printf(wr, "</%s", subfield_name[turbo]);
+                if (turbo)
+                    element_name_append_attribute_value(mt, wr, 0, s->code_data, using_code_len);
+                wrbuf_puts(wr, ">\n");
             }
             }
-            wrbuf_printf(wr, "  </datafield>\n");
+            wrbuf_printf(wr, "  </%s", datafield_name[turbo]);
+            /* TODO Not CDATA */
+            if (turbo)
+               wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.datafield.tag,
+                                        strlen(n->u.datafield.tag));
+            wrbuf_printf(wr, ">\n");
             break;
         case YAZ_MARC_CONTROLFIELD:
             break;
         case YAZ_MARC_CONTROLFIELD:
-            wrbuf_printf(wr, "  <controlfield tag=\"");
-            wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
-                                    strlen(n->u.controlfield.tag));
-            wrbuf_printf(wr, "\">");
-            wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
-            wrbuf_printf(wr, "</controlfield>\n");
+            wrbuf_printf(wr, "  <%s", controlfield_name[turbo]);
+            if (!turbo)
+            {
+               wrbuf_printf(wr, " tag=\"");
+                wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
+                                       strlen(n->u.controlfield.tag));
+                wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
+            }
+            else
+            {
+                /* TODO convert special */
+                wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
+                                       strlen(n->u.controlfield.tag));
+                wrbuf_iconv_puts(wr, mt->iconv_cd, ">");
+            }
+            wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
+                                    n->u.controlfield.data,
+                                    strlen(n->u.controlfield.data));
+            marc_iconv_reset(mt, wr);
+            wrbuf_printf(wr, "</%s", controlfield_name[turbo]);
+            /* TODO convert special */
+            if (turbo)
+                wrbuf_iconv_write_cdata(wr, mt->iconv_cd, n->u.controlfield.tag,
+                                       strlen(n->u.controlfield.tag));
+            wrbuf_puts(wr, ">\n");
             break;
         case YAZ_MARC_COMMENT:
             break;
         case YAZ_MARC_COMMENT:
-            wrbuf_printf(wr, "<!-- %s -->\n", n->u.comment);
+            wrbuf_printf(wr, "<!-- ");
+            wrbuf_puts(wr, n->u.comment);
+            wrbuf_printf(wr, " -->\n");
             break;
         case YAZ_MARC_LEADER:
             break;
         case YAZ_MARC_LEADER:
-            wrbuf_printf(wr, "  <leader>");
-            wrbuf_iconv_write_cdata(wr, 
-                                    0 /* no charset conversion for leader */,
+            wrbuf_printf(wr, "  <%s>", leader_name[turbo]);
+            wrbuf_iconv_write_cdata(wr,
+                                    0 , /* no charset conversion for leader */
                                     n->u.leader, strlen(n->u.leader));
                                     n->u.leader, strlen(n->u.leader));
-            wrbuf_printf(wr, "</leader>\n");
+            wrbuf_printf(wr, "</%s>\n", leader_name[turbo]);
         }
     }
         }
     }
-    wrbuf_puts(wr, "</record>\n");
+    wrbuf_printf(wr, "</%s>\n", record_name[turbo]);
     return 0;
 }
 
     return 0;
 }
 
+static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
+                                     const char *ns,
+                                     const char *format,
+                                     const char *type,
+                                     int turbo)
+{
+    if (mt->write_using_libxml2)
+    {
+#if YAZ_HAVE_XML2
+        int ret;
+        xmlNode *root_ptr;
+
+        if (!turbo)
+            ret = yaz_marc_write_xml(mt, &root_ptr, ns, format, type);
+        else
+            ret = yaz_marc_write_xml_turbo_xml(mt, &root_ptr, ns, format, type);
+        if (ret == 0)
+        {
+            xmlChar *buf_out;
+            xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
+            int len_out;
+
+            xmlDocSetRootElement(doc, root_ptr);
+            xmlDocDumpMemory(doc, &buf_out, &len_out);
+
+            wrbuf_write(wr, (const char *) buf_out, len_out);
+            wrbuf_puts(wr, "");
+            xmlFree(buf_out);
+            xmlFreeDoc(doc);
+        }
+        return ret;
+#else
+        return -1;
+#endif
+    }
+    else
+        return yaz_marc_write_marcxml_wrbuf(mt, wr, ns, format, type, turbo);
+}
+
 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
 {
 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
 {
-    yaz_marc_modify_leader(mt, 9, "a");
-    return yaz_marc_write_marcxml_ns(mt, wr, "http://www.loc.gov/MARC21/slim",
-                                     0, 0);
+    /* set leader 09 to 'a' for UNICODE */
+    /* http://www.loc.gov/marc/bibliographic/ecbdldrd.html#mrcblea */
+    if (!mt->leader_spec)
+        yaz_marc_modify_leader(mt, 9, "a");
+    return yaz_marc_write_marcxml_ns(mt, wr,
+                                     "http://www.loc.gov/MARC21/slim",
+                                     0, 0, 0);
+}
+
+int yaz_marc_write_turbomarc(yaz_marc_t mt, WRBUF wr)
+{
+    /* set leader 09 to 'a' for UNICODE */
+    /* http://www.loc.gov/marc/bibliographic/ecbdldrd.html#mrcblea */
+    if (!mt->leader_spec)
+        yaz_marc_modify_leader(mt, 9, "a");
+    return yaz_marc_write_marcxml_ns(mt, wr,
+                                     "http://www.indexdata.com/turbomarc", 0, 0, 1);
 }
 
 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
 }
 
 int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
@@ -593,10 +858,250 @@ int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
                                const char *type)
 {
     return yaz_marc_write_marcxml_ns(mt, wr,
                                const char *type)
 {
     return yaz_marc_write_marcxml_ns(mt, wr,
-                                     "http://www.bs.dk/standards/MarcXchange",
-                                     0, 0);
+                                     "info:lc/xmlns/marcxchange-v1",
+                                     0, 0, 0);
+}
+
+#if YAZ_HAVE_XML2
+
+void add_marc_datafield_turbo_xml(yaz_marc_t mt, struct yaz_marc_node *n,
+                                  xmlNode *record_ptr,
+                                  xmlNsPtr ns_record, WRBUF wr_cdata,
+                                  int identifier_length)
+{
+    xmlNode *ptr;
+    struct yaz_marc_subfield *s;
+    WRBUF subfield_name = wrbuf_alloc();
+
+    /* TODO consider if safe */
+    char field[10];
+    field[0] = 'd';
+    strncpy(field + 1, n->u.datafield.tag, 3);
+    field[4] = '\0';
+    ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST field, 0);
+
+    if (n->u.datafield.indicator)
+    {
+        int i;
+        for (i = 0; n->u.datafield.indicator[i]; i++)
+        {
+            char ind_str[6];
+            char ind_val[2];
+
+            ind_val[0] = n->u.datafield.indicator[i];
+            ind_val[1] = '\0';
+            sprintf(ind_str, "%s%d", indicator_name[1], i+1);
+            xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
+        }
+    }
+    for (s = n->u.datafield.subfields; s; s = s->next)
+    {
+        int not_written;
+        xmlNode *ptr_subfield;
+        size_t using_code_len = get_subfield_len(mt, s->code_data,
+                                                 identifier_length);
+        wrbuf_rewind(wr_cdata);
+        wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, s->code_data + using_code_len);
+        marc_iconv_reset(mt, wr_cdata);
+
+        wrbuf_rewind(subfield_name);
+        wrbuf_puts(subfield_name, "s");
+        not_written = element_name_append_attribute_value(mt, subfield_name, 0, s->code_data, using_code_len) != 0;
+        ptr_subfield = xmlNewTextChild(ptr, ns_record,
+                                       BAD_CAST wrbuf_cstr(subfield_name),
+                                       BAD_CAST wrbuf_cstr(wr_cdata));
+        if (not_written)
+        {
+            /* Generate code attribute value and add */
+            wrbuf_rewind(wr_cdata);
+            wrbuf_iconv_write(wr_cdata, mt->iconv_cd,s->code_data, using_code_len);
+            xmlNewProp(ptr_subfield, BAD_CAST "code",  BAD_CAST wrbuf_cstr(wr_cdata));
+        }
+    }
+    wrbuf_destroy(subfield_name);
+}
+
+static int yaz_marc_write_xml_turbo_xml(yaz_marc_t mt, xmlNode **root_ptr,
+                                        const char *ns,
+                                        const char *format,
+                                        const char *type)
+{
+    struct yaz_marc_node *n;
+    int identifier_length;
+    const char *leader = 0;
+    xmlNode *record_ptr;
+    xmlNsPtr ns_record;
+    WRBUF wr_cdata = 0;
+
+    for (n = mt->nodes; n; n = n->next)
+        if (n->which == YAZ_MARC_LEADER)
+        {
+            leader = n->u.leader;
+            break;
+        }
+
+    if (!leader)
+        return -1;
+    if (!atoi_n_check(leader+11, 1, &identifier_length))
+        return -1;
+
+    wr_cdata = wrbuf_alloc();
+
+    record_ptr = xmlNewNode(0, BAD_CAST "r");
+    *root_ptr = record_ptr;
+
+    ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
+    xmlSetNs(record_ptr, ns_record);
+
+    if (format)
+        xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
+    if (type)
+        xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
+    for (n = mt->nodes; n; n = n->next)
+    {
+        xmlNode *ptr;
+
+        char field[10];
+        field[0] = 'c';
+        field[4] = '\0';
+
+        switch(n->which)
+        {
+        case YAZ_MARC_DATAFIELD:
+            add_marc_datafield_turbo_xml(mt, n, record_ptr, ns_record, wr_cdata, identifier_length);
+            break;
+        case YAZ_MARC_CONTROLFIELD:
+            wrbuf_rewind(wr_cdata);
+            wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
+            marc_iconv_reset(mt, wr_cdata);
+
+            strncpy(field + 1, n->u.controlfield.tag, 3);
+            ptr = xmlNewTextChild(record_ptr, ns_record,
+                                  BAD_CAST field,
+                                  BAD_CAST wrbuf_cstr(wr_cdata));
+            break;
+        case YAZ_MARC_COMMENT:
+            ptr = xmlNewComment(BAD_CAST n->u.comment);
+            xmlAddChild(record_ptr, ptr);
+            break;
+        case YAZ_MARC_LEADER:
+            xmlNewTextChild(record_ptr, ns_record, BAD_CAST "l",
+                            BAD_CAST n->u.leader);
+            break;
+        }
+    }
+    wrbuf_destroy(wr_cdata);
+    return 0;
+}
+
+
+int yaz_marc_write_xml(yaz_marc_t mt, xmlNode **root_ptr,
+                       const char *ns,
+                       const char *format,
+                       const char *type)
+{
+    struct yaz_marc_node *n;
+    int identifier_length;
+    const char *leader = 0;
+    xmlNode *record_ptr;
+    xmlNsPtr ns_record;
+    WRBUF wr_cdata = 0;
+
+    for (n = mt->nodes; n; n = n->next)
+        if (n->which == YAZ_MARC_LEADER)
+        {
+            leader = n->u.leader;
+            break;
+        }
+
+    if (!leader)
+        return -1;
+    if (!atoi_n_check(leader+11, 1, &identifier_length))
+        return -1;
+
+    wr_cdata = wrbuf_alloc();
+
+    record_ptr = xmlNewNode(0, BAD_CAST "record");
+    *root_ptr = record_ptr;
+
+    ns_record = xmlNewNs(record_ptr, BAD_CAST ns, 0);
+    xmlSetNs(record_ptr, ns_record);
+
+    if (format)
+        xmlNewProp(record_ptr, BAD_CAST "format", BAD_CAST format);
+    if (type)
+        xmlNewProp(record_ptr, BAD_CAST "type", BAD_CAST type);
+    for (n = mt->nodes; n; n = n->next)
+    {
+        struct yaz_marc_subfield *s;
+        xmlNode *ptr;
+
+        switch(n->which)
+        {
+        case YAZ_MARC_DATAFIELD:
+            ptr = xmlNewChild(record_ptr, ns_record, BAD_CAST "datafield", 0);
+            xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.datafield.tag);
+            if (n->u.datafield.indicator)
+            {
+                int i;
+                for (i = 0; n->u.datafield.indicator[i]; i++)
+                {
+                    char ind_str[6];
+                    char ind_val[2];
+
+                    sprintf(ind_str, "ind%d", i+1);
+                    ind_val[0] = n->u.datafield.indicator[i];
+                    ind_val[1] = '\0';
+                    xmlNewProp(ptr, BAD_CAST ind_str, BAD_CAST ind_val);
+                }
+            }
+            for (s = n->u.datafield.subfields; s; s = s->next)
+            {
+                xmlNode *ptr_subfield;
+                size_t using_code_len = get_subfield_len(mt, s->code_data,
+                                                         identifier_length);
+                wrbuf_rewind(wr_cdata);
+                wrbuf_iconv_puts(wr_cdata, mt->iconv_cd,
+                                 s->code_data + using_code_len);
+                marc_iconv_reset(mt, wr_cdata);
+                ptr_subfield = xmlNewTextChild(
+                    ptr, ns_record,
+                    BAD_CAST "subfield",  BAD_CAST wrbuf_cstr(wr_cdata));
+
+                wrbuf_rewind(wr_cdata);
+                wrbuf_iconv_write(wr_cdata, mt->iconv_cd,
+                                  s->code_data, using_code_len);
+                xmlNewProp(ptr_subfield, BAD_CAST "code",
+                           BAD_CAST wrbuf_cstr(wr_cdata));
+            }
+            break;
+        case YAZ_MARC_CONTROLFIELD:
+            wrbuf_rewind(wr_cdata);
+            wrbuf_iconv_puts(wr_cdata, mt->iconv_cd, n->u.controlfield.data);
+            marc_iconv_reset(mt, wr_cdata);
+
+            ptr = xmlNewTextChild(record_ptr, ns_record,
+                                  BAD_CAST "controlfield",
+                                  BAD_CAST wrbuf_cstr(wr_cdata));
+
+            xmlNewProp(ptr, BAD_CAST "tag", BAD_CAST n->u.controlfield.tag);
+            break;
+        case YAZ_MARC_COMMENT:
+            ptr = xmlNewComment(BAD_CAST n->u.comment);
+            xmlAddChild(record_ptr, ptr);
+            break;
+        case YAZ_MARC_LEADER:
+            xmlNewTextChild(record_ptr, ns_record, BAD_CAST "leader",
+                            BAD_CAST n->u.leader);
+            break;
+        }
+    }
+    wrbuf_destroy(wr_cdata);
+    return 0;
 }
 
 }
 
+#endif
+
 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
 {
     struct yaz_marc_node *n;
 int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
 {
     struct yaz_marc_node *n;
@@ -607,13 +1112,13 @@ int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
     int length_implementation;
     int data_offset = 0;
     const char *leader = 0;
     int length_implementation;
     int data_offset = 0;
     const char *leader = 0;
-    WRBUF wr_dir, wr_head;
+    WRBUF wr_dir, wr_head, wr_data_tmp;
     int base_address;
     int base_address;
-    
+
     for (n = mt->nodes; n; n = n->next)
         if (n->which == YAZ_MARC_LEADER)
             leader = n->u.leader;
     for (n = mt->nodes; n; n = n->next)
         if (n->which == YAZ_MARC_LEADER)
             leader = n->u.leader;
-    
+
     if (!leader)
         return -1;
     if (!atoi_n_check(leader+10, 1, &indicator_length))
     if (!leader)
         return -1;
     if (!atoi_n_check(leader+10, 1, &indicator_length))
@@ -627,24 +1132,41 @@ int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
     if (!atoi_n_check(leader+22, 1, &length_implementation))
         return -1;
 
     if (!atoi_n_check(leader+22, 1, &length_implementation))
         return -1;
 
+    wr_data_tmp = wrbuf_alloc();
     wr_dir = wrbuf_alloc();
     for (n = mt->nodes; n; n = n->next)
     {
         int data_length = 0;
         struct yaz_marc_subfield *s;
     wr_dir = wrbuf_alloc();
     for (n = mt->nodes; n; n = n->next)
     {
         int data_length = 0;
         struct yaz_marc_subfield *s;
+
         switch(n->which)
         {
         case YAZ_MARC_DATAFIELD:
             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
             data_length += indicator_length;
         switch(n->which)
         {
         case YAZ_MARC_DATAFIELD:
             wrbuf_printf(wr_dir, "%.3s", n->u.datafield.tag);
             data_length += indicator_length;
+            wrbuf_rewind(wr_data_tmp);
             for (s = n->u.datafield.subfields; s; s = s->next)
             for (s = n->u.datafield.subfields; s; s = s->next)
-                data_length += 1+strlen(s->code_data);
-            data_length++;
+            {
+                /* write dummy IDFS + content */
+                wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
+                wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd, s->code_data);
+                marc_iconv_reset(mt, wr_data_tmp);
+            }
+            /* write dummy FS (makes MARC-8 to become ASCII) */
+            wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');
+            marc_iconv_reset(mt, wr_data_tmp);
+            data_length += wrbuf_len(wr_data_tmp);
             break;
         case YAZ_MARC_CONTROLFIELD:
             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
             break;
         case YAZ_MARC_CONTROLFIELD:
             wrbuf_printf(wr_dir, "%.3s", n->u.controlfield.tag);
-            data_length += strlen(n->u.controlfield.data);
-            data_length++;
+
+            wrbuf_rewind(wr_data_tmp);
+            wrbuf_iconv_puts(wr_data_tmp, mt->iconv_cd,
+                             n->u.controlfield.data);
+            marc_iconv_reset(mt, wr_data_tmp);
+            wrbuf_iconv_putchar(wr_data_tmp, mt->iconv_cd, ' ');/* field sep */
+            marc_iconv_reset(mt, wr_data_tmp);
+            data_length += wrbuf_len(wr_data_tmp);
             break;
         case YAZ_MARC_COMMENT:
             break;
             break;
         case YAZ_MARC_COMMENT:
             break;
@@ -674,26 +1196,33 @@ int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
     wrbuf_printf(wr_head, "%05d", base_address);
     /* from "original" leader */
     wrbuf_write(wr_head, leader+17, 7);
     wrbuf_printf(wr_head, "%05d", base_address);
     /* from "original" leader */
     wrbuf_write(wr_head, leader+17, 7);
-    
+
     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
     wrbuf_write(wr, wrbuf_buf(wr_head), 24);
     wrbuf_write(wr, wrbuf_buf(wr_dir), wrbuf_len(wr_dir));
-    wrbuf_free(wr_head, 1);
-    wrbuf_free(wr_dir, 1);
+    wrbuf_destroy(wr_head);
+    wrbuf_destroy(wr_dir);
+    wrbuf_destroy(wr_data_tmp);
 
     for (n = mt->nodes; n; n = n->next)
     {
         struct yaz_marc_subfield *s;
 
     for (n = mt->nodes; n; n = n->next)
     {
         struct yaz_marc_subfield *s;
+
         switch(n->which)
         {
         case YAZ_MARC_DATAFIELD:
         switch(n->which)
         {
         case YAZ_MARC_DATAFIELD:
-            wrbuf_printf(wr, "%.*s", indicator_length,
-                         n->u.datafield.indicator);
+            wrbuf_write(wr, n->u.datafield.indicator, indicator_length);
             for (s = n->u.datafield.subfields; s; s = s->next)
             for (s = n->u.datafield.subfields; s; s = s->next)
-                wrbuf_printf(wr, "%c%s", ISO2709_IDFS, s->code_data);
-            wrbuf_printf(wr, "%c", ISO2709_FS);
+            {
+                wrbuf_putc(wr, ISO2709_IDFS);
+                wrbuf_iconv_puts(wr, mt->iconv_cd, s->code_data);
+                marc_iconv_reset(mt, wr);
+            }
+            wrbuf_putc(wr, ISO2709_FS);
             break;
         case YAZ_MARC_CONTROLFIELD:
             break;
         case YAZ_MARC_CONTROLFIELD:
-            wrbuf_printf(wr, "%s%c", n->u.controlfield.data, ISO2709_FS);
+            wrbuf_iconv_puts(wr, mt->iconv_cd, n->u.controlfield.data);
+            marc_iconv_reset(mt, wr);
+            wrbuf_putc(wr, ISO2709_FS);
             break;
         case YAZ_MARC_COMMENT:
             break;
             break;
         case YAZ_MARC_COMMENT:
             break;
@@ -705,393 +1234,6 @@ int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
     return 0;
 }
 
     return 0;
 }
 
-#if HAVE_XML2
-int yaz_marc_read_xml_subfields(yaz_marc_t mt, const xmlNode *ptr)
-{
-    for (; ptr; ptr = ptr->next)
-    {
-        if (ptr->type == XML_ELEMENT_NODE)
-        {
-            if (!strcmp((const char *) ptr->name, "subfield"))
-            {
-                size_t ctrl_data_len = 0;
-                char *ctrl_data_buf = 0;
-                const xmlNode *p = 0, *ptr_code = 0;
-                struct _xmlAttr *attr;
-                for (attr = ptr->properties; attr; attr = attr->next)
-                    if (!strcmp((const char *)attr->name, "code"))
-                        ptr_code = attr->children;
-                    else
-                    {
-                        yaz_marc_cprintf(
-                            mt, "Bad attribute '%.80s' for 'subfield'",
-                            attr->name);
-                        return -1;
-                    }
-                if (!ptr_code)
-                {
-                    yaz_marc_cprintf(
-                        mt, "Missing attribute 'code' for 'subfield'" );
-                    return -1;
-                }
-                if (ptr_code->type == XML_TEXT_NODE)
-                {
-                    ctrl_data_len = 
-                        strlen((const char *)ptr_code->content);
-                }
-                else
-                {
-                    yaz_marc_cprintf(
-                        mt, "Missing value for 'code' in 'subfield'" );
-                    return -1;
-                }
-                for (p = ptr->children; p ; p = p->next)
-                    if (p->type == XML_TEXT_NODE)
-                        ctrl_data_len += strlen((const char *)p->content);
-                ctrl_data_buf = nmem_malloc(mt->nmem, ctrl_data_len+1);
-                strcpy(ctrl_data_buf, (const char *)ptr_code->content);
-                for (p = ptr->children; p ; p = p->next)
-                    if (p->type == XML_TEXT_NODE)
-                        strcat(ctrl_data_buf, (const char *)p->content);
-                yaz_marc_add_subfield(mt, ctrl_data_buf, ctrl_data_len);
-            }
-            else
-            {
-                yaz_marc_cprintf(
-                    mt, "Expected element 'subfield', got '%.80s'", ptr->name);
-                return -1;
-            }
-        }
-    }
-    return 0;
-}
-
-static int yaz_marc_read_xml_leader(yaz_marc_t mt, const xmlNode **ptr_p)
-{
-    int indicator_length;
-    int identifier_length;
-    int base_address;
-    int length_data_entry;
-    int length_starting;
-    int length_implementation;
-    const char *leader = 0;
-    const xmlNode *ptr = *ptr_p;
-
-    for(; ptr; ptr = ptr->next)
-        if (ptr->type == XML_ELEMENT_NODE)
-        {
-            if (!strcmp((const char *) ptr->name, "leader"))
-            {
-                xmlNode *p = ptr->children;
-                for(; p; p = p->next)
-                    if (p->type == XML_TEXT_NODE)
-                        leader = (const char *) p->content;
-                break;
-            }
-            else
-            {
-                yaz_marc_cprintf(
-                    mt, "Expected element 'leader', got '%.80s'", ptr->name);
-                return -1;
-            }
-        }
-    if (!leader)
-    {
-        yaz_marc_cprintf(mt, "Missing element 'leader'");
-        return -1;
-    }
-    if (strlen(leader) != 24)
-    {
-        yaz_marc_cprintf(mt, "Bad length %d of leader data."
-                         " Must have length of 24 characters", strlen(leader));
-        return -1;
-    }
-    yaz_marc_read_leader(mt, leader,
-                         &indicator_length,
-                         &identifier_length,
-                         &base_address,
-                         &length_data_entry,
-                         &length_starting,
-                         &length_implementation);
-    *ptr_p = ptr;
-    return 0;
-}
-
-static int yaz_marc_read_xml_fields(yaz_marc_t mt, const xmlNode *ptr)
-{
-    for(; ptr; ptr = ptr->next)
-        if (ptr->type == XML_ELEMENT_NODE)
-        {
-            if (!strcmp((const char *) ptr->name, "controlfield"))
-            {
-                const xmlNode *ptr_tag = 0;
-                struct _xmlAttr *attr;
-                for (attr = ptr->properties; attr; attr = attr->next)
-                    if (!strcmp((const char *)attr->name, "tag"))
-                        ptr_tag = attr->children;
-                    else
-                    {
-                        yaz_marc_cprintf(
-                            mt, "Bad attribute '%.80s' for 'controlfield'",
-                            attr->name);
-                        return -1;
-                    }
-                if (!ptr_tag)
-                {
-                    yaz_marc_cprintf(
-                        mt, "Missing attribute 'tag' for 'controlfield'" );
-                    return -1;
-                }
-                yaz_marc_add_controlfield_xml(mt, ptr_tag, ptr->children);
-            }
-            else if (!strcmp((const char *) ptr->name, "datafield"))
-            {
-                char indstr[11]; /* 0(unused), 1,....9, + zero term */
-                const xmlNode *ptr_tag = 0;
-                struct _xmlAttr *attr;
-                int i;
-                for (i = 0; i<11; i++)
-                    indstr[i] = '\0';
-                for (attr = ptr->properties; attr; attr = attr->next)
-                    if (!strcmp((const char *)attr->name, "tag"))
-                        ptr_tag = attr->children;
-                    else if (strlen((const char *)attr->name) == 4 &&
-                             !memcmp(attr->name, "ind", 3))
-                    {
-                        int no = atoi((const char *)attr->name+3);
-                        if (attr->children
-                            && attr->children->type == XML_TEXT_NODE)
-                            indstr[no] = attr->children->content[0];
-                    }
-                    else
-                    {
-                        yaz_marc_cprintf(
-                            mt, "Bad attribute '%.80s' for 'datafield'",
-                            attr->name);
-                        return -1;
-                    }
-                if (!ptr_tag)
-                {
-                    yaz_marc_cprintf(
-                        mt, "Missing attribute 'tag' for 'datafield'" );
-                    return -1;
-                }
-                /* note that indstr[0] is unused so we use indstr[1..] */
-                yaz_marc_add_datafield_xml(mt, ptr_tag,
-                                           indstr+1, strlen(indstr+1));
-                
-                if (yaz_marc_read_xml_subfields(mt, ptr->children))
-                    return -1;
-            }
-            else
-            {
-                yaz_marc_cprintf(mt,
-                                 "Expected element controlfield or datafield,"
-                                 " got %.80s", ptr->name);
-                return -1;
-            }
-        }
-    return 0;
-}
-
-int yaz_marc_read_xml(yaz_marc_t mt, const void *xmlnode)
-{
-    const xmlNode *ptr = xmlnode;
-    for(; ptr; ptr = ptr->next)
-        if (ptr->type == XML_ELEMENT_NODE)
-        {
-            if (!strcmp((const char *) ptr->name, "record"))
-                break;
-            else
-            {
-                yaz_marc_cprintf(
-                    mt, "Unknown element '%.80s' in MARC XML reader",
-                    ptr->name);
-                return -1;
-            }
-        }
-    if (!ptr)
-    {
-        yaz_marc_cprintf(mt, "Missing element 'record' in MARC XML record");
-        return -1;
-    }
-    /* ptr points to record node now */
-    ptr = ptr->children;
-    if (yaz_marc_read_xml_leader(mt, &ptr))
-        return -1;
-    return yaz_marc_read_xml_fields(mt, ptr->next);
-}
-#else
-int yaz_marc_read_xml(yaz_marc_t mt, const void *xmlnode)
-{
-    return -1;
-}
-#endif
-
-int yaz_marc_read_iso2709(yaz_marc_t mt, const char *buf, int bsize)
-{
-    int entry_p;
-    int record_length;
-    int indicator_length;
-    int identifier_length;
-    int end_of_directory;
-    int base_address;
-    int length_data_entry;
-    int length_starting;
-    int length_implementation;
-
-    yaz_marc_reset(mt);
-
-    record_length = atoi_n (buf, 5);
-    if (record_length < 25)
-    {
-        yaz_marc_cprintf(mt, "Record length %d < 24", record_length);
-        return -1;
-    }
-    /* ballout if bsize is known and record_length is less than that */
-    if (bsize != -1 && record_length > bsize)
-    {
-        yaz_marc_cprintf(mt, "Record appears to be larger than buffer %d < %d",
-                         record_length, bsize);
-        return -1;
-    }
-    if (mt->debug)
-        yaz_marc_cprintf(mt, "Record length         %5d", record_length);
-
-    yaz_marc_read_leader(mt, buf,
-                         &indicator_length,
-                         &identifier_length,
-                         &base_address,
-                         &length_data_entry,
-                         &length_starting,
-                         &length_implementation);
-
-    /* First pass. determine length of directory & base of data */
-    for (entry_p = 24; buf[entry_p] != ISO2709_FS; )
-    {
-        /* length of directory entry */
-        int l = 3 + length_data_entry + length_starting;
-        if (entry_p + l >= record_length)
-        {
-            yaz_marc_cprintf(mt, "Directory offset %d: end of record."
-                             " Missing FS char", entry_p);
-            return -1;
-        }
-        if (mt->debug)
-        {
-            yaz_marc_cprintf(mt, "Directory offset %d: Tag %.3s",
-                             entry_p, buf+entry_p);
-        }
-        /* Check for digits in length info */
-        while (--l >= 3)
-            if (!isdigit(*(const unsigned char *) (buf + entry_p+l)))
-                break;
-        if (l >= 3)
-        {
-            /* Not all digits, so stop directory scan */
-            yaz_marc_cprintf(mt, "Directory offset %d: Bad value for data"
-                             " length and/or length starting", entry_p);
-            break;
-        }
-        entry_p += 3 + length_data_entry + length_starting;
-    }
-    end_of_directory = entry_p;
-    if (base_address != entry_p+1)
-    {
-        yaz_marc_cprintf(mt, "Base address not at end of directory,"
-                         " base %d, end %d", base_address, entry_p+1);
-    }
-
-    /* Second pass. parse control - and datafields */
-    for (entry_p = 24; entry_p != end_of_directory; )
-    {
-        int data_length;
-        int data_offset;
-        int end_offset;
-        int i;
-        char tag[4];
-        int identifier_flag = 0;
-        int entry_p0 = entry_p;
-
-        memcpy (tag, buf+entry_p, 3);
-        entry_p += 3;
-        tag[3] = '\0';
-        data_length = atoi_n(buf+entry_p, length_data_entry);
-        entry_p += length_data_entry;
-        data_offset = atoi_n(buf+entry_p, length_starting);
-        entry_p += length_starting;
-        i = data_offset + base_address;
-        end_offset = i+data_length-1;
-
-        if (data_length <= 0 || data_offset < 0)
-            break;
-        
-        if (mt->debug)
-        {
-            yaz_marc_cprintf(mt, "Tag: %s. Directory offset %d: data-length %d,"
-                             " data-offset %d",
-                             tag, entry_p0, data_length, data_offset);
-        }
-        if (end_offset >= record_length)
-        {
-            yaz_marc_cprintf(mt, "Directory offset %d: Data out of bounds %d >= %d",
-                             entry_p0, end_offset, record_length);
-            break;
-        }
-        
-        if (memcmp (tag, "00", 2))
-            identifier_flag = 1;  /* if not 00X assume subfields */
-        else if (indicator_length < 4 && indicator_length > 0)
-        {
-            /* Danmarc 00X have subfields */
-            if (buf[i + indicator_length] == ISO2709_IDFS)
-                identifier_flag = 1;
-            else if (buf[i + indicator_length + 1] == ISO2709_IDFS)
-                identifier_flag = 2;
-        }
-
-        if (identifier_flag)
-        {
-            /* datafield */
-            i += identifier_flag-1;
-            yaz_marc_add_datafield(mt, tag, buf+i, indicator_length);
-            i += indicator_length;
-
-            while (i < end_offset &&
-                    buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
-            {
-                int code_offset = i+1;
-
-                i ++;
-                while (i < end_offset &&
-                        buf[i] != ISO2709_RS && buf[i] != ISO2709_IDFS &&
-                       buf[i] != ISO2709_FS)
-                    i++;
-                yaz_marc_add_subfield(mt, buf+code_offset, i - code_offset);
-            }
-        }
-        else
-        {
-            /* controlfield */
-            int i0 = i;
-            while (i < end_offset && 
-                buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
-                i++;
-            yaz_marc_add_controlfield(mt, tag, buf+i0, i-i0);
-        }
-        if (i < end_offset)
-        {
-            yaz_marc_cprintf(mt, "Separator but not at end of field length=%d",
-                    data_length);
-        }
-        if (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS)
-        {
-            yaz_marc_cprintf(mt, "No separator at end of field length=%d",
-                    data_length);
-        }
-    }
-    return record_length;
-}
 
 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
 {
 
 int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
 {
@@ -1105,14 +1247,14 @@ int yaz_marc_decode_wrbuf(yaz_marc_t mt, const char *buf, int bsize, WRBUF wr)
 }
 
 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
 }
 
 int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
-                         char **result, int *rsize)
+                         const char **result, size_t *rsize)
 {
     int r;
 
     wrbuf_rewind(mt->m_wr);
     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
     if (result)
 {
     int r;
 
     wrbuf_rewind(mt->m_wr);
     r = yaz_marc_decode_wrbuf(mt, buf, bsize, mt->m_wr);
     if (result)
-        *result = wrbuf_buf(mt->m_wr);
+        *result = wrbuf_cstr(mt->m_wr);
     if (rsize)
         *rsize = wrbuf_len(mt->m_wr);
     return r;
     if (rsize)
         *rsize = wrbuf_len(mt->m_wr);
     return r;
@@ -1120,8 +1262,7 @@ int yaz_marc_decode_buf (yaz_marc_t mt, const char *buf, int bsize,
 
 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
 {
 
 void yaz_marc_xml(yaz_marc_t mt, int xmlmode)
 {
-    if (mt)
-        mt->xml = xmlmode;
+    mt->output_format = xmlmode;
 }
 
 void yaz_marc_debug(yaz_marc_t mt, int level)
 }
 
 void yaz_marc_debug(yaz_marc_t mt, int level)
@@ -1135,6 +1276,11 @@ void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
     mt->iconv_cd = cd;
 }
 
     mt->iconv_cd = cd;
 }
 
+yaz_iconv_t yaz_marc_get_iconv(yaz_marc_t mt)
+{
+    return mt->iconv_cd;
+}
+
 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
 {
     struct yaz_marc_node *n;
 void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
 {
     struct yaz_marc_node *n;
@@ -1148,56 +1294,88 @@ void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
         }
 }
 
         }
 }
 
-/* deprecated */
-int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
+int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
 {
 {
-    yaz_marc_t mt = yaz_marc_create();
-    int r;
-
-    mt->debug = debug;
-    mt->xml = xml;
-    r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
-    yaz_marc_destroy(mt);
-    return r;
+    xfree(mt->leader_spec);
+    mt->leader_spec = 0;
+    if (leader_spec)
+    {
+        char dummy_leader[24];
+        if (marc_exec_leader(leader_spec, dummy_leader, 24))
+            return -1;
+        mt->leader_spec = xstrdup(leader_spec);
+    }
+    return 0;
 }
 
 }
 
-/* deprecated */
-int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
+static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
 {
 {
-    return yaz_marc_decode(buf, wr, debug, bsize, 0);
-}
+    const char *cp = leader_spec;
+    while (cp)
+    {
+        char val[21];
+        int pos;
+        int no_read = 0, no = 0;
 
 
-/* deprecated */
-int marc_display_exl (const char *buf, FILE *outf, int debug, int bsize)
-{
-    yaz_marc_t mt = yaz_marc_create();
-    int r;
+        no = sscanf(cp, "%d=%20[^,]%n", &pos, val, &no_read);
+        if (no < 2 || no_read < 3)
+            return -1;
+        if (pos < 0 || (size_t) pos >= size)
+            return -1;
 
 
-    mt->debug = debug;
-    r = yaz_marc_decode_wrbuf (mt, buf, bsize, mt->m_wr);
-    if (!outf)
-        outf = stdout;
-    if (r > 0)
-        fwrite (wrbuf_buf(mt->m_wr), 1, wrbuf_len(mt->m_wr), outf);
-    yaz_marc_destroy(mt);
-    return r;
+        if (*val == '\'')
+        {
+            const char *vp = strchr(val+1, '\'');
+            size_t len;
+
+            if (!vp)
+                return -1;
+            len = vp-val-1;
+            if (len + pos > size)
+                return -1;
+            memcpy(leader + pos, val+1, len);
+        }
+        else if (*val >= '0' && *val <= '9')
+        {
+            int ch = atoi(val);
+            leader[pos] = ch;
+        }
+        else
+            return -1;
+        cp += no_read;
+        if (*cp != ',')
+            break;
+
+        cp++;
+    }
+    return 0;
 }
 
 }
 
-/* deprecated */
-int marc_display_ex (const char *buf, FILE *outf, int debug)
+int yaz_marc_decode_formatstr(const char *arg)
 {
 {
-    return marc_display_exl (buf, outf, debug, -1);
+    int mode = -1;
+    if (!strcmp(arg, "marc"))
+        mode = YAZ_MARC_ISO2709;
+    if (!strcmp(arg, "marcxml"))
+        mode = YAZ_MARC_MARCXML;
+    if (!strcmp(arg, "turbomarc"))
+        mode = YAZ_MARC_TURBOMARC;
+    if (!strcmp(arg, "marcxchange"))
+        mode = YAZ_MARC_XCHANGE;
+    if (!strcmp(arg, "line"))
+        mode = YAZ_MARC_LINE;
+    return mode;
 }
 
 }
 
-/* deprecated */
-int marc_display (const char *buf, FILE *outf)
+void yaz_marc_write_using_libxml2(yaz_marc_t mt, int enable)
 {
 {
-    return marc_display_ex (buf, outf, 0);
+    mt->write_using_libxml2 = enable;
 }
 
 /*
  * Local variables:
  * c-basic-offset: 4
 }
 
 /*
  * Local variables:
  * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
  * indent-tabs-mode: nil
  * End:
  * vim: shiftwidth=4 tabstop=8 expandtab
  * indent-tabs-mode: nil
  * End:
  * vim: shiftwidth=4 tabstop=8 expandtab