* 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

@ -774,7 +774,7 @@ json_parse_file(int fd,
/*
* Turn this on to get a json parse and pretty print test program
* Usage: xpath
* Usage: json
* read json from input
* Example compile:
gcc -g -o json -I. -I../clixon ./clixon_json.c -lclixon -lcligen

View file

@ -68,6 +68,8 @@
#include "clixon_xml.h"
#include "clixon_plugin.h"
#include "clixon_xsl.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xml_map.h"
/* Mapping between Clicon startup modes string <--> constants,

View file

@ -65,6 +65,9 @@
#include "clixon_xml.h"
#include "clixon_plugin.h"
#include "clixon_xsl.h"
#include "clixon_string.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_proto.h"
#include "clixon_err.h"
#include "clixon_proto_client.h"

View file

@ -83,6 +83,8 @@
#include "clixon_options.h"
#include "clixon_xml.h"
#include "clixon_plugin.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xsl.h"
#include "clixon_log.h"
#include "clixon_err.h"
@ -439,7 +441,7 @@ xml_yang_validate_all(cxobj *xt,
case Y_LEAF_LIST:
/* Special case if leaf is leafref, then first check against
current xml tree
*/
*/
if ((yc = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){
if (strcmp(yc->ys_argument, "leafref") == 0){
if (validate_leafref(xt, yc) < 0)
@ -995,7 +997,7 @@ api_path_fmt2api_path(char *api_path_fmt,
* @example
* api_path_fmt: /interface/%s/address/%s
* cvv: name=eth0
* xpath: /interface/[name=eth0]/address
* xpath: /interface/[name='eth0']/address
* @example
* api_path_fmt: /ip/me/%s (if key)
* cvv: -
@ -1038,7 +1040,13 @@ api_path_fmt2xpath(char *api_path_fmt,
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
cprintf(cb, "[%s=%s]", cv_name_get(cv), str);
cprintf(cb,
#ifdef XPATH_USE_NEW
"[%s='%s']",
#else
"[%s=%s]",
#endif
cv_name_get(cv), str);
free(str);
}
}
@ -1455,7 +1463,13 @@ api_path2xpath_cvv(yang_spec *yspec,
cprintf(xpath, "/%s", name);
v = val;
while ((cvi = cvec_each(cvk, cvi)) != NULL){
cprintf(xpath, "[%s=%s]", cv_string_get(cvi), v);
cprintf(xpath,
#ifdef XPATH_USE_NEW
"[%s='%s']",
#else
"[%s=%s]",
#endif
cv_string_get(cvi), v);
v += strlen(v)+1;
}
if (val)

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

View file

@ -239,8 +239,8 @@ rellocpath : step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL
;
step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,0.0, NULL, NULL, $2, $3);clicon_debug(1,"step->axisspec(%d) nodetest", $1); }
| '.' { $$=xp_new(XP_STEP,A_SELF, 0.0,NULL, NULL, NULL, NULL); clicon_debug(1,"step-> ."); }
| DOUBLEDOT { $$=xp_new(XP_STEP, A_PARENT, 0.0,NULL, NULL, NULL, NULL); clicon_debug(1,"step-> .."); }
| '.' predicates { $$=xp_new(XP_STEP,A_SELF, 0.0,NULL, NULL, NULL, $2); clicon_debug(1,"step-> ."); }
| DOUBLEDOT predicates { $$=xp_new(XP_STEP, A_PARENT, 0.0,NULL, NULL, NULL, $2); clicon_debug(1,"step-> .."); }
;
axisspec : AXISNAME DOUBLECOLON { clicon_debug(1,"axisspec-> AXISNAME(%d) ::", $1); $$=$1;}

View file

@ -42,6 +42,10 @@ The code is implemented according to XPATH 1.0:
The primary syntactic construct in XPath is the expression. An expression matches
the production Expr (see https://www.w3.org/TR/xpath-10/#NT-Expr)
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@ -896,7 +900,7 @@ xpath_first0(cxobj *xcur,
* @see also xpath_vec.
*/
cxobj *
xpath_first(cxobj *xcur,
xpath_first_xsl(cxobj *xcur,
char *format,
...)
{
@ -1012,7 +1016,7 @@ xpath_each(cxobj *xcur,
* @see also xpath_first, xpath_each.
*/
int
xpath_vec(cxobj *xcur,
xpath_vec_xsl(cxobj *xcur,
char *format,
cxobj ***vec,
size_t *veclen,
@ -1048,7 +1052,6 @@ xpath_vec(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
@ -1073,12 +1076,12 @@ xpath_vec(cxobj *xcur,
* @see also xpath_vec This is a specialized version.
*/
int
xpath_vec_flag(cxobj *xcur,
char *format,
uint16_t flags,
cxobj ***vec,
size_t *veclen,
...)
xpath_vec_flag_xsl(cxobj *xcur,
char *format,
uint16_t flags,
cxobj ***vec,
size_t *veclen,
...)
{
int retval = -1;
va_list ap;

View file

@ -545,7 +545,6 @@ cv_validate1(cg_var *cv,
if ((vec = clicon_strsep(str, " \t", &nvec)) == NULL)
goto done;
for (i=0; i<nvec; i++){
clicon_log(LOG_NOTICE, "%s: vec[i]: %s", __FUNCTION__, vec[i]);
if ((v = vec[i]) == NULL || !strlen(v))
continue;
found = 0;