Fixed bug #2352: yaz-marcdump crashes for certain record.
[yaz-moved-to-github.git] / src / marcdisp.c
index ea4b2bc..a5084ce 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * Copyright (C) 1995-2006, Index Data ApS
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2008 Index Data
  * See the file LICENSE for details.
- *
- * $Id: marcdisp.c,v 1.26 2006-04-19 10:05:03 adam Exp $
  */
 
 /**
 
 #include <stdarg.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <ctype.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
 
-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
 { 
@@ -84,9 +89,12 @@ struct yaz_marc_t_ {
     NMEM nmem;
     int xml;
     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];
+    char *leader_spec;
     struct yaz_marc_node *nodes;
     struct yaz_marc_node **nodes_pp;
     struct yaz_marc_subfield **subfield_pp;
@@ -97,8 +105,11 @@ yaz_marc_t yaz_marc_create(void)
     yaz_marc_t mt = (yaz_marc_t) xmalloc(sizeof(*mt));
     mt->xml = YAZ_MARC_LINE;
     mt->debug = 0;
+    mt->write_using_libxml2 = 0;
+    mt->enable_collection = no_collection;
     mt->m_wr = wrbuf_alloc();
     mt->iconv_cd = 0;
+    mt->leader_spec = 0;
     strcpy(mt->subfield_str, " $");
     strcpy(mt->endline_str, "\n");
 
@@ -112,19 +123,47 @@ void yaz_marc_destroy(yaz_marc_t mt)
     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);
+
+
+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;
 }
 
+#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);
+    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);
+}
+#endif
+
+
 void yaz_marc_add_comment(yaz_marc_t mt, char *comment)
 {
     struct yaz_marc_node *n = yaz_marc_add_node(mt);
@@ -132,51 +171,28 @@ void yaz_marc_add_comment(yaz_marc_t mt, char *comment)
     n->u.comment = nmem_strdup(mt->nmem, comment);
 }
 
-#if HAVE_XML2
-static char *yaz_marc_get_xml_text(const xmlNode *ptr_cdata, NMEM nmem)
-{
-    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;
-}
-#endif
-
 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);
 }
 
+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);
+    marc_exec_leader(mt->leader_spec, n->u.leader, leader_len);
 }
 
 void yaz_marc_add_controlfield(yaz_marc_t mt, const char *tag,
@@ -200,17 +216,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)
 {
@@ -225,13 +230,13 @@ void yaz_marc_add_datafield(yaz_marc_t mt, const char *tag,
     mt->subfield_pp = &n->u.datafield.subfields;
 }
 
-#if HAVE_XML2
+#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;
-    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;
@@ -259,7 +264,8 @@ void yaz_marc_add_subfield(yaz_marc_t mt,
 
     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 */
@@ -268,31 +274,23 @@ void yaz_marc_add_subfield(yaz_marc_t mt,
     }
 }
 
-static int atoi_n_check(const char *buf, int size, int *val)
+int atoi_n_check(const char *buf, int size, int *val)
 {
-    if (!isdigit(*(const unsigned char *) buf))
-        return 0;
+    int i;
+    for (i = 0; i < size; i++)
+        if (!isdigit(i[(const unsigned char *) buf]))
+            return 0;
     *val = atoi_n(buf, size);
     return 1;
 }
 
-/** \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];
 
@@ -370,16 +368,6 @@ void yaz_marc_endline_str(yaz_marc_t mt, const char *s)
     mt->endline_str[sizeof(mt->endline_str)-1] = '\0';
 }
 
-static void marc_cdata (yaz_marc_t mt, const char *buf, size_t len, WRBUF wr)
-{
-    if (mt->xml == YAZ_MARC_ISO2709)
-        wrbuf_iconv_write(wr, mt->iconv_cd, buf, len);
-    else if (mt->xml == YAZ_MARC_LINE)
-        wrbuf_iconv_write(wr, mt->iconv_cd, buf, len);
-    else
-        wrbuf_iconv_write_cdata(wr, mt->iconv_cd, buf, len);
-}
-
 /* try to guess how many bytes the identifier really is! */
 static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
 {
@@ -404,7 +392,7 @@ static size_t cdata_one_character(yaz_marc_t mt, const char *buf)
     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;
@@ -412,6 +400,41 @@ static void yaz_marc_reset(yaz_marc_t mt)
     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;
+}
+
+
 int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
 {
     struct yaz_marc_node *n;
@@ -444,37 +467,64 @@ int yaz_marc_write_line(yaz_marc_t mt, WRBUF wr)
                    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
+                    (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, 
                                   using_code_len);
-                wrbuf_printf(wr, " ");
+                wrbuf_iconv_puts(wr, mt->iconv_cd, " ");
                 wrbuf_iconv_puts(wr, mt->iconv_cd, 
                                  s->code_data + using_code_len);
+                marc_iconv_reset(mt, wr);
             }
             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);
+            marc_iconv_reset(mt, wr);
             wrbuf_puts (wr, mt->endline_str);
             break;
         case YAZ_MARC_COMMENT:
             wrbuf_puts(wr, "(");
             wrbuf_iconv_write(wr, mt->iconv_cd, 
                               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");
     return 0;
 }
 
+int yaz_marc_write_trailer(yaz_marc_t mt, WRBUF wr)
+{
+    if (mt->enable_collection == collection_second)
+    {
+        switch(mt->xml)
+        {
+        case YAZ_MARC_MARCXML:
+            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)
 {
     switch(mt->xml)
@@ -484,15 +534,26 @@ int yaz_marc_write_mode(yaz_marc_t mt, WRBUF wr)
     case YAZ_MARC_MARCXML:
         return yaz_marc_write_marcxml(mt, wr);
     case YAZ_MARC_XCHANGE:
-        return yaz_marc_write_marcxchange(mt, wr);
+        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;
 }
 
-static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
-                                     const char *ns)
+/** \brief common MARC XML/Xchange 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")
+*/
+static int yaz_marc_write_marcxml_ns1(yaz_marc_t mt, WRBUF wr,
+                                      const char *ns, 
+                                      const char *format,
+                                      const char *type)
 {
     struct yaz_marc_node *n;
     int identifier_length;
@@ -509,11 +570,27 @@ static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
         return -1;
     if (!atoi_n_check(leader+11, 1, &identifier_length))
         return -1;
-
-    wrbuf_printf(wr, "<record xmlns=\"%s\">\n", 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, "<record");
+    }
+    else
+    {
+        wrbuf_printf(wr, "<record xmlns=\"%s\"", ns);
+    }
+    if (format)
+        wrbuf_printf(wr, " format=\"%.80s\"", format);
+    if (type)
+        wrbuf_printf(wr, " type=\"%.80s\"", type);
+    wrbuf_printf(wr, ">\n");
     for (n = mt->nodes; n; n = n->next)
     {
         struct yaz_marc_subfield *s;
+
         switch(n->which)
         {
         case YAZ_MARC_DATAFIELD:
@@ -529,7 +606,7 @@ static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
                     wrbuf_printf(wr, " ind%d=\"", i+1);
                     wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
                                           n->u.datafield.indicator+i, 1);
-                    wrbuf_printf(wr, "\"");
+                    wrbuf_iconv_puts(wr, mt->iconv_cd, "\"");
                 }
             }
             wrbuf_printf(wr, ">\n");
@@ -539,18 +616,20 @@ static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
                    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
+                    (identifier_length > 2) ? identifier_length - 1
                     :
                     cdata_one_character(mt, s->code_data);
                 
-                wrbuf_puts(wr, "    <subfield code=\"");
+                wrbuf_iconv_puts(wr, mt->iconv_cd, "    <subfield code=\"");
                 wrbuf_iconv_write_cdata(wr, mt->iconv_cd,
                                         s->code_data, using_code_len);
-                wrbuf_puts(wr, "\">");
+                wrbuf_iconv_puts(wr, mt->iconv_cd, "\">");
                 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_iconv_puts(wr, mt->iconv_cd, "</subfield>");
+                wrbuf_puts(wr, "\n");
             }
             wrbuf_printf(wr, "  </datafield>\n");
             break;
@@ -558,12 +637,19 @@ static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
             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_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_iconv_puts(wr, mt->iconv_cd, "</controlfield>");
