From 21ac47915b5bf88670340b9eff44cc19c6ac56f3 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 23 Sep 2020 15:35:01 +0200 Subject: [PATCH] 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). --- CHANGELOG.md | 26 ++- README.md | 6 +- lib/clixon/clixon_xpath.h | 3 +- lib/src/clixon_xpath.c | 7 +- lib/src/clixon_xpath_eval.c | 100 ++++++--- lib/src/clixon_xpath_function.c | 358 ++++++++++++++++++++++++++++---- lib/src/clixon_xpath_function.h | 59 +++++- lib/src/clixon_xpath_optimize.c | 2 +- lib/src/clixon_xpath_parse.l | 26 +-- lib/src/clixon_xpath_parse.y | 187 ++++++++++++++++- lib/src/clixon_yang_parse_lib.c | 7 + test/test_xpath.sh | 17 +- test/test_yang_models.sh | 30 +-- util/clixon_util_xpath.c | 19 +- 14 files changed, 715 insertions(+), 132 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 779370e5..acb51c66 100644 --- a/CHANGELOG.md +++ b/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,15 +25,29 @@ * [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 diff --git a/README.md b/README.md index 4a9293b6..04c8e22b 100644 --- a/README.md +++ b/README.md @@ -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/) diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h index f842de4d..edf7ab03 100644 --- a/lib/clixon/clixon_xpath.h +++ b/lib/clixon/clixon_xpath.h @@ -87,6 +87,7 @@ enum xp_type{ XP_ADD, XP_UNION, XP_PATHEXPR, + XP_FILTEREXPR, XP_LOCPATH, XP_ABSPATH, XP_RELLOCPATH, @@ -114,7 +115,7 @@ struct xpath_tree{ char *xs_s1; /* set if XP_NODE NAME */ struct xpath_tree *xs_c0; /* child 0 */ 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; diff --git a/lib/src/clixon_xpath.c b/lib/src/clixon_xpath.c index 434c3125..8353ff5f 100644 --- a/lib/src/clixon_xpath.c +++ b/lib/src/clixon_xpath.c @@ -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; } diff --git a/lib/src/clixon_xpath_eval.c b/lib/src/clixon_xpath_eval.c index d4d400ac..f9c8c0a0 100644 --- a/lib/src/clixon_xpath_eval.c +++ b/lib/src/clixon_xpath_eval.c @@ -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,27 +334,21 @@ 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) + for (i=0; ixc_size; i++){ + xv = xc->xc_nodeset[i]; + x = NULL; + if ((ret = xpath_optimize_check(xs, xv, &vec, &veclen)) < 0) goto done; - } - else for (i=0; ixc_size; i++){ - xv = xc->xc_nodeset[i]; - x = NULL; - if ((ret = xpath_optimize_check(xs, xv, &vec, &veclen)) < 0) - 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; - } + 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); break; @@ -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; diff --git a/lib/src/clixon_xpath_function.c b/lib/src/clixon_xpath_function.c index 69ff1fc0..74b02533 100644 --- a/lib/src/clixon_xpath_function.c +++ b/lib/src/clixon_xpath_function.c @@ -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, - struct xpath_tree *xs, - cvec *nsc, - int localonly, - xp_ctx **xrp) +xp_fnname_str2int(char *fnname) { - int retval = -1; - xp_ctx *xr0 = NULL; - xp_ctx *xr1 = NULL; - xp_ctx *xr = NULL; - char *s0 = NULL; - char *s1 = NULL; + return clicon_str2int(xpath_fnname_map, fnname); +} - /* contains two arguments in xs: boolean contains(string, string) */ - if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0) +/*! 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; + cxobj **vec = NULL; + int veclen = 0; + xp_ctx *xc = NULL; + + 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; ixc_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; ixc_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; +} diff --git a/lib/src/clixon_xpath_function.h b/lib/src/clixon_xpath_function.h index 29942ced..e0404e74 100644 --- a/lib/src/clixon_xpath_function.h +++ b/lib/src/clixon_xpath_function.h @@ -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 */ diff --git a/lib/src/clixon_xpath_optimize.c b/lib/src/clixon_xpath_optimize.c index 96ec90df..e370dd53 100644 --- a/lib/src/clixon_xpath_optimize.c +++ b/lib/src/clixon_xpath_optimize.c @@ -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 */ } diff --git a/lib/src/clixon_xpath_parse.l b/lib/src/clixon_xpath_parse.l index 79a411a8..e6e267e4 100644 --- a/lib/src/clixon_xpath_parse.l +++ b/lib/src/clixon_xpath_parse.l @@ -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}* ">=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; } "<=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; } [<>=] { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; } -last { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } -position { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } -count { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } -contains { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } -re-match { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } -deref { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } -derived-from { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } -derived-from-or-self { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } -enum-value { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } -bit-is-set { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; } + +{fnname} { clixon_xpath_parselval.string = strdup(yytext); striplast(clixon_xpath_parselval.string); return FUNCTIONNAME; } @ { return *yytext; } ancestor:: { clixon_xpath_parselval.intval = A_ANCESTOR; return AXISNAME; } @@ -136,12 +136,6 @@ ncname {namestart}{namechar}* preceding-sibling:: { clixon_xpath_parselval.intval = A_PRECEDING_SIBLING; return AXISNAME; } self:: { clixon_xpath_parselval.intval = A_SELF; return AXISNAME; } -current { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; } -comment { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; } -text { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; } -processing-instructions { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; } -node { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; } - \" { BEGIN(QLITERAL); return QUOTE; } \' { BEGIN(ALITERAL); return APOST; } \-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; } diff --git a/lib/src/clixon_xpath_parse.y b/lib/src/clixon_xpath_parse.y index 2ea8a393..372b8996 100644 --- a/lib/src/clixon_xpath_parse.y +++ b/lib/src/clixon_xpath_parse.y @@ -60,7 +60,6 @@ %token APOST %token CHARS %token NAME -%token NODETYPE %token DOUBLEDOT %token DOUBLESLASH %token FUNCTIONNAME @@ -75,6 +74,7 @@ %type addexpr %type unionexpr %type pathexpr +%type filterexpr %type locationpath %type abslocpath %type 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, @@ -155,7 +157,17 @@ 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 */ @@ -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;} | '@' { $$=A_ATTRIBUTE; clicon_debug(3,"axisspec-> @"); } | { clicon_debug(3,"axisspec-> "); $$=A_CHILD;} - ; + ; 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 ':' 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); diff --git a/lib/src/clixon_yang_parse_lib.c b/lib/src/clixon_yang_parse_lib.c index da5a5128..adcc39f5 100644 --- a/lib/src/clixon_yang_parse_lib.c +++ b/lib/src/clixon_yang_parse_lib.c @@ -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) diff --git a/test/test_xpath.sh b/test/test_xpath.sh index debf774b..ecaaa47a 100755 --- a/test/test_xpath.sh +++ b/test/test_xpath.sh @@ -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:foo" + +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 diff --git a/test/test_yang_models.sh b/test/test_yang_models.sh index c723fb89..4f610b91 100755 --- a/test/test_yang_models.sh +++ b/test/test_yang_models.sh @@ -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 diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c index 6d104e70..14c5137c 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -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 \t(optional) Initial XPATH string\n" "\t-n \tNamespace binding (pfx=NULL for default)\n" "\t-c \t\tMap xpath to canonical form\n" + "\t-l > \tLog on (s)yslog, std(e)rr, std(o)ut or (f)ile (stderr is default)\n" "\t-y \tYang filename or dir (load all files)\n" "\t-Y \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 */