From 0adcd94f3ffd5e32d5ce5e98a06b691ba8fc68cf Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Wed, 17 Jun 2020 14:04:19 +0200 Subject: [PATCH] * Added new cli show functions to work with cligen_output for cligen pageing to work. To acheive this, add a callback function as follows: * xml2txt(...) --> xml2txt_cb(..., cligen_output) * xml2cli(...) --> xml2cli_cb(..., cligen_output) * clicon_xml2file(...) --> clicon_xml2file_cb(..., cligen_output) * xml2json(...) --> xml2json_cb(..., cligen_output) * yang_print(...) --> yang_print_cb(..., cligen_output) --- CHANGELOG.md | 9 ++- apps/cli/cli_common.c | 14 ++-- apps/cli/cli_show.c | 44 ++++++------ example/main/example_cli.c | 6 +- lib/clixon/clixon_handle.h | 14 ++++ lib/clixon/clixon_json.h | 1 + lib/clixon/clixon_xml_io.h | 3 +- lib/clixon/clixon_xml_map.h | 7 +- lib/clixon/clixon_yang.h | 1 + lib/src/clixon_json.c | 58 ++++++++++----- lib/src/clixon_xml_io.c | 86 +++++++++++++++------- lib/src/clixon_xml_map.c | 138 +++++++++++++++++++++++++++--------- lib/src/clixon_yang.c | 24 +++++-- 13 files changed, 291 insertions(+), 114 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a2a0329..db93bd3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,12 +42,19 @@ Expected: July 2020 ### API changes on existing protocol/config features (For users) - * Restconf FCGI (eg via nginx) have changed reply message syntax as follows (due to refactoring and common code with evhtp): + * Restconf FCGI (eg via nginx) have changed reply message syntax slightly as follows (due to refactoring and common code with evhtp): * Bodies in error reyruns including html code have been removed * Some (extra) CRLF:s have been removed ### C/CLI-API changes on existing features (For developers) +* Added new cli show functions to work with cligen_output for cligen pageing to work. To acheive this, add a callback function as follows: + * xml2txt(...) --> xml2txt_cb(..., cligen_output) + * xml2cli(...) --> xml2cli_cb(..., cligen_output) + * clicon_xml2file(...) --> clicon_xml2file_cb(..., cligen_output) + * xml2json(...) --> xml2json_cb(..., cligen_output) + * yang_print(...) --> yang_print_cb(..., cligen_output) + * Added prefix for cli_show_config/cli_show_auto so that it can produce parseable output * Replaced the global variable `debug` with access function: `clicon_debug_get()`. * Due to name collision with libevent, all clixon event functions prepended with `clixon_`. You need to rename your event functions as follows: diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 3ab4115b..b9016e92 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -652,10 +652,10 @@ compare_xmls(cxobj *xc1, xc = NULL; if (astext) while ((xc = xml_child_each(xc1, xc, -1)) != NULL) - xml2txt(f, xc, 0); + xml2txt_cb(f, xc, cligen_output); else while ((xc = xml_child_each(xc1, xc, -1)) != NULL) - xml_print(f, xc); + clicon_xml2file_cb(f, xc, 0, 1, cligen_output); fclose(f); close(fd); @@ -669,10 +669,10 @@ compare_xmls(cxobj *xc1, xc = NULL; if (astext) while ((xc = xml_child_each(xc2, xc, -1)) != NULL) - xml2txt(f, xc, 0); + xml2txt_cb(f, xc, cligen_output); else while ((xc = xml_child_each(xc2, xc, -1)) != NULL) - xml_print(f, xc); + clicon_xml2file_cb(f, xc, 0, 1, cligen_output); fclose(f); close(fd); @@ -1002,15 +1002,15 @@ cli_notification_cb(int s, while ((x = xml_child_each(xe, x, -1)) != NULL) { switch (format){ case FORMAT_XML: - if (clicon_xml2file(stdout, x, 0, 1) < 0) + if (clicon_xml2file_cb(stdout, x, 0, 1, cligen_output) < 0) goto done; break; case FORMAT_TEXT: - if (xml2txt(stdout, x, 0) < 0) + if (xml2txt_cb(stdout, x, cligen_output) < 0) goto done; break; case FORMAT_JSON: - if (xml2json(stdout, x, 1) < 0) + if (xml2json_cb(stdout, x, 1, cligen_output) < 0) goto done; break; default: diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 11146ba0..9c230aae 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -395,19 +395,19 @@ show_yang(clicon_handle h, cvec *cvv, cvec *argv) { - yang_stmt *yn; - char *str = NULL; - yang_stmt *yspec; + yang_stmt *yn; + char *str = NULL; + yang_stmt *yspec; - yspec = clicon_dbspec_yang(h); - if (cvec_len(argv) > 0){ - str = cv_string_get(cvec_i(argv, 0)); - yn = yang_find(yspec, 0, str); - } - else - yn = yspec; - yang_print(stdout, yn); - return 0; + yspec = clicon_dbspec_yang(h); + if (cvec_len(argv) > 0){ + str = cv_string_get(cvec_i(argv, 0)); + yn = yang_find(yspec, 0, str); + } + else + yn = yspec; + yang_print_cb(stdout, yn, cligen_output); /* Doesnt use cligen_output */ + return 0; } /*! Show configuration and state internal function @@ -506,15 +506,15 @@ cli_show_config1(clicon_handle h, case FORMAT_XML: xc = NULL; /* Dont print xt itself */ while ((xc = xml_child_each(xt, xc, -1)) != NULL) - clicon_xml2file(stdout, xc, 0, 1); + clicon_xml2file_cb(stdout, xc, 0, 1, cligen_output); break; case FORMAT_JSON: - xml2json(stdout, xt, 1); + xml2json_cb(stdout, xt, 1, cligen_output); break; case FORMAT_TEXT: xc = NULL; /* Dont print xt itself */ while ((xc = xml_child_each(xt, xc, -1)) != NULL) - xml2txt(stdout, xc, 0); /* tree-formed text */ + xml2txt_cb(stdout, xc, cligen_output); /* tree-formed text */ break; case FORMAT_CLI: /* get CLI generatade mode: VARS|ALL */ @@ -522,14 +522,14 @@ cli_show_config1(clicon_handle h, goto done; xc = NULL; /* Dont print xt itself */ while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) - xml2cli(stdout, xc, prefix, gt); /* cli syntax */ + xml2cli_cb(stdout, xc, prefix, gt, cligen_output); /* cli syntax */ break; case FORMAT_NETCONF: - fprintf(stdout, "\n"); + cligen_output(stdout, "\n"); xc = NULL; /* Dont print xt itself */ while ((xc = xml_child_each(xt, xc, -1)) != NULL) - clicon_xml2file(stdout, xc, 2, 1); - fprintf(stdout, "]]>]]>\n"); + clicon_xml2file_cb(stdout, xc, 2, 1, cligen_output); + cligen_output(stdout, "]]>]]>\n"); break; } retval = 0; @@ -763,15 +763,15 @@ cli_show_auto1(clicon_handle h, clicon_xml2file(stdout, xp, 0, 1); break; case FORMAT_JSON: - xml2json(stdout, xp, 1); + xml2json_cb(stdout, xp, 1, cligen_output); break; case FORMAT_TEXT: - xml2txt(stdout, xp, 0); /* tree-formed text */ + xml2txt_cb(stdout, xp, cligen_output); /* tree-formed text */ break; case FORMAT_CLI: if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR) goto done; - xml2cli(stdout, xp, prefix, gt); /* cli syntax */ + xml2cli_cb(stdout, xp, prefix, gt, cligen_output); /* cli syntax */ break; case FORMAT_NETCONF: fprintf(stdout, "\n"); diff --git a/example/main/example_cli.c b/example/main/example_cli.c index 80400688..4038ff7b 100644 --- a/example/main/example_cli.c +++ b/example/main/example_cli.c @@ -74,7 +74,7 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv) nsc, &xret) < 0) goto done; - xml_print(stdout, xret); + clicon_xml2file_cb(stdout, xret, 0, 1, cligen_output); retval = 0; done: if (nsc) @@ -117,11 +117,11 @@ example_client_rpc(clicon_handle h, goto done; } /* Print result */ - clicon_xml2file(stdout, xml_child_i(xret, 0), 0, 0); + clicon_xml2file_cb(stdout, xml_child_i(xret, 0), 0, 0, cligen_output); fprintf(stdout,"\n"); /* pretty-print: - xml2txt(stdout, xml_child_i(xret, 0), 0); + xml2txt_cb(stdout, xml_child_i(xret, 0), cligen_output); */ retval = 0; done: diff --git a/lib/clixon/clixon_handle.h b/lib/clixon/clixon_handle.h index 7da96fad..20f2dbcd 100644 --- a/lib/clixon/clixon_handle.h +++ b/lib/clixon/clixon_handle.h @@ -53,6 +53,20 @@ typedef void *clicon_handle; /* The dynamicically loadable plugin object handle (should be in clixon_plugin.h) */ typedef void *plghndl_t; +/*! Indirect output functions to print with something else than fprintf + * @param[in] h Clicon handle + * @param[in] xn Request: + * @param[out] cbret Return xml tree, eg ..., 0 -> {"a":["0"]} and not {"a":"0"} + * @code + * if (xml2json(stderr, xn, 0) < 0) + * goto err; + * @endcode + */ +int +xml2json_cb(FILE *f, + cxobj *x, + int pretty, + clicon_output_cb *fn) +{ + int retval = 1; + cbuf *cb = NULL; + + if ((cb = cbuf_new()) ==NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + if (xml2json_cbuf(cb, x, pretty) < 0) + goto done; + (*fn)(f, "%s", cbuf_get(cb)); + retval = 0; + done: + if (cb) + cbuf_free(cb); + return retval; +} + /*! Translate from xml tree to JSON and print to file * @param[in] f File to print to * @param[in] x XML tree to translate from @@ -1026,23 +1063,10 @@ xml2json(FILE *f, cxobj *x, int pretty) { - int retval = 1; - cbuf *cb = NULL; - - if ((cb = cbuf_new()) ==NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); - goto done; - } - if (xml2json_cbuf(cb, x, pretty) < 0) - goto done; - fprintf(f, "%s", cbuf_get(cb)); - retval = 0; - done: - if (cb) - cbuf_free(cb); - return retval; + return xml2json_cb(f, x, pretty, fprintf); } + /*! Print an XML tree structure to an output stream as JSON * * @param[in] f UNIX output stream @@ -1050,9 +1074,9 @@ xml2json(FILE *f, */ int json_print(FILE *f, - cxobj *xn) + cxobj *x) { - return xml2json(f, xn, 1); + return xml2json_cb(f, x, 1, fprintf); } /*! Translate a vector of xml objects to JSON File. diff --git a/lib/src/clixon_xml_io.c b/lib/src/clixon_xml_io.c index cb4d3982..1cfedd1b 100644 --- a/lib/src/clixon_xml_io.c +++ b/lib/src/clixon_xml_io.c @@ -96,15 +96,17 @@ * @param[in] xn clicon xml tree * @param[in] level how many spaces to insert before each line * @param[in] prettyprint insert \n and spaces tomake the xml more readable. + * @param[in] fn Callback to make print function * @see clicon_xml2cbuf * One can use clicon_xml2cbuf to get common code, but using fprintf is * much faster than using cbuf and then printing that,... */ int -clicon_xml2file(FILE *f, - cxobj *x, - int level, - int prettyprint) +xml2file_recurse(FILE *f, + cxobj *x, + int level, + int prettyprint, + clicon_output_cb *fn) { int retval = -1; char *name; @@ -125,19 +127,19 @@ clicon_xml2file(FILE *f, break; if (xml_chardata_encode(&encstr, "%s", val) < 0) goto done; - fprintf(f, "%s", encstr); + (*fn)(f, "%s", encstr); break; case CX_ATTR: - fprintf(f, " "); + (*fn)(f, " "); if (namespace) - fprintf(f, "%s:", namespace); - fprintf(f, "%s=\"%s\"", name, xml_value(x)); + (*fn)(f, "%s:", namespace); + (*fn)(f, "%s=\"%s\"", name, xml_value(x)); break; case CX_ELMNT: - fprintf(f, "%*s<", prettyprint?(level*XML_INDENT):0, ""); + (*fn)(f, "%*s<", prettyprint?(level*XML_INDENT):0, ""); if (namespace) - fprintf(f, "%s:", namespace); - fprintf(f, "%s", name); + (*fn)(f, "%s:", namespace); + (*fn)(f, "%s", name); hasbody = 0; haselement = 0; xc = NULL; @@ -145,7 +147,7 @@ clicon_xml2file(FILE *f, while ((xc = xml_child_each(x, xc, -1)) != NULL) { switch (xml_type(xc)){ case CX_ATTR: - if (clicon_xml2file(f, xc, level+1, prettyprint) <0) + if (xml2file_recurse(f, xc, level+1, prettyprint, fn) <0) goto done; break; case CX_BODY: @@ -162,26 +164,26 @@ clicon_xml2file(FILE *f, * Ie, no CX_BODY or CX_ELMNT child. */ if (hasbody==0 && haselement==0) - fprintf(f, "/>"); + (*fn)(f, "/>"); else{ - fprintf(f, ">"); + (*fn)(f, ">"); if (prettyprint && hasbody == 0) - fprintf(f, "\n"); + (*fn)(f, "\n"); xc = NULL; while ((xc = xml_child_each(x, xc, -1)) != NULL) { if (xml_type(xc) != CX_ATTR) - if (clicon_xml2file(f, xc, level+1, prettyprint) <0) + if (xml2file_recurse(f, xc, level+1, prettyprint, fn) <0) goto done; } if (prettyprint && hasbody==0) - fprintf(f, "%*s", level*XML_INDENT, ""); - fprintf(f, "", name); + (*fn)(f, "%s:", namespace); + (*fn)(f, "%s>", name); } if (prettyprint) - fprintf(f, "\n"); + (*fn)(f, "\n"); break; default: break; @@ -194,6 +196,42 @@ clicon_xml2file(FILE *f, return retval; } +/*! Print an XML tree structure to an output stream and encode chars "<>&" + * + * @param[in] f UNIX output stream + * @param[in] xn clicon xml tree + * @param[in] level how many spaces to insert before each line + * @param[in] prettyprint insert \n and spaces tomake the xml more readable. + * @see clicon_xml2cbuf print to a cbuf string + * @see clicon_xml2cbuf_cb print using a callback + */ +int +clicon_xml2file(FILE *f, + cxobj *x, + int level, + int prettyprint) +{ + return xml2file_recurse(f, x, level, prettyprint, fprintf); +} + +/*! Print an XML tree structure to an output stream and encode chars "<>&" + * + * @param[in] f UNIX output stream + * @param[in] xn clicon xml tree + * @param[in] level how many spaces to insert before each line + * @param[in] prettyprint insert \n and spaces tomake the xml more readable. + * @see clicon_xml2cbuf + */ +int +clicon_xml2file_cb(FILE *f, + cxobj *x, + int level, + int prettyprint, + clicon_output_cb *fn) +{ + return xml2file_recurse(f, x, level, prettyprint, fn); +} + /*! Print an XML tree structure to an output stream * * Uses clicon_xml2file internally @@ -201,13 +239,13 @@ clicon_xml2file(FILE *f, * @param[in] f UNIX output stream * @param[in] xn clicon xml tree * @see clicon_xml2cbuf - * @see clicon_xml2file + * @see clicon_xml2cbuf_cb print using a callback */ int xml_print(FILE *f, - cxobj *xn) + cxobj *x) { - return clicon_xml2file(f, xn, 0, 1); + return xml2file_recurse(f, x, 0, 1, fprintf); } /*! Print an XML tree structure to a cligen buffer and encode chars "<>&" diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 8a9a441c..29cb67f1 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -129,19 +129,26 @@ tleaf(cxobj *x) return (xml_child_nr_notype(xc, CX_ATTR) == 0); } -/*! Translate XML -> TEXT +/*! Translate XML to a "pseudo-code" textual format using a callback - internal function + * @param[in] f File to print to + * @param[in] x XML object to print + * @param[in] fn Callback to make print function * @param[in] level print 4 spaces per level in front of each line - * XXX rewrite using YANG and remove encrypted password KLUDGE */ -int -xml2txt(FILE *f, - cxobj *x, - int level) +static int +xml2txt_recurse(FILE *f, + cxobj *x, + clicon_output_cb *fn, + int level) { cxobj *xc = NULL; int children=0; int retval = -1; + if (f == NULL || x == NULL || fn == NULL){ + clicon_err(OE_XML, EINVAL, "f, x or fn is NULL"); + goto done; + } xc = NULL; /* count children (elements and bodies, not attributes) */ while ((xc = xml_child_each(x, xc, -1)) != NULL) if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY) @@ -149,48 +156,76 @@ xml2txt(FILE *f, if (!children){ /* If no children print line */ switch (xml_type(x)){ case CX_BODY: - fprintf(f, "%s;\n", xml_value(x)); + (*fn)(f, "%s;\n", xml_value(x)); break; case CX_ELMNT: - fprintf(f, "%*s;\n", 4*level, xml_name(x)); + (*fn)(f, "%*s;\n", 4*level, xml_name(x)); break; default: break; } goto ok; } - fprintf(f, "%*s", 4*level, ""); - fprintf(f, "%s ", xml_name(x)); + (*fn)(f, "%*s", 4*level, ""); + (*fn)(f, "%s ", xml_name(x)); if (!tleaf(x)) - fprintf(f, "{\n"); + (*fn)(f, "{\n"); xc = NULL; while ((xc = xml_child_each(x, xc, -1)) != NULL){ if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY) - if (xml2txt(f, xc, level+1) < 0) + if (xml2txt_recurse(f, xc, fn, level+1) < 0) break; } if (!tleaf(x)) - fprintf(f, "%*s}\n", 4*level, ""); + (*fn)(f, "%*s}\n", 4*level, ""); ok: retval = 0; - // done: + done: return retval; } +/*! Translate XML to a "pseudo-code" textual format using a callback + * @param[in] f File to print to + * @param[in] x XML object to print + * @param[in] fn Callback to make print function + */ +int +xml2txt_cb(FILE *f, + cxobj *x, + clicon_output_cb *fn) +{ + return xml2txt_recurse(f, x, fn, 0); +} + +/*! Translate XML to a "pseudo-code" textual format using stdio file + * @param[in] f File to print to + * @param[in] x XML object to print + * @param[in] level print 4 spaces per level in front of each line + * @see xml2txt_cb + */ +int +xml2txt(FILE *f, + cxobj *x, + int level) +{ + return xml2txt_recurse(f, x, fprintf, 0); +} + /*! Translate from XML to CLI commands * Howto: join strings and pass them down. * Identify unique/index keywords for correct set syntax. - * Args: - * @param[in] f Where to print cli commands - * @param[in] x XML Parse-tree (to translate) - * @param[in] prepend0 Print this text in front of all commands. - * @param[in] gt option to steer cli syntax + * @param[in] f Where to print cli commands + * @param[in] x XML Parse-tree (to translate) + * @param[in] prepend Print this text in front of all commands. + * @param[in] gt option to steer cli syntax + * @param[in] fn Callback to make print function */ int -xml2cli(FILE *f, - cxobj *x, - char *prepend0, - enum genmodel_type gt) +xml2cli_recurse(FILE *f, + cxobj *x, + char *prepend, + enum genmodel_type gt, + clicon_output_cb *fn) { int retval = -1; cxobj *xe = NULL; @@ -204,17 +239,17 @@ xml2cli(FILE *f, if ((ys = xml_spec(x)) == NULL) goto ok; if (yang_keyword_get(ys) == Y_LEAF || yang_keyword_get(ys) == Y_LEAF_LIST){ - if (prepend0) - fprintf(f, "%s", prepend0); + if (prepend) + (*fn)(f, "%s", prepend); if (gt == GT_ALL || gt == GT_VARS || gt == GT_HIDE) - fprintf(f, "%s ", xml_name(x)); + (*fn)(f, "%s ", xml_name(x)); if ((body = xml_body(x)) != NULL){ if (index(body, ' ')) - fprintf(f, "\"%s\"", body); + (*fn)(f, "\"%s\"", body); else - fprintf(f, "%s", body); + (*fn)(f, "%s", body); } - fprintf(f, "\n"); + (*fn)(f, "\n"); goto ok; } /* Create prepend variable string */ @@ -222,8 +257,8 @@ xml2cli(FILE *f, clicon_err(OE_PLUGIN, errno, "cbuf_new"); goto done; } - if (prepend0) - cprintf(cbpre, "%s", prepend0); + if (prepend) + cprintf(cbpre, "%s", prepend); /* If non-presence container && HIDE mode && only child is * a list, then skip container keyword @@ -251,11 +286,11 @@ xml2cli(FILE *f, if ((match = yang_key_match(ys, xml_name(xe))) < 0) goto done; if (match){ - fprintf(f, "%s\n", cbuf_get(cbpre)); + (*fn)(f, "%s\n", cbuf_get(cbpre)); continue; /* Not key itself */ } } - if (xml2cli(f, xe, cbuf_get(cbpre), gt) < 0) + if (xml2cli_recurse(f, xe, cbuf_get(cbpre), gt, fn) < 0) goto done; } ok: @@ -266,6 +301,43 @@ xml2cli(FILE *f, return retval; } +/*! Translate from XML to CLI commands + * Howto: join strings and pass them down. + * Identify unique/index keywords for correct set syntax. + * @param[in] f Where to print cli commands + * @param[in] x XML Parse-tree (to translate) + * @param[in] prepend Print this text in front of all commands. + * @param[in] gt option to steer cli syntax + * @param[in] fn Callback to make print function + */ +int +xml2cli_cb(FILE *f, + cxobj *x, + char *prepend, + enum genmodel_type gt, + clicon_output_cb *fn) +{ + return xml2cli_recurse(f, x, prepend, gt, fn); +} + +/*! Translate from XML to CLI commands + * Howto: join strings and pass them down. + * Identify unique/index keywords for correct set syntax. + * Args: + * @param[in] f Where to print cli commands + * @param[in] x XML Parse-tree (to translate) + * @param[in] prepend Print this text in front of all commands. + * @param[in] gt option to steer cli syntax + */ +int +xml2cli(FILE *f, + cxobj *x, + char *prepend, + enum genmodel_type gt) +{ + return xml2cli_recurse(f, x, prepend, gt, fprintf); +} + /*! Translate a single xml node to a cligen variable vector. Note not recursive * @param[in] xt XML tree containing one top node * @param[in] ys Yang spec containing type specification of top-node of xt diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index db23a747..9bbfffcd 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -1245,13 +1245,15 @@ quotedstring(char *s) } /*! Print yang specification to file - * @param[in] f File to print to. - * @param[in] yn Yang node to print + * @param[in] f File to print to. + * @param[in] yn Yang node to print + * @param[in] fn Callback to make print function * @see yang_print_cbuf */ int -yang_print(FILE *f, - yang_stmt *yn) +yang_print_cb(FILE *f, + yang_stmt *yn, + clicon_output_cb *fn) { int retval = -1; cbuf *cb = NULL; @@ -1262,7 +1264,7 @@ yang_print(FILE *f, } if (yang_print_cbuf(cb, yn, 0) < 0) goto done; - fprintf(f, "%s", cbuf_get(cb)); + (*fn)(f, "%s", cbuf_get(cb)); if (cb) cbuf_free(cb); retval = 0; @@ -1270,6 +1272,18 @@ yang_print(FILE *f, return retval; } +/*! Print yang specification to file + * @param[in] f File to print to. + * @param[in] yn Yang node to print + * @see yang_print_cbuf + */ +int +yang_print(FILE *f, + yang_stmt *yn) +{ + return yang_print_cb(f, yn, fprintf); +} + /*! Print yang specification to cligen buf * @param[in] cb Cligen buffer. This is where the pretty print is. * @param[in] yn Yang node to print