New yaz_xml_get_prop utility YAZ-839
[yaz-moved-to-github.git] / src / xmlquery.c
index 515206e..4728de6 100644 (file)
-/*
- * Copyright (C) 1995-2005, Index Data ApS
- * All rights reserved.
- *
- * $Id: xmlquery.c,v 1.1 2006-01-27 17:28:16 adam Exp $
- */
-
-/**
- * \file querytostr.c
- * \brief Query / XML conversions
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) Index Data
+ * See the file LICENSE for details.
  */
+/** \file xmlquery.c
+    \brief Query / XML conversions
+*/
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
 
 #include <stdio.h>
+#include <string.h>
 #include <assert.h>
 
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 
 #include <yaz/logrpn.h>
 #include <yaz/xmlquery.h>
+#include <yaz/nmem_xml.h>
+#include <yaz/xml_get.h>
+#include <yaz/oid_db.h>
 
-xmlNodePtr yaz_query2xml_attribute_element(const Z_AttributeElement *element,
-                                          xmlNodePtr parent)
+static int check_diagnostic(const xmlNode *ptr, ODR odr,
+                            int *error_code, const char **addinfo)
 {
-    xmlNodePtr node = xmlNewChild(parent, 0, BAD_CAST "attr", 0);
-#if 0
-    int i;
-    char *setname  = 0;
+    if (ptr && ptr->type == XML_ELEMENT_NODE &&
+        !xmlStrcmp(ptr->name, BAD_CAST "diagnostic"))
+    {
+        struct _xmlAttr *attr;
+        const char *code_str = 0;
+        const char *addinfo_str = 0;
+        for (attr = ptr->properties; attr; attr = attr->next)
+        {
+            if (!xmlStrcmp(attr->name, BAD_CAST "code") &&
+                attr->children && attr->children->type == XML_TEXT_NODE)
+                code_str = (const char *) attr->children->content;
+            else if (!xmlStrcmp(attr->name, BAD_CAST "addinfo") &&
+                     attr->children && attr->children->type == XML_TEXT_NODE)
+                addinfo_str = (const char *) attr->children->content;
+            else
+            {
+                *error_code = 1;
+                *addinfo = "bad attribute for diagnostic element";
+                return 1;
+            }
+        }
+        if (!code_str)
+        {
+            *error_code = 1;
+            *addinfo = "missing @code for diagnostic element";
+            return 1;
+        }
+        *error_code = atoi(code_str);
+        if (addinfo_str)
+            *addinfo = odr_strdup(odr, addinfo_str);
+        return 1;
+    }
+    else
+        return 0;
+}
+
+static void yaz_query2xml_attribute_element(const Z_AttributeElement *element,
+                                            xmlNodePtr parent)
+{
+    char formstr[30];
+    const char *setname = 0;
+    char oid_name_str[OID_STR_MAX];
+
     if (element->attributeSet)
     {
-        oident *attrset;
-        attrset = oid_getentbyoid (element->attributeSet);
-        setname = attrset->desc;
+        setname = yaz_oid_to_string_buf(element->attributeSet,
+                                        0, oid_name_str);
     }
-    switch (element->which) 
+
+    if (element->which == Z_AttributeValue_numeric)
     {
-    case Z_AttributeValue_numeric:
-        wrbuf_printf(b,"@attr %s%s%d=%d ", setname, sep,
-                     *element->attributeType, *element->value.numeric);
-        break;
-    case Z_AttributeValue_complex:
-        wrbuf_printf(b,"@attr %s%s\"%d=", setname, sep,
-                     *element->attributeType);
+        xmlNodePtr node = xmlNewChild(parent, 0, BAD_CAST "attr", 0);
+
+        if (setname)
+            xmlNewProp(node, BAD_CAST "set", BAD_CAST setname);
+
+        assert(*element->attributeType > 0 && *element->attributeType < 20);
+        sprintf(formstr, ODR_INT_PRINTF, *element->attributeType);
+        xmlNewProp(node, BAD_CAST "type", BAD_CAST formstr);
+
+        sprintf(formstr, ODR_INT_PRINTF, *element->value.numeric);
+        xmlNewProp(node, BAD_CAST "value", BAD_CAST formstr);
+    }
+    else if (element->which == Z_AttributeValue_complex)
+    {
+        int i;
         for (i = 0; i<element->value.complex->num_list; i++)
         {
-            if (i)
-                wrbuf_printf(b,",");
+            xmlNodePtr node = xmlNewChild(parent, 0, BAD_CAST "attr", 0);
+
+            if (setname)
+                xmlNewProp(node, BAD_CAST "set", BAD_CAST setname);
+
+            sprintf(formstr, ODR_INT_PRINTF, *element->attributeType);
+            xmlNewProp(node, BAD_CAST "type", BAD_CAST formstr);
+
             if (element->value.complex->list[i]->which ==
                 Z_StringOrNumeric_string)
-                wrbuf_printf (b, "%s",
-                              element->value.complex->list[i]->u.string);
+            {
+                xmlNewProp(node, BAD_CAST "value", BAD_CAST
+                           element->value.complex->list[i]->u.string);
+            }
             else if (element->value.complex->list[i]->which ==
                      Z_StringOrNumeric_numeric)
-                wrbuf_printf (b, "%d", 
-                              *element->value.complex->list[i]->u.numeric);
+            {
+                sprintf(formstr, ODR_INT_PRINTF,
+                        *element->value.complex->list[i]->u.numeric);
+                xmlNewProp(node, BAD_CAST "value", BAD_CAST formstr);
+            }
         }
-        wrbuf_printf(b, "\" ");
-        break;
-    default:
-        wrbuf_printf (b, "@attr 1=unknown ");
     }
-#endif
-    return node;
 }
 
 
