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:
Olof hagsand 2020-09-23 15:35:01 +02:00
parent 2994d2f9a9
commit 21ac47915b
14 changed files with 715 additions and 132 deletions

View file

@ -1,6 +1,6 @@
# Clixon Changelog # 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.7.0](#470) 14 September 2020
* [4.6.0](#460) 14 August 2020 * [4.6.0](#460) 14 August 2020
* [4.5.0](#450) 12 May 2020 * [4.5.0](#450) 12 May 2020
@ -25,15 +25,29 @@
* [3.3.1](#331) June 7 2017 * [3.3.1](#331) June 7 2017
## 4.8.0 ## 4.8.0
Expected: October 2020 Expected: 15 October 2020
### New features ### New features
* Added support for the following XPATH functions: * Added support for the following XPATH functions:
* `contains`, see https://www.w3.org/TR/xpath-10 * `count`, `name`, `contains`, `not`, as defined in [xpath 1.0](https://www.w3.org/TR/xpath-10)
* `derived-from` and `derived-from-or-self` * `deref`, `derived-from` and `derived-from-or-self` from RFC7950 Section 10.
* in particular in augment/when statements as shown in eg RFC 7950. * 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 ## 4.7.0
14 September 2020 14 September 2020

View file

@ -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) 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 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 Clixon interaction is best done posting issues, pull requests, or joining the
[slack channel](https://clixondev.slack.com). [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/) Clixon is sponsored by [Rubicon Communications LLC(Netgate)](https://www.netgate.com/)

View file

@ -87,6 +87,7 @@ enum xp_type{
XP_ADD, XP_ADD,
XP_UNION, XP_UNION,
XP_PATHEXPR, XP_PATHEXPR,
XP_FILTEREXPR,
XP_LOCPATH, XP_LOCPATH,
XP_ABSPATH, XP_ABSPATH,
XP_RELLOCPATH, XP_RELLOCPATH,
@ -114,7 +115,7 @@ struct xpath_tree{
char *xs_s1; /* set if XP_NODE NAME */ char *xs_s1; /* set if XP_NODE NAME */
struct xpath_tree *xs_c0; /* child 0 */ struct xpath_tree *xs_c0; /* child 0 */
struct xpath_tree *xs_c1; /* child 1 */ struct xpath_tree *xs_c1; /* child 1 */
int xs_match; /* meta: match this node */ int xs_match; /* meta: match this node */
}; };
typedef struct xpath_tree xpath_tree; typedef struct xpath_tree xpath_tree;

View file

@ -104,6 +104,7 @@ static const map_str2int xpath_tree_map[] = {
{"addexpr", XP_ADD}, {"addexpr", XP_ADD},
{"unionexpr", XP_UNION}, {"unionexpr", XP_UNION},
{"pathexpr", XP_PATHEXPR}, {"pathexpr", XP_PATHEXPR},
{"filterexpr", XP_FILTEREXPR},
{"locationpath", XP_LOCPATH}, {"locationpath", XP_LOCPATH},
{"abslocpath", XP_ABSPATH}, {"abslocpath", XP_ABSPATH},
{"rellocpath", XP_RELLOCPATH}, {"rellocpath", XP_RELLOCPATH},
@ -514,12 +515,16 @@ xpath_parse(const char *xpath,
} }
xpath_parse_exit(&xpy); xpath_parse_exit(&xpy);
xpath_scan_exit(&xpy); xpath_scan_exit(&xpy);
if (xptree) if (xptree){
*xptree = xpy.xpy_top; *xptree = xpy.xpy_top;
xpy.xpy_top = NULL;
}
retval = 0; retval = 0;
done: done:
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (xpy.xpy_top)
xpath_tree_free(xpy.xpy_top);
return retval; return retval;
} }

View file

@ -217,7 +217,6 @@ nodetest_eval(cxobj *x,
int localonly) int localonly)
{ {
int retval = 0; /* NB: no match is default (not error) */ int retval = 0; /* NB: no match is default (not error) */
char *fn;
if (xs->xs_type == XP_NODE){ if (xs->xs_type == XP_NODE){
if (localonly) if (localonly)
@ -226,11 +225,14 @@ nodetest_eval(cxobj *x,
retval = nodetest_eval_node(x, xs, nsc); retval = nodetest_eval_node(x, xs, nsc);
} }
else if (xs->xs_type == XP_NODE_FN){ else if (xs->xs_type == XP_NODE_FN){
fn = xs->xs_s0; switch (xs->xs_int){
if (strcmp(fn, "node")==0) case XPATHFN_NODE:
retval = 1; case XPATHFN_TEXT:
else if (strcmp(fn, "text")==0)
retval = 1; retval = 1;
break;
default:
break;
}
} }
/* note, retval set by previous statement */ /* note, retval set by previous statement */
return retval; return retval;
@ -332,27 +334,21 @@ xp_eval_step(xp_ctx *xc0,
xc->xc_descendant = 0; xc->xc_descendant = 0;
} }
else{ else{
if (nodetest->xs_type==XP_NODE_FN && for (i=0; i<xc->xc_size; i++){
nodetest->xs_s0 && xv = xc->xc_nodeset[i];
strcmp(nodetest->xs_s0, "current")==0){ x = NULL;
if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0) if ((ret = xpath_optimize_check(xs, xv, &vec, &veclen)) < 0)
goto done; goto done;
} if (ret == 0){/* regular code, no optimization made */
else for (i=0; i<xc->xc_size; i++){ while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
xv = xc->xc_nodeset[i]; /* xs->xs_c0 is nodetest */
x = NULL; if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
if ((ret = xpath_optimize_check(xs, xv, &vec, &veclen)) < 0) if (cxvec_append(x, &vec, &veclen) < 0)
goto done; goto done;
if (ret == 0){/* regular code, no optimization made */
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
/* xs->xs_c0 is nodetest */
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
if (cxvec_append(x, &vec, &veclen) < 0)
goto done;
}
} }
} }
} }
}
} }
ctx_nodeset_replace(xc, vec, veclen); ctx_nodeset_replace(xc, vec, veclen);
break; break;
@ -974,20 +970,51 @@ xp_eval(xp_ctx *xc,
break; break;
case XP_PRIME_FN: case XP_PRIME_FN:
if (xs->xs_s0){ if (xs->xs_s0){
if (strcmp(xs->xs_s0, "contains") == 0){ switch (xs->xs_int){
if (xp_function_contains(xc, xs->xs_c0, nsc, localonly, xrp) < 0) case XPATHFN_CURRENT:
if (xp_function_current(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
goto done; goto done;
goto ok; goto ok;
} break;
else if (strcmp(xs->xs_s0, "derived-from") == 0){ 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) if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 0, xrp) < 0)
goto done; goto done;
goto ok; goto ok;
} break;
else if (strcmp(xs->xs_s0, "derived-from-or-self") == 0){ case XPATHFN_DERIVED_FROM_OR_SELF:
if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 1, xrp) < 0) if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 1, xrp) < 0)
goto done; goto done;
goto ok; 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; break;
@ -1014,6 +1041,10 @@ xp_eval(xp_ctx *xc,
case XP_UNION: case XP_UNION:
break; break;
case XP_PATHEXPR: case XP_PATHEXPR:
if (xs->xs_c1)
use_xr0++;
break;
case XP_FILTEREXPR:
break; break;
case XP_LOCPATH: case XP_LOCPATH:
break; break;
@ -1098,7 +1129,14 @@ xp_eval(xp_ctx *xc,
break; break;
} }
xc->xc_descendant = 0; 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){ if (xr2){
*xrp = xr2; *xrp = xr2;
xr2 = NULL; xr2 = NULL;

View file

@ -74,58 +74,136 @@
#include "clixon_xpath_eval.h" #include "clixon_xpath_eval.h"
#include "clixon_xpath_function.h" #include "clixon_xpath_function.h"
/*! Eval xpath function contains static const map_str2int xpath_fnname_map[] = { /* alphabetic order */
* @param[in] xc Incoming context {"bit-is-set", XPATHFN_BIT_IS_SET},
* @param[in] xs XPATH node tree {"boolean", XPATHFN_BOOLEAN},
* @param[in] nsc XML Namespace context {"eiling", XPATHFN_CEILING},
* @param[in] localonly Skip prefix and namespace tests (non-standard) {"comment", XPATHFN_COMMENT},
* @param[out] xrp Resulting context {"concat", XPATHFN_CONCAT},
* @retval 0 OK {"contains", XPATHFN_CONTAINS},
* @retval -1 Error {"count", XPATHFN_COUNT},
* @see https://www.w3.org/TR/xpath-10/#NT-FunctionName 4.2 String Functions {"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 int
xp_function_contains(xp_ctx *xc, xp_fnname_str2int(char *fnname)
struct xpath_tree *xs,
cvec *nsc,
int localonly,
xp_ctx **xrp)
{ {
int retval = -1; return clicon_str2int(xpath_fnname_map, fnname);
xp_ctx *xr0 = NULL; }
xp_ctx *xr1 = NULL;
xp_ctx *xr = NULL;
char *s0 = NULL;
char *s1 = NULL;
/* contains two arguments in xs: boolean contains(string, string) */ /*! Translate xpath function code to string name
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0) */
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;
cxobj **vec = NULL;
int veclen = 0;
xp_ctx *xc = NULL;
if ((xc = ctx_dup(xc0)) == NULL)
goto done; goto done;
if (ctx2string(xr0, &s0) < 0) if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0)
goto done; goto done;
if (xp_eval(xc, xs->xs_c1, nsc, localonly, &xr1) < 0) ctx_nodeset_replace(xc, vec, veclen);
goto done; *xrp = xc;
if (ctx2string(xr1, &s1) < 0) xc = NULL;
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; retval = 0;
done: done:
if (xr0) if (xc)
ctx_free(xr0); ctx_free(xc);
if (xr1) return retval;
ctx_free(xr1); }
if (s0)
free(s0); int
if (s1) xp_function_deref(xp_ctx *xc0,
free(s1); 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; return retval;
} }
@ -255,6 +333,10 @@ xp_function_derived_from(xp_ctx *xc,
int i; int i;
int ret = 0; 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) */ /* contains two arguments in xs: boolean derived-from(node-set, string) */
/* This evolves to a set of (identityref) nodes */ /* This evolves to a set of (identityref) nodes */
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0) if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
@ -293,3 +375,191 @@ xp_function_derived_from(xp_ctx *xc,
free(identity); free(identity);
return retval; 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;
}

View file

@ -38,10 +38,67 @@
#ifndef _CLIXON_XPATH_FUNCTION_H #ifndef _CLIXON_XPATH_FUNCTION_H
#define _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 * 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_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 */ #endif /* _CLIXON_XPATH_FUNCTION_H */

View file

@ -145,7 +145,7 @@ xpath_optimize_init(xpath_tree **xm,
goto done; goto done;
xs->xs_match++; /* in loop_preds get name in xs_s1 XXX: leaf-list is different */ xs->xs_match++; /* in loop_preds get name in xs_s1 XXX: leaf-list is different */
/* get keyval (_z) */ /* 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; goto done;
xs->xs_match++; /* in loop_preds get value in xs_s0 or xs_strnr */ xs->xs_match++; /* in loop_preds get value in xs_s0 or xs_strnr */
} }

View file

@ -78,6 +78,13 @@ clixon_xpath_parsewrap(void)
return 1; return 1;
} }
/* strip last char */
void
striplast(char *s)
{
s[strlen(s)-1] = 0;
}
%} %}
digit [0-9] digit [0-9]
@ -87,6 +94,7 @@ real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
namestart [A-Z_a-z] namestart [A-Z_a-z]
namechar [A-Z_a-z\-\.0-9] namechar [A-Z_a-z\-\.0-9]
ncname {namestart}{namechar}* ncname {namestart}{namechar}*
fnname {ncname}\(
%x TOKEN %x TOKEN
%s QLITERAL %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>"<=" { 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>{fnname} { clixon_xpath_parselval.string = strdup(yytext); striplast(clixon_xpath_parselval.string); 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>@ { return *yytext; } <TOKEN>@ { return *yytext; }
<TOKEN>ancestor:: { clixon_xpath_parselval.intval = A_ANCESTOR; return AXISNAME; } <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>preceding-sibling:: { clixon_xpath_parselval.intval = A_PRECEDING_SIBLING; return AXISNAME; }
<TOKEN>self:: { clixon_xpath_parselval.intval = A_SELF; 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(QLITERAL); return QUOTE; }
<TOKEN>\' { BEGIN(ALITERAL); return APOST; } <TOKEN>\' { BEGIN(ALITERAL); return APOST; }
<TOKEN>\-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; } <TOKEN>\-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; }

View file

@ -60,7 +60,6 @@
%token <string> APOST %token <string> APOST
%token <string> CHARS %token <string> CHARS
%token <string> NAME %token <string> NAME
%token <string> NODETYPE
%token <string> DOUBLEDOT %token <string> DOUBLEDOT
%token <string> DOUBLESLASH %token <string> DOUBLESLASH
%token <string> FUNCTIONNAME %token <string> FUNCTIONNAME
@ -75,6 +74,7 @@
%type <stack> addexpr %type <stack> addexpr
%type <stack> unionexpr %type <stack> unionexpr
%type <stack> pathexpr %type <stack> pathexpr
%type <stack> filterexpr
%type <stack> locationpath %type <stack> locationpath
%type <stack> abslocpath %type <stack> abslocpath
%type <stack> rellocpath %type <stack> rellocpath
@ -122,6 +122,7 @@
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xpath_ctx.h" #include "clixon_xpath_ctx.h"
#include "clixon_xpath.h" #include "clixon_xpath.h"
#include "clixon_xpath_function.h"
#include "clixon_xpath_parse.h" #include "clixon_xpath_parse.h"
@ -135,7 +136,8 @@ void
clixon_xpath_parseerror(void *_xpy, clixon_xpath_parseerror(void *_xpy,
char *s) 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_name,
_XPY->xpy_linenum , _XPY->xpy_linenum ,
s, s,
@ -155,7 +157,17 @@ xpath_parse_exit(clixon_xpath_yacc *xpy)
{ {
return 0; 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 * static xpath_tree *
xp_new(enum xp_type type, xp_new(enum xp_type type,
int i0, int i0,
@ -191,6 +203,158 @@ xp_new(enum xp_type type,
return xs; 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"); } 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 */ /* location path returns a node-set */
@ -251,28 +421,27 @@ step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,NULL, NULL, NUL
axisspec : AXISNAME { clicon_debug(3,"axisspec-> AXISNAME(%d) ::", $1); $$=$1;} axisspec : AXISNAME { clicon_debug(3,"axisspec-> AXISNAME(%d) ::", $1); $$=$1;}
| '@' { $$=A_ATTRIBUTE; clicon_debug(3,"axisspec-> @"); } | '@' { $$=A_ATTRIBUTE; clicon_debug(3,"axisspec-> @"); }
| { clicon_debug(3,"axisspec-> "); $$=A_CHILD;} | { clicon_debug(3,"axisspec-> "); $$=A_CHILD;}
; ;
nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, NULL, NULL, NULL); clicon_debug(3,"nodetest-> *"); } nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, NULL, NULL, NULL); clicon_debug(3,"nodetest-> *"); }
| NAME { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, $1, NULL, NULL); clicon_debug(3,"nodetest-> name(%s)",$1); } | 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 ':' 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); } | 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 */ /* evaluates to boolean */
predicates : predicates '[' expr ']' { $$=xp_new(XP_PRED,A_NAN,NULL, NULL, NULL, $1, $3); clicon_debug(3,"predicates-> [ expr ]"); } 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->"); } | { $$=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 )"); } 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*/} | 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 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-> \" \""); } | 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 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-> ' '"); } | 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 ')' { if (($$ = xp_primary_function(_XPY, $1, NULL)) == NULL) YYERROR; clicon_debug(3,"primaryexpr-> functionname ()"); }
| FUNCTIONNAME '(' args ')' { $$=xp_new(XP_PRIME_FN,A_NAN,NULL, $1, NULL, $3, NULL);clicon_debug(3,"primaryexpr-> functionname ( arguments )"); } | 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); args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, $3);

View file

@ -90,6 +90,8 @@
#include "clixon_hash.h" #include "clixon_hash.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xml_nsctx.h" #include "clixon_xml_nsctx.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_yang_module.h" #include "clixon_yang_module.h"
#include "clixon_plugin.h" #include "clixon_plugin.h"
#include "clixon_data.h" #include "clixon_data.h"
@ -1493,6 +1495,11 @@ ys_parse_sub(yang_stmt *ys,
goto done; goto done;
} }
break; 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:
case Y_REVISION_DATE: /* YYYY-MM-DD encoded as uint32 YYYYMMDD */ case Y_REVISION_DATE: /* YYYY-MM-DD encoded as uint32 YYYYMMDD */
if (ys_parse_date_arg(arg, &date) < 0) if (ys_parse_date_arg(arg, &date) < 0)

View file

@ -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$" 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'" 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'" 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$" 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" new "xpath contains"
expectpart "$($clixon_util_xpath -f $xml3 -p "contains(../../objectClass,'BTSFunction') or contains(../../objectClass,'RNCFunction')")" 0 "bool:false" 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 rm -rf $dir

View file

@ -54,37 +54,37 @@ EOF
new "yangmodels parse: -f $cfg" new "yangmodels parse: -f $cfg"
new "yangmodel Experimental IEEE 802.1: $YANGMODELS/experimental/ieee/802.1" 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" 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 # Standard IEEE
new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/draft/802.1/ABcu" 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" 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" 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" 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" 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 # Published
new "yangmodel Standard IEEE 802.1: $YANGMODELS/standard/ieee/published/802.1" 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" 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 # Standard IETF
new "yangmodel Standard IETF: $YANGMODELS/standard/ietf/RFC" 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 # vendor/junos
#junos : M/MX, T/TX, Some EX platforms, ACX #junos : M/MX, T/TX, Some EX platforms, ACX
@ -107,7 +107,7 @@ let i=0;
for f in $files; do for f in $files; do
if [ -n "$(head -5 $f|grep '^ module')" ]; then 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" 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++; let i++;
sleep 1 sleep 1
fi fi
@ -120,16 +120,16 @@ done
if false; then if false; then
# vendor/cisco/xr # vendor/cisco/xr
new "yangmodel vendor cisco xr 623: $YANGMODELS/vendor/cisco/xr/623" 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" 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" 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" 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 fi ### cisco
rm -rf $dir rm -rf $dir

