rearranged default code to be outside of copying in xml_get inner loop, and that no defaults are computed if no yang binding is made

This commit is contained in:
Olof hagsand 2020-09-09 15:52:54 +02:00
parent 8f8b49331f
commit 804b329301
10 changed files with 251 additions and 166 deletions

View file

@ -1650,7 +1650,6 @@ from_client_msg(clicon_handle h,
if ((ret = nacm_access_pre(h, ce->ce_username, username, &xnacm)) < 0) if ((ret = nacm_access_pre(h, ce->ce_username, username, &xnacm)) < 0)
goto done; goto done;
/* Cache XML NACM tree here. Use with caution, only valid on from_client_msg stack /* Cache XML NACM tree here. Use with caution, only valid on from_client_msg stack
*
*/ */
if (clicon_nacm_cache_set(h, xnacm) < 0) if (clicon_nacm_cache_set(h, xnacm) < 0)
goto done; goto done;

View file

@ -198,7 +198,6 @@ startup_common(clicon_handle h,
clicon_err(OE_YANG, 0, "Yang spec not set"); clicon_err(OE_YANG, 0, "Yang spec not set");
goto done; goto done;
} }
clicon_debug(1, "Reading startup config done"); clicon_debug(1, "Reading startup config done");
/* Clear flags xpath for get */ /* Clear flags xpath for get */
xml_apply0(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, xml_apply0(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
@ -251,8 +250,16 @@ startup_common(clicon_handle h,
goto done; goto done;
goto fail; goto fail;
} }
/* Sort xml */
if (xml_sort_recurse(xt) < 0) if (xml_sort_recurse(xt) < 0)
goto done; goto done;
/* Add global defaults. */
if (xml_global_defaults(h, xt, NULL, NULL, yspec) < 0)
goto done;
/* Apply default values (removed in clear function) */
if (xml_default_recurse(xt) < 0)
goto done;
/* Handcraft transition with with only add tree */ /* Handcraft transition with with only add tree */
td->td_target = xt; td->td_target = xt;
xt = NULL; xt = NULL;

View file

@ -238,7 +238,7 @@ startup_extraxml(clicon_handle h,
goto fail; goto fail;
} }
/* /*
* Check if tmp db is empty. XXX no this is not possible. * Check if tmp db is empty.
* It should be empty if extra-xml is null and reset plugins did nothing * It should be empty if extra-xml is null and reset plugins did nothing
* then skip validation. * then skip validation.
*/ */

View file

@ -66,7 +66,7 @@ int xml_tree_prune_flagged(cxobj *xt, int flag, int test);
int xml_namespace_change(cxobj *x, char *ns, char *prefix); int xml_namespace_change(cxobj *x, char *ns, char *prefix);
int xml_default(cxobj *x); int xml_default(cxobj *x);
int xml_default_recurse(cxobj *xn); int xml_default_recurse(cxobj *xn);
int xml_default_yspec(yang_stmt *yspec, cxobj *xn); int xml_global_defaults(clicon_handle h, cxobj *xn, cvec *nsc, const char *xpath, yang_stmt *yspec);
int xml_nopresence_default(cxobj *xt); int xml_nopresence_default(cxobj *xt);
int xml_nopresence_default_mark(cxobj *x, void *arg); int xml_nopresence_default_mark(cxobj *x, void *arg);
int xml_sanity(cxobj *x, void *arg); int xml_sanity(cxobj *x, void *arg);
@ -77,5 +77,6 @@ int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p);
int assign_namespace_body(cxobj *x0, char *x0bstr, cxobj *x1); int assign_namespace_body(cxobj *x0, char *x0bstr, cxobj *x1);
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason); int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
int yang_enum_int_value(cxobj *node, int32_t *val); int yang_enum_int_value(cxobj *node, int32_t *val);
int xml_copy_marked(cxobj *x0, cxobj *x1);
#endif /* _CLIXON_XML_MAP_H_ */ #endif /* _CLIXON_XML_MAP_H_ */

View file

