More XPath function support
* `count`, `name`, `contains`, `not`, as defined in [xpath 1.0](https://www.w3.org/TR/xpath-10) * `deref`, `derived-from` and `derived-from-or-self` from RFC7950 Section 10. * in particular in augment/when statements * Improved error handling * Verification of XPath functions is done at startup when yang modules are loaded, not when XPaths are evaluated. * Separation of "not found" and "not implemented" XPath functions * Both give a fatal error (backend does not start).
This commit is contained in:
parent
2994d2f9a9
commit
21ac47915b
14 changed files with 715 additions and 132 deletions
24
CHANGELOG.md
24
CHANGELOG.md
|
|
@ -1,6 +1,6 @@
|
|||
# Clixon Changelog
|
||||
|
||||
* [4.8.0](#480) Expected: October 2020
|
||||
* [4.8.0](#480) Expected: 15 October 2020
|
||||
* [4.7.0](#470) 14 September 2020
|
||||
* [4.6.0](#460) 14 August 2020
|
||||
* [4.5.0](#450) 12 May 2020
|
||||
|
|
@ -25,14 +25,28 @@
|
|||
* [3.3.1](#331) June 7 2017
|
||||
|
||||
## 4.8.0
|
||||
Expected: October 2020
|
||||
Expected: 15 October 2020
|
||||
|
||||
### New features
|
||||
|
||||
* Added support for the following XPATH functions:
|
||||
* `contains`, see https://www.w3.org/TR/xpath-10
|
||||
* `derived-from` and `derived-from-or-self`
|
||||
* in particular in augment/when statements as shown in eg RFC 7950.
|
||||
* `count`, `name`, `contains`, `not`, as defined in [xpath 1.0](https://www.w3.org/TR/xpath-10)
|
||||
* `deref`, `derived-from` and `derived-from-or-self` from RFC7950 Section 10.
|
||||
* in particular in augment/when statements
|
||||
* Improved error handling
|
||||
* Verification of XPath functions is done at startup when yang modules are loaded, not when XPaths are evaluated.
|
||||
* Separation of "not found" and "not implemented" XPath functions
|
||||
* Both give a fatal error (backend does not start).
|
||||
|
||||
### API changes on existing protocol/config features
|
||||
|
||||
Users may have to change how they access the system
|
||||
|
||||
* Not implemented XPath functions will cause a backend exit on startup, instead of being ignored.
|
||||
|
||||
### Minor changes
|
||||
|
||||
* Added filterexpr to xpath
|
||||
|
||||
## 4.7.0
|
||||
14 September 2020
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ mechanism.
|
|||
See [documentation](https://clixon-docs.readthedocs.io), [project page](https://www.clicon.org) and [examples](https://github.com/clicon/clixon-examples), [Travis-CI](https://travis-ci.org/clicon/clixon)
|
||||
|
||||
Clixon is open-source and dual licensed. Either Apache License, Version 2.0 or GNU
|
||||
General Public License Version 2; you choose.
|
||||
General Public License Version 2; you choose, see [LICENSE.md](LICENSE.md).
|
||||
|
||||
See [LICENSE.md](LICENSE.md) for the license.
|
||||
See [CHANGELOG.md](CHANGELOG.md) release history.
|
||||
|
||||
Clixon interaction is best done posting issues, pull requests, or joining the
|
||||
[slack channel](https://clixondev.slack.com).
|
||||
[Slack invite](https://join.slack.com/t/clixondev/shared_invite/zt-grej02z5-fNz0b7Su8RcOn5hWKt8yOw)
|
||||
[Slack invite](https://join.slack.com/t/clixondev/shared_invite/zt-hw9lofnk-C1iDFJ~E_CTiwtyGZi8fdQ)
|
||||
|
||||
Clixon is sponsored by [Rubicon Communications LLC(Netgate)](https://www.netgate.com/)
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ enum xp_type{
|
|||
XP_ADD,
|
||||
XP_UNION,
|
||||
XP_PATHEXPR,
|
||||
XP_FILTEREXPR,
|
||||
XP_LOCPATH,
|
||||
XP_ABSPATH,
|
||||
XP_RELLOCPATH,
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ static const map_str2int xpath_tree_map[] = {
|
|||
{"addexpr", XP_ADD},
|
||||
{"unionexpr", XP_UNION},
|
||||
{"pathexpr", XP_PATHEXPR},
|
||||
{"filterexpr", XP_FILTEREXPR},
|
||||
{"locationpath", XP_LOCPATH},
|
||||
{"abslocpath", XP_ABSPATH},
|
||||
{"rellocpath", XP_RELLOCPATH},
|
||||
|
|
@ -514,12 +515,16 @@ xpath_parse(const char *xpath,
|
|||
}
|
||||
xpath_parse_exit(&xpy);
|
||||
xpath_scan_exit(&xpy);
|
||||
if (xptree)
|
||||
if (xptree){
|
||||
*xptree = xpy.xpy_top;
|
||||
xpy.xpy_top = NULL;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xpy.xpy_top)
|
||||
xpath_tree_free(xpy.xpy_top);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -217,7 +217,6 @@ nodetest_eval(cxobj *x,
|
|||
int localonly)
|
||||
{
|
||||
int retval = 0; /* NB: no match is default (not error) */
|
||||
char *fn;
|
||||
|
||||
if (xs->xs_type == XP_NODE){
|
||||
if (localonly)
|
||||
|
|
@ -226,11 +225,14 @@ nodetest_eval(cxobj *x,
|
|||
retval = nodetest_eval_node(x, xs, nsc);
|
||||
}
|
||||
else if (xs->xs_type == XP_NODE_FN){
|
||||
fn = xs->xs_s0;
|
||||
if (strcmp(fn, "node")==0)
|
||||
retval = 1;
|
||||
else if (strcmp(fn, "text")==0)
|
||||
switch (xs->xs_int){
|
||||
case XPATHFN_NODE:
|
||||
case XPATHFN_TEXT:
|
||||
retval = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* note, retval set by previous statement */
|
||||
return retval;
|
||||
|
|
@ -332,13 +334,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
xc->xc_descendant = 0;
|
||||
}
|
||||
else{
|
||||
if (nodetest->xs_type==XP_NODE_FN &&
|
||||
nodetest->xs_s0 &&
|
||||
strcmp(nodetest->xs_s0, "current")==0){
|
||||
if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
else for (i=0; i<xc->xc_size; i++){
|
||||
for (i=0; i<xc->xc_size; i++){
|
||||
xv = xc->xc_nodeset[i];
|
||||
x = NULL;
|
||||
if ((ret = xpath_optimize_check(xs, xv, &vec, &veclen)) < 0)
|
||||
|
|
@ -974,20 +970,51 @@ xp_eval(xp_ctx *xc,
|
|||
break;
|
||||
case XP_PRIME_FN:
|
||||
if (xs->xs_s0){
|
||||
if (strcmp(xs->xs_s0, "contains") == 0){
|
||||
if (xp_function_contains(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
switch (xs->xs_int){
|
||||
case XPATHFN_CURRENT:
|
||||
if (xp_function_current(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
else if (strcmp(xs->xs_s0, "derived-from") == 0){
|
||||
break;
|
||||
case XPATHFN_DEREF:
|
||||
if (xp_function_deref(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
case XPATHFN_DERIVED_FROM:
|
||||
if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 0, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
else if (strcmp(xs->xs_s0, "derived-from-or-self") == 0){
|
||||
break;
|
||||
case XPATHFN_DERIVED_FROM_OR_SELF:
|
||||
if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 1, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
case XPATHFN_COUNT:
|
||||
if (xp_function_count(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
case XPATHFN_NAME:
|
||||
if (xp_function_name(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
case XPATHFN_CONTAINS:
|
||||
if (xp_function_contains(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
case XPATHFN_NOT:
|
||||
if (xp_function_not(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
default:
|
||||
clicon_err(OE_XML, EFAULT, "XPATH function not implemented: %s", xs->xs_s0);
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -1014,6 +1041,10 @@ xp_eval(xp_ctx *xc,
|
|||
case XP_UNION:
|
||||
break;
|
||||
case XP_PATHEXPR:
|
||||
if (xs->xs_c1)
|
||||
use_xr0++;
|
||||
break;
|
||||
case XP_FILTEREXPR:
|
||||
break;
|
||||
case XP_LOCPATH:
|
||||
break;
|
||||
|
|
@ -1098,7 +1129,14 @@ xp_eval(xp_ctx *xc,
|
|||
break;
|
||||
}
|
||||
xc->xc_descendant = 0;
|
||||
assert(xr0||xr1||xr2);
|
||||
#if 0
|
||||
assert(xr0||xr1||xr2); /* for debugging */
|
||||
#else
|
||||
if (xr0 == NULL && xr1 == NULL && xr2 == NULL){
|
||||
clicon_err(OE_XML, EFAULT, "Internal error: no result produced");
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
if (xr2){
|
||||
*xrp = xr2;
|
||||
xr2 = NULL;
|
||||
|
|
|
|||
|
|
@ -74,58 +74,136 @@
|
|||
#include "clixon_xpath_eval.h"
|
||||
#include "clixon_xpath_function.h"
|
||||
|
||||
/*! Eval xpath function contains
|
||||
* @param[in] xc Incoming context
|
||||
* @param[in] xs XPATH node tree
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[in] localonly Skip prefix and namespace tests (non-standard)
|
||||
* @param[out] xrp Resulting context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see https://www.w3.org/TR/xpath-10/#NT-FunctionName 4.2 String Functions
|
||||
static const map_str2int xpath_fnname_map[] = { /* alphabetic order */
|
||||
{"bit-is-set", XPATHFN_BIT_IS_SET},
|
||||
{"boolean", XPATHFN_BOOLEAN},
|
||||
{"eiling", XPATHFN_CEILING},
|
||||
{"comment", XPATHFN_COMMENT},
|
||||
{"concat", XPATHFN_CONCAT},
|
||||
{"contains", XPATHFN_CONTAINS},
|
||||
{"count", XPATHFN_COUNT},
|
||||
{"current", XPATHFN_CURRENT},
|
||||
{"deref", XPATHFN_DEREF},
|
||||
{"derived-from", XPATHFN_DERIVED_FROM},
|
||||
{"derived-from-or-self", XPATHFN_DERIVED_FROM_OR_SELF},
|
||||
{"enum-value", XPATHFN_ENUM_VALUE},
|
||||
{"false", XPATHFN_FALSE},
|
||||
{"floor", XPATHFN_FLOOR},
|
||||
{"id", XPATHFN_ID},
|
||||
{"lang", XPATHFN_LANG},
|
||||
{"last", XPATHFN_LAST},
|
||||
{"local-name", XPATHFN_LOCAL_NAME},
|
||||
{"name", XPATHFN_NAME},
|
||||
{"namespace-uri", XPATHFN_NAMESPACE_URI},
|
||||
{"normalize-space", XPATHFN_NORMALIZE_SPACE},
|
||||
{"node", XPATHFN_NODE},
|
||||
{"not", XPATHFN_NOT},
|
||||
{"number", XPATHFN_NUMBER},
|
||||
{"position", XPATHFN_POSITION},
|
||||
{"processing-instructions", XPATHFN_PROCESSING_INSTRUCTIONS},
|
||||
{"re-match", XPATHFN_RE_MATCH},
|
||||
{"round", XPATHFN_ROUND},
|
||||
{"starts-with", XPATHFN_STARTS_WITH},
|
||||
{"string", XPATHFN_STRING},
|
||||
{"substring", XPATHFN_SUBSTRING},
|
||||
{"substring-after", XPATHFN_SUBSTRING_AFTER},
|
||||
{"substring-before", XPATHFN_SUBSTRING_BEFORE},
|
||||
{"sum", XPATHFN_SUM},
|
||||
{"text", XPATHFN_TEXT},
|
||||
{"translate", XPATHFN_TRANSLATE},
|
||||
{"true", XPATHFN_TRUE},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/*! Translate xpath function name to int code
|
||||
*/
|
||||
int
|
||||
xp_function_contains(xp_ctx *xc,
|
||||
xp_fnname_str2int(char *fnname)
|
||||
{
|
||||
return clicon_str2int(xpath_fnname_map, fnname);
|
||||
}
|
||||
|
||||
/*! Translate xpath function code to string name
|
||||
*/
|
||||
const char *
|
||||
xp_fnname_int2str(enum clixon_xpath_function code)
|
||||
{
|
||||
return clicon_int2str(xpath_fnname_map, code);
|
||||
}
|
||||
|
||||
int
|
||||
xp_function_current(xp_ctx *xc0,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
xp_ctx *xr0 = NULL;
|
||||
xp_ctx *xr1 = NULL;
|
||||
xp_ctx *xr = NULL;
|
||||
char *s0 = NULL;
|
||||
char *s1 = NULL;
|
||||
cxobj **vec = NULL;
|
||||
int veclen = 0;
|
||||
xp_ctx *xc = NULL;
|
||||
|
||||
/* contains two arguments in xs: boolean contains(string, string) */
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
|
||||
if ((xc = ctx_dup(xc0)) == NULL)
|
||||
goto done;
|
||||
if (ctx2string(xr0, &s0) < 0)
|
||||
if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
if (xp_eval(xc, xs->xs_c1, nsc, localonly, &xr1) < 0)
|
||||
goto done;
|
||||
if (ctx2string(xr1, &s1) < 0)
|
||||
goto done;
|
||||
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(xr, 0, sizeof(*xr));
|
||||
xr->xc_type = XT_BOOL;
|
||||
xr->xc_bool = (strstr(s0, s1) != NULL);
|
||||
*xrp = xr;
|
||||
xr = NULL;
|
||||
ctx_nodeset_replace(xc, vec, veclen);
|
||||
*xrp = xc;
|
||||
xc = NULL;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xr0)
|
||||
ctx_free(xr0);
|
||||
if (xr1)
|
||||
ctx_free(xr1);
|
||||
if (s0)
|
||||
free(s0);
|
||||
if (s1)
|
||||
free(s1);
|
||||
if (xc)
|
||||
ctx_free(xc);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
xp_function_deref(xp_ctx *xc0,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
xp_ctx *xc = NULL;
|
||||
int i;
|
||||
cxobj **vec = NULL;
|
||||
int veclen = 0;
|
||||
cxobj *xv;
|
||||
cxobj *xref;
|
||||
yang_stmt *ys;
|
||||
yang_stmt *yt;
|
||||
yang_stmt *ypath;
|
||||
char *path;
|
||||
|
||||
/* Create new xc */
|
||||
if ((xc = ctx_dup(xc0)) == NULL)
|
||||
goto done;
|
||||
for (i=0; i<xc->xc_size; i++){
|
||||
xv = xc->xc_nodeset[i];
|
||||
if ((ys = xml_spec(xv)) == NULL)
|
||||
continue;
|
||||
/* Get base type yc */
|
||||
if (yang_type_get(ys, NULL, &yt, NULL, NULL, NULL, NULL, NULL) < 0)
|
||||
goto done;
|
||||
if (strcmp(yang_argument_get(yt), "leafref") == 0){
|
||||
if ((ypath = yang_find(yt, Y_PATH, NULL)) != NULL){
|
||||
path = yang_argument_get(ypath);
|
||||
if ((xref = xpath_first(xv, nsc, "%s", path)) != NULL)
|
||||
if (cxvec_append(xref, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
ctx_nodeset_replace(xc, vec, veclen);
|
||||
}
|
||||
else if (strcmp(yang_argument_get(yt), "identityref") == 0){
|
||||
}
|
||||
}
|
||||
*xrp = xc;
|
||||
xc = NULL;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xc)
|
||||
ctx_free(xc);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -255,6 +333,10 @@ xp_function_derived_from(xp_ctx *xc,
|
|||
int i;
|
||||
int ret = 0;
|
||||
|
||||
if (xs == NULL || xs->xs_c0 == NULL || xs->xs_c1 == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "derived-from expects but did not get two arguments");
|
||||
goto done;
|
||||
}
|
||||
/* contains two arguments in xs: boolean derived-from(node-set, string) */
|
||||
/* This evolves to a set of (identityref) nodes */
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
|
||||
|
|
@ -293,3 +375,191 @@ xp_function_derived_from(xp_ctx *xc,
|
|||
free(identity);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! The count function returns the number of nodes in the argument node-set.
|
||||
*
|
||||
* Signature: number count(node-set)
|
||||
*/
|
||||
int
|
||||
xp_function_count(xp_ctx *xc0,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
xp_ctx *xc = NULL;
|
||||
xp_ctx *xr = NULL;
|
||||
xp_ctx *xr0 = NULL;
|
||||
|
||||
if (xs == NULL || xs->xs_c0 == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "count expects but did not get one argument");
|
||||
goto done;
|
||||
}
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
|
||||
goto done;
|
||||
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(xr, 0, sizeof(*xr));
|
||||
xr->xc_type = XT_NUMBER;
|
||||
xr->xc_number = xr0->xc_number;
|
||||
*xrp = xc;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xr0)
|
||||
ctx_free(xr0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! The name function returns a string of a QName
|
||||
*
|
||||
* The name function returns a string containing a QName representing the expanded-name
|
||||
* of the node in the argument node-set that is first in document order.
|
||||
* Signature: string name(node-set?)
|
||||
* XXX: should return expanded-name, should namespace be included?
|
||||
*/
|
||||
int
|
||||
xp_function_name(xp_ctx *xc0,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
xp_ctx *xc = NULL;
|
||||
xp_ctx *xr = NULL;
|
||||
xp_ctx *xr0 = NULL;
|
||||
char *s0 = NULL;
|
||||
int i;
|
||||
cxobj *x;
|
||||
|
||||
if (xs == NULL || xs->xs_c0 == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "not expects but did not get one argument");
|
||||
goto done;
|
||||
}
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
|
||||
goto done;
|
||||
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(xr, 0, sizeof(*xr));
|
||||
xr->xc_type = XT_STRING;
|
||||
for (i=0; i<xr0->xc_size; i++){
|
||||
if ((x = xr0->xc_nodeset[i]) == NULL)
|
||||
continue;
|
||||
if ((xr->xc_string = strdup(xml_name(x))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
*xrp = xc;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xr0)
|
||||
ctx_free(xr0);
|
||||
if (s0)
|
||||
free(s0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Eval xpath function contains
|
||||
* @param[in] xc Incoming context
|
||||
* @param[in] xs XPATH node tree
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[in] localonly Skip prefix and namespace tests (non-standard)
|
||||
* @param[out] xrp Resulting context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see https://www.w3.org/TR/xpath-10/#NT-FunctionName 4.2 String Functions
|
||||
*/
|
||||
int
|
||||
xp_function_contains(xp_ctx *xc,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
xp_ctx *xr0 = NULL;
|
||||
xp_ctx *xr1 = NULL;
|
||||
xp_ctx *xr = NULL;
|
||||
char *s0 = NULL;
|
||||
char *s1 = NULL;
|
||||
|
||||
if (xs == NULL || xs->xs_c0 == NULL || xs->xs_c1 == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "contains expects but did not get two arguments");
|
||||
goto done;
|
||||
}
|
||||
/* contains two arguments in xs: boolean contains(string, string) */
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
|
||||
goto done;
|
||||
if (ctx2string(xr0, &s0) < 0)
|
||||
goto done;
|
||||
if (xp_eval(xc, xs->xs_c1, nsc, localonly, &xr1) < 0)
|
||||
goto done;
|
||||
if (ctx2string(xr1, &s1) < 0)
|
||||
goto done;
|
||||
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(xr, 0, sizeof(*xr));
|
||||
xr->xc_type = XT_BOOL;
|
||||
xr->xc_bool = (strstr(s0, s1) != NULL);
|
||||
*xrp = xr;
|
||||
xr = NULL;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xr0)
|
||||
ctx_free(xr0);
|
||||
if (xr1)
|
||||
ctx_free(xr1);
|
||||
if (s0)
|
||||
free(s0);
|
||||
if (s1)
|
||||
free(s1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! The not function returns true if its argument is false, and false otherwise.
|
||||
*
|
||||
* Signatire: boolean contains(boolean)
|
||||
*/
|
||||
int
|
||||
xp_function_not(xp_ctx *xc0,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
xp_ctx *xc = NULL;
|
||||
xp_ctx *xr = NULL;
|
||||
xp_ctx *xr0 = NULL;
|
||||
int bool;
|
||||
|
||||
if (xs == NULL || xs->xs_c0 == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "not expects but did not get one argument");
|
||||
goto done;
|
||||
}
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
|
||||
goto done;
|
||||
bool = ctx2boolean(xr0);
|
||||
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(xr, 0, sizeof(*xr));
|
||||
xr->xc_type = XT_BOOL;
|
||||
xr->xc_bool = !bool;
|
||||
*xrp = xc;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xr0)
|
||||
ctx_free(xr0);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,10 +38,67 @@
|
|||
#ifndef _CLIXON_XPATH_FUNCTION_H
|
||||
#define _CLIXON_XPATH_FUNCTION_H
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
/*
|
||||
* XPath functions from Xpath 1.0 spec or YANG
|
||||
* @see xp_checkfn where they are parsed and checked
|
||||
* @see clixon_xpath_function.c for implementations
|
||||
*/
|
||||
enum clixon_xpath_function{
|
||||
XPATHFN_CURRENT, /* RFC 7950 10.1.1 */
|
||||
XPATHFN_RE_MATCH, /* RFC 7950 10.2.1 NYI */
|
||||
XPATHFN_DEREF, /* RFC 7950 10.3.1 */
|
||||
XPATHFN_DERIVED_FROM, /* RFC 7950 10.4.1 */
|
||||
XPATHFN_DERIVED_FROM_OR_SELF, /* RFC 7950 10.4.2 */
|
||||
XPATHFN_ENUM_VALUE, /* RFC 7950 10.5.1 NYI */
|
||||
XPATHFN_BIT_IS_SET, /* RFC 7950 10.6.1 NYI */
|
||||
XPATHFN_LAST, /* XPATH 1.0 4.1 NYI */
|
||||
XPATHFN_POSITION, /* XPATH 1.0 4.1 NYI */
|
||||
XPATHFN_COUNT, /* XPATH 1.0 4.1 */
|
||||
XPATHFN_ID, /* XPATH 1.0 4.1 NYI */
|
||||
XPATHFN_LOCAL_NAME, /* XPATH 1.0 4.1 NYI */
|
||||
XPATHFN_NAMESPACE_URI, /* XPATH 1.0 4.1 NYI */
|
||||
XPATHFN_NAME, /* XPATH 1.0 4.1 */
|
||||
XPATHFN_STRING, /* XPATH 1.0 4.2 NYI */
|
||||
XPATHFN_CONCAT, /* XPATH 1.0 4.2 NYI */
|
||||
XPATHFN_STARTS_WITH, /* XPATH 1.0 4.2 NYI */
|
||||
XPATHFN_CONTAINS, /* XPATH 1.0 4.2 */
|
||||
XPATHFN_SUBSTRING_BEFORE, /* XPATH 1.0 4.2 NYI */
|
||||
XPATHFN_SUBSTRING_AFTER, /* XPATH 1.0 4.2 NYI */
|
||||
XPATHFN_SUBSTRING, /* XPATH 1.0 4.2 NYI */
|
||||
XPATHFN_STRING_LENGTH, /* XPATH 1.0 4.2 NYI */
|
||||
XPATHFN_NORMALIZE_SPACE, /* XPATH 1.0 4.2 NYI */
|
||||
XPATHFN_TRANSLATE, /* XPATH 1.0 4.2 NYI */
|
||||
XPATHFN_BOOLEAN, /* XPATH 1.0 4.3 NYI */
|
||||
XPATHFN_NOT, /* XPATH 1.0 4.3 */
|
||||
XPATHFN_TRUE, /* XPATH 1.0 4.3 NYI */
|
||||
XPATHFN_FALSE, /* XPATH 1.0 4.3 NYI */
|
||||
XPATHFN_LANG, /* XPATH 1.0 4.3 NYI */
|
||||
XPATHFN_NUMBER, /* XPATH 1.0 4.4 NYI */
|
||||
XPATHFN_SUM, /* XPATH 1.0 4.4 NYI */
|
||||
XPATHFN_FLOOR, /* XPATH 1.0 4.4 NYI */
|
||||
XPATHFN_CEILING, /* XPATH 1.0 4.4 NYI */
|
||||
XPATHFN_ROUND, /* XPATH 1.0 4.4 NYI */
|
||||
XPATHFN_COMMENT, /* XPATH 1.0 nodetype NYI */
|
||||
XPATHFN_TEXT, /* XPATH 1.0 nodetype */
|
||||
XPATHFN_PROCESSING_INSTRUCTIONS,/* XPATH 1.0 nodetype NYI */
|
||||
XPATHFN_NODE, /* XPATH 1.0 nodetype */
|
||||
};
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int xp_function_contains(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_fnname_str2int(char *fnname);
|
||||
const char *xp_fnname_int2str(enum clixon_xpath_function code);
|
||||
|
||||
int xp_function_current(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_deref(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_derived_from(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, int self, xp_ctx **xrp);
|
||||
int xp_function_count(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_name(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_contains(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_not(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
|
||||
#endif /* _CLIXON_XPATH_FUNCTION_H */
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ xpath_optimize_init(xpath_tree **xm,
|
|||
goto done;
|
||||
xs->xs_match++; /* in loop_preds get name in xs_s1 XXX: leaf-list is different */
|
||||
/* get keyval (_z) */
|
||||
if ((xs = xpath_tree_traverse(_xe, 0, 0, 1, 0, 0, 0, -1)) == NULL)
|
||||
if ((xs = xpath_tree_traverse(_xe, 0, 0, 1, 0, 0, 0, 0, -1)) == NULL)
|
||||
goto done;
|
||||
xs->xs_match++; /* in loop_preds get value in xs_s0 or xs_strnr */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,13 @@ clixon_xpath_parsewrap(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* strip last char */
|
||||
void
|
||||
striplast(char *s)
|
||||
{
|
||||
s[strlen(s)-1] = 0;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
digit [0-9]
|
||||
|
|
@ -87,6 +94,7 @@ real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
|
|||
namestart [A-Z_a-z]
|
||||
namechar [A-Z_a-z\-\.0-9]
|
||||
ncname {namestart}{namechar}*
|
||||
fnname {ncname}\(
|
||||
|
||||
%x TOKEN
|
||||
%s QLITERAL
|
||||
|
|
@ -110,16 +118,8 @@ ncname {namestart}{namechar}*
|
|||
<TOKEN>">=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
|
||||
<TOKEN>"<=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
|
||||
<TOKEN>[<>=] { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
|
||||
<TOKEN>last { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>position { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>count { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>contains { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>re-match { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>deref { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>derived-from { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>derived-from-or-self { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>enum-value { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>bit-is-set { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
|
||||
<TOKEN>{fnname} { clixon_xpath_parselval.string = strdup(yytext); striplast(clixon_xpath_parselval.string); return FUNCTIONNAME; }
|
||||
|
||||
<TOKEN>@ { return *yytext; }
|
||||
<TOKEN>ancestor:: { clixon_xpath_parselval.intval = A_ANCESTOR; return AXISNAME; }
|
||||
|
|
@ -136,12 +136,6 @@ ncname {namestart}{namechar}*
|
|||
<TOKEN>preceding-sibling:: { clixon_xpath_parselval.intval = A_PRECEDING_SIBLING; return AXISNAME; }
|
||||
<TOKEN>self:: { clixon_xpath_parselval.intval = A_SELF; return AXISNAME; }
|
||||
|
||||
<TOKEN>current { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
|
||||
<TOKEN>comment { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
|
||||
<TOKEN>text { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
|
||||
<TOKEN>processing-instructions { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
|
||||
<TOKEN>node { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
|
||||
|
||||
<TOKEN>\" { BEGIN(QLITERAL); return QUOTE; }
|
||||
<TOKEN>\' { BEGIN(ALITERAL); return APOST; }
|
||||
<TOKEN>\-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; }
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@
|
|||
%token <string> APOST
|
||||
%token <string> CHARS
|
||||
%token <string> NAME
|
||||
%token <string> NODETYPE
|
||||
%token <string> DOUBLEDOT
|
||||
%token <string> DOUBLESLASH
|
||||
%token <string> FUNCTIONNAME
|
||||
|
|
@ -75,6 +74,7 @@
|
|||
%type <stack> addexpr
|
||||
%type <stack> unionexpr
|
||||
%type <stack> pathexpr
|
||||
%type <stack> filterexpr
|
||||
%type <stack> locationpath
|
||||
%type <stack> abslocpath
|
||||
%type <stack> rellocpath
|
||||
|
|
@ -122,6 +122,7 @@
|
|||
#include "clixon_xml.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_xpath_function.h"
|
||||
|
||||
#include "clixon_xpath_parse.h"
|
||||
|
||||
|
|
@ -135,7 +136,8 @@ void
|
|||
clixon_xpath_parseerror(void *_xpy,
|
||||
char *s)
|
||||
{
|
||||
clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'",
|
||||
errno = 0;
|
||||
clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'", /* Note lineno here is xpath, not yang */
|
||||
_XPY->xpy_name,
|
||||
_XPY->xpy_linenum ,
|
||||
s,
|
||||
|
|
@ -156,6 +158,16 @@ xpath_parse_exit(clixon_xpath_yacc *xpy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Generic creator function for an xpath tree object
|
||||
*
|
||||
* @param[in] type XPATH tree node type
|
||||
* @param[in] i0 step-> axis_type
|
||||
* @param[in] numstr original string xs_double: numeric value
|
||||
* @param[in] s0 String 0 set if XP_PRIME_STR, XP_PRIME_FN, XP_NODE[_FN] prefix
|
||||
* @param[in] s1 String 1 set if XP_NODE NAME
|
||||
* @param[in] c0 Child 0
|
||||
* @param[in] c1 Child 1
|
||||
*/
|
||||
static xpath_tree *
|
||||
xp_new(enum xp_type type,
|
||||
int i0,
|
||||
|
|
@ -191,6 +203,158 @@ xp_new(enum xp_type type,
|
|||
return xs;
|
||||
}
|
||||
|
||||
/*! Specialized xpath-tree creation for xpath functions (wrapper for functions around xp_new)
|
||||
*
|
||||
* Sanity check xpath functions before adding them
|
||||
* @param[in] xpy XPath parse handle
|
||||
* @param[in] name Name of function
|
||||
* @param[in] xpt Sub-parse-tree
|
||||
*/
|
||||
static xpath_tree *
|
||||
xp_primary_function(clixon_xpath_yacc *xpy,
|
||||
char *name,
|
||||
xpath_tree *xpt)
|
||||
{
|
||||
xpath_tree *xtret = NULL;
|
||||
enum clixon_xpath_function fn;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((fn = xp_fnname_str2int(name)) < 0){
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "Unknown xpath function \"%s\"", name);
|
||||
clixon_xpath_parseerror(xpy, cbuf_get(cb));
|
||||
goto done;
|
||||
}
|
||||
switch (fn){
|
||||
case XPATHFN_RE_MATCH: /* Group of NOT IMPLEMENTED xpath functions */
|
||||
case XPATHFN_ENUM_VALUE:
|
||||
case XPATHFN_BIT_IS_SET:
|
||||
case XPATHFN_LAST:
|
||||
case XPATHFN_POSITION:
|
||||
case XPATHFN_ID:
|
||||
case XPATHFN_LOCAL_NAME:
|
||||
case XPATHFN_NAMESPACE_URI:
|
||||
case XPATHFN_STRING:
|
||||
case XPATHFN_CONCAT:
|
||||
case XPATHFN_STARTS_WITH:
|
||||
case XPATHFN_SUBSTRING_BEFORE:
|
||||
case XPATHFN_SUBSTRING_AFTER:
|
||||
case XPATHFN_SUBSTRING:
|
||||
case XPATHFN_STRING_LENGTH:
|
||||
case XPATHFN_NORMALIZE_SPACE:
|
||||
case XPATHFN_TRANSLATE:
|
||||
case XPATHFN_BOOLEAN:
|
||||
case XPATHFN_TRUE:
|
||||
case XPATHFN_FALSE:
|
||||
case XPATHFN_LANG:
|
||||
case XPATHFN_NUMBER:
|
||||
case XPATHFN_SUM:
|
||||
case XPATHFN_FLOOR:
|
||||
case XPATHFN_CEILING:
|
||||
case XPATHFN_ROUND:
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "XPATH function \"%s\" is not implemented", name);
|
||||
clixon_xpath_parseerror(xpy, cbuf_get(cb));
|
||||
goto done;
|
||||
break;
|
||||
case XPATHFN_CURRENT: /* Group of implemented xpath functions */
|
||||
case XPATHFN_DEREF:
|
||||
case XPATHFN_DERIVED_FROM:
|
||||
case XPATHFN_DERIVED_FROM_OR_SELF:
|
||||
case XPATHFN_COUNT:
|
||||
case XPATHFN_NAME:
|
||||
case XPATHFN_CONTAINS:
|
||||
case XPATHFN_NOT:
|
||||
break;
|
||||
default:
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "Unknown xpath function \"%s\"", name);
|
||||
clixon_xpath_parseerror(xpy, cbuf_get(cb));
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
xtret = xp_new(XP_PRIME_FN, fn, NULL, name, NULL, xpt, NULL);
|
||||
name = NULL;
|
||||
done:
|
||||
if (name)
|
||||
free(name);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return xtret;
|
||||
}
|
||||
|
||||
/*! Specialized xpath-tree creation for xpath nodetest functions (wrapper for functions around xp_new)
|
||||
*
|
||||
* Sanity check xpath functions before adding them
|
||||
* @param[in] xpy XPath parse handle
|
||||
* @param[in] name Name of function
|
||||
* @param[in] xpt Sub-parse-tree
|
||||
*/
|
||||
static xpath_tree *
|
||||
xp_nodetest_function(clixon_xpath_yacc *xpy,
|
||||
char *name,
|
||||
xpath_tree *xpt)
|
||||
{
|
||||
xpath_tree *xtret = NULL;
|
||||
enum clixon_xpath_function fn;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((fn = xp_fnname_str2int(name)) < 0){
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "Unknown xpath function \"%s\"", name);
|
||||
clixon_xpath_parseerror(xpy, cbuf_get(cb));
|
||||
goto done;
|
||||
}
|
||||
switch (fn){
|
||||
case XPATHFN_COMMENT: /* Group of not implemented node functions */
|
||||
case XPATHFN_PROCESSING_INSTRUCTIONS:
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "XPATH function \"%s\" is not implemented", name);
|
||||
clixon_xpath_parseerror(xpy, cbuf_get(cb));
|
||||
goto done;
|
||||
break;
|
||||
case XPATHFN_TEXT: /* Group of implemented node functions */
|
||||
case XPATHFN_NODE:
|
||||
break;
|
||||
default:
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "Unknown xpath nodetest function \"%s\"", name);
|
||||
clixon_xpath_parseerror(xpy, cbuf_get(cb));
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
xtret = xp_new(XP_NODE_FN, fn, NULL, name, NULL, xpt, NULL);
|
||||
name = NULL; /* Consumed by xp_new */
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (name)
|
||||
free(name);
|
||||
return xtret;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
|
|
@ -224,7 +388,13 @@ unionexpr : unionexpr '|' pathexpr { $$=xp_new(XP_UNION,A_NAN,NULL,NULL,NULL,$
|
|||
;
|
||||
|
||||
pathexpr : locationpath { $$=xp_new(XP_PATHEXPR,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(3,"pathexpr-> locationpath"); }
|
||||
| primaryexpr { $$=xp_new(XP_PATHEXPR,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(3,"pathexpr-> primaryexpr"); }
|
||||
| filterexpr { $$=xp_new(XP_PATHEXPR,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(3,"pathexpr-> filterexpr"); }
|
||||
| filterexpr '/' rellocpath { $$=xp_new(XP_PATHEXPR,A_NAN,NULL,NULL,NULL,$1, $3);clicon_debug(3,"pathexpr-> filterexpr / rellocpath"); }
|
||||
/* Filterexpr // relativelocationpath */
|
||||
;
|
||||
|
||||
filterexpr : primaryexpr { $$=xp_new(XP_FILTEREXPR,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(3,"filterexpr-> primaryexpr"); }
|
||||
/* Filterexpr predicate */
|
||||
;
|
||||
|
||||
/* location path returns a node-set */
|
||||
|
|
@ -257,22 +427,21 @@ nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, NULL, NULL,
|
|||
| NAME { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, $1, NULL, NULL); clicon_debug(3,"nodetest-> name(%s)",$1); }
|
||||
| NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,NULL, $1, $3, NULL, NULL);clicon_debug(3,"nodetest-> name(%s) : name(%s)", $1, $3); }
|
||||
| NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,NULL, $1, NULL, NULL, NULL);clicon_debug(3,"nodetest-> name(%s) : *", $1); }
|
||||
| NODETYPE '(' ')' { $$=xp_new(XP_NODE_FN,A_NAN,NULL, $1, NULL, NULL, NULL); clicon_debug(3,"nodetest-> nodetype():%s", $1); }
|
||||
| FUNCTIONNAME ')' { if (($$ = xp_nodetest_function(_XPY, $1, NULL)) == NULL) YYERROR;; clicon_debug(3,"nodetest-> nodetype():%s", $1); }
|
||||
;
|
||||
|
||||
/* evaluates to boolean */
|
||||
predicates : predicates '[' expr ']' { $$=xp_new(XP_PRED,A_NAN,NULL, NULL, NULL, $1, $3); clicon_debug(3,"predicates-> [ expr ]"); }
|
||||
| { $$=xp_new(XP_PRED,A_NAN,NULL, NULL, NULL, NULL, NULL); clicon_debug(3,"predicates->"); }
|
||||
;
|
||||
|
||||
primaryexpr : '(' expr ')' { $$=xp_new(XP_PRI0,A_NAN,NULL, NULL, NULL, $2, NULL); clicon_debug(3,"primaryexpr-> ( expr )"); }
|
||||
| NUMBER { $$=xp_new(XP_PRIME_NR,A_NAN, $1, NULL, NULL, NULL, NULL);clicon_debug(3,"primaryexpr-> NUMBER(%s)", $1); /*XXX*/}
|
||||
| QUOTE string QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, $2, NULL, NULL, NULL);clicon_debug(3,"primaryexpr-> \" string \""); }
|
||||
| QUOTE QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, NULL, NULL, NULL, NULL);clicon_debug(3,"primaryexpr-> \" \""); }
|
||||
| APOST string APOST { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, $2, NULL, NULL, NULL);clicon_debug(3,"primaryexpr-> ' string '"); }
|
||||
| APOST APOST { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, NULL, NULL, NULL, NULL);clicon_debug(3,"primaryexpr-> ' '"); }
|
||||
| FUNCTIONNAME '(' ')' { $$=xp_new(XP_PRIME_FN,A_NAN,NULL, $1, NULL, NULL, NULL);clicon_debug(3,"primaryexpr-> functionname ( arguments )"); }
|
||||
| FUNCTIONNAME '(' args ')' { $$=xp_new(XP_PRIME_FN,A_NAN,NULL, $1, NULL, $3, NULL);clicon_debug(3,"primaryexpr-> functionname ( arguments )"); }
|
||||
| FUNCTIONNAME ')' { if (($$ = xp_primary_function(_XPY, $1, NULL)) == NULL) YYERROR; clicon_debug(3,"primaryexpr-> functionname ()"); }
|
||||
| FUNCTIONNAME args ')' { if (($$ = xp_primary_function(_XPY, $1, $2)) == NULL) YYERROR; clicon_debug(3,"primaryexpr-> functionname (arguments)"); }
|
||||
;
|
||||
|
||||
args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, $3);
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@
|
|||
#include "clixon_hash.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_data.h"
|
||||
|
|
@ -1493,6 +1495,11 @@ ys_parse_sub(yang_stmt *ys,
|
|||
goto done;
|
||||
}
|
||||
break;
|
||||
case Y_MUST:
|
||||
case Y_WHEN:
|
||||
if (xpath_parse(yang_argument_get(ys), NULL) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case Y_REVISION:
|
||||
case Y_REVISION_DATE: /* YYYY-MM-DD encoded as uint32 YYYYMMDD */
|
||||
if (ys_parse_date_arg(arg, &date) < 0)
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ new "xpath ../../../rt:address-family = 'v6ur:ipv6-unicast'"
|
|||
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb/here2/here" 0 "../../../rt:address-family = 'v6ur:ipv6-unicast'" "^bool:true$"
|
||||
|
||||
new "xpath /if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'"
|
||||
expecteof "$clixon_util_xpath -D 1 -f $xml2" 0 "/if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'" "^bool:true$"
|
||||
expectpart "$($clixon_util_xpath -f $xml2 -p "/if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'")" 0 "^bool:true$"
|
||||
|
||||
new "xpath rt:address-family='v6ur:ipv6-unicast'"
|
||||
expecteof "$clixon_util_xpath -f $xml2 -i /aaa" 0 "rt:address-family='v6ur:ipv6-unicast'" "^bool:true$"
|
||||
|
|
@ -210,8 +210,21 @@ expectpart "$($clixon_util_xpath -f $xml3 -p 'derived-from-or-self(../../change-
|
|||
new "xpath contains"
|
||||
expectpart "$($clixon_util_xpath -f $xml3 -p "contains(../../objectClass,'BTSFunction') or contains(../../objectClass,'RNCFunction')")" 0 "bool:false"
|
||||
|
||||
# Just syntax - no semantic meaning
|
||||
# Nodetests
|
||||
|
||||
new "xpath nodetest: node"
|
||||
expectpart "$($clixon_util_xpath -f $xml3 -p "/bbb/ccc/self::node()")" 0 "nodeset:0:<ccc>foo</ccc>"
|
||||
|
||||
new "xpath nodetest: comment nyi"
|
||||
expectpart "$($clixon_util_xpath -f $xml3 -l o -p "/descendant-or-self::comment()")" 255 "XPATH function \"comment\" is not implemented"
|
||||
|
||||
# Negative
|
||||
|
||||
new "xpath dontexist"
|
||||
expectpart "$($clixon_util_xpath -f $xml3 -l o -p "dontexist()")" 255 "Unknown xpath function \"dontexist\""
|
||||
|
||||
new "xpath enum-value nyi"
|
||||
expectpart "$($clixon_util_xpath -f $xml3 -l o -p "enum-value()")" 255 "XPATH function \"enum-value\" is not implemented"
|
||||
|
||||
rm -rf $dir
|
||||
|
||||
|
|
|
|||
|
|
@ -54,37 +54,37 @@ EOF
|
|||
new "yangmodels parse: -f $cfg"
|
||||
|
||||
new "yangmodel Experimental IEEE 802.1: $YANGMODELS/experimental/ieee/802.1"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/experimental/ieee/802.1 -p $YANGMODELS/experimental/ieee/1588 show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/experimental/ieee/802.1 -p $YANGMODELS/experimental/ieee/1588 show version)" 0 "$version."
|
||||
|
||||
new "yangmodel Experimental IEEE 1588: $YANGMODELS/experimental/ieee/1588"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/experimental/ieee/1588 show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/experimental/ieee/1588 show version)" 0 "$version."
|
||||
|
||||
# Standard IEEE
|
||||
new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/ABcu"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/ABcu show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/ABcu show version)" 0 "$version."
|
||||
|
||||
new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/Qcr"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/Qcr show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/Qcr show version)" 0 "$version."
|
||||
|
||||
new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/Qcw"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/Qcw show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/Qcw show version)" 0 "$version."
|
||||
|
||||
new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/Qcx"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/Qcx -p $YANGMODELS/standard/ieee/draft/802.1/ABcu show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/Qcx -p $YANGMODELS/standard/ieee/draft/802.1/ABcu show version)" 0 "$version."
|
||||
|
||||
new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/x"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/x show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/draft/802.1/x show version)" 0 "$version."
|
||||
|
||||
# Published
|
||||
new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/published/802.1"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/published/802.1 show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/published/802.1 show version)" 0 "$version."
|
||||
|
||||
new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/published/802.3"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/published/802.3 show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ieee/published/802.3 show version)" 0 "$version."
|
||||
|
||||
# Standard IETF
|
||||
new "yangmodel Standard IETF: $YANGMODELS/standard/ietf/RFC"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ietf/RFC show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/standard/ietf/RFC show version)" 0 "$version."
|
||||
|
||||
# vendor/junos
|
||||
#junos : M/MX, T/TX, Some EX platforms, ACX
|
||||
|
|
@ -107,7 +107,7 @@ let i=0;
|
|||
for f in $files; do
|
||||
if [ -n "$(head -5 $f|grep '^ module')" ]; then
|
||||
new "$clixon_cli -1f $cfg -o CLICON_YANG_MAIN_FILE=$f -p $YANGMODELS/vendor/juniper/18.2/18.2R1/common -p $YANGMODELS/vendor/juniper/18.2/18.2R1/junos/conf show version"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_FILE=$f -p $YANGMODELS/vendor/juniper/18.2/18.2R1/common -p $YANGMODELS/vendor/juniper/18.2/18.2R1/junos/conf -o CLICON_CLI_GENMODEL=0 show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_FILE=$f -p $YANGMODELS/vendor/juniper/18.2/18.2R1/common -p $YANGMODELS/vendor/juniper/18.2/18.2R1/junos/conf -o CLICON_CLI_GENMODEL=0 show version)" 0 "$version."
|
||||
let i++;
|
||||
sleep 1
|
||||
fi
|
||||
|
|
@ -120,16 +120,16 @@ done
|
|||
if false; then
|
||||
# vendor/cisco/xr
|
||||
new "yangmodel vendor cisco xr 623: $YANGMODELS/vendor/cisco/xr/623"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/623 show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/623 show version)" 0 "$version."
|
||||
|
||||
new "yangmodel vendor cisco xr 632: $YANGMODELS/vendor/cisco/xr/632"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/632 show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/632 show version)" 0 "$version."
|
||||
|
||||
new "yangmodel vendor cisco xr 623: $YANGMODELS/vendor/cisco/xr/642"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/642 show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/642 show version)" 0 "$version."
|
||||
|
||||
new "yangmodel vendor cisco xr 651: $YANGMODELS/vendor/cisco/xr/651"
|
||||
expectfn "$clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/651 show version" 0 "$version."
|
||||
expectpart "$($clixon_cli -D $DBG -1f $cfg -o CLICON_YANG_MAIN_DIR=$YANGMODELS/vendor/cisco/xr/651 show version)" 0 "$version."
|
||||
fi ### cisco
|
||||
|
||||
rm -rf $dir
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ See https://www.w3.org/TR/xpath/
|
|||
#include "clixon/clixon.h"
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define XPATH_OPTS "hD:f:p:i:n:cy:Y:"
|
||||
#define XPATH_OPTS "hD:f:p:i:n:cl:y:Y:"
|
||||
|
||||
static int
|
||||
usage(char *argv0)
|
||||
|
|
@ -73,6 +73,7 @@ usage(char *argv0)
|
|||
"\t-i <xpath0>\t(optional) Initial XPATH string\n"
|
||||
"\t-n <pfx:id>\tNamespace binding (pfx=NULL for default)\n"
|
||||
"\t-c \t\tMap xpath to canonical form\n"
|
||||
"\t-l <s|e|o|f<file>> \tLog on (s)yslog, std(e)rr, std(o)ut or (f)ile (stderr is default)\n"
|
||||
"\t-y <filename> \tYang filename or dir (load all files)\n"
|
||||
"\t-Y <dir> \tYang dirs (can be several)\n"
|
||||
"and the following extra rules:\n"
|
||||
|
|
@ -140,10 +141,11 @@ main(int argc,
|
|||
cxobj *xcfg = NULL;
|
||||
cbuf *cbret = NULL;
|
||||
cxobj *xerr = NULL; /* malloced must be freed */
|
||||
int logdst = CLICON_LOG_STDERR;
|
||||
int dbg = 0;
|
||||
|
||||
/* In the startup, logs to stderr & debug flag set later */
|
||||
clicon_log_init("xpath", LOG_DEBUG, CLICON_LOG_STDERR);
|
||||
clicon_log_init("xpath", LOG_DEBUG, logdst);
|
||||
/* Initialize clixon handle */
|
||||
if ((h = clicon_handle_init()) == NULL)
|
||||
goto done;
|
||||
|
|
@ -200,6 +202,14 @@ main(int argc,
|
|||
case 'c': /* Map namespace to canonical form */
|
||||
canonical = 1;
|
||||
break;
|
||||
case 'l': /* Log destination: s|e|o|f */
|
||||
if ((logdst = clicon_log_opt(optarg[0])) < 0)
|
||||
usage(argv[0]);
|
||||
if (logdst == CLICON_LOG_FILE &&
|
||||
strlen(optarg)>1 &&
|
||||
clicon_log_file(optarg+1) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case 'y':
|
||||
yang_file_dir = optarg;
|
||||
break;
|
||||
|
|
@ -211,6 +221,11 @@ main(int argc,
|
|||
usage(argv[0]);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Logs, error and debug to stderr or syslog, set debug level
|
||||
*/
|
||||
clicon_log_init("xpath", dbg?LOG_DEBUG:LOG_INFO, logdst);
|
||||
|
||||
clicon_debug_init(dbg, NULL);
|
||||
|
||||
/* Parse yang */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue