This commit is contained in:
Olof hagsand 2019-04-24 15:35:57 +02:00
commit 16de5f47ba
65 changed files with 2534 additions and 653 deletions

View file

@ -188,14 +188,15 @@ xmldb_copy(clicon_handle h,
int retval = -1;
char *fromfile = NULL;
char *tofile = NULL;
db_elmnt *de1 = NULL;
db_elmnt *de2 = NULL;
db_elmnt *de1 = NULL; /* from */
db_elmnt *de2 = NULL; /* to */
db_elmnt de0 = {0,};
cxobj *x1 = NULL;
cxobj *x2 = NULL;
cxobj *x1 = NULL; /* from */
cxobj *x2 = NULL; /* to */
/* XXX lock */
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE")){
/* Copy in-memory cache */
/* 1. "to" xml tree in x1 */
if ((de1 = clicon_db_elmnt_get(h, from)) != NULL)
x1 = de1->de_xml;
@ -208,7 +209,7 @@ xmldb_copy(clicon_handle h,
xml_free(x2);
x2 = NULL;
}
else if (x2 == NULL){ /* create x2 and copy x1 to it */
else if (x2 == NULL){ /* create x2 and copy from x1 */
if ((x2 = xml_new(xml_name(x1), NULL, xml_spec(x1))) == NULL)
goto done;
if (xml_copy(x1, x2) < 0)
@ -221,12 +222,13 @@ xmldb_copy(clicon_handle h,
if (xml_copy(x1, x2) < 0)
goto done;
}
if (x1 || x2){
if (de2)
de0 = *de2;
de0.de_xml = x2; /* The new tree */
clicon_db_elmnt_set(h, to, &de0);
}
/* always set cache although not strictly necessary in case 1
* above, but logic gets complicated due to differences with
* de and de->de_xml */
if (de2)
de0 = *de2;
de0.de_xml = x2; /* The new tree */
clicon_db_elmnt_set(h, to, &de0);
}
/* Copy the files themselves (above only in-memory cache) */
if (xmldb_db2file(h, from, &fromfile) < 0)

View file

@ -128,9 +128,7 @@ text_modify(clicon_handle h,
int i;
int ret;
int changed = 0; /* Only if x0p's children have changed-> sort is necessary */
assert(x1 && xml_type(x1) == CX_ELMNT);
assert(y0);
/* Check for operations embedded in tree according to netconf */
if ((opstr = xml_find_value(x1, "operation")) != NULL)
if (xml_operation(opstr, &op) < 0)
@ -157,8 +155,16 @@ text_modify(clicon_handle h,
permit = 1;
}
// int iamkey=0;
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
#ifdef USE_XML_INSERT
/* Add new xml node but without parent - insert when node fully
copied (see changed conditional below) */
if ((x0 = xml_new(x1name, NULL, (yang_stmt*)y0)) == NULL)
goto done;
#else
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
#endif
changed++;
/* Copy xmlns attributes */
@ -204,6 +210,12 @@ text_modify(clicon_handle h,
}
}
}
#ifdef USE_XML_INSERT
if (changed){
if (xml_insert(x0p, x0) < 0)
goto done;
}
#endif
break;
case OP_DELETE:
if (x0==NULL){
@ -283,8 +295,15 @@ text_modify(clicon_handle h,
goto fail;
permit = 1;
}
#ifdef USE_XML_INSERT
/* Add new xml node but without parent - insert when node fully
copied (see changed conditional below) */
if ((x0 = xml_new(x1name, NULL, (yang_stmt*)y0)) == NULL)
goto done;
#else
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
#endif
changed++;
/* Copy xmlns attributes */
x1a = NULL;
@ -346,6 +365,12 @@ text_modify(clicon_handle h,
if (ret == 0)
goto fail;
}
#ifdef USE_XML_INSERT
if (changed){
if (xml_insert(x0p, x0) < 0)
goto done;
}
#endif
break;
case OP_DELETE:
if (x0==NULL){
@ -363,15 +388,16 @@ text_modify(clicon_handle h,
}
if (xml_purge(x0) < 0)
goto done;
changed++;
}
break;
default:
break;
} /* CONTAINER switch op */
} /* else Y_CONTAINER */
#ifndef USE_XML_INSERT
if (changed)
xml_sort(x0p, NULL);
#endif
retval = 1;
done:
if (x0vec)
@ -418,8 +444,8 @@ text_modify_top(clicon_handle h,
int ret;
/* Assure top-levels are 'config' */
assert(x0 && strcmp(xml_name(x0),"config")==0);
assert(x1 && strcmp(xml_name(x1),"config")==0);
// assert(x0 && strcmp(xml_name(x0),"config")==0);
// assert(x1 && strcmp(xml_name(x1),"config")==0);
/* Check for operations embedded in tree according to netconf */
if ((opstr = xml_find_value(x1, "operation")) != NULL)
@ -584,11 +610,11 @@ xml_container_presence(cxobj *x,
*/
int
xmldb_put(clicon_handle h,
const char *db,
enum operation_type op,
cxobj *x1,
char *username,
cbuf *cbret)
const char *db,
enum operation_type op,
cxobj *x1,
char *username,
cbuf *cbret)
{
int retval = -1;
char *dbfile = NULL;

View file

@ -112,6 +112,7 @@ hash_bucket(const char *str)
*
* @retval hash Pointer to new hash table.
* @retval NULL Error
* @see hash_free For freeing the hash-table
*/
clicon_hash_t *
hash_init(void)

View file

@ -915,79 +915,4 @@ json_parse_file(int fd,
return retval;
}
/*
* Turn this on to get a json parse and pretty print test program
* Usage: json
* read json from input
* Example compile:
gcc -g -o json -I. -I../clixon ./clixon_json.c -lclixon -lcligen
* Example run:
echo '{"foo": -23}' | ./json
*/
#if 0 /* Test program */
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s.\n\tInput on stdin\n", argv0);
exit(0);
}
int
main(int argc,
char **argv)
{
cxobj *xt;
cxobj *xc;
cbuf *cb = cbuf_new();
char *buf = NULL;
int i;
int c;
int len;
FILE *f = stdin;
if (argc != 1){
usage(argv[0]);
return 0;
}
clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR);
len = 1024; /* any number is fine */
if ((buf = malloc(len)) == NULL){
perror("malloc");
return -1;
}
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 (json_parse_str(buf, &xt) < 0)
return -1;
xc = NULL;
while ((xc = xml_child_each(xt, xc, -1)) != NULL) {
xmltree2cbuf(cb, xc, 0); /* dump data structures */
//clicon_xml2cbuf(cb, xc, 0, 1); /* print xml */
}
fprintf(stdout, "%s", cbuf_get(cb));
if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
done:
return 0;
}
#endif /* Test program */

View file