View file

@ -59,7 +59,7 @@ See https://www.w3.org/TR/xpath/
#include "clixon/clixon.h" #include "clixon/clixon.h"
/* Command line options to be passed to getopt(3) */ /* 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 static int
usage(char *argv0) usage(char *argv0)
@ -73,6 +73,7 @@ usage(char *argv0)
"\t-i <xpath0>\t(optional) Initial XPATH string\n" "\t-i <xpath0>\t(optional) Initial XPATH string\n"
"\t-n <pfx:id>\tNamespace binding (pfx=NULL for default)\n" "\t-n <pfx:id>\tNamespace binding (pfx=NULL for default)\n"
"\t-c \t\tMap xpath to canonical form\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 <filename> \tYang filename or dir (load all files)\n"
"\t-Y <dir> \tYang dirs (can be several)\n" "\t-Y <dir> \tYang dirs (can be several)\n"
"and the following extra rules:\n" "and the following extra rules:\n"
@ -140,10 +141,11 @@ main(int argc,
cxobj *xcfg = NULL; cxobj *xcfg = NULL;
cbuf *cbret = NULL; cbuf *cbret = NULL;
cxobj *xerr = NULL; /* malloced must be freed */ cxobj *xerr = NULL; /* malloced must be freed */
int logdst = CLICON_LOG_STDERR;
int dbg = 0; int dbg = 0;
/* In the startup, logs to stderr & debug flag set later */ /* 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 */ /* Initialize clixon handle */
if ((h = clicon_handle_init()) == NULL) if ((h = clicon_handle_init()) == NULL)
goto done; goto done;
@ -200,6 +202,14 @@ main(int argc,
case 'c': /* Map namespace to canonical form */ case 'c': /* Map namespace to canonical form */
canonical = 1; canonical = 1;
break; 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': case 'y':
yang_file_dir = optarg; yang_file_dir = optarg;
break; break;
@ -211,6 +221,11 @@ main(int argc,
usage(argv[0]); usage(argv[0]);
break; 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); clicon_debug_init(dbg, NULL);
/* Parse yang */ /* Parse yang */