@ -660,6 +660,7 @@ clicon_db_elmnt_get(clicon_handle h,
* @param[in] de Database element * @param[in] de Database element
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @see xmldb_disconnect
*/ */
int int
clicon_db_elmnt_set(clicon_handle h, clicon_db_elmnt_set(clicon_handle h,

View file

@ -231,96 +231,6 @@ xml_copy_from_bottom(cxobj *x0t,
return retval; return retval;
} }
/*! Given XML tree x0 with marked nodes, copy marked nodes to new tree x1
* Two marks are used: XML_FLAG_MARK and XML_FLAG_CHANGE
*
* The algorithm works as following:
* (1) Copy individual nodes marked with XML_FLAG_CHANGE
* until nodes marked with XML_FLAG_MARK are reached, where
* (2) the complete subtree of that node is copied.
* (3) Special case: key nodes in lists are copied if any node in list is marked
* @note you may want to check:!yang_config(ys)
*/
static int
xml_copy_marked(cxobj *x0,
cxobj *x1)
{
int retval = -1;
int mark;
cxobj *x;
cxobj *xcopy;
int iskey;
yang_stmt *yt;
char *name;
char *prefix;
assert(x0 && x1);
yt = xml_spec(x0); /* can be null */
xml_spec_set(x1, yt);
/* Copy prefix*/
if ((prefix = xml_prefix(x0)) != NULL)
if (xml_prefix_set(x1, prefix) < 0)
goto done;
/* Copy all attributes */
x = NULL;
while ((x = xml_child_each(x0, x, CX_ATTR)) != NULL) {
name = xml_name(x);
if ((xcopy = xml_new(name, x1, CX_ATTR)) == NULL)
goto done;
if (xml_copy(x, xcopy) < 0)
goto done;
}
/* Go through children to detect any marked nodes:
* (3) Special case: key nodes in lists are copied if any
* node in list is marked
*/
mark = 0;
x = NULL;
while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
if (xml_flag(x, XML_FLAG_MARK|XML_FLAG_CHANGE)){
mark++;
break;
}
}
x = NULL;
while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
name = xml_name(x);
if (xml_flag(x, XML_FLAG_MARK)){
/* (2) the complete subtree of that node is copied. */
if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
goto done;
if (xml_copy(x, xcopy) < 0)
goto done;
continue;
}
if (xml_flag(x, XML_FLAG_CHANGE)){
/* Copy individual nodes marked with XML_FLAG_CHANGE */
if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
goto done;
if (xml_copy_marked(x, xcopy) < 0) /* */
goto done;
}
/* (3) Special case: key nodes in lists are copied if any
* node in list is marked */
if (mark && yt && yang_keyword_get(yt) == Y_LIST){
/* XXX: I think yang_key_match is suboptimal here */
if ((iskey = yang_key_match(yt, name)) < 0)
goto done;
if (iskey){
if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
goto done;
if (xml_copy(x, xcopy) < 0)
goto done;
}
}
}
retval = 0;
done:
return retval;
}
/*! Read module-state in an XML tree /*! Read module-state in an XML tree
* *
* @param[in] th Datastore text handle * @param[in] th Datastore text handle
@ -620,20 +530,6 @@ xmldb_get_nocache(clicon_handle h,
/* Check if empty */ /* Check if empty */
if (xml_child_nr(xt) == 0) if (xml_child_nr(xt) == 0)
empty = 1; empty = 1;
/* Add global defaults.
* Must do it before xpath check, since globals may be filtered out
*/
if (xml_default_yspec(yspec, xt) < 0)
goto done;
/* If empty database, then disable NACM if loaded
* This has some drawbacks. One is that a config may become empty at a later stage
* and then this does not hold.
*/
if (empty &&
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
if (disable_nacm_on_empty(xt, yspec) < 0)
goto done;
}
/* Here xt looks like: <config>...</config> */ /* Here xt looks like: <config>...</config> */
/* Given the xpath, return a vector of matches in xvec */ /* Given the xpath, return a vector of matches in xvec */
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
@ -656,9 +552,23 @@ xmldb_get_nocache(clicon_handle h,
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0) if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done; goto done;
if (yb != YB_NONE){
/* Add global defaults. */
if (xml_global_defaults(h, xt, nsc, xpath, yspec) < 0)
goto done;
/* Add default values (if not set) */ /* Add default values (if not set) */
if (xml_default_recurse(xt) < 0) if (xml_default_recurse(xt) < 0)
goto done; goto done;
}
/* If empty database, then disable NACM if loaded
* This has some drawbacks. One is that a config may become empty at a later stage
* and then this does not hold.
*/
if (empty &&
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
if (disable_nacm_on_empty(xt, yspec) < 0)
goto done;
}
#if 0 /* debug */ #if 0 /* debug */
if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0) if (xml_apply0(xt, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: sort verify failed #2", __FUNCTION__); clicon_log(LOG_NOTICE, "%s: sort verify failed #2", __FUNCTION__);
@ -740,26 +650,10 @@ xmldb_get_cache(clicon_handle h,
} /* x0t == NULL */ } /* x0t == NULL */
else else
x0t = de->de_xml; x0t = de->de_xml;
/* Check if empty, must be before add global defaults */ /* Check if empty, must be before add global defaults */
if (xml_child_nr(x0t) == 0) if (xml_child_nr(x0t) == 0)
empty = 1; empty = 1;
/* Add global defaults.
* Cant do it to x1t since that is after xpath check, since globals may be filtered out
*/
if (xml_default_yspec(yspec, x0t) < 0)
goto done;
/* If empty database, then disable NACM if loaded
* This has some drawbacks. One is that a config may become empty at a later stage
* and then this does not hold.
*/
if (empty &&
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
if (disable_nacm_on_empty(x0t, yspec) < 0)
goto done;
}
/* Here x0t looks like: <config>...</config> */ /* Here x0t looks like: <config>...</config> */
/* Given the xpath, return a vector of matches in xvec /* Given the xpath, return a vector of matches in xvec
* Can we do everything in one go? * Can we do everything in one go?
@ -812,10 +706,23 @@ xmldb_get_cache(clicon_handle h,
/* Clear XML tree of defaults */ /* Clear XML tree of defaults */
if (xml_tree_prune_flagged(x0t, XML_FLAG_DEFAULT, 1) < 0) if (xml_tree_prune_flagged(x0t, XML_FLAG_DEFAULT, 1) < 0)
goto done; goto done;
/* x1t is wrong here should be <config><system>.. but is <system>.. */ if (yb != YB_NONE){
/* XXX where should we apply default values once? */ /* Add default global values */
if (xml_global_defaults(h, x1t, nsc, xpath, yspec) < 0)
goto done;
/* Add default recursive values */
if (xml_default_recurse(x1t) < 0) if (xml_default_recurse(x1t) < 0)
goto done; goto done;
}
/* If empty database, then disable NACM if loaded
* This has some drawbacks. One is that a config may become empty at a later stage
* and then this does not hold.
*/
if (empty &&
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
if (disable_nacm_on_empty(x1t, yspec) < 0)
goto done;
}
/* Copy the matching parts of the (relevant) XML tree. /* Copy the matching parts of the (relevant) XML tree.
* If cache was empty, also update to datastore cache * If cache was empty, also update to datastore cache
*/ */
@ -892,21 +799,6 @@ xmldb_get_zerocopy(clicon_handle h,
if (xml_child_nr(x0t) == 0) if (xml_child_nr(x0t) == 0)
empty = 1; empty = 1;
/* Add global defaults.
* Cant do it to x1t since that is after xpath check, since globals may be filtered out
*/
if (xml_default_yspec(yspec, x0t) < 0)
goto done;
/* If empty database, then disable NACM if loaded
* This has some drawbacks. One is that a config may become empty at a later stage
* and then this does not hold.
*/
if (empty &&
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
if (disable_nacm_on_empty(x0t, yspec) < 0)
goto done;
}
/* Here xt looks like: <config>...</config> */ /* Here xt looks like: <config>...</config> */
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done; goto done;
@ -918,9 +810,23 @@ xmldb_get_zerocopy(clicon_handle h,
xml_flag_set(x0, XML_FLAG_MARK); xml_flag_set(x0, XML_FLAG_MARK);
xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE); xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
} }
if (yb != YB_NONE){
/* Add global defaults. */
if (xml_global_defaults(h, x0t, nsc, xpath, yspec) < 0)
goto done;
/* Apply default values (removed in clear function) */ /* Apply default values (removed in clear function) */
if (xml_default_recurse(x0t) < 0) if (xml_default_recurse(x0t) < 0)
goto done; goto done;
}
/* If empty database, then disable NACM if loaded
* This has some drawbacks. One is that a config may become empty at a later stage
* and then this does not hold.
*/
if (empty &&
clicon_option_bool(h, "CLICON_NACM_DISABLED_ON_EMPTY")){
if (disable_nacm_on_empty(x0t, yspec) < 0)
goto done;
}
if (clicon_debug_get()>1) if (clicon_debug_get()>1)
clicon_xml2file(stderr, x0t, 0, 1); clicon_xml2file(stderr, x0t, 0, 1);
*xtop = x0t; *xtop = x0t;
@ -970,7 +876,7 @@ xmldb_get(clicon_handle h,
* freeing tree must be made after use. * freeing tree must be made after use.
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] db Name of datastore, eg "running" * @param[in] db Name of datastore, eg "running"
* @param[in] yb How to bind yang to XML top-level when parsing * @param[in] yb How to bind yang to XML top-level when parsing (if YB_NONE, no defaults)
* @param[in] nsc External XML namespace context, or NULL * @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all * @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] copy Force copy. Overrides cache_zerocopy -> cache * @param[in] copy Force copy. Overrides cache_zerocopy -> cache