-xmlNodePtr yaz_query2xml_term(const Z_Term *term,
-                             xmlNodePtr parent)
+static xmlNodePtr yaz_query2xml_term(const Z_Term *term, xmlNodePtr parent)
 {
     xmlNodePtr t = 0;
     xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "term", 0);
     char formstr[20];
+    const char *type = 0;
 
     switch (term->which)
     {
     case Z_Term_general:
+        type = "general";
        t = xmlNewTextLen(BAD_CAST term->u.general->buf, term->u.general->len);
         break;
+    case Z_Term_numeric:
+        type = "numeric";
+       sprintf(formstr, ODR_INT_PRINTF, *term->u.numeric);
+       t = xmlNewText(BAD_CAST formstr);
+        break;
     case Z_Term_characterString:
+        type = "string";
        t = xmlNewText(BAD_CAST term->u.characterString);
         break;
-    case Z_Term_numeric:
-       sprintf(formstr, "%d", *term->u.numeric);
-       t = xmlNewText(BAD_CAST formstr);       
+    case Z_Term_oid:
+        type = "oid";
+        break;
+    case Z_Term_dateTime:
+        type = "dateTime";
+        break;
+    case Z_Term_external:
+        type = "external";
+        break;
+    case Z_Term_integerAndUnit:
+        type ="integerAndUnit";
         break;
     case Z_Term_null:
+        type = "null";
         break;
     default:
        break;
     }
     if (t) /* got a term node ? */
        xmlAddChild(node, t);
+    if (type)
+        xmlNewProp(node, BAD_CAST "type", BAD_CAST type);
     return node;
 }
 
-xmlNodePtr yaz_query2xml_apt(const Z_AttributesPlusTerm *zapt,
-                            xmlNodePtr parent)
+static xmlNodePtr yaz_query2xml_apt(const Z_AttributesPlusTerm *zapt,
+                                    xmlNodePtr parent)
 {
     xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "apt", 0);
     int num_attributes = zapt->attributes->num_attributes;
@@ -107,47 +180,80 @@ xmlNodePtr yaz_query2xml_apt(const Z_AttributesPlusTerm *zapt,
     return node;
 }
 
-xmlNodePtr yaz_query2xml_rpnstructure(const Z_RPNStructure *zs,
-                                     xmlNodePtr parent)
+
+static void yaz_query2xml_operator(Z_Operator *op, xmlNodePtr node)
 {
-    if (zs->which == Z_RPNStructure_complex)
+    const char *type = 0;
+    switch(op->which)
+    {
+    case Z_Operator_and:
+        type = "and";
+        break;
+    case Z_Operator_or:
+        type = "or";
+        break;
+    case Z_Operator_and_not:
+        type = "not";
+        break;
+    case Z_Operator_prox:
+        type = "prox";
+        break;
+    default:
+        return;
+    }
+    xmlNewProp(node, BAD_CAST "type", BAD_CAST type);
+
+    if (op->which == Z_Operator_prox)
     {
-       return 0;
-#if 0
-        Z_Operator *op = zs->u.complex->roperator;
-        wrbuf_printf(b, "@%s ", complex_op_name(op) );
-        if (op->which== Z_Operator_prox)
+        char formstr[30];
+
+        if (op->u.prox->exclusion)
         {
-            if (!op->u.prox->exclusion)
-                wrbuf_putc(b, 'n');
-            else if (*op->u.prox->exclusion)
-                wrbuf_putc(b, '1');
+            if (*op->u.prox->exclusion)
+                xmlNewProp(node, BAD_CAST "exclusion", BAD_CAST "true");
             else
-                wrbuf_putc(b, '0');
+                xmlNewProp(node, BAD_CAST "exclusion", BAD_CAST "false");
+        }
+        sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->distance);
+        xmlNewProp(node, BAD_CAST "distance", BAD_CAST formstr);
 
-            wrbuf_printf(b, " %d %d %d ", *op->u.prox->distance,
-                         *op->u.prox->ordered,
-                         *op->u.prox->relationType);
+        if (*op->u.prox->ordered)
+            xmlNewProp(node, BAD_CAST "ordered", BAD_CAST "true");
+        else
+            xmlNewProp(node, BAD_CAST "ordered", BAD_CAST "false");
 
-            switch(op->u.prox->which)
-            {
-            case Z_ProximityOperator_known:
-                wrbuf_putc(b, 'k');
-                break;
-            case Z_ProximityOperator_private:
-                wrbuf_putc(b, 'p');
-                break;
-            default:
-                wrbuf_printf(b, "%d", op->u.prox->which);
-            }
-            if (op->u.prox->u.known)
-                wrbuf_printf(b, " %d ", *op->u.prox->u.known);
-            else
-                wrbuf_printf(b, " 0 ");
+        sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->relationType);
+        xmlNewProp(node, BAD_CAST "relationType", BAD_CAST formstr);
+
+        switch(op->u.prox->which)
+        {
+        case Z_ProximityOperator_known:
+            sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->u.known);
+            xmlNewProp(node, BAD_CAST "knownProximityUnit",
+                       BAD_CAST formstr);
+            break;
+        case Z_ProximityOperator_private:
+        default:
+            xmlNewProp(node, BAD_CAST "privateProximityUnit",
+                       BAD_CAST "private");
+            break;
         }
-        yaz_rpnstructure_to_wrbuf(b,zs->u.complex->s1);
-        yaz_rpnstructure_to_wrbuf(b,zs->u.complex->s2);
-#endif
+    }
+}
+
+static xmlNodePtr yaz_query2xml_rpnstructure(const Z_RPNStructure *zs,
+                                             xmlNodePtr parent)
+{
+    if (zs->which == Z_RPNStructure_complex)
+    {
+        Z_Complex *zc = zs->u.complex;
+
+        xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "operator", 0);
+        if (zc->roperator)
+            yaz_query2xml_operator(zc->roperator, node);
+        yaz_query2xml_rpnstructure(zc->s1, node);
+        yaz_query2xml_rpnstructure(zc->s2, node);
+        return node;
     }
     else if (zs->which == Z_RPNStructure_simple)
     {
@@ -155,48 +261,52 @@ xmlNodePtr yaz_query2xml_rpnstructure(const Z_RPNStructure *zs,
             return yaz_query2xml_apt(zs->u.simple->u.attributesPlusTerm,
                                     parent);
         else if (zs->u.simple->which == Z_Operand_resultSetId)
-        {
-           return 0;
-#if 0
-            yaz_term_to_wrbuf(b, zs->u.simple->u.resultSetId,
-                              strlen(zs->u.simple->u.resultSetId));
-#endif
-        }
-        else
-           return 0;
+            return xmlNewChild(parent, /* NS */ 0, BAD_CAST "rset",
+                               BAD_CAST zs->u.simple->u.resultSetId);
     }
     return 0;
 }
 
