* 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:
parent
9b9b53c4ee
commit
c79baf1b1f
16 changed files with 937 additions and 301 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue