* XPath parser: fixing lexical issues cornercases

* Some complexities in Section 3.7 Lexical Structure of XPath 1.0 spec
  * There used to be some cornercases where function-names could not be used as nodes
  * For example, `node()` is a nodetest, so `/node/` caused an error.
  * In the grammar these include: axisnames,  nodetests, functionnames
  * The NCNames vs functionnames is now impölemented according to the lexical structure section
This commit is contained in:
Olof hagsand 2022-04-05 12:11:16 +02:00
parent f1300d7a12
commit a51abd0063
9 changed files with 357 additions and 138 deletions

View file

@ -68,6 +68,12 @@ Users may have to change how they access the system
* Instead of removing YANG which is disabled by `if-feature`, replace it with an yang `anydata` node.
* This means XML specified by such YANG is ignored, and it is not an error to access it
* Note the similarity with `CLICON_YANG_UNKNOWN_ANYDATA`
* XPath parser: fixing lexical issues cornercases
* Some complexities in Section 3.7 Lexical Structure of XPath 1.0 spec
* There used to be some cornercases where function-names could not be used as nodes
* For example, `node()` is a nodetest, so `/node/` caused an error.
* In the grammar these include: axisnames, nodetests, functionnames
* The NCNames vs functionnames is now impölemented according to the lexical structure section
* [provide support for load config of cli format along with json and xml format as save config is supported for all 3 formats](https://github.com/clicon/clixon/issues/320)
* [prevent clixon-restconf@2021-05-20.yang module from loading](https://github.com/clicon/clixon/issues/318)
* Instead of always loading it, load it to datastore YANGs only if `CLICON_BACKEND_RESTCONF_PROCESS` is `true`

View file

@ -124,6 +124,7 @@ typedef struct xpath_tree xpath_tree;
* Prototypes
*/
char* axis_type_int2str(int axis_type);
int axis_type_str2int(char *name);
char* xpath_tree_int2str(int nodetype);
int xpath_tree_print_cb(cbuf *cb, xpath_tree *xs);
int xpath_tree_print(FILE *f, xpath_tree *xs);

View file

@ -160,6 +160,11 @@ axis_type_int2str(int axis_type)
return (char*)clicon_int2str(axis_type_map, axis_type);
}
int
axis_type_str2int(char *name)
{
return clicon_str2int(axis_type_map, name);
}
/*! Map from xpath_tree node name int to string
*/

View file

@ -44,6 +44,7 @@ struct clixon_xpath_yacc{
const char *xpy_name; /* Name of syntax (for error string) */
int xpy_linenum; /* Number of \n in parsed buffer */
const char *xpy_parse_string; /* original (copy of) parse string */
int xpy_lex_string_state; /* lex start condition (STRING) */
void *xpy_lexbuf; /* internal parse buffer from lex */
xpath_tree *xpy_top;
};

View file

@ -32,6 +32,26 @@
***** END LICENSE BLOCK *****
There are some special lexical rules in https://www.w3.org/TR/xpath-10
1. If there is a preceding token and the preceding token is not one of
@, ::, (, [, , or an Operator, then a * must be recognized as a
MultiplyOperator and an NCName must be recognized as an
OperatorName. (and,or,div,mod)
2. If the character following an NCName (possibly after intervening
ExprWhitespace) is (, then the token must be recognized as a
NodeType or a FunctionName.
3. If the two characters following an NCName (possibly after
intervening ExprWhitespace) are ::, then the token must be
recognized as an AxisName.
4. Otherwise, the token must not be recognized as a MultiplyOperator,
an OperatorName, a NodeType, a FunctionName, or an AxisName.
These rules are implemented in this parser by two states: TOKEN0 and TOKEN2.
TOKEN0 is the start and normative state and has only a basic NCNAME rule
TOKEN2 is only entered after some of the rules above, and has special nodetest rules
(maybe function/axisname as well?).
This state is left immediately to TOKEN0 after a single token
*/
%{
@ -58,6 +78,7 @@
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xpath_parse.h"
#include "clixon_xpath_function.h"
#include "clixon_xpath_eval.h"
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */
@ -76,8 +97,8 @@ clixon_xpath_parsewrap(void)
return 1;
}
/* strip last char */
void
/* strip last char: kludge to peek to next character */
static void
striplast(char *s)
{
s[strlen(s)-1] = 0;
@ -92,60 +113,83 @@ 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 TOKEN0
%s TOKEN2
%s QLITERAL
%s ALITERAL
%%
<TOKEN>[ \t]
<TOKEN>\n { _XPY->xpy_linenum++; }
<TOKEN>\r { }
<TOKEN><<EOF>> { return X_EOF; }
<TOKEN>".." { return DOUBLEDOT; }
<TOKEN>[()\[\]\.,/:|] { return *yytext; }
<TOKEN>and { clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN>or { clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN>div { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>mod { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>[+*\-] { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>\? { return *yytext; }
<TOKEN>"//" { return DOUBLESLASH; }
<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; }
<TOKEN0>[ \t]
<TOKEN0>\n { _XPY->xpy_linenum++; }
<TOKEN0>\r { }
<TOKEN0><<EOF>> { return X_EOF; }
<TOKEN0>".." { return DOUBLEDOT; }
<TOKEN0>:: { BEGIN(TOKEN2); return DOUBLECOLON; /* axisname */ }
<TOKEN0>[(\[] { BEGIN(TOKEN2); return *yytext; }
<TOKEN0>[)\]\.,/:|] { return *yytext; }
<TOKEN0>and { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN0>or { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN0>div { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN0>mod { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN0>[+*\-] { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN0>\? { return *yytext; }
<TOKEN0>"//" { BEGIN(TOKEN2);return DOUBLESLASH; }
<TOKEN0>"!=" { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return RELOP; }
<TOKEN0>">=" { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN0>"<=" { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN0>[<>=] { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN>{fnname} { clixon_xpath_parselval.string = strdup(yytext); striplast(clixon_xpath_parselval.string); return FUNCTIONNAME; }
<TOKEN0>@ { BEGIN(TOKEN2); return *yytext; }
<TOKEN0>\" { _XPY->xpy_lex_string_state = TOKEN0; BEGIN(QLITERAL); return QUOTE; }
<TOKEN0>\' { _XPY->xpy_lex_string_state = TOKEN0; BEGIN(ALITERAL); return APOST; }
<TOKEN0>\-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; }
<TOKEN>@ { return *yytext; }
<TOKEN>ancestor:: { clixon_xpath_parselval.intval = A_ANCESTOR; return AXISNAME; }
<TOKEN>ancestor-or-self:: { clixon_xpath_parselval.intval = A_ANCESTOR_OR_SELF; return AXISNAME; }
<TOKEN>attribute:: { clixon_xpath_parselval.intval = A_ATTRIBUTE; return AXISNAME; }
<TOKEN>child:: { clixon_xpath_parselval.intval = A_CHILD; return AXISNAME; }
<TOKEN>descendant:: { clixon_xpath_parselval.intval = A_DESCENDANT; return AXISNAME; }
<TOKEN>descendant-or-self:: { clixon_xpath_parselval.intval = A_DESCENDANT_OR_SELF; return AXISNAME; }
<TOKEN>following:: { clixon_xpath_parselval.intval = A_FOLLOWING; return AXISNAME; }
<TOKEN>following-sibling:: { clixon_xpath_parselval.intval = A_FOLLOWING_SIBLING; return AXISNAME; }
<TOKEN>namespace:: { clixon_xpath_parselval.intval = A_NAMESPACE; return AXISNAME; }
<TOKEN>parent:: { clixon_xpath_parselval.intval = A_PARENT; return AXISNAME; }
<TOKEN>preceding:: { clixon_xpath_parselval.intval = A_PRECEDING; return AXISNAME; }
<TOKEN>preceding-sibling:: { clixon_xpath_parselval.intval = A_PRECEDING_SIBLING; return AXISNAME; }
<TOKEN>self:: { clixon_xpath_parselval.intval = A_SELF; return AXISNAME; }
<TOKEN0>{ncname} { /* See lexical rules 2 and 3 in the file header */
clixon_xpath_parselval.string = strdup(yytext);
return NCNAME;
}
<TOKEN0>. { fprintf(stderr,"LEXICAL ERROR\n"); return -1; }
<TOKEN>\" { BEGIN(QLITERAL); return QUOTE; }
<TOKEN>\' { BEGIN(ALITERAL); return APOST; }
<TOKEN>\-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; }
<TOKEN>{ncname} { clixon_xpath_parselval.string = strdup(yytext);
return NAME; /* rather be catch-all */
<TOKEN2>[ \t]
<TOKEN2>\n { _XPY->xpy_linenum++; }
<TOKEN2>\r { }
<TOKEN2><<EOF>> { return X_EOF; }
<TOKEN2>".." { BEGIN(TOKEN0); return DOUBLEDOT; }
<TOKEN2>:: { BEGIN(TOKEN0); return DOUBLECOLON; /* axisname */ }
<TOKEN2>[()\[\]\.,/:|] { BEGIN(TOKEN0); return *yytext; }
<TOKEN2>and { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN2>or { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN2>div { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN2>mod { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN2>[+*\-] { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN2>\? { BEGIN(TOKEN0); return *yytext; }
<TOKEN2>"//" { BEGIN(TOKEN0); return DOUBLESLASH; }
<TOKEN2>"!=" { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return RELOP; }
<TOKEN2>">=" { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN2>"<=" { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN2>[<>=] { BEGIN(TOKEN0); clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN2>@ { BEGIN(TOKEN0); return *yytext; }
<TOKEN2>\" { BEGIN(TOKEN0); _XPY->xpy_lex_string_state=TOKEN2; BEGIN(QLITERAL); return QUOTE; }
<TOKEN2>\' { BEGIN(TOKEN0); _XPY->xpy_lex_string_state=TOKEN2; BEGIN(ALITERAL); return APOST; }
<TOKEN2>\-?({integer}|{real}) { BEGIN(TOKEN0); clixon_xpath_parselval.string = strdup(yytext); return NUMBER; }
<TOKEN2>comment\( { BEGIN(TOKEN0); clixon_xpath_parselval.string = strdup(yytext); striplast(clixon_xpath_parselval.string); return NODETYPE; }
<TOKEN2>text\( { BEGIN(TOKEN0); clixon_xpath_parselval.string = strdup(yytext); striplast(clixon_xpath_parselval.string); return NODETYPE; }
<TOKEN2>processing-instructions\( { BEGIN(TOKEN0); clixon_xpath_parselval.string = strdup(yytext); striplast(clixon_xpath_parselval.string); return NODETYPE; }
<TOKEN2>node\( { BEGIN(TOKEN0); clixon_xpath_parselval.string = strdup(yytext); striplast(clixon_xpath_parselval.string); return NODETYPE; }
<TOKEN2>{ncname} { /* See lexical rules 2 and 3 in the file header */
BEGIN(TOKEN0);
clixon_xpath_parselval.string = strdup(yytext);
return NCNAME;
}
<TOKEN>. { fprintf(stderr,"LEXICAL ERROR\n"); return -1; }
<TOKEN2>. { fprintf(stderr,"LEXICAL ERROR\n"); return -1; }
<QLITERAL>\" { BEGIN(TOKEN); return QUOTE; }
<QLITERAL>\" { BEGIN(_XPY->xpy_lex_string_state); return QUOTE; }
<QLITERAL>[^"]+ { clixon_xpath_parselval.string = strdup(yytext);
return CHARS;}
<ALITERAL>\' { BEGIN(TOKEN); return APOST; }
<ALITERAL>\' { BEGIN(_XPY->xpy_lex_string_state); return APOST; }
<ALITERAL>[^']+ { clixon_xpath_parselval.string = strdup(yytext);
return CHARS;}
@ -157,7 +201,7 @@ fnname {ncname}\(
int
xpath_scan_init(clixon_xpath_yacc *xpy)
{
BEGIN(TOKEN);
BEGIN(TOKEN0);
xpy->xpy_lexbuf = yy_scan_string (xpy->xpy_parse_string);
#if 1 /* XXX: just to use unput to avoid warning */
if (0)

View file

@ -49,7 +49,6 @@
void *stack; /* xpath_tree */
}
%token <intval> AXISNAME
%token <intval> LOGOP
%token <intval> ADDOP
%token <intval> RELOP
@ -59,12 +58,15 @@
%token <string> QUOTE
%token <string> APOST
%token <string> CHARS
%token <string> NAME
%token <string> NCNAME
%token <string> NODETYPE
%token <string> DOUBLEDOT
%token <string> DOUBLECOLON
%token <string> DOUBLESLASH
%token <string> FUNCTIONNAME
%type <intval> axisspec
%type <intval> abbreviatedaxisspec
%type <string> string
%type <stack> args
@ -79,9 +81,13 @@
%type <stack> abslocpath
%type <stack> rellocpath
%type <stack> step
%type <stack> abbreviatedstep
%type <stack> nodetest
%type <stack> nametest
%type <stack> predicates
%type <stack> primaryexpr
%type <stack> literal
%type <stack> functioncall
%lex-param {void *_xpy} /* Add this argument to parse() and lex() function */
%parse-param {void *_xpy}
@ -319,15 +325,13 @@ xp_primary_function(clixon_xpath_yacc *xpy,
*/
static xpath_tree *
xp_nodetest_function(clixon_xpath_yacc *xpy,
char *name,
xpath_tree *xpt)
char *name)
{
xpath_tree *xtret = NULL;
enum clixon_xpath_function fn;
cbuf *cb = NULL;
int ret;
enum clixon_xpath_function fn;
if ((ret = xp_fnname_str2int(name)) < 0){
if ((fn = xp_fnname_str2int(name)) < 0){
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
@ -336,7 +340,6 @@ xp_nodetest_function(clixon_xpath_yacc *xpy,
clixon_xpath_parseerror(xpy, cbuf_get(cb));
goto done;
}
fn = ret;
switch (fn){
case XPATHFN_COMMENT: /* Group of not implemented node functions */
case XPATHFN_PROCESSING_INSTRUCTIONS:
@ -363,16 +366,42 @@ xp_nodetest_function(clixon_xpath_yacc *xpy,
}
if (cb)
cbuf_free(cb);
xtret = xp_new(XP_NODE_FN, fn, NULL, name, NULL, xpt, NULL);
name = NULL; /* Consumed by xp_new */
xtret = xp_new(XP_NODE_FN, fn, NULL, name, NULL, NULL, NULL);
done:
if (cb)
cbuf_free(cb);
if (name)
free(name);
return xtret;
}
/*! Axisname functions
*
* See rule [6] AxisName ::= 'ancestor',...
* in https://www.w3.org/TR/xpath-10/
* @param[in] xpy XPath parse handle
* @param[in] name Name of axisname function
*/
static int
xp_axisname_function(clixon_xpath_yacc *xpy,
char *name)
{
int fn = -1;
cbuf *cb = NULL;
if ((fn = axis_type_str2int(name)) < 0){
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cb, "Unknown xpath axisname \"%s\"", name);
clixon_xpath_parseerror(xpy, cbuf_get(cb));
goto done;
}
done:
if (cb)
cbuf_free(cb);
return fn;
}
%}
%%
@ -411,6 +440,7 @@ pathexpr : locationpath { $$=xp_new(XP_PATHEXPR,A_NAN,NULL,NULL,NULL,$1, NULL
| filterexpr DOUBLESLASH rellocpath { $$=xp_new(XP_PATHEXPR,A_NAN,NULL,strdup("//"),NULL,$1, $3);_XPY->xpy_top=$$;_PARSE_DEBUG("pathexpr-> filterexpr // rellocpath"); }
;
/* */
filterexpr : primaryexpr { $$=xp_new(XP_FILTEREXPR,A_NAN,NULL,NULL,NULL,$1, NULL);_PARSE_DEBUG("filterexpr-> primaryexpr"); }
/* Filterexpr predicate */
;
@ -431,21 +461,58 @@ rellocpath : step { $$=xp_new(XP_RELLOCPATH,A_NAN,NULL,NULL,NUL
| rellocpath DOUBLESLASH step { $$=xp_new(XP_RELLOCPATH,A_DESCENDANT_OR_SELF,NULL,NULL,NULL,$1, $3); _PARSE_DEBUG("rellocpath-> rellocpath // step"); }
;
step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,NULL, NULL, NULL, $2, $3);_PARSE_DEBUG1("step->axisspec(%d) nodetest", $1); }
| '.' predicates { $$=xp_new(XP_STEP,A_SELF, NULL,NULL, NULL, NULL, $2); _PARSE_DEBUG("step-> ."); }
| DOUBLEDOT predicates { $$=xp_new(XP_STEP, A_PARENT, NULL,NULL, NULL, NULL, $2); _PARSE_DEBUG("step-> .."); }
step : nodetest predicates
{ $$=xp_new(XP_STEP, A_CHILD, NULL, NULL, NULL, $1, $2);
_PARSE_DEBUG("step->nodetest predicates"); }
| axisspec nodetest predicates
{$$=xp_new(XP_STEP, $1, NULL, NULL, NULL, $2, $3);
_PARSE_DEBUG1("step->axisspec(%d) nodetest", $1); }
| abbreviatedstep { $$ = $1; }
;
abbreviatedstep : '.' predicates { $$=xp_new(XP_STEP,A_SELF, NULL,NULL, NULL, NULL, $2); _PARSE_DEBUG("step-> ."); }
| DOUBLEDOT predicates { $$=xp_new(XP_STEP, A_PARENT, NULL,NULL, NULL, NULL, $2); _PARSE_DEBUG("step-> .."); }
;
/* [5] AxisSpecifier::= AxisName '::'
| AbbreviatedAxisSpecifier
*/
axisspec : NCNAME DOUBLECOLON
{ if (($$=xp_axisname_function(_XPY, $1)) < 0) YYERROR;
_PARSE_DEBUG2("axisspec-> AXISNAME(%s -> %d) ::", $1, $$);
}
| abbreviatedaxisspec
{ $$ = $1; }
;
axisspec : AXISNAME { _PARSE_DEBUG1("axisspec-> AXISNAME(%d) ::", $1); $$=$1;}
| '@' { $$=A_ATTRIBUTE; _PARSE_DEBUG("axisspec-> @"); }
| { _PARSE_DEBUG("axisspec-> "); $$=A_CHILD;}
;
/* [13] AbbreviatedAxisSpecifier ::= '@'?
* empty built into 2nd step rule
*/
abbreviatedaxisspec :'@' { $$=A_ATTRIBUTE; _PARSE_DEBUG("axisspec-> @"); }
;
nodetest : ADDOP { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, strdup(clicon_int2str(xpopmap,$1)), NULL, NULL); _PARSE_DEBUG("nodetest-> *"); }
| NAME { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, $1, NULL, NULL); _PARSE_DEBUG1("nodetest-> name(%s)",$1); }
| NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,NULL, $1, $3, NULL, NULL);_PARSE_DEBUG2("nodetest-> name(%s) : name(%s)", $1, $3); }
| NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,NULL, $1, NULL, NULL, NULL);_PARSE_DEBUG1("nodetest-> name(%s) : *", $1); }
| FUNCTIONNAME ')' { if (($$ = xp_nodetest_function(_XPY, $1, NULL)) == NULL) YYERROR;; _PARSE_DEBUG1("nodetest-> nodetype():%s", $1); }
nodetest : nametest { $$ = $1;
_PARSE_DEBUG("nodetest-> nametest");}
| NODETYPE ')'
{ if (($$ = xp_nodetest_function(_XPY, $1)) == NULL) YYERROR;
_PARSE_DEBUG1("nodetest-> nodetype(%s)", $1);
}
;
nametest : ADDOP
{ char *str;
str = strdup(clicon_int2str(xpopmap,$1));
$$=xp_new(XP_NODE,A_NAN,NULL, NULL, str, NULL, NULL);
_PARSE_DEBUG("nametest-> *"); }
| NCNAME
{ $$=xp_new(XP_NODE,A_NAN,NULL, NULL, $1, NULL, NULL);
_PARSE_DEBUG1("nametest-> name[%s]",$1); }
| NCNAME ':' NCNAME
{ $$=xp_new(XP_NODE,A_NAN,NULL, $1, $3, NULL, NULL);
_PARSE_DEBUG2("nametest-> name[%s] : name[%s]", $1, $3); }
| NCNAME ':' '*'
{ $$=xp_new(XP_NODE,A_NAN,NULL, $1, NULL, NULL, NULL);
_PARSE_DEBUG1("nametest-> name[%s] : *", $1); }
;
/* evaluates to boolean */
@ -453,13 +520,9 @@ predicates : predicates '[' expr ']' { $$=xp_new(XP_PRED,A_NAN,NULL, NULL, NULL
| { $$=xp_new(XP_PRED,A_NAN,NULL, NULL, NULL, NULL, NULL); _PARSE_DEBUG("predicates->"); }
;
primaryexpr : '(' expr ')' { $$=xp_new(XP_PRI0,A_NAN,NULL, NULL, NULL, $2, NULL); _PARSE_DEBUG("primaryexpr-> ( expr )"); }
| literal { $$ = $1; }
| NUMBER { $$=xp_new(XP_PRIME_NR,A_NAN, $1, NULL, NULL, NULL, NULL);_PARSE_DEBUG1("primaryexpr-> NUMBER(%s)", $1); /*XXX*/}
| QUOTE string QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, $2, NULL, NULL, NULL);_PARSE_DEBUG("primaryexpr-> \" string \""); }
| QUOTE QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, strdup(""), NULL, NULL, NULL);_PARSE_DEBUG("primaryexpr-> \" \""); }
| APOST string APOST { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, $2, NULL, NULL, NULL);_PARSE_DEBUG("primaryexpr-> ' string '"); }
| APOST APOST { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, strdup(""), NULL, NULL, NULL);_PARSE_DEBUG("primaryexpr-> ' '"); }
| FUNCTIONNAME ')' { if (($$ = xp_primary_function(_XPY, $1, NULL)) == NULL) YYERROR; _PARSE_DEBUG("primaryexpr-> functionname ()"); }
| FUNCTIONNAME args ')' { if (($$ = xp_primary_function(_XPY, $1, $2)) == NULL) YYERROR; _PARSE_DEBUG("primaryexpr-> functionname (arguments)"); }
| functioncall { $$ = $1; }
;
args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, $3);
@ -468,6 +531,29 @@ args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, $3);
_PARSE_DEBUG("args -> expr "); }
;
literal : QUOTE string QUOTE
{ $$=xp_new(XP_PRIME_STR,A_NAN,NULL, $2, NULL, NULL, NULL);
_PARSE_DEBUG("literal-> \" string \""); }
| QUOTE QUOTE
{ $$=xp_new(XP_PRIME_STR,A_NAN,NULL, strdup(""), NULL, NULL, NULL);
_PARSE_DEBUG("primaryexpr-> \" \""); }
| APOST string APOST
{ $$=xp_new(XP_PRIME_STR,A_NAN,NULL, $2, NULL, NULL, NULL);
_PARSE_DEBUG("primaryexpr-> ' string '"); }
| APOST APOST
{ $$=xp_new(XP_PRIME_STR,A_NAN,NULL, strdup(""), NULL, NULL, NULL);
_PARSE_DEBUG("primaryexpr-> ' '"); }
;
functioncall : NCNAME '(' ')'
{ /* XXX warning: rule useless in parser due to conflicts */
if (($$ = xp_primary_function(_XPY, $1, NULL)) == NULL) YYERROR;
_PARSE_DEBUG("primaryexpr-> functionname ()"); }
| NCNAME '(' args ')'
{ if (($$ = xp_primary_function(_XPY, $1, $3)) == NULL) YYERROR;
_PARSE_DEBUG("primaryexpr-> functionname (arguments)"); }
;
string : string CHARS {
int len = strlen($1);
$$ = realloc($1, len+strlen($2) + 1);

View file

@ -1,5 +1,9 @@
#!/usr/bin/env bash
# XPATH tests
# Some XPATH cases clixon cannot handle
# - /aaa/bbb/comment, where "comment" is nodetype
# - //b*, combinations of // and "*"
# For more (outdated info): https://github.com/clicon/clixon/issues/54
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@ -11,6 +15,7 @@ xml=$dir/xml.xml
xml2=$dir/xml2.xml
xml3=$dir/xml3.xml
xml4=$dir/xml4.xml
xmlfn=$dir/xmlfn.xml
cat <<EOF > $xml
<aaa>
@ -94,173 +99,229 @@ cat <<EOF > $xml4
</root>
EOF
# XPath functions
cat <<EOF > $xmlfn
<root>
<ancestor>
<count>
<node>42</node>
</count>
</ancestor>
<count>
<node>
<ancestor>99</ancestor>
</node>
</count>
<node>
<ancestor>
<count>73</count>
</ancestor>
</node>
</root>
EOF
new "xpath not(aaa)"
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p "not(aaa)")" 0 "bool:false"
new "xpath not (aaa) - delimiter"
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p "not(aaa)")" 0 "bool:false"
new "xpath not(xyz)"
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p "not(xyz)")" 0 "bool:true"
new "xpath /"
expecteof "$clixon_util_xpath -f $xml -p /" 0 "" "^nodeset:0:<aaa><bbb x=\"hello\"><ccc>42</ccc></bbb><bbb x=\"bye\"><ccc>99</ccc></bbb><ddd><ccc>22</ccc></ddd></aaa>$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p /)" 0 "^nodeset:0:<aaa><bbb x=\"hello\"><ccc>42</ccc></bbb><bbb x=\"bye\"><ccc>99</ccc></bbb><ddd><ccc>22</ccc></ddd></aaa>$"
new "xpath /aaa"
expecteof "$clixon_util_xpath -f $xml -p /aaa" 0 "" "^nodeset:0:<aaa><bbb x=\"hello\"><ccc>42</ccc></bbb><bbb x=\"bye\"><ccc>99</ccc></bbb><ddd><ccc>22</ccc></ddd></aaa>$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p /aaa)" 0 "^nodeset:0:<aaa><bbb x=\"hello\"><ccc>42</ccc></bbb><bbb x=\"bye\"><ccc>99</ccc></bbb><ddd><ccc>22</ccc></ddd></aaa>$"
new "xpath aaa"
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p aaa)" 0 "^nodeset:0:<aaa><bbb x=\"hello\"><ccc>42</ccc></bbb><bbb x=\"bye\"><ccc>99</ccc></bbb><ddd><ccc>22</ccc></ddd></aaa>$"
new "xpath /bbb"
expecteof "$clixon_util_xpath -f $xml -p /bbb" 0 "" "^nodeset:$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p /bbb)" 0 "^nodeset:$"
new "xpath /aaa/bbb"
expecteof "$clixon_util_xpath -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>$"
new "xpath /aaa/bbb union "
expecteof "$clixon_util_xpath -f $xml -p aaa/bbb[ccc=42]|aaa/ddd[ccc=22]" 0 "" '^nodeset:0:<bbb x="hello"><ccc>42</ccc></bbb>1:<ddd><ccc>22</ccc></ddd>$'
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p "aaa/bbb[ccc=42]|aaa/ddd[ccc=22]")" 0 '^nodeset:0:<bbb x="hello"><ccc>42</ccc></bbb>1:<ddd><ccc>22</ccc></ddd>$'
new "xpath //bbb"
expecteof "$clixon_util_xpath -f $xml -p //bbb" 0 "" "0:<bbb x=\"hello\"><ccc>42</ccc></bbb>
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p //bbb)" 0 "0:<bbb x=\"hello\"><ccc>42</ccc></bbb>
1:<bbb x=\"bye\"><ccc>99</ccc></bbb>"
new "xpath //b?b"
#expecteof "$clixon_util_xpath -f $xml" 0 "//b?b" ""
#expecteof "$clixon_util_xpath -D $DBG -f $xml" 0 "//b?b" ""
# Clixon cant do //* things
new "xpath //b*"
#expecteof "$clixon_util_xpath -f $xml" 0 "//b*" ""
#expecteof "$clixon_util_xpath -D $DBG -f $xml" 0 "//b*" ""
new "xpath //b*/ccc"
#expecteof "$clixon_util_xpath -f $xml" 0 "//b*/ccc" ""
#expecteof "$clixon_util_xpath -D $DBG -f $xml" 0 "//b*/ccc" ""
new "xpath //bbb[0]"
expecteof "$clixon_util_xpath -f $xml -p //bbb[0]" 0 "" "^nodeset:0:<bbb x=\"hello\"><ccc>42</ccc></bbb>$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p //bbb[0])" 0 "^nodeset:0:<bbb x=\"hello\"><ccc>42</ccc></bbb>$"
new "xpath //bbb[ccc=99]"
expecteof "$clixon_util_xpath -f $xml -p //bbb[ccc=99]" 0 "" "^nodeset:0:<bbb x=\"bye\"><ccc>99</ccc></bbb>$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml -p //bbb[ccc=99])" 0 "^nodeset:0:<bbb x=\"bye\"><ccc>99</ccc></bbb>$"
new "xpath ../connection-type = 'responder-only'"
expecteof "$clixon_util_xpath -f $xml2 -p ../connection-type='responder-only' -i /aaa/bbb/here" 0 "" "^bool:true$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "../connection-type='responder-only'" -i /aaa/bbb/here)" 0 "^bool:true$"
new "xpath ../connection-type = 'no-responder'"
expecteof "$clixon_util_xpath -f $xml2 -p ../connection-type='no-responder' -i /aaa/bbb/here" 0 "" "^bool:false$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p "../connection-type='no-responder'" -i /aaa/bbb/here)" 0 "^bool:false$"
new "xpath . <= 0.75 * ../max-rtr-adv-interval"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb/here" 0 ". <= 0.75 * ../max-rtr-adv-interval" "^bool:true$"
new "xpath . > 0.75 * ../max-rtr-adv-interval"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb/here" 0 ". > 0.75 * ../max-rtr-adv-interval" "^bool:false$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb/here" 0 ". > 0.75 * ../max-rtr-adv-interval" "^bool:false$"
new "xpath . <= ../valid-lifetime"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb/here" 0 ". <= ../valid-lifetime" "^bool:true$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb/here" 0 ". <= ../valid-lifetime" "^bool:true$"
new "xpath ../../rt:address-family = 'v6ur:ipv6-unicast'"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb/here" 0 "../../rt:address-family = 'v6ur:ipv6-unicast'" "^bool:true$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb/here" 0 "../../rt:address-family = 'v6ur:ipv6-unicast'" "^bool:true$"
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 -D $DBG -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'"
expectpart "$($clixon_util_xpath -f $xml2 -p "/if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'")" 0 "^bool:true$"
expectpart "$($clixon_util_xpath -D $DBG -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$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa" 0 "rt:address-family='v6ur:ipv6-unicast'" "^bool:true$"
new "xpath ../type='rt:static'"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb/here" 0 "../type='rt:static'" "^bool:true$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb/here" 0 "../type='rt:static'" "^bool:true$"
new "xpath rib-name != ../../name"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "rib-name != ../name" "^bool:true$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "rib-name != ../name" "^bool:true$"
new "xpath routing/ribs/rib[name=current()/rib-name]/address-family=../../address-family"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "routing/ribs/rib[name=current()/rib-name]/address-family=../../address-family" "^bool:true$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "routing/ribs/rib[name=current()/rib-name]/address-family=../../address-family" "^bool:true$"
new "xpath ifType = \"ethernet\" or ifMTU = 1500"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" or ifMTU = 1500" "^bool:true$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" or ifMTU = 1500" "^bool:true$"
new "xpath ifType != \"ethernet\" or ifMTU = 1500"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" or ifMTU = 1500" "^bool:true$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" or ifMTU = 1500" "^bool:true$"
new "xpath ifType = \"ethernet\" or ifMTU = 1400"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" or ifMTU = 1400" "^bool:true$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" or ifMTU = 1400" "^bool:true$"
new "xpath ifType != \"ethernet\" or ifMTU = 1400"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" or ifMTU = 1400" "^bool:false$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" or ifMTU = 1400" "^bool:false$"
new "xpath ifType = \"ethernet\" and ifMTU = 1500"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" and ifMTU = 1500" "^bool:true$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" and ifMTU = 1500" "^bool:true$"
new "xpath ifType != \"ethernet\" and ifMTU = 1500"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" and ifMTU = 1500" "^bool:false$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" and ifMTU = 1500" "^bool:false$"
new "xpath ifType = \"ethernet\" and ifMTU = 1400"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" and ifMTU = 1400" "^bool:false$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "ifType = \"ethernet\" and ifMTU = 1400" "^bool:false$"
new "xpath ifType != \"ethernet\" and ifMTU = 1400"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" and ifMTU = 1400" "^bool:false$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "ifType != \"ethernet\" and ifMTU = 1400" "^bool:false$"
new "xpath ifType != \"atm\" or (ifMTU <= 17966 and ifMTU >= 64)"
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb" 0 "ifType != \"atm\" or (ifMTU <= 17966 and ifMTU >= 64)" "^bool:true$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -i /aaa/bbb" 0 "ifType != \"atm\" or (ifMTU <= 17966 and ifMTU >= 64)" "^bool:true$"
new "xpath .[name='bar']"
expecteof "$clixon_util_xpath -f $xml2 -p .[name='bar'] -i /aaa/bbb/routing/ribs/rib" 0 "" "^nodeset:0:<rib><name>bar</name><address-family>myfamily</address-family></rib>$"
expecteof "$clixon_util_xpath -D $DBG -f $xml2 -p .[name='bar'] -i /aaa/bbb/routing/ribs/rib" 0 "^nodeset:0:<rib><name>bar</name><address-family>myfamily</address-family></rib>$"
new "xpath /aaa/bbb/namespace (namespace is xpath axisname)"
expecteof "$clixon_util_xpath -f $xml2 -p /aaa/bbb/namespace" 0 "" "^nodeset:0:<namespace>urn:example:foo</namespace>$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p /aaa/bbb/namespace)" 0 "^nodeset:0:<namespace>urn:example:foo</namespace>$"
# See https://github.com/clicon/clixon/issues/54
# But it is not only axis names. There are also, for example, nodetype like this example:
#new "xpath /aaa/bbb/comment (comment is xpath nodetype)"
#expecteof "$clixon_util_xpath -f $xml2 -p /aaa/bbb/comment" 0 "" "^kalle$"
new "xpath /aaa/bbb/comment (comment is xpath nodetype)"
expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p /aaa/bbb/comment)" 0 "^nodeset:$"
new "Multiple entries"
new "xpath bbb[ccc='foo']"
expecteof "$clixon_util_xpath -f $xml3 -p bbb[ccc='foo']" 0 "" "^nodeset:0:<bbb x=\"hello\"><ccc>foo</ccc><ccc>42</ccc><ccc>bar</ccc></bbb>1:<bbb x=\"bye\"><ccc>99</ccc><ccc>foo</ccc></bbb>$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "bbb[ccc='foo']")" 0 "^nodeset:0:<bbb x=\"hello\"><ccc>foo</ccc><ccc>42</ccc><ccc>bar</ccc></bbb>1:<bbb x=\"bye\"><ccc>99</ccc><ccc>foo</ccc></bbb>$"
new "xpath bbb[ccc=\"foo\"]"
expecteof "$clixon_util_xpath -f $xml3 -p bbb[ccc=\"foo\"]" 0 "" "^nodeset:0:<bbb x=\"hello\"><ccc>foo</ccc><ccc>42</ccc><ccc>bar</ccc></bbb>1:<bbb x=\"bye\"><ccc>99</ccc><ccc>foo</ccc></bbb>$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "bbb[ccc=\"foo\"]")" 0 "^nodeset:0:<bbb x=\"hello\"><ccc>foo</ccc><ccc>42</ccc><ccc>bar</ccc></bbb>1:<bbb x=\"bye\"><ccc>99</ccc><ccc>foo</ccc></bbb>$"
new "xpath bbb[ccc='42']"
expecteof "$clixon_util_xpath -f $xml3 -p bbb[ccc='42']" 0 "" "^nodeset:0:<bbb x=\"hello\"><ccc>foo</ccc><ccc>42</ccc><ccc>bar</ccc></bbb>$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p bbb[ccc='42'])" 0 "^nodeset:0:<bbb x=\"hello\"><ccc>foo</ccc><ccc>42</ccc><ccc>bar</ccc></bbb>$"
new "xpath bbb[ccc=99] (number w/o quotes)"
expecteof "$clixon_util_xpath -f $xml3 -p bbb[ccc=99]" 0 "" "^nodeset:0:<bbb x=\"bye\"><ccc>99</ccc><ccc>foo</ccc></bbb>$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p bbb[ccc=99])" 0 "^nodeset:0:<bbb x=\"bye\"><ccc>99</ccc><ccc>foo</ccc></bbb>$"
new "xpath bbb[ccc='bar']"
expecteof "$clixon_util_xpath -f $xml3 -p bbb[ccc='bar']" 0 "" "^nodeset:0:<bbb x=\"hello\"><ccc>foo</ccc><ccc>42</ccc><ccc>bar</ccc></bbb>$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "bbb[ccc='bar']")" 0 "^nodeset:0:<bbb x=\"hello\"><ccc>foo</ccc><ccc>42</ccc><ccc>bar</ccc></bbb>$"
new "xpath bbb[ccc='fie']"
expecteof "$clixon_util_xpath -f $xml3 -p bbb[ccc='fie']" 0 "" "^nodeset:$"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "bbb[ccc='fie']")" 0 "^nodeset:$"
# Just syntax - no semantic meaning
new "xpath derived-from 10.4.1"
expectpart "$($clixon_util_xpath -f $xml3 -p 'derived-from(../../change-operation,"modify")')" 0 "bool:false"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p 'derived-from(../../change-operation,"modify")')" 0 "bool:false"
new "xpath derived-from-or-self"
expectpart "$($clixon_util_xpath -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"
expectpart "$($clixon_util_xpath -f $xml3 -p "contains(../../objectClass,'BTSFunction') or contains(../../objectClass,'RNCFunction')")" 0 "bool:false"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -p "contains(../../objectClass,'BTSFunction') or contains(../../objectClass,'RNCFunction')")" 0 "bool:false"
# Nodetests
new "xpath nodetest: node"
expectpart "$($clixon_util_xpath -f $xml3 -p "/bbb/ccc/self::node()")" 0 "nodeset:0:<ccc>foo</ccc>"
expectpart "$($clixon_util_xpath -D $DBG -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"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -l o -p "/descendant-or-self::comment()")" 255 "XPATH function \"comment\" is not implemented"
# Count
new "find bbb with 3 ccc children using count"
expectpart "$($clixon_util_xpath -f $xml3 -l o -p "(/bbb[count(ccc)=3])")" 0 "<bbb x=\"hello\"><ccc>foo</ccc><ccc>42</ccc><ccc>bar</ccc></bbb>"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -l o -p "(/bbb[count(ccc)=3])")" 0 "<bbb x=\"hello\"><ccc>foo</ccc><ccc>42</ccc><ccc>bar</ccc></bbb>"
# Negative
new "xpath dontexist"
expectpart "$($clixon_util_xpath -f $xml3 -l o -p "dontexist()")" 255 "Unknown xpath function \"dontexist\""
expectpart "$($clixon_util_xpath -D $DBG -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"
expectpart "$($clixon_util_xpath -D $DBG -f $xml3 -l o -p "enum-value()")" 255 "XPATH function \"enum-value\" is not implemented"
new "xpath /root/*/a"
expecteof "$clixon_util_xpath -f $xml4 -p /root/*/a" 0 "" "nodeset:0:<a>111</a>1:<a>222</a>2:<a>111</a>"
expecteof "$clixon_util_xpath -D $DBG -f $xml4 -p /root/*/a" 0 "" "nodeset:0:<a>111</a>1:<a>222</a>2:<a>111</a>"
new "xpath /root/*/b"
expecteof "$clixon_util_xpath -f $xml4 -p /root/*/b" 0 "" "nodeset:0:<b>111</b>"
expecteof "$clixon_util_xpath -D $DBG -f $xml4 -p /root/*/b" 0 "" "nodeset:0:<b>111</b>"
new "xpath /root/*/*[.='111']"
expecteof "$clixon_util_xpath -f $xml4 -p /root/*/*[.='111']" 0 "" "nodeset:0:<a>111</a>1:<b>111</b>2:<a>111</a>"
expecteof "$clixon_util_xpath -D $DBG -f $xml4 -p /root/*/*[.='111']" 0 "" "nodeset:0:<a>111</a>1:<b>111</b>2:<a>111</a>"
# Try functionnames in place of node nc-names
new "xpath nodetest: node"
expectpart "$($clixon_util_xpath -D $DBG -f $xmlfn -p "count(/root/count)")" 0 "number:1"
new "xpath nodetest: node"
expectpart "$($clixon_util_xpath -D $DBG -f $xmlfn -p "/root/node/self::node()")" 0 "<node><ancestor><count>73</count></ancestor></node>"
new "xpath functions as ncname: nodetype:node"
expectpart "$($clixon_util_xpath -D $DBG -f $xmlfn -p "root/ancestor/count/node")" 0 "<node>42</node>"
new "xpath functions as ncname: nodetype:node"
expectpart "$($clixon_util_xpath -D $DBG -f $xmlfn -p "root/ancestor/count[node=42]")" 0 "<count><node>42</node></count>"
new "xpath functions as ncname: axisname:ancestor"
expectpart "$($clixon_util_xpath -D $DBG -f $xmlfn -p "root/count/node[99=ancestor]")" 0 "<node><ancestor>99</ancestor></node>"
new "xpath functions as ncname: functioname:count"
expectpart "$($clixon_util_xpath -D $DBG -f $xmlfn -p "root/node/ancestor[73=count]")" 0 "<ancestor><count>73</count></ancestor>"
rm -rf $dir

View file

@ -1,9 +1,12 @@
#!/usr/bin/env bash
# test xpath functions in YANG conditionals
# test xpath functions within YANG conditionals
# XPATH base https://www.w3.org/TR/xpath-10/
# YANG XPATH functions: https://tools.ietf.org/html/rfc7950
# Tests:
# - Contains
# Test of xpath functions:
# - contains
# - derived-from
# - derived-from-or-self
# - bit-is-set
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@ -102,6 +105,24 @@ module $APPNAME{
type uint32;
}
}
/* Example derved from yangmodels ietf-mpls-ldp.yang */
container mustnot{
list mustlist{
key name;
leaf name{
type string;
}
container mycont{
must "not (../../ex:mustlist[ex:name!=current()/../ex:name])" {
description
"Only one list instance is allowed.";
}
leaf foo{
type string;
}
}
}
}
}
EOF
@ -120,7 +141,6 @@ fi
new "wait backend"
wait_backend
# contains
new "contains: Set site to foo that validates site OK"
expecteof_netconf "$clixon_netconf -qf $cfg -D $DBG" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><top xmlns=\"urn:example:clixon\"><class>foo</class><mylist><id>12</id><site>42</site></mylist></top></config></edit-config></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"

View file

@ -45,11 +45,6 @@ files=$(find ${YANG_STANDARD_DIR}/ietf/RFC -type f -name "*.yang")
for f in $files; do
if [ -n "$(head -1 $f|grep '^module')" ]; then
# Mask old revision
if [ $f = ${YANG_STANDARD_DIR}/ietf/RFC/ietf-mpls-ldp@2022-03-14.yang -o $f = ${YANG_STANDARD_DIR}/ietf/RFC/ietf-mpls-ldp-extended@2022-03-14.yang ]; then
echo "FOOOOO"
# XXX xpath parse error: new (..)
continue;
fi
if [ $f = ${YANG_STANDARD_DIR}/ietf/RFC/ietf-yang-types@2010-09-24.yang ]; then
continue;
fi