* Optimized validation of large lists

* New xmldb_get1() returning actual cache - not a copy. This has lead to some householding instead of just deleting the copy
  * xml_diff rewritten to work linearly instead of O(2)
  * New xml_insert function using tree search. The new code uses this in insertion xmldb_put and defaults. (Note previous xml_insert renamed to xml_wrap_all)
This commit is contained in:
Olof hagsand 2019-04-14 14:36:41 +02:00
parent 9b9b53c4ee
commit c79baf1b1f
16 changed files with 937 additions and 301 deletions

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){
@ -282,8 +294,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;
@ -345,6 +364,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){
@ -362,15 +387,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)
@ -417,8 +443,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)
@ -583,11 +609,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

@ -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

@ -562,6 +562,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,
@ -652,7 +653,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,
@ -668,7 +669,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)
@ -731,6 +756,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;
@ -966,15 +992,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)
@ -982,7 +1007,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;
}

View file

@ -1060,6 +1060,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,
@ -1079,35 +1086,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 */
@ -1120,29 +1146,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;
@ -1622,7 +1635,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;
@ -1637,8 +1651,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;
@ -1650,11 +1670,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;
@ -1946,8 +1974,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
@ -2156,8 +2184,8 @@ api_path2xml(char *api_path,
goto fail;
}
nvec--; /* NULL-terminated */
if ((retval = api_path2xml_vec(vec+1, nvec,
xtop, yspec, nodeclass,
if ((retval = api_path2xml_vec(vec+1, nvec,
xtop, yspec, nodeclass,
xbotp, ybotp)) < 1)
goto done;
xml_yang_root(*xbotp, &xroot);

View file

@ -188,19 +188,22 @@ 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
* @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
* @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)
*/
static int
int
xml_cmp(cxobj *x1,
cxobj *x2)
cxobj *x2,
int same)
{
yang_stmt *y1;
yang_stmt *y2;
@ -218,18 +221,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){
@ -240,24 +243,22 @@ 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 (yang_config(y1)==0 ||
yang_find(y1, Y_ORDERED_BY, "user") != NULL){
equal = nr1-nr2;
if (same)
equal = nr1-nr2;
goto done; /* Ordered by user or state data : maintain existing order */
}
e=4;
switch (y1->ys_keyword){
case Y_LEAF_LIST: /* Match with name and value */
if ((b1 = xml_body(x1)) == NULL)
@ -299,9 +300,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;
}
@ -312,7 +312,7 @@ 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
@ -531,76 +531,151 @@ xml_search(cxobj *x0,
{
cxobj *xa;
int low = 0;
int high = xml_child_nr(x0);
int upper = xml_child_nr(x0);
/* 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++)
for (low=0; low<upper; low++)
if ((xa = xml_child_i(x0, low)) == NULL || xml_type(xa)!=CX_ATTR)
break;
return xml_search1(x0, name, yangi, keyword, keynr, keyvec, keyval, keycvec,
low, high);
low, upper);
}
#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 0
if (xml_child_nr(xp) <= mid+1){
retval = mid;
goto done;
}
#endif
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;
}
}
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);
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
*/
// assert(xml_parent(xi) == NULL);
// assert(y = xml_spec(xi));
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 (y->ys_keyword == Y_LIST || y->ys_keyword == 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
@ -626,7 +701,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;

View file

@ -660,6 +660,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
@ -677,10 +701,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;
@ -703,7 +733,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.
@ -1890,6 +1927,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,