-xmlNodePtr yaz_query2xml_rpn(const Z_RPNQuery *rpn, xmlNodePtr parent)
+static xmlNodePtr yaz_query2xml_rpn(const Z_RPNQuery *rpn, xmlNodePtr parent)
 {
-    oident *attrset = oid_getentbyoid (rpn->attributeSetId);
-    if (attrset && attrset->value)
-       parent = xmlNewChild(parent, /*ns */ 0,
-                            BAD_CAST "attrset", BAD_CAST attrset->desc);
+    if (rpn->attributeSetId)
+    {
+        char oid_name_str[OID_STR_MAX];
+        const char *setname = yaz_oid_to_string_buf(rpn->attributeSetId,
+                                                    0, oid_name_str);
+        if (setname)
+            xmlNewProp(parent, BAD_CAST "set", BAD_CAST setname);
+    }
     return yaz_query2xml_rpnstructure(rpn->RPNStructure, parent);
 }
 
-xmlNodePtr yaz_query2xml_ccl(const Odr_oct *ccl, xmlNodePtr node)
+static xmlNodePtr yaz_query2xml_ccl(const Odr_oct *ccl, xmlNodePtr node)
 {
     return 0;
 }
 
-xmlNodePtr yaz_query2xml_z3958(const Odr_oct *ccl, xmlNodePtr node)
+static xmlNodePtr yaz_query2xml_z3958(const Odr_oct *ccl, xmlNodePtr node)
 {
     return 0;
 }
 
-xmlNodePtr yaz_query2xml_cql(const char *cql, xmlNodePtr node)
+static xmlNodePtr yaz_query2xml_cql(const char *cql, xmlNodePtr node)
 {
     return 0;
 }
 
-void yaz_query2xml(const Z_Query *q, void *docp_void)
+void yaz_rpnquery2xml(const Z_RPNQuery *rpn, xmlDocPtr *docp)
 {
-    xmlDocPtr *docp = (xmlDocPtr *) docp_void;
-    xmlNodePtr top_node, child_node = 0;
-    const char *type = 0;
+    Z_Query query;
+
+    query.which = Z_Query_type_1;
+    query.u.type_1 = (Z_RPNQuery *) rpn;
+    yaz_query2xml(&query, docp);
+}
+
+void yaz_query2xml(const Z_Query *q, xmlDocPtr *docp)
+{
+    xmlNodePtr top_node, q_node = 0, child_node = 0;
 
     assert(q);
     assert(docp);
@@ -205,34 +315,30 @@ void yaz_query2xml(const Z_Query *q, void *docp_void)
 
     switch (q->which)
     {
-    case Z_Query_type_1: 
+    case Z_Query_type_1:
     case Z_Query_type_101:
-       type = "rpn";
-       child_node = yaz_query2xml_rpn(q->u.type_1, top_node);
+        q_node = xmlNewChild(top_node, 0, BAD_CAST "rpn", 0);
+       child_node = yaz_query2xml_rpn(q->u.type_1, q_node);
         break;
     case Z_Query_type_2:
-       type = "ccl";
-       child_node = yaz_query2xml_ccl(q->u.type_2, top_node);
+        q_node = xmlNewChild(top_node, 0, BAD_CAST "ccl", 0);
+       child_node = yaz_query2xml_ccl(q->u.type_2, q_node);
         break;
     case Z_Query_type_100:
-       type = "z39.58";
-       child_node = yaz_query2xml_z3958(q->u.type_100, top_node);
+        q_node = xmlNewChild(top_node, 0, BAD_CAST "z39.58", 0);
+       child_node = yaz_query2xml_z3958(q->u.type_100, q_node);
         break;
     case Z_Query_type_104:
         if (q->u.type_104->which == Z_External_CQL)
        {
-           type = "cql";
-           child_node = yaz_query2xml_cql(q->u.type_104->u.cql, top_node);
+            q_node = xmlNewChild(top_node, 0, BAD_CAST "cql", 0);
+           child_node = yaz_query2xml_cql(q->u.type_104->u.cql, q_node);
        }
     }
-    
-    if (child_node && type)
+    if (child_node && q_node)
     {
        *docp = xmlNewDoc(BAD_CAST "1.0");
        xmlDocSetRootElement(*docp, top_node); /* make it top node in doc */
-
-       /* set type attribute now */
-       xmlNewProp(top_node, BAD_CAST "type", BAD_CAST type);
     }
     else
     {
@@ -241,20 +347,493 @@ void yaz_query2xml(const Z_Query *q, void *docp_void)
     }
 }
 
