* 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. * 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 * 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` * 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) * [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) * [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` * 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 * Prototypes
*/ */
char* axis_type_int2str(int axis_type); char* axis_type_int2str(int axis_type);
int axis_type_str2int(char *name);
char* xpath_tree_int2str(int nodetype); char* xpath_tree_int2str(int nodetype);
int xpath_tree_print_cb(cbuf *cb, xpath_tree *xs); int xpath_tree_print_cb(cbuf *cb, xpath_tree *xs);
int xpath_tree_print(FILE *f, 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); 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 /*! 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) */ const char *xpy_name; /* Name of syntax (for error string) */
int xpy_linenum; /* Number of \n in parsed buffer */ int xpy_linenum; /* Number of \n in parsed buffer */
const char *xpy_parse_string; /* original (copy of) parse string */ 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 */ void *xpy_lexbuf; /* internal parse buffer from lex */
xpath_tree *xpy_top; xpath_tree *xpy_top;
}; };

View file

@ -32,6 +32,26 @@
***** END LICENSE BLOCK ***** ***** 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_ctx.h"
#include "clixon_xpath.h" #include "clixon_xpath.h"
#include "clixon_xpath_parse.h" #include "clixon_xpath_parse.h"
#include "clixon_xpath_function.h"
#include "clixon_xpath_eval.h" #include "clixon_xpath_eval.h"
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */ /* 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; return 1;
} }
/* strip last char */ /* strip last char: kludge to peek to next character */
void static void
striplast(char *s) striplast(char *s)
{ {
s[strlen(s)-1] = 0; s[strlen(s)-1] = 0;
@ -92,60 +113,83 @@ real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
namestart [A-Z_a-z] namestart [A-Z_a-z]
namechar [A-Z_a-z\-\.0-9] namechar [A-Z_a-z\-\.0-9]
ncname {namestart}{namechar}* ncname {namestart}{namechar}*
fnname {ncname}\(
%x TOKEN %s TOKEN0
%s TOKEN2
%s QLITERAL %s QLITERAL
%s ALITERAL %s ALITERAL
%% %%
<TOKEN>[ \t] <TOKEN0>[ \t]
<TOKEN>\n { _XPY->xpy_linenum++; } <TOKEN0>\n { _XPY->xpy_linenum++; }
<TOKEN>\r { } <TOKEN0>\r { }
<TOKEN><<EOF>> { return X_EOF; } <TOKEN0><<EOF>> { return X_EOF; }
<TOKEN>".." { return DOUBLEDOT; } <TOKEN0>".." { return DOUBLEDOT; }
<TOKEN>[()\[\]\.,/:|] { return *yytext; } <TOKEN0>:: { BEGIN(TOKEN2); return DOUBLECOLON; /* axisname */ }
<TOKEN>and { clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; } <TOKEN0>[(\[] { BEGIN(TOKEN2); return *yytext; }
<TOKEN>or { clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; } <TOKEN0>[)\]\.,/:|] { return *yytext; }
<TOKEN>div { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; } <TOKEN0>and { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN>mod { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; } <TOKEN0>or { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN>[+*\-] { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; } <TOKEN0>div { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>\? { return *yytext; } <TOKEN0>mod { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>"//" { return DOUBLESLASH; } <TOKEN0>[+*\-] { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>"!=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return RELOP; } <TOKEN0>\? { return *yytext; }
<TOKEN>">=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; } <TOKEN0>"//" { BEGIN(TOKEN2);return DOUBLESLASH; }
<TOKEN>"<=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; } <TOKEN0>"!=" { BEGIN(TOKEN2);clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return RELOP; }
<TOKEN>[<>=] { 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; } <TOKEN0>{ncname} { /* See lexical rules 2 and 3 in the file header */
<TOKEN>ancestor:: { clixon_xpath_parselval.intval = A_ANCESTOR; return AXISNAME; } clixon_xpath_parselval.string = strdup(yytext);
<TOKEN>ancestor-or-self:: { clixon_xpath_parselval.intval = A_ANCESTOR_OR_SELF; return AXISNAME; } return NCNAME;
<TOKEN>attribute:: { clixon_xpath_parselval.intval = A_ATTRIBUTE; return AXISNAME; } }
<TOKEN>child:: { clixon_xpath_parselval.intval = A_CHILD; return AXISNAME; } <TOKEN0>. { fprintf(stderr,"LEXICAL ERROR\n"); return -1; }
<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; }
<TOKEN>\" { BEGIN(QLITERAL); return QUOTE; } <TOKEN2>[ \t]
<TOKEN>\' { BEGIN(ALITERAL); return APOST; } <TOKEN2>\n { _XPY->xpy_linenum++; }
<TOKEN>\-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; } <TOKEN2>\r { }
<TOKEN>{ncname} { clixon_xpath_parselval.string = strdup(yytext); <TOKEN2><<EOF>> { return X_EOF; }
return NAME; /* rather be catch-all */ <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); <QLITERAL>[^"]+ { clixon_xpath_parselval.string = strdup(yytext);
return CHARS;} return CHARS;}
<ALITERAL>\' { BEGIN(TOKEN); return APOST; } <ALITERAL>\' { BEGIN(_XPY->xpy_lex_string_state); return APOST; }
<ALITERAL>[^']+ { clixon_xpath_parselval.string = strdup(yytext); <ALITERAL>[^']+ { clixon_xpath_parselval.string = strdup(yytext);
return CHARS;} return CHARS;}
@ -157,7 +201,7 @@ fnname {ncname}\(
int int
xpath_scan_init(clixon_xpath_yacc *xpy) xpath_scan_init(clixon_xpath_yacc *xpy)
{ {
BEGIN(TOKEN); BEGIN(TOKEN0);
xpy->xpy_lexbuf = yy_scan_string (xpy->xpy_parse_string); xpy->xpy_lexbuf = yy_scan_string (xpy->xpy_parse_string);
#if 1 /* XXX: just to use unput to avoid warning */ #if 1 /* XXX: just to use unput to avoid warning */
if (0) if (0)

View file

@ -49,7 +49,6 @@
void *stack; /* xpath_tree */ void *stack; /* xpath_tree */
} }
%token <intval> AXISNAME
%token <intval> LOGOP %token <intval> LOGOP
%token <intval> ADDOP %token <intval> ADDOP
%token <intval> RELOP %token <intval> RELOP
@ -59,12 +58,15 @@
%token <string> QUOTE %token <string> QUOTE
%token <string> APOST %token <string> APOST
%token <string> CHARS %token <string> CHARS
%token <string> NAME %token <string> NCNAME
%token <string> NODETYPE
%token <string> DOUBLEDOT %token <string> DOUBLEDOT
%token <string> DOUBLECOLON
%token <string> DOUBLESLASH %token <string> DOUBLESLASH
%token <string> FUNCTIONNAME %token <string> FUNCTIONNAME
%type <intval> axisspec %type <intval> axisspec
%type <intval> abbreviatedaxisspec
%type <string> string %type <string> string
%type <stack> args %type <stack> args
@ -79,9 +81,13 @@
%type <stack> abslocpath %type <stack> abslocpath
%type <stack> rellocpath %type <stack> rellocpath
%type <stack> step %type <stack> step
%type <stack> abbreviatedstep
%type <stack> nodetest %type <stack> nodetest
%type <stack> nametest
%type <stack> predicates %type <stack> predicates
%type <stack> primaryexpr %type <stack> primaryexpr
%type <stack> literal
%type <stack> functioncall
%lex-param {void *_xpy} /* Add this argument to parse() and lex() function */ %lex-param {void *_xpy} /* Add this argument to parse() and lex() function */
%parse-param {void *_xpy} %parse-param {void *_xpy}
@ -319,15 +325,13 @@ xp_primary_function(clixon_xpath_yacc *xpy,
*/ */
static xpath_tree * static xpath_tree *
xp_nodetest_function(clixon_xpath_yacc *xpy, xp_nodetest_function(clixon_xpath_yacc *xpy,
char *name, char *name)
xpath_tree *xpt)
{ {
xpath_tree *xtret = NULL; xpath_tree *xtret = NULL;
enum clixon_xpath_function fn;
cbuf *cb = NULL; 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){ if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new"); clicon_err(OE_XML, errno, "cbuf_new");
goto done; goto done;
@ -336,7 +340,6 @@ xp_nodetest_function(clixon_xpath_yacc *xpy,
clixon_xpath_parseerror(xpy, cbuf_get(cb)); clixon_xpath_parseerror(xpy, cbuf_get(cb));
goto done; goto done;
} }
fn = ret;
switch (fn){ switch (fn){
case XPATHFN_COMMENT: /* Group of not implemented node functions */ case XPATHFN_COMMENT: /* Group of not implemented node functions */
case XPATHFN_PROCESSING_INSTRUCTIONS: case XPATHFN_PROCESSING_INSTRUCTIONS:
@ -363,16 +366,42 @@ xp_nodetest_function(clixon_xpath_yacc *xpy,
} }
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
xtret = xp_new(XP_NODE_FN, fn, NULL, name, NULL, xpt, NULL); xtret = xp_new(XP_NODE_FN, fn, NULL, name, NULL, NULL, NULL);
name = NULL; /* Consumed by xp_new */
done: done:
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (name)
free(name);
return xtret; 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 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 : primaryexpr { $$=xp_new(XP_FILTEREXPR,A_NAN,NULL,NULL,NULL,$1, NULL);_PARSE_DEBUG("filterexpr-> primaryexpr"); }
/* Filterexpr predicate */ /* 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"); } | 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); } step : nodetest predicates
| '.' predicates { $$=xp_new(XP_STEP,A_SELF, NULL,NULL, NULL, NULL, $2); _PARSE_DEBUG("step-> ."); } { $$=xp_new(XP_STEP, A_CHILD, NULL, NULL, NULL, $1, $2);
| DOUBLEDOT predicates { $$=xp_new(XP_STEP, A_PARENT, NULL,NULL, NULL, NULL, $2); _PARSE_DEBUG("step-> .."); } _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;} /* [13] AbbreviatedAxisSpecifier ::= '@'?
| '@' { $$=A_ATTRIBUTE; _PARSE_DEBUG("axisspec-> @"); } * empty built into 2nd step rule
| { _PARSE_DEBUG("axisspec-> "); $$=A_CHILD;} */
; 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-> *"); } nodetest : nametest { $$ = $1;
| NAME { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, $1, NULL, NULL); _PARSE_DEBUG1("nodetest-> name(%s)",$1); } _PARSE_DEBUG("nodetest-> nametest");}
| NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,NULL, $1, $3, NULL, NULL);_PARSE_DEBUG2("nodetest-> name(%s) : name(%s)", $1, $3); } | NODETYPE ')'
| NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,NULL, $1, NULL, NULL, NULL);_PARSE_DEBUG1("nodetest-> name(%s) : *", $1); } { if (($$ = xp_nodetest_function(_XPY, $1)) == NULL) YYERROR;
| FUNCTIONNAME ')' { if (($$ = xp_nodetest_function(_XPY, $1, NULL)) == NULL) YYERROR;; _PARSE_DEBUG1("nodetest-> nodetype():%s", $1); } _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 */ /* 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->"); } | { $$=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 )"); } 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*/} | 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 \""); } | functioncall { $$ = $1; }
| 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)"); }
; ;
args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, $3); args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, $3);
@ -468,6 +531,29 @@ args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, $3);
_PARSE_DEBUG("args -> expr "); } _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 { string : string CHARS {
int len = strlen($1); int len = strlen($1);
$$ = realloc($1, len+strlen($2) + 1); $$ = realloc($1, len+strlen($2) + 1);

View file

@ -1,5 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# XPATH tests # 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) # Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi 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 xml2=$dir/xml2.xml
xml3=$dir/xml3.xml xml3=$dir/xml3.xml
xml4=$dir/xml4.xml xml4=$dir/xml4.xml
xmlfn=$dir/xmlfn.xml
cat <<EOF > $xml cat <<EOF > $xml
<aaa> <aaa>
@ -94,173 +99,229 @@ cat <<EOF > $xml4
</root> </root>
EOF 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 /" 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" 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" 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" 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>$" 1:<bbb x=\"bye\"><ccc>99</ccc></bbb>$"
new "xpath /aaa/bbb union " 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" 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>" 1:<bbb x=\"bye\"><ccc>99</ccc></bbb>"
new "xpath //b?b" 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*" 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" 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]" 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]" 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'" 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'" 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" 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$" 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" 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" 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'" 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'" 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'" 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'" 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'" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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" 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)" 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']" 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)" 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 # See https://github.com/clicon/clixon/issues/54
# But it is not only axis names. There are also, for example, nodetype like this example: # 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)" new "xpath /aaa/bbb/comment (comment is xpath nodetype)"
#expecteof "$clixon_util_xpath -f $xml2 -p /aaa/bbb/comment" 0 "" "^kalle$" expectpart "$($clixon_util_xpath -D $DBG -f $xml2 -p /aaa/bbb/comment)" 0 "^nodeset:$"
new "Multiple entries" new "Multiple entries"
new "xpath bbb[ccc='foo']" 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\"]" 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']" 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)" 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']" 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']" 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 # Just syntax - no semantic meaning
new "xpath derived-from 10.4.1" 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" 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" 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 # Nodetests
new "xpath nodetest: node" 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" 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 # Count
new "find bbb with 3 ccc children using 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 # Negative
new "xpath dontexist" 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" 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" 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" 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']" 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 rm -rf $dir

View file

@ -1,9 +1,12 @@
#!/usr/bin/env bash #!/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/ # 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
# Tests: # Test of xpath functions:
# - Contains # - contains
# - derived-from
# - derived-from-or-self
# - bit-is-set
# Magic line must be first in script (see README.md) # Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@ -102,6 +105,24 @@ module $APPNAME{
type uint32; 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 EOF
@ -120,7 +141,6 @@ fi
new "wait backend" new "wait backend"
wait_backend wait_backend
# contains
new "contains: Set site to foo that validates site OK" 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>" 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 for f in $files; do
if [ -n "$(head -1 $f|grep '^module')" ]; then if [ -n "$(head -1 $f|grep '^module')" ]; then
# Mask old revision # 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 if [ $f = ${YANG_STANDARD_DIR}/ietf/RFC/ietf-yang-types@2010-09-24.yang ]; then
continue; continue;
fi fi