* New "general-purpose" datastore upgrade callback added which i called once on startup, intended for lo
w-level general upgrades and as a complement to module-specific upgrade. * Called on startup after initial XML parsing, but before module-specific upgrades * Enabled by definign the `.ca_datastore_upgrade` * [General-purpose upgrade documentation](https://clixon-docs.readthedocs.io/en/latest/backend.html#ge neral-purpose) * JSON parse error messages change from ` on line x: syntax error,..` to `json_parse: line x: syntax err or` * Unknown-element error message is more descriptive, eg from `namespace is: urn:example:clixon` to: `Fai led to find YANG spec of XML node: x with parent: xp in namespace urn:example:clixon`. * C-API parse and validation API more capable * `xml_spec_populate` family of functions extended with three-value return values * -1: error, 0: parse OK, 1: parse and YANG binding OK. * `xml_parse` and `json_parse` API changes * Three value returns: -1: error, 0: parse OK, 1: parse and YANG binding OK. * Extended `xml_parse_file2` and `xml_parse_string2` extended API functions with all options available. * New concept called `yang_bind` that defines how XML symbols are bound to YANG after parsing * Existing API same except `xml_parse_file` `endtag` argument moved to `xml_parse_file2` * C-API: Added instrumentation: `xml_size` and `xml_stats_get`. * Fixed: Enabling modstate (CLICON_XMLDB_MODSTATE), changing a revision on a yang, and restarting made the backend daemon exit at start (thanks Matt) * Also: ensure to load `ietf-yang-library.yang ` if CLICON_XMLDB_MODSTATE is set
This commit is contained in:
parent
d665992f7c
commit
9fa5e216c4
80 changed files with 2204 additions and 755 deletions
|
|
@ -68,6 +68,7 @@
|
|||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
|
|
|
|||
|
|
@ -69,12 +69,11 @@
|
|||
#include "clixon_file.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_data.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_datastore.h"
|
||||
|
||||
#include "clixon_datastore_write.h"
|
||||
#include "clixon_datastore_read.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -245,6 +246,7 @@ text_read_modstate(clicon_handle h,
|
|||
/* 1) There is no modules-state info in the file */
|
||||
}
|
||||
else if (xmcache && msd){
|
||||
msd->md_status = 1; /* There is module state in the file */
|
||||
/* Create diff trees */
|
||||
if (xml_parse_string("<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\"/>", yspec, &msd->md_del) < 0)
|
||||
goto done;
|
||||
|
|
@ -257,6 +259,12 @@ text_read_modstate(clicon_handle h,
|
|||
|
||||
/* 3) For each module state m in the file */
|
||||
while ((xm = xml_child_each(xmodst, xm, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(xml_name(xm), "module-set-id") == 0){
|
||||
if (xml_body(xm) && (msd->md_set_id = strdup(xml_body(xm))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (strcmp(xml_name(xm), "module"))
|
||||
continue; /* ignore other tags, such as module-set-id */
|
||||
if ((name = xml_find_body(xm, "name")) == NULL)
|
||||
|
|
@ -342,7 +350,7 @@ xmldb_readfile(clicon_handle h,
|
|||
if ((ret = json_parse_file(fd, yspec, &x0, NULL)) < 0) /* XXX: ret == 0*/
|
||||
goto done;
|
||||
}
|
||||
else if ((xml_parse_file(fd, "</config>", yspec, &x0)) < 0)
|
||||
else if ((xml_parse_file2(fd, YB_TOP, yspec, "</config>", &x0, NULL)) < 0)
|
||||
goto done;
|
||||
|
||||
/* Always assert a top-level called "config".
|
||||
|
|
@ -548,7 +556,6 @@ xmldb_get_cache(clicon_handle h,
|
|||
/* XXX where should we apply default values once? */
|
||||
if (xml_apply(x1t, CX_ELMNT, xml_default, h) < 0)
|
||||
goto done;
|
||||
|
||||
/* Copy the matching parts of the (relevant) XML tree.
|
||||
* If cache was empty, also update to datastore cache
|
||||
*/
|
||||
|
|
@ -671,7 +678,7 @@ xmldb_get(clicon_handle h,
|
|||
* The tree returned may be the actual cache, therefore calls for cleaning and
|
||||
* freeing tree must be made after use.
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Name of database to search in (filename including dir path
|
||||
* @param[in] db Name of datastore, eg "running"
|
||||
* @param[in] nsc External XML namespace context, or NULL
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] copy Force copy. Overrides cache_zerocopy -> cache
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -203,7 +204,7 @@ check_identityref(cxobj *x0,
|
|||
}
|
||||
|
||||
/*! Modify a base tree x0 with x1 with yang spec y according to operation op
|
||||
* @param[in] th Datastore text handle
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
|
||||
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
||||
* @param[in] x0p Parent of x0
|
||||
|
|
@ -616,7 +617,7 @@ text_modify(clicon_handle h,
|
|||
} /* text_modify */
|
||||
|
||||
/*! Modify a top-level base tree x0 with modification tree x1
|
||||
* @param[in] th Datastore text handle
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
|
||||
* @param[in] x1 XML tree which modifies base
|
||||
* @param[in] yspec Top-level yang spec (if y is NULL)
|
||||
|
|
@ -847,7 +848,7 @@ xmldb_put(clicon_handle h,
|
|||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
}
|
||||
if (x1 && strcmp(xml_name(x1),"config")!=0){
|
||||
if (x1 && strcmp(xml_name(x1), "config") != 0){
|
||||
clicon_err(OE_XML, 0, "Top-level symbol of modification tree is %s, expected \"config\"",
|
||||
xml_name(x1));
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -57,6 +58,7 @@
|
|||
#include "clixon_err.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_file.h"
|
||||
|
||||
/*! qsort "compar" for directory alphabetically sorting, see qsort(3)
|
||||
|
|
@ -173,12 +175,12 @@ int
|
|||
clicon_file_copy(char *src,
|
||||
char *target)
|
||||
{
|
||||
int inF = 0, ouF = 0;
|
||||
int err = 0;
|
||||
char line[512];
|
||||
int bytes;
|
||||
int retval = -1;
|
||||
int inF = 0, ouF = 0;
|
||||
int err = 0;
|
||||
char line[512];
|
||||
int bytes;
|
||||
struct stat st;
|
||||
int retval = -1;
|
||||
|
||||
if (stat(src, &st) != 0){
|
||||
clicon_err(OE_UNIX, errno, "stat");
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -34,6 +35,7 @@
|
|||
* JSON support functions.
|
||||
* JSON syntax is according to:
|
||||
* http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
||||
* RFC 7951 JSON Encoding of Data Modeled with YANG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
@ -421,7 +423,7 @@ json2xml_decode(cxobj *x,
|
|||
goto done;
|
||||
|
||||
if (ytype){
|
||||
if (strcmp(yang_argument_get(ytype),"identityref")==0){
|
||||
if (strcmp(yang_argument_get(ytype), "identityref")==0){
|
||||
if ((ret = json2xml_decode_identityref(x, y, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
|
|
@ -1062,34 +1064,25 @@ json_xmlns_translate(yang_stmt *yspec,
|
|||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ymod;
|
||||
char *namespace0;
|
||||
char *namespace;
|
||||
char *prefix = NULL;
|
||||
char *modname = NULL;
|
||||
char *prefix;
|
||||
cxobj *xc;
|
||||
int ret;
|
||||
|
||||
prefix = xml_prefix(x); /* prefix is here module name */
|
||||
if (prefix != NULL){
|
||||
if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){
|
||||
if ((modname = xml_prefix(x)) != NULL){ /* prefix is here module name */
|
||||
if ((ymod = yang_find_module_by_name(yspec, modname)) == NULL){
|
||||
if (xerr &&
|
||||
netconf_unknown_namespace_xml(xerr, "application",
|
||||
prefix,
|
||||
modname,
|
||||
"No yang module found corresponding to prefix") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
namespace = yang_find_mynamespace(ymod);
|
||||
/* Get existing default namespace in tree */
|
||||
if (xml2ns(x, NULL, &namespace0) < 0)
|
||||
prefix = yang_find_myprefix(ymod);
|
||||
if (xml_namespace_change(x, namespace, prefix) < 0)
|
||||
goto done;
|
||||
/* Set xmlns="" default namespace attribute (if diff from default) */
|
||||
if (namespace0 == NULL ||
|
||||
strcmp(namespace0, namespace)){
|
||||
if (xmlns_set(x, NULL, namespace) < 0)
|
||||
goto done;
|
||||
/* and remove prefix */
|
||||
xml_prefix_set(x, NULL);
|
||||
}
|
||||
}
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL){
|
||||
|
|
@ -1112,12 +1105,12 @@ json_xmlns_translate(yang_stmt *yspec,
|
|||
* are split and interpreted as in RFC7951
|
||||
*
|
||||
* @param[in] str Input string containing JSON
|
||||
* @param[in] yb How to bind yang to XML top-level when parsing
|
||||
* @param[in] yspec If set, also do yang validation
|
||||
* @param[in] name Log string, typically filename
|
||||
* @param[out] xt XML top of tree typically w/o children on entry (but created)
|
||||
* @param[out] xerr Reason for invalid returned as netconf err msg
|
||||
*
|
||||
* @see _xml_parse for XML variant
|
||||
* @see _xml_parse for XML variant
|
||||
* @retval 1 OK and valid
|
||||
* @retval 0 Invalid (only if yang spec)
|
||||
* @retval -1 Error with clicon_err called
|
||||
|
|
@ -1125,57 +1118,88 @@ json_xmlns_translate(yang_stmt *yspec,
|
|||
* @see RFC 7951
|
||||
*/
|
||||
static int
|
||||
json_parse(char *str,
|
||||
yang_stmt *yspec,
|
||||
const char *name,
|
||||
cxobj *xt,
|
||||
cxobj **xerr)
|
||||
_json_parse(char *str,
|
||||
enum yang_bind yb,
|
||||
yang_stmt *yspec,
|
||||
cxobj *xt,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_json_yacc_arg jy = {0,};
|
||||
int ret;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
int retval = -1;
|
||||
clixon_json_yacc jy = {0,};
|
||||
int ret;
|
||||
cxobj *x;
|
||||
cbuf *cberr = NULL;
|
||||
int i;
|
||||
|
||||
clicon_debug(1, "%s %s", __FUNCTION__, str);
|
||||
jy.jy_parse_string = str;
|
||||
jy.jy_name = name;
|
||||
jy.jy_linenum = 1;
|
||||
jy.jy_current = xt;
|
||||
jy.jy_xtop = xt;
|
||||
if (json_scan_init(&jy) < 0)
|
||||
goto done;
|
||||
if (json_parse_init(&jy) < 0)
|
||||
goto done;
|
||||
if (clixon_json_parseparse(&jy) != 0) { /* yacc returns 1 on error */
|
||||
clicon_log(LOG_NOTICE, "JSON error: %s on line %d", name, jy.jy_linenum);
|
||||
clicon_log(LOG_NOTICE, "JSON error: line %d", jy.jy_linenum);
|
||||
if (clicon_errno == 0)
|
||||
clicon_err(OE_XML, 0, "JSON parser error with no error code (should not happen)");
|
||||
goto done;
|
||||
}
|
||||
if (yspec){
|
||||
/* Traverse new objects */
|
||||
for (i = 0; i < jy.jy_xlen; i++) {
|
||||
x = jy.jy_xvec[i];
|
||||
/* RFC 7951 Section 4: A namespace-qualified member name MUST be used for all
|
||||
* members of a top-level JSON object
|
||||
*/
|
||||
if (yspec && xml_prefix(x) == NULL){
|
||||
if ((cberr = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cberr, "Top-level JSON object %s is not qualified with namespace which is a MUST according to RFC 7951", xml_name(x));
|
||||
if (netconf_malformed_message_xml(xerr, cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
/* Names are split into name/prefix, but now add namespace info */
|
||||
if ((ret = json_xmlns_translate(yspec, xt, xerr)) < 0)
|
||||
if ((ret = json_xmlns_translate(yspec, x, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
/* Populate, ie associate xml nodes with yang specs.
|
||||
* XXX But this wrong if xt is not on top-level, can give false positives.
|
||||
/* Now assign yang stmts to each XML node
|
||||
* XXX should be xml_spec_populate0_parent() sometimes.
|
||||
*/
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0){
|
||||
goto done;
|
||||
switch (yb){
|
||||
case YB_NONE:
|
||||
break;
|
||||
case YB_PARENT:
|
||||
if (xml_spec_populate0_parent(x, NULL) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case YB_TOP:
|
||||
if (xml_spec_populate0(x, yspec, NULL) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
||||
goto done;
|
||||
/* Now find leafs with identityrefs (+transitive) and translate
|
||||
* prefixes in values to XML namespaces */
|
||||
if ((ret = json2xml_decode(xt, xerr)) < 0)
|
||||
if ((ret = json2xml_decode(x, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0) /* XXX necessary? */
|
||||
goto fail;
|
||||
}
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
||||
goto done;
|
||||
retval = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (cberr)
|
||||
cbuf_free(cberr);
|
||||
json_parse_exit(&jy);
|
||||
json_scan_exit(&jy);
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (jy.jy_xvec)
|
||||
free(jy.jy_xvec);
|
||||
return retval;
|
||||
fail: /* invalid */
|
||||
retval = 0;
|
||||
|
|
@ -1185,7 +1209,7 @@ json_parse(char *str,
|
|||
/*! Parse string containing JSON and return an XML tree
|
||||
*
|
||||
* @param[in] str String containing JSON
|
||||
* @param[in] yspec Yang specification, or NULL
|
||||
* @param[in] yspec Yang specification, mandatory to make module->xmlns translation
|
||||
* @param[in,out] xt Top object, if not exists, on success it is created with name 'top'
|
||||
* @param[out] xerr Reason for invalid returned as netconf err msg
|
||||
*
|
||||
|
|
@ -1208,11 +1232,23 @@ json_parse_str(char *str,
|
|||
cxobj **xt,
|
||||
cxobj **xerr)
|
||||
{
|
||||
enum yang_bind yb = YB_PARENT;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (*xt == NULL)
|
||||
if (xt==NULL){
|
||||
clicon_err(OE_XML, EINVAL, "xt is NULL");
|
||||
return -1;
|
||||
}
|
||||
if (*xt == NULL){
|
||||
yb = YB_TOP; /* ad-hoc #1 */
|
||||
if ((*xt = xml_new("top", NULL, NULL)) == NULL)
|
||||
return -1;
|
||||
return json_parse(str, yspec, "", *xt, xerr);
|
||||
}
|
||||
else{
|
||||
if (xml_spec(*xt) == NULL)
|
||||
yb = YB_TOP; /* ad-hoc #2 */
|
||||
}
|
||||
return _json_parse(str, yb, yspec, *xt, xerr);
|
||||
}
|
||||
|
||||
/*! Read a JSON definition from file and parse it into a parse-tree.
|
||||
|
|
@ -1263,7 +1299,14 @@ json_parse_file(int fd,
|
|||
char *ptr;
|
||||
char ch;
|
||||
int len = 0;
|
||||
enum yang_bind yb = YB_PARENT;
|
||||
|
||||
if (xt==NULL){
|
||||
clicon_err(OE_XML, EINVAL, "xt is NULL");
|
||||
return -1;
|
||||
}
|
||||
if (*xt==NULL)
|
||||
yb = YB_TOP;
|
||||
if ((jsonbuf = malloc(jsonbuflen)) == NULL){
|
||||
clicon_err(OE_XML, errno, "malloc");
|
||||
goto done;
|
||||
|
|
@ -1282,7 +1325,7 @@ json_parse_file(int fd,
|
|||
if ((*xt = xml_new(JSON_TOP_SYMBOL, NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
if (len){
|
||||
if ((ret = json_parse(ptr, yspec, "", *xt, xerr)) < 0)
|
||||
if ((ret = _json_parse(ptr, yb, yspec, *xt, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -39,13 +40,16 @@
|
|||
* Types
|
||||
*/
|
||||
|
||||
struct clicon_json_yacc_arg{ /* XXX: mostly unrelevant */
|
||||
const char *jy_name; /* Name of syntax (for error string) */
|
||||
int jy_linenum; /* Number of \n in parsed buffer */
|
||||
char *jy_parse_string; /* original (copy of) parse string */
|
||||
void *jy_lexbuf; /* internal parse buffer from lex */
|
||||
cxobj *jy_current;
|
||||
struct clixon_json_yacc_arg{ /* XXX: mostly unrelevant */
|
||||
int jy_linenum; /* Number of \n in parsed buffer */
|
||||
char *jy_parse_string; /* original (copy of) parse string */
|
||||
void *jy_lexbuf; /* internal parse buffer from lex */
|
||||
cxobj *jy_xtop; /* cxobj top element (fixed) */
|
||||
cxobj *jy_current; /* cxobj active element (changes with parse context) */
|
||||
cxobj **jy_xvec; /* Vector of created top-level nodes (to know which are created) */
|
||||
size_t jy_xlen; /* Length of jy_xvec */
|
||||
};
|
||||
typedef struct clixon_json_yacc_arg clixon_json_yacc;
|
||||
|
||||
/*
|
||||
* Variables
|
||||
|
|
@ -55,11 +59,11 @@ extern char *clixon_json_parsetext;
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int json_scan_init(struct clicon_json_yacc_arg *jy);
|
||||
int json_scan_exit(struct clicon_json_yacc_arg *jy);
|
||||
int json_scan_init(clixon_json_yacc *jy);
|
||||
int json_scan_exit(clixon_json_yacc *jy);
|
||||
|
||||
int json_parse_init(struct clicon_json_yacc_arg *jy);
|
||||
int json_parse_exit(struct clicon_json_yacc_arg *jy);
|
||||
int json_parse_init(clixon_json_yacc *jy);
|
||||
int json_parse_exit(clixon_json_yacc *jy);
|
||||
|
||||
int clixon_json_parselex(void *);
|
||||
int clixon_json_parseparse(void *);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -62,7 +63,7 @@
|
|||
#define YY_NO_INPUT
|
||||
|
||||
/* typecast macro */
|
||||
#define _JY ((struct clicon_json_yacc_arg *)_yy)
|
||||
#define _JY ((clixon_json_yacc *)_yy)
|
||||
|
||||
#define MAXBUF 4*4*64*1024
|
||||
|
||||
|
|
@ -119,7 +120,7 @@ exp ({integer}|{real})[eE][+-]{integer}
|
|||
/*! Initialize scanner.
|
||||
*/
|
||||
int
|
||||
json_scan_init(struct clicon_json_yacc_arg *jy)
|
||||
json_scan_init(clixon_json_yacc *jy)
|
||||
{
|
||||
BEGIN(START);
|
||||
jy->jy_lexbuf = yy_scan_string (jy->jy_parse_string);
|
||||
|
|
@ -136,7 +137,7 @@ json_scan_init(struct clicon_json_yacc_arg *jy)
|
|||
* Even within Flex version 2.5 (this is assumed), freeing buffers is different.
|
||||
*/
|
||||
int
|
||||
json_scan_exit(struct clicon_json_yacc_arg *jy)
|
||||
json_scan_exit(clixon_json_yacc *jy)
|
||||
{
|
||||
yy_delete_buffer(jy->jy_lexbuf);
|
||||
clixon_json_parselex_destroy(); /* modern */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ object.
|
|||
/* Here starts user C-code */
|
||||
|
||||
/* typecast macro */
|
||||
#define _JY ((struct clicon_json_yacc_arg *)_jy)
|
||||
#define _JY ((clixon_json_yacc *)_jy)
|
||||
|
||||
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_json_parsetext, _JY->jy_linenum); YYERROR;}
|
||||
|
||||
|
|
@ -142,8 +143,7 @@ void
|
|||
clixon_json_parseerror(void *_jy,
|
||||
char *s)
|
||||
{
|
||||
clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'",
|
||||
_JY->jy_name,
|
||||
clicon_err(OE_XML, XMLPARSE_ERRNO, "json_parse: line %d: %s at or before: '%s'",
|
||||
_JY->jy_linenum ,
|
||||
s,
|
||||
clixon_json_parsetext);
|
||||
|
|
@ -151,14 +151,14 @@ clixon_json_parseerror(void *_jy,
|
|||
}
|
||||
|
||||
int
|
||||
json_parse_init(struct clicon_json_yacc_arg *jy)
|
||||
json_parse_init(clixon_json_yacc *jy)
|
||||
{
|
||||
// clicon_debug_init(2, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
json_parse_exit(struct clicon_json_yacc_arg *jy)
|
||||
json_parse_exit(clixon_json_yacc *jy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -167,8 +167,8 @@ json_parse_exit(struct clicon_json_yacc_arg *jy)
|
|||
* Split name into prefix:name (extended JSON RFC7951)
|
||||
*/
|
||||
static int
|
||||
json_current_new(struct clicon_json_yacc_arg *jy,
|
||||
char *name)
|
||||
json_current_new(clixon_json_yacc *jy,
|
||||
char *name)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
|
|
@ -183,6 +183,11 @@ json_current_new(struct clicon_json_yacc_arg *jy,
|
|||
goto done;
|
||||
if (prefix && xml_prefix_set(x, prefix) < 0)
|
||||
goto done;
|
||||
/* If topmost, add to top-list created list */
|
||||
if (jy->jy_current == jy->jy_xtop){
|
||||
if (cxvec_append(x, &jy->jy_xvec, &jy->jy_xlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
jy->jy_current = x;
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -194,7 +199,7 @@ json_current_new(struct clicon_json_yacc_arg *jy,
|
|||
}
|
||||
|
||||
static int
|
||||
json_current_pop(struct clicon_json_yacc_arg *jy)
|
||||
json_current_pop(clixon_json_yacc *jy)
|
||||
{
|
||||
if (jy->jy_current)
|
||||
jy->jy_current = xml_parent(jy->jy_current);
|
||||
|
|
@ -202,7 +207,7 @@ json_current_pop(struct clicon_json_yacc_arg *jy)
|
|||
}
|
||||
|
||||
static int
|
||||
json_current_clone(struct clicon_json_yacc_arg *jy)
|
||||
json_current_clone(clixon_json_yacc *jy)
|
||||
{
|
||||
cxobj *xn;
|
||||
|
||||
|
|
@ -217,8 +222,8 @@ json_current_clone(struct clicon_json_yacc_arg *jy)
|
|||
}
|
||||
|
||||
static int
|
||||
json_current_body(struct clicon_json_yacc_arg *jy,
|
||||
char *value)
|
||||
json_current_body(clixon_json_yacc *jy,
|
||||
char *value)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xn;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -240,7 +241,7 @@ parse_configfile(clicon_handle h,
|
|||
}
|
||||
clicon_debug(2, "%s: Reading config file %s", __FUNCTION__, filename);
|
||||
fd = fileno(f);
|
||||
if (xml_parse_file(fd, "</clicon>", yspec, &xt) < 0)
|
||||
if (xml_parse_file(fd, yspec, &xt) < 0)
|
||||
goto done;
|
||||
if (xml_child_nr(xt)==1 && xml_child_nr_type(xt, CX_BODY)==1){
|
||||
clicon_err(OE_CFG, 0, "Config file %s: Expected XML but is probably old sh style", filename);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -1484,8 +1485,8 @@ clixon_path_search(cxobj *xt,
|
|||
* @code
|
||||
* cxobj **xvec = NULL;
|
||||
* size_t xlen;
|
||||
* if (clixon_xml_find_api_path(x, &xvec, &xlen, "/symbol/%s", "foo") < 0)
|
||||
* goto err;
|
||||
* if (clixon_xml_find_api_path(x, yspec, &xvec, &xlen, "/symbol/%s", "foo") < 0)
|
||||
* err;
|
||||
* for (i=0; i<xlen; i++){
|
||||
* xn = xvec[i];
|
||||
* ...
|
||||
|
|
@ -1547,9 +1548,9 @@ clixon_xml_find_api_path(cxobj *xt,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! Given (instance-id) path and an XML tree, return matching xml node vector using stdarg
|
||||
/*! Given (instance-id) path and 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.
|
||||
* 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
|
||||
|
|
@ -1573,7 +1574,7 @@ clixon_xml_find_api_path(cxobj *xt,
|
|||
* }
|
||||
* free(xvec);
|
||||
* @endcode
|
||||
* @see xpath_vec for full XML Xpaths
|
||||
* @see xpath_vec for full XML XPaths
|
||||
* @see api_path_search for RESTCONF api-paths
|
||||
* @see RFC7950 Sec 9.13
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -59,6 +61,7 @@
|
|||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
|
||||
/* List of plugins XXX
|
||||
|
|
@ -361,7 +364,7 @@ clixon_plugin_start(clicon_handle h)
|
|||
if ((startfn = cp->cp_api.ca_start) == NULL)
|
||||
continue;
|
||||
if (startfn(h) < 0) {
|
||||
clicon_debug(1, "plugin_start() failed");
|
||||
clicon_debug(1, "%s() failed", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -384,7 +387,7 @@ clixon_plugin_exit(clicon_handle h)
|
|||
if ((exitfn = cp->cp_api.ca_exit) == NULL)
|
||||
continue;
|
||||
if (exitfn(h) < 0) {
|
||||
clicon_debug(1, "plugin_exit() failed");
|
||||
clicon_debug(1, "%s() failed", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
if (dlclose(cp->cp_handle) != 0) {
|
||||
|
|
@ -425,7 +428,7 @@ clixon_plugin_auth(clicon_handle h,
|
|||
if ((authfn = cp->cp_api.ca_auth) == NULL)
|
||||
continue;
|
||||
if ((retval = authfn(h, arg)) < 0) {
|
||||
clicon_debug(1, "plugin_auth() failed");
|
||||
clicon_debug(1, "%s() failed", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
|
@ -460,13 +463,48 @@ clixon_plugin_extension(clicon_handle h,
|
|||
if ((extfn = cp->cp_api.ca_extension) == NULL)
|
||||
continue;
|
||||
if ((retval = extfn(h, yext, ys)) < 0) {
|
||||
clicon_debug(1, "plugin_extension() failed");
|
||||
clicon_debug(1, "%s() failed", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Call plugingeneral-purpose datastore upgrade in all plugins
|
||||
*
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Name of datastore, eg "running", "startup" or "tmp"
|
||||
* @param[in] xt XML tree. Upgrade this "in place"
|
||||
* @param[in] msd Module-state diff, info on datastore module-state
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK
|
||||
* Upgrade datastore on load before or as an alternative to module-specific upgrading mechanism
|
||||
* @param[in] h Clicon handle
|
||||
* Call plugin start functions (if defined)
|
||||
* @note Start functions used to have argc/argv. Use clicon_argv_get() instead
|
||||
*/
|
||||
int
|
||||
clixon_plugin_datastore_upgrade(clicon_handle h,
|
||||
char *db,
|
||||
cxobj *xt,
|
||||
modstate_diff_t *msd)
|
||||
{
|
||||
clixon_plugin *cp;
|
||||
int i;
|
||||
datastore_upgrade_t *repairfn;
|
||||
|
||||
for (i = 0; i < _clixon_nplugins; i++) {
|
||||
cp = &_clixon_plugins[i];
|
||||
if ((repairfn = cp->cp_api.ca_datastore_upgrade) == NULL)
|
||||
continue;
|
||||
if (repairfn(h, db, xt, msd) < 0) {
|
||||
clicon_debug(1, "%s() failed", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------
|
||||
* RPC callbacks for both client/frontend and backend plugins.
|
||||
* RPC callbacks are explicitly registered in the plugin_init() function
|
||||
|
|
@ -715,7 +753,7 @@ upgrade_callback_call(clicon_handle h,
|
|||
int ret;
|
||||
|
||||
if (upgrade_cb_list == NULL)
|
||||
return 0;
|
||||
return 1;
|
||||
uc = upgrade_cb_list;
|
||||
do {
|
||||
/* For matching an upgrade callback:
|
||||
|
|
@ -753,3 +791,4 @@ upgrade_callback_call(clicon_handle h,
|
|||
retval =0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -65,6 +66,7 @@
|
|||
#include "clixon_xml.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_data.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
|
|
@ -73,13 +75,15 @@
|
|||
#include "clixon_err.h"
|
||||
#include "clixon_err_string.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_proto_client.h"
|
||||
|
||||
/*! Send internal netconf rpc from client to backend
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] msg Encoded message. Deallocate woth free
|
||||
* @param[out] xret Return value from backend as xml tree. Free w xml_free
|
||||
* @param[out] xret0 Return value from backend as xml tree. Free w xml_free
|
||||
* @param[inout] sock0 If pointer exists, do not close socket to backend on success
|
||||
* and return it here. For keeping a notify socket open
|
||||
* @note sock0 is if connection should be persistent, like a notification/subscribe api
|
||||
|
|
@ -96,7 +100,6 @@ clicon_rpc_msg(clicon_handle h,
|
|||
int port;
|
||||
char *retdata = NULL;
|
||||
cxobj *xret = NULL;
|
||||
yang_stmt *yspec;
|
||||
|
||||
#ifdef RPC_USERNAME_ASSERT
|
||||
assert(strstr(msg->op_body, "username")!=NULL); /* XXX */
|
||||
|
|
@ -135,8 +138,10 @@ clicon_rpc_msg(clicon_handle h,
|
|||
clicon_debug(1, "%s retdata:%s", __FUNCTION__, retdata);
|
||||
|
||||
if (retdata){
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
if (xml_parse_string(retdata, yspec, &xret) < 0)
|
||||
/* Cannot populate xret here because need to know RPC name (eg "lock") in order to associate yang
|
||||
* to reply.
|
||||
*/
|
||||
if (xml_parse_string2(retdata, YB_NONE, NULL, &xret, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (xret0){
|
||||
|
|
@ -239,17 +244,33 @@ clicon_rpc_netconf_xml(clicon_handle h,
|
|||
cxobj **xret,
|
||||
int *sp)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
cxobj *xname;
|
||||
char *rpcname;
|
||||
cxobj *xreply;
|
||||
yang_stmt *yspec;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if ((xname = xml_child_i_type(xml, 0, 0)) == NULL){
|
||||
clicon_err(OE_NETCONF, EINVAL, "Missing rpc name");
|
||||
goto done;
|
||||
}
|
||||
rpcname = xml_name(xname); /* Store rpc name and use in yang binding after reply */
|
||||
if (clicon_xml2cbuf(cb, xml, 0, 0, -1) < 0)
|
||||
goto done;
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cb), xret, sp) < 0)
|
||||
goto done;
|
||||
if ((xreply = xml_find_type(*xret, NULL, "rpc-reply", CX_ELMNT)) != NULL &&
|
||||
xml_find_type(xreply, NULL, "rpc-error", CX_ELMNT) == NULL){
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
/* Here use rpc name to bind to yang */
|
||||
if (xml_spec_populate_rpc_reply(xreply, rpcname, yspec, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
|
|
@ -318,7 +339,9 @@ clicon_rpc_generate_error(cxobj *xerr,
|
|||
* if (nsc)
|
||||
* xml_nsctx_free(nsc);
|
||||
* @endcode
|
||||
* @see clicon_rpc_get
|
||||
* @see clicon_rpc_generate_error
|
||||
* @note the netconf return message us yang populated, but returned data is not
|
||||
*/
|
||||
int
|
||||
clicon_rpc_get_config(clicon_handle h,
|
||||
|
|
@ -388,7 +411,6 @@ clicon_rpc_get_config(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Send database entries as XML to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
|
|
@ -650,6 +672,7 @@ clicon_rpc_unlock(clicon_handle h,
|
|||
* @endcode
|
||||
* @see clicon_rpc_get_config which is almost the same as with content=config, but you can also select dbname
|
||||
* @see clicon_rpc_generate_error
|
||||
* @note the netconf return message us yang populated, but returned data is not
|
||||
*/
|
||||
int
|
||||
clicon_rpc_get(clicon_handle h,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -233,10 +234,10 @@ validate_identityref(cxobj *xt,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
#if 0 /* Assume proper namespace, otherwise we assume module prefixes,
|
||||
* see IDENTITYREF_KLUDGE
|
||||
*/
|
||||
{
|
||||
/* Assume proper namespace, otherwise we assume module prefixes,
|
||||
* see IDENTITYREF_KLUDGE
|
||||
*/
|
||||
if (0){
|
||||
char *namespace;
|
||||
yang_stmt *ymod;
|
||||
yang_stmt *yspec;
|
||||
|
|
@ -255,7 +256,7 @@ validate_identityref(cxobj *xt,
|
|||
}
|
||||
cprintf(cb, "%s:%s", yang_argument_get(ymod), id);
|
||||
}
|
||||
#else
|
||||
#if 1
|
||||
{
|
||||
yang_stmt *ymod;
|
||||
/* idref from prefix:id to module:id */
|
||||
|
|
@ -1071,6 +1072,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
int nr;
|
||||
int ret;
|
||||
cxobj *x;
|
||||
cxobj *xp;
|
||||
char *namespace = NULL;
|
||||
cbuf *cb = NULL;
|
||||
cvec *nsc = NULL;
|
||||
|
|
@ -1085,13 +1087,16 @@ xml_yang_validate_all(clicon_handle h,
|
|||
}
|
||||
if (xml2ns(xt, xml_prefix(xt), &namespace) < 0)
|
||||
goto done;
|
||||
cprintf(cb, "Failed to find YANG spec of XML node: %s", xml_name(xt));
|
||||
if ((xp = xml_parent(xt)) != NULL)
|
||||
cprintf(cb, " with parent: %s", xml_name(xp));
|
||||
if (namespace){
|
||||
cprintf(cb, "namespace is: %s", namespace);
|
||||
cprintf(cb, " in namespace: %s", namespace);
|
||||
if (netconf_unknown_element_xml(xret, "application", xml_name(xt), cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (netconf_unknown_element_xml(xret, "application", xml_name(xt), NULL) < 0)
|
||||
if (netconf_unknown_element_xml(xret, "application", xml_name(xt), cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -45,11 +46,12 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* cligen */
|
||||
|
|
@ -165,6 +167,72 @@ xml_type2str(enum cxobj_type type)
|
|||
return (char*)clicon_int2str(xsmap, type);
|
||||
}
|
||||
|
||||
/* Stats */
|
||||
uint64_t _stats_nr = 0;
|
||||
|
||||
/*! Get global statistics about XML objects
|
||||
*/
|
||||
int
|
||||
xml_stats_get(uint64_t *nr)
|
||||
{
|
||||
if (nr)
|
||||
*nr = _stats_nr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Return the alloced memory of a single XML obj
|
||||
* @param[in] x XML object
|
||||
* @param[out] szp Size of this XML obj
|
||||
* @retval 0 OK
|
||||
* (baseline: 96 bytes per object on x86-64)
|
||||
*/
|
||||
static int
|
||||
xml_size_one(cxobj *x,
|
||||
size_t *szp)
|
||||
{
|
||||
size_t sz = 0;
|
||||
|
||||
sz += sizeof(struct xml);
|
||||
if (x->x_name)
|
||||
sz += strlen(x->x_name) + 1;
|
||||
if (x->x_prefix)
|
||||
sz += strlen(x->x_prefix) + 1;
|
||||
sz += x->x_childvec_max*sizeof(struct xml*);
|
||||
if (x->x_value_cb)
|
||||
sz += cbuf_buflen(x->x_value_cb);
|
||||
if (x->x_cv)
|
||||
sz += cv_size(x->x_cv);
|
||||
if (x->x_ns_cache)
|
||||
sz += cvec_size(x->x_ns_cache);
|
||||
if (szp)
|
||||
*szp = sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Return the alloced memory of a XML obj tree recursively
|
||||
* @param[in] x XML object
|
||||
* @param[out] szp Size of this XML obj recursively
|
||||
* @retval 0 OK
|
||||
*/
|
||||
size_t
|
||||
xml_size(cxobj *xt,
|
||||
size_t *szp)
|
||||
{
|
||||
size_t sz = 0;
|
||||
cxobj *xc;
|
||||
|
||||
xml_size_one(xt, &sz);
|
||||
if (szp)
|
||||
*szp += sz;
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(xt, xc, -1)) != NULL) {
|
||||
xml_size(xc, &sz);
|
||||
if (szp)
|
||||
*szp += sz;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Access functions
|
||||
*/
|
||||
|
|
@ -353,6 +421,10 @@ nscache_clear(cxobj *x)
|
|||
* @param[out] namespace URI namespace (or NULL). Note pointer into xml tree
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* if (xml2ns(xt, NULL, &namespace) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* @see xmlns_check
|
||||
* @see xmlns_set cache is set
|
||||
* @note, this function uses a cache.
|
||||
|
|
@ -444,7 +516,7 @@ xmlns_set(cxobj *x,
|
|||
* @param[out] prefixp Pointer to prefix if found
|
||||
* @retval -1 Error
|
||||
* @retval 0 No namespace found
|
||||
* @retval 1 Namespace found
|
||||
* @retval 1 Namespace found, prefix returned in prefixp
|
||||
*/
|
||||
int
|
||||
xml2prefix(cxobj *xn,
|
||||
|
|
@ -455,6 +527,7 @@ xml2prefix(cxobj *xn,
|
|||
cxobj *xa = NULL;
|
||||
cxobj *xp;
|
||||
char *prefix = NULL;
|
||||
char *xaprefix;
|
||||
int ret;
|
||||
|
||||
if (nscache_get_prefix(xn, namespace, &prefix) == 1) /* found */
|
||||
|
|
@ -471,7 +544,8 @@ xml2prefix(cxobj *xn,
|
|||
}
|
||||
}
|
||||
/* xmlns:prefix=namespace */
|
||||
else if (strcmp("xmlns", xml_prefix(xa)) == 0){
|
||||
else if ((xaprefix=xml_prefix(xa)) != NULL &&
|
||||
strcmp("xmlns", xaprefix) == 0){
|
||||
if (strcmp(xml_value(xa), namespace) == 0){
|
||||
prefix = xml_name(xa);
|
||||
if (nscache_set(xn, prefix, namespace) < 0)
|
||||
|
|
@ -837,10 +911,6 @@ xml_child_order(cxobj *xp,
|
|||
}
|
||||
|
||||
/*! Iterator over xml children objects
|
||||
*
|
||||
* @note Never manipulate the child-list during operation or using the
|
||||
* same object recursively, the function uses an internal field to remember the
|
||||
* index used. It works as long as the same object is not iterated concurrently.
|
||||
*
|
||||
* @param[in] xparent xml tree node whose children should be iterated
|
||||
* @param[in] xprev previous child, or NULL on init
|
||||
|
|
@ -852,6 +922,9 @@ xml_child_order(cxobj *xp,
|
|||
* }
|
||||
* @endcode
|
||||
* @note makes uses _x_vector_i:can be changed if list changed between calls
|
||||
* @note Never manipulate the child-list during operation or using the
|
||||
* same object recursively, the function uses an internal field to remember the
|
||||
* index used. It works as long as the same object is not iterated concurrently.
|
||||
*/
|
||||
cxobj *
|
||||
xml_child_each(cxobj *xparent,
|
||||
|
|
@ -867,7 +940,7 @@ xml_child_each(cxobj *xparent,
|
|||
xn = xparent->x_childvec[i];
|
||||
if (xn == NULL)
|
||||
continue;
|
||||
if (type != CX_ERROR && xn->x_type != type)
|
||||
if (type != CX_ERROR && xml_type(xn) != type)
|
||||
continue;
|
||||
break; /* this is next object after previous */
|
||||
}
|
||||
|
|
@ -994,6 +1067,7 @@ xml_new(char *name,
|
|||
x->_x_i = xml_child_nr(xp)-1;
|
||||
}
|
||||
x->x_spec = yspec; /* Can be NULL */
|
||||
_stats_nr++;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
|
@ -1236,9 +1310,8 @@ xml_child_rm(cxobj *xp,
|
|||
xp->x_childvec[i] = NULL;
|
||||
xml_parent_set(xc, NULL);
|
||||
xp->x_childvec_len--;
|
||||
/* shift up, note same index i used but ok since we break */
|
||||
for (; i<xp->x_childvec_len; i++)
|
||||
xp->x_childvec[i] = xp->x_childvec[i+1];
|
||||
if (i<xp->x_childvec_len)
|
||||
memmove(&xp->x_childvec[i], &xp->x_childvec[i+1], (xp->x_childvec_len-i)*sizeof(cxobj*));
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -1655,6 +1728,7 @@ xml_free(cxobj *x)
|
|||
if (x->x_ns_cache)
|
||||
xml_nsctx_free(x->x_ns_cache);
|
||||
free(x);
|
||||
_stats_nr--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1717,7 +1791,7 @@ clicon_xml2file(FILE *f,
|
|||
xc = NULL;
|
||||
/* print attributes only */
|
||||
while ((xc = xml_child_each(x, xc, -1)) != NULL) {
|
||||
switch (xc->x_type){
|
||||
switch (xml_type(xc)){
|
||||
case CX_ATTR:
|
||||
if (clicon_xml2file(f, xc, level+1, prettyprint) <0)
|
||||
goto done;
|
||||
|
|
@ -1846,7 +1920,7 @@ clicon_xml2cbuf(cbuf *cb,
|
|||
xc = NULL;
|
||||
/* print attributes only */
|
||||
while ((xc = xml_child_each(x, xc, -1)) != NULL)
|
||||
switch (xc->x_type){
|
||||
switch (xml_type(xc)){
|
||||
case CX_ATTR:
|
||||
if (clicon_xml2cbuf(cb, xc, level+1, prettyprint, -1) < 0)
|
||||
goto done;
|
||||
|
|
@ -1938,22 +2012,34 @@ xmltree2cbuf(cbuf *cb,
|
|||
*
|
||||
* Given a string containing XML, parse into existing XML tree and return
|
||||
* @param[in] str Pointer to string containing XML definition.
|
||||
* @param[in] yspec Yang specification or NULL
|
||||
* @param[in] yb How to bind yang to XML top-level when parsing
|
||||
* @param[in] yspec Yang specification (only if bind is TOP or CONFIG)
|
||||
* @param[in,out] xtop Top of XML parse tree. Assume created. Holds new tree.
|
||||
* @param[out] xerr Reason for failure (yang assignment not made)
|
||||
* @retval 1 Parse OK and all yang assignment made
|
||||
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
|
||||
* @retval -1 Error with clicon_err called. Includes parse error
|
||||
* @see xml_parse_file
|
||||
* @see xml_parse_string
|
||||
* @see xml_parse_va
|
||||
* @see _json_parse
|
||||
* @note special case is empty XML where the parser is not invoked.
|
||||
*/
|
||||
static int
|
||||
_xml_parse(const char *str,
|
||||
yang_stmt *yspec,
|
||||
cxobj *xt)
|
||||
_xml_parse(const char *str,
|
||||
enum yang_bind yb,
|
||||
yang_stmt *yspec,
|
||||
cxobj *xt,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
struct xml_parse_yacc_arg ya = {0,};
|
||||
clixon_xml_yacc ya = {0,};
|
||||
cxobj *x;
|
||||
int ret;
|
||||
int failed = 0; /* yang assignment */
|
||||
int i;
|
||||
|
||||
clicon_debug(1, "%s %s", __FUNCTION__, str);
|
||||
if (strlen(str) == 0)
|
||||
return 0; /* OK */
|
||||
if (xt == NULL){
|
||||
|
|
@ -1964,35 +2050,109 @@ _xml_parse(const char *str,
|
|||
clicon_err(OE_XML, errno, "strdup");
|
||||
return -1;
|
||||
}
|
||||
ya.ya_xtop = xt;
|
||||
ya.ya_xparent = xt;
|
||||
ya.ya_yspec = yspec;
|
||||
if (clixon_xml_parsel_init(&ya) < 0)
|
||||
goto done;
|
||||
if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */
|
||||
goto done;
|
||||
/* Purge all top-level body objects */
|
||||
x = NULL;
|
||||
while ((x = xml_find_type(xt, NULL, "body", CX_BODY)) != NULL)
|
||||
xml_purge(x);
|
||||
/* Verify namespaces after parsing */
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_localname_check, NULL) < 0)
|
||||
goto done;
|
||||
/* Sort the complete tree after parsing */
|
||||
if (yspec){
|
||||
/* Populate, ie associate xml nodes with yang specs */
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
/* Sort according to yang */
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
||||
/* Traverse new objects */
|
||||
for (i = 0; i < ya.ya_xlen; i++) {
|
||||
x = ya.ya_xvec[i];
|
||||
/* Verify namespaces after parsing */
|
||||
if (xml_apply0(x, CX_ELMNT, xml_localname_check, NULL) < 0)
|
||||
goto done;
|
||||
/* Populate, ie associate xml nodes with yang specs
|
||||
*/
|
||||
switch (yb){
|
||||
case YB_NONE:
|
||||
break;
|
||||
case YB_PARENT:
|
||||
/* xt:n Has spec
|
||||
* x: <a> <-- populate from parent
|
||||
*/
|
||||
if ((ret = xml_spec_populate0_parent(x, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
failed++;
|
||||
break;
|
||||
case YB_TOP:
|
||||
/* xt:<top> nospec
|
||||
* x: <a> <-- populate from modules
|
||||
*/
|
||||
#ifdef XMLDB_CONFIG_HACK
|
||||
if (strcmp(xml_name(x),"config") == 0){
|
||||
/* xt:<top> nospec
|
||||
* x: <config>
|
||||
* <a> <-- populate from modules
|
||||
*/
|
||||
if ((ret = xml_spec_populate(x, yspec, xerr)) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if ((ret = xml_spec_populate0(x, yspec, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
failed++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
/* Sort the complete tree after parsing. Sorting is less meaningful if Yang not bound */
|
||||
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
|
||||
goto done;
|
||||
retval = (failed==0) ? 1 : 0;
|
||||
done:
|
||||
clixon_xml_parsel_exit(&ya);
|
||||
if (ya.ya_parse_string != NULL)
|
||||
free(ya.ya_parse_string);
|
||||
if (ya.ya_xvec)
|
||||
free(ya.ya_xvec);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Read an XML definition from file and parse it into a parse-tree.
|
||||
*
|
||||
* @param[in] fd A file descriptor containing the XML file (as ASCII characters)
|
||||
* @param[in] yspec Yang specification, or NULL
|
||||
* @param[in,out] xt Pointer to XML parse tree. If empty, create.
|
||||
* @retval 1 Parse OK and all yang assignment made
|
||||
* @retval 0 Parse OK but yang assigment not made (or only partial)
|
||||
* @retval -1 Error with clicon_err called. Includes parse error *
|
||||
* @code
|
||||
* cxobj *xt = NULL;
|
||||
* int fd;
|
||||
* fd = open(filename, O_RDONLY);
|
||||
* xml_parse_file(fd, yspec, &xt);
|
||||
* xml_free(xt);
|
||||
* @endcode
|
||||
* @see xml_parse_string
|
||||
* @see xml_parse_va
|
||||
* @note, If xt empty, a top-level symbol will be added so that <tree../> will be: <top><tree.../></tree></top>
|
||||
* @note May block on file I/O
|
||||
* @see xml_parse_file2 for a more advanced API
|
||||
*/
|
||||
int
|
||||
xml_parse_file(int fd,
|
||||
yang_stmt *yspec,
|
||||
cxobj **xt)
|
||||
{
|
||||
enum yang_bind yb = YB_PARENT;
|
||||
|
||||
if (xt==NULL){
|
||||
clicon_err(OE_XML, EINVAL, "xt is NULL");
|
||||
return -1;
|
||||
}
|
||||
if (*xt==NULL)
|
||||
yb = YB_TOP;
|
||||
return xml_parse_file2(fd, yb, yspec, NULL, xt, NULL);
|
||||
}
|
||||
|
||||
/*! FSM to detect substring
|
||||
*/
|
||||
static inline int
|
||||
|
|
@ -2006,32 +2166,38 @@ FSM(char *tag,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Read an XML definition from file and parse it into a parse-tree.
|
||||
/*! Read an XML definition from file and parse it into a parse-tree, advanced API
|
||||
*
|
||||
* @param[in] fd A file descriptor containing the XML file (as ASCII characters)
|
||||
* @param[in] endtag Read until encounter "endtag" in the stream, or NULL
|
||||
* @param[in] yspec Yang specification, or NULL
|
||||
* @param[in,out] xt Pointer to XML parse tree. If empty, create.
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error with clicon_err called
|
||||
* @param[in] fd A file descriptor containing the XML file (as ASCII characters)
|
||||
* @param[in] yb How to bind yang to XML top-level when parsing
|
||||
* @param[in] yspec Yang specification (only if bind is TOP or CONFIG)
|
||||
* @param[in] endtag Read until encounter "endtag" in the stream, or NULL
|
||||
* @param[in,out] xt Pointer to XML parse tree. If empty, create.
|
||||
* @retval 1 Parse OK and all yang assignment made
|
||||
* @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set
|
||||
* @retval -1 Error with clicon_err called. Includes parse error
|
||||
*
|
||||
* @code
|
||||
* cxobj *xt = NULL;
|
||||
* cxobj *xerr = NULL;
|
||||
* int fd;
|
||||
* fd = open(filename, O_RDONLY);
|
||||
* xml_parse_file(fd, "</config>", yspec, &xt);
|
||||
* if ((ret = xml_parse_file2(fd, YB_TOP, yspec, "</config>", &xt, &xerr)) < 0)
|
||||
* err;
|
||||
* xml_free(xt);
|
||||
* @endcode
|
||||
* @see xml_parse_string
|
||||
* @see xml_parse_va
|
||||
* @see xml_parse_file
|
||||
* @note, If xt empty, a top-level symbol will be added so that <tree../> will be: <top><tree.../></tree></top>
|
||||
* @note May block on file I/O
|
||||
*/
|
||||
int
|
||||
xml_parse_file(int fd,
|
||||
char *endtag,
|
||||
yang_stmt *yspec,
|
||||
cxobj **xt)
|
||||
xml_parse_file2(int fd,
|
||||
enum yang_bind yb,
|
||||
yang_stmt *yspec,
|
||||
char *endtag,
|
||||
cxobj **xt,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
int ret;
|
||||
|
|
@ -2043,6 +2209,7 @@ xml_parse_file(int fd,
|
|||
int endtaglen = 0;
|
||||
int state = 0;
|
||||
int oldxmlbuflen;
|
||||
int failed = 0;
|
||||
|
||||
if (endtag != NULL)
|
||||
endtaglen = strlen(endtag);
|
||||
|
|
@ -2069,8 +2236,10 @@ xml_parse_file(int fd,
|
|||
if (*xt == NULL)
|
||||
if ((*xt = xml_new(XML_TOP_SYMBOL, NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
if (_xml_parse(ptr, yspec, *xt) < 0)
|
||||
if ((ret = _xml_parse(ptr, yb, yspec, *xt, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
failed++;
|
||||
break;
|
||||
}
|
||||
if (len>=xmlbuflen-1){ /* Space: one for the null character */
|
||||
|
|
@ -2084,7 +2253,7 @@ xml_parse_file(int fd,
|
|||
ptr = xmlbuf;
|
||||
}
|
||||
} /* while */
|
||||
retval = 0;
|
||||
retval = (failed==0) ? 1 : 0;
|
||||
done:
|
||||
if (retval < 0 && *xt){
|
||||
free(*xt);
|
||||
|
|
@ -2095,12 +2264,55 @@ xml_parse_file(int fd,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Read an XML definition from string and parse it into a parse-tree.
|
||||
/*! Read an XML definition from string and parse it into a parse-tree, advanced API
|
||||
*
|
||||
* @param[in] str String containing XML definition.
|
||||
* @param[in] yb How to bind yang to XML top-level when parsing
|
||||
* @param[in] yspec Yang specification, or NULL
|
||||
* @param[in,out] xt Pointer to XML parse tree. If empty will be created.
|
||||
* @param[out] xerr Reason for failure (yang assignment not made)
|
||||
* @retval 1 Parse OK and all yang assignment made
|
||||
* @retval 0 Parse OK but yang assigment not made (or only partial)
|
||||
* @retval -1 Error with clicon_err called. Includes parse error
|
||||
*
|
||||
* @code
|
||||
* cxobj *xt = NULL;
|
||||
* cxobj *xerr = NULL;
|
||||
* if (xml_parse_string2(str, YB_TOP, yspec, &xt, &xerr) < 0)
|
||||
* err;
|
||||
* if (xml_rootchild(xt, 0, &xt) < 0) # If you want to remove TOP
|
||||
* err;
|
||||
* @endcode
|
||||
* @see xml_parse_file
|
||||
* @see xml_parse_va
|
||||
* @note You need to free the xml parse tree after use, using xml_free()
|
||||
* @note If empty on entry, a new TOP xml will be created named "top"
|
||||
*/
|
||||
int
|
||||
xml_parse_string2(const char *str,
|
||||
enum yang_bind yb,
|
||||
yang_stmt *yspec,
|
||||
cxobj **xt,
|
||||
cxobj **xerr)
|
||||
{
|
||||
if (xt==NULL){
|
||||
clicon_err(OE_XML, EINVAL, "xt is NULL");
|
||||
return -1;
|
||||
}
|
||||
if (*xt == NULL){
|
||||
if ((*xt = xml_new(XML_TOP_SYMBOL, NULL, NULL)) == NULL)
|
||||
return -1;
|
||||
}
|
||||
return _xml_parse(str, yb, yspec, *xt, xerr);
|
||||
}
|
||||
|
||||
/*! Read an XML definition from string and parse it into a parse-tree
|
||||
*
|
||||
* @param[in] str String containing XML definition.
|
||||
* @param[in] yspec Yang specification, or NULL
|
||||
* @param[in,out] xt Pointer to XML parse tree. If empty will be created.
|
||||
* @retval 0 OK
|
||||
* @retval 1 Parse OK and all yang assignment made
|
||||
* @retval 0 Parse OK but yang assigment not made (or only partial)
|
||||
* @retval -1 Error with clicon_err called. Includes parse error
|
||||
*
|
||||
* @code
|
||||
|
|
@ -2118,24 +2330,36 @@ xml_parse_file(int fd,
|
|||
int
|
||||
xml_parse_string(const char *str,
|
||||
yang_stmt *yspec,
|
||||
cxobj **xtop)
|
||||
cxobj **xt)
|
||||
{
|
||||
if (*xtop == NULL)
|
||||
if ((*xtop = xml_new(XML_TOP_SYMBOL, NULL, NULL)) == NULL)
|
||||
enum yang_bind yb = YB_PARENT;
|
||||
|
||||
if (xt==NULL){
|
||||
clicon_err(OE_XML, EINVAL, "xt is NULL");
|
||||
return -1;
|
||||
}
|
||||
if (*xt == NULL){
|
||||
yb = YB_TOP; /* ad-hoc #1 */
|
||||
if ((*xt = xml_new(XML_TOP_SYMBOL, NULL, NULL)) == NULL)
|
||||
return -1;
|
||||
return _xml_parse(str, yspec, *xtop);
|
||||
}
|
||||
else{
|
||||
if (xml_spec(*xt) == NULL)
|
||||
yb = YB_TOP; /* ad-hoc #2 */
|
||||
}
|
||||
return _xml_parse(str, yb, yspec, *xt, NULL);
|
||||
}
|
||||
|
||||
/*! Read XML from var-arg list and parse it into xml tree
|
||||
*
|
||||
* Utility function using stdarg instead of static string.
|
||||
* @param[in,out] xtop Top of XML parse tree. If it is NULL, top element
|
||||
called 'top' will be created. Call xml_free() after use
|
||||
* @param[in] yspec Yang specification, or NULL
|
||||
* @param[in] format Format string for stdarg according to printf(3)
|
||||
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error with clicon_err called
|
||||
* @param[in,out] xtop Top of XML parse tree. If it is NULL, top element
|
||||
called 'top' will be created. Call xml_free() after use
|
||||
* @param[in] yspec Yang specification, or NULL
|
||||
* @param[in] format Format string for stdarg according to printf(3)
|
||||
* @retval 1 Parse OK and all yang assignment made
|
||||
* @retval 0 Parse OK but yang assigment not made (or only partial)
|
||||
* @retval -1 Error with clicon_err called. Includes parse error
|
||||
*
|
||||
* @code
|
||||
* cxobj *xt = NULL;
|
||||
|
|
@ -2168,9 +2392,7 @@ xml_parse_va(cxobj **xtop,
|
|||
va_start(args, format);
|
||||
len = vsnprintf(str, len, format, args) + 1;
|
||||
va_end(args);
|
||||
if (xml_parse_string(str, yspec, xtop) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
retval = xml_parse_string(str, yspec, xtop); /* xml_parse_string2 */
|
||||
done:
|
||||
if (str)
|
||||
free(str);
|
||||
|
|
@ -2201,6 +2423,7 @@ xml_copy_one(cxobj *x0,
|
|||
if ((s = xml_prefix(x0))) /* malloced string */
|
||||
if ((xml_prefix_set(x1, s)) < 0)
|
||||
goto done;
|
||||
xml_spec_set(x1, xml_spec(x0));
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -2219,8 +2442,9 @@ xml_copy_one(cxobj *x0,
|
|||
* if (xml_copy(x0, x1) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* @see xml_dup
|
||||
*/
|
||||
int
|
||||
int
|
||||
xml_copy(cxobj *x0,
|
||||
cxobj *x1)
|
||||
{
|
||||
|
|
@ -2249,6 +2473,7 @@ xml_copy(cxobj *x0,
|
|||
* x1 = xml_dup(x0);
|
||||
* @endcode
|
||||
* Note, returned tree should be freed as: xml_free(x1)
|
||||
* @see xml_cp
|
||||
*/
|
||||
cxobj *
|
||||
xml_dup(cxobj *x0)
|
||||
|
|
@ -2357,7 +2582,6 @@ cxvec_prepend(cxobj *x,
|
|||
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
|
||||
|
|
@ -2751,4 +2975,3 @@ clicon_log_xml(int level,
|
|||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -445,7 +446,7 @@ clixon_xml_changelog_init(clicon_handle h)
|
|||
clicon_err(OE_UNIX, errno, "open(%s)", filename);
|
||||
goto done;
|
||||
}
|
||||
if (xml_parse_file(fd, NULL, yspec, &xt) < 0)
|
||||
if (xml_parse_file(fd, yspec, &xt) < 0)
|
||||
goto done;
|
||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -65,6 +66,8 @@
|
|||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_data.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
|
|
@ -612,12 +615,19 @@ xml_tree_prune_flagged_sub(cxobj *xt,
|
|||
int iskey;
|
||||
int anykey=0;
|
||||
yang_stmt *yt;
|
||||
int i;
|
||||
|
||||
mark = 0;
|
||||
yt = xml_spec(xt); /* xan be null */
|
||||
x = NULL;
|
||||
xprev = x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
i = 0;
|
||||
while ((x = xml_child_each(xt, x, -1)) != NULL) {
|
||||
i++;
|
||||
if (xml_type(x) != CX_ELMNT){
|
||||
xprev = x;
|
||||
continue;
|
||||
}
|
||||
if (xml_flag(x, flag) == test?flag:0){
|
||||
/* Pass test */
|
||||
mark++;
|
||||
|
|
@ -641,8 +651,9 @@ xml_tree_prune_flagged_sub(cxobj *xt,
|
|||
if (submark)
|
||||
mark++;
|
||||
else{ /* Safe with xml_child_each if last */
|
||||
if (xml_purge(x) < 0)
|
||||
if (xml_child_rm(xt, i-1) < 0)
|
||||
goto done;
|
||||
i--;
|
||||
x = xprev;
|
||||
}
|
||||
xprev = x;
|
||||
|
|
@ -651,13 +662,20 @@ xml_tree_prune_flagged_sub(cxobj *xt,
|
|||
if (anykey && !mark){
|
||||
x = NULL;
|
||||
xprev = x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
i = 0;
|
||||
while ((x = xml_child_each(xt, x, -1)) != NULL) {
|
||||
i++;
|
||||
if (xml_type(x) != CX_ELMNT){
|
||||
xprev = x;
|
||||
continue;
|
||||
}
|
||||
/* If it is key remove it here */
|
||||
if (yt){
|
||||
if ((iskey = yang_key_match(yt, xml_name(x))) < 0)
|
||||
goto done;
|
||||
if (iskey && xml_purge(x) < 0)
|
||||
if (xml_child_rm(xt, i-1) < 0)
|
||||
goto done;
|
||||
i--;
|
||||
x = xprev;
|
||||
}
|
||||
xprev = x;
|
||||
|
|
@ -710,7 +728,6 @@ xml_tree_prune_flagged(cxobj *xt,
|
|||
*/
|
||||
static int
|
||||
add_namespace(cxobj *x1, /* target */
|
||||
cxobj *x1p,
|
||||
char *prefix1,
|
||||
char *namespace)
|
||||
{
|
||||
|
|
@ -746,6 +763,47 @@ add_namespace(cxobj *x1, /* target */
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Change namespace of XML node
|
||||
*
|
||||
* @param[in] x XML node
|
||||
* @param[in] namespace Change to this namespace (if ns does not exist in tree)
|
||||
* @param[in] prefix If change, use this prefix
|
||||
* @param 0 OK
|
||||
* @param -1 Error
|
||||
*/
|
||||
int
|
||||
xml_namespace_change(cxobj *x,
|
||||
char *namespace,
|
||||
char *prefix)
|
||||
{
|
||||
int retval = -1;
|
||||
char *ns0 = NULL; /* existing namespace */
|
||||
char *prefix0 = NULL; /* existing prefix */
|
||||
|
||||
ns0 = NULL;
|
||||
if (xml2ns(x, xml_prefix(x), &ns0) < 0)
|
||||
goto done;
|
||||
if (ns0 && strcmp(ns0, namespace) == 0)
|
||||
goto ok; /* Already has right namespace */
|
||||
/* Is namespace already declared? */
|
||||
if (xml2prefix(x, namespace, &prefix0) == 1){
|
||||
/* Yes it is declared and the prefix is pexists */
|
||||
if (xml_prefix_set(x, prefix0) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{ /* Namespace does not exist, add it */
|
||||
/* Clear old prefix if any */
|
||||
if (xml_prefix_set(x, NULL) < 0)
|
||||
goto done;
|
||||
if (add_namespace(x, prefix0, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Add default values (if not set)
|
||||
* @param[in] xt XML tree with some node marked
|
||||
* @param[in] arg Ignored
|
||||
|
|
@ -799,7 +857,7 @@ xml_default(cxobj *xt,
|
|||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
if (add_namespace(xc, xt, prefix, namespace) < 0)
|
||||
if (add_namespace(xc, prefix, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
|
@ -878,31 +936,36 @@ xml_non_config_data(cxobj *xt,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Find yang spec association of XML node for incoming RPC
|
||||
/*! Find yang spec association of XML node for incoming RPC starting with <rpc>
|
||||
*
|
||||
* Incoming RPC has an "input" structure that is not taken care of by xml_spec_populate
|
||||
* @param[in] xt XML tree node
|
||||
* @param[in] arg Yang spec
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @param[in] xrpc XML rpc node
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[out] xerr Reason for failure, or NULL
|
||||
* @retval 1 OK yang assignment made
|
||||
* @retval 0 Partial or no yang assigment made (at least one failed) and xerr set
|
||||
* @retval -1 Error
|
||||
* The
|
||||
* @code
|
||||
* xml_apply(xc, CX_ELMNT, xml_spec_populate_rpc_input, yspec)
|
||||
* if (xml_spec_populate_rpc(h, x, NULL) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* @see xml_spec_populate For other generic cases
|
||||
* @see xml_spec_populate_rpc_reply
|
||||
*/
|
||||
int
|
||||
xml_spec_populate_rpc_input(clicon_handle h,
|
||||
cxobj *xrpc,
|
||||
yang_stmt *yspec)
|
||||
xml_spec_populate_rpc(cxobj *xrpc,
|
||||
yang_stmt *yspec,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yrpc = NULL; /* yang node */
|
||||
yang_stmt *ymod=NULL; /* yang module */
|
||||
yang_stmt *yi = NULL; /* input */
|
||||
cxobj *x;
|
||||
int ret;
|
||||
|
||||
if ((strcmp(xml_name(xrpc), "rpc"))!=0){
|
||||
if ((strcmp(xml_name(xrpc), "rpc")) != 0){
|
||||
clicon_err(OE_UNIX, EINVAL, "RPC expected");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -921,14 +984,225 @@ xml_spec_populate_rpc_input(clicon_handle h,
|
|||
* recursive population to work. Therefore, assign input yang
|
||||
* to rpc level although not 100% intuitive */
|
||||
xml_spec_set(x, yi);
|
||||
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
if ((ret = xml_spec_populate_parent(x, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Find yang spec association of XML node for outgoing RPC starting with <rpc-reply>
|
||||
*
|
||||
* Incoming RPC has an "input" structure that is not taken care of by xml_spec_populate
|
||||
* @param[in] xrpc XML rpc node
|
||||
* @param[in] name Name of RPC (not seen in output/reply)
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[out] xerr Reason for failure, or NULL
|
||||
* @retval 1 OK yang assignment made
|
||||
* @retval 0 Partial or no yang assigment made (at least one failed) and xerr set
|
||||
* @retval -1 Error
|
||||
*
|
||||
* @code
|
||||
* if (xml_spec_populate_rpc_reply(x, "get-config", yspec, name) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* @see xml_spec_populate For other generic cases
|
||||
*/
|
||||
int
|
||||
xml_spec_populate_rpc_reply(cxobj *xrpc,
|
||||
char *name,
|
||||
yang_stmt *yspec,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yrpc = NULL; /* yang node */
|
||||
yang_stmt *ymod=NULL; /* yang module */
|
||||
yang_stmt *yo = NULL; /* output */
|
||||
cxobj *x;
|
||||
int ret;
|
||||
|
||||
if (strcmp(xml_name(xrpc), "rpc-reply")){
|
||||
clicon_err(OE_UNIX, EINVAL, "rpc-reply expected");
|
||||
goto done;
|
||||
}
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xrpc, x, CX_ELMNT)) != NULL) {
|
||||
if (ys_module_by_xml(yspec, x, &ymod) < 0)
|
||||
goto done;
|
||||
if (ymod == NULL)
|
||||
continue;
|
||||
if ((yrpc = yang_find(ymod, Y_RPC, name)) == NULL)
|
||||
continue;
|
||||
// xml_spec_set(xrpc, yrpc);
|
||||
if ((yo = yang_find(yrpc, Y_OUTPUT, NULL)) == NULL)
|
||||
continue;
|
||||
/* xml_spec_populate need to have parent with yang spec for
|
||||
* recursive population to work. Therefore, assign input yang
|
||||
* to rpc level although not 100% intuitive */
|
||||
break;
|
||||
}
|
||||
if (yo != NULL){
|
||||
xml_spec_set(xrpc, yo);
|
||||
if ((ret = xml_spec_populate(xrpc, yspec, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Associate XML node x with x:s parents yang:s matching child
|
||||
*
|
||||
* @param[in] xt XML tree node
|
||||
* @param[out] xerr Reason for failure, or NULL
|
||||
* @retval 2 OK yang assignment made
|
||||
* @retval 1 OK, Yang assignment not made because yang parent is anyxml or anydata
|
||||
* @retval 0 Yang assigment not made and xerr set
|
||||
* @retval -1 Error
|
||||
* @see populate_self_top
|
||||
*/
|
||||
static int
|
||||
populate_self_parent(cxobj *xt,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *y = NULL; /* yang node */
|
||||
yang_stmt *yparent; /* yang parent */
|
||||
cxobj *xp = NULL; /* xml parent */
|
||||
char *name;
|
||||
char *ns = NULL; /* XML namespace of xt */
|
||||
char *nsy = NULL; /* Yang namespace of xt */
|
||||
|
||||
xp = xml_parent(xt);
|
||||
name = xml_name(xt);
|
||||
if (xp == NULL){
|
||||
if (xerr &&
|
||||
netconf_bad_element_xml(xerr, "application", name, "Missing parent") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if ((yparent = xml_spec(xp)) == NULL){
|
||||
if (xerr &&
|
||||
netconf_bad_element_xml(xerr, "application", name, "Missing parent yang node") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (yang_keyword_get(yparent) == Y_ANYXML || yang_keyword_get(yparent) == Y_ANYDATA){
|
||||
retval = 1;
|
||||
goto done;
|
||||
}
|
||||
if ((y = yang_find_datanode(yparent, name)) == NULL){
|
||||
if (xerr &&
|
||||
netconf_bad_element_xml(xerr, "application", name, "Missing matching yang node") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (xml2ns(xt, xml_prefix(xt), &ns) < 0)
|
||||
goto done;
|
||||
nsy = yang_find_mynamespace(y);
|
||||
if (ns == NULL || nsy == NULL){
|
||||
if (xerr &&
|
||||
netconf_bad_element_xml(xerr, "application", name, "Missing namespace") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
/* Assign spec only if namespaces match */
|
||||
if (strcmp(ns, nsy) != 0){
|
||||
if (xerr &&
|
||||
netconf_bad_element_xml(xerr, "application", name, "Namespace mismatch") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
xml_spec_set(xt, y);
|
||||
retval = 2;
|
||||
done:
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Associate XML node x with yang spec y by going through all top-level modules and finding match
|
||||
*
|
||||
* @param[in] xt XML tree node
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[out] xerr Reason for failure, or NULL
|
||||
* @retval 2 OK yang assignment made
|
||||
* @retval 1 OK, Yang assignment not made because yang parent is anyxml or anydata
|
||||
* @retval 0 yang assigment not made and xerr set
|
||||
* @retval -1 Error
|
||||
* @see populate_self_parent
|
||||
*/
|
||||
static int
|
||||
populate_self_top(cxobj *xt,
|
||||
yang_stmt *yspec,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *y = NULL; /* yang node */
|
||||
yang_stmt *ymod; /* yang module */
|
||||
char *name;
|
||||
char *ns = NULL; /* XML namespace of xt */
|
||||
char *nsy = NULL; /* Yang namespace of xt */
|
||||
|
||||
name = xml_name(xt);
|
||||
if (yspec == NULL){
|
||||
if (xerr &&
|
||||
netconf_bad_element_xml(xerr, "application", name, "Missing yang spec") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (ys_module_by_xml(yspec, xt, &ymod) < 0)
|
||||
goto done;
|
||||
/* ymod is "real" module, name may belong to included submodule */
|
||||
if (ymod == NULL){
|
||||
if (xerr &&
|
||||
netconf_bad_element_xml(xerr, "application", name, "No such yang module") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if ((y = yang_find_schemanode(ymod, name)) == NULL){ /* also rpc */
|
||||
if (xerr &&
|
||||
netconf_bad_element_xml(xerr, "application", name, "Missing matching yang node") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (xml2ns(xt, xml_prefix(xt), &ns) < 0)
|
||||
goto done;
|
||||
nsy = yang_find_mynamespace(y);
|
||||
if (ns == NULL || nsy == NULL){
|
||||
if (xerr &&
|
||||
netconf_bad_element_xml(xerr, "application", name, "Missing namespace") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
/* Assign spec only if namespaces match */
|
||||
if (strcmp(ns, nsy) != 0){
|
||||
if (xerr &&
|
||||
netconf_bad_element_xml(xerr, "application", name, "Namespace mismatch") < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
xml_spec_set(xt, y);
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Find yang spec association of XML node
|
||||
|
|
@ -937,95 +1211,111 @@ xml_spec_populate_rpc_input(clicon_handle h,
|
|||
* need specialized function.
|
||||
* XXX: maybe it can be built into the same function, but you dont know whether it is input or output rpc, so
|
||||
* it seems like you need specialized functions.
|
||||
* @param[in] xt XML tree node
|
||||
* @param[in] arg Yang spec
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @param[in] xt XML tree node
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[out] xerr Reason for failure, or NULL
|
||||
* @retval 1 OK yang assignment made
|
||||
* @retval 0 Partial or no yang assigment made (at least one failed) and xerr set
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec)
|
||||
* if (xml_spec_populate(x, yspec, NULL) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* @note For subs to anyxml nodes will not have spec set
|
||||
* @see xml_spec_populate_rpc_input for incoming rpc
|
||||
* XXX: can give false positives
|
||||
* @see xml_spec_populate_rpc for incoming rpc
|
||||
*/
|
||||
#undef DEBUG /* Set this for debug */
|
||||
int
|
||||
xml_spec_populate(cxobj *x,
|
||||
void *arg)
|
||||
xml_spec_populate(cxobj *xt,
|
||||
yang_stmt *yspec,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yspec = NULL; /* yang spec */
|
||||
yang_stmt *y = NULL; /* yang node */
|
||||
yang_stmt *yparent; /* yang parent */
|
||||
yang_stmt *ymod; /* yang module */
|
||||
cxobj *xp = NULL; /* xml parent */
|
||||
char *name;
|
||||
char *ns = NULL; /* XML namespace of x */
|
||||
char *nsy = NULL; /* Yang namespace of x */
|
||||
int retval = -1;
|
||||
cxobj *xc; /* xml child */
|
||||
int ret;
|
||||
int failed = 0; /* we continue loop after failure, should we stop at fail?`*/
|
||||
|
||||
yspec = (yang_stmt*)arg;
|
||||
xp = xml_parent(x);
|
||||
name = xml_name(x);
|
||||
#ifdef DEBUG
|
||||
clicon_debug(1, "%s name:%s", __FUNCTION__, name);
|
||||
#endif
|
||||
if (xml2ns(x, xml_prefix(x), &ns) < 0)
|
||||
xc = NULL; /* Apply on children */
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_spec_populate0(xc, yspec, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
failed++;
|
||||
}
|
||||
retval = (failed==0) ? 1 : 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
xml_spec_populate_parent(cxobj *xt,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xc; /* xml child */
|
||||
int ret;
|
||||
int failed = 0; /* we continue loop after failure, should we stop at fail?`*/
|
||||
|
||||
xc = NULL; /* Apply on children */
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_spec_populate0_parent(xc, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
failed++;
|
||||
}
|
||||
retval = (failed==0) ? 1 : 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
xml_spec_populate0(cxobj *xt,
|
||||
yang_stmt *yspec,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xc; /* xml child */
|
||||
int ret;
|
||||
int failed = 0; /* we continue loop after failure, should we stop at fail?`*/
|
||||
|
||||
if ((ret = populate_self_top(xt, yspec, xerr)) < 0)
|
||||
goto done;
|
||||
if (xp && (yparent = xml_spec(xp)) != NULL){
|
||||
#ifdef DEBUG
|
||||
clicon_debug(1, "%s yang parent:%s", __FUNCTION__, yang_argument_get(yparent));
|
||||
#endif
|
||||
y = yang_find_datanode(yparent, name);
|
||||
}
|
||||
else if (yspec){ /* XXX this gives false positives */
|
||||
if (ys_module_by_xml(yspec, x, &ymod) < 0)
|
||||
goto done;
|
||||
/* ymod is "real" module, name may belong to included submodule */
|
||||
if (ymod != NULL){
|
||||
#ifdef DEBUG
|
||||
clicon_debug(1, "%s %s mod:%s", __FUNCTION__, name, yang_argument_get(ymod));
|
||||
#endif
|
||||
y = yang_find_schemanode(ymod, name);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else
|
||||
clicon_debug(1, "%s %s mod:NULL", __FUNCTION__, name);
|
||||
#endif
|
||||
}
|
||||
if (y) {
|
||||
nsy = yang_find_mynamespace(y);
|
||||
if (ns == NULL || nsy == NULL){
|
||||
clicon_err(OE_XML, EFAULT, "Namespace NULL");
|
||||
goto done;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
clicon_debug(1, "%s y:%s", __FUNCTION__, yang_argument_get(y));
|
||||
#endif
|
||||
/* Assign spec only if namespaces match */
|
||||
if (strcmp(ns, nsy) == 0){
|
||||
#if 0
|
||||
/* Cornercase:
|
||||
* The XML parser may have kept pretty-printed whitespaces in empty nodes, eg "<x> </x>"
|
||||
* since it is valid leaf(-list) content.
|
||||
* But if it is not a container, list or something, else, the content should be stripped.
|
||||
* But we cannot do this because of false positives (above)
|
||||
*/
|
||||
if (xml_spec(x) == NULL && /* Not assigned yet, ensure to do just once,... */
|
||||
yang_keyword_get(y) != Y_LEAF &&
|
||||
yang_keyword_get(y) != Y_LEAF_LIST){
|
||||
if (xml_rm_children(x, CX_BODY) < 0)
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
xml_spec_set(x, y);
|
||||
if (ret == 1){
|
||||
xc = NULL; /* Apply on children */
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_spec_populate0_parent(xc, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
else{
|
||||
#ifdef DEBUG
|
||||
clicon_debug(1, "%s y:NULL", __FUNCTION__);
|
||||
#endif
|
||||
retval = (failed==0) ? 1 : 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
xml_spec_populate0_parent(cxobj *xt,
|
||||
cxobj **xerr)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xc; /* xml child */
|
||||
int ret;
|
||||
int failed = 0; /* we continue loop after failure, should we stop at fail?`*/
|
||||
|
||||
if ((ret = populate_self_parent(xt, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
failed++;
|
||||
else if (ret != 1){ /* 1 means anyxml parent */
|
||||
xc = NULL; /* Apply on children */
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
||||
if ((ret = xml_spec_populate0_parent(xc, xerr)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
retval = (failed==0) ? 1 : 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -1242,11 +1532,8 @@ assign_namespaces(cxobj *x0, /* source */
|
|||
* Check if it is exists in x1 itself */
|
||||
if (xml2prefix(x1, namespace, &pexist) == 1){
|
||||
/* Yes it exists, but is it equal? */
|
||||
if ((pexist == NULL && prefix0 == NULL) ||
|
||||
(pexist && prefix0 &&
|
||||
strcmp(pexist, prefix0)==0)){ /* Equal, reuse */
|
||||
;
|
||||
}
|
||||
if (clicon_strcmp(pexist, prefix0) == 0)
|
||||
; /* Equal, reuse */
|
||||
else{ /* namespace exist, but not equal, use existing */
|
||||
/* Add prefix to x1, if any */
|
||||
if (pexist && xml_prefix_set(x1, pexist) < 0)
|
||||
|
|
@ -1254,8 +1541,7 @@ assign_namespaces(cxobj *x0, /* source */
|
|||
}
|
||||
goto ok; /* skip */
|
||||
}
|
||||
else
|
||||
{ /* namespace does not exist in target x1, use source prefix
|
||||
else { /* namespace does not exist in target x1, use source prefix
|
||||
* use the prefix defined in the module
|
||||
*/
|
||||
if (isroot){
|
||||
|
|
@ -1272,7 +1558,7 @@ assign_namespaces(cxobj *x0, /* source */
|
|||
}
|
||||
}
|
||||
}
|
||||
if (add_namespace(x1, x1p, prefix1, namespace) < 0)
|
||||
if (add_namespace(x1, prefix1, namespace) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -356,10 +358,20 @@ xml_nsctx_yang(yang_stmt *yn,
|
|||
*
|
||||
* That is, create a "canonical" XML namespace mapping from all loaded yang
|
||||
* modules which are children of the yang specification.
|
||||
* ALso add netconf base namespace: nc , urn:ietf:params:xml:ns:netconf:base:1.0
|
||||
* Also add netconf base namespace: nc , urn:ietf:params:xml:ns:netconf:base:1.0
|
||||
* Fully explore all prefix:namespace pairs of all yang modules
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[out] ncp XML namespace context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cvec *nsc = NULL;
|
||||
* yang_stmt *yspec = clicon_dbspec_yang(h);
|
||||
* if (xml_nsctx_yangspec(yspec, &nsc) < 0)
|
||||
* goto done;
|
||||
* ...
|
||||
* cvec_free(nsc);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xml_nsctx_yangspec(yang_stmt *yspec,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -47,20 +48,23 @@ struct xml_parse_yacc_arg{
|
|||
char *ya_parse_string; /* original (copy of) parse string */
|
||||
int ya_linenum; /* Number of \n in parsed buffer */
|
||||
void *ya_lexbuf; /* internal parse buffer from lex */
|
||||
|
||||
cxobj *ya_xelement; /* xml active element */
|
||||
cxobj *ya_xparent; /* xml parent element*/
|
||||
cxobj *ya_xtop; /* cxobj top element (fixed) */
|
||||
cxobj *ya_xelement; /* cxobj active element (changes with parse context) */
|
||||
cxobj *ya_xparent; /* cxobj parent element (changes with parse context) */
|
||||
yang_stmt *ya_yspec; /* If set, top-level yang-spec */
|
||||
int ya_lex_state; /* lex return state */
|
||||
cxobj **ya_xvec; /* Vector of created top-level nodes (to know which are created) */
|
||||
size_t ya_xlen; /* Length of ya_xvec */
|
||||
};
|
||||
typedef struct xml_parse_yacc_arg clixon_xml_yacc;
|
||||
|
||||
extern char *clixon_xml_parsetext;
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int clixon_xml_parsel_init(struct xml_parse_yacc_arg *ya);
|
||||
int clixon_xml_parsel_exit(struct xml_parse_yacc_arg *ya);
|
||||
int clixon_xml_parsel_init(clixon_xml_yacc *ya);
|
||||
int clixon_xml_parsel_exit(clixon_xml_yacc *ya);
|
||||
|
||||
int clixon_xml_parsel_linenr(void);
|
||||
int clixon_xml_parselex(void *);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -66,7 +68,7 @@
|
|||
#define YY_NO_INPUT
|
||||
|
||||
/* typecast macro */
|
||||
#define _YA ((struct xml_parse_yacc_arg *)_ya)
|
||||
#define _YA ((clixon_xml_yacc *)_ya)
|
||||
|
||||
#undef clixon_xml_parsewrap
|
||||
int clixon_xml_parsewrap(void)
|
||||
|
|
@ -182,7 +184,7 @@ ncname {namestart}{namechar}*
|
|||
/*! Initialize XML scanner.
|
||||
*/
|
||||
int
|
||||
clixon_xml_parsel_init(struct xml_parse_yacc_arg *ya)
|
||||
clixon_xml_parsel_init(clixon_xml_yacc *ya)
|
||||
{
|
||||
BEGIN(START);
|
||||
ya->ya_lexbuf = yy_scan_string (ya->ya_parse_string);
|
||||
|
|
@ -193,7 +195,7 @@ clixon_xml_parsel_init(struct xml_parse_yacc_arg *ya)
|
|||
|
||||
/*! Exit xml scanner */
|
||||
int
|
||||
clixon_xml_parsel_exit(struct xml_parse_yacc_arg *ya)
|
||||
clixon_xml_parsel_exit(clixon_xml_yacc *ya)
|
||||
{
|
||||
yy_delete_buffer(ya->ya_lexbuf);
|
||||
clixon_xml_parselex_destroy(); /* modern */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -58,7 +60,7 @@
|
|||
%{
|
||||
|
||||
/* typecast macro */
|
||||
#define _YA ((struct xml_parse_yacc_arg *)_ya)
|
||||
#define _YA ((clixon_xml_yacc *)_ya)
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
|
@ -82,11 +84,14 @@
|
|||
#include "clixon_xml_parse.h"
|
||||
|
||||
void
|
||||
clixon_xml_parseerror(void *_ya, char *s)
|
||||
clixon_xml_parseerror(void *_ya,
|
||||
char *s)
|
||||
{
|
||||
clicon_err(OE_XML, XMLPARSE_ERRNO, "xml_parse: line %d: %s: at or before: %s",
|
||||
_YA->ya_linenum, s, clixon_xml_parsetext);
|
||||
return;
|
||||
clicon_err(OE_XML, XMLPARSE_ERRNO, "xml_parse: line %d: %s: at or before: %s",
|
||||
_YA->ya_linenum,
|
||||
s,
|
||||
clixon_xml_parsetext);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -94,7 +99,7 @@ clixon_xml_parseerror(void *_ya, char *s)
|
|||
* there may also be some leakage here on NULL return
|
||||
*/
|
||||
static int
|
||||
xml_parse_content(struct xml_parse_yacc_arg *ya,
|
||||
xml_parse_content(clixon_xml_yacc *ya,
|
||||
char *str)
|
||||
{
|
||||
cxobj *xn = ya->ya_xelement;
|
||||
|
|
@ -120,7 +125,7 @@ xml_parse_content(struct xml_parse_yacc_arg *ya,
|
|||
* But if there is an element, then skip all whitespace.
|
||||
*/
|
||||
static int
|
||||
xml_parse_whitespace(struct xml_parse_yacc_arg *ya,
|
||||
xml_parse_whitespace(clixon_xml_yacc *ya,
|
||||
char *str)
|
||||
{
|
||||
cxobj *xn = ya->ya_xelement;
|
||||
|
|
@ -151,7 +156,7 @@ xml_parse_whitespace(struct xml_parse_yacc_arg *ya,
|
|||
}
|
||||
|
||||
static int
|
||||
xml_parse_version(struct xml_parse_yacc_arg *ya,
|
||||
xml_parse_version(clixon_xml_yacc *ya,
|
||||
char *ver)
|
||||
{
|
||||
if(strcmp(ver, "1.0")){
|
||||
|
|
@ -164,59 +169,34 @@ xml_parse_version(struct xml_parse_yacc_arg *ya,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Parse Qualified name --> Unprefixed name
|
||||
* @param[in] ya XML parser yacc handler struct
|
||||
* @param[in] localpart Name
|
||||
* @note the call to xml_child_spec() may not have xmlns attribute read yet XXX
|
||||
*/
|
||||
static int
|
||||
xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya,
|
||||
char *name)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
yang_stmt *y = NULL; /* yang node */
|
||||
cxobj *xp; /* xml parent */
|
||||
|
||||
xp = ya->ya_xparent;
|
||||
if ((x = xml_new(name, xp, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_child_spec(x, xp, ya->ya_yspec, &y) < 0)
|
||||
goto done;
|
||||
if (y && xml_spec_set(x, y) < 0)
|
||||
goto done;
|
||||
ya->ya_xelement = x;
|
||||
retval = 0;
|
||||
done:
|
||||
free(name);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Parse Qualified name -> PrefixedName
|
||||
/*! Parse Qualified name -> (Un)PrefixedName
|
||||
*
|
||||
* This is where all (parsed) xml elements are created
|
||||
* @param[in] ya XML parser yacc handler struct
|
||||
* @param[in] prefix Prefix, namespace, or NULL
|
||||
* @param[in] localpart Name
|
||||
*/
|
||||
static int
|
||||
xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya,
|
||||
xml_parse_prefixed_name(clixon_xml_yacc *ya,
|
||||
char *prefix,
|
||||
char *name)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
yang_stmt *y = NULL; /* yang node */
|
||||
cxobj *xp; /* xml parent */
|
||||
|
||||
xp = ya->ya_xparent;
|
||||
if ((x = xml_new(name, xp, NULL)) == NULL)
|
||||
goto done;
|
||||
if (xml_child_spec(x, xp, ya->ya_yspec, &y) < 0)
|
||||
goto done;
|
||||
if (y && xml_spec_set(x, y) < 0)
|
||||
goto done;
|
||||
if (xml_prefix_set(x, prefix) < 0)
|
||||
xml_type_set(x, CX_ELMNT);
|
||||
if (prefix && xml_prefix_set(x, prefix) < 0)
|
||||
goto done;
|
||||
ya->ya_xelement = x;
|
||||
/* If topmost, add to top-list created list */
|
||||
if (xp == ya->ya_xtop){
|
||||
if (cxvec_append(x, &ya->ya_xvec, &ya->ya_xlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (prefix)
|
||||
|
|
@ -226,7 +206,7 @@ xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya,
|
|||
}
|
||||
|
||||
static int
|
||||
xml_parse_endslash_pre(struct xml_parse_yacc_arg *ya)
|
||||
xml_parse_endslash_pre(clixon_xml_yacc *ya)
|
||||
{
|
||||
ya->ya_xparent = ya->ya_xelement;
|
||||
ya->ya_xelement = NULL;
|
||||
|
|
@ -234,7 +214,7 @@ xml_parse_endslash_pre(struct xml_parse_yacc_arg *ya)
|
|||
}
|
||||
|
||||
static int
|
||||
xml_parse_endslash_mid(struct xml_parse_yacc_arg *ya)
|
||||
xml_parse_endslash_mid(clixon_xml_yacc *ya)
|
||||
{
|
||||
if (ya->ya_xelement != NULL)
|
||||
ya->ya_xelement = xml_parent(ya->ya_xelement);
|
||||
|
|
@ -245,7 +225,7 @@ xml_parse_endslash_mid(struct xml_parse_yacc_arg *ya)
|
|||
}
|
||||
|
||||
static int
|
||||
xml_parse_endslash_post(struct xml_parse_yacc_arg *ya)
|
||||
xml_parse_endslash_post(clixon_xml_yacc *ya)
|
||||
{
|
||||
ya->ya_xelement = NULL;
|
||||
return 0;
|
||||
|
|
@ -261,7 +241,7 @@ xml_parse_endslash_post(struct xml_parse_yacc_arg *ya)
|
|||
* @param[in] name
|
||||
*/
|
||||
static int
|
||||
xml_parse_bslash(struct xml_parse_yacc_arg *ya,
|
||||
xml_parse_bslash(clixon_xml_yacc *ya,
|
||||
char *prefix,
|
||||
char *name)
|
||||
{
|
||||
|
|
@ -309,7 +289,7 @@ xml_parse_bslash(struct xml_parse_yacc_arg *ya,
|
|||
* - PrefixedAttName: xmlns:NAME
|
||||
*/
|
||||
static int
|
||||
xml_parse_attr(struct xml_parse_yacc_arg *ya,
|
||||
xml_parse_attr(clixon_xml_yacc *ya,
|
||||
char *prefix,
|
||||
char *name,
|
||||
char *attval)
|
||||
|
|
@ -389,7 +369,7 @@ element : '<' qname attrs element1
|
|||
{ clicon_debug(2, "element -> < qname attrs element1"); }
|
||||
;
|
||||
|
||||
qname : NAME { if (xml_parse_unprefixed_name(_YA, $1) < 0) YYABORT;
|
||||
qname : NAME { if (xml_parse_prefixed_name(_YA, NULL, $1) < 0) YYABORT;
|
||||
clicon_debug(2, "qname -> NAME %s", $1);}
|
||||
| NAME ':' NAME { if (xml_parse_prefixed_name(_YA, $1, $3) < 0) YYABORT;
|
||||
clicon_debug(2, "qname -> NAME : NAME");}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -135,61 +136,6 @@ xml_cv_cache(cxobj *x,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Given a child name and an XML object, return yang stmt of child
|
||||
* If no xml parent, find root yang stmt matching name
|
||||
* @param[in] x Child
|
||||
* @param[in] xp XML parent, can be NULL.
|
||||
* @param[in] yspec Yang specification (top level)
|
||||
* @param[out] yresult Pointer to yang stmt of result, or NULL, if not found
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note special rule for rpc, ie <rpc><foo>,look for top "foo" node.
|
||||
* @note works for import prefix, but not work for generic XML parsing where
|
||||
* xmlns and xmlns:ns are used.
|
||||
*/
|
||||
int
|
||||
xml_child_spec(cxobj *x,
|
||||
cxobj *xp,
|
||||
yang_stmt *yspec,
|
||||
yang_stmt **yresult)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *y = NULL; /* result yang node */
|
||||
yang_stmt *yparent; /* parent yang */
|
||||
yang_stmt *ymod = NULL;
|
||||
yang_stmt *yi;
|
||||
char *name;
|
||||
|
||||
name = xml_name(x);
|
||||
if (xp && (yparent = xml_spec(xp)) != NULL){
|
||||
/* First case: parent already has an associated yang statement,
|
||||
* then find matching child of that */
|
||||
if (yang_keyword_get(yparent) == Y_RPC){
|
||||
if ((yi = yang_find(yparent, Y_INPUT, NULL)) != NULL)
|
||||
y = yang_find_datanode(yi, name);
|
||||
}
|
||||
else
|
||||
y = yang_find_datanode(yparent, name);
|
||||
}
|
||||
else if (yspec){
|
||||
/* Second case, this is a "root", need to find yang stmt from spec
|
||||
*/
|
||||
if (ys_module_by_xml(yspec, xp, &ymod) < 0)
|
||||
goto done;
|
||||
if (ymod != NULL)
|
||||
y = yang_find_schemanode(ymod, name);
|
||||
}
|
||||
else
|
||||
y = NULL;
|
||||
/* kludge rpc -> input */
|
||||
if (y && yang_keyword_get(y) == Y_RPC && yang_find(y, Y_INPUT, NULL))
|
||||
y = yang_find(y, Y_INPUT, NULL);
|
||||
*yresult = y;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Help function to qsort for sorting entries in xml child vector same parent
|
||||
* @param[in] x1 object 1
|
||||
* @param[in] x2 object 2
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -123,11 +124,11 @@ static const map_str2int xpath_tree_map[] = {
|
|||
static const map_str2int axis_type_map[] = {
|
||||
{"NaN", A_NAN},
|
||||
{"ancestor", A_ANCESTOR},
|
||||
{"ancestor-or-selgf", A_ANCESTOR_OR_SELF},
|
||||
{"ancestor-or-self", A_ANCESTOR_OR_SELF},
|
||||
{"attribute", A_ATTRIBUTE},
|
||||
{"child", A_CHILD},
|
||||
{"descendant", A_DESCENDANT},
|
||||
{"descendeant-or-self", A_DESCENDANT_OR_SELF},
|
||||
{"descendant-or-self", A_DESCENDANT_OR_SELF},
|
||||
{"following", A_FOLLOWING},
|
||||
{"following-sibling", A_FOLLOWING_SIBLING},
|
||||
{"namespace", A_NAMESPACE},
|
||||
|
|
@ -702,7 +703,7 @@ xpath_first_localonly(cxobj *xcur,
|
|||
* cxobj **vec;
|
||||
* size_t veclen;
|
||||
* if (xpath_vec(xcur, nsc, "//symbol/foo", &vec, &veclen) < 0)
|
||||
* goto err;
|
||||
* err;
|
||||
* for (i=0; i<veclen; i++){
|
||||
* xn = vec[i];
|
||||
* ...
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -354,8 +355,23 @@ xp_eval_step(xp_ctx *xc0,
|
|||
}
|
||||
ctx_nodeset_replace(xc, vec, veclen);
|
||||
break;
|
||||
case A_DESCENDANT:
|
||||
case A_DESCENDANT_OR_SELF:
|
||||
for (i=0; i<xc->xc_size; i++){
|
||||
xv = xc->xc_nodeset[i];
|
||||
if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
for (i=0; i<veclen; i++){
|
||||
x = vec[i];
|
||||
if (cxvec_append(x, &xc->xc_nodeset, &xc->xc_size) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (vec){
|
||||
free(vec);
|
||||
vec = NULL;
|
||||
}
|
||||
break;
|
||||
case A_DESCENDANT:
|
||||
for (i=0; i<xc->xc_size; i++){
|
||||
xv = xc->xc_nodeset[i];
|
||||
if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, localonly, &vec, &veclen) < 0)
|
||||
|
|
|
|||
|
|
@ -74,13 +74,13 @@
|
|||
#include "clixon_yang.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_data.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_yang_parse.h"
|
||||
#include "clixon_yang_parse_lib.h"
|
||||
#include "clixon_yang_cardinality.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_yang_type.h"
|
||||
#include "clixon_yang_internal.h" /* internal included by this file only, not API*/
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -74,11 +75,11 @@
|
|||
#include "clixon_xpath.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_data.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#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"
|
||||
|
||||
modstate_diff_t *
|
||||
modstate_diff_new(void)
|
||||
|
|
@ -97,6 +98,8 @@ modstate_diff_free(modstate_diff_t *md)
|
|||
{
|
||||
if (md == NULL)
|
||||
return 0;
|
||||
if (md->md_set_id)
|
||||
free(md->md_set_id);
|
||||
if (md->md_del)
|
||||
xml_free(md->md_del);
|
||||
if (md->md_mod)
|
||||
|
|
|
|||
|
|
@ -88,13 +88,13 @@
|
|||
#include "clixon_yang_internal.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_data.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_yang_type.h"
|
||||
#include "clixon_yang_parse.h"
|
||||
#include "clixon_yang_cardinality.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_yang_parse_lib.h"
|
||||
|
||||
/* Size of json read buffer when reading from file*/
|
||||
|
|
|
|||
|
|
@ -94,9 +94,9 @@
|
|||
#include "clixon_hash.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_yang_type.h"
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue