Added witddefaults parameter to xml2file and xml2cbuf

This commit is contained in:
Olof hagsand 2024-01-25 23:12:18 +01:00
parent dff3b25c34
commit b3ea6b550d
2 changed files with 349 additions and 104 deletions

View file

@ -43,10 +43,15 @@
/* /*
* Prototypes * Prototypes
*/ */
int clixon_xml2file1(FILE *f, cxobj *xn, int level, int pretty, char *prefix,
clicon_output_cb *fn, int skiptop, int autocliext, withdefaults_type wdef);
int clixon_xml2file(FILE *f, cxobj *xn, int level, int pretty, char *prefix, clicon_output_cb *fn, int skiptop, int autocliext); int clixon_xml2file(FILE *f, cxobj *xn, int level, int pretty, char *prefix, clicon_output_cb *fn, int skiptop, int autocliext);
int xml_print(FILE *f, cxobj *xn); int xml_print(FILE *f, cxobj *xn);
int xml_dump(FILE *f, cxobj *x); int xml_dump(FILE *f, cxobj *x);
int clixon_xml2cbuf(cbuf *cb, cxobj *x, int level, int prettyprint, char *prefix, int32_t depth, int skiptop); int clixon_xml2cbuf1(cbuf *cb, cxobj *x, int level, int prettyprint, char *prefix,
int32_t depth, int skiptop, withdefaults_type wdef);
int clixon_xml2cbuf(cbuf *cb, cxobj *x, int level, int prettyprint, char *prefix, int32_t depth,
int skiptop);
int xmltree2cbuf(cbuf *cb, cxobj *x, int level); int xmltree2cbuf(cbuf *cb, cxobj *x, int level);
int clixon_xml_parse_file(FILE *f, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr); int clixon_xml_parse_file(FILE *f, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr);
int clixon_xml_parse_string(const char *str, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr); int clixon_xml_parse_string(const char *str, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr);

View file

@ -73,6 +73,8 @@
#include "clixon_xml_sort.h" #include "clixon_xml_sort.h"
#include "clixon_xml_nsctx.h" #include "clixon_xml_nsctx.h"
#include "clixon_xml_parse.h" #include "clixon_xml_parse.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_default.h"
#include "clixon_xml_io.h" #include "clixon_xml_io.h"
/* /*
@ -88,21 +90,119 @@ static int xml_diff2cbuf(cbuf *cb, cxobj *x0, cxobj *x1, int level, int skiptop)
* XML printing functions. Output a parse tree to file, string cligen buf * XML printing functions. Output a parse tree to file, string cligen buf
*------------------------------------------------------------------------*/ *------------------------------------------------------------------------*/
/*! For xml2 output: compute with-defaults: if object should be printed or not
*
* @param[in] x Clixon xml tree
* @param[in] wdef With-defaults parameter, default is WITHDEFAULTS_REPORT_ALL
* @param[out] tag If set, use XML tag to mark value (WITHDEFAULTS_REPORT_ALL_TAGGED)
* @retval 1 Keep it
* @retval 0 Remove it
* @retval -1 Error
*/
static int
xml2output_wdef(cxobj *x,
withdefaults_type wdef,
int *tag)
{
int retval = -1;
int keep = 1;
cg_var *cv;
char *yv = NULL;
char *body;
cxobj *xc;
yang_stmt *y;
int ret;
int config;
if ((y = xml_spec(x)) == NULL)
goto ok;
switch (wdef){
case WITHDEFAULTS_EXPLICIT:
case WITHDEFAULTS_TRIM:
/* inline of xml_defaults_nopresence()
and xml_flag_state_default_value()
*/
switch(yang_keyword_get(y)){
case Y_CONTAINER:
if (yang_find(y, Y_PRESENCE, NULL) == NULL){
keep = 0;
/* Loop thru children */
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) {
if ((ret = xml2output_wdef(xc, wdef, NULL)) < 0)
goto done;
if (ret == 1)
keep = 1;
}
}
break;
case Y_LEAF:
config = yang_config_ancestor(y);
if (xml_flag(x, XML_FLAG_DEFAULT)) {
/* RFC 6243: Any value set by the NETCONF server [eg: state-data] that is not the schema
defined default value is also considered explicitly set data.*/
if (!config && wdef == WITHDEFAULTS_EXPLICIT)
;
else
keep = 0;
}
else if (wdef == WITHDEFAULTS_TRIM &&
(cv = yang_cv_get(y)) != NULL &&
(body = xml_body(x)) != NULL){
if ((yv = cv2str_dup(cv)) == NULL)
goto done;
if (body && strcmp(body, yv) == 0)
keep = 0;
}
break;
default:
break;
}
break;
case WITHDEFAULTS_REPORT_ALL_TAGGED:
if (tag && yang_keyword_get(y) == Y_LEAF){
if (xml_flag(x, XML_FLAG_DEFAULT))
*tag = 1;
else if ((cv = yang_cv_get(y)) != NULL &&
(body = xml_body(x)) != NULL){
if ((yv = cv2str_dup(cv)) == NULL)
goto done;
if (body && strcmp(body, yv) == 0)
*tag = 1;
}
}
break;
default:
break;
}
ok:
retval = keep;
done:
if (yv)
free(yv);
return retval;
}
/*! Print an XML tree structure to an output stream and encode chars "<>&" /*! Print an XML tree structure to an output stream and encode chars "<>&"
* *
* @param[in] f UNIX output stream * @param[in] f UNIX output stream
* @param[in] xn Clicon xml tree * @param[in] x Clixon xml tree
* @param[in] level How many spaces to insert before each line * @param[in] level How many spaces to insert before each line
* @param[in] pretty Insert \n and spaces to make the xml more readable. * @param[in] pretty Insert \n and spaces to make the xml more readable.
* @param[in] prefix Add string to beginning of each line (if pretty) * @param[in] prefix Add string to beginning of each line (if pretty)
* @param[in] fn Callback to make print function * @param[in] fn Callback to make print function
* @param[in] autocliext How to handle autocli extensions: 0: ignore 1: follow * @param[in] autocliext How to handle autocli extensions: 0: ignore 1: follow
* @param[in] wdef With-defaults parameter, default is WITHDEFAULTS_REPORT_ALL
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @see clixon_xml2cbuf
* One can use clixon_xml2cbuf to get common code, but using fprintf is * One can use clixon_xml2cbuf to get common code, but using fprintf is
* much faster than using cbuf and then printing that,... * much faster than using cbuf and then printing that,...
* * wdef changes the output as follows:
* - WITHDEFAULTS_REPORT_ALL - keep as-is
* - WITHDEFAULTS_TRIM - remove defaults + equal value, and no-presence
* - WITHDEFAULTS_EXPLICIT - remove defaults and no-presence
* - WITHDEFAULTS_REPORT_ALL_TAGGED
* @see xml2cbuf_recurse same with cbuf
*/ */
static int static int
xml2file_recurse(FILE *f, xml2file_recurse(FILE *f,
@ -111,7 +211,8 @@ xml2file_recurse(FILE *f,
int pretty, int pretty,
char *prefix, char *prefix,
clicon_output_cb *fn, clicon_output_cb *fn,
int autocliext) int autocliext,
withdefaults_type wdef)
{ {
int retval = -1; int retval = -1;
char *name; char *name;
@ -124,19 +225,26 @@ xml2file_recurse(FILE *f,
int exist = 0; int exist = 0;
yang_stmt *y; yang_stmt *y;
int level1; int level1;
int tag = 0;
int ret;
if (x == NULL) if (x == NULL)
goto ok; goto ok;
level1 = level*PRETTYPRINT_INDENT; level1 = level*PRETTYPRINT_INDENT;
if (prefix) if (prefix)
level1 -= strlen(prefix); level1 -= strlen(prefix);
if (autocliext && if ((y = xml_spec(x)) != NULL){
(y = xml_spec(x)) != NULL){ if (autocliext){
if (yang_extension_value(y, "hide-show", CLIXON_AUTOCLI_NS, &exist, NULL) < 0) if (yang_extension_value(y, "hide-show", CLIXON_AUTOCLI_NS, &exist, NULL) < 0)
goto done; goto done;
if (exist) if (exist)
goto ok; goto ok;
} }
if ((ret = xml2output_wdef(x, wdef, &tag)) < 0)
goto done;
if (ret == 0)
goto ok;
}
name = xml_name(x); name = xml_name(x);
namespace = xml_prefix(x); namespace = xml_prefix(x);
switch(xml_type(x)){ switch(xml_type(x)){
@ -160,6 +268,8 @@ xml2file_recurse(FILE *f,
if (namespace) if (namespace)
(*fn)(f, "%s:", namespace); (*fn)(f, "%s:", namespace);
(*fn)(f, "%s", name); (*fn)(f, "%s", name);
if (tag) /* If default and WITHDEFAULTS_REPORT_ALL_TAGGED */
(*fn)(f, " wd:default=\"true\"");
hasbody = 0; hasbody = 0;
haselement = 0; haselement = 0;
xc = NULL; xc = NULL;
@ -167,7 +277,7 @@ xml2file_recurse(FILE *f,
while ((xc = xml_child_each(x, xc, -1)) != NULL) { while ((xc = xml_child_each(x, xc, -1)) != NULL) {
switch (xml_type(xc)){ switch (xml_type(xc)){
case CX_ATTR: case CX_ATTR:
if (xml2file_recurse(f, xc, level+1, pretty, prefix, fn, autocliext) <0) if (xml2file_recurse(f, xc, level+1, pretty, prefix, fn, autocliext, wdef) <0)
goto done; goto done;
break; break;
case CX_BODY: case CX_BODY:
@ -192,9 +302,27 @@ xml2file_recurse(FILE *f,
} }
xc = NULL; xc = NULL;
while ((xc = xml_child_each(x, xc, -1)) != NULL) { while ((xc = xml_child_each(x, xc, -1)) != NULL) {
if (xml_type(xc) != CX_ATTR) cxobj *xa = NULL;
if (xml2file_recurse(f, xc, level+1, pretty, prefix, fn, autocliext) <0) char *ns = NULL;
if (wdef == WITHDEFAULTS_REPORT_ALL_TAGGED &&
y == NULL &&
xml_spec(xc) != NULL){
if (xml2ns(xc, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, &ns) < 0)
goto done; goto done;
if (ns == NULL){
if (xmlns_set(xc, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE) < 0)
goto done;
xa = xml_find_type(xc, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE, CX_ATTR);
}
}
if (xml_type(xc) != CX_ATTR)
if (xml2file_recurse(f, xc, level+1, pretty, prefix, fn, autocliext, wdef) <0)
goto done;
if (xa){
if (xml_purge(xa) < 0)
goto done;
}
} }
if (pretty && hasbody==0){ if (pretty && hasbody==0){
if (pretty && prefix) if (pretty && prefix)
@ -223,6 +351,57 @@ xml2file_recurse(FILE *f,
/*! Print an XML tree structure to an output stream and encode chars "<>&" /*! Print an XML tree structure to an output stream and encode chars "<>&"
* *
* Extended version with with-defaults
* Assume xn being in REPORT_ALL state, modify default values according to wdef
* @param[in] f Output file
* @param[in] xn XML tree
* @param[in] level How many spaces to insert before each line
* @param[in] pretty Insert \n and spaces to make the xml more readable.
* @param[in] prefix Add string to beginning of each line (if pretty)
* @param[in] fn File print function (if NULL, use fprintf)
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
* @param[in] autocliext How to handle autocli extensions: 0: ignore 1: follow
* @param[in] wdef With-defaults parameter, default is WITHDEFAULTS_REPORT_ALL
* @retval 0 OK
* @retval -1 Error
* @see clixon_xml2cbuf print to a cbuf string
* @note There is a slight "layer violation" with the autocli parameter: it should normally be set
* for CLI calls, but not for others.
*/
int
clixon_xml2file1(FILE *f,
cxobj *xn,
int level,
int pretty,
char *prefix,
clicon_output_cb *fn,
int skiptop,
int autocliext,
withdefaults_type wdef)
{
int retval = 1;
cxobj *xc;
if (fn == NULL)
fn = fprintf;
if (skiptop){
xc = NULL;
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL)
if (xml2file_recurse(f, xc, level, pretty, prefix, fn, autocliext, wdef) < 0)
goto done;
}
else {
if (xml2file_recurse(f, xn, level, pretty, prefix, fn, autocliext, wdef) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Print an XML tree structure to an output stream and encode chars "<>&"
*
* Assume xn being in REPORT_ALL state, modify default values according to wdef
* @param[in] f Output file * @param[in] f Output file
* @param[in] xn XML tree * @param[in] xn XML tree
* @param[in] level How many spaces to insert before each line * @param[in] level How many spaces to insert before each line
@ -236,6 +415,7 @@ xml2file_recurse(FILE *f,
* @see clixon_xml2cbuf print to a cbuf string * @see clixon_xml2cbuf print to a cbuf string
* @note There is a slight "layer violation" with the autocli parameter: it should normally be set * @note There is a slight "layer violation" with the autocli parameter: it should normally be set
* for CLI calls, but not for others. * for CLI calls, but not for others.
* @see clixon_xml2file1 for extended version with wdef
*/ */
int int
clixon_xml2file(FILE *f, clixon_xml2file(FILE *f,
@ -247,31 +427,14 @@ clixon_xml2file(FILE *f,
int skiptop, int skiptop,
int autocliext) int autocliext)
{ {
int retval = 1; return clixon_xml2file1(f, xn, level, pretty, prefix, fn, skiptop, autocliext, 0);
cxobj *xc;
if (fn == NULL)
fn = fprintf;
if (skiptop){
xc = NULL;
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL)
if (xml2file_recurse(f, xc, level, pretty, prefix, fn, autocliext) < 0)
goto done;
}
else {
if (xml2file_recurse(f, xn, level, pretty, prefix, fn, autocliext) < 0)
goto done;
}
retval = 0;
done:
return retval;
} }
/*! Print an XML tree structure to an output stream /*! Print an XML tree structure to an output stream
* *
* Utility function eg in gdb. For code, use clixon_xml2file * Utility function eg in gdb. For code, use clixon_xml2file
* @param[in] f UNIX output stream * @param[in] f UNIX output stream
* @param[in] xn clicon xml tree * @param[in] xn clixon xml tree
* @see clixon_xml2cbuf * @see clixon_xml2cbuf
* @see clixon_xml2cbuf_cb print using a callback * @see clixon_xml2cbuf_cb print using a callback
*/ */
@ -279,7 +442,7 @@ int
xml_print(FILE *f, xml_print(FILE *f,
cxobj *x) cxobj *x)
{ {
return xml2file_recurse(f, x, 0, 1, NULL, fprintf, 0); return xml2file_recurse(f, x, 0, 1, NULL, fprintf, 0, WITHDEFAULTS_REPORT_ALL);
} }
/*! Dump cxobj structure with pointers and flags for debugging, internal function /*! Dump cxobj structure with pointers and flags for debugging, internal function
@ -335,16 +498,24 @@ xml_dump(FILE *f,
* @param[in] pretty Insert \n and spaces to make the xml more readable. * @param[in] pretty Insert \n and spaces to make the xml more readable.
* @param[in] prefix Add string to beginning of each line (if pretty) * @param[in] prefix Add string to beginning of each line (if pretty)
* @param[in] depth Limit levels of child resources: -1 is all, 0 is none, 1 is node itself * @param[in] depth Limit levels of child resources: -1 is all, 0 is none, 1 is node itself
* @param[in] wdef With-defaults parameter, default is WITHDEFAULTS_REPORT_ALL
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* wdef changes the output as follows:
* - WITHDEFAULTS_REPORT_ALL - keep as-is
* - WITHDEFAULTS_TRIM - remove defaults + equal value, and no-presence
* - WITHDEFAULTS_EXPLICIT - remove defaults and no-presence
* - WITHDEFAULTS_REPORT_ALL_TAGGED
* @see xml2file_recurse same with FILE
*/ */
static int static int
clixon_xml2cbuf1(cbuf *cb, xml2cbuf_recurse(cbuf *cb,
cxobj *x, cxobj *x,
int level, int level,
int pretty, int pretty,
char *prefix, char *prefix,
int32_t depth) int32_t depth,
withdefaults_type wdef)
{ {
int retval = -1; int retval = -1;
cxobj *xc; cxobj *xc;
@ -354,9 +525,19 @@ clixon_xml2cbuf1(cbuf *cb,
char *namespace; char *namespace;
char *val; char *val;
int level1; int level1;
yang_stmt *y;
int tag = 0;
int ret;
if (depth == 0) if (depth == 0)
goto ok; goto ok;
if ((y = xml_spec(x)) != NULL){
/* with-defaults: if object should be printed or not */
if ((ret = xml2output_wdef(x, wdef, &tag)) < 0)
goto done;
if (ret == 0)
goto ok;
}
level1 = level*PRETTYPRINT_INDENT; level1 = level*PRETTYPRINT_INDENT;
if (prefix) if (prefix)
level1 -= strlen(prefix); level1 -= strlen(prefix);
@ -390,6 +571,8 @@ clixon_xml2cbuf1(cbuf *cb,
cbuf_append_str(cb, ":"); cbuf_append_str(cb, ":");
} }
cbuf_append_str(cb, name); cbuf_append_str(cb, name);
if (tag) /* If default and WITHDEFAULTS_REPORT_ALL_TAGGED */
cbuf_append_str(cb, " wd:default=\"true\"");
hasbody = 0; hasbody = 0;
haselement = 0; haselement = 0;
xc = NULL; xc = NULL;
@ -397,7 +580,7 @@ clixon_xml2cbuf1(cbuf *cb,
while ((xc = xml_child_each(x, xc, -1)) != NULL) while ((xc = xml_child_each(x, xc, -1)) != NULL)
switch (xml_type(xc)){ switch (xml_type(xc)){
case CX_ATTR: case CX_ATTR:
if (clixon_xml2cbuf1(cb, xc, level+1, pretty, prefix, -1) < 0) if (xml2cbuf_recurse(cb, xc, level+1, pretty, prefix, -1, wdef) < 0)
goto done; goto done;
break; break;
case CX_BODY: case CX_BODY:
@ -418,9 +601,29 @@ clixon_xml2cbuf1(cbuf *cb,
cbuf_append_str(cb, "\n"); cbuf_append_str(cb, "\n");
xc = NULL; xc = NULL;
while ((xc = xml_child_each(x, xc, -1)) != NULL) while ((xc = xml_child_each(x, xc, -1)) != NULL)
if (xml_type(xc) != CX_ATTR) if (xml_type(xc) != CX_ATTR){
if (clixon_xml2cbuf1(cb, xc, level+1, pretty, prefix, depth-1) < 0) cxobj *xa = NULL;
char *ns = NULL;
/* If tagged withdefaults */
if (wdef == WITHDEFAULTS_REPORT_ALL_TAGGED &&
y == NULL &&
xml_spec(xc) != NULL){
if (xml2ns(xc, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, &ns) < 0)
goto done; goto done;
if (ns == NULL){
if (xmlns_set(xc, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE) < 0)
goto done;
xa = xml_find_type(xc, IETF_NETCONF_WITH_DEFAULTS_ATTR_PREFIX, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE, CX_ATTR);
}
}
if (xml2cbuf_recurse(cb, xc, level+1, pretty, prefix, depth-1, wdef) < 0)
goto done;
if (xa){
if (xml_purge(xa) < 0)
goto done;
}
}
if (pretty && hasbody == 0){ if (pretty && hasbody == 0){
if (prefix) if (prefix)
cprintf(cb, "%s", prefix); cprintf(cb, "%s", prefix);
@ -446,6 +649,57 @@ clixon_xml2cbuf1(cbuf *cb,
return retval; return retval;
} }
/*! Print an XML tree structure to a cligen buffer and encode chars "<>&"
*
* Extended version with with-defaults
* @param[in,out] cb Cligen buffer to write to
* @param[in] xn Top-level xml object
* @param[in] level Indentation level for pretty
* @param[in] pretty Insert \n and spaces to make the xml more readable.
* @param[in] prefix Add string to beginning of each line (or NULL) (if pretty)
* @param[in] depth Limit levels of child resources: -1: all, 0: none, 1: node itself
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
* @param[in] wdef With-defaults parameter, default is WITHDEFAULTS_REPORT_ALL
* @retval 0 OK
* @retval -1 Error
* Depth is used in NACM
* @code
* cbuf *cb = cbuf_new();
* if (clixon_xml2cbuf(cb, xn, 0, 1, NULL, -1, 0, 0) < 0)
* goto err;
* fprintf(stderr, "%s", cbuf_get(cb));
* cbuf_free(cb);
* @endcode
* @see clixon_xml2file to file, which is faster
*/
int
clixon_xml2cbuf1(cbuf *cb,
cxobj *xn,
int level,
int pretty,
char *prefix,
int32_t depth,
int skiptop,
withdefaults_type wdef)
{
int retval = -1;
cxobj *xc;
if (skiptop){
xc = NULL;
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL)
if (xml2cbuf_recurse(cb, xc, level, pretty, prefix, depth, wdef) < 0)
goto done;
}
else {
if (xml2cbuf_recurse(cb, xn, level, pretty, prefix, depth, wdef) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Print an XML tree structure to a cligen buffer and encode chars "<>&" /*! Print an XML tree structure to a cligen buffer and encode chars "<>&"
* *
* @param[in,out] cb Cligen buffer to write to * @param[in,out] cb Cligen buffer to write to
@ -460,12 +714,13 @@ clixon_xml2cbuf1(cbuf *cb,
* Depth is used in NACM * Depth is used in NACM
* @code * @code
* cbuf *cb = cbuf_new(); * cbuf *cb = cbuf_new();
* if (clixon_xml2cbuf(cb, xn, 0, 1, NULL, -1, 0) < 0) * if (clixon_xml2cbuf(cb, xn, 0, 1, NULL, -1, 0, 0) < 0)
* goto err; * goto err;
* fprintf(stderr, "%s", cbuf_get(cb)); * fprintf(stderr, "%s", cbuf_get(cb));
* cbuf_free(cb); * cbuf_free(cb);
* @endcode * @endcode
* @see clixon_xml2file * @see clixon_xml2file to file, which is faster
* @see clixon_xml2cbuf1 for extended version with wdef
*/ */
int int
clixon_xml2cbuf(cbuf *cb, clixon_xml2cbuf(cbuf *cb,
@ -476,28 +731,13 @@ clixon_xml2cbuf(cbuf *cb,
int32_t depth, int32_t depth,
int skiptop) int skiptop)
{ {
int retval = -1; return clixon_xml2cbuf1(cb, xn, level, pretty, prefix, depth, skiptop, 0);
cxobj *xc;
if (skiptop){
xc = NULL;
while ((xc = xml_child_each(xn, xc, CX_ELMNT)) != NULL)
if (clixon_xml2cbuf1(cb, xc, level, pretty, prefix, depth) < 0)
goto done;
}
else {
if (clixon_xml2cbuf1(cb, xn, level, pretty, prefix, depth) < 0)
goto done;
}
retval = 0;
done:
return retval;
} }
/*! Print actual xml tree datastructures (not xml), mainly for debugging /*! Print actual xml tree datastructures (not xml), mainly for debugging
* *
* @param[in,out] cb Cligen buffer to write to * @param[in,out] cb Cligen buffer to write to
* @param[in] xn Clicon xml tree * @param[in] xn Clixon xml tree
* @param[in] level Indentation level * @param[in] level Indentation level
*/ */
int int