View file

@ -583,7 +583,7 @@ xml_parent_set(cxobj *xn,
/*! Get xml node flags, used for internal algorithms /*! Get xml node flags, used for internal algorithms
* @param[in] xn xml node * @param[in] xn xml node
* @retval flag Flag value(s), see XML_FLAG_* * @retval flag Flag value(s), see XML_FLAG_MARK et al
*/ */
uint16_t uint16_t
xml_flag(cxobj *xn, xml_flag(cxobj *xn,
@ -594,7 +594,7 @@ xml_flag(cxobj *xn,
/*! Set xml node flags, used for internal algorithms /*! Set xml node flags, used for internal algorithms
* @param[in] xn xml node * @param[in] xn xml node
* @param[in] flag Flag values to set, see XML_FLAG_* * @param[in] flag Flag values to set, see XML_FLAG_MARK et al
*/ */
int int
xml_flag_set(cxobj *xn, xml_flag_set(cxobj *xn,
@ -1926,6 +1926,8 @@ xml_copy(cxobj *x0,
return retval; return retval;
} }
/*! Create and return a copy of xml tree. /*! Create and return a copy of xml tree.
* *
* @code * @code

View file

@ -1239,17 +1239,19 @@ xml_default_recurse(cxobj *xn)
return retval; return retval;
} }
/*! Ensure default values are set on top-level /*! Expand and set default values of global top-level on XML tree
* *
* Not recursive, except in one case with one or several non-presence containers * Not recursive, except in one case with one or several non-presence containers
* @param[in] h Clixon handle
* @param[in] yspec Top-level YANG specification tree, all modules
* @param[in] xt XML tree * @param[in] xt XML tree
* Typically called in a recursive apply function:
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
*/ */
int static int
xml_default_yspec(yang_stmt *yspec, xml_global_defaults_create(cxobj *xt,
cxobj *xt) yang_stmt *yspec)
{ {
int retval = -1; int retval = -1;
yang_stmt *ymod = NULL; yang_stmt *ymod = NULL;
@ -1261,12 +1263,86 @@ xml_default_yspec(yang_stmt *yspec,
while ((ymod = yn_each(yspec, ymod)) != NULL) while ((ymod = yn_each(yspec, ymod)) != NULL)
if (xml_default1(ymod, xt) < 0) if (xml_default1(ymod, xt) < 0)
goto done; goto done;
retval = 0; retval = 0;
done: done:
return retval; return retval;
} }
/*! Expand and set default values of global top-level on XML tree
*
* Not recursive, except in one case with one or several non-presence containers
* @param[in] h Clixon handle
* @param[in] xt XML tree, assume already filtered with xpath
* @param[in] xpath Filter global defaults with this and merge with xt
* @param[in] yspec Top-level YANG specification tree, all modules
* @retval 0 OK
* @retval -1 Error
* Uses cache?
*/
int
xml_global_defaults(clicon_handle h,
cxobj *xt,
cvec *nsc,
const char *xpath,
yang_stmt *yspec)
{
int retval = -1;
db_elmnt de0 = {0,};
db_elmnt *de = NULL;
cxobj *xcache = NULL;
cxobj *xpart = NULL;
cxobj **xvec = NULL;
size_t xlen;
int i;
cxobj *x0;
int ret;
/* First get or compute global xml tree cache */
if ((de = clicon_db_elmnt_get(h, "global-defaults")) == NULL){
/* Create it it */
if ((xcache = xml_new("config", NULL, CX_ELMNT)) == NULL)
goto done;
if (xml_global_defaults_create(xcache, yspec) < 0)
goto done;
de0.de_xml = xcache;
clicon_db_elmnt_set(h, "global-defaults", &de0);
}
else
xcache = de->de_xml;
/* Here xcache has all global defaults. Now find the matching nodes
* XXX: nsc as 2nd argument
*/
if (xpath_vec(xcache, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* Iterate through match vector
* For every node found in x0, mark the tree up to t1
*/
for (i=0; i<xlen; i++){
x0 = xvec[i];
xml_flag_set(x0, XML_FLAG_MARK);
xml_apply_ancestor(x0, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
if ((xpart = xml_new("config", NULL, CX_ELMNT)) == NULL)
goto done;
if (xml_copy_marked(xcache, xpart) < 0) /* config */
goto done;
if (xml_apply(xcache, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
goto done;
if (xml_apply(xpart, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
goto done;
/* Merge global pruned tree with xt */
if ((ret = xml_merge(xt, xpart, yspec, NULL)) < 1) /* XXX reason */
goto done;
retval = 0;
done:
if (xpart)
xml_free(xpart);
if (xvec)
free(xvec);
return retval;
}
/*! This node is a default set value or (recursively) a non-presence container /*! This node is a default set value or (recursively) a non-presence container
* @retval 1 xt is a nopresence/default node (ie "virtual") * @retval 1 xt is a nopresence/default node (ie "virtual")
* @retval 0 xt is not such a node * @retval 0 xt is not such a node
@ -1812,6 +1888,9 @@ xml_merge1(cxobj *x0, /* the target */
x0c = NULL; x0c = NULL;
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0) if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
goto done; goto done;
/* If x0 already has a value, do not replace it with a default value in x1 */
if (x0c && xml_flag(x1c, XML_FLAG_DEFAULT))
continue;
/* Save x0c, x1c, yc and merge in second wave, so that x1c entries dont "interfer" /* Save x0c, x1c, yc and merge in second wave, so that x1c entries dont "interfer"
* with itself, ie that later searches are among earlier objects already added * with itself, ie that later searches are among earlier objects already added
* to x0 */ * to x0 */
@ -1922,6 +2001,9 @@ xml_merge(cxobj *x0,
/* See if there is a corresponding node (x1c) in the base tree (x0) */ /* See if there is a corresponding node (x1c) in the base tree (x0) */
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0) if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
goto done; goto done;
/* If x0 already has a value, do not replace it with a default value in x1 */
if (x0c && xml_flag(x1c, XML_FLAG_DEFAULT))
continue;
/* Save x0c, x1c, yc and merge in second wave, so that x1c entries don't "interfere" /* Save x0c, x1c, yc and merge in second wave, so that x1c entries don't "interfere"
* with itself, ie that later searches are among earlier objects already added * with itself, ie that later searches are among earlier objects already added
* to x0 */ * to x0 */
@ -2009,5 +2091,92 @@ done:
} }
/*! Given XML tree x0 with marked nodes, copy marked nodes to new tree x1
* Two marks are used: XML_FLAG_MARK and XML_FLAG_CHANGE
*
* The algorithm works as following:
* (1) Copy individual nodes marked with XML_FLAG_CHANGE
* until nodes marked with XML_FLAG_MARK are reached, where
* (2) the complete subtree of that node is copied.
* (3) Special case: key nodes in lists are copied if any node in list is marked
* @note you may want to check:!yang_config(ys)
*/
int
xml_copy_marked(cxobj *x0,
cxobj *x1)
{
int retval = -1;
int mark;
cxobj *x;
cxobj *xcopy;
int iskey;
yang_stmt *yt;
char *name;
char *prefix;
assert(x0 && x1);
yt = xml_spec(x0); /* can be null */
xml_spec_set(x1, yt);
/* Copy prefix*/
if ((prefix = xml_prefix(x0)) != NULL)
if (xml_prefix_set(x1, prefix) < 0)
goto done;
/* Copy all attributes */
x = NULL;
while ((x = xml_child_each(x0, x, CX_ATTR)) != NULL) {
name = xml_name(x);
if ((xcopy = xml_new(name, x1, CX_ATTR)) == NULL)
goto done;
if (xml_copy(x, xcopy) < 0)
goto done;
}
/* Go through children to detect any marked nodes:
* (3) Special case: key nodes in lists are copied if any
* node in list is marked
*/
mark = 0;
x = NULL;
while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
if (xml_flag(x, XML_FLAG_MARK|XML_FLAG_CHANGE)){
mark++;
break;
}
}
x = NULL;
while ((x = xml_child_each(x0, x, CX_ELMNT)) != NULL) {
name = xml_name(x);
if (xml_flag(x, XML_FLAG_MARK)){
/* (2) the complete subtree of that node is copied. */
if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
goto done;
if (xml_copy(x, xcopy) < 0)
goto done;
continue;
}
if (xml_flag(x, XML_FLAG_CHANGE)){
/* Copy individual nodes marked with XML_FLAG_CHANGE */
if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
goto done;
if (xml_copy_marked(x, xcopy) < 0) /* */
goto done;
}
/* (3) Special case: key nodes in lists are copied if any
* node in list is marked */
if (mark && yt && yang_keyword_get(yt) == Y_LIST){
/* XXX: I think yang_key_match is suboptimal here */
if ((iskey = yang_key_match(yt, name)) < 0)
goto done;
if (iskey){
if ((xcopy = xml_new(name, x1, CX_ELMNT)) == NULL)
goto done;
if (xml_copy(x, xcopy) < 0)
goto done;
}
}
}
retval = 0;
done:
return retval;
}