New: [feature request: support xpath functions for strings](https://github.com/clicon/clixon/issues/556)
Added: re-match, substring, string, string-length, translate, substring-before, substring-after, starts-with
This commit is contained in:
parent
3188e3cc59
commit
515d30bdd7
7 changed files with 741 additions and 45 deletions
|
|
@ -15,6 +15,10 @@
|
||||||
## 7.3.0
|
## 7.3.0
|
||||||
Expected: January 2025
|
Expected: January 2025
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* New: [feature request: support xpath functions for strings](https://github.com/clicon/clixon/issues/556)
|
||||||
|
* Added: re-match, substring, string, string-length, translate, substring-before, substring-after, starts-with
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
|
||||||
* Fixed: [string length validation doesn't work for the entry "" in case it has default value specified](https://github.com/clicon/clixon/issues/563)
|
* Fixed: [string length validation doesn't work for the entry "" in case it has default value specified](https://github.com/clicon/clixon/issues/563)
|
||||||
|
|
|
||||||
|
|
@ -1197,6 +1197,11 @@ xp_eval(xp_ctx *xc,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
break;
|
break;
|
||||||
|
case XPATHFN_RE_MATCH:
|
||||||
|
if (xp_function_re_match(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
break;
|
||||||
case XPATHFN_DEREF:
|
case XPATHFN_DEREF:
|
||||||
if (xp_function_deref(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
if (xp_function_deref(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1232,8 +1237,43 @@ xp_eval(xp_ctx *xc,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
break;
|
break;
|
||||||
|
case XPATHFN_STRING:
|
||||||
|
if (xp_function_string(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
break;
|
||||||
|
case XPATHFN_STARTS_WITH:
|
||||||
|
if (xp_function_contains(xc, xs->xs_c0, nsc, 1, localonly, xrp) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
break;
|
||||||
case XPATHFN_CONTAINS:
|
case XPATHFN_CONTAINS:
|
||||||
if (xp_function_contains(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
if (xp_function_contains(xc, xs->xs_c0, nsc, 0, localonly, xrp) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
break;
|
||||||
|
case XPATHFN_SUBSTRING_BEFORE:
|
||||||
|
if (xp_function_substring_str(xc, xs->xs_c0, nsc, 1, localonly, xrp) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
break;
|
||||||
|
case XPATHFN_SUBSTRING_AFTER:
|
||||||
|
if (xp_function_substring_str(xc, xs->xs_c0, nsc, 0, localonly, xrp) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
break;
|
||||||
|
case XPATHFN_SUBSTRING:
|
||||||
|
if (xp_function_substring(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
break;
|
||||||
|
case XPATHFN_STRING_LENGTH:
|
||||||
|
if (xp_function_string_length(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
break;
|
||||||
|
case XPATHFN_TRANSLATE:
|
||||||
|
if (xp_function_translate(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
#include "clixon_debug.h"
|
#include "clixon_debug.h"
|
||||||
#include "clixon_options.h"
|
#include "clixon_options.h"
|
||||||
|
#include "clixon_regex.h"
|
||||||
#include "clixon_yang_type.h"
|
#include "clixon_yang_type.h"
|
||||||
#include "clixon_xml_map.h"
|
#include "clixon_xml_map.h"
|
||||||
#include "clixon_yang_module.h"
|
#include "clixon_yang_module.h"
|
||||||
|
|
@ -82,7 +83,7 @@
|
||||||
static const map_str2int xpath_fnname_map[] = { /* alphabetic order */
|
static const map_str2int xpath_fnname_map[] = { /* alphabetic order */
|
||||||
{"bit-is-set", XPATHFN_BIT_IS_SET},
|
{"bit-is-set", XPATHFN_BIT_IS_SET},
|
||||||
{"boolean", XPATHFN_BOOLEAN},
|
{"boolean", XPATHFN_BOOLEAN},
|
||||||
{"eiling", XPATHFN_CEILING},
|
{"ceiling", XPATHFN_CEILING},
|
||||||
{"comment", XPATHFN_COMMENT},
|
{"comment", XPATHFN_COMMENT},
|
||||||
{"concat", XPATHFN_CONCAT},
|
{"concat", XPATHFN_CONCAT},
|
||||||
{"contains", XPATHFN_CONTAINS},
|
{"contains", XPATHFN_CONTAINS},
|
||||||
|
|
@ -110,6 +111,7 @@ static const map_str2int xpath_fnname_map[] = { /* alphabetic order */
|
||||||
{"round", XPATHFN_ROUND},
|
{"round", XPATHFN_ROUND},
|
||||||
{"starts-with", XPATHFN_STARTS_WITH},
|
{"starts-with", XPATHFN_STARTS_WITH},
|
||||||
{"string", XPATHFN_STRING},
|
{"string", XPATHFN_STRING},
|
||||||
|
{"string-length", XPATHFN_STRING_LENGTH},
|
||||||
{"substring", XPATHFN_SUBSTRING},
|
{"substring", XPATHFN_SUBSTRING},
|
||||||
{"substring-after", XPATHFN_SUBSTRING_AFTER},
|
{"substring-after", XPATHFN_SUBSTRING_AFTER},
|
||||||
{"substring-before", XPATHFN_SUBSTRING_BEFORE},
|
{"substring-before", XPATHFN_SUBSTRING_BEFORE},
|
||||||
|
|
@ -136,6 +138,17 @@ xp_fnname_int2str(enum clixon_xpath_function code)
|
||||||
return clicon_int2str(xpath_fnname_map, code);
|
return clicon_int2str(xpath_fnname_map, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Returns a node set with the initial context node as its only member.
|
||||||
|
*
|
||||||
|
* @param[in] xc0 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 RFC 7950 10.1.1
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
xp_function_current(xp_ctx *xc0,
|
xp_function_current(xp_ctx *xc0,
|
||||||
struct xpath_tree *xs,
|
struct xpath_tree *xs,
|
||||||
|
|
@ -162,6 +175,117 @@ xp_function_current(xp_ctx *xc0,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Returns "true" if the "subject" string matches the regular expression "pattern";
|
||||||
|
*
|
||||||
|
* @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 RFC 7950 10.2.1
|
||||||
|
* @note Uses xml2 regexp if libxml2 enabled, otherwise posix
|
||||||
|
* This means for xml2, you have to configure BOTH cligen and clixon with --with-libxml2
|
||||||
|
* @note Compiling regexp takes a lot of resources, no caching is made of re here
|
||||||
|
* as is done for eg YANG patterns
|
||||||
|
* Example: re-match("1.22.333", "\d{1,3}\.\d{1,3}\.\d{1,3}") returns true
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xp_function_re_match(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 *regexp = NULL;
|
||||||
|
char *posix = NULL;
|
||||||
|
void *re = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (xs == NULL || xs->xs_c0 == NULL || xs->xs_c1 == NULL){
|
||||||
|
clixon_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, ®exp) < 0)
|
||||||
|
goto done;
|
||||||
|
#ifdef HAVE_LIBXML2
|
||||||
|
if ((ret = cligen_regex_libxml2_compile(regexp, &re)) < 0)
|
||||||
|
goto done;
|
||||||
|
#else
|
||||||
|
if (regexp_xsd2posix(regexp, &posix) < 0)
|
||||||
|
goto done;
|
||||||
|
if ((ret = cligen_regex_posix_compile(posix, &re)) < 0)
|
||||||
|
goto done;
|
||||||
|
#endif
|
||||||
|
if (ret == 0){
|
||||||
|
clixon_err(OE_YANG, 0, "regexp compile fail: \"%s\"", regexp);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
// s0 = "1.22.333";
|
||||||
|
#ifdef HAVE_LIBXML2
|
||||||
|
if ((ret = cligen_regex_libxml2_exec(re, s0)) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
#else
|
||||||
|
if ((ret = cligen_regex_posix_exec(re, s0)) < 0)
|
||||||
|
goto done;
|
||||||
|
#endif
|
||||||
|
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "malloc");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
memset(xr, 0, sizeof(*xr));
|
||||||
|
xr->xc_type = XT_BOOL;
|
||||||
|
xr->xc_bool = ret;
|
||||||
|
*xrp = xr;
|
||||||
|
xr = NULL;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (re){
|
||||||
|
#ifdef HAVE_LIBXML2
|
||||||
|
cligen_regex_libxml2_free(re);
|
||||||
|
#else
|
||||||
|
cligen_regex_posix_free(re);
|
||||||
|
free(re);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (xr0)
|
||||||
|
ctx_free(xr0);
|
||||||
|
if (xr1)
|
||||||
|
ctx_free(xr1);
|
||||||
|
if (s0)
|
||||||
|
free(s0);
|
||||||
|
if (regexp)
|
||||||
|
free(regexp);
|
||||||
|
if (posix)
|
||||||
|
free(posix);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Follows reference defined by the first node and returns nodes it refers to
|
||||||
|
*
|
||||||
|
* @param[in] xc0 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 RFC 7950 10.3.1
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
xp_function_deref(xp_ctx *xc0,
|
xp_function_deref(xp_ctx *xc0,
|
||||||
struct xpath_tree *xs,
|
struct xpath_tree *xs,
|
||||||
|
|
@ -316,7 +440,7 @@ derived_from_one(char *baseidentity,
|
||||||
* @param[out] xrp Resulting context
|
* @param[out] xrp Resulting context
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @see rfc7950 10.4.1
|
* @see RFC7950 10.4.1
|
||||||
* Returns "true" if any node in the argument "nodes" is a node of type "identityref" and its
|
* Returns "true" if any node in the argument "nodes" is a node of type "identityref" and its
|
||||||
* value is an identity that is derived from (see Section 7.18.2) the identity "identity"
|
* value is an identity that is derived from (see Section 7.18.2) the identity "identity"
|
||||||
* boolean derived-from(node-set nodes, string identity)
|
* boolean derived-from(node-set nodes, string identity)
|
||||||
|
|
@ -542,12 +666,12 @@ xp_function_name(xp_ctx *xc,
|
||||||
int localonly,
|
int localonly,
|
||||||
xp_ctx **xrp)
|
xp_ctx **xrp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
xp_ctx *xr = NULL;
|
xp_ctx *xr = NULL;
|
||||||
xp_ctx *xr0 = NULL;
|
xp_ctx *xr0 = NULL;
|
||||||
char *s0 = NULL;
|
char *s0 = NULL;
|
||||||
int i;
|
int i;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
|
|
||||||
if (xs == NULL || xs->xs_c0 == NULL){
|
if (xs == NULL || xs->xs_c0 == NULL){
|
||||||
clixon_err(OE_XML, EINVAL, "not expects but did not get one argument");
|
clixon_err(OE_XML, EINVAL, "not expects but did not get one argument");
|
||||||
|
|
@ -583,7 +707,7 @@ xp_function_name(xp_ctx *xc,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Eval xpath function contains
|
/*! Eval xpath function converts an object to a string
|
||||||
*
|
*
|
||||||
* @param[in] xc Incoming context
|
* @param[in] xc Incoming context
|
||||||
* @param[in] xs XPath node tree
|
* @param[in] xs XPath node tree
|
||||||
|
|
@ -595,18 +719,75 @@ xp_function_name(xp_ctx *xc,
|
||||||
* @see https://www.w3.org/TR/xpath-10/#NT-FunctionName 4.2 String Functions
|
* @see https://www.w3.org/TR/xpath-10/#NT-FunctionName 4.2 String Functions
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
xp_function_string(xp_ctx *xc,
|
||||||
|
struct xpath_tree *xs,
|
||||||
|
cvec *nsc,
|
||||||
|
int localonly,
|
||||||
|
xp_ctx **xrp)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
xp_ctx *xr0 = NULL;
|
||||||
|
xp_ctx *xr = NULL;
|
||||||
|
char *s0 = NULL;
|
||||||
|
|
||||||
|
if (xs != NULL && xs->xs_c0){
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((s0 = strdup("")) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "malloc");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
memset(xr, 0, sizeof(*xr));
|
||||||
|
xr->xc_type = XT_STRING;
|
||||||
|
xr->xc_string = s0;
|
||||||
|
s0 = NULL;
|
||||||
|
*xrp = xr;
|
||||||
|
xr = NULL;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xr0)
|
||||||
|
ctx_free(xr0);
|
||||||
|
if (s0)
|
||||||
|
free(s0);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Eval xpath function contains sub-string
|
||||||
|
*
|
||||||
|
* @param[in] xc Incoming context
|
||||||
|
* @param[in] xs XPath node tree
|
||||||
|
* @param[in] nsc XML Namespace context
|
||||||
|
* @param[in] starts 0: contains, 1: starts with
|
||||||
|
* @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,
|
xp_function_contains(xp_ctx *xc,
|
||||||
struct xpath_tree *xs,
|
struct xpath_tree *xs,
|
||||||
cvec *nsc,
|
cvec *nsc,
|
||||||
|
int starts,
|
||||||
int localonly,
|
int localonly,
|
||||||
xp_ctx **xrp)
|
xp_ctx **xrp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
xp_ctx *xr0 = NULL;
|
xp_ctx *xr0 = NULL;
|
||||||
xp_ctx *xr1 = NULL;
|
xp_ctx *xr1 = NULL;
|
||||||
xp_ctx *xr = NULL;
|
xp_ctx *xr = NULL;
|
||||||
char *s0 = NULL;
|
char *s0 = NULL;
|
||||||
char *s1 = NULL;
|
char *s1 = NULL;
|
||||||
|
|
||||||
if (xs == NULL || xs->xs_c0 == NULL || xs->xs_c1 == NULL){
|
if (xs == NULL || xs->xs_c0 == NULL || xs->xs_c1 == NULL){
|
||||||
clixon_err(OE_XML, EINVAL, "contains expects but did not get two arguments");
|
clixon_err(OE_XML, EINVAL, "contains expects but did not get two arguments");
|
||||||
|
|
@ -627,7 +808,10 @@ xp_function_contains(xp_ctx *xc,
|
||||||
}
|
}
|
||||||
memset(xr, 0, sizeof(*xr));
|
memset(xr, 0, sizeof(*xr));
|
||||||
xr->xc_type = XT_BOOL;
|
xr->xc_type = XT_BOOL;
|
||||||
xr->xc_bool = (strstr(s0, s1) != NULL);
|
if (starts)
|
||||||
|
xr->xc_bool = (strncmp(s0, s1, strlen(s1)) == 0);
|
||||||
|
else
|
||||||
|
xr->xc_bool = (strstr(s0, s1) != NULL);
|
||||||
*xrp = xr;
|
*xrp = xr;
|
||||||
xr = NULL;
|
xr = NULL;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -643,6 +827,363 @@ xp_function_contains(xp_ctx *xc,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Eval xpath function contains sub-string
|
||||||
|
*
|
||||||
|
* @param[in] xc Incoming context
|
||||||
|
* @param[in] xs XPath node tree
|
||||||
|
* @param[in] nsc XML Namespace context
|
||||||
|
* @param[in] before 0:Return substring after, 1: before
|
||||||
|
* @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
|
||||||
|
* Example: substring-before("1999/04/01","/") returns "1999"
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xp_function_substring_str(xp_ctx *xc,
|
||||||
|
struct xpath_tree *xs,
|
||||||
|
cvec *nsc,
|
||||||
|
int before,
|
||||||
|
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;
|
||||||
|
char *sp;
|
||||||
|
|
||||||
|
if (xs == NULL || xs->xs_c0 == NULL || xs->xs_c1 == NULL){
|
||||||
|
clixon_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){
|
||||||
|
clixon_err(OE_UNIX, errno, "malloc");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
memset(xr, 0, sizeof(*xr));
|
||||||
|
xr->xc_type = XT_STRING;
|
||||||
|
sp = strstr(s0, s1);
|
||||||
|
if (before) {
|
||||||
|
if (sp != NULL)
|
||||||
|
*sp = '\0';
|
||||||
|
else
|
||||||
|
*s0 = '\0';
|
||||||
|
if ((xr->xc_string = strdup(s0)) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (sp)
|
||||||
|
sp += strlen(s1);
|
||||||
|
if (sp == NULL)
|
||||||
|
sp = "";
|
||||||
|
if ((xr->xc_string = strdup(sp)) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Eval xpath function return substring
|
||||||
|
*
|
||||||
|
* @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_substring(xp_ctx *xc,
|
||||||
|
struct xpath_tree *xs,
|
||||||
|
cvec *nsc,
|
||||||
|
int localonly,
|
||||||
|
xp_ctx **xrp)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct xpath_tree *a0;
|
||||||
|
struct xpath_tree *a1;
|
||||||
|
struct xpath_tree *a2;
|
||||||
|
xp_ctx *xr0 = NULL;
|
||||||
|
xp_ctx *xr1 = NULL;
|
||||||
|
xp_ctx *xr2 = NULL;
|
||||||
|
xp_ctx *xr = NULL;
|
||||||
|
char *s0 = NULL;
|
||||||
|
char *s0p;
|
||||||
|
double d1;
|
||||||
|
double d2;
|
||||||
|
int32_t i1;
|
||||||
|
int32_t i10;
|
||||||
|
int32_t i2;
|
||||||
|
int32_t it;
|
||||||
|
|
||||||
|
if (xs == NULL || xs->xs_c0 == NULL || xs->xs_c1 == NULL){
|
||||||
|
clixon_err(OE_XML, EINVAL, "contains expects but did not get two arguments");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (xs->xs_c0->xs_c1 != NULL){
|
||||||
|
a0 = xs->xs_c0->xs_c0;
|
||||||
|
a1 = xs->xs_c0->xs_c1;
|
||||||
|
a2 = xs->xs_c1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
a0 = xs->xs_c0;
|
||||||
|
a1 = xs->xs_c1;
|
||||||
|
a2 = NULL;
|
||||||
|
}
|
||||||
|
/* contains two arguments in xs: boolean contains(string, string) */
|
||||||
|
if (xp_eval(xc, a0, nsc, localonly, &xr0) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ctx2string(xr0, &s0) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xp_eval(xc, a1, nsc, localonly, &xr1) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ctx2number(xr1, &d1) < 0)
|
||||||
|
goto done;
|
||||||
|
if ((i10 = round(d1)-1) < 0)
|
||||||
|
i1 = 0;
|
||||||
|
else
|
||||||
|
i1 = i10;
|
||||||
|
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "malloc");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
memset(xr, 0, sizeof(*xr));
|
||||||
|
xr->xc_type = XT_STRING;
|
||||||
|
if (i1 < strlen(s0))
|
||||||
|
s0p = &s0[i1];
|
||||||
|
else
|
||||||
|
s0p = "";
|
||||||
|
if (a2) {
|
||||||
|
if (xp_eval(xc, a2, nsc, localonly, &xr2) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ctx2number(xr2, &d2) < 0)
|
||||||
|
goto done;
|
||||||
|
if ((i2 = round(d2)) < 0)
|
||||||
|
i2 = 0;
|
||||||
|
it = i10+i2;
|
||||||
|
if (it < (int)strlen(s0)){
|
||||||
|
if (it < 0)
|
||||||
|
*s0p = '\0';
|
||||||
|
else
|
||||||
|
*(s0+i10+i2) = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((xr->xc_string = strdup(s0p)) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
*xrp = xr;
|
||||||
|
xr = NULL;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xr0)
|
||||||
|
ctx_free(xr0);
|
||||||
|
if (xr1)
|
||||||
|
ctx_free(xr1);
|
||||||
|
if (xr2)
|
||||||
|
ctx_free(xr2);
|
||||||
|
if (s0)
|
||||||
|
free(s0);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Eval xpath function returns the number of characters in the string
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* XXX Dont know how to implement string-length() without arg
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xp_function_string_length(xp_ctx *xc,
|
||||||
|
struct xpath_tree *xs,
|
||||||
|
cvec *nsc,
|
||||||
|
int localonly,
|
||||||
|
xp_ctx **xrp)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
xp_ctx *xr0 = NULL;
|
||||||
|
xp_ctx *xr = NULL;
|
||||||
|
char *s0 = NULL;
|
||||||
|
|
||||||
|
if (xs != NULL && xs->xs_c0){
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ((s0 = strdup("")) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "malloc");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
memset(xr, 0, sizeof(*xr));
|
||||||
|
xr->xc_type = XT_NUMBER;
|
||||||
|
xr->xc_number = strlen(s0);
|
||||||
|
s0 = NULL;
|
||||||
|
*xrp = xr;
|
||||||
|
xr = NULL;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xr0)
|
||||||
|
ctx_free(xr0);
|
||||||
|
if (s0)
|
||||||
|
free(s0);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Eval xpath function replaces characters in the first string
|
||||||
|
*
|
||||||
|
* Returns the first argument string with occurrences of characters in the second argument string
|
||||||
|
* replaced by the character at the corresponding position in the third argument string.
|
||||||
|
* @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_translate(xp_ctx *xc,
|
||||||
|
struct xpath_tree *xs,
|
||||||
|
cvec *nsc,
|
||||||
|
int localonly,
|
||||||
|
xp_ctx **xrp)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct xpath_tree *a0;
|
||||||
|
struct xpath_tree *a1;
|
||||||
|
struct xpath_tree *a2;
|
||||||
|
xp_ctx *xr0 = NULL;
|
||||||
|
xp_ctx *xr1 = NULL;
|
||||||
|
xp_ctx *xr2 = NULL;
|
||||||
|
xp_ctx *xr = NULL;
|
||||||
|
char *s0 = NULL;
|
||||||
|
char *s1 = NULL;
|
||||||
|
char *s2 = NULL;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
char *p1;
|
||||||
|
char ch;
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (xs == NULL || xs->xs_c0 == NULL || xs->xs_c1 == NULL){
|
||||||
|
clixon_err(OE_XML, EINVAL, "contains expects but did not get two arguments");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (xs->xs_c0->xs_c1 != NULL){
|
||||||
|
a0 = xs->xs_c0->xs_c0;
|
||||||
|
a1 = xs->xs_c0->xs_c1;
|
||||||
|
a2 = xs->xs_c1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
a0 = xs->xs_c0;
|
||||||
|
a1 = xs->xs_c1;
|
||||||
|
a2 = NULL;
|
||||||
|
}
|
||||||
|
/* contains two arguments in xs: boolean contains(string, string) */
|
||||||
|
if (xp_eval(xc, a0, nsc, localonly, &xr0) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ctx2string(xr0, &s0) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xp_eval(xc, a1, nsc, localonly, &xr1) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ctx2string(xr1, &s1) < 0)
|
||||||
|
goto done;
|
||||||
|
if (a2) {
|
||||||
|
if (xp_eval(xc, a2, nsc, localonly, &xr2) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ctx2string(xr2, &s2) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "malloc");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
memset(xr, 0, sizeof(*xr));
|
||||||
|
xr->xc_type = XT_STRING;
|
||||||
|
if ((cb = cbuf_new()) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
for (i=0; i<strlen(s0); i++){
|
||||||
|
ch = s0[i];
|
||||||
|
if ((p1 = strchr(s1, s0[i])) != NULL){
|
||||||
|
j = p1 - s1;
|
||||||
|
if (j < strlen(s2))
|
||||||
|
cprintf(cb, "%c", s2[j]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cprintf(cb, "%c", ch);
|
||||||
|
}
|
||||||
|
if ((xr->xc_string = strdup(cbuf_get(cb))) == NULL){
|
||||||
|
clixon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
*xrp = xr;
|
||||||
|
xr = NULL;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xr0)
|
||||||
|
ctx_free(xr0);
|
||||||
|
if (xr1)
|
||||||
|
ctx_free(xr1);
|
||||||
|
if (xr2)
|
||||||
|
ctx_free(xr2);
|
||||||
|
if (s0)
|
||||||
|
free(s0);
|
||||||
|
if (s1)
|
||||||
|
free(s1);
|
||||||
|
if (s2)
|
||||||
|
free(s2);
|
||||||
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! The boolean function converts its argument to a boolean
|
/*! The boolean function converts its argument to a boolean
|
||||||
*
|
*
|
||||||
* Conversion is as follows:
|
* Conversion is as follows:
|
||||||
|
|
|
||||||
|
|
@ -42,13 +42,13 @@
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* XPath functions from Xpath 1.0 spec or YANG
|
* XPath functions from XPath 1.0 spec or YANG
|
||||||
* @see xp_eval,xp_primary_function where they are parsed and checked
|
* @see xp_eval,xp_primary_function where they are parsed and checked
|
||||||
* @see clixon_xpath_function.ch] for implementation
|
* @see clixon_xpath_function.ch for implementation
|
||||||
*/
|
*/
|
||||||
enum clixon_xpath_function{
|
enum clixon_xpath_function{
|
||||||
XPATHFN_CURRENT, /* RFC 7950 10.1.1 */
|
XPATHFN_CURRENT, /* RFC 7950 10.1.1 */
|
||||||
XPATHFN_RE_MATCH, /* RFC 7950 10.2.1 NYI */
|
XPATHFN_RE_MATCH, /* RFC 7950 10.2.1 */
|
||||||
XPATHFN_DEREF, /* RFC 7950 10.3.1 */
|
XPATHFN_DEREF, /* RFC 7950 10.3.1 */
|
||||||
XPATHFN_DERIVED_FROM, /* RFC 7950 10.4.1 */
|
XPATHFN_DERIVED_FROM, /* RFC 7950 10.4.1 */
|
||||||
XPATHFN_DERIVED_FROM_OR_SELF, /* RFC 7950 10.4.2 */
|
XPATHFN_DERIVED_FROM_OR_SELF, /* RFC 7950 10.4.2 */
|
||||||
|
|
@ -61,16 +61,16 @@ enum clixon_xpath_function{
|
||||||
XPATHFN_LOCAL_NAME, /* 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_NAMESPACE_URI, /* XPATH 1.0 4.1 NYI */
|
||||||
XPATHFN_NAME, /* XPATH 1.0 4.1 */
|
XPATHFN_NAME, /* XPATH 1.0 4.1 */
|
||||||
XPATHFN_STRING, /* XPATH 1.0 4.2 NYI */
|
XPATHFN_STRING, /* XPATH 1.0 4.2 */
|
||||||
XPATHFN_CONCAT, /* XPATH 1.0 4.2 NYI */
|
XPATHFN_CONCAT, /* XPATH 1.0 4.2 NYI */
|
||||||
XPATHFN_STARTS_WITH, /* XPATH 1.0 4.2 NYI */
|
XPATHFN_STARTS_WITH, /* XPATH 1.0 4.2 */
|
||||||
XPATHFN_CONTAINS, /* XPATH 1.0 4.2 */
|
XPATHFN_CONTAINS, /* XPATH 1.0 4.2 */
|
||||||
XPATHFN_SUBSTRING_BEFORE, /* XPATH 1.0 4.2 NYI */
|
XPATHFN_SUBSTRING_BEFORE, /* XPATH 1.0 4.2 */
|
||||||
XPATHFN_SUBSTRING_AFTER, /* XPATH 1.0 4.2 NYI */
|
XPATHFN_SUBSTRING_AFTER, /* XPATH 1.0 4.2 */
|
||||||
XPATHFN_SUBSTRING, /* XPATH 1.0 4.2 NYI */
|
XPATHFN_SUBSTRING, /* XPATH 1.0 4.2 */
|
||||||
XPATHFN_STRING_LENGTH, /* XPATH 1.0 4.2 NYI */
|
XPATHFN_STRING_LENGTH, /* XPATH 1.0 4.2 */
|
||||||
XPATHFN_NORMALIZE_SPACE, /* XPATH 1.0 4.2 NYI */
|
XPATHFN_NORMALIZE_SPACE, /* XPATH 1.0 4.2 NYI */
|
||||||
XPATHFN_TRANSLATE, /* XPATH 1.0 4.2 NYI */
|
XPATHFN_TRANSLATE, /* XPATH 1.0 4.2 */
|
||||||
XPATHFN_BOOLEAN, /* XPATH 1.0 4.3 */
|
XPATHFN_BOOLEAN, /* XPATH 1.0 4.3 */
|
||||||
XPATHFN_NOT, /* XPATH 1.0 4.3 */
|
XPATHFN_NOT, /* XPATH 1.0 4.3 */
|
||||||
XPATHFN_TRUE, /* XPATH 1.0 4.3 */
|
XPATHFN_TRUE, /* XPATH 1.0 4.3 */
|
||||||
|
|
@ -94,13 +94,19 @@ int xp_fnname_str2int(char *fnname);
|
||||||
const char *xp_fnname_int2str(enum clixon_xpath_function code);
|
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_current(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||||
|
int xp_function_re_match(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_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_bit_is_set(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
int xp_function_bit_is_set(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||||
int xp_function_position(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
int xp_function_position(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||||
int xp_function_count(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, 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_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_string(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 starts, int localonly, xp_ctx **xrp);
|
||||||
|
int xp_function_substring_str(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int before, int localonly, xp_ctx **xrp);
|
||||||
|
int xp_function_substring(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||||
|
int xp_function_string_length(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||||
|
int xp_function_translate(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||||
int xp_function_boolean(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
int xp_function_boolean(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);
|
int xp_function_not(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||||
int xp_function_true(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
int xp_function_true(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||||
|
|
|
||||||
|
|
@ -251,21 +251,13 @@ xp_primary_function(clixon_xpath_yacc *xpy,
|
||||||
}
|
}
|
||||||
fn = ret;
|
fn = ret;
|
||||||
switch (fn){
|
switch (fn){
|
||||||
case XPATHFN_RE_MATCH: /* Group of NOT IMPLEMENTED xpath functions */
|
case XPATHFN_ENUM_VALUE: /* Group of NOT IMPLEMENTED xpath functions */
|
||||||
case XPATHFN_ENUM_VALUE:
|
|
||||||
case XPATHFN_LAST:
|
case XPATHFN_LAST:
|
||||||
case XPATHFN_ID:
|
case XPATHFN_ID:
|
||||||
case XPATHFN_LOCAL_NAME:
|
case XPATHFN_LOCAL_NAME:
|
||||||
case XPATHFN_NAMESPACE_URI:
|
case XPATHFN_NAMESPACE_URI:
|
||||||
case XPATHFN_STRING:
|
|
||||||
case XPATHFN_CONCAT:
|
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_NORMALIZE_SPACE:
|
||||||
case XPATHFN_TRANSLATE:
|
|
||||||
case XPATHFN_LANG:
|
case XPATHFN_LANG:
|
||||||
case XPATHFN_NUMBER:
|
case XPATHFN_NUMBER:
|
||||||
case XPATHFN_SUM:
|
case XPATHFN_SUM:
|
||||||
|
|
@ -280,7 +272,8 @@ xp_primary_function(clixon_xpath_yacc *xpy,
|
||||||
clixon_xpath_parseerror(xpy, cbuf_get(cb));
|
clixon_xpath_parseerror(xpy, cbuf_get(cb));
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
case XPATHFN_CURRENT: /* Group of implemented xpath functions */
|
case XPATHFN_RE_MATCH: /* Group of implemented xpath functions */
|
||||||
|
case XPATHFN_CURRENT:
|
||||||
case XPATHFN_DEREF:
|
case XPATHFN_DEREF:
|
||||||
case XPATHFN_DERIVED_FROM:
|
case XPATHFN_DERIVED_FROM:
|
||||||
case XPATHFN_BIT_IS_SET:
|
case XPATHFN_BIT_IS_SET:
|
||||||
|
|
@ -288,7 +281,14 @@ xp_primary_function(clixon_xpath_yacc *xpy,
|
||||||
case XPATHFN_POSITION:
|
case XPATHFN_POSITION:
|
||||||
case XPATHFN_COUNT:
|
case XPATHFN_COUNT:
|
||||||
case XPATHFN_NAME:
|
case XPATHFN_NAME:
|
||||||
|
case XPATHFN_STRING:
|
||||||
|
case XPATHFN_STARTS_WITH:
|
||||||
case XPATHFN_CONTAINS:
|
case XPATHFN_CONTAINS:
|
||||||
|
case XPATHFN_SUBSTRING_BEFORE:
|
||||||
|
case XPATHFN_SUBSTRING_AFTER:
|
||||||
|
case XPATHFN_SUBSTRING:
|
||||||
|
case XPATHFN_STRING_LENGTH:
|
||||||
|
case XPATHFN_TRANSLATE:
|
||||||
case XPATHFN_BOOLEAN:
|
case XPATHFN_BOOLEAN:
|
||||||
case XPATHFN_NOT:
|
case XPATHFN_NOT:
|
||||||
case XPATHFN_TRUE:
|
case XPATHFN_TRUE:
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# XPATH tests
|
# Basic XPATH tests
|
||||||
|
# See also test_xpath_functions.sh for XPaths with YANG conditionals
|
||||||
# Some XPATH cases clixon cannot handle
|
# Some XPATH cases clixon cannot handle
|
||||||
# - /aaa/bbb/comment, where "comment" is nodetype
|
# - /aaa/bbb/comment, where "comment" is nodetype
|
||||||
# - //b*, combinations of // and "*"
|
# - //b*, combinations of // and "*"
|
||||||
# For more (outdated info): https://github.com/clicon/clixon/issues/54
|
|
||||||
# Test has three parts:
|
# Test has three parts:
|
||||||
# - Only XML no YANG
|
# - Only XML no YANG
|
||||||
# - negative tests with YANG
|
# - negative tests with YANG
|
||||||
|
|
@ -23,7 +23,6 @@ xmlfn=$dir/xmlfn.xml
|
||||||
|
|
||||||
fyang=$dir/clixon-example.yang
|
fyang=$dir/clixon-example.yang
|
||||||
|
|
||||||
|
|
||||||
cat <<EOF > $xml
|
cat <<EOF > $xml
|
||||||
<aaa>
|
<aaa>
|
||||||
<bbb x="hello">
|
<bbb x="hello">
|
||||||
|
|
@ -71,6 +70,8 @@ cat <<EOF > $xml2
|
||||||
<ifType>ethernet</ifType>
|
<ifType>ethernet</ifType>
|
||||||
<ifMTU>1500</ifMTU>
|
<ifMTU>1500</ifMTU>
|
||||||
<namespace>urn:example:foo</namespace>
|
<namespace>urn:example:foo</namespace>
|
||||||
|
<mylength>7</mylength>
|
||||||
|
<myaddr>10.22.33.44</myaddr>
|
||||||
</bbb>
|
</bbb>
|
||||||
</aaa>
|
</aaa>
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -167,7 +168,6 @@ expectpart "$($clixon_util_xpath -D $DBG -f $xml -p aaa)" 0 "^nodeset:0:<aaa><bb
|
||||||
new "xpath /bbb"
|
new "xpath /bbb"
|
||||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p /bbb)" 0 "^nodeset:$"
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p /bbb)" 0 "^nodeset:$"
|
||||||
|
|
||||||
|
|
||||||
new "xpath /aaa/bbb"
|
new "xpath /aaa/bbb"
|
||||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p /aaa/bbb)" 0 "^0:<bbb x=\"hello\"><ccc>42</ccc></bbb>
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p /aaa/bbb)" 0 "^0:<bbb x=\"hello\"><ccc>42</ccc></bbb>
|
||||||
1:<bbb x=\"bye\"><ccc>99</ccc></bbb>$"
|
1:<bbb x=\"bye\"><ccc>99</ccc></bbb>$"
|
||||||
|
|
@ -309,8 +309,112 @@ expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p 'derived-from(../../change-
|
||||||
new "xpath derived-from-or-self"
|
new "xpath derived-from-or-self"
|
||||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p 'derived-from-or-self(../../change-operation,"modify")')" 0 "bool:false"
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p 'derived-from-or-self(../../change-operation,"modify")')" 0 "bool:false"
|
||||||
|
|
||||||
new "xpath contains"
|
# re-match
|
||||||
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "contains(../../objectClass,'BTSFunction') or contains(../../objectClass,'RNCFunction')")" 0 "bool:false"
|
new "xpath re-match match true" # example from rfc 7950
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p 're-match("1.22.333", "\d{1,3}\.\d{1,3}\.\d{1,3}")')" 0 "bool:true"
|
||||||
|
|
||||||
|
new "xpath re-match match path"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p 're-match(aaa/bbb/myaddr, "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")')" 0 "bool:true"
|
||||||
|
|
||||||
|
new "xpath re-match match path fail"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p 're-match(aaa/bbb/myaddr, "\d{1,3}\.\d{1,3}\.\d{1,3}")')" 0 "bool:false"
|
||||||
|
|
||||||
|
# string
|
||||||
|
new "xpath string empty"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "string()")" 0 "string:$"
|
||||||
|
|
||||||
|
new "xpath string path"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml4 -p "string(root/y/a)")" 0 "string:222$"
|
||||||
|
|
||||||
|
# starts-with
|
||||||
|
new "xpath starts-with true"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "starts-with('kalle','kal')")" 0 "bool:true"
|
||||||
|
|
||||||
|
new "xpath starts-with false"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "starts-with('kalle','all')")" 0 "bool:false"
|
||||||
|
|
||||||
|
new "xpath starts-with empty"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "starts-with('kalle','')")" 0 "bool:true"
|
||||||
|
|
||||||
|
new "xpath starts-with too long"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "starts-with('kalle','kalle42')")" 0 "bool:false"
|
||||||
|
|
||||||
|
new "xpath contains direct strings true"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "contains('kalle','all')")" 0 "bool:true"
|
||||||
|
|
||||||
|
new "xpath contains direct strings false"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "contains('kalle','ball')")" 0 "bool:false"
|
||||||
|
|
||||||
|
new "xpath contains path true"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "contains(aaa/bbb/namespace,aaa/name)")" 0 "bool:true"
|
||||||
|
|
||||||
|
new "xpath contains path false"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "contains(aaa/bbb/ifMTU,aaa/name)")" 0 "bool:false"
|
||||||
|
|
||||||
|
# substring-before / after
|
||||||
|
new "xpath substring-before 1"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring-before(\"1999/04/01\",\"/\")")" 0 "string:1999" --not-- "1999/"
|
||||||
|
|
||||||
|
new "xpath substring-before 2"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring-before(\"1999/04/01\",\"04\")")" 0 "string:1999/" --not-- "04"
|
||||||
|
|
||||||
|
new "xpath substring-before 3"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring-before(\"1999/04/01\",\"zzz\")")" 0 "string:" --not-- "string:1"
|
||||||
|
|
||||||
|
new "xpath substring-after 1"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring-after(\"1999/04/01\",\"/\")")" 0 "string:04/01"
|
||||||
|
|
||||||
|
new "xpath substring-after 2"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring-after(\"1999/04/01\",\"19\")")" 0 "string:99/04/01"
|
||||||
|
|
||||||
|
new "xpath substring-after 3"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring-after(\"1999/04/01\",\"z\")")" 0 "string:" --not-- "1999"
|
||||||
|
|
||||||
|
# substring, see examples in https://www.w3.org/TR/xpath-10/ Sections 4.2
|
||||||
|
new "xpath substring 1"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring(\"12345\",2,3)")" 0 "string:234" --not-- "45"
|
||||||
|
|
||||||
|
new "xpath substring 2"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring(\"12345\",1.5,2.6)")" 0 "string:234" --not-- "45"
|
||||||
|
|
||||||
|
new "xpath substring 3"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring(\"12345\",0,3)")" 0 "string:12" --not-- "123"
|
||||||
|
|
||||||
|
new "xpath substring 4"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring(\"12345\",0 div 0,3)")" 0 "string:" --not-- "12"
|
||||||
|
|
||||||
|
new "xpath substring 5"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring(\"12345\",1, 0 div 0)")" 0 "string:" --not-- "12"
|
||||||
|
|
||||||
|
# XXX cornercase does not work
|
||||||
|
#new "xpath substring 6"
|
||||||
|
#expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring(\"12345\",-42, 1 div 0)")" 0 "string:12345"
|
||||||
|
|
||||||
|
new "xpath substring 7"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring(\"12345\",-1, 1 div 0)")" 0 "string:" --not-- "string:1"
|
||||||
|
|
||||||
|
new "xpath substring paths"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "substring(aaa/bbb/namespace,5,aaa/bbb/mylength)")" 0 "string:example" --not-- "example:"
|
||||||
|
|
||||||
|
# string-length
|
||||||
|
new "xpath string-length empty" # XXX without args not supported
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "string-length()")" 0 "number:0"
|
||||||
|
|
||||||
|
new "xpath string-length direct"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "string-length(\"12345\")")" 0 "number:5"
|
||||||
|
|
||||||
|
new "xpath string-length path"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "string-length(aaa/name)")" 0 "number:3"
|
||||||
|
|
||||||
|
# translate
|
||||||
|
new "xpath translate" # modified
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "translate(\"bar\", \"abc\",\"DEF\")")" 0 "string:EDr$"
|
||||||
|
|
||||||
|
new "xpath translate remove"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "translate(\"--aaa--\", \"abc-\",\"DEF\")")" 0 "string:DDD$"
|
||||||
|
|
||||||
|
new "xpath translate none"
|
||||||
|
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "translate(\"bar\", \"cde-\",\"fgh\")")" 0 "string:bar$"
|
||||||
|
|
||||||
# Nodetests
|
# Nodetests
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# test xpath functions within YANG conditionals
|
# test xpath functions within YANG conditionals
|
||||||
|
# For basic XPath tests see test_xpath.sh
|
||||||
# XPATH base https://www.w3.org/TR/xpath-10/
|
# XPATH base https://www.w3.org/TR/xpath-10/
|
||||||
# YANG XPATH functions: https://tools.ietf.org/html/rfc7950
|
# YANG XPATH functions: https://tools.ietf.org/html/rfc7950
|
||||||
# Test of xpath functions:
|
# Test of xpath functions:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue