* Much better support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex

* NOTE: Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]`
    * This includes all calls to `xpath_vec, xpath_first`, etc.
    * All calls to cli_copy_config in CLI spec files must replace 2nd argument from `x[%s=%s]` to `x[%s='%s']`
  * The old API is stillenabled. To define the new, define XPATH_USE_NEW in include/clixon_custom.h and recompile
This commit is contained in:
Olof hagsand 2018-07-17 16:59:32 +02:00
parent 5d7c4a8d18
commit ba7f84afee
29 changed files with 395 additions and 79 deletions

View file

@ -33,6 +33,10 @@
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@ -246,16 +250,15 @@ xp_eval_step(xp_ctx *xc0,
xpath_tree *xs,
xp_ctx **xrp)
{
int retval = -1;
int i;
cxobj *x;
cxobj *xv;
cxobj *xp;
cxobj **vec = NULL;
size_t veclen = 0;
xpath_tree *nodetest = xs->xs_c0;
xp_ctx *xr0 = NULL;
xp_ctx *xc = NULL;
int retval = -1;
int i;
cxobj *x;
cxobj *xv;
cxobj *xp;
cxobj **vec = NULL;
size_t veclen = 0;
xpath_tree *nodetest = xs->xs_c0;
xp_ctx *xc = NULL;
/* Create new xc */
if ((xc = ctx_dup(xc0)) == NULL)
@ -312,16 +315,14 @@ xp_eval_step(xp_ctx *xc0,
case A_NAMESPACE: /* principal node type is namespace */
break;
case A_PARENT:
if ((xr0 = malloc(sizeof(*xr0))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(xr0, 0, sizeof(*xr0));
xr0->xc_initial = xc->xc_initial;
for (i=0; i<xc->xc_size; i++){
x = xc->xc_nodeset[i];
veclen = xc->xc_size;
vec = xc->xc_nodeset;
xc->xc_size = 0;
xc->xc_nodeset = NULL;
for (i=0; i<veclen; i++){
x = vec[i];
if ((xp = xml_parent(x)) != NULL)
if (cxvec_append(xp, &xr0->xc_nodeset, &xr0->xc_size) < 0)
if (cxvec_append(xp, &xc->xc_nodeset, &xc->xc_size) < 0)
goto done;
}
break;
@ -330,19 +331,20 @@ xp_eval_step(xp_ctx *xc0,
case A_PRECEEDING_SIBLING:
break;
case A_SELF:
xr0 = ctx_dup(xc);
break;
default:
clicon_err(OE_XML, 0, "No such axisname: %d", xs->xs_int);
goto done;
break;
}
if (xr0)
*xrp = xr0;
if (xs->xs_c1){
if (xp_eval(xc, xs->xs_c1, xrp) < 0)
goto done;
}
else{
*xrp = xc;
xc = NULL;
}
assert(*xrp);
retval = 0;
done:
@ -623,8 +625,8 @@ xp_relop(xp_ctx *xc1,
if (xc1->xc_type == xc2->xc_type){ /* cases (2-3) above */
switch (xc1->xc_type){
case XT_NODESET:
/* If both are node-sets, then it is true iff the string value of a
node in the first node-set and in the second node-set is true */
/* If both are node-sets, then it is true iff the string value of one
node in the first node-set and one in the second node-set is true */
for (i=0; i<xc1->xc_size; i++){
if ((s1 = xml_body(xc1->xc_nodeset[i])) == NULL){
xr->xc_bool = 0;
@ -658,8 +660,12 @@ xp_relop(xp_ctx *xc1,
clicon_err(OE_XML, 0, "Operator %s not supported for nodeset/nodeset comparison", clicon_int2str(xpopmap,op));
goto done;
break;
}
}
if (xr->xc_bool) /* enough to find a single node */
break;
}
if (xr->xc_bool) /* enough to find a single node */
break;
}
break;
case XT_BOOL:
@ -711,10 +717,10 @@ xp_relop(xp_ctx *xc1,
if there is a node in the node-set such that the result of
performing the comparison on the string-value of the node and
the other string is true.*/
s2 = xc2->xc_string;
for (i=0; i<xc1->xc_size; i++){
x = xc1->xc_nodeset[i]; /* node in nodeset */
s1 = xml_body(x);
s2 = xc2->xc_string;
switch(op){
case XO_EQ:
xr->xc_bool = (strcmp(s1, s2)==0);
@ -727,6 +733,8 @@ xp_relop(xp_ctx *xc1,
goto done;
break;
}
if (xr->xc_bool) /* enough to find a single node */
break;
}
break;
case XT_NUMBER:
@ -759,6 +767,8 @@ xp_relop(xp_ctx *xc1,
goto done;
break;
}
if (xr->xc_bool) /* enough to find a single node */
break;
}
break;
default:
@ -862,7 +872,7 @@ xp_eval(xp_ctx *xc,
x = xc->xc_node;
while (xml_parent(x) != NULL)
x = xml_parent(x);
// xc->xc_node = x;
xc->xc_node = x;
xc->xc_nodeset[0] = x;
xc->xc_size=1;
/* // is short for /descendant-or-self::node()/ */
@ -921,7 +931,6 @@ xp_eval(xp_ctx *xc,
while ((x = xml_child_each(xc->xc_node, x, CX_ELMNT)) != NULL) {
if (cxvec_append(x, &xr0->xc_nodeset, &xr0->xc_size) < 0)
goto done;
break;
}
}
break;
@ -1027,7 +1036,6 @@ xp_eval(xp_ctx *xc,
}
/*! Given XML tree and xpath, returns xpath context
* @param[in] xcur XML-tree where to search
* @param[in] xpath String with XPATH 1.0 syntax
* @param[out] xrp Return XPATH context
@ -1081,6 +1089,45 @@ xpath_vec_ctx(cxobj *xcur,
return retval;
}
cxobj *
xpath_first_nodeset(cxobj *xcur,
char *format,
...)
{
cxobj *cx = NULL;
va_list ap;
size_t len;
char *xpath = NULL;
xp_ctx *xr = NULL;
va_start(ap, format);
len = vsnprintf(NULL, 0, format, ap);
va_end(ap);
/* allocate a message string exactly fitting the message length */
if ((xpath = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from reason and args */
va_start(ap, format);
if (vsnprintf(xpath, len+1, format, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
goto done;
if (xr && xr->xc_type == XT_NODESET && xr->xc_size)
cx = xr->xc_nodeset[0];
done:
if (xr)
ctx_free(xr);
if (xpath)
free(xpath);
return cx;
}
/*! Given XML tree and xpath, returns nodeset as xml node vector
* If result is not nodeset, return empty nodeset
* @param[in] xcur xml-tree where to search
@ -1135,6 +1182,81 @@ xpath_vec_nodeset(cxobj *xcur,
return retval;
}
/* A restricted xpath that returns a vector of matches (only nodes marked with flags)
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[in] flags Set of flags that return nodes must match (0 if all)
* @param[out] vec vector of xml-trees. Vector must be free():d after use
* @param[out] veclen returns length of vector in return value
* @retval 0 OK
* @retval -1 error.
* @code
* cxobj **vec;
* size_t veclen;
* if (xpath_vec_flag(xcur, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
* goto err;
* for (i=0; i<veclen; i++){
* xn = vec[i];
* ...
* }
* free(vec);
* @endcode
* @Note that although the returned vector must be freed after use, the returned xml
* trees need not be.
* @see also xpath_vec This is a specialized version.
*/
int
xpath_vec_nodeset_flag(cxobj *xcur,
char *format,
uint16_t flags,
cxobj ***vec,
size_t *veclen,
...)
{
int retval = -1;
va_list ap;
size_t len;
char *xpath = NULL;
xp_ctx *xr = NULL;
int i;
cxobj *x;
va_start(ap, veclen);
len = vsnprintf(NULL, 0, format, ap);
va_end(ap);
/* allocate a message string exactly fitting the message length */
if ((xpath = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from reason and args */
va_start(ap, veclen);
if (vsnprintf(xpath, len+1, format, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
goto done;
if (xr && xr->xc_type == XT_NODESET){
for (i=0; i<xr->xc_size; i++){
x = xr->xc_nodeset[i];
if (flags==0x0 || xml_flag(x, flags))
if (cxvec_append(x, vec, veclen) < 0)
goto done;
}
}
retval = 0;
done:
if (xr)
ctx_free(xr);
if (xpath)
free(xpath);
return retval;
}
/*! Given XML tree and xpath, returns boolean
* @param[in] xcur xml-tree where to search
* @param[in] xpath stdarg string with XPATH 1.0 syntax