Very large commit for upcoming 4.4 release

Major New features

* New and updated search functions using xpath, api-path and instance-id
  * New search functions using api-path and instance_id:
    * C search functions: `clixon_find_instance_id()` and `clixon_find_api_path()`
  * Binary search optimization in lists for indexed leafs in all three formats.
    * This improves search performance to O(logN) which is drastical improvements for large lists.
  * You can also register explicit indexes for making binary search (not only list keys)
  * For more info, see docs at [paths](https://clixon-docs.readthedocs.io/en/latest/paths.html) and
[search](https://clixon-docs.readthedocs.io/en/latest/xml.html#searching-in-xml)

API changes on existing features (you may need to change your code)
* On failed validation of leafrefs, error message changed from: `No such leaf` to `No leaf <name> matching path <path>`.
* CLI Error message (clicon_rpc_generate_error()) changed when backend returns netconf error to be more descriptive:
  * Original: `Config error: Validate failed. Edit and try again or discard changes: Invalid argument`
  * New (example): `Netconf error: application operation-failed Identityref validation failed, undefined not derived from acl-base . Validate failed. Edit and try again or discard changes"

Minor changes

* Test framework
  * Added `-- -S <file>` command-line to main example to be able to return any state to main example.
  * Added `test/cicd` test scripts for running on a set of other hosts
* C-code restructuring
  * clixon_yang.c partitioned and moved code into clixon_yang_parse_lib.c and clixon_yang_module.c and move back some code from clixon_yang_type.c.
    * partly to reduce size, but most important to limit code that accesses internal yang structures, only clixon_yang.c does this now.
This commit is contained in:
Olof hagsand 2020-02-02 15:52:30 +01:00
parent e8ae628d06
commit 19e21be0bc
132 changed files with 6241 additions and 2332 deletions

View file

@ -1,7 +1,8 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
# Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
# Copyright (C) 2017-2020 Olof Hagsand
#
# This file is part of CLIXON
#
@ -68,10 +69,10 @@ INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$
SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
clixon_string.c clixon_regex.c clixon_handle.c clixon_file.c \
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c \
clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_json.c \
clixon_yang.c clixon_yang_type.c clixon_yang_module.c clixon_yang_parse_lib.c \
clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \
clixon_api_path.c clixon_validate.c \
clixon_path.c clixon_validate.c \
clixon_hash.c clixon_options.c clixon_data.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \
clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_xpath_optimize.c \
@ -82,7 +83,9 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
lex.clixon_json_parse.o clixon_json_parse.tab.o \
lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o
lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o \
lex.clixon_api_path_parse.o clixon_api_path_parse.tab.o \
lex.clixon_instance_id_parse.o clixon_instance_id_parse.tab.o
# Generated src
GENSRC = build.c
@ -105,10 +108,14 @@ clean:
rm -f clixon_yang_parse.tab.[ch] clixon_yang_parse.[co]
rm -f clixon_json_parse.tab.[ch] clixon_json_parse.[co]
rm -f clixon_xpath_parse.tab.[ch] clixon_xpath_parse.[co]
rm -f clixon_api_path_parse.tab.[ch] clixon_api_path_parse.[co]
rm -f clixon_instance_id_parse.tab.[ch] clixon_instance_id_parse.[co]
rm -f lex.clixon_xml_parse.c
rm -f lex.clixon_yang_parse.c
rm -f lex.clixon_json_parse.c
rm -f lex.clixon_xpath_parse.c
rm -f lex.clixon_api_path_parse.c
rm -f lex.clixon_instance_id_parse.c
#############################################################################
# Implicit rules for lex and yacc.
@ -174,6 +181,32 @@ clixon_xpath_parse.tab.c: clixon_xpath_parse.tab.h
lex.clixon_xpath_parse.o : lex.clixon_xpath_parse.c clixon_xpath_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
# api-path parser
lex.clixon_api_path_parse.c : clixon_api_path_parse.l clixon_api_path_parse.tab.h
$(LEX) -Pclixon_api_path_parse clixon_api_path_parse.l # -d is debug
clixon_api_path_parse.tab.h: clixon_api_path_parse.y
$(YACC) -l -d -b clixon_api_path_parse -p clixon_api_path_parse clixon_api_path_parse.y # -t is debug
# extra rule to avoid parallell yaccs
clixon_api_path_parse.tab.c: clixon_api_path_parse.tab.h
lex.clixon_api_path_parse.o : lex.clixon_api_path_parse.c clixon_api_path_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
# instance-identifier parser
lex.clixon_instance_id_parse.c : clixon_instance_id_parse.l clixon_instance_id_parse.tab.h
$(LEX) -Pclixon_instance_id_parse clixon_instance_id_parse.l # -d is debug
clixon_instance_id_parse.tab.h: clixon_instance_id_parse.y
$(YACC) -l -d -b clixon_instance_id_parse -p clixon_instance_id_parse clixon_instance_id_parse.y # -t is debug
# extra rule to avoid parallell yaccs
clixon_instance_id_parse.tab.c: clixon_instance_id_parse.tab.h
lex.clixon_instance_id_parse.o : lex.clixon_instance_id_parse.c clixon_instance_id_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
distclean: clean
rm -f Makefile *~ .depend

View file

@ -0,0 +1,68 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/
#ifndef _CLIXON_API_PATH_PARSE_H_
#define _CLIXON_API_PATH_PARSE_H_
/*
* Types
*/
struct clicon_api_path_yacc_arg{
const char *ay_name; /* Name of syntax (for error string) */
int ay_linenum; /* Number of \n in parsed buffer */
char *ay_parse_string; /* original (copy of) parse string */
void *ay_lexbuf; /* internal parse buffer from lex */
clixon_path *ay_top;
};
/*
* Variables
*/
extern char *clixon_api_path_parsetext;
/*
* Prototypes
*/
int api_path_scan_init(struct clicon_api_path_yacc_arg *);
int api_path_scan_exit(struct clicon_api_path_yacc_arg *);
int api_path_parse_init(struct clicon_api_path_yacc_arg *);
int api_path_parse_exit(struct clicon_api_path_yacc_arg *);
int clixon_api_path_parselex(void *);
int clixon_api_path_parseparse(void *);
void clixon_api_path_parseerror(void *, char*);
#endif /* _CLIXON_API_PATH_PARSE_H_ */

View file

@ -0,0 +1,150 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.53.
* BNF:
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
* <root> := <string>
* <api-identifier> := [<module-name> ":"] <identifier>
* <module-name> := <identifier>
* <list-instance> := <api-identifier> "=" key-value *("," key-value)
* <key-value> := <string>
* <string> := <an unquoted string>
* <identifier> := (<ALPHA> | "_") (<ALPHA> | <DIGIT> | "_" | "-" | ".")
* @note 1. <root> is the RESTCONF root resource (Sec 3.3) omitted in all calls below, it is
* assumed to be stripped from api-path before calling these functions.
* @note 2. characters in a key value string are constrained, and some characters need to be
* percent-encoded,
* XXX For some reason, I cant use "return *string" in these rules, so I resort to symbols
* (eg slash)
*/
%{
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include "clixon_api_path_parse.tab.h" /* generated */
#include <cligen/cligen.h>
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_xml.h"
#include "clixon_path.h"
#include "clixon_api_path_parse.h"
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */
#define YY_DECL int clixon_api_path_parselex(void *_ay)
/* Dont use input function (use user-buffer) */
#define YY_NO_INPUT
/* typecast macro */
#define _AY ((struct clicon_api_path_yacc_arg *)_ay)
#define MAXBUF 4*4*64*1024
#undef clixon_api_path_parsewrap
int
clixon_api_path_parsewrap(void)
{
return 1;
}
%}
namestart [A-Z_a-z]
namechar [A-Z_a-z\-\.0-9]
identifier {namestart}{namechar}*
%x INIT
%s KEYV
%%
<INIT,KEYV>[ \t]
<INIT,KEYV>\n { _AY->ay_linenum++; }
<INIT,KEYV>\r
<INIT,KEYV><<EOF>> { return X_EOF; }
<INIT>\/ { return SLASH;}
<INIT>\= { BEGIN(KEYV); return EQUAL; }
<INIT>\: { return COLON; }
<INIT>{identifier} { clixon_api_path_parselval.string = strdup(yytext);
return IDENTIFIER; }
<<INIT>. { clixon_api_path_parseerror(_AY, "LEXICAL ERROR\n"); return -1; }
<KEYV>\, { return COMMA; }
<KEYV>\/ { BEGIN(INIT); return SLASH; }
<KEYV>[^:/?#\[\]@,]+ { clixon_api_path_parselval.string = strdup(yytext);
return STRING;}
<KEYV>. { clixon_api_path_parseerror(_AY, "LEXICAL ERROR\n"); return -1; }
%%
/*! Initialize scanner.
*/
int
api_path_scan_init(struct clicon_api_path_yacc_arg *ay)
{
BEGIN(INIT);
ay->ay_lexbuf = yy_scan_string(ay->ay_parse_string);
#if 1 /* XXX: just to use unput to avoid warning */
if (0)
yyunput(0, "");
#endif
return 0;
}
/*
* free buffers
* Even within Flex version 2.5 (this is assumed), freeing buffers is different.
*/
int
api_path_scan_exit(struct clicon_api_path_yacc_arg *ay)
{
yy_delete_buffer(ay->ay_lexbuf);
clixon_api_path_parselex_destroy(); /* modern */
return 0;
}

View file

@ -0,0 +1,298 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
* BNF:
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
* <root> := <string> # See note 1 below
* <root> := <string>
* <api-identifier> := [<module-name> ":"] <identifier>
* <module-name> := <identifier>
* <list-instance> := <api-identifier> "=" key-value *("," key-value)
* <key-value> := <string>
* <string> := <an unquoted string>
* <identifier> := (<ALPHA> | "_") (<ALPHA> | <DIGIT> | "_" | "-" | ".")
* @note 1. <root> is the RESTCONF root resource (Sec 3.3) omitted in all calls below, it is
* assumed to be stripped from api-path before calling these functions.
* @note 2. characters in a key value string are constrained, and some characters need to be
* percent-encoded,
*/
%start start
/* Must be here to define YYSTYPE */
%union {
char *string;
void *stack; /* cv / cvec */
}
%token <string> IDENTIFIER
%token <string> STRING
%token <string> SLASH
%token <string> COLON
%token <string> COMMA
%token <string> EQUAL
%token <string> X_EOF
%type <stack> list
%type <stack> element
%type <stack> api_identifier
%type <string> module_name
%type <stack> list_instance
%type <stack> key_values
%type <stack> key_value
%lex-param {void *_ay} /* Add this argument to parse() and lex() function */
%parse-param {void *_ay}
%{
/* Here starts user C-code */
/* typecast macro */
#define _AY ((struct clicon_api_path_yacc_arg *)_ay)
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_api_path_parsetext, _AY->ay_linenum); YYERROR;}
/* add _yy to error parameters */
#define YY_(msgid) msgid
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <cligen/cligen.h>
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_path.h"
#include "clixon_api_path_parse.h"
/*
also called from yacc generated code *
*/
void
clixon_api_path_parseerror(void *_ay,
char *s)
{
clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'",
_AY->ay_name,
_AY->ay_linenum ,
s,
clixon_api_path_parsetext);
return;
}
int
api_path_parse_init(struct clicon_api_path_yacc_arg *ay)
{
return 0;
}
int
api_path_parse_exit(struct clicon_api_path_yacc_arg *ay)
{
return 0;
}
/*! Append new path structure to clixon path list
*/
static clixon_path *
path_append(clixon_path *list,
clixon_path *new)
{
clicon_debug(1, "%s()", __FUNCTION__);
if (new == NULL)
return NULL;
ADDQ(new, list);
return list;
}
/*! Add keyvalue to existing clixon path
*/
static clixon_path *
path_add_keyvalue(clixon_path *cp,
cvec *cvk)
{
clicon_debug(1, "%s()", __FUNCTION__);
if (cp)
cp->cp_cvk = cvk;
return cp;
}
static clixon_path *
path_new(char *module_name,
char *id)
{
clixon_path *cp = NULL;
clicon_debug(1, "%s(%s,%s)", __FUNCTION__, module_name, id);
if ((cp = malloc(sizeof(*cp))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(cp, 0, sizeof(*cp));
if (module_name)
if ((cp->cp_prefix = strdup(module_name)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
if ((cp->cp_id = strdup(id)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
return cp;
done:
return NULL;
}
/*! Append a key-value cv to a cvec, create the cvec if not exist
* @param[in] cvv Either created cvv or NULL, in whihc case it is created
* @param[in] cv Is consumed by thius function (if appended)
* @retval NULL Error
* @retval cvv Cvec
*/
static cvec *
keyval_add(cvec *cvv,
cg_var *cv)
{
clicon_debug(1, "%s()", __FUNCTION__);
if (cv == NULL)
goto done;
if (cvv == NULL &&
(cvv = cvec_new(0)) == NULL) {
clicon_err(OE_UNIX, errno, "cvec_new");
goto done;
}
if (cvec_append_var(cvv, cv) == NULL){
clicon_err(OE_UNIX, errno, "cvec_append_var");
cvv = NULL;
goto done;
}
cv_free(cv);
done:
return cvv;
}
/*! Create a single key-value as cv and return it
*/
static cg_var *
keyval_set(char *name,
char *val)
{
cg_var *cv = NULL;
clicon_debug(1, "%s(%s=%s)", __FUNCTION__, name, val);
if ((cv = cv_new(CGV_STRING)) == NULL){
clicon_err(OE_UNIX, errno, "cv_new");
goto done;
}
if (name && cv_name_set(cv, name) == NULL){
clicon_err(OE_UNIX, errno, "cv_string_set");
cv = NULL;
goto done;
}
if (cv_string_set(cv, val) == NULL){
clicon_err(OE_UNIX, errno, "cv_string_set");
cv = NULL;
goto done;
}
done:
return cv;
}
%}
%%
/*
*/
start : list X_EOF {clicon_debug(2,"top");_AY->ay_top=$1; YYACCEPT; }
;
list : list SLASH element { if (($$ = path_append($1, $3)) == NULL) YYABORT;
clicon_debug(2,"list = list / element");}
| { $$ = NULL;
clicon_debug(2,"list = ");}
;
element : api_identifier { $$=$1;
clicon_debug(2,"element = api_identifier");}
| list_instance { $$=$1;
clicon_debug(2,"element = list_instance");}
;
api_identifier : module_name COLON IDENTIFIER { $$ = path_new($1, $3); free($1); free($3);
clicon_debug(2,"api_identifier = module_name : IDENTIFIER");}
| IDENTIFIER { $$ = path_new(NULL, $1); free($1);
clicon_debug(2,"api_identifier = IDENTIFIER");}
;
module_name : IDENTIFIER { $$ = $1;
clicon_debug(2,"module_name = IDENTIFIER");}
;
list_instance : api_identifier EQUAL key_values { $$ = path_add_keyvalue($1, $3);
clicon_debug(2,"list_instance->api_identifier = key_values");}
;
key_values : key_values COMMA key_value { if (($$ = keyval_add($1, $3)) == NULL) YYABORT;
clicon_debug(2,"key_values->key_values , key_value");}
| key_value { if (($$ = keyval_add(NULL, $1)) == NULL) YYABORT;
clicon_debug(2,"key_values->key_value");}
;
key_value : STRING { $$ = keyval_set(NULL, $1); free($1); clicon_debug(2,"keyvalue->STRING"); }
| { $$ = keyval_set(NULL, ""); clicon_debug(2,"keyvalue->"); }
;
%%

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -853,7 +853,6 @@ xmldb_put(clicon_handle h,
xml_name(x1));
goto done;
}
if ((de = clicon_db_elmnt_get(h, db)) != NULL){
if (clicon_datastore_cache(h) != DATASTORE_NOCACHE)
x0 = de->de_xml;

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -88,6 +89,7 @@ static struct errvec EV[] = {
{"Daemon error", OE_DAEMON},
{"Event error", OE_EVENTS},
{"Config error", OE_CFG},
{"Netconf error", OE_NETCONF},
{"Protocol error", OE_PROTO},
{"Regexp error", OE_REGEX},
{"UNIX error", OE_UNIX},

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -60,10 +60,11 @@
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_yang_type.h"
#include "clixon_options.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_yang_type.h"
#include "clixon_yang_module.h"
#include "clixon_xml_sort.h"
#include "clixon_xml_map.h"
#include "clixon_xml_nsctx.h" /* namespace context */
@ -331,7 +332,7 @@ json2xml_decode_identityref(cxobj *x,
if (yang_find_prefix_by_namespace(y, namespace, &prefix2) == 0){
#ifndef IDENTITYREF_KLUDGE
/* Just get the prefix from the module's own namespace */
if (netconf_unknown_namespace_xml(xerr, "application",
if (xerr && netconf_unknown_namespace_xml(xerr, "application",
namespace,
"No local prefix corresponding to namespace") < 0)
goto done;
@ -365,7 +366,7 @@ json2xml_decode_identityref(cxobj *x,
goto done;
}
else{
if (netconf_unknown_namespace_xml(xerr, "application",
if (xerr && netconf_unknown_namespace_xml(xerr, "application",
prefix,
"No module corresponding to prefix") < 0)
goto done;
@ -1187,7 +1188,7 @@ json_parse(char *str,
*
* @code
* cxobj *cx = NULL;
* if (json_parse_str(str, &cx) < 0)
* if (json_parse_str(str, yspec, &cx, &xerr) < 0)
* err;
* xml_free(cx);
* @endcode

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -82,7 +82,7 @@ integer {digit}+
real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
exp ({integer}|{real})[eE][+-]{integer}
%x START
%x START§
%s STRING
%s ESCAPE

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -245,7 +245,7 @@ json_current_body(struct clicon_json_yacc_arg *jy,
*/
/* top: json -> value is also possible */
json : value J_EOF { clicon_debug(2,"json->object"); YYACCEPT; }
§json : value J_EOF { clicon_debug(2,"json->object"); YYACCEPT; }
;
value : J_TRUE { json_current_body(_JY, "true");}

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -498,7 +498,7 @@ nacm_data_read_xr(cxobj *xt,
* For NETCONF filtering purposes, the selection criteria are applied to the
* subset of nodes that the user is authorized to read, not the entire datastore.
* @note assume mode is internal or external, not disabled
* @node There is unclarity on what "a data node" means wrt a read operation.
* @note There is unclarity on what "a data node" means wrt a read operation.
* Suppose a tree is accessed. Is "the data node" just the top of the tree?
* (1) Or is it all nodes, recursively, in the data-tree?
* (2) Or is the datanode only the requested tree, NOT the whole datatree?

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -66,6 +67,7 @@
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_yang_module.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_netconf_lib.h"
/*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -72,6 +73,7 @@
#include "clixon_data.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_nsctx.h"
#include "clixon_validate.h"

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -31,17 +31,30 @@
***** END LICENSE BLOCK *****
* This file has code for several variants of paths in cxobj trees:
* - api-path as defined by RESTCONF
* - instance-identifier as defined by YANG
* - clixon-path is an internal format which both ^ use as internal representation
*
* "Instance-identifier" is a subset of XML Xpaths and defined in Yang, used in NACM for example.
* and defined in RF7950 Sections 9.13 and 14.
* To note: prefixes depend on the XML context in which the value occurs,
*
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
* BNF:
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
* <root> := <string>
* <root> := <string> # See note 1 below
* <api-identifier> := [<module-name> ":"] <identifier>
* <module-name> := <identifier>
* <list-instance> := <api-identifier> "=" key-value *("," key-value)
* <key-value> := <string>
* <string> := <an unquoted string>
* <identifier> := (<ALPHA> | "_") (<ALPHA> | <DIGIT> | "_" | "-" | ".")
*
* @note 1. <root> is the RESTCONF root resource (Sec 3.3) omitted in all calls below, it is
* assumed to be stripped from api-path before calling these functions.
* @note 2. characters in a key value string are constrained, and some characters need to be
* percent-encoded,
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
@ -75,9 +88,158 @@
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_sort.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_map.h"
#include "clixon_api_path.h"
#include "clixon_yang_module.h"
#include "clixon_path.h"
#include "clixon_api_path_parse.h"
#include "clixon_instance_id_parse.h"
/*! Given api-path, parse it, and return a clixon-path struct
*
* @param[in] api_path String with api-path syntax according to RESTCONF RFC8040
* @param[out] cplist Structured internal clixon-path
* @retval 0 OK
* @retval -1 Error
* @code
* clixon_path *cplist = NULL;
* if (api_path_parse(api_path, &cplist) < 0)
* err;
* if (api_path_resolve(cplist, yt) < 0)
* err;
* ...
* if (cplist)
* clixon_path_free(cplist);
* @endcode
* @see clixon_path_free
*/
static int
api_path_parse(char *api_path,
clixon_path **cplist)
{
int retval = -1;
struct clicon_api_path_yacc_arg ay = {0,};
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
ay.ay_parse_string = api_path;
ay.ay_name = "api-path parser";
ay.ay_linenum = 1;
if (api_path_scan_init(&ay) < 0)
goto done;
if (api_path_parse_init(&ay) < 0)
goto done;
if (clixon_api_path_parseparse(&ay) != 0) { /* yacc returns 1 on error */
clicon_log(LOG_NOTICE, "API-PATH error: on line %d", ay.ay_linenum);
if (clicon_errno == 0)
clicon_err(OE_XML, 0, "API-PATH parser error with no error code (should not happen)");
goto done;
}
api_path_parse_exit(&ay);
api_path_scan_exit(&ay);
*cplist = ay.ay_top;
retval = 0;
done:
return retval;
}
/*! Given instance-id path, parse it, and return an clixon-path struct
*
* @param[in] api_path String with syntax according to YANG RFC7980
* @param[out] cplist Structured internal clixon-path
* @retval 0 OK
* @retval -1 Error
* @code
* clixon_path *cplist = NULL;
* if (instance_id_parse(api_path, &cplist) < 0)
* err;
* ...
* if (cplist)
* clixon_path_free(cplist);
* @endcode
* @see clixon_path_free
*/
static int
instance_id_parse(char *path,
clixon_path **cplist)
{
int retval = -1;
struct clicon_instance_id_yacc_arg iy = {0,};
clicon_debug(1, "%s path:%s", __FUNCTION__, path);
iy.iy_parse_string = path;
iy.iy_name = "instance-id parser";
iy.iy_linenum = 1;
if (instance_id_scan_init(&iy) < 0)
goto done;
if (instance_id_parse_init(&iy) < 0)
goto done;
if (clixon_instance_id_parseparse(&iy) != 0) { /* yacc returns 1 on error */
clicon_log(LOG_NOTICE, "Instance-id error: on line %d", iy.iy_linenum);
if (clicon_errno == 0)
clicon_err(OE_XML, 0, "Instance-id parser error with no error code (should not happen)");
goto done;
}
instance_id_parse_exit(&iy);
instance_id_scan_exit(&iy);
*cplist = iy.iy_top;
retval = 0;
done:
return retval;
}
static int
clixon_path_free(clixon_path *cplist)
{
clixon_path *cp;
while ((cp = cplist) != NULL){
DELQ(cp, cplist, clixon_path *);
if (cp->cp_prefix)
free(cp->cp_prefix);
if (cp->cp_id)
free(cp->cp_id);
if (cp->cp_cvk)
cvec_free(cp->cp_cvk);
free(cp);
}
return 0;
}
/*! Print path on instance-id/xpath form
*/
static int
clixon_path_print(FILE *f,
clixon_path *cplist)
{
clixon_path *cp;
cg_var *cv;
if ((cp = cplist) != NULL){
do {
fprintf(f, "/");
if (cp->cp_prefix)
fprintf(f, "%s:", cp->cp_prefix);
fprintf(f, "%s", cp->cp_id);
if (cp->cp_cvk){
fprintf(f, "=");
cv = NULL;
while ((cv = cvec_each(cp->cp_cvk, cv)) != NULL){
fprintf(f, "[");
/* If cvk has one integer argument, interpret as position, eg x/y[42] */
if (cvec_len(cp->cp_cvk) == 1 && (cv_type_get(cv) == CGV_UINT32))
fprintf(f, "%u", cv_uint32_get(cv));
else
fprintf(f, "%s=\"%s\"", cv_name_get(cv), cv_string_get(cv));
fprintf(f, "]");
}
}
cp = NEXTQ(clixon_path *, cp);
} while (cp && cp != cplist);
}
fprintf(f, "\n");
return 0;
}
/*! Given an XML node, return root node
* A root node is an ancestor xr of x with one or both of the following properties
@ -936,7 +1098,7 @@ api_path2xml(char *api_path,
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (*api_path!='/'){
if (*api_path != '/'){
cprintf(cberr, "Invalid api-path: %s (must start with '/')", api_path);
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
goto done;
@ -955,8 +1117,8 @@ api_path2xml(char *api_path,
}
nvec--; /* NULL-terminated */
if ((retval = api_path2xml_vec(vec+1, nvec,
xtop, yspec, nodeclass, strict,
xbotp, ybotp, xerr)) < 1)
xtop, yspec, nodeclass, strict,
xbotp, ybotp, xerr)) < 1)
goto done;
xml_yang_root(*xbotp, &xroot);
if (xmlns_assign(xroot) < 0)
@ -1062,3 +1224,409 @@ xml2api_path_1(cxobj *x,
done:
return retval;
}
/*! Resolve api-path module:names to yang statements
* @param[in] cplist Lisp of clixon-path
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
* @retval -1 Error
* @retval 0 Fail
* @retval 1 OK
* Reasons for fail (retval = 0) are: XXX
* - Modulename in api-path does not correspond to existing module
* - Modulename not defined for top-level id.
* - Corresponding yang node for id not found
* - Number of keys in key-value list does not match Yang list
* - Key-values only defined for list or leaf-list
* @see instance_id_resolve
*/
static int
api_path_resolve(clixon_path *cplist,
yang_stmt *yt)
{
int retval = -1;
clixon_path *cp;
yang_stmt *yc;
int i;
cg_var *cva;
cg_var *cvy;
if ((cp = cplist) != NULL){
do {
if (yang_keyword_get(yt) == Y_SPEC){
if (cp->cp_prefix == NULL){
clicon_err(OE_YANG, ENOENT, "Modulename not defined for top-level id.");
goto fail;
}
if ((yt = yang_find_module_by_name(yt, cp->cp_prefix)) == NULL){
clicon_err(OE_YANG, ENOENT, "Modulename in api-path does not correspond to existing module");
goto fail;
}
}
if ((yc = yang_find_datanode(yt, cp->cp_id)) == NULL){
clicon_err(OE_YANG, ENOENT, "Corresponding yang node for id not found");
goto fail;
}
cp->cp_yang = yc;
if (cp->cp_cvk){
/* Iterate over yang list keys and assign as names (or "." for leaf-list) in cvk */
if (yang_keyword_get(yc) == Y_LEAF_LIST){
cva = NULL;
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
if (cv_name_get(cva) == NULL)
cv_name_set(cva, ".");
}
}
else if (yang_keyword_get(yc) == Y_LIST){
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list");
goto fail;
}
i = 0;
cva = NULL;
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
if (cv_name_get(cva) == NULL){
cvy = cvec_i(yang_cvec_get(yc), i);
cv_name_set(cva, cv_string_get(cvy));
}
i++;
}
}
else{
clicon_err(OE_YANG, ENOENT, "key-values only defined for list or leaf-list");
goto fail;
}
}
yt = yc;
cp = NEXTQ(clixon_path *, cp);
} while (cp && cp != cplist);
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Resolve instance-id prefix:names to yang statements
* @param[in] cplist Lisp of clixon-path
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
* @param[in] xt XML statement for prefix context
* @retval -1 Error
* @retval 0 Fail
* @retval 1 OK
* @note: The spec says: prefixes depend on the XML context in which the value occurs. However,
* canonical prefixes/namespaces are used based on loaded yang modules.
* This means that prefix=NULL is not allowed.
* Reasons for fail (retval = 0) are:
* - No prefix of identifier (keynames may omit prefix)
* - Prefix does not correspond to existing module.
* - Corresponding yang node for id not found
* - Number of keys in key-value list does not match Yang list
* - Key-values only defined for list or leaf-list
* @see api_path_resolve
*/
static int
instance_id_resolve(clixon_path *cplist,
yang_stmt *yt)
{
int retval = -1;
clixon_path *cp;
yang_stmt *yc;
int i;
cg_var *cva;
cg_var *cvy;
yang_stmt *yspec;
yspec = ys_spec(yt);
if ((cp = cplist) != NULL){
do {
if (cp->cp_prefix == NULL){
clicon_err(OE_YANG, ENOENT, "No prefix of identifier (keynames may omit prefix)");
goto fail;
}
if (yang_keyword_get(yt) == Y_SPEC){
if ((yt = yang_find_module_by_prefix_yspec(yspec, cp->cp_prefix)) == NULL){
clicon_err(OE_YANG, ENOENT, "Prefix does not correspond to existing module.");
goto fail;
}
}
if ((yc = yang_find_datanode(yt, cp->cp_id)) == NULL){
clicon_err(OE_YANG, ENOENT, "Corresponding yang node for id not found");
goto fail;
}
cp->cp_yang = yc;
if (cp->cp_cvk){
/* Iterate over yang list keys and assign as names in cvk */
if (yang_keyword_get(yc) == Y_LEAF_LIST)
;
else if (yang_keyword_get(yc) == Y_LIST){
#ifdef notused
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list ");
goto fail;
}
#endif
i = 0;
cva = NULL;
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
if (cv_name_get(cva) == NULL){
cvy = cvec_i(yang_cvec_get(yc), i);
cv_name_set(cva, cv_string_get(cvy));
}
i++;
}
}
else{
clicon_err(OE_YANG, ENOENT, "key-values only defined for list or leaf-list");
goto fail;
}
}
yt = yc;
cp = NEXTQ(clixon_path *, cp);
} while (cp && cp != cplist);
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Search XML tree using Clixon path struct
*
* @param[in] xt Top xml-tree where to search
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
* @param[in] cplist Lisp of clixon-path
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
* @param[out] xlen Returns length of vector in return value
* @retval -1 Error
* @retval 0 Fail fail: eg no yang
* @retval 1 OK with found xml nodes in xvec (if any)
*/
static int
clixon_path_search(cxobj *xt,
yang_stmt *yt,
clixon_path *cplist,
cxobj ***xvec0,
size_t *xlen0)
{
int retval = -1;
char *modns; /* Module namespace of api-path */
clixon_path *cp;
cxobj **xvecp = NULL; /* parent set */
size_t xlenp = 0;
cxobj **xvecc = NULL; /* child set */
size_t xlenc = 0;
yang_stmt *yc;
cxobj *xp;
int i;
cg_var *cv;
modns = NULL;
if ((cp = cplist) != NULL){
cxvec_append(xt, &xvecp, &xlenp); /* Initialize parent xml set */
do {
yc = cp->cp_yang;
if ((modns = yang_find_mynamespace(yc)) == NULL)
goto fail;
for (i=0; i<xlenp; i++){
xp = xvecp[i]; /* Iterate over parent set */
if (cp->cp_cvk && /* cornercase for instance-id [<pos>] special case */
(yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST) &&
cvec_len(cp->cp_cvk) == 1 && (cv = cvec_i(cp->cp_cvk,0)) &&
(cv_type_get(cv) == CGV_UINT32)){
if (clixon_xml_find_pos(xp, yc, cv_uint32_get(cv), &xvecc, &xlenc) < 0)
goto done;
}
else if (clixon_xml_find_index(xp, yang_parent_get(yc), modns, yang_argument_get(yc),
cp->cp_cvk, &xvecc, &xlenc) < 0)
goto done;
}
if (xvecp)
free(xvecp);
xvecp = xvecc; xlenp = xlenc;
xvecc = NULL, xlenc = 0;
cp = NEXTQ(clixon_path *, cp);
} while (cp && cp != cplist);
*xvec0 = xvecp; xvecp = NULL;
*xlen0 = xlenp;
}
retval = 1;
done:
if (xvecp)
free(xvecp);
if (xvecc)
free(xvecc);
return retval;
fail: /* eg no yang for api-path, no match */
*xvec0 = NULL;
*xlen0 = 0;
retval = 0;
goto done;
}
/*! Given RESTCONF api-path and an XML tree, return matching xml node vector using stdarg
*
* @param[in] xt Top xml-tree where to search
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
* @param[out] xlen Returns length of vector in return value
* @param[in] format Format string for api-path syntax
* @retval -1 Error
* @retval 0 Nomatch
* @retval 1 OK with found xml nodes in xvec (if any)
* Reasons for nomatch (retval = 0) are:
* - Modulename in api-path does not correspond to existing module
* - Modulename not defined for top-level id.
* - Number of keys in key-value list does not match Yang list
* @code
* cxobj **xvec = NULL;
* size_t xlen;
* if (clixon_xml_find_api_path(x, &xvec, &xlen, "/symbol/%s", "foo") < 0)
* goto err;
* for (i=0; i<xlen; i++){
* xn = xvec[i];
* ...
* }
* free(xvec);
* @endcode
* @see xpath_vec
*/
int
clixon_xml_find_api_path(cxobj *xt,
yang_stmt *yt,
cxobj ***xvec,
size_t *xlen,
char *format,
...)
{
int retval = -1;
va_list ap;
size_t len;
char *api_path = NULL;
clixon_path *cplist = NULL;
int ret;
va_start(ap, format);
len = vsnprintf(NULL, 0, format, ap);
va_end(ap);
/* allocate an api-path string exactly fitting the length */
if ((api_path = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: actually compute api-path string content */
va_start(ap, format);
if (vsnprintf(api_path, len+1, format, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
/* Parse api-path string to structured clixon-path data */
if (api_path_parse(api_path, &cplist) < 0)
goto done;
if (debug)
clixon_path_print(stderr, cplist);
/* Resolve module:name to yang-stmt, fail if not successful */
if ((ret = api_path_resolve(cplist, yt)) < 0)
goto done;
if (ret == 0)
goto fail;
retval = clixon_path_search(xt, yt, cplist, xvec, xlen);
done:
if (cplist)
clixon_path_free(cplist);
if (api_path)
free(api_path);
return retval;
fail:
retval = 0;
goto done;
}
/*! Given (instance-id) path and an XML tree, return matching xml node vector using stdarg
*
* Instance-identifier is a subset of XML Xpaths and defined in Yang, used in NACM for example.
* @param[in] xt Top xml-tree where to search
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
* @param[out] xlen Returns length of vector in return value
* @param[in] format Format string for api-path syntax
* @retval -1 Error
* @retval 0 Nomatch
* @retval 1 OK with found xml nodes in xvec (if any)
* Reasons for nomatch (retval = 0) are:
* - Modulename in api-path does not correspond to existing module
* - Modulename not defined for top-level id.
* - Number of keys in key-value list does not match Yang list
* @code
* cxobj **xvec = NULL;
* size_t xlen;
* if (clixon_xml_find_instance_id(x, &xvec, &xlen, "/symbol/%s", "foo") < 0)
* goto err;
* for (i=0; i<xlen; i++){
* xn = xvec[i];
* ...
* }
* free(xvec);
* @endcode
* @see xpath_vec for full XML Xpaths
* @see api_path_search for RESTCONF api-paths
* @see RFC7950 Sec 9.13
*/
int
clixon_xml_find_instance_id(cxobj *xt,
yang_stmt *yt,
cxobj ***xvec,
size_t *xlen,
char *format,
...)
{
int retval = -1;
va_list ap;
size_t len;
char *path = NULL;
clixon_path *cplist = NULL;
int ret;
va_start(ap, format);
len = vsnprintf(NULL, 0, format, ap);
va_end(ap);
/* allocate a path string exactly fitting the length */
if ((path = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: actually compute api-path string content */
va_start(ap, format);
if (vsnprintf(path, len+1, format, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
if (instance_id_parse(path, &cplist) < 0)
goto done;
if (debug)
clixon_path_print(stderr, cplist);
/* Resolve module:name to yang-stmt, fail if not successful */
if ((ret = instance_id_resolve(cplist, yt)) < 0)
goto done;
if (ret == 0)
goto fail;
if ((retval = clixon_path_search(xt, yt, cplist, xvec, xlen)) < 0)
goto done;
done:
if (cplist)
clixon_path_free(cplist);
if (path)
free(path);
return retval;
fail:
retval = 0;
goto done;
}

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -247,12 +248,10 @@ clicon_rpc_generate_error(cxobj *xerr,
}
if (netconf_err2cb(xerr, cb) < 0)
goto done;
if (arg){
cprintf(cb, "%s: \"%s\": ", msg, arg);
clicon_err(OE_CFG, EINVAL, "%s", cbuf_get(cb));
}
else /* XXX: should really be clicon_err but dont do it yet for backward compatability */
clicon_log(LOG_ERR, "%s: %s", msg, cbuf_get(cb));
cprintf(cb, ". %s", msg);
if (arg)
cprintf(cb, " \"%s\" ", arg);
clicon_err(OE_NETCONF, 0, "%s", cbuf_get(cb));
retval = 0;
done:
if (cb)

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -71,22 +72,14 @@
#include "clixon_xml_nsctx.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#if 0
#include "clixon_plugin.h"
#include "clixon_log.h"
#include "clixon_xml_sort.h"
#include "clixon_yang_internal.h" /* internal */
#endif
#include "clixon_yang_module.h"
#include "clixon_yang_type.h"
#include "clixon_xml_map.h"
#include "clixon_validate.h"
/*! Validate xml node of type leafref, ensure the value is one of that path's reference
* @param[in] xt XML leaf node of type leafref
* @param[in] ys Yang spec of leaf
* @param[in] ytype Yang type statement belonging to the XML node
* @param[out] xret Error XML tree. Free with xml_free after use
* @retval 1 Validation OK
@ -97,17 +90,19 @@
* in addition to the definition in Section 6.4.1:
* o If the "path" statement is defined within a typedef, the context
* node is the leaf or leaf-list node in the data tree that
* references the typedef.
* references the typedef. (ie ys)
* o Otherwise, the context node is the node in the data tree for which
* the "path" statement is defined.
* the "path" statement is defined. (ie yc)
*/
static int
validate_leafref(cxobj *xt,
yang_stmt *ys,
yang_stmt *ytype,
cxobj **xret)
{
int retval = -1;
yang_stmt *ypath;
yang_stmt *yp;
cxobj **xvec = NULL;
cxobj *x;
int i;
@ -115,6 +110,8 @@ validate_leafref(cxobj *xt,
char *leafrefbody;
char *leafbody;
cvec *nsc = NULL;
cbuf *cberr = NULL;
char *path;
if ((leafrefbody = xml_body(xt)) == NULL)
goto ok;
@ -123,10 +120,17 @@ validate_leafref(cxobj *xt,
goto done;
goto fail;
}
/* XXX see comment above regarding typeref or not */
if (xml_nsctx_yang(ytype, &nsc) < 0)
goto done;
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0)
/* See comment^: If path is defined in typedef or not */
if ((yp = yang_parent_get(ytype)) != NULL &&
yang_keyword_get(yp) == Y_TYPEDEF){
if (xml_nsctx_yang(ys, &nsc) < 0)
goto done;
}
else
if (xml_nsctx_yang(ytype, &nsc) < 0)
goto done;
path = yang_argument_get(ypath);
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, path) < 0)
goto done;
for (i = 0; i < xlen; i++) {
x = xvec[i];
@ -136,13 +140,20 @@ validate_leafref(cxobj *xt,
break;
}
if (i==xlen){
if (netconf_bad_element_xml(xret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0)
if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cberr, "Leafref validation failed: No leaf %s matching path %s", leafrefbody, path);
if (netconf_bad_element_xml(xret, "application", leafrefbody, cbuf_get(cberr)) < 0)
goto done;
goto fail;
}
ok:
retval = 1;
done:
if (cberr)
cbuf_free(cberr);
if (nsc)
xml_nsctx_free(nsc);
if (xvec)
@ -153,7 +164,6 @@ validate_leafref(cxobj *xt,
goto done;
}
/*! Validate xml node of type identityref, ensure value is a defined identity
* Check if a given node has value derived from base identity. This is
* a run-time check necessary when validating eg netconf.
@ -963,7 +973,6 @@ xml_yang_validate_add(clicon_handle h,
goto fail;
}
}
if ((ys_cv_validate(h, cv, yt, &reason)) != 1){
if (netconf_bad_element_xml(xret, "application", yang_argument_get(yt), reason) < 0)
goto done;
@ -1064,6 +1073,7 @@ xml_yang_validate_all(clicon_handle h,
cxobj *x;
char *namespace = NULL;
cbuf *cb = NULL;
cvec *nsc = NULL;
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
@ -1102,7 +1112,7 @@ xml_yang_validate_all(clicon_handle h,
if (yang_type_get(ys, NULL, &yc, NULL, NULL, NULL, NULL, NULL) < 0)
goto done;
if (strcmp(yang_argument_get(yc), "leafref") == 0){
if ((ret = validate_leafref(xt, yc, xret)) < 0)
if ((ret = validate_leafref(xt, ys, yc, xret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -1124,7 +1134,9 @@ xml_yang_validate_all(clicon_handle h,
if (yang_keyword_get(yc) != Y_MUST)
continue;
xpath = yang_argument_get(yc); /* "must" has xpath argument */
if ((nr = xpath_vec_bool(xt, NULL, "%s", xpath)) < 0)
if (xml_nsctx_yang(yc, &nsc) < 0)
goto done;
if ((nr = xpath_vec_bool(xt, nsc, "%s", xpath)) < 0)
goto done;
if (!nr){
ye = yang_find(yc, Y_ERROR_MESSAGE, NULL);
@ -1133,6 +1145,10 @@ xml_yang_validate_all(clicon_handle h,
goto done;
goto fail;
}
if (nsc){
xml_nsctx_free(nsc);
nsc = NULL;
}
}
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
if ((yc = yang_find(ys, Y_WHEN, NULL)) != NULL){
@ -1167,6 +1183,8 @@ xml_yang_validate_all(clicon_handle h,
done:
if (cb)
cbuf_free(cb);
if (nsc)
xml_nsctx_free(nsc);
return retval;
fail:
retval = 0;

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -62,6 +63,7 @@
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_options.h" /* xml_spec_populate */
#include "clixon_yang_module.h"
#include "clixon_xml_map.h" /* xml_spec_populate */
#include "clixon_xml_sort.h"
#include "clixon_xml_parse.h"
@ -263,7 +265,7 @@ nscache_get_prefix(cxobj *x,
return 0;
}
/*! Dump whole namespacet context cache of one xml node
/*! Dump whole namespace context cache of one xml node
* @param[in] x XML node
* @retval nsc Whole namespace context of x
* @retval NULL Empty nsc
@ -581,7 +583,7 @@ xml_parent_set(cxobj *xn,
/*! Get xml node flags, used for internal algorithms
* @param[in] xn xml node
* @retval flag Flags value, see XML_FLAG_*
* @retval flag Flag value(s), see XML_FLAG_*
*/
uint16_t
xml_flag(cxobj *xn,
@ -592,7 +594,7 @@ xml_flag(cxobj *xn,
/*! Set xml node flags, used for internal algorithms
* @param[in] xn xml node
* @param[in] flag Flags value to set, see XML_FLAG_*
* @param[in] flag Flag values to set, see XML_FLAG_*
*/
int
xml_flag_set(cxobj *xn,
@ -604,7 +606,7 @@ xml_flag_set(cxobj *xn,
/*! Reset xml node flags, used for internal algorithms
* @param[in] xn xml node
* @param[in] flag Flags value to reset, see XML_FLAG_*
* @param[in] flag Flag value(s) to reset, see XML_FLAG_*
*/
int
xml_flag_reset(cxobj *xn,
@ -1048,8 +1050,12 @@ xml_cv_set(cxobj *x,
*
* @retval xmlobj if found.
* @retval NULL if no such node found.
* @see xml_find_type A more generic function
* @note Linear scalability and relies on strcmp
* There are several issues with this function:
* @note (1) Ignores prefix which means namespaces are ignored
* @note (2) Does not differentiate between element,attributes and body. You usually want elements.
* @note (3) Linear scalability and relies on strcmp, does not use search/key indexes
* @note (4) Only returns first match, eg a list/leaf-list may have several children with same name
* @see xml_find_type A more generic function fixes (1) and (2) above
*/
cxobj *
xml_find(cxobj *x_up,
@ -1494,9 +1500,10 @@ xml_find_type(cxobj *xt,
char *xprefix; /* xprefix */
while ((x = xml_child_each(xt, x, type)) != NULL) {
xprefix = xml_prefix(x);
if (prefix)
pmatch = xprefix?strcmp(prefix,xprefix)==0:0;
if (prefix){
xprefix = xml_prefix(x);
pmatch = xprefix ? strcmp(prefix,xprefix)==0 : 0;
}
else
pmatch = 1;
if (pmatch && strcmp(name, xml_name(x)) == 0)
@ -1980,7 +1987,9 @@ FSM(char *tag,
*
* @code
* cxobj *xt = NULL;
* xml_parse_file(0, "</config>", yspec, &xt);
* int fd;
* fd = open(filename, O_RDONLY);
* xml_parse_file(fd, "</config>", yspec, &xt);
* xml_free(xt);
* @endcode
* @see xml_parse_string
@ -2246,10 +2255,23 @@ cxvec_dup(cxobj **vec0,
return retval;
}
/*! Append a new xml tree to an existing xml vector
/*! Append a new xml tree to an existing xml vector last in the list
* @param[in] x XML tree (append this to vector)
* @param[in,out] vec XML tree vector
* @param[in,out] len Length of XML tree vector
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj **xvec = NULL;
* size_t xlen = 0;
* cxobj *x;
*
* if (cxvec_append(x, &xvec, &xlen) < 0)
* err;
* if (xvec)
* free(xvec);
* @endcode
* @see cxvec_prepend
*/
int
cxvec_append(cxobj *x,
@ -2268,6 +2290,44 @@ cxvec_append(cxobj *x,
return retval;
}
/*! Prepend a new xml tree to an existing xml vector first in the list
* @param[in] x XML tree (append this to vector)
* @param[in,out] vec XML tree vector
* @param[in,out] len Length of XML tree vector
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj **xvec = NULL;
* size_t xlen = 0;
* cxobj *x;
*
* if (cxvec_append(x, &xvec, &xlen) < 0)
* err;
* if (xvec)
* free(xvec);
* @endcode
* @see cxvec_prepend
*/
int
cxvec_prepend(cxobj *x,
cxobj ***vec,
size_t *len)
{
int retval = -1;
if ((*vec = realloc(*vec, sizeof(cxobj *) * (*len+1))) == NULL){
clicon_err(OE_XML, errno, "realloc");
goto done;
}
memmove(&(*vec)[1], &(*vec)[0], sizeof(cxobj *) * (*len));
(*vec)[0] = x;
(*len)++;
retval = 0;
done:
return retval;
}
/*! Apply a function call recursively on all xml node children recursively
* Recursively traverse all xml nodes in a parse-tree and apply fn(arg) for
* each object found. The function is called with the xml node and an
@ -2548,8 +2608,6 @@ xml_operation(char *opstr,
return 0;
}
/*! Map xml operation from enumeration to string
* @param[in] op enumeration operation, eg OP_MERGE,...
* @retval str String, eg "merge". Static string, no free necessary

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -66,6 +67,7 @@
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_yang_module.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_map.h"

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -72,7 +73,6 @@
#include "clixon_err.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_sort.h"
#include "clixon_yang_internal.h" /* internal */
#include "clixon_yang_type.h"
#include "clixon_xml_map.h"
@ -296,7 +296,7 @@ xml2cvec(cxobj *xt,
name = xml_name(xc);
if ((ys = yang_find_datanode(yt, name)) == NULL){
clicon_debug(0, "%s: yang sanity problem: %s in xml but not present in yang under %s",
__FUNCTION__, name, yt->ys_argument);
__FUNCTION__, name, yang_argument_get(yt));
if ((body = xml_body(xc)) != NULL){
if ((cv = cv_new(CGV_STRING)) == NULL){
clicon_err(OE_PLUGIN, errno, "cv_new");
@ -476,7 +476,7 @@ xml_diff1(yang_stmt *ys,
continue;
}
/* Both x0c and x1c exists, check if they are equal. */
eq = xml_cmp(x0c, x1c, 0);
eq = xml_cmp(x0c, x1c, 0, 0);
if (eq < 0){
if (cxvec_append(x0c, x0vec, x0veclen) < 0)
goto done;
@ -502,7 +502,7 @@ xml_diff1(yang_stmt *ys,
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
goto done;
}
else if (yc->ys_keyword == Y_LEAF){
else if (yang_keyword_get(yc) == Y_LEAF){
/* if x0c and x1c are leafs w bodies, then they are changed */
if ((b1 = xml_body(x0c)) == NULL) /* empty type */
break;
@ -761,7 +761,7 @@ xml_default(cxobj *xt,
int retval = -1;
yang_stmt *ys;
yang_stmt *y;
int i;
// int i; // XXX
cxobj *xc;
cxobj *xb;
char *str;
@ -775,15 +775,15 @@ xml_default(cxobj *xt,
goto done;
}
/* Check leaf defaults */
if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST ||
ys->ys_keyword == Y_INPUT){
for (i=0; i<ys->ys_len; i++){
y = ys->ys_stmt[i];
if (y->ys_keyword != Y_LEAF)
if (yang_keyword_get(ys) == Y_CONTAINER || yang_keyword_get(ys) == Y_LIST ||
yang_keyword_get(ys) == Y_INPUT){
y = NULL;
while ((y = yn_each(ys, y)) != NULL) {
if (yang_keyword_get(y) != Y_LEAF)
continue;
if (!cv_flag(yang_cv_get(y), V_UNSET)){ /* Default value exists */
if (!xml_find(xt, y->ys_argument)){
if ((xc = xml_new(y->ys_argument, NULL, y)) == NULL)
if (!xml_find(xt, yang_argument_get(y))){
if ((xc = xml_new(yang_argument_get(y), NULL, y)) == NULL)
goto done;
/* assign right prefix */
if ((namespace = yang_find_mynamespace(y)) != NULL){
@ -843,9 +843,9 @@ xml_sanity(cxobj *xt,
goto done;
}
name = xml_name(xt);
if (strstr(ys->ys_argument, name)==NULL){
if (strstr(yang_argument_get(ys), name)==NULL){
clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
name, ys->ys_argument);
name, yang_argument_get(ys));
goto done;
}
retval = 0;
@ -1126,7 +1126,7 @@ xmlns_assign(cxobj *x)
}
/* 1. Check which namespace x should have (via yang). This is correct namespace. */
if ((ns_correct = yang_find_mynamespace(y)) == NULL){
clicon_err(OE_YANG, ENOENT, "yang node %s does not have namespace", y->ys_argument);
clicon_err(OE_YANG, ENOENT, "yang node %s does not have namespace", yang_argument_get(y));
goto done;
}
/* 2. Check which namespace x has via its XML tree */
@ -1293,7 +1293,7 @@ xml_merge1(cxobj *x0, /* the target */
assert(y0);
x1name = xml_name(x1);
if (y0->ys_keyword == Y_LEAF_LIST || y0->ys_keyword == Y_LEAF){
if (yang_keyword_get(y0) == Y_LEAF_LIST || yang_keyword_get(y0) == Y_LEAF){
x1bstr = xml_body(x1);
if (x0==NULL){
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
@ -1479,7 +1479,7 @@ yang_enum_int_value(cxobj *node,
if (yang_type_resolve(ys, ys, ytype, &yrestype,
NULL, NULL, NULL, NULL, NULL) < 0)
goto done;
if (yrestype==NULL || strcmp(yrestype->ys_argument, "enumeration"))
if (yrestype==NULL || strcmp(yang_argument_get(yrestype), "enumeration"))
goto done;
if ((yenum = yang_find(yrestype, Y_ENUM, xml_body(node))) == NULL)
goto done;
@ -1487,7 +1487,7 @@ yang_enum_int_value(cxobj *node,
if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL)
goto done;
/* reason is string containing why int could not be parsed */
if (parse_int32(yval->ys_argument, val, &reason) < 0)
if (parse_int32(yang_argument_get(yval), val, &reason) < 0)
goto done;
retval = 0;
done:

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -51,7 +52,7 @@ struct xml_parse_yacc_arg{
cxobj *ya_xparent; /* xml parent element*/
int ya_skipspace; /* If set, remove all non-terminal bodies (strip pretty-print) */
yang_stmt *ya_yspec; /* If set, top-level yang-spec */
int ya_lex_state; /* lex start condition (AMPERSAND) */
int ya_lex_state; /* lex return state */
};
extern char *clixon_xml_parsetext;

View file

@ -2,7 +2,8 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2020 Olof Hagsand
This file is part of CLIXON.
@ -31,7 +32,7 @@
***** END LICENSE BLOCK *****
* XML search functions when used with YANG
* XML sort and search functions when used with YANG
*/
#ifdef HAVE_CONFIG_H
@ -65,6 +66,7 @@
#include "clixon_options.h"
#include "clixon_xml_map.h"
#include "clixon_yang_type.h"
#include "clixon_yang_module.h"
#include "clixon_xml_sort.h"
/*! Get xml body value as cligen variable
@ -193,10 +195,10 @@ xml_child_spec(cxobj *x,
* @param[in] x2 object 2
* @param[in] same If set, x1 and x2 are member of same parent & enumeration
* is used (see explanation below)
* @param[in] skip1 Key matching skipped for keys not in x1 (see explanation)
* @retval 0 If equal
* @retval <0 If x1 is less than x2
* @retval >0 If x1 is greater than x2
* @see xml_cmp1 Similar, but for one object
*
* There are distinct calls for this function:
* 1. For sorting in an existing list of XML children
@ -205,11 +207,19 @@ xml_child_spec(cxobj *x,
* if they have the same yang-spec, the existing order is used as tie-breaker.
* In other words, if order-by-system, or if the case (2) above, the existing
* order is ignored and the actual xml element contents is examined.
*
* Also, if pattern is set, list matching is not made so that x1 and x2 must have same keys.
* instead, x1 and x2 are considered equal if the keys that x1 have match. Keys that x2 but not
* x1 has are ignored.
* Example: x1: <x><k1>1</k1></x> and x2: <x><k1>1</k1><k2>2</k2></x> are considered equal.
* This is useful in searching for indexes in trees, where x1 is a search index and not a
* complete tree.
*
* @note empty value/NULL is smallest value
* @note some error cases return as -1 (qsort cant handle errors)
* @note some error cases return as -1 (qsort cant handle errors)
*
* NOTE: "comparing" x1 and x2 here judges equality from a structural (model)
* @note "comparing" x1 and x2 here judges equality from a structural (model)
* perspective, ie both have the same yang spec, if they are lists, they have the
* the same keys. NOT that the values are equal!
* In other words, if x is a leaf with the same yang spec, <x>1</x> and <x>2</x> are
@ -221,7 +231,8 @@ xml_child_spec(cxobj *x,
int
xml_cmp(cxobj *x1,
cxobj *x2,
int same)
int same,
int skip1)
{
yang_stmt *y1;
yang_stmt *y2;
@ -281,9 +292,13 @@ xml_cmp(cxobj *x1,
}
switch (yang_keyword_get(y1)){
case Y_LEAF_LIST: /* Match with name and value */
if ((b1 = xml_body(x1)) == NULL)
b1 = xml_body(x1);
b2 = xml_body(x2);
if (b1 == NULL && b2 == NULL)
;
else if (b1 == NULL)
equal = -1;
else if ((b2 = xml_body(x2)) == NULL)
else if (b2 == NULL)
equal = 1;
else{
if (xml_cv_cache(x1, &cv1) < 0) /* error case */
@ -301,33 +316,47 @@ xml_cmp(cxobj *x1,
}
break;
case Y_LIST: /* Match with key values
* Use Y_LIST cache (see struct yang_stmt)
*/
* Use Y_LIST cache (see struct yang_stmt) */
cvk = yang_cvec_get(y1); /* Use Y_LIST cache, see ys_populate_list() */
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi); /* operational data may have NULL keys*/
if ((x1b = xml_find(x1, keyname)) == NULL ||
xml_body(x1b) == NULL)
x1b = xml_find(x1, keyname);
/* match1: key matching skipped for keys not in x1 (see explanation) */
if (skip1 && x1b == NULL)
continue;
x2b = xml_find(x2, keyname);
if (x1b == NULL && x2b == NULL)
;
else if (x1b == NULL)
equal = -1;
else if ((x2b = xml_find(x2, keyname)) == NULL ||
xml_body(x2b) == NULL)
else if (x2b == NULL)
equal = 1;
else{
if (xml_cv_cache(x1b, &cv1) < 0) /* error case */
goto done;
if (xml_cv_cache(x2b, &cv2) < 0) /* error case */
goto done;
assert(cv1 && cv2);
if ((equal = cv_cmp(cv1, cv2)) != 0)
goto done;
b1 = xml_body(x1b);
b2 = xml_body(x2b);
if (b1 == NULL && b2 == NULL)
;
else if (b1 == NULL)
equal = -1;
else if (b2 == NULL)
equal = 1;
else{
if (xml_cv_cache(x1b, &cv1) < 0) /* error case */
goto done;
if (xml_cv_cache(x2b, &cv2) < 0) /* error case */
goto done;
assert(cv1 && cv2);
equal = cv_cmp(cv1, cv2);
}
}
}
equal = 0;
if (equal)
break;
} /* while cvi */
break;
default:
break;
}
} /* switch */
done:
clicon_debug(2, "%s %s %s %d nr: %d %d yi: %d %d", __FUNCTION__, xml_name(x1), xml_name(x2), equal, nr1, nr2, yi1, yi2);
return equal;
@ -340,7 +369,7 @@ static int
xml_cmp_qsort(const void* arg1,
const void* arg2)
{
return xml_cmp(*(struct xml**)arg1, *(struct xml**)arg2, 1);
return xml_cmp(*(struct xml**)arg1, *(struct xml**)arg2, 1, 0);
}
/*! Sort children of an XML node
@ -366,21 +395,28 @@ xml_sort(cxobj *x,
return 0;
}
/*! Special case search for ordered-by user where linear sort is used
/*! Special case search for ordered-by user or state data where linear sort is used
*
* @param[in] xp Parent XML node (go through its childre)
* @param[in] x1 XML node to match
* @param[in] yangi Yang order number (according to spec)
* @param[in] mid Where to start from (may be in middle of interval)
* @param[out] xretp XML return object, or NULL
* @retval 0 OK, see xretp
* @param[out] xvec Vector of matching XML return objects (can be empty)
* @param[out] xlen Length of xvec
* @retval 0 OK, see xvec (may be empty)
* @retval -1 Error
* XXX: only first match
*/
static int
xml_search_userorder(cxobj *xp,
cxobj *x1,
int yangi,
int mid,
cxobj **xretp)
xml_find_keys_notsorted(cxobj *xp,
cxobj *x1,
int yangi,
int mid,
int skip1,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
int i;
cxobj *xc;
yang_stmt *yc;
@ -388,113 +424,186 @@ xml_search_userorder(cxobj *xp,
for (i=mid+1; i<xml_child_nr(xp); i++){ /* First increment */
xc = xml_child_i(xp, i);
yc = xml_spec(xc);
if (yangi!=yang_order(yc))
if (yangi != yang_order(yc)) /* wrong yang */
break;
if (xml_cmp(xc, x1, 0) == 0){
*xretp = xc;
goto done;
if (xml_cmp(xc, x1, 0, skip1) == 0){
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
goto ok; /* found */
}
}
for (i=mid-1; i>=0; i--){ /* Then decrement */
xc = xml_child_i(xp, i);
yc = xml_spec(xc);
if (yangi!=yang_order(yc))
if (yangi != yang_order(yc)) /* wrong yang */
break;
if (xml_cmp(xc, x1, 0) == 0){
*xretp = xc;
goto done;
if (xml_cmp(xc, x1, 0, skip1) == 0){
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
goto ok; /* found */
}
}
*xretp = NULL;
ok:
retval = 0;
done:
return 0;
return retval;
}
/*! Find more equal objects in a vector up and down in the array of the present
* @param[in] xp Parent XML node (go through its childre)
* @param[in] x1 XML node to match
* @param[in] yangi Yang order number (according to spec)
* @param[in] mid Where to start from (may be in middle of interval)
* @param[out] xvec Vector of matching XML return objects (can be empty)
* @param[out] xlen Length of xvec
* @retval 0 OK, see xvec (may be empty)
* @retval -1 Error
*/
static int
more_equals(cxobj *xp,
cxobj *x1,
int yangi,
int mid,
int skip1,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
int i;
cxobj *xc;
yang_stmt *yc;
for (i=mid-1; i>=0; i--){ /* First decrement */
xc = xml_child_i(xp, i);
yc = xml_spec(xc);
if (yangi != yang_order(yc)) /* wrong yang */
break;
if (xml_cmp(x1, xc, 0, skip1) != 0)
break;
if (cxvec_prepend(xc, xvec, xlen) < 0)
goto done;
}
for (i=mid+1; i<xml_child_nr(xp); i++){ /* Then increment */
xc = xml_child_i(xp, i);
yc = xml_spec(xc);
if (yangi != yang_order(yc)) /* wrong yang */
break;
if (xml_cmp(x1, xc, 0, skip1) != 0)
break;
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Find XML child under xp matching x1 using binary search
* @param[in] xp Parent xml node.
* @param[in] x1 Find this object among xp:s children
* @param[in] userorder If x1 is ordered by user
* @param[in] sorted If x1 is ordered by user or statedata, the list is not sorted
* @param[in] yangi Yang order
* @param[in] low Lower bound of childvec search interval
* @param[in] upper Lower bound of childvec search interval
* @param[out] xretp XML return object, or NULL
* @retval 0 OK, see xretp
* @param[in] skip1 Key matching skipped for keys not in x1
* @param[out] xvec Vector of matching XML return objects (can be empty)
* @param[out] xlen Length of xvec
* @retval 0 OK, see xvec (may be empty)
* @retval -1 Error
*/
static int
xml_search1(cxobj *xp,
cxobj *x1,
int userorder,
int yangi,
int low,
int upper,
cxobj **xretp)
xml_find_keys1(cxobj *xp,
cxobj *x1,
int sorted,
int yangi,
int low,
int upper,
int skip1,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
int mid;
int cmp;
cxobj *xc;
yang_stmt *y;
if (upper < low)
goto notfound;
goto ok;
mid = (low + upper) / 2;
if (mid >= xml_child_nr(xp)) /* beyond range */
goto notfound;
goto ok;
xc = xml_child_i(xp, mid);
if ((y = xml_spec(xc)) == NULL)
goto notfound;
goto ok;
cmp = yangi-yang_order(y);
/* Here is right yang order == same yang? */
if (cmp == 0){
cmp = xml_cmp(x1, xc, 0);
if (cmp && userorder){ /* Ordered by user (if not equal) */
xml_search_userorder(xp, x1, yangi, mid, xretp);
cmp = xml_cmp(x1, xc, 0, skip1);
if (cmp && !sorted){ /* Ordered by user (if not equal) */
retval = xml_find_keys_notsorted(xp, x1, yangi, mid, skip1, xvec, xlen);
goto done;
}
}
if (cmp == 0)
*xretp = xc;
if (cmp == 0){
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
/* there may be more? */
if (more_equals(xp, x1, yangi, mid, skip1, xvec, xlen) < 0)
goto done;
}
else if (cmp < 0)
xml_search1(xp, x1, userorder, yangi, low, mid-1, xretp);
xml_find_keys1(xp, x1, sorted, yangi, low, mid-1, skip1, xvec, xlen);
else
xml_search1(xp, x1, userorder, yangi, mid+1, upper, xretp);
xml_find_keys1(xp, x1, sorted, yangi, mid+1, upper, skip1, xvec, xlen);
ok:
retval = 0;
done:
return 0;
notfound:
*xretp = NULL;
goto done;
return retval;
}
/*! Find XML child under xp matching x1 using binary search
/*! Find XML child under xp matching x1 using binary search for list/leaf-list keys
*
* Match is tried xp with x1 with either name only (container/leaf) or using keys (list/leaf-lists)
* Any non-key leafs or other structure of x1 is not matched.
*
* @param[in] xp Parent xml node.
* @param[in] x1 Find this object among xp:s children
* @param[in] yc Yang spec of x1
* @param[out] xretp XML return object, or NULL
* @retval 0 OK, see xretp
* @param[in] skip1 Key matching skipped for keys not in x1
* @param[out] xvec Vector of matching XML return objects (can be empty)
* @param[out] xlen Length of xvec
* @retval 0 OK, see xvec (may be empty)
* @retval -1 Error
* @see xml_find_index for a generic search function
*/
static int
xml_search(cxobj *xp,
cxobj *x1,
yang_stmt *yc,
cxobj **xretp)
xml_find_keys(cxobj *xp,
cxobj *x1,
yang_stmt *yc,
int skip1,
cxobj ***xvec,
size_t *xlen)
{
cxobj *xa;
int low = 0;
int upper = xml_child_nr(xp);
int userorder=0;
int yangi;
cxobj *xa;
int low = 0;
int upper = xml_child_nr(xp);
int sorted = 1;
int yangi;
/* Assume if there are any attributes, they are first in the list, mask
them by raising low to skip them */
for (low=0; low<upper; low++)
if ((xa = xml_child_i(xp, low)) == NULL || xml_type(xa)!=CX_ATTR)
if ((xa = xml_child_i(xp, low)) == NULL || xml_type(xa) != CX_ATTR)
break;
/* Find if non-config and if ordered-by-user */
if (yang_config(yc)==0)
userorder = 1;
sorted = 0;
else if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST)
userorder = (yang_find(yc, Y_ORDERED_BY, "user") != NULL);
sorted = (yang_find(yc, Y_ORDERED_BY, "user") == NULL);
yangi = yang_order(yc);
return xml_search1(xp, x1, userorder, yangi, low, upper, xretp);
return xml_find_keys1(xp, x1, sorted, yangi, low, upper, skip1, xvec, xlen);
}
/*! Insert xn in xp:s sorted child list (special case of ordered-by user)
@ -652,7 +761,7 @@ xml_insert2(cxobj *xp,
goto done;
}
else /* Ordered by system */
cmp = xml_cmp(xn, xc, 0);
cmp = xml_cmp(xn, xc, 0, 0);
}
else{ /* Not equal yang - compute diff */
cmp = yni - yang_order(yc);
@ -769,7 +878,7 @@ xml_sort_verify(cxobj *x0,
xml_enumerate_children(x0);
while ((x = xml_child_each(x0, x, -1)) != NULL) {
if (xprev != NULL){ /* Check xprev <= x */
if (xml_cmp(xprev, x, 1) > 0)
if (xml_cmp(xprev, x, 1, 0) > 0)
goto done;
}
xprev = x;
@ -779,13 +888,14 @@ xml_sort_verify(cxobj *x0,
return retval;
}
/*! Given child tree x1c, find matching child in base tree x0 and return as x0cp
/*! Given child tree x1c, find (first) matching child in base tree x0 and return as x0cp
* @param[in] x0 Base tree node
* @param[in] x1c Modification tree child
* @param[in] yc Yang spec of tree child
* @param[out] x0cp Matching base tree child (if any)
* @retval 0 OK
* @retval -1 Error
* XXX: only handles first match
*/
int
match_base_child(cxobj *x0,
@ -802,6 +912,8 @@ match_base_child(cxobj *x0,
yang_stmt *y0c;
yang_stmt *y0p;
yang_stmt *yp; /* yang parent */
cxobj **xvec = NULL;
size_t xlen = 0;
*x0cp = NULL; /* init return value */
/* Special case is if yc parent (yp) is choice/case
@ -816,6 +928,7 @@ match_base_child(cxobj *x0,
y0p == yp)
break; /* x0c will have a value */
}
*x0cp = x0c;
goto ok; /* What to do if not found? */
}
switch (yang_keyword_get(yc)){
@ -823,58 +936,146 @@ match_base_child(cxobj *x0,
case Y_LEAF: /* Equal regardless */
break;
case Y_LEAF_LIST: /* Match with name and value */
if (xml_body(x1c) == NULL){ /* Treat as empty string */
// assert(0);
if (xml_body(x1c) == NULL) /* Treat as empty string */
goto ok;
}
break;
case Y_LIST: /* Match with key values */
cvk = yang_cvec_get(yc); /* Use Y_LIST cache, see ys_populate_list() */
/* Count number of key indexes
* Then create two vectors one with names and one with values of x1c,
* ec: keyvec: [a,b,c] keyval: [1,2,3]
*/
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
// keyvec[i] = keyname;
if ((xb = xml_find(x1c, keyname)) == NULL){
if ((xb = xml_find(x1c, keyname)) == NULL)
goto ok;
}
}
default:
break;
}
/* Get match. */
xml_search(x0, x1c, yc, &x0c);
if (xml_find_keys(x0, x1c, yc, 0, &xvec, &xlen) < 0)
goto done;
if (xlen)
*x0cp = xvec[0];
ok:
*x0cp = x0c;
retval = 0;
done:
if (xvec)
free(xvec);
return retval;
}
/*! Experimental API for binary search
*
* Create a temporary search object: a list (xc) with a key (xk) and call the binary search.
* @param[in] xp Parent xml node.
* @param[in] yc Yang spec of list child
* @param[in] cvk List of keys and values as CLIgen vector
* @param[out] xret Found XML object, NULL if not founs
* @retval 0 OK, see xret
* @code
* cvec *cvk = NULL; vector of index keys
* ... Populate cvk with key/values eg a:5 b:6
* if (xml_binsearch(xp, yc, cvk, xp) < 0)
* err;
* @endcode
* @retval -1 Error
* Can extend to leaf-list?
/*! API for search in XML child list with non-indexed variables
*/
int
xml_binsearch(cxobj *xp,
yang_stmt *yc,
cvec *cvk,
cxobj **xretp)
static int
xml_find_noyang_cvk(char *ns0,
cxobj *xc,
cvec *cvk,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
cg_var *cvi;
char *ns;
cxobj *xcc;
char *keyname;
char *keyval;
char *body;
cvi = NULL;
/* Loop through index variables. xc should match all, on exit if cvi=NULL it macthes */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_name_get(cvi);
keyval = cv_string_get(cvi);
if (keyname==NULL || strcmp(keyname, ".")==0){ /* Index variable on form .=<val> (self) */
body = xml_body(xc);
if ((body==NULL && (keyval==NULL || strlen(keyval) == 0)) || /* both null, break */
(body != NULL && strcmp(body, keyval) == 0)) /* Name match, break */
continue; /* match */
break; /* nomatch */
}
else{
/* Index variable on form <id>=<val>
* Loop through children of the matched x (to match keyname and value) */
xcc = NULL;
while ((xcc = xml_child_each(xc, xcc, CX_ELMNT)) != NULL) {
if (xml2ns(xcc, xml_prefix(xcc), &ns) < 0)
goto done;
if (strcmp(ns0, ns) != 0) /* Namespace does not match, skip */
continue;
if (strcmp(keyname, xml_name(xcc)) != 0) /* Name does not match, skip */
continue;
body = xml_body(xcc);
if (body==NULL && (keyval==NULL || strlen(keyval) == 0)) /* both null, break */
break;
if (body != NULL && strcmp(body, keyval) == 0) /* Name match, break */
break;
}
if (xcc == NULL)
break; /* No match found */
}
}
if (cvi == NULL) /* means we iterated through all indexes without breaks */
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! API for search in XML child list with no yang available
* Fallback if no yang available. Only linear search for matching name, and eventual index match
*/
static int
xml_find_noyang_name(cxobj *xp,
char *ns0,
char *name,
cvec *cvk,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
cxobj *xc;
char *ns;
if (name == NULL || ns0 == NULL){
clicon_err(OE_XML, EINVAL, "name and namespace required");
goto done;
}
/* Go through children linearly */
xc = NULL;
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) {
ns = NULL;
if (xml2ns(xc, xml_prefix(xc), &ns) < 0)
goto done;
if (ns == NULL)
continue;
if (strcmp(ns0, ns) != 0) /* Namespace does not match, skip */
continue;
if (strcmp(name, xml_name(xc)) != 0) /* Namespace does not match, skip */
continue;
if (cvk){ /* Check indexes */
if (xml_find_noyang_cvk(ns0, xc, cvk, xvec, xlen) < 0)
goto done;
}
else /* No index variables: just add node to found list */
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! API for search in XML child list with yang available
* @retval 1 OK
* @retval 0 Revert, try again with no-yang search
* @retval -1 Error
*/
static int
xml_find_index_yang(cxobj *xp,
yang_stmt *yc,
cvec *cvk,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
cxobj *xc = NULL;
@ -882,30 +1083,58 @@ xml_binsearch(cxobj *xp,
cg_var *cvi = NULL;
cbuf *cb = NULL;
yang_stmt *yk;
char *kname;
cvec *ycvk;
cg_var *ycv = NULL;
int i;
char *name;
if (yc == NULL || xml_spec(xp) == NULL){
clicon_err(OE_YANG, ENOENT, "yang spec not found");
goto done;
}
name = yang_argument_get(yc);
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb, "<%s>", name);
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
cprintf(cb, "<%s>%s</%s>",
cv_name_get(cvi),
cv_string_get(cvi),
cv_name_get(cvi));
switch (yang_keyword_get(yc)){
case Y_LIST:
cprintf(cb, "<%s>", name);
ycvk = yang_cvec_get(yc); /* Check that those are proper index keys */
cvi = NULL;
i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
if ((kname = cv_name_get(cvi)) == NULL){
clicon_err(OE_YANG, ENOENT, "missing yang key name in cvk");
goto done;
}
/* Parameter in cvk is not key or not in right key order, then we cannot call
* xml_find_keys and we need to revert to noyang
*/
if ((ycv = cvec_i(ycvk, i)) == NULL || strcmp(kname, cv_string_get(ycv))){
goto revert;
break;
}
cprintf(cb, "<%s>%s</%s>", kname, cv_string_get(cvi), kname);
i++;
}
cprintf(cb, "</%s>", name);
break;
case Y_LEAF_LIST:
if (cvec_len(cvk) != 1){
clicon_err(OE_YANG, ENOENT, "expected exactly one leaf-list key");
goto done;
}
cvi = cvec_i(cvk, 0);
cprintf(cb, "<%s>%s</%s>", name, cv_string_get(cvi), name);
break;
default:
cprintf(cb, "<%s/>", name);
break;
}
cprintf(cb, "</%s>", name);
clicon_debug(1, "%s XML:%s", __FUNCTION__, cbuf_get(cb));
if (xml_parse_string(cbuf_get(cb), yc, &xc) < 0)
goto done;
if (xml_rootchild(xc, 0, &xc) < 0)
goto done;
/* Populate created XML tree with yang specs */
if (xml_spec_set(xc, yc) < 0)
goto done;
xk = NULL;
@ -917,11 +1146,150 @@ xml_binsearch(cxobj *xp,
if (xml_spec_set(xk, yk) < 0)
goto done;
}
retval = xml_search(xp, xc, yc, xretp);
if (xml_find_keys(xp, xc, yc, 1, xvec, xlen) < 0)
goto done;
retval = 1; /* OK */
done:
if (cb)
cbuf_free(cb);
if (xc)
xml_free(xc);
return retval;
revert: /* means try name-only*/
retval = 0;
goto done;
}
/*! API for search in XML child list using indexes and binary search if applicable
*
* Generic search function as alternative to xml_find() and others that finds YANG associated
* children of an XML tree node using indexes and optimized lookup. Optimized lookup is used only
* if an associated YANG exists. The optimized lookup is based on binary trees but is used
* The function takes as input:
* 1) An XML tree (parent) node (cxobj). See note(1) below
* 2) A set of parameters to define wanted children by name or yang-spec using yc, name, ns, yp:
* a. yp and ns:name given: YANG spec of parent + ns:name derives yc. Not found: only ns:name
* b. name given: yp is derived from xp spec, then goto 2b above.
* 3) A vector of index expressions on the form <id>=<val> refining the child set:
* a. cvk is NULL, return all children matching according to (B)
* b. cvk contains <id>=<val> entries, return children with <id> leafs matching <val>.
* See "Optimized" section below.
* 4) A return vector of XML nodes matching name and index variables
*
* # Optimized
* The function makes the index matching <id>=<val> above optimized using binary search if:
* - yc is defined AND one of the following
* - if xp is leaf-list and "id" is "."
* - if xp is a yang list and "id" is a registered index key
* - if xp is a yang list and first "id" is first leaf key, second "id" is second leaf key, etc.
* - Otherwise search is made using linear search
*
* @param[in] xp Parent xml node.
* @param[in] yc Yang spec of list child (preferred) See rule (2) above
* @param[in] yp Yang spec of parent node or yang-spec/yang (Alternative if yc not given).
* @param[in] ns Namespace (needed only if name is derived from top-symbol)
* @param[in] name Name of child (not required if yc given)
* @param[in] cvk List of keys and values as CLIgen vector on the form k1=foo, k2=bar
* @param[out] xvec Array of found nodes
* @param[out] xlen Len of xvec
* @retval 0 OK, see xret
* @retval -1 Error
* @code
* cxobj **xvec = NULL;
* size_t xlen = 0;
* cvec *cvk = NULL; vector of index keys
* ... Populate cvk with key/values eg a:5 b:6
* if (clixon_xml_find_index(xp, yp, "a", ns, cvk, &xvec, &xlen) < 0)
* err;
* @endcode
* Discussion:
* (1) Rule 2 on how to get the child name election seems unecessary complex. First, it would be
* nice to just state name and parent 2c. But xp as top-objects may sometimes be dummies, for
* parsing, copy-buffers, or simply for XML nodes that do not have YANG.
* (2) Short form could be: "first" only returning first object to get rid of vectors.
* (3) Another short form could be: keyname,keyval instead of cvk for single keys.
*/
int
clixon_xml_find_index(cxobj *xp,
yang_stmt *yp,
char *namespace,
char *name,
cvec *cvk,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
int ret;
yang_stmt *yc = NULL;
if (name == NULL){
clicon_err(OE_YANG, ENOENT, "name");
goto done;
}
if (yp == NULL)
yp = xml_spec(xp);
if (yp){/* 2. YANG spec of parent + name derives yc. If not found use just name. */
if (yang_keyword_get(yp) == Y_SPEC)
yp = yang_find_module_by_namespace(yp, namespace);
if (yp)
yc = yang_find_datanode(yp, name);
}
if (yc){
if ((ret = xml_find_index_yang(xp, yc, cvk, xvec, xlen)) < 0)
goto done;
if (ret == 0)
if (xml_find_noyang_name(xp, namespace, name, cvk, xvec, xlen) < 0)
goto done;
}
else
if (xml_find_noyang_name(xp, namespace, name, cvk, xvec, xlen) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Find positional parameter in xml child list, eg x/y[42]. Note, not optimized
*
* Create a temporary search object: a list (xc) with a key (xk) and call the binary search.
* @param[in] xp Parent xml node.
* @param[in] yc Yang spec of list child
* @param[in] pos Position
* @param[out] xvec Array of found nodes
* @param[out] xlen Len of xvec
* @retval 0 OK, see xret
* @retval -1 Error
*/
int
clixon_xml_find_pos(cxobj *xp,
yang_stmt *yc,
uint32_t pos,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
cxobj *xc = NULL;
char *name;
uint32_t u;
if (yc == NULL){
clicon_err(OE_YANG, ENOENT, "yang spec not found");
goto done;
}
name = yang_argument_get(yc);
u = 0;
xc = NULL;
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) {
if (strcmp(name, xml_name(xc)))
continue;
if (pos == u++){ /* Found */
if (cxvec_append(xc, xvec, xlen) < 0)
goto done;
break;
}
}
retval = 0;
done:
return retval;
}

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -83,6 +83,7 @@
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_nsctx.h"
#include "clixon_yang_module.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xpath_parse.h"
@ -726,12 +727,12 @@ xpath_vec(cxobj *xcur,
va_start(ap, veclen);
len = vsnprintf(NULL, 0, xpformat, ap);
va_end(ap);
/* allocate a message string exactly fitting the message length */
/* allocate an xpath string exactly fitting the length */
if ((xpath = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from reason and args */
/* second round: actually compute xpath string content */
va_start(ap, veclen);
if (vsnprintf(xpath, len+1, xpformat, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -117,8 +117,6 @@ ctx_dup(xp_ctx *xc0)
* @param[in] xc XPATH evaluation context
* @param[in] ind Indentation margin
* @param[in] str Prefix string in printout
*/
int
ctx_print_cb(cbuf *cb,

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -339,25 +339,17 @@ xp_eval_step(xp_ctx *xc0,
else for (i=0; i<xc->xc_size; i++){
xv = xc->xc_nodeset[i];
x = NULL;
if ((ret = xpath_optimize_check(xs, xv, &x)) < 0)
if ((ret = xpath_optimize_check(xs, xv, &vec, &veclen)) < 0)
goto done;
switch (ret){
case 1: /* optimized */
if (x) /* keep only x */
if (cxvec_append(x, &vec, &veclen) < 0)
goto done;
break;
case 0: /* regular code */
if (ret == 0){ /* No optimization made */
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
/* xs->xs_c0 is nodetest */
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
if (cxvec_append(x, &vec, &veclen) < 0)
goto done;
}
if (ret == 0){/* regular code, no optimization made */
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
/* xs->xs_c0 is nodetest */
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
if (cxvec_append(x, &vec, &veclen) < 0)
goto done;
}
}
} /* switch */
}
}
}
ctx_nodeset_replace(xc, vec, veclen);
@ -932,7 +924,7 @@ xp_eval(xp_ctx *xc,
xp_ctx *xr2 = NULL;
int use_xr0 = 0; /* In 2nd child use transitively result of 1st child */
if (debug)
if (debug > 1)
ctx_print(stderr, xc, xpath_tree_int2str(xs->xs_type));
/* Pre-actions before check first child c0
*/
@ -1088,7 +1080,7 @@ xp_eval(xp_ctx *xc,
xr0 = NULL;
}
ok:
if (debug)
if (debug>1)
ctx_print(stderr, *xrp, xpath_tree_int2str(xs->xs_type));
retval = 0;
done:

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -68,7 +68,6 @@
#include "clixon_xpath.h"
#include "clixon_xpath_optimize.h"
#ifdef XPATH_LIST_OPTIMIZE
static xpath_tree *_xmtop = NULL; /* pattern match tree top */
static xpath_tree *_xm = NULL;
@ -77,7 +76,6 @@ static int _optimize_enable = 1;
static int _optimize_hits = 0;
#endif /* XPATH_LIST_OPTIMIZE */
/* XXX development in clixon_xpath_eval */
int
xpath_list_optimize_stats(int *hits)
@ -89,6 +87,9 @@ xpath_list_optimize_stats(int *hits)
return 0;
}
/*! Enable xpath optimize
* Cant replace this with optin since there is no handle in xpath functions,...
*/
int
xpath_list_optimize_set(int enable)
{
@ -213,7 +214,8 @@ loop_preds(xpath_tree *xt,
*
* @param[in] xt XPath tree
* @param[in] xv XML base node
* @param[out] xp XML found node (retval == 1)
* @param[out] xvec Array of found nodes
* @param[out] xlen Len of xvec
* @param[out] key
* @param[out] keyval
* @retval -1 Error
@ -225,7 +227,8 @@ loop_preds(xpath_tree *xt,
static int
xpath_list_optimize_fn(xpath_tree *xt,
cxobj *xv,
cxobj **xp)
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
xpath_tree *xm = NULL;
@ -262,9 +265,9 @@ xpath_list_optimize_fn(xpath_tree *xt,
if (veclen != 2)
goto ok;
name = vec[0]->xs_s1;
/* Extract variables XXX strdups */
/* Extract variables */
if ((yc = yang_find(yp, Y_LIST, name)) == NULL)
#ifdef NOTYET
#ifdef NOTYET /* leaf-list is not detected by xpath optimize detection */
if ((yc = yang_find(yp, Y_LEAF_LIST, name)) == NULL) /* XXX */
#endif
goto ok;
@ -286,14 +289,12 @@ xpath_list_optimize_fn(xpath_tree *xt,
i = 0;
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
if (strcmp(cv_name_get(cvi), cv_string_get(cvec_i(cvv,i)))){
fprintf(stderr, "%s key name mismatch %s vs %s\n",
__FUNCTION__, cv_name_get(cvi), cv_string_get(cvec_i(cvv,i)));
if (strcmp(cv_name_get(cvi), cv_string_get(cvec_i(cvv,i))))
goto ok;
}
i++;
}
if (xml_binsearch(xv, yc, cvk, xp) < 0)
/* Use 2a form since yc allready given to compute cvk */
if (clixon_xml_find_index(xv, yp, NULL, name, cvk, xvec, xlen) < 0)
goto done;
retval = 1; /* match */
done:
@ -316,8 +317,9 @@ xpath_list_optimize_fn(xpath_tree *xt,
*/
int
xpath_optimize_check(xpath_tree *xs,
cxobj *xv,
cxobj **xp)
cxobj *xv,
cxobj ***xvec,
size_t *xlen)
{
#ifdef XPATH_LIST_OPTIMIZE
@ -325,7 +327,7 @@ xpath_optimize_check(xpath_tree *xs,
if (!_optimize_enable)
return 0; /* use regular code */
if ((ret = xpath_list_optimize_fn(xs, xv, xp)) < 0)
if ((ret = xpath_list_optimize_fn(xs, xv, xvec, xlen)) < 0)
return -1;
if (ret == 1){
_optimize_hits++;

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -39,7 +39,7 @@
/*
* Types
*/
struct clicon_xpath_yacc_arg{ /* XXX: mostly unrelevant */
struct clicon_xpath_yacc_arg{
const char *xy_name; /* Name of syntax (for error string) */
int xy_linenum; /* Number of \n in parsed buffer */
char *xy_parse_string; /* original (copy of) parse string */
@ -55,11 +55,11 @@ extern char *clixon_xpath_parsetext;
/*
* Prototypes
*/
int xpath_scan_init(struct clicon_xpath_yacc_arg *jy);
int xpath_scan_exit(struct clicon_xpath_yacc_arg *jy);
int xpath_scan_init(struct clicon_xpath_yacc_arg *xy);
int xpath_scan_exit(struct clicon_xpath_yacc_arg *xy);
int xpath_parse_init(struct clicon_xpath_yacc_arg *jy);
int xpath_parse_exit(struct clicon_xpath_yacc_arg *jy);
int xpath_parse_init(struct clicon_xpath_yacc_arg *xy);
int xpath_parse_exit(struct clicon_xpath_yacc_arg *xy);
int clixon_xpath_parselex(void *);
int clixon_xpath_parseparse(void *);

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -77,8 +77,6 @@ clixon_xpath_parsewrap(void)
return 1;
}
%}
digit [0-9]

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -124,7 +124,7 @@
#include "clixon_xpath_parse.h"
extern int clixon_xpath_parseget_lineno (void);
extern int clixon_xpath_parseget_lineno (void); /*XXX obsolete ? */
/*
also called from yacc generated code *

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -63,7 +63,6 @@
#include "clixon_handle.h"
#include "clixon_err.h"
#include "clixon_yang.h"
#include "clixon_yang_internal.h" /* internal */
#include "clixon_yang_cardinality.h"
/*
@ -545,8 +544,8 @@ yang_cardinality(clicon_handle h,
/* 4) Recurse */
i = 0;
while (i<yt->ys_len){ /* Note, children may be removed cant use yn_each */
ys = yt->ys_stmt[i++];
while (i< yang_len_get(yt)){ /* Note, children may be removed cant use yn_each */
ys = yang_child_i(yt, i++);
if (yang_cardinality(h, ys, modname) < 0)
goto done;
}

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -31,24 +31,15 @@
***** END LICENSE BLOCK *****
* Yang functions
* @see https://tools.ietf.org/html/rfc6020 YANG 1.0
* @see https://tools.ietf.org/html/rfc7950 YANG 1.1
*/
*
* This file defines the internal YANG data structures used by the Clixon implementation
* This file SHOULD ONLY be included by clixon_yang.h.
* Accesses should be made via the API defined in clixon_yang.h
*/
#ifndef _CLIXON_YANG_INTERNAL_H_
#define _CLIXON_YANG_INTERNAL_H_
/*
* Clixon-specific cligen variable (cv) flags
* CLIgen flags defined are in the range 0x01 -0x0f
* An application can use any flags above that
* @see cv_flag
*/
#define V_UNSET 0x10 /* Used by XML code to denote a value is not default */
#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
/*! Yang type cache. Yang type statements can cache all typedef info here
* @note unions not cached
*/
@ -76,7 +67,7 @@ struct yang_stmt{
enum rfc_6020 ys_keyword; /* See clicon_yang_parse.tab.h */
char *ys_argument; /* String / argument depending on keyword */
int ys_flags; /* Flags according to YANG_FLAG_* above */
uint16_t ys_flags; /* Flags according to YANG_FLAG_* */
yang_stmt *ys_mymodule; /* Shortcut to "my" module. Augmented
nodes can belong to other
modules than the ancestor module */
@ -116,3 +107,4 @@ struct yang_stmt{
#define yang_schemanode(y) (yang_datanode(y) || (y)->ys_keyword == Y_RPC || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_INPUT || (y)->ys_keyword == Y_OUTPUT || (y)->ys_keyword == Y_NOTIFICATION)
#endif /* _CLIXON_YANG_INTERNAL_H_ */

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -77,8 +77,8 @@
#include "clixon_plugin.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_map.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_yang_module.h"
#include "clixon_yang_internal.h" /* internal */
modstate_diff_t *
modstate_diff_new(void)
@ -155,7 +155,7 @@ yang_modules_revision(clicon_handle h)
if ((ymod = yang_find(yspec, Y_MODULE, "ietf-yang-library")) != NULL ||
(ymod = yang_find(yspec, Y_SUBMODULE, "ietf-yang-library")) != NULL){
if ((yrev = yang_find(ymod, Y_REVISION, NULL)) != NULL){
revision = yrev->ys_argument;
revision = yang_argument_get(yrev);
}
}
return revision;
@ -189,25 +189,25 @@ yms_build(clicon_handle h,
goto done;
}
cprintf(cb,"<modules-state xmlns=\"%s\">", yns->ys_argument);
cprintf(cb,"<modules-state xmlns=\"%s\">", yang_argument_get(yns));
cprintf(cb,"<module-set-id>%s</module-set-id>", msid);
ymod = NULL;
while ((ymod = yn_each(yspec, ymod)) != NULL) {
if (ymod->ys_keyword != Y_MODULE &&
ymod->ys_keyword != Y_SUBMODULE)
if (yang_keyword_get(ymod) != Y_MODULE &&
yang_keyword_get(ymod) != Y_SUBMODULE)
continue;
cprintf(cb,"<module>");
cprintf(cb,"<name>%s</name>", ymod->ys_argument);
cprintf(cb,"<name>%s</name>", yang_argument_get(ymod));
if ((ys = yang_find(ymod, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
else{
/* RFC7895 1 If no (such) revision statement exists, the module's or
submodule's revision is the zero-length string. */
cprintf(cb,"<revision></revision>");
}
if ((ys = yang_find(ymod, Y_NAMESPACE, NULL)) != NULL)
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
cprintf(cb,"<namespace>%s</namespace>", yang_argument_get(ys));
else
cprintf(cb,"<namespace></namespace>");
/* This follows order in rfc 7895: feature, conformance-type,
@ -215,10 +215,10 @@ yms_build(clicon_handle h,
if (!brief){
yc = NULL;
while ((yc = yn_each(ymod, yc)) != NULL) {
switch(yc->ys_keyword){
switch(yang_keyword_get(yc)){
case Y_FEATURE:
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
cprintf(cb,"<feature>%s</feature>", yc->ys_argument);
if (yang_cv_get(yc) && cv_bool_get(yang_cv_get(yc)))
cprintf(cb,"<feature>%s</feature>", yang_argument_get(yc));
break;
default:
break;
@ -228,12 +228,12 @@ yms_build(clicon_handle h,
}
yc = NULL;
while ((yc = yn_each(ymod, yc)) != NULL) {
switch(yc->ys_keyword){
switch(yang_keyword_get(yc)){
case Y_SUBMODULE:
cprintf(cb,"<submodule>");
cprintf(cb,"<name>%s</name>", yc->ys_argument);
cprintf(cb,"<name>%s</name>", yang_argument_get(yc));
if ((ys = yang_find(yc, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
else
cprintf(cb,"<revision></revision>");
cprintf(cb,"</submodule>");
@ -413,7 +413,7 @@ mod_ns_upgrade(clicon_handle h,
goto fail;
if ((yrev = yang_find(ymod, Y_REVISION, NULL)) == NULL)
goto fail;
if (ys_parse_date_arg(yrev->ys_argument, &to) < 0)
if (ys_parse_date_arg(yang_argument_get(yrev), &to) < 0)
goto done;
}
if ((ret = upgrade_callback_call(h, xt, ns, from, to, cbret)) < 0)
@ -482,3 +482,125 @@ clixon_module_upgrade(clicon_handle h,
retval = 0;
goto done;
}
/*! Given a yang statement and a prefix, return yang module to that relative prefix
* Note, not the other module but the proxy import statement only
* @param[in] ys A yang statement
* @param[in] prefix prefix
* @retval ymod Yang module statement if found
* @retval NULL not found
* @note Prefixes are relative to the module they are defined
* @see yang_find_module_by_name
* @see yang_find_module_by_namespace
*/
yang_stmt *
yang_find_module_by_prefix(yang_stmt *ys,
char *prefix)
{
yang_stmt *yimport;
yang_stmt *yprefix;
yang_stmt *my_ymod;
yang_stmt *ymod = NULL;
yang_stmt *yspec;
char *myprefix;
if ((yspec = ys_spec(ys)) == NULL){
clicon_err(OE_YANG, 0, "My yang spec not found");
goto done;
}
if ((my_ymod = ys_module(ys)) == NULL){
clicon_err(OE_YANG, 0, "My yang module not found");
goto done;
}
myprefix = yang_find_myprefix(ys);
if (myprefix && strcmp(myprefix, prefix) == 0){
ymod = my_ymod;
goto done;
}
yimport = NULL;
while ((yimport = yn_each(my_ymod, yimport)) != NULL) {
if (yang_keyword_get(yimport) != Y_IMPORT)
continue;
if ((yprefix = yang_find(yimport, Y_PREFIX, NULL)) != NULL &&
strcmp(yang_argument_get(yprefix), prefix) == 0){
break;
}
}
if (yimport){
if ((ymod = yang_find(yspec, Y_MODULE, yang_argument_get(yimport))) == NULL){
clicon_err(OE_YANG, 0, "No module or sub-module found with prefix %s",
prefix);
yimport = NULL;
goto done; /* unresolved */
}
}
done:
return ymod;
}
/* Get module from its own prefix
* This is really not a valid usecase, a kludge for the identityref derived
* list workaround (IDENTITYREF_KLUDGE)
* Actually, for canonical prefixes it is!
*/
yang_stmt *
yang_find_module_by_prefix_yspec(yang_stmt *yspec,
char *prefix)
{
yang_stmt *ymod = NULL;
yang_stmt *yprefix;
while ((ymod = yn_each(yspec, ymod)) != NULL)
if (yang_keyword_get(ymod) == Y_MODULE &&
(yprefix = yang_find(ymod, Y_PREFIX, NULL)) != NULL &&
strcmp(yang_argument_get(yprefix), prefix) == 0)
return ymod;
return NULL;
}
/*! Given a yang spec and a namespace, return yang module
*
* @param[in] yspec A yang specification
* @param[in] namespace namespace
* @retval ymod Yang module statement if found
* @retval NULL not found
* @see yang_find_module_by_name
* @see yang_find_module_by_prefix module-specific prefix
*/
yang_stmt *
yang_find_module_by_namespace(yang_stmt *yspec,
char *namespace)
{
yang_stmt *ymod = NULL;
if (namespace == NULL)
goto done;
while ((ymod = yn_each(yspec, ymod)) != NULL) {
if (yang_find(ymod, Y_NAMESPACE, namespace) != NULL)
break;
}
done:
return ymod;
}
/*! Given a yang spec and a module name, return yang module or submodule
*
* @param[in] yspec A yang specification
* @param[in] name Name of module
* @retval ymod Yang module statement if found
* @retval NULL not found
* @see yang_find_module_by_namespace
* @see yang_find_module_by_prefix module-specific prefix
*/
yang_stmt *
yang_find_module_by_name(yang_stmt *yspec,
char *name)
{
yang_stmt *ymod = NULL;
while ((ymod = yn_each(yspec, ymod)) != NULL)
if ((yang_keyword_get(ymod) == Y_MODULE || yang_keyword_get(ymod) == Y_SUBMODULE) &&
strcmp(yang_argument_get(ymod), name)==0)
return ymod;
return NULL;
}

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -51,7 +51,6 @@
%start file
%union {
int intval;
char *string;
}
@ -190,8 +189,8 @@
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_yang_parse.h"
#include "clixon_yang_internal.h" /* internal */
extern int clixon_yang_parseget_lineno (void);
@ -300,7 +299,7 @@ ysp_add(struct clicon_yang_yacc_arg *yy,
if ((ys = ys_new(keyword)) == NULL)
goto err;
/* NOTE: does not make a copy of string, ie argument is 'consumed' here */
ys->ys_argument = argument;
yang_argument_set(ys, argument);
if (yn_insert(yn, ys) < 0) /* Insert into hierarchy */
goto err;
if (ys_parse_sub(ys, extra) < 0) /* Check statement-specific syntax */

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand
Copyright (C) 2009-2020 Olof Hagsand
This file is part of CLIXON.
@ -91,13 +91,12 @@
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_regex.h"
#include "clixon_yang.h"
#include "clixon_hash.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_plugin.h"
#include "clixon_options.h"
#include "clixon_yang.h"
#include "clixon_yang_internal.h" /* internal */
#include "clixon_yang_module.h"
#include "clixon_yang_type.h"
/*
@ -166,176 +165,6 @@ yang_builtin(char *type)
return 0;
}
/*! Set type cache for yang type
* @param[in] rxmode Kludge to know which regexp engine is used
* @see yang_type_cache_regexp_set where cache is extended w compiled regexps
*/
static int
yang_type_cache_set(yang_type_cache **ycache0,
yang_stmt *resolved,
int options,
cvec *cvv,
cvec *patterns,
uint8_t fraction)
{
int retval = -1;
yang_type_cache *ycache = *ycache0;
assert (ycache == NULL);
if ((ycache = (yang_type_cache *)malloc(sizeof(*ycache))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(ycache, 0, sizeof(*ycache));
*ycache0 = ycache;
ycache->yc_resolved = resolved;
ycache->yc_options = options;
if (cvv){
if ((ycache->yc_cvv = cvec_dup(cvv)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
}
if (patterns && (ycache->yc_patterns = cvec_dup(patterns)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
ycache->yc_fraction = fraction;
retval = 0;
done:
return retval;
}
/*! Extend yang type cache with compiled regexps
* Compiled Regexps are computed in validate code - after initial cache set
* @param[in] regexps
*/
static int
yang_type_cache_regexp_set(yang_stmt *ytype,
int rxmode,
cvec *regexps)
{
int retval = -1;
yang_type_cache *ycache;
assert(regexps);
assert(yang_keyword_get(ytype) == Y_TYPE);
assert((ycache = ytype->ys_typecache) != NULL);
assert(ycache->yc_regexps == NULL);
ycache->yc_rxmode = rxmode;
if ((ycache->yc_regexps = cvec_dup(regexps)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
retval = 0;
done:
return retval;
}
/*! Get individual fields (direct/destructively) from yang type cache.
* @param[out] patterns Initialized cvec of regexp patterns strings
*/
static int
yang_type_cache_get(yang_type_cache *ycache,
yang_stmt **resolved,
int *options,
cvec **cvv,
cvec *patterns,
int *rxmode,
cvec *regexps,
uint8_t *fraction)
{
int retval = -1;
cg_var *cv = NULL;
if (resolved)
*resolved = ycache->yc_resolved;
if (options)
*options = ycache->yc_options;
if (cvv)
*cvv = ycache->yc_cvv;
if (patterns){
cv = NULL;
while ((cv = cvec_each(ycache->yc_patterns, cv)) != NULL)
cvec_append_var(patterns, cv);
}
if (regexps){
cv = NULL;
while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL)
cvec_append_var(regexps, cv);
}
if (rxmode)
*rxmode = ycache->yc_rxmode;
if (fraction)
*fraction = ycache->yc_fraction;
retval = 0;
// done:
return retval;
}
int
yang_type_cache_cp(yang_type_cache **ycnew,
yang_type_cache *ycold)
{
int retval = -1;
int options;
cvec *cvv;
cvec *patterns = NULL;
uint8_t fraction;
yang_stmt *resolved;
if ((patterns = cvec_new(0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
goto done;
}
/* Note, regexps are not copied since they are voids, if they were, they
* could not be freed in a simple way since copies are made at augment/group
*/
yang_type_cache_get(ycold, &resolved, &options, &cvv, patterns, NULL, NULL, &fraction);
if (yang_type_cache_set(ycnew, resolved, options, cvv, patterns, fraction) < 0)
goto done;
retval = 0;
done:
if (patterns)
cvec_free(patterns);
return retval;
}
int
yang_type_cache_free(yang_type_cache *ycache)
{
cg_var *cv;
void *p;
if (ycache->yc_cvv)
cvec_free(ycache->yc_cvv);
if (ycache->yc_patterns)
cvec_free(ycache->yc_patterns);
if (ycache->yc_regexps){
cv = NULL;
while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL){
/* need to store mode since clicon_handle is not available */
switch (ycache->yc_rxmode){
case REGEXP_POSIX:
cligen_regex_posix_free(cv_void_get(cv));
break;
case REGEXP_LIBXML2:
cligen_regex_libxml2_free(cv_void_get(cv));
break;
default:
break;
}
if ((p = cv_void_get(cv)) != NULL){
free(p);
cv_void_set(cv, NULL);
}
}
cvec_free(ycache->yc_regexps);
}
free(ycache);
return 0;
}
/*! Compile yang patterns in string form to regex compiled void* form
* and re-store into "patterns" cvec.
* This is done here instead of deep in resolve code (resolve_restrictions)
@ -415,7 +244,7 @@ ys_resolve_type(yang_stmt *ys,
/* Recursively resolve ys -> resolve with restrictions(options, etc)
* Note that the resolved type could be ys itself.
*/
if (yang_type_resolve(ys->ys_parent, ys->ys_parent,
if (yang_type_resolve(yang_parent_get(ys), yang_parent_get(ys),
ys, &resolved,
&options, &cvv, patterns, NULL, &fraction) < 0)
goto done;
@ -423,8 +252,7 @@ ys_resolve_type(yang_stmt *ys,
/* Cache the type resolving locally. Only place where this is done.
* Why not do it in yang_type_resolve? (compile regexps needs clicon_handle)
*/
if (yang_type_cache_set(&ys->ys_typecache,
resolved, options, cvv,
if (yang_type_cache_set(ys, resolved, options, cvv,
patterns, fraction) < 0)
goto done;
retval = 0;
@ -512,13 +340,15 @@ clicon_type2cv(char *origtype,
enum cv_type *cvtype)
{
int retval = -1;
yang_stmt *ym;
*cvtype = CGV_ERR;
ym = ys_module(ys);
if (restype != NULL){
yang2cv_type(restype, cvtype);
if (*cvtype == CGV_ERR){
clicon_err(OE_YANG, 0, "%s: \"%s\" type not translated",
ys_module(ys)->ys_argument, restype);
yang_argument_get(ym), restype);
goto done;
}
}
@ -530,7 +360,7 @@ clicon_type2cv(char *origtype,
yang2cv_type(origtype, cvtype);
if (*cvtype == CGV_ERR){
clicon_err(OE_YANG, 0, "%s:\"%s\": type not resolved",
ys_module(ys)->ys_argument, origtype);
yang_argument_get(ym), origtype);
goto done;
}
}
@ -832,9 +662,9 @@ cv_validate1(clicon_handle h,
yi = NULL;
if (str != NULL)
while ((yi = yn_each(yrestype, yi)) != NULL){
if (yi->ys_keyword != Y_ENUM)
if (yang_keyword_get(yi) != Y_ENUM)
continue;
if (strcmp(yi->ys_argument, str) == 0){
if (strcmp(yang_argument_get(yi), str) == 0){
found++;
break;
}
@ -860,9 +690,9 @@ cv_validate1(clicon_handle h,
found = 0;
yi = NULL;
while ((yi = yn_each(yrestype, yi)) != NULL){
if (yi->ys_keyword != Y_BIT)
if (yang_keyword_get(yi) != Y_BIT)
continue;
if (strcmp(yi->ys_argument, v) == 0){
if (strcmp(yang_argument_get(yi), v) == 0){
found++;
break;
}
@ -944,7 +774,7 @@ ys_cv_validate_union_one(clicon_handle h,
if (yang_type_resolve(ys, ys, yt, &yrt, &options, &cvv, patterns, regexps,
&fraction) < 0)
goto done;
restype = yrt?yrt->ys_argument:NULL;
restype = yrt?yang_argument_get(yrt):NULL;
if (restype && strcmp(restype, "union") == 0){ /* recursive union */
if ((retval = ys_cv_validate_union(h, ys, reason, yrt, type, val)) < 0)
goto done;
@ -957,6 +787,10 @@ ys_cv_validate_union_one(clicon_handle h,
clicon_err(OE_UNIX, errno, "cv_new");
goto done;
}
if (val == NULL){ /* Fail validation on NULL */
retval = 0;
goto done;
}
if ((retval = cv_parse1(val, cvt, reason)) < 0){
clicon_err(OE_UNIX, errno, "cv_parse");
goto done;
@ -988,7 +822,7 @@ ys_cv_validate_union_one(clicon_handle h,
return retval;
}
/*!
/*! Validate union
* @param[out] reason If given, and return value is 0, contains malloced string
* @retval -1 Error (fatal), with errno set to indicate error
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
@ -1007,7 +841,7 @@ ys_cv_validate_union(clicon_handle h,
char *reason1 = NULL; /* saved reason */
while ((yt = yn_each(yrestype, yt)) != NULL){
if (yt->ys_keyword != Y_TYPE)
if (yang_keyword_get(yt) != Y_TYPE)
continue;
if ((retval = ys_cv_validate_union_one(h, ys, reason, yt, type, val)) < 0)
goto done;
@ -1069,11 +903,11 @@ ys_cv_validate(clicon_handle h,
if (reason)
*reason=NULL;
if (ys->ys_keyword != Y_LEAF && ys->ys_keyword != Y_LEAF_LIST){
if (yang_keyword_get(ys) != Y_LEAF && yang_keyword_get(ys) != Y_LEAF_LIST){
retval = 1;
goto done;
}
ycv = ys->ys_cv;
ycv = yang_cv_get(ys);
if ((regexps = cvec_new(0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
goto done;
@ -1087,7 +921,7 @@ ys_cv_validate(clicon_handle h,
patterns, regexps,
&fraction) < 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
restype = yrestype?yang_argument_get(yrestype):NULL;
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
goto done;
@ -1104,7 +938,11 @@ ys_cv_validate(clicon_handle h,
/* Note restype can be NULL here for example with unresolved hardcoded uuid */
if (restype && strcmp(restype, "union") == 0){
assert(cvtype == CGV_REST);
val = cv_string_get(cv);
/* Instead of NULL, give an empty string to validate, this is to avoid cv_parse
* errors and may actually be the wrong thing to do.
*/
if ((val = cv_string_get(cv)) == NULL)
val = "";
if ((retval2 = ys_cv_validate_union(h, ys, reason, yrestype, origtype, val)) < 0)
goto done;
retval = retval2; /* invalid (0) with latest reason or valid 1 */
@ -1146,8 +984,8 @@ ys_cv_validate(clicon_handle h,
static inline int
ys_typedef(yang_stmt *ys)
{
return ys->ys_keyword == Y_MODULE || ys->ys_keyword == Y_SUBMODULE ||
ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST;
return yang_keyword_get(ys) == Y_MODULE || yang_keyword_get(ys) == Y_SUBMODULE ||
yang_keyword_get(ys) == Y_CONTAINER || yang_keyword_get(ys) == Y_LIST;
}
/* find next ys up which can contain a typedef */
@ -1157,9 +995,9 @@ ys_typedef_up(yang_stmt *ys)
yang_stmt *yn;
while (ys != NULL && !ys_typedef(ys)){
yn = ys->ys_parent;
yn = yang_parent_get(ys);
/* Some extra stuff to ensure ys is a stmt */
if (yn && yn->ys_keyword == Y_SPEC)
if (yn && yang_keyword_get(yn) == Y_SPEC)
yn = NULL;
ys = (yang_stmt*)yn;
}
@ -1222,8 +1060,8 @@ yang_find_identity(yang_stmt *ys,
if ((yid = yang_find(ys, Y_IDENTITY, id)) != NULL)
break;
/* Did not find a matching typedef there, proceed to next level */
yn = ys->ys_parent;
if (yn && yn->ys_keyword == Y_SPEC)
yn = yang_parent_get(ys);
if (yn && yang_keyword_get(yn) == Y_SPEC)
yn = NULL;
ys = (yang_stmt*)yn;
}
@ -1263,12 +1101,12 @@ yang_type_resolve_restrictions(yang_stmt *ytype,
if (options && cvv &&
(ys = yang_find(ytype, Y_RANGE, NULL)) != NULL){
*cvv = ys->ys_cvec;
*cvv = yang_cvec_get(ys);
*options |= YANG_OPTIONS_RANGE;
}
if (options && cvv &&
(ys = yang_find(ytype, Y_LENGTH, NULL)) != NULL){
*cvv = ys->ys_cvec;
*cvv = yang_cvec_get(ys);
*options |= YANG_OPTIONS_LENGTH;
}
/* Find all patterns */
@ -1281,7 +1119,7 @@ yang_type_resolve_restrictions(yang_stmt *ytype,
clicon_err(OE_UNIX, errno, "cvec_add");
goto done;
}
pattern = ys->ys_argument; /* clear text pattern */
pattern = yang_argument_get(ys); /* clear text pattern */
/* Check 1.1 invert pattern */
if (yang_find(ys, Y_MODIFIER, "invert-match") != NULL)
cv_flag_set(cv, V_INVERT);
@ -1290,7 +1128,7 @@ yang_type_resolve_restrictions(yang_stmt *ytype,
}
if (options && fraction &&
(ys = yang_find(ytype, Y_FRACTION_DIGITS, NULL)) != NULL){
*fraction = cv_uint8_get(ys->ys_cv);
*fraction = cv_uint8_get(yang_cv_get(ys));
*options |= YANG_OPTIONS_FRACTION_DIGITS;
}
retval = 0;
@ -1339,6 +1177,7 @@ yang_type_resolve(yang_stmt *yorig,
int retval = -1;
yang_stmt *yn;
yang_stmt *yrmod; /* module where resolved type is looked for */
int ret;
if (options)
*options = 0x0;
@ -1347,13 +1186,20 @@ yang_type_resolve(yang_stmt *yorig,
if (nodeid_split(yang_argument_get(ytype), &prefix, &type) < 0)
goto done;
/* Cache does not work for eg string length 32? */
#if 1
if ((ret = yang_type_cache_get(ytype, yrestype,
options, cvv, patterns, NULL, regexps, fraction)) < 0)
goto done;
if (ret == 1)
goto ok;
#else
if (ytype->ys_typecache != NULL){
if (yang_type_cache_get(ytype->ys_typecache, yrestype,
if (yang_type_cache_get(ytype, yrestype,
options, cvv, patterns, NULL, regexps, fraction) < 0)
goto done;
goto ok;
}
#endif
/* Check if type is basic type. If so, return that */
if ((prefix == NULL && yang_builtin(type))){
*yrestype = ytype;
@ -1366,7 +1212,7 @@ yang_type_resolve(yang_stmt *yorig,
if (prefix){ /* Go to top and find import that matches */
if ((yrmod = yang_find_module_by_prefix(ytype, prefix)) == NULL){
clicon_err(OE_DB, 0, "Type not resolved: \"%s:%s\" in module %s",
prefix, type, ys_module(yorig)->ys_argument);
prefix, type, yang_argument_get(ys_module(yorig)));
goto done;
}
if ((rytypedef = yang_find(yrmod, Y_TYPEDEF, type)) == NULL)
@ -1384,8 +1230,8 @@ yang_type_resolve(yang_stmt *yorig,
if ((rytypedef = yang_find(ys, Y_TYPEDEF, type)) != NULL)
break;
/* Did not find a matching typedef there, proceed to next level */
yn = ys->ys_parent;
if (yn && (yn->ys_keyword == Y_SPEC))
yn = yang_parent_get(ys);
if (yn && (yang_keyword_get(yn) == Y_SPEC))
yn = NULL;
ys = (yang_stmt*)yn;
}
@ -1488,8 +1334,6 @@ yang_type_get(yang_stmt *ys,
if (yang_type_resolve(ys, ys, ytype, yrestype,
options, cvv, patterns, regexps, fraction) < 0)
goto done;
clicon_debug(3, "%s: %s %s->%s", __FUNCTION__, ys->ys_argument, type,
*yrestype?(*yrestype)->ys_argument:"null");
retval = 0;
done:
if (type)
@ -1512,7 +1356,7 @@ yang_type2cv(yang_stmt *ys)
if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL)
< 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
restype = yrestype?yang_argument_get(yrestype):NULL;
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) /* This handles non-resolved also */
goto done;
done: