* 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:
parent
5d7c4a8d18
commit
ba7f84afee
29 changed files with 395 additions and 79 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue