1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2019 Olof Hagsand
6 Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
7
8 This file is part of CLIXON.
9
10 Licensed under the Apache License, Version 2.0 (the "License");
11 you may not use this file except in compliance with the License.
12 You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21
22 Alternatively, the contents of this file may be used under the terms of
23 the GNU General Public License Version 3 or later (the "GPL"),
24 in which case the provisions of the GPL are applicable instead
25 of those above. If you wish to allow use of your version of this file only
26 under the terms of the GPL, and not to allow others to
27 use your version of this file under the terms of Apache License version 2, indicate
28 your decision by deleting the provisions above and replace them with the
29 notice and other provisions required by the GPL. If you do not delete
30 the provisions above, a recipient may use your version of this file under
31 the terms of any one of the Apache License version 2 or the GPL.
32
33 ***** END LICENSE BLOCK *****
34
35 * Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
36 *
37 * Some notes on namespace extensions in Netconf/Yang
38 * RFC6241 8.9.1
39 * The set of namespace declarations are those in scope on the <filter> element.
40 * <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
41 * <get-config>
42 * <filter xmlns:t="http://example.com/schema/1.2/config"
43 * type="xpath"
44 * select="/t:top/t:users/t:user[t:name='fred']"/>
45 * </get-config>
46 * We need to add namespace context to the cpath tree, typically in eval. How do
47 * we do that?
48 * One observation is that the namespace context is static, so it can not be a part
49 * of the xpath-tree, which is context-dependent.
50 * Best is to send it as a (read-only) parameter to the xp_eval family of functions
51 * as an exlicit namespace context.
52 * For that you need an API to get/set namespaces: clixon_xml_nscache.c?
53 * Then you need to fix API functions and this is the real work:
54 * - Replace all existing functions or create new?
55 * - Expose explicit namespace parameter, or xml object, or default namespace?
56 */
57 #ifdef HAVE_CONFIG_H
58 #include "clixon_config.h" /* generated by config & autoconf */
59 #endif
60
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <unistd.h>
64 #include <errno.h>
65 #include <string.h>
66 #include <limits.h>
67 #include <stdint.h>
68 #include <assert.h>
69 #include <syslog.h>
70 #include <fcntl.h>
71 #include <math.h> /* NaN */
72
73 /* cligen */
74 #include <cligen/cligen.h>
75
76 /* clicon */
77 #include "clixon_err.h"
78 #include "clixon_log.h"
79 #include "clixon_string.h"
80 #include "clixon_queue.h"
81 #include "clixon_hash.h"
82 #include "clixon_handle.h"
83 #include "clixon_yang.h"
84 #include "clixon_xml.h"
85 #include "clixon_xml_sort.h"
86 #include "clixon_xml_nsctx.h"
87 #include "clixon_xpath_ctx.h"
88 #include "clixon_xpath.h"
89 #include "clixon_xpath_optimize.h"
90 #include "clixon_xpath_function.h"
91 #include "clixon_xpath_eval.h"
92
93 /* Mapping between XPATH operator string <--> int */
94 const map_str2int xpopmap[] = {
95 {"and", XO_AND},
96 {"or", XO_OR},
97 {"div", XO_DIV},
98 {"mod", XO_MOD},
99 {"+", XO_ADD},
100 {"*", XO_MULT},
101 {"-", XO_SUB},
102 {"=", XO_EQ},
103 {"!=", XO_NE},
104 {">=", XO_GE},
105 {"<=", XO_LE},
106 {"<", XO_LT},
107 {">", XO_GT},
108 {"|", XO_UNION},
109 {NULL, -1}
110 };
111
112 /*! Eval an XPATH nodetest
113 * @retval -1 Error XXX: retval -1 not properly handled
114 * @retval 0 No match
115 * @retval 1 Match
116 */
117 static int
nodetest_eval_node(cxobj * x,xpath_tree * xs,cvec * nsc)118 nodetest_eval_node(cxobj *x,
119 xpath_tree *xs,
120 cvec *nsc)
121 {
122 int retval = -1;
123 char *name1 = xml_name(x);
124 char *prefix1 = xml_prefix(x);
125 char *nsxml = NULL; /* xml body namespace */
126 char *nsxpath = NULL; /* xpath context namespace */
127 char *prefix2 = NULL;
128 char *name2 = NULL;
129
130 /* Namespaces is s0, name is s1 */
131 if (strcmp(xs->xs_s1, "*")==0)
132 return 1;
133 /* get namespace of xml tree */
134 if (xml2ns(x, prefix1, &nsxml) < 0)
135 goto done;
136 prefix2 = xs->xs_s0;
137 name2 = xs->xs_s1;
138 /* Before going into namespaces, check name equality and filter out noteq */
139 if (strcmp(name1, name2) != 0){
140 retval = 0; /* no match */
141 goto done;
142 }
143 /* here names are equal
144 * Now look for namespaces
145 * 1) prefix1 and prefix2 point to same namespace <<-- try this first
146 * 2) prefix1 is equal to prefix2 <<-- then try this
147 * (1) is strict yang xml
148 * (2) without yang
149 */
150 if (nsc != NULL) { /* solution (1) */
151 nsxpath = xml_nsctx_get(nsc, prefix2);
152 if (nsxml != NULL && nsxpath != NULL)
153 retval = (strcmp(nsxml, nsxpath) == 0);
154 else
155 retval = (nsxml == nsxpath); /* True only if both are NULL */
156 }
157 else{ /* solution (2) */
158 if (prefix1 == NULL && prefix2 == NULL)
159 retval = 1;
160 else if (prefix1 == NULL || prefix2 == NULL)
161 retval = 0;
162 else
163 retval = strcmp(prefix1, prefix2) == 0;
164 }
165 #if 0 /* debugging */
166 /* If retval == 0 here, then there is name match, but not ns match */
167 if (retval == 0){
168 fprintf(stderr, "%s NOMATCH xml: (%s)%s\n\t\t xpath: (%s)%s\n", __FUNCTION__,
169 name1, nsxml,
170 name2, nsxpath);
171 }
172 #endif
173 done: /* retval set in preceding statement */
174 return retval;
175 }
176
177 /*! Eval an XPATH nodetest but skip prefix and namespace tests
178 * This is NOT according to standard
179 */
180 static int
nodetest_eval_node_localonly(cxobj * x,xpath_tree * xs,cvec * nsc)181 nodetest_eval_node_localonly(cxobj *x,
182 xpath_tree *xs,
183 cvec *nsc)
184 {
185 int retval = -1;
186 char *name1 = xml_name(x);
187 char *name2 = NULL;
188
189 /* Namespaces is s0, name is s1 */
190 if (strcmp(xs->xs_s1, "*")==0)
191 return 1;
192 name2 = xs->xs_s1;
193 /* Before going into namespaces, check name equality and filter out noteq */
194 if (strcmp(name1, name2) != 0){
195 retval = 0; /* no match */
196 goto done;
197 }
198 done: /* retval set in preceding statement */
199 return retval;
200 }
201
202 /*! Make a nodetest
203 * @param[in] x XML node
204 * @param[in] xs XPATH stack of type XP_NODE or XP_NODE_FN
205 * @param[in] nsc XML Namespace context
206 * @param[in] localonly Skip prefix and namespace tests (non-standard)
207 * @retval -1 Error
208 * @retval 0 No match
209 * @retval 1 Match
210 * - node() is true for any node of any type whatsoever.
211 * - text() is true for any text node.
212 */
213 static int
nodetest_eval(cxobj * x,xpath_tree * xs,cvec * nsc,int localonly)214 nodetest_eval(cxobj *x,
215 xpath_tree *xs,
216 cvec *nsc,
217 int localonly)
218 {
219 int retval = 0; /* NB: no match is default (not error) */
220
221 if (xs->xs_type == XP_NODE){
222 if (localonly)
223 retval = nodetest_eval_node_localonly(x, xs, nsc);
224 else
225 retval = nodetest_eval_node(x, xs, nsc);
226 }
227 else if (xs->xs_type == XP_NODE_FN){
228 switch (xs->xs_int){
229 case XPATHFN_NODE:
230 case XPATHFN_TEXT:
231 retval = 1;
232 break;
233 default:
234 break;
235 }
236 }
237 /* note, retval set by previous statement */
238 return retval;
239 }
240
241 /*!
242 * @param[in] xn
243 * @param[in] nodetest XPATH stack
244 * @param[in] node_type
245 * @param[in] flags
246 * @param[in] nsc XML Namespace context
247 * @param[in] localonly Skip prefix and namespace tests (non-standard)
248 * @param[out] vec0
249 * @param[out] vec0len
250 */
251 int
nodetest_recursive(cxobj * xn,xpath_tree * nodetest,int node_type,uint16_t flags,cvec * nsc,int localonly,cxobj *** vec0,int * vec0len)252 nodetest_recursive(cxobj *xn,
253 xpath_tree *nodetest,
254 int node_type,
255 uint16_t flags,
256 cvec *nsc,
257 int localonly,
258 cxobj ***vec0,
259 int *vec0len)
260 {
261 int retval = -1;
262 cxobj *xsub;
263 cxobj **vec = *vec0;
264 int veclen = *vec0len;
265
266 xsub = NULL;
267 while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
268 if (nodetest_eval(xsub, nodetest, nsc, localonly) == 1){
269 clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags));
270 if (flags==0x0 || xml_flag(xsub, flags))
271 if (cxvec_append(xsub, &vec, &veclen) < 0)
272 goto done;
273 // continue; /* Dont go deeper */
274 }
275 if (nodetest_recursive(xsub, nodetest, node_type, flags, nsc, localonly, &vec, &veclen) < 0)
276 goto done;
277 }
278 retval = 0;
279 *vec0 = vec;
280 *vec0len = veclen;
281 done:
282 return retval;
283 }
284
285 /*! Evaluate xpath step rule of an XML tree
286 *
287 * @param[in] xc0 Incoming context
288 * @param[in] xs XPATH node tree
289 * @param[in] nsc XML Namespace context
290 * @param[in] localonly Skip prefix and namespace tests (non-standard)
291 * @param[out] xrp Resulting context
292 *
293 * - A node test that is a QName is true if and only if the type of the node (see [5 Data Model])
294 * is the principal node type and has an expanded-name equal to the expanded-name specified by the QName.
295 * - A node test * is true for any node of the principal node type.
296 * - node() is true for any node of any type whatsoever.
297 * - text() is true for any text node.
298 */
299 static int
xp_eval_step(xp_ctx * xc0,xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)300 xp_eval_step(xp_ctx *xc0,
301 xpath_tree *xs,
302 cvec *nsc,
303 int localonly,
304 xp_ctx **xrp)
305 {
306 int retval = -1;
307 int i;
308 cxobj *x;
309 cxobj *xv;
310 cxobj *xp;
311 cxobj **vec = NULL;
312 int veclen = 0;
313 xpath_tree *nodetest = xs->xs_c0;
314 xp_ctx *xc = NULL;
315 int ret;
316
317 /* Create new xc */
318 if ((xc = ctx_dup(xc0)) == NULL)
319 goto done;
320 switch (xs->xs_int){
321 case A_ANCESTOR:
322 break;
323 case A_ANCESTOR_OR_SELF:
324 break;
325 case A_ATTRIBUTE: /* principal node type is attribute */
326 break;
327 case A_CHILD:
328 if (xc->xc_descendant){
329 for (i=0; i<xc->xc_size; i++){
330 xv = xc->xc_nodeset[i];
331 if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0)
332 goto done;
333 }
334 xc->xc_descendant = 0;
335 }
336 else{
337 for (i=0; i<xc->xc_size; i++){
338 xv = xc->xc_nodeset[i];
339 x = NULL;
340 if ((ret = xpath_optimize_check(xs, xv, &vec, &veclen)) < 0)
341 goto done;
342 if (ret == 0){/* regular code, no optimization made */
343 while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
344 /* xs->xs_c0 is nodetest */
345 if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
346 if (cxvec_append(x, &vec, &veclen) < 0)
347 goto done;
348 }
349 }
350 }
351 }
352 }
353 ctx_nodeset_replace(xc, vec, veclen);
354 break;
355 case A_DESCENDANT_OR_SELF:
356 for (i=0; i<xc->xc_size; i++){
357 xv = xc->xc_nodeset[i];
358 if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0)
359 goto done;
360 }
361 for (i=0; i<veclen; i++){
362 x = vec[i];
363 if (cxvec_append(x, &xc->xc_nodeset, &xc->xc_size) < 0)
364 goto done;
365 }
366 if (vec){
367 free(vec);
368 vec = NULL;
369 }
370 break;
371 case A_DESCENDANT:
372 for (i=0; i<xc->xc_size; i++){
373 xv = xc->xc_nodeset[i];
374 if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0)
375 goto done;
376 }
377 ctx_nodeset_replace(xc, vec, veclen);
378 break;
379 case A_FOLLOWING:
380 break;
381 case A_FOLLOWING_SIBLING:
382 break;
383 case A_NAMESPACE: /* principal node type is namespace */
384 break;
385 case A_PARENT:
386 veclen = xc->xc_size;
387 vec = xc->xc_nodeset;
388 xc->xc_size = 0;
389 xc->xc_nodeset = NULL;
390 for (i=0; i<veclen; i++){
391 x = vec[i];
392 if ((xp = xml_parent(x)) != NULL)
393 if (cxvec_append(xp, &xc->xc_nodeset, &xc->xc_size) < 0)
394 goto done;
395 }
396 if (vec){
397 free(vec);
398 vec = NULL;
399 }
400 break;
401 case A_PRECEDING:
402 break;
403 case A_PRECEDING_SIBLING:
404 break;
405 case A_SELF:
406 break;
407 default:
408 clicon_err(OE_XML, 0, "No such axisname: %d", xs->xs_int);
409 goto done;
410 break;
411 }
412 if (xs->xs_c1){
413 if (xp_eval(xc, xs->xs_c1, nsc, localonly, xrp) < 0)
414 goto done;
415 }
416 else{
417 *xrp = xc;
418 xc = NULL;
419 }
420 assert(*xrp);
421 retval = 0;
422 done:
423 if (xc)
424 ctx_free(xc);
425 return retval;
426 }
427
428 /*! Evaluate xpath predicates rule
429 *
430 * pred -> pred expr
431 * @param[in] xc Incoming context
432 * @param[in] xs XPATH node tree
433 * @param[in] nsc XML Namespace context
434 * @param[in] localonly Skip prefix and namespace tests (non-standard)
435 * @param[out] xrp Resulting context
436 *
437 * A predicate filters a node-set with respect to an axis to produce a new
438 * node-set. For each node in the node-set to be filtered, the PredicateExpr is
439 * evaluated with that node as the context node, with the number of nodes in
440 * the node-set as the context size, and with the proximity position of the node
441 * in the node-set with respect to the axis as the context position; if
442 * PredicateExpr evaluates to true for that node, the node is included in the
443 * new node-set; otherwise, it is not included.
444 * A PredicateExpr is evaluated by evaluating the Expr and converting the result
445 * to a boolean. If the result is a
446 * - number, the result will be converted to true if the number is equal to the
447 * context position and will be converted to false otherwise;
448 * - if the result is not a number, then the result will be converted as if by a
449 * call to the boolean function.
450 * Thus a location path para[3] is equivalent to para[position()=3].
451 */
452 static int
xp_eval_predicate(xp_ctx * xc,xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)453 xp_eval_predicate(xp_ctx *xc,
454 xpath_tree *xs,
455 cvec *nsc,
456 int localonly,
457 xp_ctx **xrp)
458 {
459 int retval = -1;
460 xp_ctx *xr0 = NULL;
461 xp_ctx *xr1 = NULL;
462 xp_ctx *xrc = NULL;
463 int i;
464 cxobj *x;
465 xp_ctx *xcc;
466
467 if (xs->xs_c0 == NULL){ /* empty */
468 if ((xr0 = ctx_dup(xc)) == NULL)
469 goto done;
470 }
471 else{ /* eval previous predicates */
472 if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
473 goto done;
474 }
475 if (xs->xs_c1){
476 /* Loop over each node in the nodeset */
477 assert (xr0->xc_type == XT_NODESET);
478 if ((xr1 = malloc(sizeof(*xr1))) == NULL){
479 clicon_err(OE_UNIX, errno, "malloc");
480 goto done;
481 }
482 memset(xr1, 0, sizeof(*xr1));
483 xr1->xc_type = XT_NODESET;
484 xr1->xc_node = xc->xc_node;
485 xr1->xc_initial = xc->xc_initial;
486 for (i=0; i<xr0->xc_size; i++){
487 x = xr0->xc_nodeset[i];
488 /* Create new context */
489 if ((xcc = malloc(sizeof(*xcc))) == NULL){
490 clicon_err(OE_XML, errno, "malloc");
491 goto done;
492 }
493 memset(xcc, 0, sizeof(*xcc));
494 xcc->xc_type = XT_NODESET;
495 xcc->xc_initial = xc->xc_initial;
496 xcc->xc_node = x;
497 /* For each node in the node-set to be filtered, the PredicateExpr is
498 * evaluated with that node as the context node */
499 if (cxvec_append(x, &xcc->xc_nodeset, &xcc->xc_size) < 0)
500 goto done;
501 if (xp_eval(xcc, xs->xs_c1, nsc, localonly, &xrc) < 0)
502 goto done;
503 if (xcc)
504 ctx_free(xcc);
505 if (xrc->xc_type == XT_NUMBER){
506 /* If the result is a number, the result will be converted to true
507 if the number is equal to the context position */
508 if ((int)xrc->xc_number == i)
509 if (cxvec_append(x, &xr1->xc_nodeset, &xr1->xc_size) < 0)
510 goto done;
511 }
512 else {
513 /* if PredicateExpr evaluates to true for that node, the node is
514 included in the new node-set */
515 if (ctx2boolean(xrc))
516 if (cxvec_append(x, &xr1->xc_nodeset, &xr1->xc_size) < 0)
517 goto done;
518 }
519 if (xrc)
520 ctx_free(xrc);
521 }
522 }
523 assert(xr0||xr1);
524 if (xr1){
525 *xrp = xr1;
526 xr1 = NULL;
527 }
528 else
529 if (xr0){
530 *xrp = xr0;
531 xr0 = NULL;
532 }
533 retval = 0;
534 done:
535 if (xr0)
536 ctx_free(xr0);
537 if (xr1)
538 ctx_free(xr1);
539 return retval;
540 }
541
542 /*! Given two XPATH contexts, eval logical operations: or,and
543 * The logical operators convert their operands to booleans
544 * @param[in] xc1 Context of operand1
545 * @param[in] xc2 Context of operand2
546 * @param[in] op Relational operator
547 * @param[out] xrp Result context
548 * @retval 0 OK
549 * @retval -1 Error
550 */
551 static int
xp_logop(xp_ctx * xc1,xp_ctx * xc2,enum xp_op op,xp_ctx ** xrp)552 xp_logop(xp_ctx *xc1,
553 xp_ctx *xc2,
554 enum xp_op op,
555 xp_ctx **xrp)
556 {
557 int retval = -1;
558 xp_ctx *xr = NULL;
559 int b1;
560 int b2;
561
562 if ((xr = malloc(sizeof(*xr))) == NULL){
563 clicon_err(OE_UNIX, errno, "malloc");
564 goto done;
565 }
566 memset(xr, 0, sizeof(*xr));
567 xr->xc_initial = xc1->xc_initial;
568 xr->xc_type = XT_BOOL;
569 if ((b1 = ctx2boolean(xc1)) < 0)
570 goto done;
571 if ((b2 = ctx2boolean(xc2)) < 0)
572 goto done;
573 switch (op){
574 case XO_AND:
575 xr->xc_bool = b1 && b2;
576 break;
577 case XO_OR:
578 xr->xc_bool = b1 || b2;
579 break;
580 default:
581 clicon_err(OE_UNIX, errno, "%s:Invalid operator %s in this context",
582 __FUNCTION__, clicon_int2str(xpopmap,op));
583 goto done;
584 }
585 *xrp = xr;
586 retval = 0;
587 done:
588 return retval;
589 }
590
591 /*! Given two XPATH contexts, eval numeric operations: +-*,div,mod
592 * The numeric operators convert their operands to numbers as if by
593 * calling the number function.
594 * @param[in] xc1 Context of operand1
595 * @param[in] xc2 Context of operand2
596 * @param[in] op Relational operator
597 * @param[out] xrp Result context
598 * @retval 0 OK
599 * @retval -1 Error
600 */
601 static int
xp_numop(xp_ctx * xc1,xp_ctx * xc2,enum xp_op op,xp_ctx ** xrp)602 xp_numop(xp_ctx *xc1,
603 xp_ctx *xc2,
604 enum xp_op op,
605 xp_ctx **xrp)
606 {
607 int retval = -1;
608 xp_ctx *xr = NULL;
609 double n1;
610 double n2;
611
612 if ((xr = malloc(sizeof(*xr))) == NULL){
613 clicon_err(OE_UNIX, errno, "malloc");
614 goto done;
615 }
616 memset(xr, 0, sizeof(*xr));
617 xr->xc_initial = xc1->xc_initial;
618 xr->xc_type = XT_NUMBER;
619 if (ctx2number(xc1, &n1) < 0)
620 goto done;
621 if (ctx2number(xc2, &n2) < 0)
622 goto done;
623 if (isnan(n1) || isnan(n2))
624 xr->xc_number = NAN;
625 else
626 switch (op){
627 case XO_DIV:
628 xr->xc_number = n1/n2;
629 break;
630 case XO_MOD:
631 xr->xc_number = ((int)n1)%((int)n2);
632 break;
633 case XO_ADD:
634 xr->xc_number = n1+n2;
635 break;
636 case XO_MULT:
637 xr->xc_number = n1*n2;
638 break;
639 case XO_SUB:
640 xr->xc_number = n1-n2;
641 break;
642 default:
643 clicon_err(OE_UNIX, errno, "Invalid operator %s in this context",
644 clicon_int2str(xpopmap,op));
645 goto done;
646 }
647 *xrp = xr;
648 retval = 0;
649 done:
650 return retval;
651 }
652
653 /*! Given two XPATH contexts, eval relational operations: <>=
654 * A RelationalExpr is evaluated by comparing the objects that result from
655 * evaluating the two operands.
656 * This is covered:
657 * (a) Both are INTs, BOOLs, STRINGs. Result type is boolean
658 * (b) Both are nodesets and one is empty. Result type is boolean.
659 * (c) One is nodeset and other is INT or STRING. Result type is nodeset
660 * (d) All others (eg two nodesets, BOOL+STRING) are not supported.
661 * Op is = EQ
662 * From XPATH 1.0 standard, the evaluation has three variants:
663 * (1) comparisons that involve node-sets are defined in terms of comparisons that
664 * do not involve node-sets; this is defined uniformly for =, !=, <=, <, >= and >.
665 * (2) comparisons that do not involve node-sets are defined for = and !=.
666 * (3) comparisons that do not involve node-sets are defined for <=, <, >= and >.
667 * @param[in] xc1 Context of operand1
668 * @param[in] xc2 Context of operand2
669 * @param[in] op Relational operator
670 * @param[out] xrp Result context
671 * @retval 0 OK
672 * @retval -1 Error
673 */
674 static int
xp_relop(xp_ctx * xc1,xp_ctx * xc2,enum xp_op op,xp_ctx ** xrp)675 xp_relop(xp_ctx *xc1,
676 xp_ctx *xc2,
677 enum xp_op op,
678 xp_ctx **xrp)
679 {
680 int retval = -1;
681 xp_ctx *xr = NULL;
682 xp_ctx *xc;
683 cxobj *x;
684 int i;
685 int j;
686 int b;
687 char *s1;
688 char *s2;
689 int reverse = 0;
690 double n1, n2;
691
692 if ((xr = malloc(sizeof(*xr))) == NULL){
693 clicon_err(OE_UNIX, errno, "malloc");
694 goto done;
695 }
696 memset(xr, 0, sizeof(*xr));
697 xr->xc_initial = xc1->xc_initial;
698 xr->xc_type = XT_BOOL;
699 if (xc1->xc_type == xc2->xc_type){ /* cases (2-3) above */
700 switch (xc1->xc_type){
701 case XT_NODESET:
702 /* If both are node-sets, then it is true iff the string value of one
703 node in the first node-set and one in the second node-set is true */
704 for (i=0; i<xc1->xc_size; i++){
705 if ((s1 = xml_body(xc1->xc_nodeset[i])) == NULL){
706 xr->xc_bool = 0;
707 goto ok;
708 }
709 for (j=0; j<xc2->xc_size; j++){
710 if ((s2 = xml_body(xc2->xc_nodeset[j])) == NULL){
711 xr->xc_bool = 0;
712 goto ok;
713 }
714 switch(op){
715 case XO_EQ:
716 xr->xc_bool = (strcmp(s1, s2)==0);
717 break;
718 case XO_NE:
719 xr->xc_bool = (strcmp(s1, s2)!=0);
720 break;
721 case XO_GE:
722 xr->xc_bool = (strcmp(s1, s2)>=0);
723 break;
724 case XO_LE:
725 xr->xc_bool = (strcmp(s1, s2)<=0);
726 break;
727 case XO_LT:
728 xr->xc_bool = (strcmp(s1, s2)<0);
729 break;
730 case XO_GT:
731 xr->xc_bool = (strcmp(s1, s2)>0);
732 break;
733 default:
734 clicon_err(OE_XML, 0, "Operator %s not supported for nodeset/nodeset comparison", clicon_int2str(xpopmap,op));
735 goto done;
736 break;
737 }
738 if (xr->xc_bool) /* enough to find a single node */
739 break;
740 }
741 if (xr->xc_bool) /* enough to find a single node */
742 break;
743 }
744 break;
745 case XT_BOOL:
746 xr->xc_bool = (xc1->xc_bool == xc2->xc_bool);
747 break;
748 case XT_NUMBER:
749 xr->xc_bool = (xc1->xc_number == xc2->xc_number);
750 break;
751 case XT_STRING:
752 xr->xc_bool = (strcmp(xc1->xc_string, xc2->xc_string)==0);
753 break;
754 }
755 }
756 else if (xc1->xc_type != XT_NODESET &&
757 xc2->xc_type != XT_NODESET){
758 clicon_err(OE_XML, 0, "Mixed types not supported, %d %d", xc1->xc_type, xc2->xc_type);
759 goto done;
760 }
761 else{ /* one is nodeset, ie (1) above */
762 if (xc2->xc_type == XT_NODESET){
763 xc = xc2;
764 xc2 = xc1;
765 xc1 = xc;
766 reverse++; /* reverse */
767 }
768 /* xc1 is nodeset
769 * xc2 is something else */
770 switch (xc2->xc_type){
771 case XT_BOOL:
772 /* comparison on the boolean and the result of converting the
773 node-set to a boolean using the boolean function is true. */
774 b = ctx2boolean(xc1);
775 switch(op){
776 case XO_EQ:
777 xr->xc_bool = (b == xc2->xc_bool);
778 break;
779 case XO_NE:
780 xr->xc_bool = (b != xc2->xc_bool);
781 break;
782 default:
783 clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and bool", clicon_int2str(xpopmap,op));
784 goto done;
785 break;
786 } /* switch op */
787 break;
788 case XT_STRING:
789 /* If one object to be compared is a node-set and the
790 other is a string, then the comparison will be true if and only
791 if there is a node in the node-set such that the result of
792 performing the comparison on the string-value of the node and
793 the other string is true.*/
794 s2 = xc2->xc_string;
795 for (i=0; i<xc1->xc_size; i++){
796 x = xc1->xc_nodeset[i]; /* node in nodeset */
797 s1 = xml_body(x);
798 switch(op){
799 case XO_EQ:
800 if (s1 == NULL || s2 == NULL)
801 xr->xc_bool = (s1==NULL && s2 == NULL);
802 else
803 xr->xc_bool = (strcmp(s1, s2)==0);
804 break;
805 case XO_NE:
806 if (s1 == NULL || s2 == NULL)
807 xr->xc_bool = !(s1==NULL && s2 == NULL);
808 else
809 xr->xc_bool = (strcmp(s1, s2));
810 break;
811 default:
812 clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and string", clicon_int2str(xpopmap,op));
813 goto done;
814 break;
815 }
816 if (xr->xc_bool) /* enough to find a single node */
817 break;
818 }
819 break;
820 case XT_NUMBER:
821 for (i=0; i<xc1->xc_size; i++){
822 x = xc1->xc_nodeset[i]; /* node in nodeset */
823 if (sscanf(xml_body(x), "%lf", &n1) != 1)
824 n1 = NAN;
825 n2 = xc2->xc_number;
826 switch(op){
827 case XO_EQ:
828 xr->xc_bool = (n1 == n2);
829 break;
830 case XO_NE:
831 xr->xc_bool = (n1 != n2);
832 break;
833 case XO_GE:
834 xr->xc_bool = reverse?(n2 >= n1):(n1 >= n2);
835 break;
836 case XO_LE:
837 xr->xc_bool = reverse?(n2 <= n1):(n1 <= n2);
838 break;
839 case XO_LT:
840 xr->xc_bool = reverse?(n2 < n1):(n1 < n2);
841 break;
842 case XO_GT:
843 xr->xc_bool = reverse?(n2 > n1):(n1 > n2);
844 break;
845 default:
846 clicon_err(OE_XML, 0, "Operator %s not supported for nodeset and number", clicon_int2str(xpopmap,op));
847 goto done;
848 break;
849 }
850 if (xr->xc_bool) /* enough to find a single node */
851 break;
852 }
853 break;
854 default:
855 clicon_err(OE_XML, 0, "Type %d not supported", xc2->xc_type);
856 } /* switch type */
857 }
858 ok:
859 /* Just ensure bool is 0 or 1 */
860 if (xr->xc_type == XT_BOOL && xr->xc_bool != 0)
861 xr->xc_bool = 1;
862 *xrp = xr;
863 retval = 0;
864 done:
865 return retval;
866 }
867
868 /*! Given two XPATH contexts, eval union operation
869 * Both operands must be nodesets, otherwise empty nodeset is returned
870 * @param[in] xc1 Context of operand1
871 * @param[in] xc2 Context of operand2
872 * @param[in] op Relational operator
873 * @param[out] xrp Result context
874 * @retval 0 OK
875 * @retval -1 Error
876 */
877 static int
xp_union(xp_ctx * xc1,xp_ctx * xc2,enum xp_op op,xp_ctx ** xrp)878 xp_union(xp_ctx *xc1,
879 xp_ctx *xc2,
880 enum xp_op op,
881 xp_ctx **xrp)
882 {
883 int retval = -1;
884 xp_ctx *xr = NULL;
885 int i;
886
887 if (op != XO_UNION){
888 clicon_err(OE_UNIX, errno, "%s:Invalid operator %s in this context",
889 __FUNCTION__, clicon_int2str(xpopmap,op));
890 goto done;
891 }
892 if ((xr = malloc(sizeof(*xr))) == NULL){
893 clicon_err(OE_UNIX, errno, "malloc");
894 goto done;
895 }
896 memset(xr, 0, sizeof(*xr));
897 xr->xc_initial = xc1->xc_initial;
898 xr->xc_type = XT_NODESET;
899
900 for (i=0; i<xc1->xc_size; i++)
901 if (cxvec_append(xc1->xc_nodeset[i], &xr->xc_nodeset, &xr->xc_size) < 0)
902 goto done;
903 for (i=0; i<xc2->xc_size; i++){
904 if (cxvec_append(xc2->xc_nodeset[i], &xr->xc_nodeset, &xr->xc_size) < 0)
905 goto done;
906 }
907 *xrp = xr;
908 retval = 0;
909 done:
910 return retval;
911 }
912
913 /*! Evaluate an XPATH on an XML tree
914
915 * The initial sequence of steps selects a set of nodes relative to a context node.
916 * Each node in that set is used as a context node for the following step.
917 * @param[in] xc Incoming context
918 * @param[in] xs XPATH node tree
919 * @param[in] nsc XML Namespace context
920 * @param[in] localonly Skip prefix and namespace tests (non-standard)
921 * @param[out] xrp Resulting context
922 * @retval 0 OK
923 * @retval -1 Error
924 */
925 int
xp_eval(xp_ctx * xc,xpath_tree * xs,cvec * nsc,int localonly,xp_ctx ** xrp)926 xp_eval(xp_ctx *xc,
927 xpath_tree *xs,
928 cvec *nsc,
929 int localonly,
930 xp_ctx **xrp)
931 {
932 int retval = -1;
933 cxobj *x;
934 xp_ctx *xr0 = NULL;
935 xp_ctx *xr1 = NULL;
936 xp_ctx *xr2 = NULL;
937 int use_xr0 = 0; /* In 2nd child use transitively result of 1st child */
938
939 if (clicon_debug_get() > 1)
940 ctx_print(stderr, xc, xpath_tree_int2str(xs->xs_type));
941 /* Pre-actions before check first child c0
942 */
943 switch (xs->xs_type){
944 case XP_RELLOCPATH:
945 if (xs->xs_int == A_DESCENDANT_OR_SELF)
946 xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
947 break;
948 case XP_ABSPATH:
949 /* Set context node to top node, and nodeset to that node only */
950 x = xc->xc_node;
951 while (xml_parent(x) != NULL)
952 x = xml_parent(x);
953 xc->xc_node = x;
954 xc->xc_nodeset[0] = x;
955 xc->xc_size=1;
956 /* // is short for /descendant-or-self::node()/ */
957 if (xs->xs_int == A_DESCENDANT_OR_SELF)
958 xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
959
960 break;
961 case XP_STEP: /* XP_NODE is first argument -not called explicitly */
962 if (xp_eval_step(xc, xs, nsc, localonly, xrp) < 0)
963 goto done;
964 goto ok;
965 break;
966 case XP_PRED:
967 if (xp_eval_predicate(xc, xs, nsc, localonly, xrp) < 0)
968 goto done;
969 goto ok;
970 break;
971 case XP_PRIME_FN:
972 if (xs->xs_s0){
973 switch (xs->xs_int){
974 case XPATHFN_CURRENT:
975 if (xp_function_current(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
976 goto done;
977 goto ok;
978 break;
979 case XPATHFN_DEREF:
980 if (xp_function_deref(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
981 goto done;
982 goto ok;
983 break;
984 case XPATHFN_DERIVED_FROM:
985 if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 0, xrp) < 0)
986 goto done;
987 goto ok;
988 break;
989 case XPATHFN_DERIVED_FROM_OR_SELF:
990 if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 1, xrp) < 0)
991 goto done;
992 goto ok;
993 break;
994 case XPATHFN_COUNT:
995 if (xp_function_count(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
996 goto done;
997 goto ok;
998 break;
999 case XPATHFN_NAME:
1000 if (xp_function_name(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
1001 goto done;
1002 goto ok;
1003 break;
1004 case XPATHFN_CONTAINS:
1005 if (xp_function_contains(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
1006 goto done;
1007 goto ok;
1008 break;
1009 case XPATHFN_NOT:
1010 if (xp_function_not(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
1011 goto done;
1012 goto ok;
1013 break;
1014 default:
1015 clicon_err(OE_XML, EFAULT, "XPATH function not implemented: %s", xs->xs_s0);
1016 goto done;
1017 break;
1018 }
1019 }
1020 break;
1021 default:
1022 break;
1023 }
1024 /* Eval first child c0
1025 */
1026 if (xs->xs_c0){
1027 if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
1028 goto done;
1029 }
1030 /* Actions between first and second child
1031 */
1032 switch (xs->xs_type){
1033 case XP_EXP:
1034 break;
1035 case XP_AND:
1036 break;
1037 case XP_RELEX: /* relexpr --> addexpr | relexpr relop addexpr */
1038 break;
1039 case XP_ADD: /* combine mult and add ops */
1040 break;
1041 case XP_UNION:
1042 break;
1043 case XP_PATHEXPR:
1044 if (xs->xs_c1)
1045 use_xr0++;
1046 break;
1047 case XP_FILTEREXPR:
1048 break;
1049 case XP_LOCPATH:
1050 break;
1051 case XP_ABSPATH:
1052 use_xr0++;
1053 /* Special case, no c0 or c1, single "/" */
1054 if (xs->xs_c0 == NULL){
1055 if ((xr0 = malloc(sizeof(*xr0))) == NULL){
1056 clicon_err(OE_UNIX, errno, "malloc");
1057 goto done;
1058 }
1059 memset(xr0, 0, sizeof(*xr0));
1060 xr0->xc_initial = xc->xc_initial;
1061 xr0->xc_type = XT_NODESET;
1062 x = NULL;
1063 while ((x = xml_child_each(xc->xc_node, x, CX_ELMNT)) != NULL) {
1064 if (cxvec_append(x, &xr0->xc_nodeset, &xr0->xc_size) < 0)
1065 goto done;
1066 }
1067 }
1068 break;
1069 case XP_RELLOCPATH:
1070 use_xr0++;
1071 if (xs->xs_int == A_DESCENDANT_OR_SELF)
1072 xc->xc_descendant = 1; /* XXX need to set to 0 in sub */
1073 break;
1074 case XP_NODE:
1075 break;
1076 case XP_NODE_FN:
1077 break;
1078 case XP_PRI0:
1079 break;
1080 case XP_PRIME_NR: /* primaryexpr -> [<number>] */
1081 if ((xr0 = malloc(sizeof(*xr0))) == NULL){
1082 clicon_err(OE_UNIX, errno, "malloc");
1083 goto done;
1084 }
1085 memset(xr0, 0, sizeof(*xr0));
1086 xr0->xc_initial = xc->xc_initial;
1087 xr0->xc_type = XT_NUMBER;
1088 xr0->xc_number = xs->xs_double;
1089 break;
1090 case XP_PRIME_STR:
1091 if ((xr0 = malloc(sizeof(*xr0))) == NULL){
1092 clicon_err(OE_UNIX, errno, "malloc");
1093 goto done;
1094 }
1095 memset(xr0, 0, sizeof(*xr0));
1096 xr0->xc_initial = xc->xc_initial;
1097 xr0->xc_type = XT_STRING;
1098 xr0->xc_string = xs->xs_s0?strdup(xs->xs_s0):NULL;
1099 break;
1100 default:
1101 break;
1102 }
1103 /* Eval second child c1
1104 * Note, some operators like locationpath, need transitive context (use_xr0)
1105 */
1106 if (xs->xs_c1)
1107 if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, nsc, localonly, &xr1) < 0)
1108 goto done;
1109 /* Actions after second child
1110 */
1111 if (xs->xs_c1)
1112 switch (xs->xs_type){
1113 case XP_AND: /* combine and and or ops */
1114 if (xp_logop(xr0, xr1, xs->xs_int, &xr2) < 0)
1115 goto done;
1116 break;
1117 case XP_RELEX: /* relexpr --> addexpr | relexpr relop addexpr */
1118 if (xp_relop(xr0, xr1, xs->xs_int, &xr2) < 0)
1119 goto done;
1120 break;
1121 case XP_ADD: /* combine mult and add ops */
1122 if (xp_numop(xr0, xr1, xs->xs_int, &xr2) < 0)
1123 goto done;
1124 break;
1125 case XP_UNION: /* combine and and or ops */
1126 if (xp_union(xr0, xr1, xs->xs_int, &xr2) < 0)
1127 goto done;
1128 default:
1129 break;
1130 }
1131 xc->xc_descendant = 0;
1132 #if 0
1133 assert(xr0||xr1||xr2); /* for debugging */
1134 #else
1135 if (xr0 == NULL && xr1 == NULL && xr2 == NULL){
1136 clicon_err(OE_XML, EFAULT, "Internal error: no result produced");
1137 goto done;
1138 }
1139 #endif
1140 if (xr2){
1141 *xrp = xr2;
1142 xr2 = NULL;
1143 }
1144 else if (xr1){
1145 *xrp = xr1;
1146 xr1 = NULL;
1147 }
1148 else
1149 if (xr0){
1150 *xrp = xr0;
1151 xr0 = NULL;
1152 }
1153 ok:
1154 if (clicon_debug_get() > 1)
1155 ctx_print(stderr, *xrp, xpath_tree_int2str(xs->xs_type));
1156 retval = 0;
1157 done:
1158 if (xr2)
1159 ctx_free(xr2);
1160 if (xr1)
1161 ctx_free(xr1);
1162 if (xr0)
1163 ctx_free(xr0);
1164 return retval;
1165 } /* xp_eval */
1166
1167
1168