-void yaz_xml2query(const xmlNode node, Z_Query **q, ODR odr)
+static bool_t *boolVal(ODR odr, const char *str)
+{
+    if (*str == '\0' || strchr("0fF", *str))
+        return odr_booldup(odr, 0);
+    return odr_booldup(odr, 1);
+}
+
+static Odr_int *intVal(ODR odr, const char *str)
+{
+    return odr_intdup(odr, atoi(str));
+}
+
+static void yaz_xml2query_operator(const xmlNode *ptr, Z_Operator **op,
+                                   ODR odr,
+                                   int *error_code, const char **addinfo)
+{
+    const char *type = yaz_xml_get_prop((xmlNodePtr) ptr, "type");
+    if (!type)
+    {
+        *error_code = 1;
+        *addinfo = "no operator type";
+        return;
+    }
+    *op = (Z_Operator*) odr_malloc(odr, sizeof(Z_Operator));
+    if (!strcmp(type, "and"))
+    {
+        (*op)->which = Z_Operator_and;
+        (*op)->u.op_and = odr_nullval();
+    }
+    else if (!strcmp(type, "or"))
+    {
+        (*op)->which = Z_Operator_or;
+        (*op)->u.op_or = odr_nullval();
+    }
+    else if (!strcmp(type, "not"))
+    {
+        (*op)->which = Z_Operator_and_not;
+        (*op)->u.and_not = odr_nullval();
+    }
+    else if (!strcmp(type, "prox"))
+    {
+        struct _xmlAttr *attr;
+        Z_ProximityOperator *pop = (Z_ProximityOperator *)
+            odr_malloc(odr, sizeof(*pop));
+        (*op)->which = Z_Operator_prox;
+        (*op)->u.prox = pop;
+        /* default values */
+        pop->exclusion = 0;
+        pop->ordered = odr_booldup(odr, 1);
+        pop->relationType =
+            odr_intdup(odr, Z_ProximityOperator_Prox_lessThanOrEqual);
+        pop->which = Z_ProximityOperator_known;
+        pop->u.known = odr_intdup(odr, Z_ProxUnit_word);
+        pop->distance = odr_intdup(odr, 1);
+
+        for (attr = ptr->properties; attr; attr = attr->next)
+        {
+            const char *value = (const char *) attr->children->content;
+            if (!xmlStrcmp(attr->name, BAD_CAST "type"))
+                ;
+            else if (!xmlStrcmp(attr->name, BAD_CAST "exclusion"))
+                pop->exclusion = boolVal(odr, value);
+            else if (!xmlStrcmp(attr->name, BAD_CAST "distance"))
+                pop->distance = intVal(odr, value);
+            else if (!xmlStrcmp(attr->name, BAD_CAST "ordered"))
+                pop->ordered = boolVal(odr, value);
+            else if (!xmlStrcmp(attr->name, BAD_CAST "relationType"))
+                pop->relationType = intVal(odr, value);
+            else if (!xmlStrcmp(attr->name, BAD_CAST "knownProximityUnit"))
+            {
+                pop->which = Z_ProximityOperator_known;
+                pop->u.known = intVal(odr, value);
+            }
+            else if (!xmlStrcmp(attr->name, BAD_CAST "privateProximityUnit"))
+            {
+                pop->which = Z_ProximityOperator_private;
+                pop->u.known = intVal(odr, value);
+            }
+            else
+            {
+                *error_code = 1;
+                *addinfo = "bad proximity attribute";
+                break;
+            }
+        }
+    }
+    else
+    {
+        *error_code = 1;
+        *addinfo = "bad operator type";
+    }
+}
+
+static void yaz_xml2query_attribute_element(const xmlNode *ptr,
+                                            Z_AttributeElement **elem, ODR odr,
+                                            int *error_code,
+                                            const char **addinfo)
+{
+    int i;
+    xmlChar *set = 0;
+    xmlChar *type = 0;
+    xmlChar *value = 0;
+    int num_values = 0;
+    struct _xmlAttr *attr;
+    for (attr = ptr->properties; attr; attr = attr->next)
+    {
+        if (!xmlStrcmp(attr->name, BAD_CAST "set") &&
+            attr->children && attr->children->type == XML_TEXT_NODE)
+            set = attr->children->content;
+        else if (!xmlStrcmp(attr->name, BAD_CAST "type") &&
+            attr->children && attr->children->type == XML_TEXT_NODE)
+            type = attr->children->content;
+        else if (!xmlStrcmp(attr->name, BAD_CAST "value") &&
+            attr->children && attr->children->type == XML_TEXT_NODE)
+        {
+            value = attr->children->content;
+            num_values++;
+        }
+        else
+        {
+            *error_code = 1;
+            *addinfo = "bad attribute for attr content";
+            return;
+        }
+    }
+    if (!type)
+    {
+        *error_code = 1;
+        *addinfo = "missing type attribute for att content";
+        return;
+    }
+    if (!value)
+    {
+        *error_code = 1;
+        *addinfo = "missing value attribute for att content";
+        return;
+    }
+
+    *elem = (Z_AttributeElement *) odr_malloc(odr, sizeof(**elem));
+    if (set)
+        (*elem)->attributeSet = yaz_string_to_oid_odr(yaz_oid_std(),
+                                                      CLASS_ATTSET,
+                                                      (const char *) set,
+                                                      odr);
+    else
+        (*elem)->attributeSet = 0;
+    (*elem)->attributeType = intVal(odr, (const char *) type);
+
+    /* looks like a number ? */
+    for (i = 0; value[i] && value[i] >= '0' && value[i] <= '9'; i++)
+        ;
+    if (num_values > 1 || value[i])
+    {   /* multiple values or string, so turn to complex attribute */
+        (*elem)->which = Z_AttributeValue_complex;
+        (*elem)->value.complex =
+            (Z_ComplexAttribute*) odr_malloc(odr, sizeof(Z_ComplexAttribute));
+        (*elem)->value.complex->num_list = num_values;
+        (*elem)->value.complex->list = (Z_StringOrNumeric **)
+            odr_malloc(odr, sizeof(Z_StringOrNumeric*) * num_values);
+
+        /* second pass over attr values */
+        i = 0;
+        for (attr = ptr->properties; attr; attr = attr->next)
+        {
+            if (!xmlStrcmp(attr->name, BAD_CAST "value") &&
+                attr->children && attr->children->type == XML_TEXT_NODE)
+            {
+                const char *val = (const char *) attr->children->content;
+                assert (i < num_values);
+                (*elem)->value.complex->list[i] = (Z_StringOrNumeric *)
+                    odr_malloc(odr, sizeof(Z_StringOrNumeric));
+                (*elem)->value.complex->list[i]->which =
+                    Z_StringOrNumeric_string;
+                (*elem)->value.complex->list[i]->u.string =
+                    odr_strdup(odr, val);
+                i++;
+            }
+        }
+        (*elem)->value.complex->num_semanticAction = 0;
+        (*elem)->value.complex->semanticAction = 0;
+    }
+    else
+    {   /* good'ld numeric value */
+        (*elem)->which = Z_AttributeValue_numeric;
+        (*elem)->value.numeric = intVal(odr, (const char *) value);
+    }
+}
+
+static char *strVal(const xmlNode *ptr_cdata, ODR odr)
+{
+    return nmem_text_node_cdata(ptr_cdata, odr_getmem(odr));
+}
+
+static void yaz_xml2query_term(const xmlNode *ptr, Z_Term **term, ODR odr,
+                               int *error_code, const char **addinfo)
+{
+    xmlChar *type = 0;
+    struct _xmlAttr *attr;
+    char *cdata = strVal(ptr->children, odr);
+
+    for (attr = ptr->properties; attr; attr = attr->next)
+    {
+        if (!xmlStrcmp(attr->name, BAD_CAST "type") &&
+            attr->children && attr->children->type == XML_TEXT_NODE)
+            type = attr->children->content;
+        else
+        {
+            *error_code = 1;
+            *addinfo = "bad attribute for attr content";
+            return;
+        }
+    }
+    *term = (Z_Term *) odr_malloc(odr, sizeof(Z_Term));
+
+    if (!type || !xmlStrcmp(type, BAD_CAST "general"))
+    {
+        (*term)->which = Z_Term_general;
+        (*term)->u.general =
+            odr_create_Odr_oct(odr, cdata, strlen(cdata));
+    }
+    else if (!xmlStrcmp(type, BAD_CAST "numeric"))
+    {
+        (*term)->which = Z_Term_numeric;
+        (*term)->u.numeric = intVal(odr, cdata);
+    }
+    else if (!xmlStrcmp(type, BAD_CAST "string"))
+    {
+        (*term)->which = Z_Term_characterString;
+        (*term)->u.characterString = cdata;
+    }
+    else if (!xmlStrcmp(type, BAD_CAST "oid"))
+    {
+        *error_code = 1;
+        *addinfo = "unhandled term type: oid";
+    }
+    else if (!xmlStrcmp(type, BAD_CAST "dateTime"))
+    {
+        *error_code = 1;
+        *addinfo = "unhandled term type: dateTime";
+    }
+    else if (!xmlStrcmp(type, BAD_CAST "integerAndUnit"))
+    {
+        *error_code = 1;
+        *addinfo = "unhandled term type: integerAndUnit";
+    }
+    else if (!xmlStrcmp(type, BAD_CAST "null"))
+    {
+        (*term)->which = Z_Term_null;
+        (*term)->u.null = odr_nullval();
+    }
+    else
+    {
+        *error_code = 1;
+        *addinfo = "unhandled term type";
+    }
+}
+
+static void yaz_xml2query_apt(const xmlNode *ptr_apt,
+                              Z_AttributesPlusTerm **zapt, ODR odr,
+                              int *error_code, const char **addinfo)
+{
+    const xmlNode *ptr = ptr_apt->children;
+    int i, num_attr = 0;
+
+    *zapt = (Z_AttributesPlusTerm *)
+        odr_malloc(odr, sizeof(Z_AttributesPlusTerm));
+
+    /* deal with attributes */
+    (*zapt)->attributes = (Z_AttributeList*)
+        odr_malloc(odr, sizeof(Z_AttributeList));
+
+    /* how many attributes? */
+    for (; ptr; ptr = ptr->next)
+        if (ptr->type == XML_ELEMENT_NODE)
+        {
+            if (!xmlStrcmp(ptr->name, BAD_CAST "attr"))
+                num_attr++;
+            else
+                break;
+        }
+
+    /* allocate and parse for real */
+    (*zapt)->attributes->num_attributes = num_attr;
+    (*zapt)->attributes->attributes = (Z_AttributeElement **)
+        odr_malloc(odr, sizeof(Z_AttributeElement*) * num_attr);
+
+    i = 0;
+    ptr = ptr_apt->children;
+    for (; ptr; ptr = ptr->next)
+        if (ptr->type == XML_ELEMENT_NODE)
+        {
+            if (!xmlStrcmp(ptr->name, BAD_CAST "attr"))
+            {
+                yaz_xml2query_attribute_element(
+                    ptr,  &(*zapt)->attributes->attributes[i], odr,
+                    error_code, addinfo);
+                i++;
+            }
+            else
+                break;
+        }
+    if (check_diagnostic(ptr, odr, error_code, addinfo))
+        return;
+
+    if (ptr && ptr->type == XML_ELEMENT_NODE)
+    {
+        if (!xmlStrcmp(ptr->name, BAD_CAST "term"))
+        {
+            /* deal with term */
+            yaz_xml2query_term(ptr, &(*zapt)->term, odr, error_code, addinfo);
+        }
+        else
+        {
+            *error_code = 1;
+            *addinfo = "bad element in apt content";
+        }
+    }
+    else
+    {
+        *error_code = 1;
+        *addinfo = "missing term node in apt content";
+    }
+}
+
+static void yaz_xml2query_rset(const xmlNode *ptr, Z_ResultSetId **rset,
+                               ODR odr, int *error_code, const char **addinfo)
 {
+    if (ptr->children)
+    {
+        *rset = strVal(ptr->children, odr);
+    }
+    else
+    {
+        *error_code = 1;
+        *addinfo = "missing rset content";
+    }
+}
 
