Dedicated xml,json,yang and xsl parser utility programs added

Sanity check of stdarg (...) added
Cleanup of error messages.
This commit is contained in:
Olof hagsand 2018-06-17 19:40:06 +02:00
parent 1306174071
commit 85c4782e36
56 changed files with 1004 additions and 379 deletions

View file

@ -45,11 +45,14 @@
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include <unistd.h>
#define __USE_GNU /* strverscmp */
#include <string.h>
#include <arpa/inet.h>
#include <regex.h>
#include <dirent.h>
#include <sys/types.h>
#include <fcntl.h>
#include <syslog.h>
#include <assert.h>
#include <sys/stat.h>
@ -74,6 +77,8 @@
#include "clixon_yang_type.h"
#include "clixon_yang_parse.h"
/* Size of json read buffer when reading from file*/
#define BUFLEN 1024
/* Mapping between yang keyword string <--> clicon constants */
static const map_str2int ykmap[] = {
@ -158,7 +163,7 @@ yspec_new(void)
yang_spec *yspec;
if ((yspec = malloc(sizeof(*yspec))) == NULL){
clicon_err(OE_YANG, errno, "%s: malloc", __FUNCTION__);
clicon_err(OE_YANG, errno, "malloc");
return NULL;
}
memset(yspec, 0, sizeof(*yspec));
@ -176,7 +181,7 @@ ys_new(enum rfc_6020 keyw)
yang_stmt *ys;
if ((ys = malloc(sizeof(*ys))) == NULL){
clicon_err(OE_YANG, errno, "%s: malloc", __FUNCTION__);
clicon_err(OE_YANG, errno, "malloc");
return NULL;
}
memset(ys, 0, sizeof(*ys));
@ -184,7 +189,7 @@ ys_new(enum rfc_6020 keyw)
/* The cvec contains stmt-specific variables. Only few stmts need variables so the
cvec could be lazily created to save some heap and cycles. */
if ((ys->ys_cvec = cvec_new(0)) == NULL){
clicon_err(OE_YANG, errno, "%s: cvec_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cvec_new");
return NULL;
}
return ys;
@ -247,7 +252,7 @@ yn_realloc(yang_node *yn)
yn->yn_len++;
if ((yn->yn_stmt = realloc(yn->yn_stmt, (yn->yn_len)*sizeof(yang_stmt *))) == 0){
clicon_err(OE_YANG, errno, "%s: realloc", __FUNCTION__);
clicon_err(OE_YANG, errno, "realloc");
return -1;
}
yn->yn_stmt[yn->yn_len - 1] = NULL; /* init field */
@ -276,22 +281,22 @@ ys_cp(yang_stmt *ynew,
ynew->ys_parent = NULL;
if (yold->ys_stmt)
if ((ynew->ys_stmt = calloc(yold->ys_len, sizeof(yang_stmt *))) == NULL){
clicon_err(OE_YANG, errno, "%s: calloc", __FUNCTION__);
clicon_err(OE_YANG, errno, "calloc");
goto done;
}
if (yold->ys_argument)
if ((ynew->ys_argument = strdup(yold->ys_argument)) == NULL){
clicon_err(OE_YANG, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
if (yold->ys_cv)
if ((ynew->ys_cv = cv_dup(yold->ys_cv)) == NULL){
clicon_err(OE_YANG, errno, "%s: cv_dup", __FUNCTION__);
clicon_err(OE_YANG, errno, "cv_dup");
goto done;
}
if (yold->ys_cvec)
if ((ynew->ys_cvec = cvec_dup(yold->ys_cvec)) == NULL){
clicon_err(OE_YANG, errno, "%s: cvec_dup", __FUNCTION__);
clicon_err(OE_YANG, errno, "cvec_dup");
goto done;
}
if (yold->ys_typecache){
@ -902,7 +907,7 @@ yang_print(FILE *f,
cbuf *cb = NULL;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_YANG, errno, "%s: cbuf_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cbuf_new");
goto done;
}
if (yang_print_cbuf(cb, yn, 0) < 0)
@ -998,14 +1003,14 @@ ys_populate_leaf(yang_stmt *ys,
goto done;
/* 2. Create the CV using cvtype and name it */
if ((cv = cv_new(cvtype)) == NULL){
clicon_err(OE_YANG, errno, "%s: cv_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cv_new");
goto done;
}
if (options & YANG_OPTIONS_FRACTION_DIGITS && cvtype == CGV_DEC64) /* XXX: Seems misplaced? / too specific */
cv_dec64_n_set(cv, fraction_digits);
if (cv_name_set(cv, ys->ys_argument) == NULL){
clicon_err(OE_YANG, errno, "%s: cv_new_set", __FUNCTION__);
clicon_err(OE_YANG, errno, "cv_new_set");
goto done;
}
/* 3. Check if default value. Here we parse the string in the default-stmt
@ -1083,7 +1088,7 @@ ys_populate_range(yang_stmt *ys,
yparent = ys->ys_parent; /* Find parent: type */
if (yparent->yn_keyword != Y_TYPE){
clicon_err(OE_YANG, 0, "%s: parent should be type", __FUNCTION__);
clicon_err(OE_YANG, 0, "parent should be type");
goto done;
}
if (yang_type_resolve(ys, (yang_stmt*)yparent, &yrestype,
@ -1103,7 +1108,8 @@ ys_populate_range(yang_stmt *ys,
}
if ((maxstr = strstr(minstr, "..")) != NULL){
if (strlen(maxstr) < 2){
clicon_err(OE_YANG, 0, "range statement: %s not on the form: <int>..<int>");
clicon_err(OE_YANG, 0, "range statement: %s not on the form: <int>..<int>",
ys->ys_argument);
goto done;
}
minstr[maxstr-minstr] = '\0';
@ -1499,7 +1505,7 @@ yang_expand(yang_node *yn)
size = (yn->yn_len - i - 1)*sizeof(struct yang_stmt *);
yn->yn_len += glen - 1;
if (glen && (yn->yn_stmt = realloc(yn->yn_stmt, (yn->yn_len)*sizeof(yang_stmt *))) == 0){
clicon_err(OE_YANG, errno, "%s: realloc", __FUNCTION__);
clicon_err(OE_YANG, errno, "realloc");
return -1;
}
/* Then move all existing elements up from i+1 (not uses-stmt) */
@ -1540,10 +1546,10 @@ yang_expand(yang_node *yn)
* Syntax parsing. A string is input and a syntax-tree is returned (or error).
* A variable record is also returned containing a list of (global) variable values.
* (cloned from cligen)
* @param h CLICON handle
* @param str String of yang statements
* @param name Log string, typically filename
* @param ysp Yang specification. Should ave been created by caller using yspec_new
* @param[in] h CLICON handle
* @param[in] str String of yang statements
* @param[in] name Log string, typically filename
* @param[in] ysp Yang specification. Should ave been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
* @retval NULL Error encountered
* Calling order:
@ -1554,15 +1560,17 @@ yang_expand(yang_node *yn)
* clixon_yang_parseparse # Actual yang parsing using yacc
*/
static yang_stmt *
yang_parse_str(clicon_handle h,
char *str,
yang_parse_str(char *str,
const char *name, /* just for errs */
yang_spec *yspec)
{
struct clicon_yang_yacc_arg yy = {0,};
yang_stmt *ymod = NULL;
yy.yy_handle = h;
if (yspec == NULL){
clicon_err(OE_YANG, 0, "Yang parse need top level yang spec");
goto done;
}
yy.yy_name = (char*)name;
yy.yy_linenum = 1;
yy.yy_parse_string = str;
@ -1595,15 +1603,66 @@ yang_parse_str(clicon_handle h,
return ymod; /* top-level (sub)module */
}
/*! Read an opened file into a string and call yang string parsing
/*! Parse yang spec from an open file descriptor
* @param[in] fd File descriptor containing the YANG file as ASCII characters
* @param[in] name For debug, eg filename
* @param[in] ysp Yang specification. Should ave been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
* @retval NULL Error
*/
yang_stmt *
yang_parse_file(int fd,
const char *name,
yang_spec *ysp)
{
char *buf = NULL;
int i;
int c;
int len;
yang_stmt *ymod = NULL;
int ret;
len = BUFLEN; /* any number is fine */
if ((buf = malloc(len)) == NULL){
perror("pt_file malloc");
return NULL;
}
memset(buf, 0, len);
i = 0; /* position in buf */
while (1){ /* read the whole file */
if ((ret = read(fd, &c, 1)) < 0){
clicon_err(OE_XML, errno, "read");
break;
}
if (ret == 0)
break; /* eof */
if (len==i){
if ((buf = realloc(buf, 2*len)) == NULL){
clicon_err(OE_XML, errno, "realloc");
goto done;
}
memset(buf+len, 0, len);
len *= 2;
}
buf[i++] = (char)(c&0xff);
} /* read a line */
if ((ymod = yang_parse_str(buf, name, ysp)) < 0)
goto done;
done:
if (buf)
free(buf);
return ymod; /* top-level (sub)module */
}
/*! Open a file, read into a string and invoke yang parsing
*
* Similar to clicon_yang_str(), just read a file first
* (cloned from cligen)
* @param h CLICON handle
* @param filename Name of file
* @param ysp Yang specification. Should ave been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
* @retval NULL Error encountered
* @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
* @retval ymod Top-level yang (sub)module
* @retval NULL Error encountered
* The database symbols are inserted in alphabetical order.
* Calling order:
@ -1614,57 +1673,27 @@ yang_parse_str(clicon_handle h,
* clixon_yang_parseparse # Actual yang parsing using yacc
*/
static yang_stmt *
yang_parse_file(clicon_handle h,
const char *filename,
yang_spec *ysp
)
yang_parse_filename(const char *filename,
yang_spec *ysp)
{
char *buf = NULL;
int i;
int c;
int len;
yang_stmt *ymod = NULL;
FILE *f = NULL;
struct stat st;
int fd = -1;
struct stat st;
clicon_log(LOG_DEBUG, "Parsing yang file: %s", filename);
if (stat(filename, &st) < 0){
clicon_err(OE_YANG, errno, "%s not found", filename);
goto done;
}
if ((f = fopen(filename, "r")) == NULL){
clicon_err(OE_UNIX, errno, "fopen(%s)", filename);
if ((fd = open(filename, O_RDONLY)) < 0){
clicon_err(OE_YANG, errno, "open(%s)", filename);
goto done;
}
len = 1024; /* any number is fine */
if ((buf = malloc(len)) == NULL){
perror("pt_file malloc");
return NULL;
}
memset(buf, 0, len);
i = 0; /* position in buf */
while (1){ /* read the whole file */
if ((c = fgetc(f)) == EOF)
break;
if (len==i){
if ((buf = realloc(buf, 2*len)) == NULL){
fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno));
goto done;
}
memset(buf+len, 0, len);
len *= 2;
}
buf[i++] = (char)(c&0xff);
} /* read a line */
if ((ymod = yang_parse_str(h, buf, filename, ysp)) < 0)
if ((ymod = yang_parse_file(fd, filename, ysp)) < 0)
goto done;
done:
if (f)
fclose(f);
if (buf)
free(buf);
if (fd != -1)
close(fd);
return ymod; /* top-level (sub)module */
}
@ -1679,8 +1708,7 @@ yang_parse_file(clicon_handle h,
* @retval -1 Error
*/
static int
yang_parse_find_match(clicon_handle h,
const char *yang_dir,
yang_parse_find_match(const char *yang_dir,
const char *module,
cbuf *fbuf)
{
@ -1724,7 +1752,7 @@ yang_parse_find_match(clicon_handle h,
* @param[in] h CLICON handle
* @param[in] yang_dir Directory where all YANG module files reside
* @param[in] module Name of main YANG module. Or absolute file name.
* @param[in] revision Optional module revision date
* @param[in] revision Module revision date or NULL
* @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
@ -1737,8 +1765,7 @@ yang_parse_find_match(clicon_handle h,
* clixon_yang_parseparse # Actual yang parsing using yacc
*/
static yang_stmt *
yang_parse_recurse(clicon_handle h,
const char *yang_dir,
yang_parse_recurse(const char *yang_dir,
const char *module,
const char *revision,
yang_spec *ysp)
@ -1752,26 +1779,26 @@ yang_parse_recurse(clicon_handle h,
int nr;
if (module[0] == '/'){
if ((ymod = yang_parse_file(h, module, ysp)) == NULL)
if ((ymod = yang_parse_filename(module, ysp)) == NULL)
goto done;
}
else {
if ((fbuf = cbuf_new()) == NULL){
clicon_err(OE_YANG, errno, "%s: cbuf_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cbuf_new");
goto done;
}
if (revision)
cprintf(fbuf, "%s/%s@%s.yang", yang_dir, module, revision);
else{
/* No specific revision, Match a yang file */
if ((nr = yang_parse_find_match(h, yang_dir, module, fbuf)) < 0)
if ((nr = yang_parse_find_match(yang_dir, module, fbuf)) < 0)
goto done;
if (nr == 0){
clicon_err(OE_YANG, errno, "No matching %s yang files found (expected module name or absolute filename)", module);
goto done;
}
}
if ((ymod = yang_parse_file(h, cbuf_get(fbuf), ysp)) == NULL)
if ((ymod = yang_parse_filename(cbuf_get(fbuf), ysp)) == NULL)
goto done;
}
@ -1786,7 +1813,7 @@ yang_parse_recurse(clicon_handle h,
subrevision = NULL;
if (yang_find((yang_node*)ysp, Y_MODULE, modname) == NULL)
/* recursive call */
if (yang_parse_recurse(h, yang_dir, modname, subrevision, ysp) == NULL){
if (yang_parse_recurse(yang_dir, modname, subrevision, ysp) == NULL){
ymod = NULL;
goto done;
}
@ -1845,7 +1872,7 @@ ys_schemanode_check(yang_stmt *ys,
* @param[in] h CLICON handle
* @param[in] yang_dir Directory where all YANG module files reside (except mainfile)
* @param[in] mainmod Name of main YANG module. Or absolute file name.
* @param[in] revision Optional main module revision date.
* @param[in] revision Main module revision date string or NULL
* @param[out] ysp Yang specification. Should ave been created by caller using yspec_new
* @retval 0 Everything OK
* @retval -1 Error encountered
@ -1854,7 +1881,8 @@ ys_schemanode_check(yang_stmt *ys,
* @note if mainmod is filename, revision is not considered.
* Calling order:
* yang_parse # Parse top-level yang module. Expand and populate yang tree
* yang_parse_recurse # Parse one yang module, go through its (sub)modules, parse them and then recursively parse them
* yang_parse_recurse # Parse one yang module, go through its (sub)modules,
* parse them and then recursively parse them
* yang_parse_file # Read yang file into a string
* yang_parse_str # Set up yacc parser and call it given a string
* clixon_yang_parseparse # Actual yang parsing using yacc
@ -1870,7 +1898,7 @@ yang_parse(clicon_handle h,
yang_stmt *ymod; /* Top-level yang (sub)module */
/* Step 1: parse from text to yang parse-tree. */
if ((ymod = yang_parse_recurse(h, yang_dir, mainmodule, revision, ysp)) == NULL)
if ((ymod = yang_parse_recurse(yang_dir, mainmodule, revision, ysp)) == NULL)
goto done;
/* Add top module name as dbspec-name */
clicon_dbspec_name_set(h, ymod->ys_argument);
@ -2080,13 +2108,13 @@ yang_abs_schema_nodeid(yang_spec *yspec,
goto done;
}
if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL){
clicon_err(OE_YANG, errno, "%s: strsep", __FUNCTION__);
clicon_err(OE_YANG, errno, "strsep");
goto done;
}
/* Assume schema nodeid looks like: "/prefix:id[/prefix:id]*" */
if (nvec < 2){
clicon_err(OE_YANG, 0, "%s: NULL or truncated path: %s",
__FUNCTION__, schema_nodeid);
clicon_err(OE_YANG, 0, "NULL or truncated path: %s",
schema_nodeid);
goto done;
}
/* split <prefix>:<id> */
@ -2095,7 +2123,7 @@ yang_abs_schema_nodeid(yang_spec *yspec,
goto ok;
}
if ((prefix = strdup(vec[1])) == NULL){
clicon_err(OE_UNIX, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
prefix[id-vec[1]] = '\0';
@ -2183,7 +2211,7 @@ ys_parse(yang_stmt *ys,
assert(ys->ys_cv == NULL); /* Cv:s are parsed in different places, difficult to separate */
if ((ys->ys_cv = cv_new(cvtype)) == NULL){
clicon_err(OE_YANG, errno, "%s: cv_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cv_new");
goto done;
}
if ((cvret = cv_parse1(ys->ys_argument, ys->ys_cv, &reason)) < 0){ /* error */
@ -2252,7 +2280,7 @@ ys_parse_sub(yang_stmt *ys,
goto done;
}
if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){
clicon_err(OE_YANG, errno, "%s: cv_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cv_new");
goto done;
}
if ((cvret = cv_parse1(extra, ys->ys_cv, &reason)) < 0){ /* error */
@ -2334,9 +2362,9 @@ yang_spec_netconf(clicon_handle h)
}
/*! Read, parse and save application yang specification as option
* @param h clicon handle
* @param f file to print to (if printspec enabled)
* @param printspec print database (YANG) specification as read from file
* @param[in] h clicon handle
* @param[in] f file to print to (if printspec enabled)
* @param[in] printspec print database (YANG) specification as read from file
*/
yang_spec*
yang_spec_main(clicon_handle h)
@ -2346,7 +2374,7 @@ yang_spec_main(clicon_handle h)
char *yang_module;
char *yang_revision;
if ((yang_dir = clicon_yang_dir(h)) == NULL){
if ((yang_dir = clicon_yang_dir(h)) == NULL){
clicon_err(OE_FATAL, 0, "CLICON_YANG_DIR option not set");
goto done;
}