Call exsltRegisterAll in yaz_init_globals
[yaz-moved-to-github.git] / src / record_conv.c
index 27381fa..ca1e41e 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * Copyright (C) 2005-2007, Index Data ApS
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) Index Data
  * See the file LICENSE for details.
- *
- * $Id: record_conv.c,v 1.15 2007-03-19 14:40:07 adam Exp $
  */
 /**
  * \file record_conv.c
@@ -21,6 +19,7 @@
 #include <yaz/xmalloc.h>
 #include <yaz/nmem.h>
 #include <yaz/tpath.h>
+#include <yaz/z-opac.h>
 
 #if YAZ_HAVE_XML2
 #include <libxml/parser.h>
@@ -52,29 +51,19 @@ struct yaz_record_conv_struct {
     char *path;
 };
 
-/** \brief tranformation types (rule types) */
-enum YAZ_RECORD_CONV_RULE 
-{
-    YAZ_RECORD_CONV_RULE_XSLT,
-    YAZ_RECORD_CONV_RULE_MARC
+struct marc_info {
+    NMEM nmem;
+    const char *input_charset;
+    const char *output_charset;
+    int input_format_mode;
+    int output_format_mode;
+    const char *leader_spec;
 };
 
-
 /** \brief tranformation info (rule info) */
 struct yaz_record_conv_rule {
-    enum YAZ_RECORD_CONV_RULE which;
-    union {
-#if YAZ_HAVE_XSLT
-        struct {
-            xsltStylesheetPtr xsp;
-        } xslt;
-#endif
-        struct {
-            yaz_iconv_t iconv_t;
-            int input_format;
-            int output_format;
-        } marc;
-    } u;
+    struct yaz_record_conv_type *type;
+    void *info;
     struct yaz_record_conv_rule *next;
 };
 
@@ -85,17 +74,7 @@ static void yaz_record_conv_reset(yaz_record_conv_t p)
     struct yaz_record_conv_rule *r;
     for (r = p->rules; r; r = r->next)
     {
-        if (r->which == YAZ_RECORD_CONV_RULE_MARC)
-        {
-            if (r->u.marc.iconv_t)
-                yaz_iconv_close(r->u.marc.iconv_t);
-        }
-#if YAZ_HAVE_XSLT
-        else if (r->which == YAZ_RECORD_CONV_RULE_XSLT)
-        {
-            xsltFreeStylesheet(r->u.xslt.xsp);
-        }
-#endif
+        r->type->destroy(r->info);
     }
     wrbuf_rewind(p->wr_error);
     nmem_reset(p->nmem);
@@ -105,21 +84,6 @@ static void yaz_record_conv_reset(yaz_record_conv_t p)
     p->rules_p = &p->rules;
 }
 
-yaz_record_conv_t yaz_record_conv_create()
-{
-    yaz_record_conv_t p = xmalloc(sizeof(*p));
-    p->nmem = nmem_create();
-    p->wr_error = wrbuf_alloc();
-    p->rules = 0;
-    p->path = 0;
-
-#if YAZ_HAVE_EXSLT
-    exsltRegisterAll(); 
-#endif
-    yaz_record_conv_reset(p);
-    return p;
-}
-
 void yaz_record_conv_destroy(yaz_record_conv_t p)
 {
     if (p)
@@ -127,29 +91,31 @@ void yaz_record_conv_destroy(yaz_record_conv_t p)
         yaz_record_conv_reset(p);
         nmem_destroy(p->nmem);
         wrbuf_destroy(p->wr_error);
+
         xfree(p->path);
         xfree(p);
     }
 }
 
-/** \brief adds a rule */
-static struct yaz_record_conv_rule *add_rule(yaz_record_conv_t p,
-                                             enum YAZ_RECORD_CONV_RULE type)
-{
-    struct yaz_record_conv_rule *r = nmem_malloc(p->nmem, sizeof(*r));
-    r->which = type;
-    r->next = 0;
-    *p->rules_p = r;
-    p->rules_p = &r->next;
-    return r;
-}
+#if YAZ_HAVE_XSLT
+struct xslt_info {
+    NMEM nmem;
+    xmlDocPtr xsp_doc;
+    const char **xsl_parms;
+};
 
-/** \brief parse 'xslt' conversion node */
-static int conv_xslt(yaz_record_conv_t p, const xmlNode *ptr)
+static void *construct_xslt(const xmlNode *ptr,
+                            const char *path, WRBUF wr_error)
 {
-#if YAZ_HAVE_XSLT
     struct _xmlAttr *attr;
     const char *stylesheet = 0;
+    struct xslt_info *info = 0;
+    NMEM nmem = 0;
+    int max_parms = 10;
+    int no_parms = 0;
+
+    if (strcmp((const char *) ptr->name, "xslt"))
+        return 0;
 
     for (attr = ptr->properties; attr; attr = attr->next)
     {
@@ -158,330 +124,564 @@ static int conv_xslt(yaz_record_conv_t p, const xmlNode *ptr)
             stylesheet = (const char *) attr->children->content;
         else
         {
-            wrbuf_printf(p->wr_error, "Bad attribute '%s'"
+            wrbuf_printf(wr_error, "Bad attribute '%s'"
                          "Expected stylesheet.", attr->name);
-            return -1;
+            return 0;
+        }
+    }
+    nmem = nmem_create();
+    info = nmem_malloc(nmem, sizeof(*info));
+    info->nmem = nmem;
+    info->xsl_parms = nmem_malloc(
+        nmem, (2 * max_parms + 1) * sizeof(*info->xsl_parms));
+
+    for (ptr = ptr->children; ptr; ptr = ptr->next)
+    {
+        const char *name = 0;
+        const char *value = 0;
+        char *qvalue = 0;
+        if (ptr->type != XML_ELEMENT_NODE)
+            continue;
+        if (strcmp((const char *) ptr->name, "param"))
+        {
+            wrbuf_printf(wr_error, "Bad element '%s'"
+                         "Expected param.", ptr->name);
+            nmem_destroy(nmem);
+            return 0;
         }
+        for (attr = ptr->properties; attr; attr = attr->next)
+        {
+            if (!xmlStrcmp(attr->name, BAD_CAST "name") &&
+                attr->children && attr->children->type == XML_TEXT_NODE)
+                name = (const char *) attr->children->content;
+            else if (!xmlStrcmp(attr->name, BAD_CAST "value") &&
+                attr->children && attr->children->type == XML_TEXT_NODE)
+                value = (const char *) attr->children->content;
+            else
+            {
+                wrbuf_printf(wr_error, "Bad attribute '%s'"
+                             "Expected name or value.", attr->name);
+                nmem_destroy(nmem);
+                return 0;
+            }
+        }
+        if (!name || !value)
+        {
+            wrbuf_printf(wr_error, "Missing attributes name or value");
+            nmem_destroy(nmem);
+            return 0;
+        }
+        if (no_parms >= max_parms)
+        {
+            wrbuf_printf(wr_error, "Too many parameters given");
+            nmem_destroy(nmem);
+            return 0;
+        }
+
+        qvalue = nmem_malloc(nmem, strlen(value) + 3);
+        strcpy(qvalue, "\'");
+        strcat(qvalue, value);
+        strcat(qvalue, "\'");
+
+        info->xsl_parms[2 * no_parms] = nmem_strdup(nmem, name);
+        info->xsl_parms[2 * no_parms + 1] = qvalue;
+        no_parms++;
     }
+
+    info->xsl_parms[2 * no_parms] = '\0';
+
     if (!stylesheet)
     {
-        wrbuf_printf(p->wr_error, "Element <xslt>: "
+        wrbuf_printf(wr_error, "Element <xslt>: "
                      "attribute 'stylesheet' expected");
-        return -1;
+        nmem_destroy(nmem);
     }
     else
     {
         char fullpath[1024];
         xsltStylesheetPtr xsp;
-        if (!yaz_filepath_resolve(stylesheet, p->path, 0, fullpath))
+        if (!yaz_filepath_resolve(stylesheet, path, 0, fullpath))
         {
-            wrbuf_printf(p->wr_error, "Element <xslt stylesheet=\"%s\"/>:"
-                         " could not locate stylesheet '%s' with path '%s'",
-                         stylesheet, fullpath, p->path);
-            return -1;
+            wrbuf_printf(wr_error, "Element <xslt stylesheet=\"%s\"/>:"
+                         " could not locate stylesheet '%s'",
+                         stylesheet, stylesheet);
+            if (path)
+                wrbuf_printf(wr_error, " with path '%s'", path);
+
+            nmem_destroy(nmem);
+            return 0;
+        }
+        info->xsp_doc = xmlParseFile(fullpath);
+        if (!info->xsp_doc)
+        {
+            wrbuf_printf(wr_error, "Element: <xslt stylesheet=\"%s\"/>:"
+                         " xml parse failed: %s", stylesheet, fullpath);
+            if (path)
+                wrbuf_printf(wr_error, " with path '%s'", path);
+            nmem_destroy(nmem);
+            return 0;
         }
-        xsp = xsltParseStylesheetFile((xmlChar*) fullpath);
+        /* need to copy this before passing it to the processor. It will
+           be encapsulated in the xsp and destroyed by xsltFreeStylesheet */
+        xsp = xsltParseStylesheetDoc(xmlCopyDoc(info->xsp_doc, 1));
         if (!xsp)
         {
-            wrbuf_printf(p->wr_error, "Element <xslt stylesheet=\"%s\"/>:"
-                         " parsing stylesheet '%s' with path '%s' failed,"
+            wrbuf_printf(wr_error, "Element: <xslt stylesheet=\"%s\"/>:"
+                         " xslt parse failed: %s", stylesheet, fullpath);
+            if (path)
+                wrbuf_printf(wr_error, " with path '%s'", path);
+            wrbuf_printf(wr_error, " ("
 #if YAZ_HAVE_EXSLT
-                         " EXSLT enabled",
+
+                         "EXSLT enabled"
 #else
-                         " EXSLT not supported",
+                         "EXSLT not supported"
 #endif
-                         stylesheet, fullpath, p->path);
-            return -1;
+                         ")");
+            xmlFreeDoc(info->xsp_doc);
+            nmem_destroy(info->nmem);
         }
         else
         {
-            struct yaz_record_conv_rule *r = 
-                add_rule(p, YAZ_RECORD_CONV_RULE_XSLT);
-            r->u.xslt.xsp = xsp;
+            xsltFreeStylesheet(xsp);
+            return info;
         }
     }
     return 0;
+}
+
+static int convert_xslt(void *vinfo, WRBUF record, WRBUF wr_error)
+{
+    int ret = 0;
+    struct xslt_info *info = vinfo;
+
+    xmlDocPtr doc = xmlParseMemory(wrbuf_buf(record),
+                                   wrbuf_len(record));
+    if (!doc)
+    {
+        wrbuf_printf(wr_error, "xmlParseMemory failed");
+        ret = -1;
+    }
+    else
+    {
+        xmlDocPtr xsp_doc = xmlCopyDoc(info->xsp_doc, 1);
+        xsltStylesheetPtr xsp = xsltParseStylesheetDoc(xsp_doc);
+        xmlDocPtr res = xsltApplyStylesheet(xsp, doc, info->xsl_parms);
+        if (res)
+        {
+            xmlChar *out_buf = 0;
+            int out_len;
+
+#if HAVE_XSLTSAVERESULTTOSTRING
+            xsltSaveResultToString(&out_buf, &out_len, res, xsp);
 #else
-    wrbuf_printf(p->wr_error, "xslt unsupported."
-                 " YAZ compiled without XSLT support");
-    return -1;
+            xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
 #endif
+            if (!out_buf)
+            {
+                wrbuf_printf(wr_error,
+                             "xsltSaveResultToString failed");
+                ret = -1;
+            }
+            else
+            {
+                wrbuf_rewind(record);
+                wrbuf_write(record, (const char *) out_buf, out_len);
+
+                xmlFree(out_buf);
+            }
+            xmlFreeDoc(res);
+        }
+        else
+        {
+            wrbuf_printf(wr_error, "xsltApplyStylesheet failed");
+            ret = -1;
+        }
+        xmlFreeDoc(doc);
+        xsltFreeStylesheet(xsp); /* frees xsp_doc too */
+    }
+    return ret;
 }
 
-/** \brief parse 'marc' conversion node */
-static int conv_marc(yaz_record_conv_t p, const xmlNode *ptr)
+static void destroy_xslt(void *vinfo)
+{
+    struct xslt_info *info = vinfo;
+
+    if (info)
+    {
+        xmlFreeDoc(info->xsp_doc);
+        nmem_destroy(info->nmem);
+    }
+}
+
+/* YAZ_HAVE_XSLT */
+#endif
+
+
+static void *construct_marc(const xmlNode *ptr,
+                            const char *path, WRBUF wr_error)
 {
+    NMEM nmem = nmem_create();
+    struct marc_info *info = nmem_malloc(nmem, sizeof(*info));
     struct _xmlAttr *attr;
-    const char *input_charset = 0;
-    const char *output_charset = 0;
     const char *input_format = 0;
     const char *output_format = 0;
-    int input_format_mode = 0;
-    int output_format_mode = 0;
-    struct yaz_record_conv_rule *r;
-    yaz_iconv_t cd = 0;
+
+    if (strcmp((const char *) ptr->name, "marc"))
+    {
+        nmem_destroy(nmem);
+        return 0;
+    }
+
+    info->nmem = nmem;
+    info->input_charset = 0;
+    info->output_charset = 0;
+    info->input_format_mode = 0;
+    info->output_format_mode = 0;
+    info->leader_spec = 0;
 
     for (attr = ptr->properties; attr; attr = attr->next)
     {
         if (!xmlStrcmp(attr->name, BAD_CAST "inputcharset") &&
             attr->children && attr->children->type == XML_TEXT_NODE)
-            input_charset = (const char *) attr->children->content;
+            info->input_charset = (const char *) attr->children->content;
         else if (!xmlStrcmp(attr->name, BAD_CAST "outputcharset") &&
             attr->children && attr->children->type == XML_TEXT_NODE)
-            output_charset = (const char *) attr->children->content;
+            info->output_charset = (const char *) attr->children->content;
         else if (!xmlStrcmp(attr->name, BAD_CAST "inputformat") &&
             attr->children && attr->children->type == XML_TEXT_NODE)
             input_format = (const char *) attr->children->content;
         else if (!xmlStrcmp(attr->name, BAD_CAST "outputformat") &&
             attr->children && attr->children->type == XML_TEXT_NODE)
             output_format = (const char *) attr->children->content;
+        else if (!xmlStrcmp(attr->name, BAD_CAST "leaderspec") &&
+                 attr->children && attr->children->type == XML_TEXT_NODE)
+            info->leader_spec =
+                nmem_strdup(info->nmem,(const char *) attr->children->content);
         else
         {
-            wrbuf_printf(p->wr_error, "Element <marc>: expected attributes"
+            wrbuf_printf(wr_error, "Element <marc>: expected attributes"
                          "'inputformat', 'inputcharset', 'outputformat' or"
-                         " 'outputcharset', got attribute '%s'", 
+                         " 'outputcharset', got attribute '%s'",
                          attr->name);
-            return -1;
+            nmem_destroy(info->nmem);
+            return 0;
         }
     }
     if (!input_format)
     {
-        wrbuf_printf(p->wr_error, "Element <marc>: "
+        wrbuf_printf(wr_error, "Element <marc>: "
                      "attribute 'inputformat' required");
-        return -1;
+        nmem_destroy(info->nmem);
+        return 0;
     }
     else if (!strcmp(input_format, "marc"))
     {
-        input_format_mode = YAZ_MARC_ISO2709;
+        info->input_format_mode = YAZ_MARC_ISO2709;
     }
     else if (!strcmp(input_format, "xml"))
     {
-        input_format_mode = YAZ_MARC_MARCXML;
+        info->input_format_mode = YAZ_MARC_MARCXML;
         /** Libxml2 generates UTF-8 encoding by default .
-            So we convert from UTF-8 to outputcharset (if defined) 
+            So we convert from UTF-8 to outputcharset (if defined)
         */
-        if (!input_charset && output_charset)
-            input_charset = "utf-8";
+        if (!info->input_charset && info->output_charset)
+            info->input_charset = "utf-8";
+    }
+    else if (!strcmp(input_format, "json"))
+    {
+        info->input_format_mode = YAZ_MARC_JSON;
     }
     else
     {
-        wrbuf_printf(p->wr_error, "Element <marc inputformat='%s'>: "
+        wrbuf_printf(wr_error, "Element <marc inputformat='%s'>: "
                      " Unsupported input format"
-                     " defined by attribute value", 
+                     " defined by attribute value",
                      input_format);
-        return -1;
+        nmem_destroy(info->nmem);
+        return 0;
     }
-    
+
     if (!output_format)
     {
-        wrbuf_printf(p->wr_error, 
+        wrbuf_printf(wr_error,
                      "Element <marc>: attribute 'outputformat' required");
-        return -1;
+        nmem_destroy(info->nmem);
+        return 0;
     }
     else if (!strcmp(output_format, "line"))
     {
-        output_format_mode = YAZ_MARC_LINE;
+        info->output_format_mode = YAZ_MARC_LINE;
     }
     else if (!strcmp(output_format, "marcxml"))
     {
-        output_format_mode = YAZ_MARC_MARCXML;
-        if (input_charset && !output_charset)
-            output_charset = "utf-8";
+        info->output_format_mode = YAZ_MARC_MARCXML;
+        if (info->input_charset && !info->output_charset)
+            info->output_charset = "utf-8";
+    }
+    else if (!strcmp(output_format, "turbomarc"))
+    {
+        info->output_format_mode = YAZ_MARC_TURBOMARC;
+        if (info->input_charset && !info->output_charset)
+            info->output_charset = "utf-8";
     }
     else if (!strcmp(output_format, "marc"))
     {
-        output_format_mode = YAZ_MARC_ISO2709;
+        info->output_format_mode = YAZ_MARC_ISO2709;
     }
     else if (!strcmp(output_format, "marcxchange"))
     {
-        output_format_mode = YAZ_MARC_XCHANGE;
-        if (input_charset && !output_charset)
-            output_charset = "utf-8";
+        info->output_format_mode = YAZ_MARC_XCHANGE;
+        if (info->input_charset && !info->output_charset)
+            info->output_charset = "utf-8";
+    }
+    else if (!strcmp(output_format, "json"))
+    {
+        info->output_format_mode = YAZ_MARC_JSON;
+        if (info->input_charset && !info->output_charset)
+            info->output_charset = "utf-8";
     }
     else
     {
-        wrbuf_printf(p->wr_error, "Element <marc outputformat='%s'>: "
+        wrbuf_printf(wr_error, "Element <marc outputformat='%s'>: "
                      " Unsupported output format"
-                     " defined by attribute value", 
+                     " defined by attribute value",
                      output_format);
-        return -1;
+        nmem_destroy(info->nmem);
+        return 0;
     }
-    if (input_charset && output_charset)
+    if (info->input_charset && info->output_charset)
     {
-        cd = yaz_iconv_open(output_charset, input_charset);
+        yaz_iconv_t cd = yaz_iconv_open(info->output_charset,
+                                        info->input_charset);
         if (!cd)
         {
-            wrbuf_printf(p->wr_error, 
+            wrbuf_printf(wr_error,
                          "Element <marc inputcharset='%s' outputcharset='%s'>:"
                          " Unsupported character set mapping"
                          " defined by attribute values",
-                         input_charset, output_charset);
-            return -1;
+                         info->input_charset, info->output_charset);
+            nmem_destroy(info->nmem);
+            return 0;
         }
+        yaz_iconv_close(cd);
     }
-    else if (input_charset)
+    else if (!info->output_charset)
     {
-        wrbuf_printf(p->wr_error, "Element <marc>: "
+        wrbuf_printf(wr_error, "Element <marc>: "
                      "attribute 'outputcharset' missing");
-        return -1;
+        nmem_destroy(info->nmem);
+        return 0;
     }
-    else if (output_charset)
+    else if (!info->input_charset)
     {
-        wrbuf_printf(p->wr_error, "Element <marc>: "
+        wrbuf_printf(wr_error, "Element <marc>: "
                      "attribute 'inputcharset' missing");
-        return -1;
+        nmem_destroy(info->nmem);
+        return 0;
     }
-    r = add_rule(p, YAZ_RECORD_CONV_RULE_MARC);
-    r->u.marc.iconv_t = cd;
+    info->input_charset = nmem_strdup(info->nmem, info->input_charset);
+    info->output_charset = nmem_strdup(info->nmem, info->output_charset);
+    return info;
+}
 
-    r->u.marc.input_format = input_format_mode;
-    r->u.marc.output_format = output_format_mode;
-    return 0;
+static int convert_marc(void *info, WRBUF record, WRBUF wr_error)
+{
+    struct marc_info *mi = info;
+    int ret = 0;
+
+    yaz_iconv_t cd = yaz_iconv_open(mi->output_charset, mi->input_charset);
+    yaz_marc_t mt = yaz_marc_create();
+
+    yaz_marc_xml(mt, mi->output_format_mode);
+    if (mi->leader_spec)
+        yaz_marc_leader_spec(mt, mi->leader_spec);
+
+    if (cd)
+        yaz_marc_iconv(mt, cd);
+    if (mi->input_format_mode == YAZ_MARC_ISO2709)
+    {
+        int sz = yaz_marc_read_iso2709(mt, wrbuf_buf(record),
+                                       wrbuf_len(record));
+        if (sz > 0)
+            ret = 0;
+        else
+            ret = -1;
+    }
+    else if (mi->input_format_mode == YAZ_MARC_MARCXML ||
+             mi->input_format_mode == YAZ_MARC_TURBOMARC)
+    {
+        xmlDocPtr doc = xmlParseMemory(wrbuf_buf(record),
+                                       wrbuf_len(record));
+        if (!doc)
+        {
+            wrbuf_printf(wr_error, "xmlParseMemory failed");
+            ret = -1;
+        }
+        else
+        {
+            ret = yaz_marc_read_xml(mt, xmlDocGetRootElement(doc));
+            if (ret)
+                wrbuf_printf(wr_error, "yaz_marc_read_xml failed");
+        }
+        xmlFreeDoc(doc);
+    }
+    else
+    {
+        wrbuf_printf(wr_error, "unsupported input format");
+        ret = -1;
+    }
+    if (ret == 0)
+    {
+        wrbuf_rewind(record);
+        ret = yaz_marc_write_mode(mt, record);
+        if (ret)
+            wrbuf_printf(wr_error, "yaz_marc_write_mode failed");
+    }
+    if (cd)
+        yaz_iconv_close(cd);
+    yaz_marc_destroy(mt);
+    return ret;
 }
 
-int yaz_record_conv_configure(yaz_record_conv_t p, const xmlNode *ptr)
+static void destroy_marc(void *info)
 {
+    struct marc_info *mi = info;
+
+    nmem_destroy(mi->nmem);
+}
+
+int yaz_record_conv_configure_t(yaz_record_conv_t p, const xmlNode *ptr,
+                                struct yaz_record_conv_type *types)
+{
+    struct yaz_record_conv_type bt[2];
+
+    /* register marc */
+    bt[0].construct = construct_marc;
+    bt[0].convert = convert_marc;
+    bt[0].destroy = destroy_marc;
+
+#if YAZ_HAVE_XSLT
+    /* register xslt */
+    bt[0].next = &bt[1];
+    bt[1].next = types;
+    bt[1].construct = construct_xslt;
+    bt[1].convert = convert_xslt;
+    bt[1].destroy = destroy_xslt;
+#else
+    bt[0].next = types;
+#endif
+
     yaz_record_conv_reset(p);
 
     /* parsing element children */
     for (ptr = ptr->children; ptr; ptr = ptr->next)
+    {
+        struct yaz_record_conv_type *t;
+        struct yaz_record_conv_rule *r;
+        void *info = 0;
+        if (ptr->type != XML_ELEMENT_NODE)
+            continue;
+        for (t = &bt[0]; t; t = t->next)
         {
-            if (ptr->type != XML_ELEMENT_NODE)
-                continue;
-            if (!strcmp((const char *) ptr->name, "xslt"))
-                {
-                    if (conv_xslt(p, ptr))
-                        return -1;
-                }
-            else if (!strcmp((const char *) ptr->name, "marc"))
-                {
-                    if (conv_marc(p, ptr))
-                        return -1;
-                }
-            else
-                {
-                    wrbuf_printf(p->wr_error, "Element <backend>: expected "
-                                 "<marc> or <xslt> element, got <%s>"
-                                 , ptr->name);
-                    return -1;
-                }
+            wrbuf_rewind(p->wr_error);
+            info = t->construct(ptr, p->path, p->wr_error);
+
+            if (info || wrbuf_len(p->wr_error))
+                break;
+            /* info== 0 and no error reported , ie not handled by it */
         }
+        if (!info)
+        {
+            if (wrbuf_len(p->wr_error) == 0)
+                wrbuf_printf(p->wr_error, "Element <backend>: expected "
+                             "<marc> or <xslt> element, got <%s>"
+                             , ptr->name);
+            return -1;
+        }
+        r = (struct yaz_record_conv_rule *) nmem_malloc(p->nmem, sizeof(*r));
+        r->next = 0;
+        r->info = info;
+        r->type = nmem_malloc(p->nmem, sizeof(*t));
+        memcpy(r->type, t, sizeof(*t));
+        *p->rules_p = r;
+        p->rules_p = &r->next;
+    }
     return 0;
 }
 
-int yaz_record_conv_record(yaz_record_conv_t p,
-                           const char *input_record_buf,
-                           size_t input_record_len,
-                           WRBUF output_record)
+int yaz_record_conv_configure(yaz_record_conv_t p, const xmlNode *ptr)
+{
+    return yaz_record_conv_configure_t(p, ptr, 0);
+}
+
+static int yaz_record_conv_record_rule(yaz_record_conv_t p,
+                                       struct yaz_record_conv_rule *r,
+                                       const char *input_record_buf,
+                                       size_t input_record_len,
+                                       WRBUF output_record)
 {
     int ret = 0;
     WRBUF record = output_record; /* pointer transfer */
-    struct yaz_record_conv_rule *r = p->rules;
     wrbuf_rewind(p->wr_error);
-    
+
     wrbuf_write(record, input_record_buf, input_record_len);
     for (; ret == 0 && r; r = r->next)
+        ret = r->type->convert(r->info, record, p->wr_error);
+    return ret;
+}
+
+int yaz_record_conv_opac_record(yaz_record_conv_t p,
+                                Z_OPACRecord *input_record,
+                                WRBUF output_record)
+{
+    int ret = 0;
+    struct yaz_record_conv_rule *r = p->rules;
+    if (!r || r->type->construct != construct_marc)
     {
-        if (r->which == YAZ_RECORD_CONV_RULE_MARC)
-        {
-            yaz_marc_t mt = yaz_marc_create();
+        wrbuf_puts(p->wr_error, "Expecting MARC rule as first rule for OPAC");
+        ret = -1; /* no marc rule so we can't do OPAC */
+    }
+    else
+    {
+        struct marc_info *mi = r->info;
 
-            yaz_marc_xml(mt, r->u.marc.output_format);
+        WRBUF res = wrbuf_alloc();
+        yaz_marc_t mt = yaz_marc_create();
+        yaz_iconv_t cd = yaz_iconv_open(mi->output_charset,
+                                        mi->input_charset);
 
-            if (r->u.marc.iconv_t)
-                yaz_marc_iconv(mt, r->u.marc.iconv_t);
-            if (r->u.marc.input_format == YAZ_MARC_ISO2709)
-            {
-                int sz = yaz_marc_read_iso2709(mt, wrbuf_buf(record),
-                                               wrbuf_len(record));
-                if (sz > 0)
-                    ret = 0;
-                else
-                    ret = -1;
-            }
-            else if (r->u.marc.input_format == YAZ_MARC_MARCXML)
-            {
-                xmlDocPtr doc = xmlParseMemory(wrbuf_buf(record),
-                                               wrbuf_len(record));
-                if (!doc)
-                {
-                    wrbuf_printf(p->wr_error, "xmlParseMemory failed");
-                    ret = -1;
-                }
-                else
-                {
-                    ret = yaz_marc_read_xml(mt, xmlDocGetRootElement(doc));
-                    if (ret)
-                        wrbuf_printf(p->wr_error, "yaz_marc_read_xml failed");
-                }
-                xmlFreeDoc(doc);
-            }
-            else
-            {
-                wrbuf_printf(p->wr_error, "unsupported input format");
-                ret = -1;
-            }
-            if (ret == 0)
-            {
-                wrbuf_rewind(record);
-                ret = yaz_marc_write_mode(mt, record);
-                if (ret)
-                    wrbuf_printf(p->wr_error, "yaz_marc_write_mode failed");
-            }
-            yaz_marc_destroy(mt);
-        }
-#if YAZ_HAVE_XSLT
-        else if (r->which == YAZ_RECORD_CONV_RULE_XSLT)
+        wrbuf_rewind(p->wr_error);
+        yaz_marc_xml(mt, mi->output_format_mode);
+
+        yaz_marc_iconv(mt, cd);
+
+        yaz_opac_decode_wrbuf(mt, input_record, res);
+        if (ret != -1)
         {
-            xmlDocPtr doc = xmlParseMemory(wrbuf_buf(record),
-                                           wrbuf_len(record));
-            if (!doc)
-            {
-                wrbuf_printf(p->wr_error, "xmlParseMemory failed");
-                ret = -1;
-            }
-            else
-            {
-                xmlDocPtr res = xsltApplyStylesheet(r->u.xslt.xsp, doc, 0);
-                if (res)
-                {
-                    xmlChar *out_buf = 0;
-                    int out_len;
-
-#if YAZ_HAVE_XSLTSAVERESULTTOSTRING
-                    xsltSaveResultToString(&out_buf, &out_len, res,
-                                           r->u.xslt.xsp); 
-#else
-                    xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
-#endif
-                    if (!out_buf)
-                    {
-                        wrbuf_printf(p->wr_error,
-                                     "xsltSaveResultToString failed");
-                        ret = -1;
-                    }
-                    else
-                    {
-                        wrbuf_rewind(record);
-                        wrbuf_write(record, (const char *) out_buf, out_len);
-                        
-                        xmlFree(out_buf);
-                    }
-                    xmlFreeDoc(res);
-                }
-                else
-                {
-                    wrbuf_printf(p->wr_error, "xsltApplyStylesheet failed");
-                    ret = -1;
-                }
-                xmlFreeDoc(doc);
-            }
+            ret = yaz_record_conv_record_rule(p,
+                                              r->next,
+                                              wrbuf_buf(res), wrbuf_len(res),
+                                              output_record);
         }
-#endif
+        yaz_marc_destroy(mt);
+        if (cd)
+            yaz_iconv_close(cd);
+        wrbuf_destroy(res);
     }
     return ret;
 }
 
+int yaz_record_conv_record(yaz_record_conv_t p,
+                           const char *input_record_buf,
+                           size_t input_record_len,
+                           WRBUF output_record)
+{
+    return yaz_record_conv_record_rule(p, p->rules,
+                                       input_record_buf,
+                                       input_record_len, output_record);
+}
+
 const char *yaz_record_conv_get_error(yaz_record_conv_t p)
 {
     return wrbuf_cstr(p->wr_error);
@@ -494,11 +694,24 @@ void yaz_record_conv_set_path(yaz_record_conv_t p, const char *path)
     if (path)
         p->path = xstrdup(path);
 }
+
+yaz_record_conv_t yaz_record_conv_create()
+{
+    yaz_record_conv_t p = (yaz_record_conv_t) xmalloc(sizeof(*p));
+    p->nmem = nmem_create();
+    p->wr_error = wrbuf_alloc();
+    p->rules = 0;
+    p->path = 0;
+    return p;
+}
+
+/* YAZ_HAVE_XML2 */
 #endif
 
 /*
  * Local variables:
  * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
  * indent-tabs-mode: nil
  * End:
  * vim: shiftwidth=4 tabstop=8 expandtab