+static void yaz_xml2query_rpnstructure(const xmlNode *ptr, Z_RPNStructure **zs,
+                                       ODR odr,
+                                       int *error_code, const char **addinfo)
+{
+    while (ptr && ptr->type != XML_ELEMENT_NODE)
+        ptr = ptr->next;
 
+    if (!ptr || ptr->type != XML_ELEMENT_NODE)
+    {
+        *error_code = 1;
+        *addinfo = "missing rpn operator, rset, apt node";
+        return;
+    }
+    if (check_diagnostic(ptr, odr, error_code, addinfo))
+        return;
+
+    *zs = (Z_RPNStructure *) odr_malloc(odr, sizeof(Z_RPNStructure));
+    if (!xmlStrcmp(ptr->name, BAD_CAST "operator"))
+    {
+        Z_Complex *zc = (Z_Complex *) odr_malloc(odr, sizeof(Z_Complex));
+
+        (*zs)->which = Z_RPNStructure_complex;
+        (*zs)->u.complex = zc;
+
+        yaz_xml2query_operator(ptr, &zc->roperator, odr, error_code, addinfo);
+
+        ptr = ptr->children;
+        while (ptr && ptr->type != XML_ELEMENT_NODE)
+            ptr = ptr->next;
+        yaz_xml2query_rpnstructure(ptr, &zc->s1, odr, error_code, addinfo);
+        if (ptr)
+            ptr = ptr->next;
+        while (ptr && ptr->type != XML_ELEMENT_NODE)
+            ptr = ptr->next;
+        yaz_xml2query_rpnstructure(ptr, &zc->s2, odr, error_code, addinfo);
+    }
+    else
+    {
+        Z_Operand *s = (Z_Operand *) odr_malloc(odr, sizeof(Z_Operand));
+        (*zs)->which = Z_RPNStructure_simple;
+        (*zs)->u.simple = s;
+        if (!xmlStrcmp(ptr->name, BAD_CAST "apt"))
+        {
+            s->which = Z_Operand_APT;
+            yaz_xml2query_apt(ptr, &s->u.attributesPlusTerm,
+                              odr, error_code, addinfo);
+        }
+        else if (!xmlStrcmp(ptr->name, BAD_CAST "rset"))
+        {
+            s->which = Z_Operand_resultSetId;
+            yaz_xml2query_rset(ptr, &s->u.resultSetId,
+                               odr, error_code, addinfo);
+        }
+        else
+        {
+            *error_code = 1;
+            *addinfo = "bad element: expected binary, apt or rset";
+        }
+    }
 }
 
+static void yaz_xml2query_rpn(const xmlNode *ptr, Z_RPNQuery **query, ODR odr,
+                              int *error_code, const char **addinfo)
+{
+    const char  *set = yaz_xml_get_prop((xmlNodePtr) ptr, "set");
+
+    *query = (Z_RPNQuery*) odr_malloc(odr, sizeof(Z_RPNQuery));
+    if (set)
+    {
+        (*query)->attributeSetId =
+            yaz_string_to_oid_odr(yaz_oid_std(),
+                                  CLASS_ATTSET, set, odr);
+    }
+    else
+        (*query)->attributeSetId = 0;
+    yaz_xml2query_rpnstructure(ptr->children, &(*query)->RPNStructure,
+                               odr, error_code, addinfo);
+}
+
+static void yaz_xml2query_(const xmlNode *ptr, Z_Query **query, ODR odr,
+                           int *error_code, const char **addinfo)
+{
+    if (check_diagnostic(ptr, odr, error_code, addinfo))
+        return;
+    if (ptr && ptr->type == XML_ELEMENT_NODE &&
+        !xmlStrcmp(ptr->name, BAD_CAST "query"))
+    {
+        const char *type;
+        ptr = ptr->children;
+        while (ptr && ptr->type != XML_ELEMENT_NODE)
+            ptr = ptr->next;
+        if (!ptr || ptr->type != XML_ELEMENT_NODE)
+        {
+            *error_code = 1;
+            *addinfo = "missing query content";
+            return;
+        }
+        type = (const char *) ptr->name;
+
+        *query = (Z_Query*) odr_malloc(odr, sizeof(Z_Query));
+        if (!type || !strcmp(type, "rpn"))
+        {
+            (*query)->which = Z_Query_type_1;
+            yaz_xml2query_rpn(ptr, &(*query)->u.type_1, odr,
+                              error_code, addinfo);
+        }
+        else if (!strcmp(type, "ccl"))
+        {
+            *error_code = 1;
+            *addinfo = "ccl not supported yet";
+        }
+        else if (!strcmp(type, "z39.58"))
+        {
+            *error_code = 1;
+            *addinfo = "z39.58 not supported yet";
+        }
+        else if (!strcmp(type, "cql"))
+        {
+            *error_code = 1;
+            *addinfo = "cql not supported yet";
+        }
+        else
+        {
+            *error_code = 1;
+            *addinfo = "unsupported query type";
+        }
+    }
+    else
+    {
+        *error_code = 1;
+        *addinfo = "missing query element";
+    }
+}
 
-/* HAVE_XML2 */
+void yaz_xml2query(const xmlNode *xmlnodep, Z_Query **query, ODR odr,
+                   int *error_code, const char **addinfo)
+{
+    yaz_xml2query_(xmlnodep, query, odr, error_code, addinfo);
+}
+
+/* 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
  */
+