+            wrbuf_puts(wr, "\n");
             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:
             wrbuf_printf(wr, "  <leader>");
@@ -577,17 +663,174 @@ static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
     return 0;
 }
 
+static int yaz_marc_write_marcxml_ns(yaz_marc_t mt, WRBUF wr,
+                                     const char *ns, 
+                                     const char *format,
+                                     const char *type)
+{
+    if (mt->write_using_libxml2)
+    {
+#if YAZ_HAVE_XML2
+        int ret;
+        xmlNode *root_ptr;
+
+        ret = yaz_marc_write_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_ns1(mt, wr, ns, format, type);
+}
+
 int yaz_marc_write_marcxml(yaz_marc_t mt, WRBUF wr)
 {
-    return yaz_marc_write_marcxml_ns(mt, wr, "http://www.loc.gov/MARC21/slim");
+    /* 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);
 }
 
-int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr)
+int yaz_marc_write_marcxchange(yaz_marc_t mt, WRBUF wr,
+                               const char *format,
+                               const char *type)
 {
     return yaz_marc_write_marcxml_ns(mt, wr,
-                                     "http://www.bs.dk/standards/MarcXchange");
+                                     "http://www.bs.dk/standards/MarcXchange",
+                                     0, 0);
 }
 
+
+#if YAZ_HAVE_XML2
+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;
+                /* 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_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;
@@ -598,7 +841,7 @@ int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
     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;
     
     for (n = mt->nodes; n; n = n->next)
@@ -618,24 +861,41 @@ int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
     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;
+
         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)
-                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);
-            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;
@@ -668,23 +928,31 @@ int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
     
     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;
+
         switch(n->which)
         {
         case YAZ_MARC_DATAFIELD:
             wrbuf_printf(wr, "%.*s", indicator_length,
                          n->u.datafield.indicator);
             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:
-            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;
@@ -696,393 +964,6 @@ int yaz_marc_write_iso2709(yaz_marc_t mt, WRBUF wr)
     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)
 {
@@ -1096,14 +977,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,
-                         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)
-        *result = wrbuf_buf(mt->m_wr);
+        *result = wrbuf_cstr(mt->m_wr);
     if (rsize)
         *rsize = wrbuf_len(mt->m_wr);
     return r;
@@ -1126,51 +1007,98 @@ void yaz_marc_iconv(yaz_marc_t mt, yaz_iconv_t cd)
     mt->iconv_cd = cd;
 }
 
-/* deprecated */
-int yaz_marc_decode(const char *buf, WRBUF wr, int debug, int bsize, int xml)
+yaz_iconv_t yaz_marc_get_iconv(yaz_marc_t mt)
 {
-    yaz_marc_t mt = yaz_marc_create();
-    int r;
+    return mt->iconv_cd;
+}
 
-    mt->debug = debug;
-    mt->xml = xml;
-    r = yaz_marc_decode_wrbuf(mt, buf, bsize, wr);
-    yaz_marc_destroy(mt);
-    return r;
+void yaz_marc_modify_leader(yaz_marc_t mt, size_t off, const char *str)
+{
+    struct yaz_marc_node *n;
+    char *leader = 0;
+    for (n = mt->nodes; n; n = n->next)
+        if (n->which == YAZ_MARC_LEADER)
+        {
+            leader = n->u.leader;
+            memcpy(leader+off, str, strlen(str));
+            break;
+        }
 }
 
-/* deprecated */
-int marc_display_wrbuf (const char *buf, WRBUF wr, int debug, int bsize)
+int yaz_marc_leader_spec(yaz_marc_t mt, const char *leader_spec)
 {
-    return yaz_marc_decode(buf, wr, debug, bsize, 0);
+    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_exl (const char *buf, FILE *outf, int debug, int bsize)
+static int marc_exec_leader(const char *leader_spec, char *leader, size_t size)
 {
-    yaz_marc_t mt = yaz_marc_create();
-    int r;
+    const char *cp = leader_spec;
+    while (cp)
+    {
+        char val[21];
+        int pos;
+        int no_read = 0, no = 0;
 
-    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;
+        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;
+
+        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, "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;
 }
 
 /*