@ -552,7 +552,7 @@ clicon_int2str(const map_str2int *mstab,
* @param[in] str Input string
* @retval int Value
* @retval -1 Error, not found
* @note linear search
* @see clicon_str2int_search for optimized lookup, but strings must be sorted
*/
int
clicon_str2int(const map_str2int *mstab,
@ -566,6 +566,65 @@ clicon_str2int(const map_str2int *mstab,
return -1;
}
/*! Map from string to int using binary (alphatical) search
* @param[in] ms String, integer map
* @param[in] str Input string
* @param[in] low Lower bound index
* @param[in] upper Upper bound index
* @param[in] len Length of array (max)
* @param[out] found Integer found (can also be negative)
* @retval 0 Not found
* @retval 1 Found with "found" value set.
* @note Assumes sorted strings, tree search
*/
static int
str2int_search1(const map_str2int *mstab,
char *str,
int low,
int upper,
int len,
int *found)
{
const struct map_str2int *ms;
int mid;
int cmp;
if (upper < low)
return 0; /* not found */
mid = (low + upper) / 2;
if (mid >= len) /* beyond range */
return 0; /* not found */
ms = &mstab[mid];
if ((cmp = strcmp(str, ms->ms_str)) == 0){
*found = ms->ms_int;
return 1; /* found */
}
else if (cmp < 0)
return str2int_search1(mstab, str, low, mid-1, len, found);
else
return str2int_search1(mstab, str, mid+1, upper, len, found);
}
/*! Map from string to int using str2int map
* @param[in] ms String, integer map
* @param[in] str Input string
* @retval int Value
* @retval -1 Error, not found
* @note Assumes sorted strings, tree search
* @note -1 can not be value
*/
int
clicon_str2int_search(const map_str2int *mstab,
char *str,
int len)
{
int found;
if (str2int_search1(mstab, str, 0, len, len, &found))
return found;
return -1; /* not found */
}
/*! Split colon-separated node identifier into prefix and name
* @param[in] node-id
* @param[out] prefix Malloced string. May be NULL.

View file

@ -127,6 +127,7 @@ struct xml{
reference, dont free */
cg_var *x_cv; /* Cached value as cligen variable
(eg xml_cmp) */
char *x_ns_cache; /* Cached namespace */
int _x_vector_i; /* internal use: xml_child_each */
int _x_i; /* internal use for sorting:
see xml_enumerate and xml_cmp */
@ -234,6 +235,7 @@ 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?
*/
int
xml2ns(cxobj *x,
@ -241,9 +243,11 @@ xml2ns(cxobj *x,
char **namespace)
{
int retval = -1;
char *ns;
char *ns = NULL;
cxobj *xp;
if ((ns = x->x_ns_cache) != NULL)
goto ok;
if (prefix != NULL) /* xmlns:<prefix>="<uri>" */
ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR);
else /* xmlns="<uri>" */
@ -261,6 +265,11 @@ xml2ns(cxobj *x,
ns = DEFAULT_XML_RPC_NAMESPACE;
#endif
}
if (ns && (x->x_ns_cache = strdup(ns)) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
ok:
if (namespace)
*namespace = ns;
retval = 0;
@ -562,6 +571,7 @@ xml_child_nr_type(cxobj *xn,
* @param[in] i the number of the child, eg order in children vector
* @retval xml The child xml node
* @retval NULL if no such child, or empty child
* @see xml_child_i_type
*/
cxobj *
xml_child_i(cxobj *xn,
@ -653,7 +663,7 @@ xml_child_each(cxobj *xparent,
}
/*! Extend child vector with one and insert xml node there
* Note: does not do anything with child, you may need to set its parent, etc
* @note does not do anything with child, you may need to set its parent, etc
*/
static int
xml_child_append(cxobj *x,
@ -669,7 +679,31 @@ xml_child_append(cxobj *x,
return 0;
}
/*! Set a a childvec to a specific size, fill with children after
/*! Insert child xc at position i under parent xp
*
* @see xml_child_append
* @note does not do anything with child, you may need to set its parent, etc
*/
int
xml_child_insert_pos(cxobj *xp,
cxobj *xc,
int i)
{
size_t size;
xp->x_childvec_len++;
xp->x_childvec = realloc(xp->x_childvec, xp->x_childvec_len*sizeof(cxobj*));
if (xp->x_childvec == NULL){
clicon_err(OE_XML, errno, "realloc");
return -1;
}
size = (xml_child_nr(xp) - i - 1)*sizeof(cxobj *);
memmove(&xp->x_childvec[i+1], &xp->x_childvec[i], size);
xp->x_childvec[i] = xc;
return 0;
}
/*! Set a childvec to a specific size, fill with children after
* @code
* xml_childvec_set(x, 2);
* xml_child_i_set(x, 0, xc0)
@ -710,8 +744,10 @@ xml_childvec_get(cxobj *x)
* ...
* xml_free(x);
* @endcode
* @note yspec may be NULL either because it is not known or it is irrelevant,
* eg for body or attribute
* @note As a rule, yspec should be given in normal Clixon calls to enable
* proper sorting and insert functionality. Except as follows:
* - type is body or attribute
* - Yang is unknown
* @see xml_sort_insert
*/
cxobj *
@ -732,6 +768,7 @@ xml_new(char *name,
xml_parent_set(x, xp);
if (xml_child_append(xp, x) < 0)
return NULL;
x->_x_i = xml_child_nr(xp)-1;
}
x->x_spec = yspec; /* Can be NULL */
return x;
@ -788,11 +825,12 @@ xml_cv_set(cxobj *x,
* name "name".
*
* @param[in] x_up Base XML object
* @param[in] name shell wildcard pattern to match with node name
* @param[in] name Node name
*
* @retval xmlobj if found.
* @retval NULL if no such node found.
* @see xml_find_type A more generic function
* @note Linear scalability and relies on strcmp
*/
cxobj *
xml_find(cxobj *x_up,
@ -967,15 +1005,14 @@ xml_child_rm(cxobj *xp,
int
xml_rm(cxobj *xc)
{
int retval = 0;
int retval = -1;
cxobj *xp;
cxobj *x;
int i;
if ((xp = xml_parent(xc)) == NULL)
goto done;
retval = -1;
/* Find child in parent */
goto ok;
/* Find child in parent XXX: search? */
x = NULL; i = 0;
while ((x = xml_child_each(xp, x, -1)) != NULL) {
if (x == xc)
@ -983,7 +1020,10 @@ xml_rm(cxobj *xc)
i++;
}
if (x != NULL)
retval = xml_child_rm(xp, i);
if (xml_child_rm(xp, i) < 0)
goto done;
ok:
retval = 0;
done:
return retval;
}
@ -1331,6 +1371,8 @@ xml_free(cxobj *x)
free(x->x_childvec);
if (x->x_cv)
cv_free(x->x_cv);
if (x->x_ns_cache)
free(x->x_ns_cache);
free(x);
return 0;
}

View file

@ -1073,6 +1073,13 @@ cvec2xml_1(cvec *cvv,
* @param[out] changed_x0 Pointervector to XML nodes changed orig value
* @param[out] changed_x1 Pointervector to XML nodes changed wanted value
* @param[out] changedlen Length of changed vector
* Algorithm to compare two sorted lists A, B:
* A 0 1 2 3 5 6
* B 0 2 4 5 6
* Let a,b be first elements of A,B respectively
* a = b : recurse; get next a,b
* a < b : add a in x0, get next a
* a > b : add b in x1, get next b
*/
static int
xml_diff1(yang_stmt *ys,
@ -1092,35 +1099,54 @@ xml_diff1(yang_stmt *ys,
yang_stmt *yc;
char *b1;
char *b2;
int eq;
clicon_debug(2, "%s: %s", __FUNCTION__, ys->ys_argument?ys->ys_argument:"yspec");
/* Check nodes present in x0 and x1 + nodes only in x0
* Loop over x0
* XXX: room for improvement. Compare with match_base_child()
*/
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL){
if ((yc = xml_spec(x0c)) == NULL){
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x0c));
goto done;
/* Traverse x0 and x1 in lock-step */
x0c = x1c = NULL;
x0c = xml_child_each(x0, x0c, CX_ELMNT);
x1c = xml_child_each(x1, x1c, CX_ELMNT);
for (;;){
if (x0c == NULL && x1c == NULL)
goto ok;
else if (x0c == NULL){
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
goto done;
x1c = xml_child_each(x1, x1c, CX_ELMNT);
continue;
}
/* Does x1 have a child matching x0c? */
if (match_base_child(x1, x0c, yc, &x1c) < 0)
goto done;
if (x1c == NULL){
else if (x1c == NULL){
if (cxvec_append(x0c, x0vec, x0veclen) < 0)
goto done;
x0c = xml_child_each(x0, x0c, CX_ELMNT);
continue;
}
else if (yang_choice(yc)){
/* if x0c and x1c are choice/case, then they are changed */
if (cxvec_append(x0c, changed_x0, changedlen) < 0)
goto done;
(*changedlen)--; /* append two vectors */
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
/* Both x0c and x1c exists, check if they are equal. */
eq = xml_cmp(x0c, x1c, 0);
if (eq < 0){
if (cxvec_append(x0c, x0vec, x0veclen) < 0)
goto done;
x0c = xml_child_each(x0, x0c, CX_ELMNT);
}
else{ /* if x0c and x1c are leafs w bodies, then they are changed */
if (yc->ys_keyword == Y_LEAF){
else if (eq > 0){
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
goto done;
x1c = xml_child_each(x1, x1c, CX_ELMNT);
}
else{ /* equal */
if ((yc = xml_spec(x0c)) == NULL){
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x0c));
goto done;
}
if (yang_choice(yc)){
/* if x0c and x1c are choice/case, then they are changed */
if (cxvec_append(x0c, changed_x0, changedlen) < 0)
goto done;
(*changedlen)--; /* append two vectors */
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
goto done;
}
else if (yc->ys_keyword == Y_LEAF){
/* if x0c and x1c are leafs w bodies, then they are changed */
if ((b1 = xml_body(x0c)) == NULL) /* empty type */
break;
if ((b2 = xml_body(x1c)) == NULL) /* empty type */
@ -1133,29 +1159,16 @@ xml_diff1(yang_stmt *ys,
goto done;
}
}
if (xml_diff1(yc, x0c, x1c,
x0vec, x0veclen,
x1vec, x1veclen,
changed_x0, changed_x1, changedlen)< 0)
else if (xml_diff1(yc, x0c, x1c,
x0vec, x0veclen,
x1vec, x1veclen,
changed_x0, changed_x1, changedlen)< 0)
goto done;
}
} /* while x0 */
/* Check nodes present only in x1
* Loop over x1
*/
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL){
if ((yc = xml_spec(x1c)) == NULL){
clicon_err(OE_UNIX, errno, "Unknown element: %s", xml_name(x1c));
goto done;
}
/* Does x0 have a child matching x1c? */
if (match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
if (x0c == NULL)
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
goto done;
} /* while x0 */
x0c = xml_child_each(x0, x0c, CX_ELMNT);
x1c = xml_child_each(x1, x1c, CX_ELMNT);
}
ok:
retval = 0;
done:
return retval;
@ -1635,7 +1648,8 @@ xml_default(cxobj *xt,
cxobj *xc;
cxobj *xb;
char *str;
int added=0;
if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
retval = 0;
goto done;
@ -1650,8 +1664,14 @@ xml_default(cxobj *xt,
assert(y->ys_cv);
if (!cv_flag(y->ys_cv, V_UNSET)){ /* Default value exists */
if (!xml_find(xt, y->ys_argument)){
#ifdef USE_XML_INSERT
if ((xc = xml_new(y->ys_argument, NULL, y)) == NULL)
goto done;
#else
if ((xc = xml_new(y->ys_argument, xt, y)) == NULL)
goto done;
#endif
xml_flag_set(xc, XML_FLAG_DEFAULT);
if ((xb = xml_new("body", xc, NULL)) == NULL)
goto done;
@ -1663,11 +1683,19 @@ xml_default(cxobj *xt,
if (xml_value_set(xb, str) < 0)
goto done;
free(str);
added++;
#ifdef USE_XML_INSERT
if (xml_insert(xt, xc) < 0)
goto done;
#endif
}
}
}
}
xml_sort(xt, NULL);
#ifndef USE_XML_INSERT
if (added)
xml_sort(xt, NULL);
#endif
retval = 0;
done:
return retval;
@ -1959,8 +1987,8 @@ api_path2xpath(yang_stmt *yspec,
* @param[out] xpathp Resulting xml tree
* @param[out] ypathp Yang spec matching xpathp
* @retval 1 OK
* @retval 0 Invalid api_path or associated XML, clicon_err called
* @retval -1 Fatal error, clicon_err called
* @retval 0 Invalid api_path or associated XML, clicon_err called
* @retval -1 Fatal error, clicon_err called
*
* @note both retval 0 and -1 set clicon_err, but the later is fatal
* @see api_path2xpath For api-path to xml xpath translation
@ -1994,6 +2022,7 @@ api_path2xml_vec(char **vec,
cxobj *x = NULL;
yang_stmt *y = NULL;
yang_stmt *ymod;
yang_stmt *ykey;
char *namespace = NULL;
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
@ -2077,7 +2106,12 @@ api_path2xml_vec(char **vec,
/* Create keys */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((xn = xml_new(keyname, x, NULL)) == NULL)
if ((ykey = yang_find(y, Y_LEAF, keyname)) == NULL){
clicon_err(OE_XML, 0, "List statement \"%s\" has no key leaf \"%s\"",
yang_argument_get(y), keyname);
goto done;
}
if ((xn = xml_new(keyname, x, ykey)) == NULL)
goto done;
xml_type_set(xn, CX_ELMNT);
if ((xb = xml_new("body", xn, NULL)) == NULL)
@ -2174,8 +2208,8 @@ api_path2xml(char *api_path,
goto fail;
}
nvec--; /* NULL-terminated */
if ((retval = api_path2xml_vec(vec+1, nvec,
xtop, yspec, nodeclass, strict,
if ((retval = api_path2xml_vec(vec+1, nvec,
xtop, yspec, nodeclass, strict,
xbotp, ybotp)) < 1)
goto done;
xml_yang_root(*xbotp, &xroot);

View file

@ -132,10 +132,10 @@ ncname {namestart}{namechar}*
<STATEA>"<?" { BEGIN(PIDECL); return BQMARK; }
<STATEA>\< { BEGIN(START); return *clixon_xml_parsetext; }
<STATEA>& { _YA->ya_lex_state =STATEA;BEGIN(AMPERSAND);}
<STATEA>[ \t] { clixon_xml_parselval.string = yytext;return WHITESPACE; }
<STATEA>\r\n { clixon_xml_parselval.string = "\n";return WHITESPACE; }
<STATEA>[ \t]+ { clixon_xml_parselval.string = yytext;return WHITESPACE; }
<STATEA>\r\n { clixon_xml_parselval.string = "\n"; _YA->ya_linenum++; return WHITESPACE; }
<STATEA>\r { clixon_xml_parselval.string = "\n";return WHITESPACE; }
<STATEA>\n { clixon_xml_parselval.string = yytext; _YA->ya_linenum++;return WHITESPACE; }
<STATEA>\n { clixon_xml_parselval.string = "\n"; _YA->ya_linenum++;return WHITESPACE; }
<STATEA>. { clixon_xml_parselval.string = yytext; return CHARDATA; }
/* @see xml_chardata_encode */

View file

@ -112,6 +112,44 @@ xml_parse_content(struct xml_parse_yacc_arg *ya,
return retval;
}
/*! Add whitespace
* If text, ie only body, keep as is.
* But if there is an element, then skip all whitespace.
*/
static int
xml_parse_whitespace(struct xml_parse_yacc_arg *ya,
char *str)
{
cxobj *xn = ya->ya_xelement;
cxobj *xp = ya->ya_xparent;
int retval = -1;
int i;
ya->ya_xelement = NULL; /* init */
/* If there is an element already, only add one whitespace child
* otherwise, keep all whitespace.
*/
#if 1
for (i=0; i<xml_child_nr(xp); i++){
if (xml_type(xml_child_i(xp, i)) == CX_ELMNT)
goto ok; /* Skip if already element */
}
#endif
if (xn == NULL){
if ((xn = xml_new("body", xp, NULL)) == NULL)
goto done;
xml_type_set(xn, CX_BODY);
}
if (xml_value_append(xn, str)==NULL)
goto done;
ya->ya_xelement = xn;
ok:
retval = 0;
done:
return retval;
}
static int
xml_parse_version(struct xml_parse_yacc_arg *ya,
char *ver)
@ -243,11 +281,17 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL)
break;
if (xc != NULL){ /* at least one element */
xc = NULL;
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL) {
xml_purge(xc);
xc = NULL; /* reset iterator */
}
int i;
for (i=0; i<xml_child_nr(x);){
xc = xml_child_i(x, i);
if (xml_type(xc) != CX_BODY){
i++;
continue;
}
if (xml_child_rm(x, i) < 0)
goto done;
xml_free(xc);
}
}
}
retval = 0;
@ -425,7 +469,7 @@ content : element { clicon_debug(2, "content -> element"); }
| pi { clicon_debug(2, "content -> pi"); }
| CHARDATA { if (xml_parse_content(_YA, $1) < 0) YYABORT;
clicon_debug(2, "content -> CHARDATA %s", $1); }
| WHITESPACE { if (xml_parse_content(_YA, $1) < 0) YYABORT;
| WHITESPACE { if (xml_parse_whitespace(_YA, $1) < 0) YYABORT;
clicon_debug(2, "content -> WHITESPACE %s", $1); }
| { clicon_debug(2, "content -> "); }
;

View file

@ -89,7 +89,8 @@ xml_cv_cache(cxobj *x,
uint8_t fraction = 0;
char *body;
body = xml_body(x);
if ((body = xml_body(x)) == NULL)
body="";
if ((cv = xml_cv(x)) != NULL)
goto ok;
if ((y = xml_spec(x)) == NULL)
@ -187,19 +188,30 @@ xml_child_spec(cxobj *x,
}
/*! Help function to qsort for sorting entries in xml child vector same parent
* @param[in] xml object 1
* @param[in] xml object 2
* @retval 0 If equal
* @retval <0 if x1 is less than x2
* @retval >0 if x1 is greater than x2
* @param[in] x1 object 1
* @param[in] x2 object 2
* @param[in] same If set, x1 and x2 are member of same parent & enumeration
* is used (see explanation below)
* @retval 0 If equal
* @retval <0 If x1 is less than x2
* @retval >0 If x1 is greater than x2
* @see xml_cmp1 Similar, but for one object
*
* There are distinct calls for this function:
* 1. For sorting in an existing list of XML children
* 2. For searching of an existing element in a list
* In the first case, there is a special case for "ordered-by-user", where
* if they have the same yang-spec, the existing order is used as tie-breaker.
* In other words, if order-by-system, or if the case (2) above, the existing
* order is ignored and the actual xml element contents is examined.
* @note empty value/NULL is smallest value
* @note xml_enumerate_children must have been called prior to this call
* @note some error cases return as -1 (qsort cant handle errors)
* @note some error cases return as -1 (qsort cant handle errors)
*/
static int
int
xml_cmp(cxobj *x1,
cxobj *x2)
cxobj *x2,
int same)
{
yang_stmt *y1;
yang_stmt *y2;
@ -217,18 +229,18 @@ xml_cmp(cxobj *x1,
int nr2 = 0;
cxobj *x1b;
cxobj *x2b;
int e;
e=0;
if (x1==NULL || x2==NULL)
goto done; /* shouldnt happen */
e=1;
y1 = xml_spec(x1);
y2 = xml_spec(x2);
nr1 = xml_enumerate_get(x1);
nr2 = xml_enumerate_get(x2);
if (same){
nr1 = xml_enumerate_get(x1);
nr2 = xml_enumerate_get(x2);
}
if (y1==NULL && y2==NULL){
equal = nr1-nr2;
if (same)
equal = nr1-nr2;
goto done;
}
if (y1==NULL){
@ -239,24 +251,24 @@ xml_cmp(cxobj *x1,
equal = 1;
goto done;
}
e=2;
if (y1 != y2){
yi1 = yang_order(y1);
yi2 = yang_order(y2);
if ((equal = yi1-yi2) != 0)
goto done;
}
e=3;
/* Now y1==y2, same Yang spec, can only be list or leaf-list,
* But first check exceptions, eg config false or ordered-by user
* otherwise sort according to key
* If the two elements are in the same list, and they are ordered-by user
* then do not look more into equivalence, use the enumeration in the
* existing list.
*/
if (yang_config(y1)==0 ||
yang_find(y1, Y_ORDERED_BY, "user") != NULL){
equal = nr1-nr2;
goto done; /* Ordered by user or state data : maintain existing order */
}
e=4;
if (same &&
(yang_config(y1)==0 || yang_find(y1, Y_ORDERED_BY, "user") != NULL)){
equal = nr1-nr2;
goto done; /* Ordered by user or state data : maintain existing order */
}
switch (yang_keyword_get(y1)){
case Y_LEAF_LIST: /* Match with name and value */
if ((b1 = xml_body(x1)) == NULL)
@ -287,8 +299,10 @@ xml_cmp(cxobj *x1,
else{
if (xml_cv_cache(x1b, &cv1) < 0) /* error case */
goto done;
assert(cv1);
if (xml_cv_cache(x2b, &cv2) < 0) /* error case */
goto done;
assert(cv2);
if ((equal = cv_cmp(cv1, cv2)) != 0)
goto done;
}
@ -298,9 +312,8 @@ xml_cmp(cxobj *x1,
default:
break;
}
e=5;
done:
clicon_debug(2, "%s %s %s %d %d nr: %d %d yi: %d %d", __FUNCTION__, xml_name(x1), xml_name(x2), equal, e, nr1, nr2, yi1, yi2);
clicon_debug(2, "%s %s %s %d nr: %d %d yi: %d %d", __FUNCTION__, xml_name(x1), xml_name(x2), equal, nr1, nr2, yi1, yi2);
return equal;
}
@ -311,95 +324,9 @@ static int
xml_cmp_qsort(const void* arg1,
const void* arg2)
{
return xml_cmp(*(struct xml**)arg1, *(struct xml**)arg2);
return xml_cmp(*(struct xml**)arg1, *(struct xml**)arg2, 1);
}
/*! Compare xml object
* @param[in] x XML node to compare with
* @param[in] y The yang spec of x
* @param[in] name Name to compare with x
* @param[in] keyword Yang keyword (stmt type) to compare w x/y
* @param[in] keynr Length of keyvec/keyval vector when applicable
* @param[in] keyvec Array of of yang key identifiers
* @param[in] keyval Array of of yang key values
* @param[out] userorder If set, this yang order is user ordered, linear search
* @retval 0 If equal (or userorder set)
* @retval <0 if arg1 is less than arg2
* @retval >0 if arg1 is greater than arg2
* @see xml_cmp Similar, but for two objects
* @note Does not care about y type of value as xml_cmp
*/
static int
xml_cmp1(cxobj *x,
yang_stmt *y,
char *name,
enum rfc_6020 keyword,
int keynr,
char **keyvec,
char **keyval,
cg_var **keycvec,
int *userorder)
{
char *b;
cxobj *xb;
int i;
char *keyname;
char *key;
int match = 0;
cg_var *cv;
/* state data = userorder */
if (userorder && yang_config(y)==0)
*userorder=1;
/* Check if same yang spec (order in yang stmt list) */
switch (keyword){
case Y_CONTAINER: /* Match with name */
case Y_LEAF: /* Match with name */
match = strcmp(name, xml_name(x));
break;
case Y_LEAF_LIST: /* Match with name and value */
if (userorder && yang_find(y, Y_ORDERED_BY, "user") != NULL)
*userorder=1;
if ((b=xml_body(x)) == NULL)
match = 1;
else{
if (keycvec[0]){
if (xml_cv_cache(x, &cv) < 0) /* error case */
goto done;
match = cv_cmp(keycvec[0], cv);
}
else
match = strcmp(keyval[0], b);
}
break;
case Y_LIST: /* Match with array of key values */
if (userorder && yang_find(y, Y_ORDERED_BY, "user") != NULL)
*userorder=1;
/* All must match */
for (i=0; i<keynr; i++){
keyname = keyvec[i];
key = keyval[i];
if ((xb = xml_find(x, keyname)) == NULL)
break; /* error case */
if ((b = xml_body(xb)) == NULL)
break; /* error case */
if (xml_cv_cache(xb, &cv) < 0) /* error case */
goto done;
if (keycvec[i]){
if ((match = cv_cmp(keycvec[i], cv)) != 0)
break;
}
else
if ((match = strcmp(key, b)) != 0)
break;
}
break;
default:
break;
}
done:
return match;
}
/*! Sort children of an XML node
* Assume populated by yang spec.
@ -427,40 +354,37 @@ xml_sort(cxobj *x,
/*! Special case search for ordered-by user where linear sort is used
*/
static cxobj *
xml_search_userorder(cxobj *x0,
xml_search_userorder(cxobj *xp,
cxobj *x1,
yang_stmt *y,
char *name,
int yangi,
int mid,
enum rfc_6020 keyword,
int keynr,
char **keyvec,
char **keyval,
cg_var **keycvec)
int mid)
{
int i;
cxobj *xc;
for (i=mid+1; i<xml_child_nr(x0); i++){ /* First increment */
xc = xml_child_i(x0, i);
for (i=mid+1; i<xml_child_nr(xp); i++){ /* First increment */
xc = xml_child_i(xp, i);
y = xml_spec(xc);
if (yangi!=yang_order(y))
break;
if (xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, keycvec, NULL) == 0)
if (xml_cmp(xc, x1, 0) == 0)
return xc;
}
for (i=mid-1; i>=0; i--){ /* Then decrement */
xc = xml_child_i(x0, i);
xc = xml_child_i(xp, i);
y = xml_spec(xc);
if (yangi!=yang_order(y))
break;
if (xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, keycvec, NULL) == 0)
if (xml_cmp(xc, x1, 0) == 0)
return xc;
}
return NULL; /* Not found */
}
/*!
* @param[in] xp Parent xml node.
* @param[in] yangi Yang order
* @param[in] keynr Length of keyvec/keyval vector when applicable
* @param[in] keyvec Array of of yang key identifiers
@ -469,14 +393,10 @@ xml_search_userorder(cxobj *x0,
* @param[in] upper Lower bound of childvec search interval
*/
static cxobj *
xml_search1(cxobj *x0,
char *name,
xml_search1(cxobj *xp,
cxobj *x1,
int userorder,
int yangi,
enum rfc_6020 keyword,
int keynr,
char **keyvec,
char **keyval,
cg_var **keycvec,
int low,
int upper)
{
@ -484,122 +404,200 @@ xml_search1(cxobj *x0,
int cmp;
cxobj *xc;
yang_stmt *y;
int userorder= 0;
if (upper < low)
return NULL; /* not found */
mid = (low + upper) / 2;
if (mid >= xml_child_nr(x0)) /* beyond range */
if (mid >= xml_child_nr(xp)) /* beyond range */
return NULL;
xc = xml_child_i(x0, mid);
xc = xml_child_i(xp, mid);
if ((y = xml_spec(xc)) == NULL)
return NULL;
cmp = yangi-yang_order(y);
/* Here is right yang order == same yang? */
if (cmp == 0){
cmp = xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, keycvec, &userorder);
if (userorder && cmp) /* Look inside this yangi order */
return xml_search_userorder(x0, y, name, yangi, mid, keyword, keynr, keyvec, keyval, keycvec);
if (userorder){
return xml_search_userorder(xp, x1, y, yangi, mid);
}
else /* Ordered by system */
cmp = xml_cmp(x1, xc, 0);
}
if (cmp == 0)
return xc;
else if (cmp < 0)
return xml_search1(x0, name, yangi, keyword,
keynr, keyvec, keyval, keycvec, low, mid-1);
return xml_search1(xp, x1, userorder, yangi, low, mid-1);
else
return xml_search1(x0, name, yangi, keyword,
keynr, keyvec, keyval, keycvec, mid+1, upper);
return xml_search1(xp, x1, userorder, yangi, mid+1, upper);
return NULL;
}
/*! Find XML children using binary search
* @param[in] yangi yang child order
/*! Find XML child under xp matching x1 using binary search
* @param[in] xp Parent xml node.
* @param[in] yangi Yang child order
* @param[in] keynr Length of keyvec/keyval vector when applicable
* @param[in] keyvec Array of of yang key identifiers
* @param[in] keyval Array of of yang key values
*/
static cxobj *
xml_search(cxobj *x0,
char *name,
int yangi,
enum rfc_6020 keyword,
int keynr,
char **keyvec,
char **keyval,
cg_var **keycvec)
xml_search(cxobj *xp,
cxobj *x1,
yang_stmt *yc)
{
cxobj *xa;
int low = 0;
int high = xml_child_nr(x0);
cxobj *xa;
int low = 0;
int upper = xml_child_nr(xp);
int userorder=0;
cxobj *xret = NULL;
int yangi;
/* Assume if there are any attributes, they are first in the list, mask
them by raising low to skip them */
for (low=0; low<high; low++)
if ((xa = xml_child_i(x0, low)) == NULL || xml_type(xa)!=CX_ATTR)
for (low=0; low<upper; low++)
if ((xa = xml_child_i(xp, low)) == NULL || xml_type(xa)!=CX_ATTR)
break;
return xml_search1(x0, name, yangi, keyword, keynr, keyvec, keyval, keycvec,
low, high);
/* Find if non-config and if ordered-by-user */
if (yang_config(yc)==0)
userorder = 1;
else if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST)
userorder = (yang_find(yc, Y_ORDERED_BY, "user") != NULL);
yangi = yang_order(yc);
xret = xml_search1(xp, x1, userorder, yangi, low, upper);
return xret;
}
#ifdef NOTUSED
/*! Position where to insert xml object into a list of children nodes
* @note EXPERIMENTAL
* Insert after position returned
* @param[in] x0 XML parent node.
* @param[in] low Lower bound
* @param[in] upper Upper bound (+1)
* @retval position
* XXX: Problem with this is that evrything must be known before insertion
/*! Insert xn in xp:s sorted child list
* Find a point in xp childvec with two adjacent nodes xi,xi+1 such that
* xi<=xn<=xi+1 or xn<=x0 or xmax<=xn
*/
static int
xml_insert2(cxobj *xp,
cxobj *xn,
yang_stmt *yn,
int yni,
int userorder,
int low,
int upper)
{
int retval = -1;
int mid;
int cmp;
cxobj *xc;
yang_stmt *yc;
int i;
if (low > upper){ /* beyond range */
clicon_err(OE_XML, 0, "low>upper %d %d", low, upper);
goto done;
}
if (low == upper){
retval = low;
goto done;
}
mid = (low + upper) / 2;
if (mid >= xml_child_nr(xp)){ /* beyond range */
clicon_err(OE_XML, 0, "Beyond range %d %d %d", low, mid, upper);
goto done;
}
xc = xml_child_i(xp, mid);
if ((yc = xml_spec(xc)) == NULL){
clicon_err(OE_XML, 0, "No spec found %s", xml_name(xc));
goto done;
}
if (yc == yn){ /* Same yang */
if (userorder){ /* append: increment linearly until no longer equal */
for (i=mid+1; i<xml_child_nr(xp); i++){ /* First increment */
xc = xml_child_i(xp, i);
yc = xml_spec(xc);
if (yc != yn){
retval = i;
goto done;
}
}
retval = i;
goto done;
}
else /* Ordered by system */
cmp = xml_cmp(xn, xc, 0);
}
else{ /* Not equal yang - compute diff */
cmp = yni - yang_order(yc);
/* One case is a choice where
* xc = <tcp/>, xn = <udp/>
* same order but different yang spec
*/
}
if (low +1 == upper){ /* termination criterium */
if (cmp<0) {
retval = mid;
goto done;
}
retval = mid+1;
goto done;
}
if (cmp == 0){
retval = mid;
goto done;
}
else if (cmp < 0)
return xml_insert2(xp, xn, yn, yni, userorder, low, mid);
else
return xml_insert2(xp, xn, yn, yni, userorder, mid+1, upper);
done:
return retval;
}
/*! Insert xc as child to xp in sorted place. Remove xc from previous parent.
* @param[in] xp Parent xml node. If NULL just remove from old parent.
* @param[in] x Child xml node to insert under xp
* @retval 0 OK
* @retval -1 Error
* @see xml_addsub where xc is appended. xml_insert is xml_addsub();xml_sort()
*/
int
xml_insert_pos(cxobj *x0,
char *name,
int yangi,
enum rfc_6020 keyword,
int keynr,
char **keyvec,
char **keyval,
int low,
int upper)
xml_insert(cxobj *xp,
cxobj *xi)
{
int mid;
cxobj *xc;
int retval = -1;
cxobj *xa;
int low = 0;
int upper;
yang_stmt *y;
int cmp;
int i;
int userorder= 0;
if (upper < low)
return low; /* not found */
mid = (low + upper) / 2;
if (mid >= xml_child_nr(x0))
return xml_child_nr(x0); /* upper range */
xc = xml_child_i(x0, mid);
y = xml_spec(xc);
cmp = yangi-yang_order(y);
if (cmp == 0){
cmp = xml_cmp1(xc, y, name, keyword, keynr, keyvec, keyval, keycvec, &userorder);
if (userorder){ /* Look inside this yangi order */
/* Special case: append last of equals if ordered by user */
for (i=mid+1;i<xml_child_nr(x0);i++){
xc = xml_child_i(x0, i);
if (strcmp(xml_name(xc), name))
break;
mid=i; /* still ok */
}
return mid;
}
int yi; /* Global yang-stmt order */
int i;
/* Ensure the intermediate state that xp is parent of x but has not yet been
* added as a child
*/
if (xml_parent(xi) != NULL){
clicon_err(OE_XML, 0, "XML node %s should not have parent", xml_name(xi));
goto done;
}
if (cmp == 0)
return mid;
else if (cmp < 0)
return xml_insert_pos(x0, name, yangi, keyword,
keynr, keyvec, keyval, keycvec, low, mid-1);
else
return xml_insert_pos(x0, name, yangi, keyword,
keynr, keyvec, keyval, keycvec, mid+1, upper);
if ((y = xml_spec(xi)) == NULL){
clicon_err(OE_XML, 0, "No spec found %s", xml_name(xi));
goto done;
}
upper = xml_child_nr(xp);
/* Assume if there are any attributes, they are first in the list, mask
them by raising low to skip them */
for (low=0; low<upper; low++)
if ((xa = xml_child_i(xp, low)) == NULL || xml_type(xa)!=CX_ATTR)
break;
/* Find if non-config and if ordered-by-user */
if (yang_config(y)==0)
userorder = 1;
else if (yang_keyword_get(y) == Y_LIST || yang_keyword_get(y) == Y_LEAF_LIST)
userorder = (yang_find(y, Y_ORDERED_BY, "user") != NULL);
yi = yang_order(y);
if ((i = xml_insert2(xp, xi, y, yi, userorder, low, upper)) < 0)
goto done;
if (xml_child_insert_pos(xp, xi, i) < 0)
goto done;
xml_parent_set(xi, xp);
retval = 0;
done:
return retval;
}
#endif /* NOTUSED */
/*! Verify all children of XML node are sorted according to xml_sort()
* @param[in] x XML node. Check its children
@ -625,7 +623,7 @@ xml_sort_verify(cxobj *x0,
xml_enumerate_children(x0);
while ((x = xml_child_each(x0, x, -1)) != NULL) {
if (xprev != NULL){ /* Check xprev <= x */
if (xml_cmp(xprev, x) > 0)
if (xml_cmp(xprev, x, 1) > 0)
goto done;
}
xprev = x;
@ -652,15 +650,8 @@ match_base_child(cxobj *x0,
int retval = -1;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *b;
cxobj *xb;
char *keyname;
char keynr = 0;
char **keyval = NULL;
char **keyvec = NULL;
cg_var **keycvec = NULL;
int i;
int yorder;
cxobj *x0c = NULL;
yang_stmt *y0c;
yang_stmt *y0p;
@ -686,19 +677,10 @@ match_base_child(cxobj *x0,
case Y_LEAF: /* Equal regardless */
break;
case Y_LEAF_LIST: /* Match with name and value */
keynr = 1;
if ((keyval = calloc(keynr+1, sizeof(char*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
if ((keyval[0] = xml_body(x1c)) == NULL)
if (xml_body(x1c) == NULL){ /* Treat as empty string */
// assert(0);
goto ok;
if ((keycvec = calloc(keynr+1, sizeof(cg_var*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
if (xml_cv_cache(x1c, &keycvec[0]) < 0) /* error case */
goto done;
break;
case Y_LIST: /* Match with key values */
cvk = yang_cvec_get(yc); /* Use Y_LIST cache, see ys_populate_list() */
@ -706,51 +688,22 @@ match_base_child(cxobj *x0,
* Then create two vectors one with names and one with values of x1c,
* ec: keyvec: [a,b,c] keyval: [1,2,3]
*/
cvi = NULL; keynr = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL)
keynr++;
if ((keyval = calloc(keynr+1, sizeof(char*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
if ((keyvec = calloc(keynr+1, sizeof(char*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
if ((keycvec = calloc(keynr+1, sizeof(char*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
cvi = NULL; i = 0;
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
keyvec[i] = keyname;
if ((xb = xml_find(x1c, keyname)) == NULL)
// keyvec[i] = keyname;
if ((xb = xml_find(x1c, keyname)) == NULL){
goto ok;
if ((b = xml_body(xb)) == NULL)
goto ok;
keyval[i] = b;
if (xml_cv_cache(xb, &keycvec[i]) < 0) /* error case */
goto done;
i++;
}
}
break;
default:
break;
}
/* Get match. */
yorder = yang_order(yc);
x0c = xml_search(x0, xml_name(x1c), yorder, yang_keyword_get(yc), keynr, keyvec, keyval, keycvec);
x0c = xml_search(x0, x1c, yc);
ok:
*x0cp = x0c;
retval = 0;
done:
if (keyval)
free(keyval);
if (keyvec)
free(keyvec);
if (keycvec)
free(keycvec);
return retval;
}

View file

@ -69,6 +69,7 @@
#include <sys/param.h>
#include <netinet/in.h>
#include <libgen.h>
#include <regex.h>
/* cligen */
#include <cligen/cligen.h>
@ -223,6 +224,46 @@ yang_cvec_get(yang_stmt *ys)
return ys->ys_cvec;
}
/*! Set yang statement CLIgen variable vector
* @param[in] ys Yang statement node
* @param[in] cvec CLIgen vector
* @retval 0 OK
* @retval -1 Error
*/
int
yang_cvec_set(yang_stmt *ys,
cvec *cvv)
{
if (ys->ys_cvec)
cvec_free(ys->ys_cvec);
ys->ys_cvec = cvv;
return 0;
}
/*! Get regular expression cache - the compiled regex
* @param[in] ys Yang statement node
* @retval re Compiled regex
* @see regcomp
*/
void*
yang_regex_cache_get(yang_stmt *ys)
{
return ys->ys_regex_cache;
}
/*! Set regular expression cache - the compiled regex
* @param[in] ys Yang statement node
* @param[in] re Compiled regex
* @see regcomp
*/
int
yang_regex_cache_set(yang_stmt *ys,
void *regex)
{
ys->ys_regex_cache = regex;
return 0;
}
/* End access functions */
/*! Create new yang specification
@ -267,6 +308,17 @@ ys_new(enum rfc_6020 keyw)
return ys;
}
static int
yang_regex_cache_free(yang_stmt *ys)
{
if (ys->ys_regex_cache){
regfree(ys->ys_regex_cache);
free(ys->ys_regex_cache);
}
return 0;
}
/*! Free a single yang statement */
static int
ys_free1(yang_stmt *ys)
@ -281,6 +333,7 @@ ys_free1(yang_stmt *ys)
cvec_free(ys->ys_cvec);
if (ys->ys_typecache)
yang_type_cache_free(ys->ys_typecache);
yang_regex_cache_free(ys);
free(ys);
return 0;
}
@ -720,6 +773,30 @@ yang_choice(yang_stmt *y)
return NULL;
}
static int
order1_choice(yang_stmt *yp,
yang_stmt *y)
{
yang_stmt *ys;
yang_stmt *yc;
int i;
int j;
for (i=0; i<yp->ys_len; i++){
ys = yp->ys_stmt[i];
if (ys->ys_keyword == Y_CASE){
for (j=0; j<ys->ys_len; j++){
yc = ys->ys_stmt[j];
if (yang_datanode(yc) && yc == y)
return 1;
}
}
else if (yang_datanode(ys) && ys == y)
return 1;
}
return 0;
}
/*! Find matching y in yp:s children, return 0 and index or -1 if not found.
* @param[in] yp Parent
* @param[in] y Yang datanode to find
@ -737,10 +814,16 @@ order1(yang_stmt *yp,
for (i=0; i<yp->ys_len; i++){
ys = yp->ys_stmt[i];
if (!yang_datanode(ys))
continue;
if (ys==y)
return 1;
if (ys->ys_keyword == Y_CHOICE){
if (order1_choice(ys, y) == 1) /* If one of the choices is "y" */
return 1;
}
else {
if (!yang_datanode(ys))
continue;
if (ys==y)
return 1;
}
(*index)++;
}
return 0;
@ -763,7 +846,14 @@ yang_order(yang_stmt *y)
int j=0;
int tot = 0;
/* Some special handling if yp is choice (or case) and maybe union?
* if so, the real parent (from an xml point of view) is the parents
* parent.
*/
yp = y->ys_parent;
while (yp->ys_keyword == Y_CASE || yp->ys_keyword == Y_CHOICE)
yp = yp->ys_parent;
/* XML nodes with yang specs that are children of modules are special -
* In clixon, they are seen as an "implicit" container where the XML can come from different
* modules. The order must therefore be global among yang top-symbols to be unique.
@ -1950,6 +2040,7 @@ yang_parse_str(char *str,
* @param[in] ysp Yang specification. Should have been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
* @retval NULL Error
* @note this function simply parse a yang spec, no dependencies or checks
*/
yang_stmt *
yang_parse_file(int fd,

View file

@ -95,6 +95,7 @@ struct yang_stmt{
Y_TYPE & identity: store all derived types
*/
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
void *ys_regex_cache; /* regex cache */
int _ys_vector_i; /* internal use: yn_each */
};

View file

@ -160,7 +160,8 @@ yang_modules_revision(clicon_handle h)
}
/*! Actually build the yang modules state XML tree
*/
* @see RFC7895
*/
static int
yms_build(clicon_handle h,
yang_stmt *yspec,
@ -198,8 +199,11 @@ yms_build(clicon_handle h,
cprintf(cb,"<name>%s</name>", ymod->ys_argument);
if ((ys = yang_find(ymod, Y_REVISION, NULL)) != NULL)
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
else
else{
/* RFC7895 1 If no (such) revision statement exists, the module's or
submodule's revision is the zero-length string. */
cprintf(cb,"<revision></revision>");
}
if ((ys = yang_find(ymod, Y_NAMESPACE, NULL)) != NULL)
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
else

View file

@ -49,6 +49,7 @@
#include <regex.h>
#include <syslog.h>
#include <assert.h>
#include <regex.h>
#include <netinet/in.h>
/* cligen */
@ -80,19 +81,19 @@ static const map_str2int ytmap[] = {
{"int32", CGV_INT32}, /* NOTE, first match on right is significant, dont move */
{"string", CGV_STRING}, /* NOTE, first match on right is significant, dont move */
{"string", CGV_REST}, /* For cv -> yang translation of rest */
{"binary", CGV_STRING},
{"bits", CGV_STRING},
{"binary", CGV_STRING},
{"bits", CGV_STRING},
{"boolean", CGV_BOOL},
{"decimal64", CGV_DEC64},
{"decimal64", CGV_DEC64},
{"empty", CGV_VOID}, /* May not include any content */
{"enumeration", CGV_STRING},
{"enumeration", CGV_STRING},
{"identityref", CGV_STRING}, /* XXX */
{"instance-identifier", CGV_STRING}, /* XXX */
{"int8", CGV_INT8},
{"int16", CGV_INT16},
{"int8", CGV_INT8},
{"int16", CGV_INT16},
{"int64", CGV_INT64},
{"leafref", CGV_STRING}, /* XXX */
{"uint8", CGV_UINT8},
{"uint8", CGV_UINT8},
{"uint16", CGV_UINT16},
{"uint32", CGV_UINT32},
{"uint64", CGV_UINT64},
@ -100,11 +101,106 @@ static const map_str2int ytmap[] = {
{NULL, -1}
};
/*! Mapping from yang string types --> cligen types
* @note not 100% same as map_str2int since it has significant order AND
* string->CGV_REST entry removed
*/
static const map_str2int ytmap2[] = {
{"binary", CGV_STRING},
{"bits", CGV_STRING},
{"boolean", CGV_BOOL},
{"decimal64", CGV_DEC64},
{"empty", CGV_VOID}, /* May not include any content */
{"enumeration", CGV_STRING},
{"identityref", CGV_STRING}, /* XXX */
{"instance-identifier", CGV_STRING}, /* XXX */
{"int16", CGV_INT16},
{"int32", CGV_INT32},
{"int64", CGV_INT64},
{"int8", CGV_INT8},
{"leafref", CGV_STRING}, /* XXX */
{"string", CGV_STRING},
{"uint16", CGV_UINT16},
{"uint32", CGV_UINT32},
{"uint64", CGV_UINT64},
{"uint8", CGV_UINT8},
{"union", CGV_REST}, /* Is replaced by actual type */
{NULL, -1}
};
/*! Regular expression compiling
* @retval -1 Error
* @retval 0 regex problem (no match?)
* @retval 1 OK Match
* @see match_regexp the CLIgen original composite function
*/
static int
regex_compile(char *pattern0,
regex_t *re)
{
int retval = -1;
char pattern[1024];
// char errbuf[1024];
int len0;
int status;
len0 = strlen(pattern0);
if (len0 > sizeof(pattern)-5){
clicon_err(OE_XML, EINVAL, "pattern too long");
goto done;
}
strncpy(pattern, "^(", 2);
strncpy(pattern+2, pattern0, sizeof(pattern)-2);
strncat(pattern, ")$", sizeof(pattern)-len0-1);
if ((status = regcomp(re, pattern, REG_NOSUB|REG_EXTENDED)) != 0) {
#if 0 /* ignore error msg for now */
regerror(status, re, errbuf, sizeof(errbuf));
#endif
goto fail;
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Regular expression execution
* @retval -1 Error
* @retval 0 regex problem (no match?)
* @retval 1 OK Match
* @see match_regexp the CLIgen original composite function
*/
static int
regex_exec(regex_t *re,
char *string)
{
int retval = -1;
int status;
// char errbuf[1024];
status = regexec(re, string, (size_t) 0, NULL, 0);
if (status != 0) {
#if 0 /* ignore error msg for now */
regerror(status, re, errbuf, sizeof(errbuf));
#endif
goto fail;
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/* return 1 if built-in, 0 if not */
static int
yang_builtin(char *type)
{
if (clicon_str2int(ytmap, type) != -1)
if (clicon_str2int_search(ytmap2, type, (sizeof(ytmap)/sizeof(map_str2int))-2) != -1)
return 1;
return 0;
}
@ -250,7 +346,7 @@ yang2cv_type(char *ytype,
*cv_type = CGV_ERR;
/* built-in types */
if ((ret = clicon_str2int(ytmap, ytype)) != -1){
if ((ret = clicon_str2int_search(ytmap2, ytype, (sizeof(ytmap)/sizeof(map_str2int))-2)) != -1){
*cv_type = ret;
return 0;
}
@ -526,14 +622,41 @@ cv_validate1(cg_var *cv,
}
if ((options & YANG_OPTIONS_PATTERN) != 0){
char *posix = NULL;
if (regexp_xsd2posix(pattern, &posix) < 0)
goto done;
if ((retval2 = match_regexp(str?str:"", posix)) < 0){
clicon_err(OE_DB, 0, "match_regexp: %s", pattern);
return -1;
regex_t *re = NULL;
if ((re = yang_regex_cache_get(yrestype)) == NULL){
/* Transform to posix regex */
if (regexp_xsd2posix(pattern, &posix) < 0)
goto done;
/* Create regex cache */
if ((re = malloc(sizeof(*re))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(re, 0, sizeof(*re));
/* Compute regex pattern for use in patterns */
if ((retval2 = regex_compile(posix, re)) < 0)
goto done;
if (retval2 == 0){
if (reason)
*reason = cligen_reason("regexp match fail: \"%s\" does not match %s",
str, pattern);
goto fail;
break;
}
yang_regex_cache_set(yrestype, re);
if (posix)
free(posix);
}
if ((retval2 = regex_exec(re, str?str:"")) < 0)
goto done;
if (retval2 == 0){
if (reason)
*reason = cligen_reason("regexp match fail: \"%s\" does not match %s",
str, pattern);
goto fail;
break;
}
if (posix)
free(posix);
if (retval2 == 0){
if (reason)
*reason = cligen_reason("regexp match fail: \"%s\" does not match %s",
@ -668,7 +791,7 @@ ys_cv_validate_union(yang_stmt *ys,
/*! Validate cligen variable cv using yang statement as spec
*
* @param[in] cv A cligen variable to validate. This is a correctly parsed cv.
* @param[in] ys A yang statement, must be leaf of leaf-list.
* @param[in] ys A yang statement, must be leaf or leaf-list.
* @param[out] reason If given, and if return value is 0, contains a malloced string
* describing the reason why the validation failed. Must be freed.
* @retval -1 Error (fatal), with errno set to indicate error