* 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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue