New yaz_xml_get_prop utility YAZ-839
[yaz-moved-to-github.git] / src / xmlquery.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /** \file xmlquery.c
6     \brief Query / XML conversions
7 */
8 #if HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <stdio.h>
13 #include <string.h>
14 #include <assert.h>
15
16 #if YAZ_HAVE_XML2
17 #include <libxml/parser.h>
18 #include <libxml/tree.h>
19
20 #include <yaz/logrpn.h>
21 #include <yaz/xmlquery.h>
22 #include <yaz/nmem_xml.h>
23 #include <yaz/xml_get.h>
24 #include <yaz/oid_db.h>
25
26 static int check_diagnostic(const xmlNode *ptr, ODR odr,
27                             int *error_code, const char **addinfo)
28 {
29     if (ptr && ptr->type == XML_ELEMENT_NODE &&
30         !xmlStrcmp(ptr->name, BAD_CAST "diagnostic"))
31     {
32         struct _xmlAttr *attr;
33         const char *code_str = 0;
34         const char *addinfo_str = 0;
35         for (attr = ptr->properties; attr; attr = attr->next)
36         {
37             if (!xmlStrcmp(attr->name, BAD_CAST "code") &&
38                 attr->children && attr->children->type == XML_TEXT_NODE)
39                 code_str = (const char *) attr->children->content;
40             else if (!xmlStrcmp(attr->name, BAD_CAST "addinfo") &&
41                      attr->children && attr->children->type == XML_TEXT_NODE)
42                 addinfo_str = (const char *) attr->children->content;
43             else
44             {
45                 *error_code = 1;
46                 *addinfo = "bad attribute for diagnostic element";
47                 return 1;
48             }
49         }
50         if (!code_str)
51         {
52             *error_code = 1;
53             *addinfo = "missing @code for diagnostic element";
54             return 1;
55         }
56         *error_code = atoi(code_str);
57         if (addinfo_str)
58             *addinfo = odr_strdup(odr, addinfo_str);
59         return 1;
60     }
61     else
62         return 0;
63 }
64
65 static void yaz_query2xml_attribute_element(const Z_AttributeElement *element,
66                                             xmlNodePtr parent)
67 {
68     char formstr[30];
69     const char *setname = 0;
70     char oid_name_str[OID_STR_MAX];
71
72     if (element->attributeSet)
73     {
74         setname = yaz_oid_to_string_buf(element->attributeSet,
75                                         0, oid_name_str);
76     }
77
78     if (element->which == Z_AttributeValue_numeric)
79     {
80         xmlNodePtr node = xmlNewChild(parent, 0, BAD_CAST "attr", 0);
81
82         if (setname)
83             xmlNewProp(node, BAD_CAST "set", BAD_CAST setname);
84
85         assert(*element->attributeType > 0 && *element->attributeType < 20);
86         sprintf(formstr, ODR_INT_PRINTF, *element->attributeType);
87         xmlNewProp(node, BAD_CAST "type", BAD_CAST formstr);
88
89         sprintf(formstr, ODR_INT_PRINTF, *element->value.numeric);
90         xmlNewProp(node, BAD_CAST "value", BAD_CAST formstr);
91     }
92     else if (element->which == Z_AttributeValue_complex)
93     {
94         int i;
95         for (i = 0; i<element->value.complex->num_list; i++)
96         {
97             xmlNodePtr node = xmlNewChild(parent, 0, BAD_CAST "attr", 0);
98
99             if (setname)
100                 xmlNewProp(node, BAD_CAST "set", BAD_CAST setname);
101
102             sprintf(formstr, ODR_INT_PRINTF, *element->attributeType);
103             xmlNewProp(node, BAD_CAST "type", BAD_CAST formstr);
104
105             if (element->value.complex->list[i]->which ==
106                 Z_StringOrNumeric_string)
107             {
108                 xmlNewProp(node, BAD_CAST "value", BAD_CAST
109                            element->value.complex->list[i]->u.string);
110             }
111             else if (element->value.complex->list[i]->which ==
112                      Z_StringOrNumeric_numeric)
113             {
114                 sprintf(formstr, ODR_INT_PRINTF,
115                         *element->value.complex->list[i]->u.numeric);
116                 xmlNewProp(node, BAD_CAST "value", BAD_CAST formstr);
117             }
118         }
119     }
120 }
121
122
123 static xmlNodePtr yaz_query2xml_term(const Z_Term *term, xmlNodePtr parent)
124 {
125     xmlNodePtr t = 0;
126     xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "term", 0);
127     char formstr[20];
128     const char *type = 0;
129
130     switch (term->which)
131     {
132     case Z_Term_general:
133         type = "general";
134         t = xmlNewTextLen(BAD_CAST term->u.general->buf, term->u.general->len);
135         break;
136     case Z_Term_numeric:
137         type = "numeric";
138         sprintf(formstr, ODR_INT_PRINTF, *term->u.numeric);
139         t = xmlNewText(BAD_CAST formstr);
140         break;
141     case Z_Term_characterString:
142         type = "string";
143         t = xmlNewText(BAD_CAST term->u.characterString);
144         break;
145     case Z_Term_oid:
146         type = "oid";
147         break;
148     case Z_Term_dateTime:
149         type = "dateTime";
150         break;
151     case Z_Term_external:
152         type = "external";
153         break;
154     case Z_Term_integerAndUnit:
155         type ="integerAndUnit";
156         break;
157     case Z_Term_null:
158         type = "null";
159         break;
160     default:
161         break;
162     }
163     if (t) /* got a term node ? */
164         xmlAddChild(node, t);
165     if (type)
166         xmlNewProp(node, BAD_CAST "type", BAD_CAST type);
167     return node;
168 }
169
170 static xmlNodePtr yaz_query2xml_apt(const Z_AttributesPlusTerm *zapt,
171                                     xmlNodePtr parent)
172 {
173     xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "apt", 0);
174     int num_attributes = zapt->attributes->num_attributes;
175     int i;
176     for (i = 0; i<num_attributes; i++)
177         yaz_query2xml_attribute_element(zapt->attributes->attributes[i], node);
178     yaz_query2xml_term(zapt->term, node);
179
180     return node;
181 }
182
183
184 static void yaz_query2xml_operator(Z_Operator *op, xmlNodePtr node)
185 {
186     const char *type = 0;
187     switch(op->which)
188     {
189     case Z_Operator_and:
190         type = "and";
191         break;
192     case Z_Operator_or:
193         type = "or";
194         break;
195     case Z_Operator_and_not:
196         type = "not";
197         break;
198     case Z_Operator_prox:
199         type = "prox";
200         break;
201     default:
202         return;
203     }
204     xmlNewProp(node, BAD_CAST "type", BAD_CAST type);
205
206     if (op->which == Z_Operator_prox)
207     {
208         char formstr[30];
209
210         if (op->u.prox->exclusion)
211         {
212             if (*op->u.prox->exclusion)
213                 xmlNewProp(node, BAD_CAST "exclusion", BAD_CAST "true");
214             else
215                 xmlNewProp(node, BAD_CAST "exclusion", BAD_CAST "false");
216         }
217         sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->distance);
218         xmlNewProp(node, BAD_CAST "distance", BAD_CAST formstr);
219
220         if (*op->u.prox->ordered)
221             xmlNewProp(node, BAD_CAST "ordered", BAD_CAST "true");
222         else
223             xmlNewProp(node, BAD_CAST "ordered", BAD_CAST "false");
224
225         sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->relationType);
226         xmlNewProp(node, BAD_CAST "relationType", BAD_CAST formstr);
227
228         switch(op->u.prox->which)
229         {
230         case Z_ProximityOperator_known:
231             sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->u.known);
232             xmlNewProp(node, BAD_CAST "knownProximityUnit",
233                        BAD_CAST formstr);
234             break;
235         case Z_ProximityOperator_private:
236         default:
237             xmlNewProp(node, BAD_CAST "privateProximityUnit",
238                        BAD_CAST "private");
239             break;
240         }
241     }
242 }
243
244 static xmlNodePtr yaz_query2xml_rpnstructure(const Z_RPNStructure *zs,
245                                              xmlNodePtr parent)
246 {
247     if (zs->which == Z_RPNStructure_complex)
248     {
249         Z_Complex *zc = zs->u.complex;
250
251         xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "operator", 0);
252         if (zc->roperator)
253             yaz_query2xml_operator(zc->roperator, node);
254         yaz_query2xml_rpnstructure(zc->s1, node);
255         yaz_query2xml_rpnstructure(zc->s2, node);
256         return node;
257     }
258     else if (zs->which == Z_RPNStructure_simple)
259     {
260         if (zs->u.simple->which == Z_Operand_APT)
261             return yaz_query2xml_apt(zs->u.simple->u.attributesPlusTerm,
262                                      parent);
263         else if (zs->u.simple->which == Z_Operand_resultSetId)
264             return xmlNewChild(parent, /* NS */ 0, BAD_CAST "rset",
265                                BAD_CAST zs->u.simple->u.resultSetId);
266     }
267     return 0;
268 }
269
270 static xmlNodePtr yaz_query2xml_rpn(const Z_RPNQuery *rpn, xmlNodePtr parent)
271 {
272     if (rpn->attributeSetId)
273     {
274         char oid_name_str[OID_STR_MAX];
275         const char *setname = yaz_oid_to_string_buf(rpn->attributeSetId,
276                                                     0, oid_name_str);
277         if (setname)
278             xmlNewProp(parent, BAD_CAST "set", BAD_CAST setname);
279     }
280     return yaz_query2xml_rpnstructure(rpn->RPNStructure, parent);
281 }
282
283 static xmlNodePtr yaz_query2xml_ccl(const Odr_oct *ccl, xmlNodePtr node)
284 {
285     return 0;
286 }
287
288 static xmlNodePtr yaz_query2xml_z3958(const Odr_oct *ccl, xmlNodePtr node)
289 {
290     return 0;
291 }
292
293 static xmlNodePtr yaz_query2xml_cql(const char *cql, xmlNodePtr node)
294 {
295     return 0;
296 }
297
298 void yaz_rpnquery2xml(const Z_RPNQuery *rpn, xmlDocPtr *docp)
299 {
300     Z_Query query;
301
302     query.which = Z_Query_type_1;
303     query.u.type_1 = (Z_RPNQuery *) rpn;
304     yaz_query2xml(&query, docp);
305 }
306
307 void yaz_query2xml(const Z_Query *q, xmlDocPtr *docp)
308 {
309     xmlNodePtr top_node, q_node = 0, child_node = 0;
310
311     assert(q);
312     assert(docp);
313
314     top_node = xmlNewNode(0, BAD_CAST "query");
315
316     switch (q->which)
317     {
318     case Z_Query_type_1:
319     case Z_Query_type_101:
320         q_node = xmlNewChild(top_node, 0, BAD_CAST "rpn", 0);
321         child_node = yaz_query2xml_rpn(q->u.type_1, q_node);
322         break;
323     case Z_Query_type_2:
324         q_node = xmlNewChild(top_node, 0, BAD_CAST "ccl", 0);
325         child_node = yaz_query2xml_ccl(q->u.type_2, q_node);
326         break;
327     case Z_Query_type_100:
328         q_node = xmlNewChild(top_node, 0, BAD_CAST "z39.58", 0);
329         child_node = yaz_query2xml_z3958(q->u.type_100, q_node);
330         break;
331     case Z_Query_type_104:
332         if (q->u.type_104->which == Z_External_CQL)
333         {
334             q_node = xmlNewChild(top_node, 0, BAD_CAST "cql", 0);
335             child_node = yaz_query2xml_cql(q->u.type_104->u.cql, q_node);
336         }
337     }
338     if (child_node && q_node)
339     {
340         *docp = xmlNewDoc(BAD_CAST "1.0");
341         xmlDocSetRootElement(*docp, top_node); /* make it top node in doc */
342     }
343     else
344     {
345         *docp = 0;
346         xmlFreeNode(top_node);
347     }
348 }
349
350 static bool_t *boolVal(ODR odr, const char *str)
351 {
352     if (*str == '\0' || strchr("0fF", *str))
353         return odr_booldup(odr, 0);
354     return odr_booldup(odr, 1);
355 }
356
357 static Odr_int *intVal(ODR odr, const char *str)
358 {
359     return odr_intdup(odr, atoi(str));
360 }
361
362 static void yaz_xml2query_operator(const xmlNode *ptr, Z_Operator **op,
363                                    ODR odr,
364                                    int *error_code, const char **addinfo)
365 {
366     const char *type = yaz_xml_get_prop((xmlNodePtr) ptr, "type");
367     if (!type)
368     {
369         *error_code = 1;
370         *addinfo = "no operator type";
371         return;
372     }
373     *op = (Z_Operator*) odr_malloc(odr, sizeof(Z_Operator));
374     if (!strcmp(type, "and"))
375     {
376         (*op)->which = Z_Operator_and;
377         (*op)->u.op_and = odr_nullval();
378     }
379     else if (!strcmp(type, "or"))
380     {
381         (*op)->which = Z_Operator_or;
382         (*op)->u.op_or = odr_nullval();
383     }
384     else if (!strcmp(type, "not"))
385     {
386         (*op)->which = Z_Operator_and_not;
387         (*op)->u.and_not = odr_nullval();
388     }
389     else if (!strcmp(type, "prox"))
390     {
391         struct _xmlAttr *attr;
392         Z_ProximityOperator *pop = (Z_ProximityOperator *)
393             odr_malloc(odr, sizeof(*pop));
394         (*op)->which = Z_Operator_prox;
395         (*op)->u.prox = pop;
396         /* default values */
397         pop->exclusion = 0;
398         pop->ordered = odr_booldup(odr, 1);
399         pop->relationType =
400             odr_intdup(odr, Z_ProximityOperator_Prox_lessThanOrEqual);
401         pop->which = Z_ProximityOperator_known;
402         pop->u.known = odr_intdup(odr, Z_ProxUnit_word);
403         pop->distance = odr_intdup(odr, 1);
404
405         for (attr = ptr->properties; attr; attr = attr->next)
406         {
407             const char *value = (const char *) attr->children->content;
408             if (!xmlStrcmp(attr->name, BAD_CAST "type"))
409                 ;
410             else if (!xmlStrcmp(attr->name, BAD_CAST "exclusion"))
411                 pop->exclusion = boolVal(odr, value);
412             else if (!xmlStrcmp(attr->name, BAD_CAST "distance"))
413                 pop->distance = intVal(odr, value);
414             else if (!xmlStrcmp(attr->name, BAD_CAST "ordered"))
415                 pop->ordered = boolVal(odr, value);
416             else if (!xmlStrcmp(attr->name, BAD_CAST "relationType"))
417                 pop->relationType = intVal(odr, value);
418             else if (!xmlStrcmp(attr->name, BAD_CAST "knownProximityUnit"))
419             {
420                 pop->which = Z_ProximityOperator_known;
421                 pop->u.known = intVal(odr, value);
422             }
423             else if (!xmlStrcmp(attr->name, BAD_CAST "privateProximityUnit"))
424             {
425                 pop->which = Z_ProximityOperator_private;
426                 pop->u.known = intVal(odr, value);
427             }
428             else
429             {
430                 *error_code = 1;
431                 *addinfo = "bad proximity attribute";
432                 break;
433             }
434         }
435     }
436     else
437     {
438         *error_code = 1;
439         *addinfo = "bad operator type";
440     }
441 }
442
443 static void yaz_xml2query_attribute_element(const xmlNode *ptr,
444                                             Z_AttributeElement **elem, ODR odr,
445                                             int *error_code,
446                                             const char **addinfo)
447 {
448     int i;
449     xmlChar *set = 0;
450     xmlChar *type = 0;
451     xmlChar *value = 0;
452     int num_values = 0;
453     struct _xmlAttr *attr;
454     for (attr = ptr->properties; attr; attr = attr->next)
455     {
456         if (!xmlStrcmp(attr->name, BAD_CAST "set") &&
457             attr->children && attr->children->type == XML_TEXT_NODE)
458             set = attr->children->content;
459         else if (!xmlStrcmp(attr->name, BAD_CAST "type") &&
460             attr->children && attr->children->type == XML_TEXT_NODE)
461             type = attr->children->content;
462         else if (!xmlStrcmp(attr->name, BAD_CAST "value") &&
463             attr->children && attr->children->type == XML_TEXT_NODE)
464         {
465             value = attr->children->content;
466             num_values++;
467         }
468         else
469         {
470             *error_code = 1;
471             *addinfo = "bad attribute for attr content";
472             return;
473         }
474     }
475     if (!type)
476     {
477         *error_code = 1;
478         *addinfo = "missing type attribute for att content";
479         return;
480     }
481     if (!value)
482     {
483         *error_code = 1;
484         *addinfo = "missing value attribute for att content";
485         return;
486     }
487
488     *elem = (Z_AttributeElement *) odr_malloc(odr, sizeof(**elem));
489     if (set)
490         (*elem)->attributeSet = yaz_string_to_oid_odr(yaz_oid_std(),
491                                                       CLASS_ATTSET,
492                                                       (const char *) set,
493                                                       odr);
494     else
495         (*elem)->attributeSet = 0;
496     (*elem)->attributeType = intVal(odr, (const char *) type);
497
498     /* looks like a number ? */
499     for (i = 0; value[i] && value[i] >= '0' && value[i] <= '9'; i++)
500         ;
501     if (num_values > 1 || value[i])
502     {   /* multiple values or string, so turn to complex attribute */
503         (*elem)->which = Z_AttributeValue_complex;
504         (*elem)->value.complex =
505             (Z_ComplexAttribute*) odr_malloc(odr, sizeof(Z_ComplexAttribute));
506         (*elem)->value.complex->num_list = num_values;
507         (*elem)->value.complex->list = (Z_StringOrNumeric **)
508             odr_malloc(odr, sizeof(Z_StringOrNumeric*) * num_values);
509
510         /* second pass over attr values */
511         i = 0;
512         for (attr = ptr->properties; attr; attr = attr->next)
513         {
514             if (!xmlStrcmp(attr->name, BAD_CAST "value") &&
515                 attr->children && attr->children->type == XML_TEXT_NODE)
516             {
517                 const char *val = (const char *) attr->children->content;
518                 assert (i < num_values);
519                 (*elem)->value.complex->list[i] = (Z_StringOrNumeric *)
520                     odr_malloc(odr, sizeof(Z_StringOrNumeric));
521                 (*elem)->value.complex->list[i]->which =
522                     Z_StringOrNumeric_string;
523                 (*elem)->value.complex->list[i]->u.string =
524                     odr_strdup(odr, val);
525                 i++;
526             }
527         }
528         (*elem)->value.complex->num_semanticAction = 0;
529         (*elem)->value.complex->semanticAction = 0;
530     }
531     else
532     {   /* good'ld numeric value */
533         (*elem)->which = Z_AttributeValue_numeric;
534         (*elem)->value.numeric = intVal(odr, (const char *) value);
535     }
536 }
537
538 static char *strVal(const xmlNode *ptr_cdata, ODR odr)
539 {
540     return nmem_text_node_cdata(ptr_cdata, odr_getmem(odr));
541 }
542
543 static void yaz_xml2query_term(const xmlNode *ptr, Z_Term **term, ODR odr,
544                                int *error_code, const char **addinfo)
545 {
546     xmlChar *type = 0;
547     struct _xmlAttr *attr;
548     char *cdata = strVal(ptr->children, odr);
549
550     for (attr = ptr->properties; attr; attr = attr->next)
551     {
552         if (!xmlStrcmp(attr->name, BAD_CAST "type") &&
553             attr->children && attr->children->type == XML_TEXT_NODE)
554             type = attr->children->content;
555         else
556         {
557             *error_code = 1;
558             *addinfo = "bad attribute for attr content";
559             return;
560         }
561     }
562     *term = (Z_Term *) odr_malloc(odr, sizeof(Z_Term));
563
564     if (!type || !xmlStrcmp(type, BAD_CAST "general"))
565     {
566         (*term)->which = Z_Term_general;
567         (*term)->u.general =
568             odr_create_Odr_oct(odr, cdata, strlen(cdata));
569     }
570     else if (!xmlStrcmp(type, BAD_CAST "numeric"))
571     {
572         (*term)->which = Z_Term_numeric;
573         (*term)->u.numeric = intVal(odr, cdata);
574     }
575     else if (!xmlStrcmp(type, BAD_CAST "string"))
576     {
577         (*term)->which = Z_Term_characterString;
578         (*term)->u.characterString = cdata;
579     }
580     else if (!xmlStrcmp(type, BAD_CAST "oid"))
581     {
582         *error_code = 1;
583         *addinfo = "unhandled term type: oid";
584     }
585     else if (!xmlStrcmp(type, BAD_CAST "dateTime"))
586     {
587         *error_code = 1;
588         *addinfo = "unhandled term type: dateTime";
589     }
590     else if (!xmlStrcmp(type, BAD_CAST "integerAndUnit"))
591     {
592         *error_code = 1;
593         *addinfo = "unhandled term type: integerAndUnit";
594     }
595     else if (!xmlStrcmp(type, BAD_CAST "null"))
596     {
597         (*term)->which = Z_Term_null;
598         (*term)->u.null = odr_nullval();
599     }
600     else
601     {
602         *error_code = 1;
603         *addinfo = "unhandled term type";
604     }
605 }
606
607 static void yaz_xml2query_apt(const xmlNode *ptr_apt,
608                               Z_AttributesPlusTerm **zapt, ODR odr,
609                               int *error_code, const char **addinfo)
610 {
611     const xmlNode *ptr = ptr_apt->children;
612     int i, num_attr = 0;
613
614     *zapt = (Z_AttributesPlusTerm *)
615         odr_malloc(odr, sizeof(Z_AttributesPlusTerm));
616
617     /* deal with attributes */
618     (*zapt)->attributes = (Z_AttributeList*)
619         odr_malloc(odr, sizeof(Z_AttributeList));
620
621     /* how many attributes? */
622     for (; ptr; ptr = ptr->next)
623         if (ptr->type == XML_ELEMENT_NODE)
624         {
625             if (!xmlStrcmp(ptr->name, BAD_CAST "attr"))
626                 num_attr++;
627             else
628                 break;
629         }
630
631     /* allocate and parse for real */
632     (*zapt)->attributes->num_attributes = num_attr;
633     (*zapt)->attributes->attributes = (Z_AttributeElement **)
634         odr_malloc(odr, sizeof(Z_AttributeElement*) * num_attr);
635
636     i = 0;
637     ptr = ptr_apt->children;
638     for (; ptr; ptr = ptr->next)
639         if (ptr->type == XML_ELEMENT_NODE)
640         {
641             if (!xmlStrcmp(ptr->name, BAD_CAST "attr"))
642             {
643                 yaz_xml2query_attribute_element(
644                     ptr,  &(*zapt)->attributes->attributes[i], odr,
645                     error_code, addinfo);
646                 i++;
647             }
648             else
649                 break;
650         }
651     if (check_diagnostic(ptr, odr, error_code, addinfo))
652         return;
653
654     if (ptr && ptr->type == XML_ELEMENT_NODE)
655     {
656         if (!xmlStrcmp(ptr->name, BAD_CAST "term"))
657         {
658             /* deal with term */
659             yaz_xml2query_term(ptr, &(*zapt)->term, odr, error_code, addinfo);
660         }
661         else
662         {
663             *error_code = 1;
664             *addinfo = "bad element in apt content";
665         }
666     }
667     else
668     {
669         *error_code = 1;
670         *addinfo = "missing term node in apt content";
671     }
672 }
673
674 static void yaz_xml2query_rset(const xmlNode *ptr, Z_ResultSetId **rset,
675                                ODR odr, int *error_code, const char **addinfo)
676 {
677     if (ptr->children)
678     {
679         *rset = strVal(ptr->children, odr);
680     }
681     else
682     {
683         *error_code = 1;
684         *addinfo = "missing rset content";
685     }
686 }
687
688 static void yaz_xml2query_rpnstructure(const xmlNode *ptr, Z_RPNStructure **zs,
689                                        ODR odr,
690                                        int *error_code, const char **addinfo)
691 {
692     while (ptr && ptr->type != XML_ELEMENT_NODE)
693         ptr = ptr->next;
694
695     if (!ptr || ptr->type != XML_ELEMENT_NODE)
696     {
697         *error_code = 1;
698         *addinfo = "missing rpn operator, rset, apt node";
699         return;
700     }
701     if (check_diagnostic(ptr, odr, error_code, addinfo))
702         return;
703
704     *zs = (Z_RPNStructure *) odr_malloc(odr, sizeof(Z_RPNStructure));
705     if (!xmlStrcmp(ptr->name, BAD_CAST "operator"))
706     {
707         Z_Complex *zc = (Z_Complex *) odr_malloc(odr, sizeof(Z_Complex));
708
709         (*zs)->which = Z_RPNStructure_complex;
710         (*zs)->u.complex = zc;
711
712         yaz_xml2query_operator(ptr, &zc->roperator, odr, error_code, addinfo);
713
714         ptr = ptr->children;
715         while (ptr && ptr->type != XML_ELEMENT_NODE)
716             ptr = ptr->next;
717         yaz_xml2query_rpnstructure(ptr, &zc->s1, odr, error_code, addinfo);
718         if (ptr)
719             ptr = ptr->next;
720         while (ptr && ptr->type != XML_ELEMENT_NODE)
721             ptr = ptr->next;
722         yaz_xml2query_rpnstructure(ptr, &zc->s2, odr, error_code, addinfo);
723     }
724     else
725     {
726         Z_Operand *s = (Z_Operand *) odr_malloc(odr, sizeof(Z_Operand));
727         (*zs)->which = Z_RPNStructure_simple;
728         (*zs)->u.simple = s;
729         if (!xmlStrcmp(ptr->name, BAD_CAST "apt"))
730         {
731             s->which = Z_Operand_APT;
732             yaz_xml2query_apt(ptr, &s->u.attributesPlusTerm,
733                               odr, error_code, addinfo);
734         }
735         else if (!xmlStrcmp(ptr->name, BAD_CAST "rset"))
736         {
737             s->which = Z_Operand_resultSetId;
738             yaz_xml2query_rset(ptr, &s->u.resultSetId,
739                                odr, error_code, addinfo);
740         }
741         else
742         {
743             *error_code = 1;
744             *addinfo = "bad element: expected binary, apt or rset";
745         }
746     }
747 }
748
749 static void yaz_xml2query_rpn(const xmlNode *ptr, Z_RPNQuery **query, ODR odr,
750                               int *error_code, const char **addinfo)
751 {
752     const char  *set = yaz_xml_get_prop((xmlNodePtr) ptr, "set");
753
754     *query = (Z_RPNQuery*) odr_malloc(odr, sizeof(Z_RPNQuery));
755     if (set)
756     {
757         (*query)->attributeSetId =
758             yaz_string_to_oid_odr(yaz_oid_std(),
759                                   CLASS_ATTSET, set, odr);
760     }
761     else
762         (*query)->attributeSetId = 0;
763     yaz_xml2query_rpnstructure(ptr->children, &(*query)->RPNStructure,
764                                odr, error_code, addinfo);
765 }
766
767 static void yaz_xml2query_(const xmlNode *ptr, Z_Query **query, ODR odr,
768                            int *error_code, const char **addinfo)
769 {
770     if (check_diagnostic(ptr, odr, error_code, addinfo))
771         return;
772     if (ptr && ptr->type == XML_ELEMENT_NODE &&
773         !xmlStrcmp(ptr->name, BAD_CAST "query"))
774     {
775         const char *type;
776         ptr = ptr->children;
777         while (ptr && ptr->type != XML_ELEMENT_NODE)
778             ptr = ptr->next;
779         if (!ptr || ptr->type != XML_ELEMENT_NODE)
780         {
781             *error_code = 1;
782             *addinfo = "missing query content";
783             return;
784         }
785         type = (const char *) ptr->name;
786
787         *query = (Z_Query*) odr_malloc(odr, sizeof(Z_Query));
788         if (!type || !strcmp(type, "rpn"))
789         {
790             (*query)->which = Z_Query_type_1;
791             yaz_xml2query_rpn(ptr, &(*query)->u.type_1, odr,
792                               error_code, addinfo);
793         }
794         else if (!strcmp(type, "ccl"))
795         {
796             *error_code = 1;
797             *addinfo = "ccl not supported yet";
798         }
799         else if (!strcmp(type, "z39.58"))
800         {
801             *error_code = 1;
802             *addinfo = "z39.58 not supported yet";
803         }
804         else if (!strcmp(type, "cql"))
805         {
806             *error_code = 1;
807             *addinfo = "cql not supported yet";
808         }
809         else
810         {
811             *error_code = 1;
812             *addinfo = "unsupported query type";
813         }
814     }
815     else
816     {
817         *error_code = 1;
818         *addinfo = "missing query element";
819     }
820 }
821
822 void yaz_xml2query(const xmlNode *xmlnodep, Z_Query **query, ODR odr,
823                    int *error_code, const char **addinfo)
824 {
825     yaz_xml2query_(xmlnodep, query, odr, error_code, addinfo);
826 }
827
828 /* YAZ_HAVE_XML2 */
829 #endif
830
831 /*
832  * Local variables:
833  * c-basic-offset: 4
834  * c-file-style: "Stroustrup"
835  * indent-tabs-mode: nil
836  * End:
837  * vim: shiftwidth=4 tabstop=8 expandtab
838  */
839