ZOOM: diagnostics for invalid ES usage, bug #3893.
[yaz-moved-to-github.git] / src / xmlquery.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2011 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/oid_db.h>
24
25 static int check_diagnostic(const xmlNode *ptr, ODR odr,
26                             int *error_code, const char **addinfo)
27 {
28     if (ptr && ptr->type == XML_ELEMENT_NODE &&
29         !xmlStrcmp(ptr->name, BAD_CAST "diagnostic"))
30     {
31         struct _xmlAttr *attr;
32         const char *code_str = 0;
33         const char *addinfo_str = 0;
34         for (attr = ptr->properties; attr; attr = attr->next)
35         {
36             if (!xmlStrcmp(attr->name, BAD_CAST "code") &&
37                 attr->children && attr->children->type == XML_TEXT_NODE)
38                 code_str = (const char *) attr->children->content;
39             else if (!xmlStrcmp(attr->name, BAD_CAST "addinfo") &&
40                      attr->children && attr->children->type == XML_TEXT_NODE)
41                 addinfo_str = (const char *) attr->children->content;
42             else
43             {
44                 *error_code = 1;
45                 *addinfo = "bad attribute for diagnostic element";
46                 return 1;
47             }
48         }
49         if (!code_str)
50         {
51             *error_code = 1;
52             *addinfo = "missing @code for diagnostic element";
53             return 1;
54         }
55         *error_code = atoi(code_str);
56         if (addinfo_str)
57             *addinfo = odr_strdup(odr, addinfo_str);
58         return 1;
59     }
60     else
61         return 0;
62 }
63
64 static void yaz_query2xml_attribute_element(const Z_AttributeElement *element,
65                                             xmlNodePtr parent)
66 {
67     char formstr[30];
68     const char *setname = 0;
69     char oid_name_str[OID_STR_MAX];
70     
71     if (element->attributeSet)
72     {
73         setname = yaz_oid_to_string_buf(element->attributeSet,
74                                         0, oid_name_str);
75     }
76
77     if (element->which == Z_AttributeValue_numeric)
78     {
79         xmlNodePtr node = xmlNewChild(parent, 0, BAD_CAST "attr", 0);
80
81         if (setname)
82             xmlNewProp(node, BAD_CAST "set", BAD_CAST setname);
83
84         assert(*element->attributeType > 0 && *element->attributeType < 20);
85         sprintf(formstr, ODR_INT_PRINTF, *element->attributeType);
86         xmlNewProp(node, BAD_CAST "type", BAD_CAST formstr);
87
88         sprintf(formstr, ODR_INT_PRINTF, *element->value.numeric);
89         xmlNewProp(node, BAD_CAST "value", BAD_CAST formstr);
90     }
91     else if (element->which == Z_AttributeValue_complex)
92     {
93         int i;
94         for (i = 0; i<element->value.complex->num_list; i++)
95         {
96             xmlNodePtr node = xmlNewChild(parent, 0, BAD_CAST "attr", 0);
97             
98             if (setname)
99                 xmlNewProp(node, BAD_CAST "set", BAD_CAST setname);
100             
101             sprintf(formstr, ODR_INT_PRINTF, *element->attributeType);
102             xmlNewProp(node, BAD_CAST "type", BAD_CAST formstr);
103             
104             if (element->value.complex->list[i]->which ==
105                 Z_StringOrNumeric_string)
106             {
107                 xmlNewProp(node, BAD_CAST "value", BAD_CAST 
108                            element->value.complex->list[i]->u.string);
109             }
110             else if (element->value.complex->list[i]->which ==
111                      Z_StringOrNumeric_numeric)
112             {
113                 sprintf(formstr, ODR_INT_PRINTF,
114                         *element->value.complex->list[i]->u.numeric);
115                 xmlNewProp(node, BAD_CAST "value", BAD_CAST formstr);
116             }
117         }
118     }
119 }
120
121
122 static xmlNodePtr yaz_query2xml_term(const Z_Term *term, xmlNodePtr parent)
123 {
124     xmlNodePtr t = 0;
125     xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "term", 0);
126     char formstr[20];
127     const char *type = 0;
128
129     switch (term->which)
130     {
131     case Z_Term_general:
132         type = "general";
133         t = xmlNewTextLen(BAD_CAST term->u.general->buf, term->u.general->len);
134         break;
135     case Z_Term_numeric:
136         type = "numeric";
137         sprintf(formstr, ODR_INT_PRINTF, *term->u.numeric);
138         t = xmlNewText(BAD_CAST formstr);       
139         break;
140     case Z_Term_characterString:
141         type = "string";
142         t = xmlNewText(BAD_CAST term->u.characterString);
143         break;
144     case Z_Term_oid:
145         type = "oid";
146         break;
147     case Z_Term_dateTime:
148         type = "dateTime";
149         break;
150     case Z_Term_external:
151         type = "external";
152         break;
153     case Z_Term_integerAndUnit:
154         type ="integerAndUnit";
155         break;
156     case Z_Term_null:
157         type = "null";
158         break;
159     default:
160         break;
161     }
162     if (t) /* got a term node ? */
163         xmlAddChild(node, t);
164     if (type)
165         xmlNewProp(node, BAD_CAST "type", BAD_CAST type);
166     return node;
167 }
168
169 static xmlNodePtr yaz_query2xml_apt(const Z_AttributesPlusTerm *zapt,
170                                     xmlNodePtr parent)
171 {
172     xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "apt", 0);
173     int num_attributes = zapt->attributes->num_attributes;
174     int i;
175     for (i = 0; i<num_attributes; i++)
176         yaz_query2xml_attribute_element(zapt->attributes->attributes[i], node);
177     yaz_query2xml_term(zapt->term, node);
178
179     return node;
180 }
181
182
183 static void yaz_query2xml_operator(Z_Operator *op, xmlNodePtr node)
184 {
185     const char *type = 0;
186     switch(op->which)
187     {
188     case Z_Operator_and:
189         type = "and";
190         break;
191     case Z_Operator_or:
192         type = "or";
193         break;
194     case Z_Operator_and_not:
195         type = "not";
196         break;
197     case Z_Operator_prox:
198         type = "prox";
199         break;
200     default:
201         return;
202     }
203     xmlNewProp(node, BAD_CAST "type", BAD_CAST type);
204     
205     if (op->which == Z_Operator_prox)
206     {
207         char formstr[30];
208         
209         if (op->u.prox->exclusion)
210         {
211             if (*op->u.prox->exclusion)
212                 xmlNewProp(node, BAD_CAST "exclusion", BAD_CAST "true");
213             else
214                 xmlNewProp(node, BAD_CAST "exclusion", BAD_CAST "false");
215         }
216         sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->distance);
217         xmlNewProp(node, BAD_CAST "distance", BAD_CAST formstr);
218
219         if (*op->u.prox->ordered)
220             xmlNewProp(node, BAD_CAST "ordered", BAD_CAST "true");
221         else 
222             xmlNewProp(node, BAD_CAST "ordered", BAD_CAST "false");
223        
224         sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->relationType);
225         xmlNewProp(node, BAD_CAST "relationType", BAD_CAST formstr);
226         
227         switch(op->u.prox->which)
228         {
229         case Z_ProximityOperator_known:
230             sprintf(formstr, ODR_INT_PRINTF, *op->u.prox->u.known);
231             xmlNewProp(node, BAD_CAST "knownProximityUnit",
232                        BAD_CAST formstr);
233             break;
234         case Z_ProximityOperator_private:
235         default:
236             xmlNewProp(node, BAD_CAST "privateProximityUnit",
237                        BAD_CAST "private");
238             break;
239         }
240     }
241 }
242
243 static xmlNodePtr yaz_query2xml_rpnstructure(const Z_RPNStructure *zs,
244                                              xmlNodePtr parent)
245 {
246     if (zs->which == Z_RPNStructure_complex)
247     {
248         Z_Complex *zc = zs->u.complex;
249
250         xmlNodePtr node = xmlNewChild(parent, /* NS */ 0, BAD_CAST "operator", 0);
251         if (zc->roperator)
252             yaz_query2xml_operator(zc->roperator, node);
253         yaz_query2xml_rpnstructure(zc->s1, node);
254         yaz_query2xml_rpnstructure(zc->s2, node);
255         return node;
256     }
257     else if (zs->which == Z_RPNStructure_simple)
258     {
259         if (zs->u.simple->which == Z_Operand_APT)
260             return yaz_query2xml_apt(zs->u.simple->u.attributesPlusTerm,
261                                      parent);
262         else if (zs->u.simple->which == Z_Operand_resultSetId)
263             return xmlNewChild(parent, /* NS */ 0, BAD_CAST "rset", 
264                                BAD_CAST zs->u.simple->u.resultSetId);
265     }
266     return 0;
267 }
268
269 static xmlNodePtr yaz_query2xml_rpn(const Z_RPNQuery *rpn, xmlNodePtr parent)
270 {
271     if (rpn->attributeSetId)
272     {
273         char oid_name_str[OID_STR_MAX];
274         const char *setname = yaz_oid_to_string_buf(rpn->attributeSetId,
275                                                     0, oid_name_str);
276         if (setname)
277             xmlNewProp(parent, BAD_CAST "set", BAD_CAST setname);
278     }
279     return yaz_query2xml_rpnstructure(rpn->RPNStructure, parent);
280 }
281
282 static xmlNodePtr yaz_query2xml_ccl(const Odr_oct *ccl, xmlNodePtr node)
283 {
284     return 0;
285 }
286
287 static xmlNodePtr yaz_query2xml_z3958(const Odr_oct *ccl, xmlNodePtr node)
288 {
289     return 0;
290 }
291
292 static xmlNodePtr yaz_query2xml_cql(const char *cql, xmlNodePtr node)
293 {
294     return 0;
295 }
296
297 void yaz_rpnquery2xml(const Z_RPNQuery *rpn, xmlDocPtr *docp)
298 {
299     Z_Query query;
300
301     query.which = Z_Query_type_1;
302     query.u.type_1 = (Z_RPNQuery *) rpn;
303     yaz_query2xml(&query, docp);
304 }
305
306 void yaz_query2xml(const Z_Query *q, xmlDocPtr *docp)
307 {
308     xmlNodePtr top_node, q_node = 0, child_node = 0;
309
310     assert(q);
311     assert(docp);
312
313     top_node = xmlNewNode(0, BAD_CAST "query");
314
315     switch (q->which)
316     {
317     case Z_Query_type_1: 
318     case Z_Query_type_101:
319         q_node = xmlNewChild(top_node, 0, BAD_CAST "rpn", 0);
320         child_node = yaz_query2xml_rpn(q->u.type_1, q_node);
321         break;
322     case Z_Query_type_2:
323         q_node = xmlNewChild(top_node, 0, BAD_CAST "ccl", 0);
324         child_node = yaz_query2xml_ccl(q->u.type_2, q_node);
325         break;
326     case Z_Query_type_100:
327         q_node = xmlNewChild(top_node, 0, BAD_CAST "z39.58", 0);
328         child_node = yaz_query2xml_z3958(q->u.type_100, q_node);
329         break;
330     case Z_Query_type_104:
331         if (q->u.type_104->which == Z_External_CQL)
332         {
333             q_node = xmlNewChild(top_node, 0, BAD_CAST "cql", 0);
334             child_node = yaz_query2xml_cql(q->u.type_104->u.cql, q_node);
335         }
336     }
337     if (child_node && q_node)
338     {
339         *docp = xmlNewDoc(BAD_CAST "1.0");
340         xmlDocSetRootElement(*docp, top_node); /* make it top node in doc */
341     }
342     else
343     {
344         *docp = 0;
345         xmlFreeNode(top_node);
346     }
347 }
348
349 static bool_t *boolVal(ODR odr, const char *str)
350 {
351     if (*str == '\0' || strchr("0fF", *str))
352         return odr_booldup(odr, 0);
353     return odr_booldup(odr, 1);
354 }
355
356 static Odr_int *intVal(ODR odr, const char *str)
357 {
358     return odr_intdup(odr, atoi(str));
359 }
360
361 static void yaz_xml2query_operator(const xmlNode *ptr, Z_Operator **op,
362                                    ODR odr,
363                                    int *error_code, const char **addinfo)
364 {
365     xmlChar *type = xmlGetProp((xmlNodePtr) ptr, BAD_CAST "type");
366     if (!type)
367     {
368         *error_code = 1;
369         *addinfo = "no operator type";
370         return;
371     }
372     *op = (Z_Operator*) odr_malloc(odr, sizeof(Z_Operator));
373     if (!xmlStrcmp(type, BAD_CAST "and"))
374     {
375         (*op)->which = Z_Operator_and;
376         (*op)->u.op_and = odr_nullval();
377     }
378     else if (!xmlStrcmp(type, BAD_CAST "or"))
379     {
380         (*op)->which = Z_Operator_or;
381         (*op)->u.op_or = odr_nullval();
382     }
383     else if (!xmlStrcmp(type, BAD_CAST "not"))
384     {
385         (*op)->which = Z_Operator_and_not;
386         (*op)->u.and_not = odr_nullval();
387     }
388     else if (!xmlStrcmp(type, BAD_CAST "prox"))
389     {
390         struct _xmlAttr *attr;
391         Z_ProximityOperator *pop = (Z_ProximityOperator *)
392             odr_malloc(odr, sizeof(*pop));
393         (*op)->which = Z_Operator_prox;
394         (*op)->u.prox = pop;
395         /* default values */
396         pop->exclusion = 0;
397         pop->ordered = odr_booldup(odr, 1);
398         pop->relationType =
399             odr_intdup(odr, Z_ProximityOperator_Prox_lessThanOrEqual);
400         pop->which = Z_ProximityOperator_known;
401         pop->u.known = odr_intdup(odr, Z_ProxUnit_word);
402         pop->distance = odr_intdup(odr, 1);
403
404         for (attr = ptr->properties; attr; attr = attr->next)
405         {
406             const char *value = (const char *) attr->children->content;
407             if (!xmlStrcmp(attr->name, BAD_CAST "type"))
408                 ;
409             else if (!xmlStrcmp(attr->name, BAD_CAST "exclusion"))
410                 pop->exclusion = boolVal(odr, value);
411             else if (!xmlStrcmp(attr->name, BAD_CAST "distance"))
412                 pop->distance = intVal(odr, value);
413             else if (!xmlStrcmp(attr->name, BAD_CAST "ordered"))
414                 pop->ordered = boolVal(odr, value);
415             else if (!xmlStrcmp(attr->name, BAD_CAST "relationType"))
416                 pop->relationType = intVal(odr, value);
417             else if (!xmlStrcmp(attr->name, BAD_CAST "knownProximityUnit"))
418             {
419                 pop->which = Z_ProximityOperator_known;
420                 pop->u.known = intVal(odr, value);
421             }
422             else if (!xmlStrcmp(attr->name, BAD_CAST "privateProximityUnit"))
423             {
424                 pop->which = Z_ProximityOperator_private;
425                 pop->u.known = intVal(odr, value);
426             }
427             else
428             {
429                 *error_code = 1;
430                 *addinfo = "bad proximity attribute";
431                 break;
432             }
433         }
434     }
435     else
436     {
437         *error_code = 1;
438         *addinfo = "bad operator type";
439     }
440     xmlFree(type);
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, (unsigned char *)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     xmlChar *set = xmlGetProp((xmlNodePtr) ptr, BAD_CAST "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, (const char *) set, odr);
760         xmlFree(set);
761     }
762     else
763         (*query)->attributeSetId = 0;
764     yaz_xml2query_rpnstructure(ptr->children, &(*query)->RPNStructure,
765                                odr, error_code, addinfo);
766 }
767
768 static void yaz_xml2query_(const xmlNode *ptr, Z_Query **query, ODR odr,
769                            int *error_code, const char **addinfo)
770 {
771     if (check_diagnostic(ptr, odr, error_code, addinfo))
772         return;
773     if (ptr && ptr->type == XML_ELEMENT_NODE && 
774         !xmlStrcmp(ptr->name, BAD_CAST "query"))
775     {
776         const char *type;
777         ptr = ptr->children;
778         while (ptr && ptr->type != XML_ELEMENT_NODE)
779             ptr = ptr->next;
780         if (!ptr || ptr->type != XML_ELEMENT_NODE)
781         {
782             *error_code = 1;
783             *addinfo = "missing query content";
784             return;
785         }
786         type = (const char *) ptr->name;
787
788         *query = (Z_Query*) odr_malloc(odr, sizeof(Z_Query));
789         if (!type || !strcmp(type, "rpn"))
790         {
791             (*query)->which = Z_Query_type_1;
792             yaz_xml2query_rpn(ptr, &(*query)->u.type_1, odr,
793                               error_code, addinfo);
794         }
795         else if (!strcmp(type, "ccl"))
796         {
797             *error_code = 1;
798             *addinfo = "ccl not supported yet";
799         }
800         else if (!strcmp(type, "z39.58"))
801         {
802             *error_code = 1;
803             *addinfo = "z39.58 not supported yet";
804         }
805         else if (!strcmp(type, "cql"))
806         {
807             *error_code = 1;
808             *addinfo = "cql not supported yet";
809         }
810         else
811         {
812             *error_code = 1;
813             *addinfo = "unsupported query type";
814         }
815     }
816     else
817     {
818         *error_code = 1;
819         *addinfo = "missing query element";
820     }
821 }
822
823 void yaz_xml2query(const xmlNode *xmlnodep, Z_Query **query, ODR odr,
824                    int *error_code, const char **addinfo)
825 {
826     yaz_xml2query_(xmlnodep, query, odr, error_code, addinfo);
827 }
828
829 /* YAZ_HAVE_XML2 */
830 #endif
831
832 /*
833  * Local variables:
834  * c-basic-offset: 4
835  * c-file-style: "Stroustrup"
836  * indent-tabs-mode: nil
837  * End:
838  * vim: shiftwidth=4 tabstop=8 expandtab
839  */
840