* JSON parse and print improvements

* Integrated parsing with namespace translation and yang spec lookup
This commit is contained in:
Olof hagsand 2019-06-03 16:40:54 +02:00
parent 18ab5e7dfe
commit 2aeb925521
15 changed files with 396 additions and 258 deletions

View file

@ -316,6 +316,7 @@ xmldb_readfile(clicon_handle h,
char *dbfile = NULL;
int fd = -1;
char *format;
int ret;
if (xmldb_db2file(h, db, &dbfile) < 0)
goto done;
@ -338,7 +339,7 @@ xmldb_readfile(clicon_handle h,
goto done;
}
if (strcmp(format, "json")==0){
if ((json_parse_file(fd, yspec, &x0)) < 0)
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)

View file

@ -59,7 +59,11 @@
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_yang_type.h"
#include "clixon_options.h"
#include "clixon_xml.h"
#include "clixon_xml_sort.h"
#include "clixon_xml_map.h"
#include "clixon_netconf_lib.h"
#include "clixon_json.h"
#include "clixon_json_parse.h"
@ -265,6 +269,44 @@ json_str_escape_cdata(cbuf *cb,
return retval;
}
/*! If set, quoute the json value with double quotes
* @þaram[in] xb XML body object
@ @retval 0 Value should not be quouted, XML value is int, boolean,..
@ @retval 1 Value should be quouted, XML value is string,..
*/
static int
jsonvaluestr(cxobj *xb)
{
int retval = 1;
cxobj *xp;
yang_stmt *yp;
enum rfc_6020 keyword;
if ((xp = xml_parent(xb)) == NULL ||
(yp = xml_spec(xp)) == NULL)
goto done; /* unknown */
keyword = yang_keyword_get(yp);
if ((keyword == Y_LEAF || keyword == Y_LEAF_LIST))
switch (yang_type2cv(yp)){
case CGV_INT8:
case CGV_INT16:
case CGV_INT32:
case CGV_INT64:
case CGV_UINT8:
case CGV_UINT16:
case CGV_UINT32:
case CGV_UINT64:
case CGV_DEC64:
case CGV_BOOL:
retval = 0;
break;
default:
break;
}
done:
return retval;
}
/*! Do the actual work of translating XML to JSON
* @param[out] cb Cligen text buffer containing json on exit
* @param[in] x XML tree structure containing XML to translate
@ -309,7 +351,7 @@ xml2json1_cbuf(cbuf *cb,
int level,
int pretty,
int flat,
int bodystr)
char *modname0)
{
int retval = -1;
int i;
@ -318,27 +360,16 @@ xml2json1_cbuf(cbuf *cb,
enum array_element_type xc_arraytype;
yang_stmt *ys;
yang_stmt *ymod; /* yang module */
yang_stmt *yspec = NULL; /* yang spec */
int bodystr0=1;
char *prefix=NULL; /* prefix / local namespace name */
char *namespace=NULL; /* namespace uri */
char *modname=NULL; /* Module name */
int commas;
char *modname = NULL;
/* If x is labelled with a default namespace, it should be translated
* to a module name.
* Harder if x has a prefix, then that should also be translated to associated
* module name
*/
prefix = xml_prefix(x);
namespace = xml_find_type_value(x, prefix, "xmlns", CX_ATTR);
if ((ys = xml_spec(x)) != NULL) /* yang spec associated with x */
yspec = ys_spec(ys);
/* Find module name associated with namspace URI */
if (namespace && yspec &&
(ymod = yang_find_module_by_namespace(yspec, namespace)) != NULL){
if ((ys = xml_spec(x)) != NULL){
ymod = ys_real_module(ys);
modname = yang_argument_get(ymod);
if (modname0 && strcmp(modname, modname0) == 0)
modname=NULL;
else
modname0 = modname; /* modname0 is ancestor ns passed to child */
}
childt = child_type(x);
if (pretty==2)
@ -347,21 +378,20 @@ xml2json1_cbuf(cbuf *cb,
childtype2str(childt));
switch(arraytype){
case BODY_ARRAY:{
if (bodystr){
/* XXX String if right type */
cprintf(cb, "\"");
if (jsonvaluestr(x)) { /* Only print quotation if string-type */
cprintf(cb, "\"");
if (json_str_escape_cdata(cb, xml_value(x)) < 0)
goto done;
cprintf(cb, "\"");
}
else
else /* No quotation marks */
cprintf(cb, "%s", xml_value(x));
break;
}
case NO_ARRAY:
if (!flat){
cprintf(cb, "%*s\"", pretty?(level*JSON_INDENT):0, "");
if (modname) /* XXX should remove this? */
if (modname)
cprintf(cb, "%s:", modname);
cprintf(cb, "%s\": ", xml_name(x));
}
@ -426,26 +456,6 @@ xml2json1_cbuf(cbuf *cb,
* arraytype=* but child-type is BODY_CHILD
* This is code for writing <a>42</a> as "a":42 and not "a":"42"
*/
if (childt == BODY_CHILD && ys!=NULL &&
(yang_keyword_get(ys) == Y_LEAF || yang_keyword_get(ys) == Y_LEAF_LIST))
switch (cv_type_get(yang_cv_get(ys))){
case CGV_INT8:
case CGV_INT16:
case CGV_INT32:
case CGV_INT64:
case CGV_UINT8:
case CGV_UINT16:
case CGV_UINT32:
case CGV_UINT64:
case CGV_DEC64:
case CGV_BOOL:
bodystr0 = 0;
break;
default:
bodystr0 = 1;
break;
}
commas = xml_child_nr_notype(x, CX_ATTR) - 1;
for (i=0; i<xml_child_nr(x); i++){
xc = xml_child_i(x, i);
@ -457,7 +467,7 @@ xml2json1_cbuf(cbuf *cb,
if (xml2json1_cbuf(cb,
xc,
xc_arraytype,
level+1, pretty, 0, bodystr0) < 0)
level+1, pretty, 0, modname0) < 0)
goto done;
if (commas > 0) {
cprintf(cb, ",%s", pretty?"\n":"");
@ -527,6 +537,9 @@ xml2json1_cbuf(cbuf *cb,
}
/*! Translate an XML tree to JSON in a CLIgen buffer
*
* XML-style namespace notation in tree, but RFC7951 in output assume yang
* populated
*
* @param[in,out] cb Cligen buffer to write to
* @param[in] x XML tree to translate from
@ -551,28 +564,18 @@ xml2json_cbuf(cbuf *cb,
{
int retval = 1;
int level = 0;
char *prefix;
char *namespace;
cprintf(cb, "%*s{%s",
pretty?level*JSON_INDENT:0,"",
pretty?"\n":"");
/* If x is labelled with a default namespace, it should be translated
* to a module name.
* Harder if x has a prefix, then that should also be translated to associated
* module name
*/
prefix = xml_prefix(x);
if (xml2ns(x, prefix, &namespace) < 0)
goto done;
/* Some complexities in grafting namespace in existing trees to new */
if (xml_find_type_value(x, prefix, "xmlns", CX_ATTR) == NULL && namespace)
if (xmlns_set(x, prefix, namespace) < 0)
goto done;
if (xml2json1_cbuf(cb,
x,
NO_ARRAY,
level+1, pretty,0,1) < 0)
level+1,
pretty,
0,
NULL /* ancestor modname / namespace */
) < 0)
goto done;
cprintf(cb, "%s%*s}%s",
pretty?"\n":"",
@ -605,24 +608,16 @@ xml2json_cbuf_vec(cbuf *cb,
{
int retval = -1;
int level = 0;
int i;
cxobj *xp = NULL;
int i;
cxobj *xc;
char *prefix;
char *namespace;
if ((xp = xml_new("xml2json", NULL, NULL)) == NULL)
goto done;
/* Some complexities in grafting namespace in existing trees to new */
for (i=0; i<veclen; i++){
prefix = xml_prefix(vec[i]);
if (xml2ns(vec[i], prefix, &namespace) < 0)
goto done;
xc = xml_dup(vec[i]);
xml_addsub(xp, xc);
if (xml_find_type_value(xc, prefix, "xmlns", CX_ATTR) == NULL && namespace)
if (xmlns_set(xc, prefix, namespace) < 0)
goto done;
}
if (0){
cprintf(cb, "[%s", pretty?"\n":" ");
@ -631,7 +626,8 @@ xml2json_cbuf_vec(cbuf *cb,
if (xml2json1_cbuf(cb,
xp,
NO_ARRAY,
level+1, pretty,1, 1) < 0)
level+1, pretty,
1, NULL) < 0)
goto done;
if (0){
@ -683,6 +679,18 @@ xml2json(FILE *f,
return retval;
}
/*! Print an XML tree structure to an output stream as JSON
*
* @param[in] f UNIX output stream
* @param[in] xn clicon xml tree
*/
int
json_print(FILE *f,
cxobj *xn)
{
return xml2json(f, xn, 1);
}
/*! Translate a vector of xml objects to JSON File.
* This is done by adding a top pseudo-object, and add the vector as subs,
* and then not pritning the top pseudo-.object using the 'flat' option.
@ -719,80 +727,99 @@ xml2json_vec(FILE *f,
return retval;
}
/*! Translate from JSON module:name to XML name xmlns="uri" recursively
/*! Translate from JSON module:name to XML default ns: xmlns="uri" recursively
* Assume an xml tree where prefix:name have been split into "module":"name"
* In other words, from JSON RFC7951 to XML namespace trees
*
* @param[in] yspec Yang spec
* @param[in,out] x XML tree. Translate it in-line
* @param[out] xerr If namespace not set, create xml error tree
* @retval 0 OK (if xerr set see above)
* @param[out] xerr Reason for invalid tree returned as netconf err msg or NULL
* @retval 1 OK
* @retval 0 Invalid, wrt namespace. xerr set
* @retval -1 Error
* @note the opposite - xml2ns is made inline in xml2json1_cbuf
* Example: <top><module:input> --> <top><input xmlns="">
*/
int
json2xml_ns(yang_stmt *yspec,
cxobj *x,
cxobj **xerr)
json_xmlns_translate(yang_stmt *yspec,
cxobj *x,
cxobj **xerr)
{
int retval = -1;
yang_stmt *ymod;
char *namespace0;
char *namespace;
char *name = NULL;
char *prefix = NULL;
cxobj *xc;
int ret;
if (nodeid_split(xml_name(x), &prefix, &name) < 0)
goto done;
prefix = xml_prefix(x); /* prefix is here module name */
if (prefix != NULL){
if ((ymod = yang_find_module_by_name(yspec, prefix)) == NULL){
if (netconf_unknown_namespace_xml(xerr, "application",
if (xerr &&
netconf_unknown_namespace_xml(xerr, "application",
prefix,
"No yang module found corresponding to prefix") < 0)
goto done;
goto ok;
goto fail;
}
namespace = yang_find_mynamespace(ymod);
/* Get existing default namespace in tree */
if (xml2ns(x, NULL, &namespace0) < 0)
goto done;
/* Set xmlns="" default namespace attribute (if diff from default) */
if (namespace0==NULL || strcmp(namespace0, namespace))
if (namespace0==NULL || strcmp(namespace0, namespace)){
if (xmlns_set(x, NULL, namespace) < 0)
goto done;
/* Remove prefix from name */
if (xml_name_set(x, name) < 0)
goto done;
/* and remove prefix */
xml_prefix_set(x, NULL);
}
}
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL){
if (json2xml_ns(yspec, xc, xerr) < 0)
if ((ret = json_xmlns_translate(yspec, xc, xerr)) < 0)
goto done;
if (*xerr != NULL)
break;
if (ret == 0)
goto fail;
}
ok:
retval = 0;
retval = 1;
done:
if (prefix)
free(prefix);
if (name)
free(name);
return retval;
fail:
retval = 0;
goto done;
}
/*! Parse a string containing JSON and return an XML tree
*
* Parsing using yacc according to JSON syntax. Names with <prefix>:<id>
* are split and interpreted as in RFC7951
*
* @param[in] str Input string containing JSON
* @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
* @retval 1 OK and valid
* @retval 0 Invalid (only if yang spec)
* @retval -1 Error with clicon_err called
* @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
* @see RFC 7951
*/
static int
json_parse(char *str,
const char *name,
cxobj *xt)
yang_stmt *yspec,
const char *name,
cxobj *xt,
cxobj **xerr)
{
int retval = -1;
struct clicon_json_yacc_arg jy = {0,};
int ret;
// clicon_debug(1, "%s", __FUNCTION__);
clicon_debug(1, "%s", __FUNCTION__);
jy.jy_parse_string = str;
jy.jy_name = name;
jy.jy_linenum = 1;
@ -807,20 +834,35 @@ json_parse(char *str,
clicon_err(OE_XML, 0, "JSON parser error with no error code (should not happen)");
goto done;
}
retval = 0;
if (yspec){
/* Names are split into name/prefix, but now add namespace info */
if ((ret = json_xmlns_translate(yspec, xt, xerr)) < 0)
goto done;
if (ret == 0)
goto fail;
/* Populate, ie associate xml nodes with yang specs */
if (xml_apply0(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
goto done;
}
retval = 1;
done:
// clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
json_parse_exit(&jy);
json_scan_exit(&jy);
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;
fail: /* invalid */
retval = 0;
goto done;
}
/*! Parse string containing JSON and return an XML tree
*
* @param[in] str String containing JSON
* @param[out] xt On success a top of XML parse tree is created with name 'top'
* @retval 0 OK
* @retval -1 Error with clicon_err called. Includes parse errors
* @param[in] str String containing JSON
* @param[in] yspec Yang specification, or NULL
* @param[in,out] xt On success a top of XML parse tree is created with name 'top'
* @param[out] xerr Reason for invalid returned as netconf err msg
*
* @code
* cxobj *cx = NULL;
@ -829,39 +871,64 @@ json_parse(char *str,
* xml_free(cx);
* @endcode
* @note you need to free the xml parse tree after use, using xml_free()
* @see json_parse_file
* @retval 1 OK and valid
* @retval 0 Invalid (only if yang spec) w xerr set
* @retval -1 Error with clicon_err called
* @see json_parse_file with a file descriptor (and more description)
*/
int
json_parse_str(char *str,
cxobj **xt)
json_parse_str(char *str,
yang_stmt *yspec,
cxobj **xt,
cxobj **xerr)
{
clicon_debug(1, "%s", __FUNCTION__);
if (*xt == NULL)
if ((*xt = xml_new("top", NULL, NULL)) == NULL)
return -1;
return json_parse(str, "", *xt);
return json_parse(str, yspec, "", *xt, xerr);
}
/*! Read a JSON definition from file and parse it into a parse-tree.
*
* @param[in] fd A file descriptor containing the JSON file (as ASCII characters)
* @param[in] yspec Yang specification, or NULL XXX Not yet used
* @param[in,out] xt Pointer to (XML) parse tree. If empty, create.
* @retval 0 OK
* @retval -1 Error with clicon_err called
* File will be parsed as follows:
* (1) parsed according to JSON; # Only this check if yspec is NULL
* (2) sanity checked wrt yang
* (3) namespaces check (using <ns>:<name> notation
* (4) an xml parse tree will be returned
* Note, only (1) and (4) will be done if yspec is NULL.
* Part of (3) is to split json names if they contain colon,
* eg: name="a:b" -> prefix="a", name="b"
* But this is not done if yspec=NULL, and is not part of the JSON spec
*
* @param[in] fd File descriptor to the JSON file (ASCII string)
* @param[in] yspec Yang specification, or NULL
* @param[in,out] xt Pointer to (XML) parse tree. If empty, create.
* @param[out] xerr Reason for invalid returned as netconf err msg
*
* @code
* cxobj *xt = NULL;
* if (json_parse_file(0, NULL, &xt) < 0)
* if (json_parse_file(0, yspec, &xt) < 0)
* err;
* xml_free(xt);
* @endcode
* @note you need to free the xml parse tree after use, using xml_free()
* @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
*
* @retval 1 OK and valid
* @retval 0 Invalid (only if yang spec) w xerr set
* @retval -1 Error with clicon_err called
*
* @see json_parse_str
* @see RFC7951
*/
int
int
json_parse_file(int fd,
yang_stmt *yspec,
cxobj **xt)
cxobj **xt,
cxobj **xerr)
{
int retval = -1;
int ret;
@ -889,8 +956,12 @@ json_parse_file(int fd,
if (*xt == NULL)
if ((*xt = xml_new(JSON_TOP_SYMBOL, NULL, NULL)) == NULL)
goto done;
if (len && json_parse(ptr, "", *xt) < 0)
goto done;
if (len){
if ((ret = json_parse(ptr, yspec, "", *xt, xerr)) < 0)
goto done;
if (ret == 0)
goto fail;
}
break;
}
if (len>=jsonbuflen-1){ /* Space: one for the null character */
@ -904,7 +975,7 @@ json_parse_file(int fd,
ptr = jsonbuf;
}
}
retval = 0;
retval = 1;
done:
if (retval < 0 && *xt){
free(*xt);
@ -913,6 +984,9 @@ json_parse_file(int fd,
if (jsonbuf)
free(jsonbuf);
return retval;
fail:
retval = 0;
goto done;
}

View file

@ -33,6 +33,7 @@
* JSON Parser
* From http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
* And RFC7951 JSON Encoding of Data Modeled with YANG
Structural tokens:
[ left square bracket
@ -72,7 +73,6 @@ object.
*/
%start json
%union {
@ -125,6 +125,7 @@ object.
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
@ -157,26 +158,39 @@ json_parse_init(struct clicon_json_yacc_arg *jy)
return 0;
}
int
json_parse_exit(struct clicon_json_yacc_arg *jy)
{
return 0;
}
/*! Create xml object from json object name (eg "string")
* Split name into prefix:name (extended JSON RFC7951)
*/
static int
json_current_new(struct clicon_json_yacc_arg *jy,
char *name)
{
int retval = -1;
cxobj *xn;
int retval = -1;
cxobj *x;
char *prefix = NULL;
char *id = NULL;
clicon_debug(2, "%s", __FUNCTION__);
if ((xn = xml_new(name, jy->jy_current, NULL)) == NULL)
/* Find colon separator and if found split into prefix:name */
if (nodeid_split(name, &prefix, &id) < 0)
goto done;
if ((x = xml_new(id, jy->jy_current, NULL)) == NULL)
goto done;
jy->jy_current = xn;
if (prefix && xml_prefix_set(x, prefix) < 0)
goto done;
jy->jy_current = x;
retval = 0;
done:
if (prefix)
free(prefix);
if (id)
free(id);
return retval;
}

View file

@ -238,7 +238,8 @@ xml_prefix_set(cxobj *xn,
* @retval 0 OK
* @retval -1 Error
* @see xmlns_check XXX can these be merged?
* @note, this function uses a cache. Any case where cache should be cleared?
* @see xml2ns_set cache is set
* @note, this function uses a cache.
*/
int
xml2ns(cxobj *x,
@ -286,12 +287,12 @@ xml2ns(cxobj *x,
* @param[out] namespace URI namespace (or NULL). Will be copied
* @retval 0 OK
* @retval -1 Error
* @see xml2ns
* @see xml2ns
*/
int
xmlns_set(cxobj *x,
char *prefix,
char *namespace)
char *ns)
{
int retval = -1;
cxobj *xa;
@ -307,8 +308,15 @@ xmlns_set(cxobj *x,
goto done;
xml_type_set(xa, CX_ATTR);
}
if (xml_value_set(xa, namespace) < 0)
if (xml_value_set(xa, ns) < 0)
goto done;
/* (re)set namespace cache (as used in xml2ns) */
if (x->x_ns_cache)
free(x->x_ns_cache);
if ((x->x_ns_cache = strdup(ns)) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
retval = 0;
done:
return retval;
@ -868,6 +876,9 @@ xml_addsub(cxobj *xp,
{
cxobj *oldp;
int i;
char *pns = NULL; /* parent namespace */
char *cns = NULL; /* child namespace */
cxobj *xa;
if ((oldp = xml_parent(xc)) != NULL){
/* Find child order i in old parent*/
@ -884,6 +895,18 @@ xml_addsub(cxobj *xp,
return -1;
/* Set new parent in child */
xml_parent_set(xc, xp);
/* Ensure default namespace is not duplicated
* here only remove duplicate default namespace, there may be more */
/* 1. Get parent default namespace */
xml2ns(xp, NULL, &pns);
/* 2. Get child default namespace */
if (pns &&
(xa = xml_find_type(xc, NULL, "xmlns", CX_ATTR)) != NULL &&
(cns = xml_value(xa)) != NULL){
/* 3. check if same, if so remove child's */
if (strcmp(pns, cns) == 0)
xml_purge(xa);
}
}
return 0;
}
@ -1704,8 +1727,10 @@ _xml_parse(const char *str,
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)
goto done;
}

View file

@ -3022,70 +3022,5 @@ done:
}
/*
* Turn this on for uni-test programs
* Usage: clixon_string join
* Example compile:
gcc -g -o clixon_xml_map -I. -I../clixon ./clixon_xml_map.c -lclixon -lcligen
* Example run:
/interfaces/interface=%s/name --> interfaces/interface/name
/interfaces/interface=%s/ipv4/address=%s e --> /interfaces/interface=e/ipv4/address
/interfaces/interface=%s,%s/ipv4/address=%s e f --> /interfaces/interface=e,f/ipv4/address
/interfaces/interface=%s/ipv4/address=%s,%s e f --> /interfaces/interface=e/ipv4/address=f
/interfaces/interface=%s/ipv4/address=%s/prefix-length eth 1.2.3.4 -->
/interfaces/interface=eth/ipv4/address=1.2.3.4/prefix-length
*/
#if 0 /* Test program */
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s <api_path_fmt> <cv0>, <cv1>,...\n", argv0);
exit(0);
}
int
main(int argc, char **argv)
{
int nvec;
char **vec;
char *str0;
char *str1;
int i;
char *api_path_fmt;
cg_var *cv;
cvec *cvv;
char *api_path=NULL;
clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR);
if (argc < 2){
usage(argv[0]);
return 0;
}
api_path_fmt = argv[1];
if ((cvv = cvec_new(0)) == NULL){
perror("cvec_new");
return -1;
}
cv = cv_new(CGV_STRING);
cv_string_set(cv, "CLI base command");
cvec_append_var(cvv, cv);
for (i=2; i<argc; i++){
cv = cv_new(CGV_STRING);
if (cv_parse(argv[i], cv) < 0){
perror("cv_parse");
return -1;
}
cvec_append_var(cvv, cv);
}
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
return -1;
printf("%s\n", api_path);
return 0;
}
#endif /* Test program */

View file

@ -166,7 +166,6 @@ xml_parse_version(struct xml_parse_yacc_arg *ya,
/*! Parse Qualified name --> Unprefixed name
* @param[in] ya XML parser yacc handler struct
* @param[in] prefix Prefix, namespace, or NULL
* @param[in] localpart Name
* @note the call to xml_child_spec() may not have xmlns attribute read yet XXX
*/

View file

@ -2292,7 +2292,7 @@ yang_parse_find_match(clicon_handle h,
* (cloned from cligen)
* @param[in] h CLICON handle
* @param[in] filename Name of file
* @param[in] ysp Yang specification. Should ave been created by caller using yspec_new
* @param[in] ysp Yang specification. Should have been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
* @retval NULL Error encountered
@ -2305,9 +2305,9 @@ yang_parse_find_match(clicon_handle h,
* yang_parse_str # Set up yacc parser and call it given a string
* clixon_yang_parseparse # Actual yang parsing using yacc
*/
static yang_stmt *
yang_parse_filename(const char *filename,
yang_stmt *ysp)
yang_stmt *
yang_parse_filename(const char *filename,
yang_stmt *ysp)
{
yang_stmt *ymod = NULL;
int fd = -1;

View file

@ -48,6 +48,7 @@
* | \ / yang_type_cache_regex_set
* ys_populate_leaf, +--> compile_pattern2regexp (compile regexps)
* xml_cv_cache (NULL) +--> cv_validate1 --> cv_validate_pattern (exec regexps)
* yang_type2cv (simplified)
*
* NOTE
* 1) ys_cv_validate/ys_cv_validate_union_one and
@ -1387,3 +1388,24 @@ yang_type_get(yang_stmt *ys,
return retval;
}
/*! Utility function to translate a leaf/leaf-list to its base CV-type only
* @see yang_type_get Full leaf/list type api
*/
enum cv_type
yang_type2cv(yang_stmt *ys)
{
yang_stmt *yrestype; /* resolved type */
char *restype; /* resolved type */
char *type; /* original type */
enum cv_type cvtype = CGV_ERR;
/* Find type specification */
if (yang_type_get(ys, &type, &yrestype, NULL, NULL, NULL, NULL, NULL)
< 0)
goto done;
restype = yrestype?yrestype->ys_argument:NULL;
if (clicon_type2cv(type, restype, ys, &cvtype) < 0) /* This handles non-resolved also */
goto done;
done:
return cvtype;
}