validation NEW duplicate instances
Also trees structure removed, some
refactoring included.
diff --git a/src/common.h b/src/common.h
index 99f5ec6..3be741d 100644
--- a/src/common.h
+++ b/src/common.h
@@ -233,6 +233,8 @@
#define LY_VCODE_NOMIN LYVE_DATA, "Too few \"%s\" instances."
#define LY_VCODE_NOMAX LYVE_DATA, "Too many \"%s\" instances."
#define LY_VCODE_NOUNIQ LYVE_DATA, "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\"."
+#define LY_VCODE_DUP LYVE_DATA, "Duplicate instance of \"%s\"."
+#define LY_VCODE_DUPCASE LYVE_DATA, "Data for both cases \"%s\" and \"%s\" exist."
/******************************************************************************
* Context
diff --git a/src/parser_xml.c b/src/parser_xml.c
index b23b5bf..be516d4 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -450,7 +450,7 @@
}
ly_set_erase(&attrs_data, free);
if (ret && *node) {
- lyd_free_withsiblings(*node);
+ lyd_free_siblings(*node);
*node = NULL;
}
return ret;
@@ -460,7 +460,6 @@
lyd_parse_xml(struct ly_ctx *ctx, const char *data, int options, struct lyd_node **result)
{
LY_ERR ret = LY_SUCCESS;
- const struct lyd_node **result_trees = NULL;
struct lyd_xml_ctx xmlctx = {0};
xmlctx.options = options;
@@ -478,19 +477,14 @@
ret = lyd_validate_defaults_top(result, NULL, 0, ctx, &xmlctx.incomplete_type_validation, &xmlctx.when_check, options);
LY_CHECK_GOTO(ret, cleanup);
- /* prepare sized array for validator */
- if (*result) {
- result_trees = lyd_trees_new(1, *result);
- }
-
/* finish incompletely validated terminal values/attributes and when conditions */
ret = lyd_validate_unres(&xmlctx.incomplete_type_validation, &xmlctx.incomplete_type_validation_attrs,
- &xmlctx.when_check, LYD_XML, lydxml_resolve_prefix, ctx, result_trees);
+ &xmlctx.when_check, LYD_XML, lydxml_resolve_prefix, ctx, *result);
LY_CHECK_GOTO(ret, cleanup);
/* context node and other validation tasks that depend on other data nodes */
- ret = lyd_validate_data(result_trees, NULL, 0, ctx, options);
- LY_CHECK_GOTO(result, cleanup);
+ ret = lyd_validate_data(result, NULL, 0, ctx, options);
+ LY_CHECK_GOTO(ret, cleanup);
}
cleanup:
@@ -502,7 +496,6 @@
ly_set_erase(&xmlctx.incomplete_type_validation_attrs, NULL);
ly_set_erase(&xmlctx.when_check, NULL);
lyxml_context_clear((struct lyxml_context *)&xmlctx);
- lyd_trees_free(result_trees, 0);
if (ret) {
lyd_free_all(*result);
*result = NULL;
diff --git a/src/plugins_types.c b/src/plugins_types.c
index bf0dd16..f05b5c2 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -484,7 +484,7 @@
static LY_ERR
ly_type_store_int(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
- const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+ const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
LY_ERR ret;
@@ -560,7 +560,7 @@
static LY_ERR
ly_type_store_uint(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
- const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+ const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
LY_ERR ret;
@@ -635,7 +635,7 @@
static LY_ERR
ly_type_store_decimal64(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
- const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+ const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
int64_t d;
@@ -719,7 +719,7 @@
static LY_ERR
ly_type_store_binary(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
- const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+ const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
size_t start = 0, stop = 0, count = 0, u, termination = 0;
@@ -824,7 +824,7 @@
static LY_ERR
ly_type_store_string(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
- const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+ const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
struct lysc_type_str *type_str = (struct lysc_type_str *)type;
@@ -868,7 +868,7 @@
static LY_ERR
ly_type_store_bits(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
- const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+ const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
LY_ERR ret = LY_EVALID;
@@ -1053,7 +1053,7 @@
static LY_ERR
ly_type_store_enum(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
- const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+ const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
unsigned int u, v;
@@ -1129,7 +1129,7 @@
static LY_ERR
ly_type_store_boolean(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
- const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+ const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
int8_t i;
@@ -1185,7 +1185,7 @@
static LY_ERR
ly_type_store_empty(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
ly_clb_resolve_prefix UNUSED(resolve_prefix), void *UNUSED(parser), LYD_FORMAT UNUSED(format),
- const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+ const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
if (options & LY_TYPE_OPTS_SECOND_CALL) {
@@ -1246,7 +1246,7 @@
static LY_ERR
ly_type_store_identityref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT UNUSED(format),
- const void *UNUSED(context_node), const struct lyd_node **UNUSED(trees),
+ const void *UNUSED(context_node), const struct lyd_node *UNUSED(tree),
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
@@ -1402,7 +1402,7 @@
ly_type_store_instanceid_checknodeid(const char *orig, size_t orig_len, int options, int require_instance,
const char **token, struct lyd_value_prefix *prefixes, LYD_FORMAT format,
const struct lysc_node **node_s, const struct lyd_node **node_d,
- const struct lyd_node **trees, char **errmsg)
+ const struct lyd_node *tree, char **errmsg)
{
const char *id, *prefix;
size_t id_len, prefix_len;
@@ -1463,12 +1463,7 @@
}
} else {
/* top-level node */
- LY_ARRAY_FOR(trees, u) {
- lyd_find_sibling_next(trees[u], mod, id, id_len, NULL, 0, (struct lyd_node **)node_d);
- if (*node_d) {
- break;
- }
- }
+ lyd_find_sibling_next(tree, mod, id, id_len, NULL, 0, (struct lyd_node **)node_d);
if (!(*node_d)) {
/* node not found */
asprintf(errmsg, "Invalid instance-identifier \"%.*s\" value - path \"%.*s\" does not exists in the data tree(s).",
@@ -1588,7 +1583,7 @@
static LY_ERR
ly_type_store_instanceid(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
- const void *UNUSED(context_node), const struct lyd_node **trees,
+ const void *UNUSED(context_node), const struct lyd_node *tree,
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
LY_ERR ret = LY_EVALID;
@@ -1620,7 +1615,7 @@
if (!(options & LY_TYPE_OPTS_SCHEMA) && (options & LY_TYPE_OPTS_SECOND_CALL) && (options & LY_TYPE_OPTS_STORE)) {
/* the second run in data tree, the first one ended with LY_EINCOMPLETE, but we have prepared the target structure */
- if (!lyd_target(storage->target, trees)) {
+ if (!lyd_target(storage->target, tree)) {
/* in error message we print the JSON format of the instance-identifier - in case of XML, it is not possible
* to get the exactly same string as original, JSON is less demanding and still well readable/understandable. */
int dynamic = 0;
@@ -1663,7 +1658,7 @@
token++;
if (ly_type_store_instanceid_checknodeid(value, value_len, options, type_inst->require_instance,
- &token, prefixes, format, &node_s, &node_d, trees, &errmsg)) {
+ &token, prefixes, format, &node_s, &node_d, tree, &errmsg)) {
goto error;
}
@@ -1724,7 +1719,7 @@
/* resolve the key in predicate */
if (ly_type_store_instanceid_checknodeid(value, value_len, options, type_inst->require_instance,
- &t, prefixes, format, &key_s, &key_d, trees, &errmsg)) {
+ &t, prefixes, format, &key_s, &key_d, tree, &errmsg)) {
goto error;
}
if (key_d) {
@@ -1740,7 +1735,7 @@
if (node_d) {
while (node_d) {
- if (!lyd_value_compare((const struct lyd_node_term*)key_d, val, val_len, resolve_prefix, parser, format, trees)) {
+ if (!lyd_value_compare((const struct lyd_node_term*)key_d, val, val_len, resolve_prefix, parser, format, tree)) {
/* match */
break;
}
@@ -1818,7 +1813,7 @@
if (key_d) {
while (key_d) {
- if (!lyd_value_compare((const struct lyd_node_term*)key_d, val, val_len, resolve_prefix, parser, format, trees)) {
+ if (!lyd_value_compare((const struct lyd_node_term*)key_d, val, val_len, resolve_prefix, parser, format, tree)) {
/* match */
break;
}
@@ -2167,12 +2162,11 @@
*/
const struct lyd_node *
ly_type_find_leafref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
- const struct lyd_node *context_node, const struct lyd_node **trees, struct lyd_value *storage, char **errmsg)
+ const struct lyd_node *context_node, const struct lyd_node *tree, struct lyd_value *storage, char **errmsg)
{
struct lysc_type_leafref *type_lr = (struct lysc_type_leafref*)type;
const char *first_pred = NULL;
const struct lyd_node *start_search;
- unsigned int u = 0;
const char *prefix, *id;
size_t prefix_len, id_len;
const struct lys_module *mod_node = NULL;
@@ -2219,14 +2213,9 @@
}
} else {
/* top-level node */
- LY_ARRAY_FOR(trees, u) {
- start_search = trees[u];
+ start_search = tree;
next_instance_toplevel:
- lyd_find_sibling_next(start_search, mod_node, id, id_len, NULL, 0, (struct lyd_node **)&node);
- if (node) {
- break;
- }
- }
+ lyd_find_sibling_next(start_search, mod_node, id, id_len, NULL, 0, (struct lyd_node **)&node);
}
if (!node) {
/* node not found */
@@ -2299,12 +2288,7 @@
if (!value) {
/* top-level search */
- LY_ARRAY_FOR(trees, u) {
- lyd_find_sibling_next(trees[u], mod_pred, src, src_len, NULL, 0, (struct lyd_node **)&value);
- if (value) {
- break;
- }
- }
+ lyd_find_sibling_next(tree, mod_pred, src, src_len, NULL, 0, (struct lyd_node **)&value);
} else {
/* inner node */
lyd_find_sibling_next(lyd_node_children(value), mod_pred, src, src_len, NULL, 0, (struct lyd_node **)&value);
@@ -2369,7 +2353,7 @@
static LY_ERR
ly_type_store_leafref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
- const void *context_node, const struct lyd_node **trees,
+ const void *context_node, const struct lyd_node *tree,
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
LY_ERR ret;
@@ -2388,7 +2372,7 @@
/* check value according to the real type of the leafref target */
ret = type_lr->realtype->plugin->store(ctx, type_lr->realtype, value, value_len, options,
- resolve_prefix, parser, format, context_node, trees,
+ resolve_prefix, parser, format, context_node, tree,
storage, canonized, err);
if (ret != LY_SUCCESS && ret != LY_EINCOMPLETE) {
return ret;
@@ -2400,7 +2384,7 @@
}
/* find corresponding data instance */
- if (!ly_type_find_leafref(ctx, type, value, value_len, (const struct lyd_node *)context_node, trees, storage, &errmsg)) {
+ if (!ly_type_find_leafref(ctx, type, value, value_len, (const struct lyd_node *)context_node, tree, storage, &errmsg)) {
goto error;
}
}
@@ -2474,7 +2458,7 @@
static LY_ERR
ly_type_store_union(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
- const void *context_node, const struct lyd_node **trees,
+ const void *context_node, const struct lyd_node *tree,
struct lyd_value *storage, const char **canonized, struct ly_err_item **err)
{
LY_ERR ret;
@@ -2490,7 +2474,7 @@
ret = subvalue->value->realtype->plugin->store(ctx, subvalue->value->realtype, value, value_len,
options & ~(LY_TYPE_OPTS_CANONIZE | LY_TYPE_OPTS_DYNAMIC),
ly_type_stored_prefixes_clb, subvalue->prefixes, format,
- context_node, trees, subvalue->value, canonized, err);
+ context_node, tree, subvalue->value, canonized, err);
if (ret) {
/* second call failed, we have to try another subtype of the union.
* Unfortunately, since the realtype can change (e.g. in leafref), we are not able to detect
@@ -2519,7 +2503,7 @@
subvalue->value->realtype = type_u->types[u];
ret = type_u->types[u]->plugin->store(ctx, type_u->types[u], value, value_len, options & ~LY_TYPE_OPTS_DYNAMIC,
ly_type_stored_prefixes_clb, subvalue->prefixes, format,
- context_node, trees, subvalue->value, canonized, err);
+ context_node, tree, subvalue->value, canonized, err);
if (ret == LY_SUCCESS || ret == LY_EINCOMPLETE) {
/* success (or not yet complete) */
break;
diff --git a/src/plugins_types.h b/src/plugins_types.h
index bdff1f7..2987382 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -145,8 +145,7 @@
* @param[in] context_node The @p value's node for the case that the require-instance restriction is supposed to be resolved.
* This argument is a lys_node (in case LY_TYPE_OPTS_INCOMPLETE_DATA or LY_TYPE_OPTS_SCHEMA set in @p options)
* or lyd_node structure.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of external data trees (e.g. when validating RPC/Notification)
- * where the required data instance can be placed.
+ * @param[in] tree External data tree (e.g. when validating RPC/Notification) where the required data instance can be placed.
* @param[in,out] storage If LY_TYPE_OPTS_STORE option set, the parsed data are stored into this structure in the type's specific way.
* If the @p canonized differs from the storage's canonized member, the canonized value is also stored here despite the
* LY_TYPE_OPTS_CANONIZE option.
@@ -161,7 +160,7 @@
*/
typedef LY_ERR (*ly_type_store_clb)(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
ly_clb_resolve_prefix resolve_prefix, void *parser, LYD_FORMAT format,
- const void *context_node, const struct lyd_node **trees,
+ const void *context_node, const struct lyd_node *tree,
struct lyd_value *storage, const char **canonized, struct ly_err_item **err);
/**
@@ -335,7 +334,7 @@
* It is never NULL, empty string is represented as "" with zero @p value_len.
* @param[in] value_len Length (number of bytes) of the given \p value.
* @param[in] context_node The @p value's node for the case that the require-instance restriction is supposed to be resolved.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of external data trees (e.g. when validating RPC/Notification) where the required data
+ * @param[in] tree External data tree (e.g. when validating RPC/Notification) where the required data
* instance can be placed.
*
* @param[in] storage Parsed @p value.
@@ -343,7 +342,7 @@
* @return Leafref target node or NULL on error when @p errmsg is always set.
*/
const struct lyd_node *ly_type_find_leafref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len,
- const struct lyd_node *context_node, const struct lyd_node **trees,
+ const struct lyd_node *context_node, const struct lyd_node *tree,
struct lyd_value *storage, char **errmsg);
/**
diff --git a/src/tree_data.c b/src/tree_data.c
index 78529f5..bf3402c 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -42,89 +42,16 @@
size_t key_count;
};
-API void
-lyd_trees_free(const struct lyd_node **trees, int free_data)
-{
- if (!trees) {
- return;
- }
-
- if (free_data) {
- unsigned int u;
- LY_ARRAY_FOR(trees, u) {
- lyd_free_all((struct lyd_node *)trees[u]);
- }
- }
- LY_ARRAY_FREE(trees);
-}
-
-static const struct lyd_node *
-lyd_trees_getstart(const struct lyd_node *tree)
-{
- if (!tree) {
- return NULL;
- }
- while (tree->prev->next) {
- tree = tree->prev;
- }
- return tree;
-}
-
-API const struct lyd_node **
-lyd_trees_new(size_t count, const struct lyd_node *tree, ...)
-{
- LY_ERR ret;
- const struct lyd_node **trees = NULL;
- va_list ap;
-
- LY_CHECK_ARG_RET(NULL, tree, count > 0, NULL);
-
- va_start(ap, tree);
-
- LY_ARRAY_CREATE_GOTO(tree->schema->module->ctx, trees, count, ret, error);
- /* first, mandatory, tree to insert */
- trees[0] = lyd_trees_getstart(tree);
- LY_ARRAY_INCREMENT(trees);
-
- /* variable arguments */
- for (unsigned int u = 1; u < count; ++u) {
- trees[u] = lyd_trees_getstart(va_arg(ap, const struct lyd_node *));
- LY_ARRAY_INCREMENT(trees);
- }
-
- va_end(ap);
- return trees;
-
-error:
- (void)ret; /* unused */
- lyd_trees_free(trees, 1);
- va_end(ap);
- return NULL;
-}
-
-API const struct lyd_node **
-lyd_trees_add(const struct lyd_node **trees, const struct lyd_node *tree)
-{
- const struct lyd_node **t = NULL;
-
- LY_CHECK_ARG_RET(NULL, tree, trees, trees);
-
- LY_ARRAY_NEW_RET(tree->schema->module->ctx, trees, t, NULL);
- *t = lyd_trees_getstart(tree);
-
- return trees;
-}
-
LY_ERR
lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int *dynamic, int second,
- ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node **trees)
+ ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node *tree)
{
LY_ERR ret = LY_SUCCESS;
struct ly_err_item *err = NULL;
struct ly_ctx *ctx;
struct lysc_type *type;
int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
- (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+ (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
assert(node);
ctx = node->schema->module->ctx;
@@ -134,7 +61,7 @@
node->value.realtype = type;
}
ret = type->plugin->store(ctx, type, value, value_len, options, get_prefix, parser, format,
- trees ? (void*)node : (void*)node->schema, trees,
+ tree ? (void *)node : (void *)node->schema, tree,
&node->value, NULL, &err);
if (ret && (ret != LY_EINCOMPLETE)) {
if (err) {
@@ -187,15 +114,15 @@
LY_ERR
lyd_value_parse_attr(struct ly_ctx *ctx, struct lyd_attr *attr, const char *value, size_t value_len, int *dynamic,
int second, ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
- const struct lysc_node *ctx_snode, const struct lyd_node **trees)
+ const struct lysc_node *ctx_snode, const struct lyd_node *tree)
{
LY_ERR ret = LY_SUCCESS;
struct ly_err_item *err = NULL;
struct lyext_metadata *ant;
int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
- (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+ (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
- assert(ctx && attr && ((trees && attr->parent) || ctx_snode));
+ assert(ctx && attr && ((tree && attr->parent) || ctx_snode));
ant = attr->annotation->data;
@@ -203,7 +130,7 @@
attr->value.realtype = ant->type;
}
ret = ant->type->plugin->store(ctx, ant->type, value, value_len, options, get_prefix, parser, format,
- trees ? (void *)attr->parent : (void *)ctx_snode, trees, &attr->value, NULL, &err);
+ tree ? (void *)attr->parent : (void *)ctx_snode, tree, &attr->value, NULL, &err);
if (ret && (ret != LY_EINCOMPLETE)) {
if (err) {
ly_err_print(err);
@@ -256,18 +183,18 @@
API LY_ERR
lyd_value_validate(struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
- ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node **trees)
+ ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node *tree)
{
LY_ERR rc;
struct ly_err_item *err = NULL;
struct lysc_type *type;
- int options = (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+ int options = (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
type = ((struct lysc_node_leaf*)node->schema)->type;
rc = type->plugin->store(ctx ? ctx : node->schema->module->ctx, type, value, value_len, options,
- get_prefix, get_prefix_data, format, trees ? (void*)node : (void*)node->schema, trees,
+ get_prefix, get_prefix_data, format, tree ? (void*)node : (void*)node->schema, tree,
NULL, NULL, &err);
if (rc == LY_EINCOMPLETE) {
return rc;
@@ -287,21 +214,21 @@
API LY_ERR
lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len,
- ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node **trees)
+ ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node *tree)
{
LY_ERR ret = LY_SUCCESS, rc;
struct ly_err_item *err = NULL;
struct ly_ctx *ctx;
struct lysc_type *type;
struct lyd_value data = {0};
- int options = LY_TYPE_OPTS_STORE | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
+ int options = LY_TYPE_OPTS_STORE | (tree ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
LY_CHECK_ARG_RET(node ? node->schema->module->ctx : NULL, node, value, LY_EINVAL);
ctx = node->schema->module->ctx;
type = ((struct lysc_node_leaf*)node->schema)->type;
rc = type->plugin->store(ctx, type, value, value_len, options, get_prefix, get_prefix_data, format, (struct lyd_node*)node,
- trees, &data, NULL, &err);
+ tree, &data, NULL, &err);
if (rc == LY_EINCOMPLETE) {
ret = rc;
/* continue with comparing, just remember what to return if storing is ok */
@@ -605,7 +532,12 @@
memset(keys, 0, sizeof *keys);
- keys->str = strndup(keys_str, keys_len ? keys_len : strlen(keys_str));
+ if (!keys_str) {
+ /* nothing to parse */
+ return LY_SUCCESS;
+ }
+
+ keys->str = strndup(keys_str, keys_len);
LY_CHECK_ERR_GOTO(!keys->str, LOGMEM(list->module->ctx); ret = LY_EMEM, cleanup);
next_key = keys->str;
@@ -686,8 +618,8 @@
/* create and insert all the keys */
for (i = 0; i < keys.key_count; ++i) {
- LY_CHECK_GOTO(ret = lyd_create_term((struct lysc_node *)keys.keys[i].schema, keys.keys[i].value, 0, 0,
- lydjson_resolve_prefix, NULL, LYD_JSON, &key), cleanup);
+ LY_CHECK_GOTO(ret = lyd_create_term((struct lysc_node *)keys.keys[i].schema, keys.keys[i].value,
+ strlen(keys.keys[i].value), NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &key), cleanup);
lyd_insert_node(list, NULL, key);
}
@@ -735,6 +667,10 @@
LY_CHECK_ARG_RET(ctx, parent || module, name, NULL);
+ if (!module) {
+ module = parent->schema->module;
+ }
+
schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYS_CONTAINER | LYS_NOTIF | LYS_ACTION, 0);
LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Inner node \"%s\" not found.", name), NULL);
@@ -756,6 +692,10 @@
LY_CHECK_ARG_RET(ctx, parent || module, name, NULL);
+ if (!module) {
+ module = parent->schema->module;
+ }
+
schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYS_LIST, 0);
LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found.", name), NULL);
@@ -768,7 +708,8 @@
for (key_s = lysc_node_children(schema, 0); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
key_val = va_arg(ap, const char *);
- LY_CHECK_GOTO(rc = lyd_create_term(key_s, key_val, 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &key), cleanup);
+ rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &key);
+ LY_CHECK_GOTO(rc, cleanup);
lyd_insert_node(ret, NULL, key);
}
@@ -797,10 +738,14 @@
LY_CHECK_ARG_RET(ctx, parent || module, name, NULL);
+ if (!module) {
+ module = parent->schema->module;
+ }
+
schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYS_LIST, 0);
LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found.", name), NULL);
- if (!lyd_create_list(schema, keys, 0, &ret) && parent) {
+ if (!lyd_create_list(schema, keys, keys ? strlen(keys) : 0, &ret) && parent) {
lyd_insert_node(parent, NULL, ret);
}
return ret;
@@ -815,10 +760,15 @@
LY_CHECK_ARG_RET(ctx, parent || module, name, NULL);
+ if (!module) {
+ module = parent->schema->module;
+ }
+
schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYD_NODE_TERM, 0);
LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Term node \"%s\" not found.", name), NULL);
- if (!lyd_create_term(schema, val_str, 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &ret) && parent) {
+ if (!lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, NULL, lydjson_resolve_prefix, NULL, LYD_JSON, &ret)
+ && parent) {
lyd_insert_node(parent, NULL, ret);
}
return ret;
@@ -834,6 +784,10 @@
LY_CHECK_ARG_RET(ctx, parent || module, name, NULL);
+ if (!module) {
+ module = parent->schema->module;
+ }
+
schema = lys_find_child(parent ? parent->schema : NULL, module, name, 0, LYD_NODE_ANY, 0);
LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Any node \"%s\" not found.", name), NULL);
@@ -867,7 +821,7 @@
* @param[in] node Node to insert.
*/
static void
-lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node)
+lyd_insert_after_node(struct lyd_node *sibling, struct lyd_node *node)
{
assert(!node->next && (node->prev == node));
@@ -896,7 +850,7 @@
* @param[in] node Node to insert.
*/
static void
-lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node)
+lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node)
{
assert(!node->next && (node->prev == node));
@@ -921,7 +875,7 @@
* @param[in] node Node to insert.
*/
static void
-lyd_insert_last(struct lyd_node *parent, struct lyd_node *node)
+lyd_insert_last_node(struct lyd_node *parent, struct lyd_node *node)
{
struct lyd_node_inner *par;
@@ -956,15 +910,15 @@
/* it is key and we need to insert it at the correct place */
anchor = lyd_get_prev_key_anchor(lyd_node_children(parent), node->schema);
if (anchor) {
- lyd_insert_after(anchor, node);
+ lyd_insert_after_node(anchor, node);
} else if (lyd_node_children(parent)) {
- lyd_insert_before((struct lyd_node *)lyd_node_children(parent), node);
+ lyd_insert_before_node((struct lyd_node *)lyd_node_children(parent), node);
} else {
- lyd_insert_last(parent, node);
+ lyd_insert_last_node(parent, node);
}
} else {
/* last child */
- lyd_insert_last(parent, node);
+ lyd_insert_last_node(parent, node);
}
} else if (*first_sibling) {
/* top-level siblings, find the last one from this module, or simply the last */
@@ -974,7 +928,7 @@
}
/* insert */
- lyd_insert_after(anchor, node);
+ lyd_insert_after_node(anchor, node);
} else {
/* the only sibling */
*first_sibling = node;
@@ -992,6 +946,187 @@
lyd_insert_hash(node);
}
+static LY_ERR
+lyd_insert_check_schema(const struct lysc_node *parent, const struct lysc_node *schema)
+{
+ const struct lysc_node *par2;
+
+ assert(schema);
+
+ /* adjust parent first */
+ while (parent && (parent->nodetype & (LYS_CASE | LYS_CHOICE))) {
+ parent = parent->parent;
+ }
+
+ /* find schema parent */
+ for (par2 = schema->parent; par2 && (par2->nodetype & (LYS_CASE | LYS_CHOICE)); par2 = par2->parent);
+
+ if (parent) {
+ /* inner node */
+ if (par2 != parent) {
+ LOGERR(parent->module->ctx, LY_EINVAL, "Cannot insert, parent of \"%s\" is not \"%s\".", schema->name, parent->name);
+ return LY_EINVAL;
+ }
+ } else {
+ /* top-level node */
+ if (par2) {
+ LOGERR(parent->module->ctx, LY_EINVAL, "Cannot insert, node \"%s\" is not top-level.", schema->name);
+ return LY_EINVAL;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+API LY_ERR
+lyd_insert(struct lyd_node *parent, struct lyd_node *node)
+{
+ struct lyd_node *iter;
+
+ LY_CHECK_ARG_RET(NULL, parent, node, !(parent->schema->nodetype & LYD_NODE_INNER), LY_EINVAL);
+
+ LY_CHECK_RET(lyd_insert_check_schema(parent->schema, node->schema));
+
+ if (node->schema->flags & LYS_KEY) {
+ LOGERR(parent->schema->module->ctx, LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
+ return LY_EINVAL;
+ }
+
+ if (node->parent || node->prev->next) {
+ lyd_unlink_tree(node);
+ }
+
+ while (node) {
+ iter = node->next;
+ lyd_unlink_tree(node);
+ lyd_insert_node(parent, NULL, node);
+ node = iter;
+ }
+ return LY_SUCCESS;
+}
+
+API LY_ERR
+lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node)
+{
+ struct lyd_node *iter;
+
+ LY_CHECK_ARG_RET(NULL, sibling, node, LY_EINVAL);
+
+ LY_CHECK_RET(lyd_insert_check_schema(sibling->schema->parent, node->schema));
+
+ if (node->schema->flags & LYS_KEY) {
+ LOGERR(sibling->schema->module->ctx, LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
+ return LY_EINVAL;
+ } else if (sibling->schema->flags & LYS_KEY) {
+ LOGERR(sibling->schema->module->ctx, LY_EINVAL, "Cannot insert into keys.");
+ return LY_EINVAL;
+ }
+
+ if (node->parent || node->prev->next) {
+ lyd_unlink_tree(node);
+ }
+
+ /* insert in reverse order to get the original order */
+ node = node->prev;
+ while (node) {
+ iter = node->prev;
+ lyd_unlink_tree(node);
+
+ lyd_insert_before_node(sibling, node);
+ /* move the anchor accordingly */
+ sibling = node;
+
+ node = (iter == node) ? NULL : iter;
+ }
+ return LY_SUCCESS;
+}
+
+API LY_ERR
+lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node)
+{
+ struct lyd_node *iter;
+
+ LY_CHECK_ARG_RET(NULL, sibling, node, LY_EINVAL);
+
+ LY_CHECK_RET(lyd_insert_check_schema(sibling->schema->parent, node->schema));
+
+ if (node->schema->flags & LYS_KEY) {
+ LOGERR(sibling->schema->module->ctx, LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name);
+ return LY_EINVAL;
+ } else if (sibling->next && (sibling->next->schema->flags & LYS_KEY)) {
+ LOGERR(sibling->schema->module->ctx, LY_EINVAL, "Cannot insert into keys.");
+ return LY_EINVAL;
+ }
+
+ if (node->parent || node->prev->next) {
+ lyd_unlink_tree(node);
+ }
+
+ while (node) {
+ iter = node->next;
+ lyd_unlink_tree(node);
+
+ lyd_insert_after_node(sibling, node);
+ /* move the anchor accordingly */
+ sibling = node;
+
+ node = iter;
+ }
+ return LY_SUCCESS;
+}
+
+API void
+lyd_unlink_tree(struct lyd_node *node)
+{
+ struct lyd_node *iter;
+
+ if (!node) {
+ return;
+ }
+
+ /* unlink from siblings */
+ if (node->prev->next) {
+ node->prev->next = node->next;
+ }
+ if (node->next) {
+ node->next->prev = node->prev;
+ } else {
+ /* unlinking the last node */
+ if (node->parent) {
+ iter = node->parent->child;
+ } else {
+ iter = node->prev;
+ while (iter->prev != node) {
+ iter = iter->prev;
+ }
+ }
+ /* update the "last" pointer from the first node */
+ iter->prev = node->prev;
+ }
+
+ /* unlink from parent */
+ if (node->parent) {
+ if (node->parent->child == node) {
+ /* the node is the first child */
+ node->parent->child = node->next;
+ }
+
+ lyd_unlink_hash(node);
+
+ /* check for keyless list and update its hash */
+ for (iter = (struct lyd_node *)node->parent; iter; iter = (struct lyd_node *)iter->parent) {
+ if (iter->schema->flags & LYS_KEYLESS) {
+ lyd_hash(iter);
+ }
+ }
+
+ node->parent = NULL;
+ }
+
+ node->next = NULL;
+ node->prev = node;
+}
+
LY_ERR
lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct lys_module *mod, const char *name,
size_t name_len, const char *value, size_t value_len, int *dynamic, ly_clb_resolve_prefix get_prefix,
@@ -1056,14 +1191,14 @@
}
API const struct lyd_node_term *
-lyd_target(struct lyd_value_path *path, const struct lyd_node **trees)
+lyd_target(struct lyd_value_path *path, const struct lyd_node *tree)
{
- unsigned int u, v, x;
+ unsigned int u, x;
const struct lyd_node *parent = NULL, *start_search;
struct lyd_node *node = NULL;
uint64_t pos = 1;
- LY_CHECK_ARG_RET(NULL, path, trees, NULL);
+ LY_CHECK_ARG_RET(NULL, path, tree, NULL);
LY_ARRAY_FOR(path, u) {
if (parent) {
@@ -1071,15 +1206,10 @@
search_inner:
lyd_find_sibling_next(start_search, path[u].node->module, path[u].node->name, 0, NULL, 0, &node);
} else {
- LY_ARRAY_FOR(trees, v) {
- start_search = trees[v];
+ start_search = tree;
search_toplevel:
- /* WARNING! to use search_toplevel label correctly, variable v must be preserved and not changed! */
- lyd_find_sibling_next(start_search, path[u].node->module, path[u].node->name, 0, NULL, 0, &node);
- if (node) {
- break;
- }
- }
+ /* WARNING! to use search_toplevel label correctly, variable v must be preserved and not changed! */
+ lyd_find_sibling_next(start_search, path[u].node->module, path[u].node->name, 0, NULL, 0, &node);
}
if (!node) {
return NULL;
@@ -1498,7 +1628,7 @@
if (top) {
lyd_free_tree(top);
} else {
- lyd_free_withsiblings(first);
+ lyd_free_siblings(first);
}
return NULL;
}
@@ -1864,7 +1994,7 @@
struct lyd_node **match_p;
struct lyd_node_inner *parent;
- LY_CHECK_ARG_RET(NULL, target, match, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, target, LY_EINVAL);
if (!siblings) {
/* no data */
@@ -2101,6 +2231,10 @@
return LY_ENOTFOUND;
}
+ if (key_or_value && !val_len) {
+ val_len = strlen(key_or_value);
+ }
+
/* create data node if needed and find it */
switch (schema->nodetype) {
case LYS_CONTAINER:
diff --git a/src/tree_data.h b/src/tree_data.h
index 0bb4e8b..4943b49 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -309,7 +309,7 @@
};
/**
- * @brief Data node structure for the terminal data tree nodes - leafs and leaf-lists.
+ * @brief Data node structure for the terminal data tree nodes - leaves and leaf-lists.
*/
struct lyd_node_term {
uint32_t hash; /**< hash of this particular node (module name + schema name + key string values if list or
@@ -517,10 +517,21 @@
struct lyd_node *lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options);
/**
+ * @brief Fully validate a data tree.
+ *
+ * @param[in] ctx libyang context. Can be NULL if @p node is set.
+ * @param[in,out] node Root data tree node to recursively validate. May be changed by validation.
+ * @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LY_ERR lyd_validate(const struct ly_ctx *ctx, struct lyd_node **node, int val_opts);
+
+/**
* @brief Create a new inner node in a data tree.
*
* @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
- * @param[in] module Module with the node being created.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
* @param[in] name Schema node name of the new data node. The node can be #LYS_CONTAINER, #LYS_NOTIF, #LYS_RPC, or #LYS_ACTION.
* @return New created node.
* @return NULL on error.
@@ -531,7 +542,7 @@
* @brief Create a new list node in a data tree.
*
* @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
- * @param[in] module Module with the node being created.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
* @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
* @param[in] ... Ordered key values of the new list instance, all must be set. In case of an instance-identifier
* or identityref value, the JSON format is expected (module names instead of prefixes).
@@ -544,7 +555,7 @@
* @brief Create a new list node in a data tree.
*
* @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
- * @param[in] module Module with the node being created.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
* @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
* @param[in] keys All key values predicate in the form of "[key1='val1'][key2='val2']...", they do not have to be ordered.
* In case of an instance-identifier or identityref value, the JSON format is expected (module names instead of prefixes).
@@ -557,7 +568,7 @@
* @brief Create a new term node in a data tree.
*
* @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
- * @param[in] module Module with the node being created.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
* @param[in] name Schema node name of the new data node. The node can be #LYS_LEAF or #LYS_LEAFLIST.
* @param[in] val_str String form of the value of the node being created. In case of an instance-identifier or identityref
* value, the JSON format is expected (module names instead of prefixes).
@@ -570,7 +581,7 @@
* @brief Create a new any node in a data tree.
*
* @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
- * @param[in] module Module with the node being created.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
* @param[in] name Schema node name of the new data node. The node can be #LYS_ANYDATA or #LYS_ANYXML.
* @param[in] value Value to be directly assigned to the node. Expected type is determined by @p value_type.
* @param[in] value_type Type of the provided value in @p value.
@@ -581,6 +592,52 @@
const void *value, LYD_ANYDATA_VALUETYPE value_type);
/**
+ * @brief Insert a child into a parent. It is inserted as the last child.
+ *
+ * - if the node is part of some other tree, it is automatically unlinked.
+ * - if the node is the first node of a node list (with no parent), all the subsequent nodes are also inserted.
+ *
+ * @param[in] parent Parent node to insert into.
+ * @param[in] node Node to insert.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LY_ERR lyd_insert(struct lyd_node *parent, struct lyd_node *node);
+
+/**
+ * @brief Insert a node before another node that is its schema sibling.
+ *
+ * - if the node is part of some other tree, it is automatically unlinked.
+ * - if the node is the first node of a node list (with no parent), all the subsequent nodes are also inserted.
+ *
+ * @param[in] sibling Sibling node to insert before.
+ * @param[in] node Node to insert.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LY_ERR lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node);
+
+/**
+ * @brief Insert a node after another node that is its schema sibling.
+ *
+ * - if the node is part of some other tree, it is automatically unlinked.
+ * - if the node is the first node of a node list (with no parent), all the subsequent nodes are also inserted.
+ *
+ * @param[in] sibling Sibling node to insert after.
+ * @param[in] node Node to insert.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LY_ERR lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node);
+
+/**
+ * @brief Unlink the specified data subtree.
+ *
+ * @param[in] node Data tree node to be unlinked (together with all the children).
+ */
+void lyd_unlink_tree(struct lyd_node *node);
+
+/**
* @brief Free all the nodes (even parents of the node) in the data tree.
*
* @param[in] node Any of the nodes inside the tree.
@@ -592,33 +649,16 @@
*
* @param[in] node Any of the sibling nodes to free.
*/
-void lyd_free_withsiblings(struct lyd_node *node);
+void lyd_free_siblings(struct lyd_node *node);
/**
* @brief Free (and unlink) the specified data (sub)tree.
*
- * __PARTIAL CHANGE__ - validate after the final change on the data tree (see @ref howtodatamanipulators).
- *
* @param[in] node Root of the (sub)tree to be freed.
*/
void lyd_free_tree(struct lyd_node *node);
/**
- * @brief Unlink the specified data subtree. All referenced namespaces are copied.
- *
- * Note, that the node's connection with the schema tree is kept. Therefore, in case of
- * reconnecting the node to a data tree using lyd_paste() it is necessary to paste it
- * to the appropriate place in the data tree following the schema.
- *
- * __PARTIAL CHANGE__ - validate after the final change on the data tree (see @ref howtodatamanipulators).
- *
- * @param[in] node Data tree node to be unlinked (together with all children).
- * @return LY_SUCCESS for success
- * @return LY_E* values in case of error
- */
-LY_ERR lyd_unlink_tree(struct lyd_node *node);
-
-/**
* @brief Destroy data attribute.
*
* @param[in] ctx Context where the attribute was created.
@@ -629,33 +669,6 @@
void lyd_free_attr(struct ly_ctx *ctx, struct lyd_attr *attr, int recursive);
/**
- * @brief Prepare ([sized array](@ref sizedarrays)) of data trees required by various (mostly validation) functions.
- *
- * @param[in] count Number of trees to include (including the mandatory @p tree).
- * @param[in] tree First (and mandatory) tree to be included into the resulting ([sized array](@ref sizedarrays)).
- * @return NULL in case of memory allocation failure or invalid argument, prepared ([sized array](@ref sizedarrays)) otherwise.
- */
-const struct lyd_node **lyd_trees_new(size_t count, const struct lyd_node *tree, ...);
-
-/**
- * @brief Add tree into the ([sized array](@ref sizedarrays)) of data trees created by lyd_trees_new(),
- *
- * @param[in] trees Existing [sized array](@ref sizedarrays)) of data trees to be extended.
- * @param[in] tree Data tree to be included into the provided @p trees ([sized array](@ref sizedarrays)).
- * @return NULL in case of memory allocation failure or invalid argument, extended @p trees ([sized array](@ref sizedarrays)) otherwise.
- */
-const struct lyd_node **lyd_trees_add(const struct lyd_node **trees, const struct lyd_node *tree);
-
-/**
- * @brief Free the trees ([sized array](@ref sizedarrays)).
- *
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees.
- * @param[in] free_data Flag to free also the particular trees in the @p trees ([sized array](@ref sizedarrays)).
- * If set to zero, only the trees envelope is freed and data are untouched.
- */
-void lyd_trees_free(const struct lyd_node **trees, int free_data);
-
-/**
* @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
*
* The given node is not modified in any way - it is just checked if the @p value can be set to the node.
@@ -670,15 +683,15 @@
* @param[in] get_prefix Callback function to resolve prefixes used in the @p value string.
* @param[in] get_prefix_data Private data for the @p get_prefix callback.
* @param[in] format Input format of the data.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
- * data instance (leafref target, instance-identifier) can be placed. NULL in case the data tree are not yet complete,
- * then LY_EINCOMPLETE can be returned. To simply prepare this structure, use lyd_trees_new().
+ * @param[in] tree Data tree (e.g. when validating RPC/Notification) where the required data instance (leafref target,
+ * instance-identifier) can be placed. NULL in case the data tree is not yet complete,
+ * then LY_EINCOMPLETE can be returned.
* @return LY_SUCCESS on success
* @return LY_EINCOMPLETE in case the @p trees is not provided and it was needed to finish the validation (e.g. due to require-instance).
* @return LY_ERR value if an error occurred.
*/
LY_ERR lyd_value_validate(struct ly_ctx *ctx, const struct lyd_node_term *node, const char *value, size_t value_len,
- ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node **trees);
+ ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node *tree);
/**
* @brief Compare the node's value with the given string value. The string value is first validated according to the node's type.
@@ -690,17 +703,16 @@
* @param[in] get_prefix Callback function to resolve prefixes used in the @p value string.
* @param[in] get_prefix_data Private data for the @p get_prefix callback.
* @param[in] format Input format of the data.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
- * data instance (leafref target, instance-identifier) can be placed. NULL in case the data tree are not yet complete,
- * then LY_EINCOMPLETE can be returned in case the validation was not completed, but values matches. To simply prepare
- * this structure, use lyd_trees_new(). To simply prepare this structure, use lyd_trees_new().
+ * @param[in] tree Data tree (e.g. when validating RPC/Notification) where the required data instance (leafref target,
+ * instance-identifier) can be placed. NULL in case the data tree is not yet complete,
+ * then LY_EINCOMPLETE can be returned.
* @return LY_SUCCESS on success
* @return LY_EINCOMPLETE in case of success when the @p trees is not provided and it was needed to finish the validation of
* the given string @p value (e.g. due to require-instance).
* @return LY_ERR value if an error occurred.
*/
LY_ERR lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t value_len,
- ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node **trees);
+ ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format, const struct lyd_node *tree);
/**
* @defgroup datacompareoptions Data compare options
@@ -773,11 +785,10 @@
* @brief Resolve instance-identifier defined by lyd_value_path structure.
*
* @param[in] path Path structure specifying the instance-identifier target.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees to be searched.
- * To simply prepare this structure, use lyd_trees_new().
- * @return Target node of the instance-identifier present in the given data @p trees.
+ * @param[in] tree Data tree to be searched.
+ * @return Target node of the instance-identifier present in the given data @p tree.
*/
-const struct lyd_node_term *lyd_target(struct lyd_value_path *path, const struct lyd_node **trees);
+const struct lyd_node_term *lyd_target(struct lyd_value_path *path, const struct lyd_node *tree);
/**
* @brief Get string value of a term data \p node.
@@ -824,7 +835,7 @@
* @param[in] first Starting sibling node for search, only succeeding ones are searched.
* @param[in] module Module of the node to find.
* @param[in] name Name of the node to find.
- * @param[in] name_len Optional length of the @p name argument in case it is not NULL-terminated string.
+ * @param[in] name_len Optional length of @p name in case it is not 0-terminated string.
* @param[in] key_or_value Expected value depends on the type of @p name node:
* LYS_CONTAINER:
* LYS_ANYXML:
@@ -843,7 +854,7 @@
* Note that any explicit values (leaf, leaf-list or list key values) will be canonized first
* before comparison. But values that do not have a canonical value are expected to be in the
* JSON format!
- * @param[in] val_len Optional length of the @p key_or_value argument in case it is not NULL-terminated string.
+ * @param[in] val_len Optional length of @p key_or_value in case it is not 0-terminated string.
* @param[out] match Found data node.
* @return LY_SUCCESS on success, @p match set.
* @return LY_ENOTFOUND if not found, @p match set to NULL.
@@ -903,6 +914,7 @@
* Note that any explicit values (leaf-list or list key values) will be canonized first
* before comparison. But values that do not have a canonical value are expected to be in the
* JSON format!
+ * @param[in] val_len Optional length of @p key_or_value in case it is not 0-terminated.
* @param[out] match Can be NULL, otherwise the found data node.
* @return LY_SUCCESS on success, @p match set.
* @return LY_ENOTFOUND if not found, @p match set to NULL.
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index 9ff54c4..aea9f7b 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -42,54 +42,6 @@
LY_ARRAY_FREE(path);
}
-API LY_ERR
-lyd_unlink_tree(struct lyd_node *node)
-{
- struct lyd_node *iter;
- struct lyd_node **first_sibling;
-
- LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
-
- if (node->parent) {
- first_sibling = lyd_node_children_p((struct lyd_node*)node->parent);
- }
-
- /* unlink from siblings */
- if (node->prev->next) {
- node->prev->next = node->next;
- }
- if (node->next) {
- node->next->prev = node->prev;
- } else {
- /* unlinking the last node */
- if (node->parent) {
- iter = *first_sibling;
- } else {
- iter = node->prev;
- while (iter->prev != node) {
- iter = iter->prev;
- }
- }
- /* update the "last" pointer from the first node */
- iter->prev = node->prev;
- }
-
- /* unlink from parent */
- if (node->parent) {
- if (*first_sibling == node) {
- /* the node is the first child */
- *first_sibling = node->next;
- }
- lyd_unlink_hash(node);
- node->parent = NULL;
- }
-
- node->next = NULL;
- node->prev = node;
-
- return EXIT_SUCCESS;
-}
-
API void
lyd_free_attr(struct ly_ctx *ctx, struct lyd_attr *attr, int recursive)
{
@@ -222,7 +174,7 @@
}
API void
-lyd_free_withsiblings(struct lyd_node *node)
+lyd_free_siblings(struct lyd_node *node)
{
lyd_free_(node, 0);
}
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 7cd5d34..4da2d49 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -34,7 +34,7 @@
*
* @param[in] schema Schema node of the new data node.
* @param[in] value String value to be parsed.
- * @param[in] value_len Length of @p value.
+ * @param[in] value_len Length of @p value, must be set correctly.
* @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
* @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
* @param[in] prefix_data User data for @p get_prefix.
@@ -82,7 +82,7 @@
* @param[in] schema Schema node of the new data node.
* @param[in] keys_str List instance key values in the form of "[key1='val1'][key2='val2']...".
* The keys do not have to be ordered but all of them must be set.
- * @param[in] keys_len Length of @p keys_str.
+ * @param[in] keys_len Length of @p keys_str, must be set correctly.
* @param[out] node Created node.
* @return LY_SUCCESS on success.
* @return LY_ERR value if an error occurred.
@@ -132,9 +132,9 @@
* @param[in,out] attr Attribute list to add at its end if @p parent is NULL, returned created attribute.
* @param[in] mod Attribute module (with the annotation definition).
* @param[in] name Attribute name.
- * @param[in] name_len Length of @p name.
+ * @param[in] name_len Length of @p name, must be set correctly.
* @param[in] value String value to be parsed.
- * @param[in] value_len Length of @p value.
+ * @param[in] value_len Length of @p value, must be set correctly.
* @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
* @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
* @param[in] prefix_data User data for @p get_prefix.
@@ -153,13 +153,13 @@
*
* @param[in] node Data node for the @p value.
* @param[in] value String value to be parsed, must not be NULL.
- * @param[in] value_len Length of the give @p value (mandatory).
+ * @param[in] value_len Length of the give @p value, must be set correctly.
* @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
* @param[in] second Flag for the second call after returning LY_EINCOMPLETE
* @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
* @param[in] parser Parser's data for @p get_prefix
* @param[in] format Input format of @p value.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
+ * @param[in] tree Data tree (e.g. when validating RPC/Notification) where the required
* data instance (leafref target, instance-identifier) can be placed. NULL in case the data tree are not yet complete,
* then LY_EINCOMPLETE can be returned.
* @return LY_SUCCESS on success
@@ -167,7 +167,7 @@
* @return LY_ERR value if an error occurred.
*/
LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int *dynamic, int second,
- ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node **trees);
+ ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node *tree);
/**
* @brief Validate, canonize and store the given @p value into the attribute according to the metadata annotation type's rules.
@@ -175,14 +175,14 @@
* @param[in] ctx libyang context.
* @param[in] attr Data attribute for the @p value.
* @param[in] value String value to be parsed, must not be NULL.
- * @param[in] value_len Length of the give @p value (mandatory).
+ * @param[in] value_len Length of the give @p value, must be set correctly.
* @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
* @param[in] second Flag for the second call after returning LY_EINCOMPLETE
* @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
* @param[in] parser Parser's data for @p get_prefix
* @param[in] format Input format of the data.
* @param[in] ctx_snode Context node for value resolution in schema.
- * @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
+ * @param[in] tree Data tree (e.g. when validating RPC/Notification) where the required
* data instance (leafref target, instance-identifier) can be placed. NULL in case the data tree are not yet complete,
* then LY_EINCOMPLETE can be returned.
* @return LY_SUCCESS on success
@@ -191,7 +191,7 @@
*/
LY_ERR lyd_value_parse_attr(struct ly_ctx *ctx, struct lyd_attr *attr, const char *value, size_t value_len, int *dynamic,
int second, ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
- const struct lysc_node *ctx_snode, const struct lyd_node **trees);
+ const struct lysc_node *ctx_snode, const struct lyd_node *tree);
/**
* @brief Parse XML string as YANG data tree.
diff --git a/src/validation.c b/src/validation.c
index 97f02d6..96246cd 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -20,16 +20,67 @@
#include "tree_data_internal.h"
#include "tree_schema_internal.h"
+static struct lyd_node *
+lys_getnext_data(const struct lyd_node *last, const struct lyd_node *sibling, const struct lysc_node **slast,
+ const struct lysc_node *parent, const struct lysc_module *module)
+{
+ const struct lysc_node *siter = NULL;
+ struct lyd_node *match = NULL;
+
+ assert(parent || module);
+ assert(!last || (slast && *slast));
+
+ if (slast) {
+ siter = *slast;
+ }
+
+ if (last && last->next) {
+ /* find next data instance */
+ lyd_find_sibling_next2(last->next, siter, NULL, 0, &match);
+ if (match) {
+ return match;
+ }
+ }
+
+ /* find next schema node data instance */
+ while ((siter = lys_getnext(siter, parent, module, 0))) {
+ switch (siter->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ case LYS_LEAF:
+ lyd_find_sibling_val(sibling, siter, NULL, 0, &match);
+ break;
+ case LYS_LIST:
+ case LYS_LEAFLIST:
+ lyd_find_sibling_next2(sibling, siter, NULL, 0, &match);
+ break;
+ default:
+ assert(0);
+ LOGINT(NULL);
+ }
+
+ if (match) {
+ break;
+ }
+ }
+
+ if (slast) {
+ *slast = siter;
+ }
+ return match;
+}
+
/**
* @brief Evaluate a single "when" condition.
*
* @param[in] when When to evaluate.
* @param[in] node Node whose existence depends on this when.
- * @param[in] trees Array of all data trees.
+ * @param[in] tree Data tree.
* @return LY_ERR value (LY_EINCOMPLETE if a referenced node does not have its when evaluated)
*/
static LY_ERR
-lyd_val_when(struct lysc_when *when, struct lyd_node *node, const struct lyd_node **trees)
+lyd_val_when(struct lysc_when *when, struct lyd_node *node, const struct lyd_node *tree)
{
LY_ERR ret;
const struct lyd_node *ctx_node;
@@ -46,7 +97,7 @@
/* evaluate when */
ret = lyxp_eval(when->cond, LYD_UNKNOWN, when->module, ctx_node, ctx_node ? LYXP_NODE_ELEM : LYXP_NODE_ROOT_CONFIG,
- trees, &xp_set, LYXP_SCHEMA);
+ tree, &xp_set, LYXP_SCHEMA);
lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
/* return error or LY_EINCOMPLETE for dependant unresolved when */
@@ -72,7 +123,7 @@
LY_ERR
lyd_validate_unres(struct ly_set *node_types, struct ly_set *attr_types, struct ly_set *node_when, LYD_FORMAT format,
- ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node **trees)
+ ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node *tree)
{
LY_ERR ret = LY_SUCCESS;
uint32_t u;
@@ -83,7 +134,7 @@
/* validate and store the value of the node */
ret = lyd_value_parse(node, node->value.original, strlen(node->value.original), 0, 1, get_prefix_clb,
- parser_data, format, trees);
+ parser_data, format, tree);
LY_CHECK_RET(ret);
}
@@ -93,7 +144,7 @@
/* validate and store the value of the node */
ret = lyd_value_parse_attr(attr->parent->schema->module->ctx, attr, attr->value.original,
- strlen(attr->value.original), 0, 1, get_prefix_clb, parser_data, format, NULL, trees);
+ strlen(attr->value.original), 0, 1, get_prefix_clb, parser_data, format, NULL, tree);
LY_CHECK_RET(ret);
}
@@ -116,7 +167,7 @@
do {
uint32_t i;
LY_ARRAY_FOR(schema->when, i) {
- ret = lyd_val_when(schema->when[i], node, trees);
+ ret = lyd_val_when(schema->when[i], node, tree);
if (ret) {
break;
}
@@ -160,47 +211,12 @@
return ly_ctx_get_module_iter(ctx, i);
}
-static int
-lyd_val_has_choice_data(const struct lysc_node *snode, struct lyd_node *sibling)
-{
- const struct lysc_node *iter = NULL;
-
- assert(snode->nodetype == LYS_CHOICE);
-
- while ((iter = lys_getnext(iter, snode, NULL, 0))) {
- switch (iter->nodetype) {
- case LYS_CONTAINER:
- case LYS_ANYXML:
- case LYS_ANYDATA:
- case LYS_LEAF:
- if (!lyd_find_sibling_val(sibling, iter, NULL, 0, NULL)) {
- /* one case child data instance found */
- return 1;
- }
- break;
- case LYS_LIST:
- case LYS_LEAFLIST:
- if (!lyd_find_sibling_next2(sibling, iter, NULL, 0, NULL)) {
- /* one case child data instance found */
- return 1;
- }
- break;
- default:
- assert(0);
- LOGINT(snode->module->ctx);
- return 0;
- }
- }
-
- return 0;
-}
-
static LY_ERR
lyd_validate_mandatory(const struct lysc_node *snode, struct lyd_node *sibling)
{
if (snode->nodetype == LYS_CHOICE) {
/* some data of a choice case exist */
- if (lyd_val_has_choice_data(snode, sibling)) {
+ if (lys_getnext_data(NULL, sibling, NULL, snode, NULL)) {
return LY_SUCCESS;
}
} else {
@@ -501,15 +517,70 @@
}
static LY_ERR
-lyd_validate_cases(const struct lysc_node_case *cases, struct lyd_node *sibling)
+lyd_validate_cases(const struct lysc_node_choice *choic, struct lyd_node **sibling)
{
- /* TODO check there are nodes only from a single case,
- * what if not? validation error or autodelete */
+ const struct lysc_node *scase, *iter, *old_case = NULL, *new_case = NULL;
+ struct lyd_node *match, *to_del;
+ int found;
+
+ LY_LIST_FOR((struct lysc_node *)choic->cases, scase) {
+ found = 0;
+ iter = NULL;
+ match = NULL;
+ while ((match = lys_getnext_data(match, *sibling, &iter, scase, NULL))) {
+ if (match->flags & LYD_NEW) {
+ /* a new case data found, nothing more to look for */
+ found = 2;
+ break;
+ } else {
+ /* and old case data found */
+ if (found == 0) {
+ found = 1;
+ }
+ }
+ }
+
+ if (found == 1) {
+ /* there cannot be 2 old cases */
+ assert(!old_case);
+
+ /* remember an old existing case */
+ old_case = scase;
+ } else if (found == 2) {
+ if (new_case) {
+ /* new data from 2 cases */
+ LOGVAL(choic->module->ctx, LY_VLOG_LYSC, choic, LY_VCODE_DUPCASE, new_case->name, scase->name);
+ return LY_EVALID;
+ }
+
+ /* remember a new existing case */
+ new_case = scase;
+ }
+ }
+
+ if (old_case && new_case) {
+ /* auto-delete old case */
+ iter = NULL;
+ match = NULL;
+ to_del = NULL;
+ while ((match = lys_getnext_data(match, *sibling, &iter, old_case, NULL))) {
+ if ((*sibling == to_del) && !(*sibling)->parent) {
+ *sibling = (*sibling)->next;
+ }
+ lyd_free_tree(to_del);
+ to_del = match;
+ }
+ if ((*sibling == to_del) && !(*sibling)->parent) {
+ *sibling = (*sibling)->next;
+ }
+ lyd_free_tree(to_del);
+ }
+
return LY_SUCCESS;
}
static LY_ERR
-lyd_validate_siblings_schema_r(struct lyd_node *sibling, const struct lysc_node *sparent, const struct lysc_module *mod,
+lyd_validate_siblings_schema_r(struct lyd_node **sibling, const struct lysc_node *sparent, const struct lysc_module *mod,
int options)
{
const struct lysc_node *snode = NULL;
@@ -521,25 +592,25 @@
if (snode->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
slist = (struct lysc_node_list *)snode;
if (slist->min || slist->max) {
- LY_CHECK_RET(lyd_validate_minmax(snode, slist->min, slist->max, sibling));
+ LY_CHECK_RET(lyd_validate_minmax(snode, slist->min, slist->max, *sibling));
}
/* check generic mandatory existence */
} else if (snode->flags & LYS_MAND_TRUE) {
- LY_CHECK_RET(lyd_validate_mandatory(snode, sibling));
+ LY_CHECK_RET(lyd_validate_mandatory(snode, *sibling));
}
/* check unique */
if (snode->nodetype == LYS_LIST) {
slist = (struct lysc_node_list *)snode;
if (slist->uniques) {
- LY_CHECK_RET(lyd_validate_unique(snode, slist->uniques, sibling));
+ LY_CHECK_RET(lyd_validate_unique(snode, slist->uniques, *sibling));
}
}
/* check case duplicites */
if (snode->nodetype == LYS_CHOICE) {
- LY_CHECK_RET(lyd_validate_cases(((struct lysc_node_choice *)snode)->cases, sibling));
+ LY_CHECK_RET(lyd_validate_cases((struct lysc_node_choice *)snode, sibling));
}
if (snode->nodetype & (LYS_CHOICE | LYS_CASE)) {
@@ -552,50 +623,115 @@
}
static LY_ERR
-lyd_validate_siblings_r(struct lyd_node *sibling, const struct lysc_node *sparent, const struct lysc_module *mod, int options)
+lyd_validate_dup_nodes(struct lyd_node *sibling, struct lyd_node *node)
{
- struct lyd_node *node;
+ struct lyd_node **match_p;
+ int fail = 0;
+
+ if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (node->schema->flags & LYS_CONFIG_R)) {
+ /* duplicate instances allowed */
+ return LY_SUCCESS;
+ }
+
+ /* find exactly the same next instance using hashes if possible */
+ if (node->parent && node->parent->children_ht) {
+ if (!lyht_find_next(node->parent->children_ht, &node, node->hash, (void **)&match_p)) {
+ fail = 1;
+ }
+ } else {
+ for (; sibling; sibling = sibling->next) {
+ if (sibling == node) {
+ continue;
+ }
+
+ if (node->schema->nodetype & (LYD_NODE_ANY | LYS_LEAF)) {
+ if (sibling->schema == node->schema) {
+ fail = 1;
+ break;
+ }
+ } else if (!lyd_compare(sibling, node, 0)) {
+ fail = 1;
+ break;
+ }
+ }
+ }
+
+ if (fail) {
+ LOGVAL(node->schema->module->ctx, LY_VLOG_LYD, node, LY_VCODE_DUP, node->schema->name);
+ return LY_EVALID;
+ }
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lyd_validate_siblings_r(struct lyd_node **sibling, const struct lysc_node *sparent, const struct lysc_module *mod, int options)
+{
+ struct lyd_node *next, *node, *match;
+
+ assert(sibling);
/* validate all restrictions of nodes themselves */
- LY_LIST_FOR(sibling, node) {
- /* TODO node instance duplicities */
+ LY_LIST_FOR_SAFE(*sibling, next, node) {
+ if (node->flags & LYD_NEW) {
+ /* new node instance duplicities */
+ LY_CHECK_RET(lyd_validate_dup_nodes(*sibling, node));
+ } else if (node->flags & LYD_DEFAULT) {
+ /* remove default if there is an explicit instance */
+ assert(node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_CONTAINER));
+ if (node->schema->nodetype == LYS_LEAFLIST) {
+ lyd_find_sibling_next2(*sibling, node->schema, NULL, 0, &match);
+ } else {
+ lyd_find_sibling_val(*sibling, node->schema, NULL, 0, &match);
+ }
+ while (match && (match->flags & LYD_DEFAULT) && !(match->flags & LYD_NEW)) {
+ lyd_find_sibling_next2(match->next, node->schema, NULL, 0, &match);
+ }
+
+ if (match) {
+ /* non-default (or at least new for container) instance found, remove the default one */
+ if ((*sibling == node) && !(*sibling)->parent) {
+ *sibling = (*sibling)->next;
+ }
+ lyd_free_tree(node);
+ continue;
+ }
+ }
+
/* TODO node's must */
/* TODO node status */
/* TODO node's if-features */
- /* TODO node list keys */
+ /* TODO list all keys existence */
/* node value including if-feature is checked by plugins */
}
/* validate schema-based restrictions */
LY_CHECK_RET(lyd_validate_siblings_schema_r(sibling, sparent, mod, options));
- LY_LIST_FOR(sibling, node) {
+ LY_LIST_FOR(*sibling, node) {
/* this sibling is valid */
node->flags &= ~LYD_NEW;
/* validate all children recursively */
- LY_CHECK_RET(lyd_validate_siblings_r((struct lyd_node *)lyd_node_children(node), node->schema, mod, options));
+ if (node->schema->nodetype & LYD_NODE_INNER) {
+ LY_CHECK_RET(lyd_validate_siblings_r(lyd_node_children_p(node), node->schema, mod, options));
+ }
}
return LY_SUCCESS;
}
LY_ERR
-lyd_validate_data(const struct lyd_node **trees, const struct lys_module **modules, int mod_count, struct ly_ctx *ctx,
+lyd_validate_data(struct lyd_node **tree, const struct lys_module **modules, int mod_count, struct ly_ctx *ctx,
int val_opts)
{
- uint32_t i = 0, j;
+ uint32_t i = 0;
const struct lys_module *mod;
- struct lyd_node *tree;
if (val_opts & LYD_VALOPT_DATA_ONLY) {
- if (trees) {
- for (j = 0; j < LY_ARRAY_SIZE(trees); ++j) {
- tree = (struct lyd_node *)trees[j];
-
- /* validate all top-level nodes and then inner nodes recursively */
- LY_CHECK_RET(lyd_validate_siblings_r(tree, NULL, tree->schema->module->compiled, val_opts));
- }
+ if (*tree) {
+ /* TODO all modules, not just first */
+ /* validate all top-level nodes and then inner nodes recursively */
+ LY_CHECK_RET(lyd_validate_siblings_r(tree, NULL, (*tree)->schema->module->compiled, val_opts));
}
} else {
while ((mod = lyd_val_next_module(modules, mod_count, ctx, &i))) {
@@ -603,17 +739,6 @@
continue;
}
- /* find data of this module, if any */
- tree = NULL;
- if (trees) {
- for (j = 0; j < LY_ARRAY_SIZE(trees); ++j) {
- if (trees[j]->schema->module == mod) {
- tree = (struct lyd_node *)trees[j];
- break;
- }
- }
- }
-
/* validate all top-level nodes and then inner nodes recursively */
LY_CHECK_RET(lyd_validate_siblings_r(tree, NULL, mod->compiled, val_opts));
}
@@ -636,7 +761,7 @@
while ((iter = lys_getnext(iter, schema, mod, LYS_GETNEXT_WITHCHOICE))) {
switch (iter->nodetype) {
case LYS_CHOICE:
- if (((struct lysc_node_choice *)iter)->dflt && !lyd_val_has_choice_data(iter, *first)) {
+ if (((struct lysc_node_choice *)iter)->dflt && !lys_getnext_data(NULL, *first, NULL, iter, NULL)) {
/* create default case data */
LY_CHECK_RET(lyd_validate_defaults_r(parent, first, (struct lysc_node *)((struct lysc_node_choice *)iter)->dflt,
NULL, node_types, node_when));
@@ -726,3 +851,58 @@
return LY_SUCCESS;
}
+
+API LY_ERR
+lyd_validate(const struct ly_ctx *ctx, struct lyd_node **tree, int val_opts)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *root, *next, *elem;
+ const struct lyd_attr *attr;
+ struct ly_set type_check = {0}, type_attr_check = {0}, when_check = {0};
+
+ LY_CHECK_ARG_RET(ctx, tree, ctx || *tree, LY_EINVAL);
+
+ if (!ctx) {
+ ctx = (*tree)->schema->module->ctx;
+ }
+
+ /* add nested defaults, collect all types and nodes with when condition */
+ LY_LIST_FOR(*tree, root) {
+ LYD_TREE_DFS_BEGIN(root, next, elem) {
+ LY_LIST_FOR(elem->attr, attr) {
+ ly_set_add(&type_attr_check, (void *)attr, LY_SET_OPT_USEASLIST);
+ }
+ if (elem->schema->nodetype & LYD_NODE_TERM) {
+ ly_set_add(&type_check, (void *)elem, LY_SET_OPT_USEASLIST);
+ } else if (elem->schema->nodetype & LYD_NODE_INNER) {
+ ret = lyd_validate_defaults_r((struct lyd_node_inner *)elem, lyd_node_children_p((struct lyd_node *)elem),
+ elem->schema, NULL, &type_check, &when_check);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ if (!(elem->schema->nodetype & (LYS_ACTION | LYS_NOTIF)) && elem->schema->when) {
+ ly_set_add(&when_check, (void *)elem, LY_SET_OPT_USEASLIST);
+ }
+
+ LYD_TREE_DFS_END(root, next, elem);
+ }
+ }
+
+ /* add top-level default nodes */
+ ret = lyd_validate_defaults_top(tree, NULL, 0, (struct ly_ctx *)ctx, &type_check, &when_check, val_opts);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* finish incompletely validated terminal values/attributes and when conditions */
+ ret = lyd_validate_unres(&type_check, &type_attr_check, &when_check, LYD_JSON, lydjson_resolve_prefix, NULL,
+ (const struct lyd_node *)tree);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* context node and other validation tasks that depend on other data nodes */
+ ret = lyd_validate_data(tree, NULL, 0, (struct ly_ctx *)ctx, val_opts);
+ LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+ ly_set_erase(&type_check, NULL);
+ ly_set_erase(&type_attr_check, NULL);
+ ly_set_erase(&when_check, NULL);
+ return ret;
+}
diff --git a/src/validation.h b/src/validation.h
index dfc81d0..d8b62a4 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -27,23 +27,23 @@
* @param[in] format Format of the unresolved data.
* @param[in] get_prefix_clb Format-specific getter to resolve prefixes.
* @param[in] parser_data Parser's data for @p get_prefix_clb.
- * @param[in] trees Array of all data trees.
+ * @param[in] tree Data tree.
* @return LY_ERR value.
*/
LY_ERR lyd_validate_unres(struct ly_set *node_types, struct ly_set *attr_types, struct ly_set *node_when, LYD_FORMAT format,
- ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node **trees);
+ ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node *tree);
/**
* @brief Perform all validation tasks, the data tree must be complete when calling this function.
*
- * @param[in] trees Array of all data trees.
+ * @param[in,out] tree Data tree.
* @param[in] modules Array of modules that should be validated, NULL for all modules.
* @param[in] mod_count Modules count.
* @param[in] ctx Context if all modules should be validated, NULL for only selected modules.
- * @param[in] val_opts Validation options.
+ * @param[in] val_opts Validation options (@ref datavalidationoptions).
* @return LY_ERR value.
*/
-LY_ERR lyd_validate_data(const struct lyd_node **trees, const struct lys_module **modules, int mod_count,
+LY_ERR lyd_validate_data(struct lyd_node **tree, const struct lys_module **modules, int mod_count,
struct ly_ctx *ctx, int val_opts);
/**
diff --git a/src/xpath.c b/src/xpath.c
index c5e3259..9cbc30a 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -751,7 +751,7 @@
new->ctx_node = set->ctx_node;
new->root_type = set->root_type;
new->local_mod = set->local_mod;
- new->trees = set->trees;
+ new->tree = set->tree;
new->format = set->format;
}
}
@@ -3560,7 +3560,7 @@
/* find leafref target */
val = lyd_value2str(leaf, &dynamic);
node = ly_type_find_leafref(set->ctx, sleaf->type, val, strlen(val), (struct lyd_node *)leaf,
- set->trees, &leaf->value, &errmsg);
+ set->tree, &leaf->value, &errmsg);
if (dynamic) {
free((char *)val);
}
@@ -3574,7 +3574,7 @@
set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
} else {
assert(sleaf->type->basetype == LY_TYPE_INST);
- node = (struct lyd_node *)lyd_target(leaf->value.target, set->trees);
+ node = (struct lyd_node *)lyd_target(leaf->value.target, set->tree);
if (!node) {
val = lyd_value2str(leaf, &dynamic);
LOGERR(set->ctx, LY_EVALID, "Invalid instance-identifier \"%s\" value - required instance not found.", val);
@@ -3649,7 +3649,7 @@
/* store args[1] into ident */
rc = sleaf->type->plugin->store(set->ctx, sleaf->type, args[1]->val.str, strlen(args[1]->val.str),
LY_TYPE_OPTS_STORE, lys_resolve_prefix, (void *)sleaf->dflt_mod, set->format,
- (struct lyd_node *)leaf, set->trees, &data, NULL, &err);
+ (struct lyd_node *)leaf, set->tree, &data, NULL, &err);
if (err) {
ly_err_print(err);
ly_err_free(err);
@@ -3732,7 +3732,7 @@
/* store args[1] into ident */
rc = sleaf->type->plugin->store(set->ctx, sleaf->type, args[1]->val.str, strlen(args[1]->val.str),
LY_TYPE_OPTS_STORE, lys_resolve_prefix, (void *)sleaf->dflt_mod, set->format,
- (struct lyd_node *)leaf, set->trees, &data, NULL, &err);
+ (struct lyd_node *)leaf, set->tree, &data, NULL, &err);
if (err) {
ly_err_print(err);
ly_err_free(err);
@@ -5326,7 +5326,7 @@
static LY_ERR
moveto_node(struct lyxp_set *set, const char *qname, uint16_t qname_len)
{
- uint32_t i, j;
+ uint32_t i;
int replaced;
const char *name_dict = NULL; /* optimization - so we can do (==) instead (!strncmp(...)) in moveto_node_check() */
const struct lys_module *moveto_mod;
@@ -5354,22 +5354,20 @@
if ((set->val.nodes[i].type == LYXP_NODE_ROOT_CONFIG) || (set->val.nodes[i].type == LYXP_NODE_ROOT)) {
assert(!set->val.nodes[i].node);
/* search in all the trees */
- LY_ARRAY_FOR(set->trees, j) {
- for (sub = set->trees[j]; sub; sub = sub->next) {
- rc = moveto_node_check(sub, set->root_type, name_dict, moveto_mod);
- if (rc == LY_SUCCESS) {
- /* pos filled later */
- if (!replaced) {
- set_replace_node(set, sub, 0, LYXP_NODE_ELEM, i);
- replaced = 1;
- } else {
- set_insert_node(set, sub, 0, LYXP_NODE_ELEM, i);
- }
- ++i;
- } else if (rc == LY_EINCOMPLETE) {
- lydict_remove(set->ctx, name_dict);
- return rc;
+ for (sub = set->tree; sub; sub = sub->next) {
+ rc = moveto_node_check(sub, set->root_type, name_dict, moveto_mod);
+ if (rc == LY_SUCCESS) {
+ /* pos filled later */
+ if (!replaced) {
+ set_replace_node(set, sub, 0, LYXP_NODE_ELEM, i);
+ replaced = 1;
+ } else {
+ set_insert_node(set, sub, 0, LYXP_NODE_ELEM, i);
}
+ ++i;
+ } else if (rc == LY_EINCOMPLETE) {
+ lydict_remove(set->ctx, name_dict);
+ return rc;
}
}
@@ -8025,7 +8023,7 @@
LY_ERR
lyxp_eval(struct lyxp_expr *exp, LYD_FORMAT format, const struct lys_module *local_mod, const struct lyd_node *ctx_node,
- enum lyxp_node_type ctx_node_type, const struct lyd_node **trees, struct lyxp_set *set, int options)
+ enum lyxp_node_type ctx_node_type, const struct lyd_node *tree, struct lyxp_set *set, int options)
{
uint16_t exp_idx = 0;
LY_ERR rc;
@@ -8041,7 +8039,7 @@
set->ctx_node = ctx_node;
set->root_type = lyxp_get_root_type(ctx_node, NULL, options);
set->local_mod = local_mod;
- set->trees = trees;
+ set->tree = tree;
set->format = format;
/* evaluate */
diff --git a/src/xpath.h b/src/xpath.h
index b2f2eb5..d645920 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -251,7 +251,7 @@
};
enum lyxp_node_type root_type;
const struct lys_module *local_mod;
- const struct lyd_node **trees;
+ const struct lyd_node *tree;
LYD_FORMAT format;
};
@@ -268,7 +268,7 @@
* this case just pass NULL for @p ctx_node and use an enum value for this kind of root (#LYXP_NODE_ROOT_CONFIG if
* @p ctx_node has config true, otherwise #LYXP_NODE_ROOT). #LYXP_NODE_TEXT and #LYXP_NODE_ATTR can also be used,
* but there are no use-cases in YANG.
- * @param[in] trees Data trees on which to perform the evaluation, they must include all the available tree (including
+ * @param[in] tree Data tree on which to perform the evaluation, it must include all the available data (including
* the tree of @p ctx_node).
* @param[out] set Result set. Must be valid and in the same libyang context as @p ctx_node.
* To be safe, always either zero or cast the @p set to empty. After done using, either cast
@@ -279,7 +279,7 @@
* LY_EINCOMPLETE for unresolved when).
*/
LY_ERR lyxp_eval(struct lyxp_expr *exp, LYD_FORMAT format, const struct lys_module *local_mod, const struct lyd_node *ctx_node,
- enum lyxp_node_type ctx_node_type, const struct lyd_node **trees, struct lyxp_set *set, int options);
+ enum lyxp_node_type ctx_node_type, const struct lyd_node *tree, struct lyxp_set *set, int options);
#define LYXP_SCHEMA 0x01 /**< Apply data node access restrictions defined for 'when' and 'must' evaluation. */
diff --git a/tests/features/test_types.c b/tests/features/test_types.c
index a08b3ce..a30e5db 100644
--- a/tests/features/test_types.c
+++ b/tests/features/test_types.c
@@ -785,7 +785,6 @@
s->func = test_instanceid;
struct lyd_node *tree;
- const struct lyd_node **trees;
const struct lyd_node_term *leaf;
struct lyd_value value = {0};
@@ -926,16 +925,14 @@
"<list2 xmlns=\"urn:tests:types\"><id>a</id><value>b</value></list2>"
"<a:inst xmlns:a=\"urn:tests:types\">/a:list2[a:id='a'][a:value='b']/a:id</a:inst>";
assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
- tree = tree->prev->prev;
- assert_int_equal(LYS_LEAF, tree->schema->nodetype);
- assert_string_equal("inst", tree->schema->name);
- leaf = (const struct lyd_node_term*)tree;
+ leaf = (const struct lyd_node_term*)tree->prev->prev;
+ assert_int_equal(LYS_LEAF, leaf->schema->nodetype);
+ assert_string_equal("inst", leaf->schema->name);
assert_null(leaf->value.canonical_cache);
- trees = lyd_trees_new(1, tree);
- assert_non_null(leaf = lyd_target(leaf->value.target, trees));
+ assert_non_null(leaf = lyd_target(leaf->value.target, tree));
assert_string_equal("a", leaf->value.canonical_cache);
assert_string_equal("b", ((struct lyd_node_term*)leaf->next)->value.canonical_cache);
- lyd_trees_free(trees, 1);
+ lyd_free_all(tree);
/* invalid value */
data = "<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:1leaftarget</t:inst>";
@@ -1079,31 +1076,30 @@
"<leaflisttarget xmlns=\"urn:tests:types\">b</leaflisttarget>"
"<a:inst xmlns:a=\"urn:tests:types\">/a:list2[a:id='a'][a:value='a']/a:id</a:inst>";
assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
- trees = lyd_trees_new(1, tree);
/* key-predicate */
data = "/a:list2[a:id='a'][a:value='b']/a:id";
assert_int_equal(LY_EVALID, lyd_value_validate(s->ctx, (const struct lyd_node_term*)tree->prev->prev, data, strlen(data),
- test_instanceid_getprefix, tree->schema->module, LYD_XML, trees));
+ test_instanceid_getprefix, tree->schema->module, LYD_XML, tree));
logbuf_assert("Invalid instance-identifier \"/a:list2[a:id='a'][a:value='b']/a:id\" value - "
"key-predicate \"[a:id='a'][a:value='b']\" does not match any \"list2\" instance. /");
/* leaf-list-predicate */
data = "/a:leaflisttarget[.='c']";
assert_int_equal(LY_EVALID, lyd_value_validate(s->ctx, (const struct lyd_node_term*)tree->prev->prev, data, strlen(data),
- test_instanceid_getprefix, tree->schema->module, LYD_XML, trees));
+ test_instanceid_getprefix, tree->schema->module, LYD_XML, tree));
logbuf_assert("Invalid instance-identifier \"/a:leaflisttarget[.='c']\" value - "
"leaf-list-predicate \"[.='c']\" does not match any \"leaflisttarget\" instance. /");
/* position predicate */
data = "/a:list2[4]";
assert_int_equal(LY_EVALID, lyd_value_validate(s->ctx, (const struct lyd_node_term*)tree->prev->prev, data, strlen(data),
- test_instanceid_getprefix, tree->schema->module, LYD_XML, trees));
+ test_instanceid_getprefix, tree->schema->module, LYD_XML, tree));
logbuf_assert("Invalid instance-identifier \"/a:list2[4]\" value - "
"position-predicate 4 is bigger than number of instances in the data tree (2). /");
- lyd_trees_free(trees, 1);
data = "<leaflisttarget xmlns=\"urn:tests:types\">b</leaflisttarget>"
"<inst xmlns=\"urn:tests:types\">/a:leaflisttarget[1]</inst>";
assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
logbuf_assert("Invalid instance-identifier \"/a:leaflisttarget[1]\" value - unable to map prefix \"a\" to YANG schema. /");
+ lyd_free_siblings(tree);
s->func = NULL;
}
@@ -1249,12 +1245,12 @@
* }
*/
- const char *data = "<int8 xmlns=\"urn:tests:types\">11</int8><int8 xmlns=\"urn:tests:types\">12</int8>"
+ const char *data = "<int8 xmlns=\"urn:tests:types\">12</int8>"
"<un1 xmlns=\"urn:tests:types\">12</un1>";
/* valid data */
assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
- tree = tree->prev->prev;
+ tree = tree->next;
assert_int_equal(LYS_LEAF, tree->schema->nodetype);
assert_string_equal("un1", tree->schema->name);
leaf = (struct lyd_node_term*)tree;
@@ -1279,10 +1275,10 @@
value.realtype->plugin->free(s->ctx, &value);
lyd_free_all(tree);
- data = "<int8 xmlns=\"urn:tests:types\">11</int8><int8 xmlns=\"urn:tests:types\">12</int8>"
+ data = "<int8 xmlns=\"urn:tests:types\">12</int8>"
"<un1 xmlns=\"urn:tests:types\">2</un1>";
assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, LYD_VALOPT_DATA_ONLY));
- tree = tree->prev->prev;
+ tree = tree->next;
assert_int_equal(LYS_LEAF, tree->schema->nodetype);
assert_string_equal("un1", tree->schema->name);
leaf = (struct lyd_node_term*)tree;
diff --git a/tests/modules/yang/ietf-netconf-with-defaults@2011-06-01.yang b/tests/modules/yang/ietf-netconf-with-defaults@2011-06-01.yang
new file mode 100644
index 0000000..e19d2b3
--- /dev/null
+++ b/tests/modules/yang/ietf-netconf-with-defaults@2011-06-01.yang
@@ -0,0 +1,140 @@
+module ietf-netconf-with-defaults {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults";
+
+ prefix ncwd;
+
+ import ietf-netconf { prefix nc; }
+
+ organization
+ "IETF NETCONF (Network Configuration Protocol) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+
+ WG List: <netconf@ietf.org>
+
+ WG Chair: Bert Wijnen
+ <bertietf@bwijnen.net>
+
+ WG Chair: Mehmet Ersue
+ <mehmet.ersue@nsn.com>
+
+ Editor: Andy Bierman
+ <andy.bierman@brocade.com>
+
+ Editor: Balazs Lengyel
+ <balazs.lengyel@ericsson.com>";
+
+ description
+ "This module defines an extension to the NETCONF protocol
+ that allows the NETCONF client to control how default
+ values are handled by the server in particular NETCONF
+ operations.
+
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ the document authors. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6243; see
+ the RFC itself for full legal notices.";
+
+ revision 2011-06-01 {
+ description
+ "Initial version.";
+ reference
+ "RFC 6243: With-defaults Capability for NETCONF";
+ }
+
+ typedef with-defaults-mode {
+ description
+ "Possible modes to report default data.";
+ reference
+ "RFC 6243; Section 3.";
+ type enumeration {
+ enum report-all {
+ description
+ "All default data is reported.";
+ reference
+ "RFC 6243; Section 3.1";
+ }
+ enum report-all-tagged {
+ description
+ "All default data is reported.
+ Any nodes considered to be default data
+ will contain a 'default' XML attribute,
+ set to 'true' or '1'.";
+ reference
+ "RFC 6243; Section 3.4";
+ }
+ enum trim {
+ description
+ "Values are not reported if they contain the default.";
+ reference
+ "RFC 6243; Section 3.2";
+ }
+ enum explicit {
+ description
+ "Report values that contain the definition of
+ explicitly set data.";
+ reference
+ "RFC 6243; Section 3.3";
+ }
+ }
+ }
+
+ grouping with-defaults-parameters {
+ description
+ "Contains the <with-defaults> parameter for control
+ of defaults in NETCONF retrieval operations.";
+
+ leaf with-defaults {
+ description
+ "The explicit defaults processing mode requested.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ type with-defaults-mode;
+ }
+ }
+
+ // extending the get-config operation
+ augment /nc:get-config/nc:input {
+ description
+ "Adds the <with-defaults> parameter to the
+ input of the NETCONF <get-config> operation.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ uses with-defaults-parameters;
+ }
+
+ // extending the get operation
+ augment /nc:get/nc:input {
+ description
+ "Adds the <with-defaults> parameter to
+ the input of the NETCONF <get> operation.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ uses with-defaults-parameters;
+ }
+
+ // extending the copy-config operation
+ augment /nc:copy-config/nc:input {
+ description
+ "Adds the <with-defaults> parameter to
+ the input of the NETCONF <copy-config> operation.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ uses with-defaults-parameters;
+ }
+
+}
diff --git a/tests/modules/yang/ietf-netconf@2011-06-01.yang b/tests/modules/yang/ietf-netconf@2011-06-01.yang
new file mode 100644
index 0000000..6449421
--- /dev/null
+++ b/tests/modules/yang/ietf-netconf@2011-06-01.yang
@@ -0,0 +1,934 @@
+module ietf-netconf {
+
+ // the namespace for NETCONF XML definitions is unchanged
+ // from RFC 4741, which this document replaces
+ namespace "urn:ietf:params:xml:ns:netconf:base:1.0";
+
+ prefix nc;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import ietf-netconf-acm { prefix nacm; }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+ WG List: <netconf@ietf.org>
+
+ WG Chair: Bert Wijnen
+ <bertietf@bwijnen.net>
+
+ WG Chair: Mehmet Ersue
+ <mehmet.ersue@nsn.com>
+
+ Editor: Martin Bjorklund
+ <mbj@tail-f.com>
+
+ Editor: Juergen Schoenwaelder
+ <j.schoenwaelder@jacobs-university.de>
+
+ Editor: Andy Bierman
+ <andy.bierman@brocade.com>";
+ description
+ "NETCONF Protocol Data Types and Protocol Operations.
+
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ the document authors. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6241; see
+ the RFC itself for full legal notices.";
+
+ revision 2011-06-01 {
+ description
+ "Initial revision;
+ 2013-09-29: Updated to include NACM attributes,
+ as specified in RFC 6536: sec 3.2.5 and 3.2.8";
+ reference
+ "RFC 6241: Network Configuration Protocol";
+ }
+
+ extension get-filter-element-attributes {
+ description
+ "If this extension is present within an 'anyxml'
+ statement named 'filter', which must be conceptually
+ defined within the RPC input section for the <get>
+ and <get-config> protocol operations, then the
+ following unqualified XML attribute is supported
+ within the <filter> element, within a <get> or
+ <get-config> protocol operation:
+
+ type : optional attribute with allowed
+ value strings 'subtree' and 'xpath'.
+ If missing, the default value is 'subtree'.
+
+ If the 'xpath' feature is supported, then the
+ following unqualified XML attribute is
+ also supported:
+
+ select: optional attribute containing a
+ string representing an XPath expression.
+ The 'type' attribute must be equal to 'xpath'
+ if this attribute is present.";
+ }
+
+ // NETCONF capabilities defined as features
+ feature writable-running {
+ description
+ "NETCONF :writable-running capability;
+ If the server advertises the :writable-running
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.2";
+ }
+
+ feature candidate {
+ description
+ "NETCONF :candidate capability;
+ If the server advertises the :candidate
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.3";
+ }
+
+ feature confirmed-commit {
+ if-feature candidate;
+ description
+ "NETCONF :confirmed-commit:1.1 capability;
+ If the server advertises the :confirmed-commit:1.1
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+
+ reference "RFC 6241, Section 8.4";
+ }
+
+ feature rollback-on-error {
+ description
+ "NETCONF :rollback-on-error capability;
+ If the server advertises the :rollback-on-error
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.5";
+ }
+
+ feature validate {
+ description
+ "NETCONF :validate:1.1 capability;
+ If the server advertises the :validate:1.1
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.6";
+ }
+
+ feature startup {
+ description
+ "NETCONF :startup capability;
+ If the server advertises the :startup
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.7";
+ }
+
+ feature url {
+ description
+ "NETCONF :url capability;
+ If the server advertises the :url
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.8";
+ }
+
+ feature xpath {
+ description
+ "NETCONF :xpath capability;
+ If the server advertises the :xpath
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.9";
+ }
+
+ // NETCONF Simple Types
+
+ typedef session-id-type {
+ type uint32 {
+ range "1..max";
+ }
+ description
+ "NETCONF Session Id";
+ }
+
+ typedef session-id-or-zero-type {
+ type uint32;
+ description
+ "NETCONF Session Id or Zero to indicate none";
+ }
+ typedef error-tag-type {
+ type enumeration {
+ enum in-use {
+ description
+ "The request requires a resource that
+ already is in use.";
+ }
+ enum invalid-value {
+ description
+ "The request specifies an unacceptable value for one
+ or more parameters.";
+ }
+ enum too-big {
+ description
+ "The request or response (that would be generated) is
+ too large for the implementation to handle.";
+ }
+ enum missing-attribute {
+ description
+ "An expected attribute is missing.";
+ }
+ enum bad-attribute {
+ description
+ "An attribute value is not correct; e.g., wrong type,
+ out of range, pattern mismatch.";
+ }
+ enum unknown-attribute {
+ description
+ "An unexpected attribute is present.";
+ }
+ enum missing-element {
+ description
+ "An expected element is missing.";
+ }
+ enum bad-element {
+ description
+ "An element value is not correct; e.g., wrong type,
+ out of range, pattern mismatch.";
+ }
+ enum unknown-element {
+ description
+ "An unexpected element is present.";
+ }
+ enum unknown-namespace {
+ description
+ "An unexpected namespace is present.";
+ }
+ enum access-denied {
+ description
+ "Access to the requested protocol operation or
+ data model is denied because authorization failed.";
+ }
+ enum lock-denied {
+ description
+ "Access to the requested lock is denied because the
+ lock is currently held by another entity.";
+ }
+ enum resource-denied {
+ description
+ "Request could not be completed because of
+ insufficient resources.";
+ }
+ enum rollback-failed {
+ description
+ "Request to roll back some configuration change (via
+ rollback-on-error or <discard-changes> operations)
+ was not completed for some reason.";
+
+ }
+ enum data-exists {
+ description
+ "Request could not be completed because the relevant
+ data model content already exists. For example,
+ a 'create' operation was attempted on data that
+ already exists.";
+ }
+ enum data-missing {
+ description
+ "Request could not be completed because the relevant
+ data model content does not exist. For example,
+ a 'delete' operation was attempted on
+ data that does not exist.";
+ }
+ enum operation-not-supported {
+ description
+ "Request could not be completed because the requested
+ operation is not supported by this implementation.";
+ }
+ enum operation-failed {
+ description
+ "Request could not be completed because the requested
+ operation failed for some reason not covered by
+ any other error condition.";
+ }
+ enum partial-operation {
+ description
+ "This error-tag is obsolete, and SHOULD NOT be sent
+ by servers conforming to this document.";
+ }
+ enum malformed-message {
+ description
+ "A message could not be handled because it failed to
+ be parsed correctly. For example, the message is not
+ well-formed XML or it uses an invalid character set.";
+ }
+ }
+ description "NETCONF Error Tag";
+ reference "RFC 6241, Appendix A";
+ }
+
+ typedef error-severity-type {
+ type enumeration {
+ enum error {
+ description "Error severity";
+ }
+ enum warning {
+ description "Warning severity";
+ }
+ }
+ description "NETCONF Error Severity";
+ reference "RFC 6241, Section 4.3";
+ }
+
+ typedef edit-operation-type {
+ type enumeration {
+ enum merge {
+ description
+ "The configuration data identified by the
+ element containing this attribute is merged
+ with the configuration at the corresponding
+ level in the configuration datastore identified
+ by the target parameter.";
+ }
+ enum replace {
+ description
+ "The configuration data identified by the element
+ containing this attribute replaces any related
+ configuration in the configuration datastore
+ identified by the target parameter. If no such
+ configuration data exists in the configuration
+ datastore, it is created. Unlike a
+ <copy-config> operation, which replaces the
+ entire target configuration, only the configuration
+ actually present in the config parameter is affected.";
+ }
+ enum create {
+ description
+ "The configuration data identified by the element
+ containing this attribute is added to the
+ configuration if and only if the configuration
+ data does not already exist in the configuration
+ datastore. If the configuration data exists, an
+ <rpc-error> element is returned with an
+ <error-tag> value of 'data-exists'.";
+ }
+ enum delete {
+ description
+ "The configuration data identified by the element
+ containing this attribute is deleted from the
+ configuration if and only if the configuration
+ data currently exists in the configuration
+ datastore. If the configuration data does not
+ exist, an <rpc-error> element is returned with
+ an <error-tag> value of 'data-missing'.";
+ }
+ enum remove {
+ description
+ "The configuration data identified by the element
+ containing this attribute is deleted from the
+ configuration if the configuration
+ data currently exists in the configuration
+ datastore. If the configuration data does not
+ exist, the 'remove' operation is silently ignored
+ by the server.";
+ }
+ }
+ default "merge";
+ description "NETCONF 'operation' attribute values";
+ reference "RFC 6241, Section 7.2";
+ }
+
+ // NETCONF Standard Protocol Operations
+
+ rpc get-config {
+ description
+ "Retrieve all or part of a specified configuration.";
+
+ reference "RFC 6241, Section 7.1";
+
+ input {
+ container source {
+ description
+ "Particular configuration to retrieve.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration to retrieve.";
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.
+ This is optional-to-implement on the server because
+ not all servers will support filtering for this
+ datastore.";
+ }
+ }
+ }
+
+ anyxml filter {
+ description
+ "Subtree or XPath filter to use.";
+ nc:get-filter-element-attributes;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the source datastore subset that matched
+ the filter criteria (if any). An empty data container
+ indicates that the request did not produce any results.";
+ }
+ }
+ }
+
+ rpc edit-config {
+ description
+ "The <edit-config> operation loads all or part of a specified
+ configuration to the specified target configuration.";
+
+ reference "RFC 6241, Section 7.2";
+
+ input {
+ container target {
+ description
+ "Particular configuration to edit.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ }
+ }
+
+ leaf default-operation {
+ type enumeration {
+ enum merge {
+ description
+ "The default operation is merge.";
+ }
+ enum replace {
+ description
+ "The default operation is replace.";
+ }
+ enum none {
+ description
+ "There is no default operation.";
+ }
+ }
+ default "merge";
+ description
+ "The default operation to use.";
+ }
+
+ leaf test-option {
+ if-feature validate;
+ type enumeration {
+ enum test-then-set {
+ description
+ "The server will test and then set if no errors.";
+ }
+ enum set {
+ description
+ "The server will set without a test first.";
+ }
+
+ enum test-only {
+ description
+ "The server will only test and not set, even
+ if there are no errors.";
+ }
+ }
+ default "test-then-set";
+ description
+ "The test option to use.";
+ }
+
+ leaf error-option {
+ type enumeration {
+ enum stop-on-error {
+ description
+ "The server will stop on errors.";
+ }
+ enum continue-on-error {
+ description
+ "The server may continue on errors.";
+ }
+ enum rollback-on-error {
+ description
+ "The server will roll back on errors.
+ This value can only be used if the 'rollback-on-error'
+ feature is supported.";
+ }
+ }
+ default "stop-on-error";
+ description
+ "The error option to use.";
+ }
+
+ choice edit-content {
+ mandatory true;
+ description
+ "The content for the edit operation.";
+
+ anyxml config {
+ description
+ "Inline Config content.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "URL-based config content.";
+ }
+ }
+ }
+ }
+
+ rpc copy-config {
+ description
+ "Create or replace an entire configuration datastore with the
+ contents of another complete configuration datastore.";
+
+ reference "RFC 6241, Section 7.3";
+
+ input {
+ container target {
+ description
+ "Particular configuration to copy to.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target of the copy operation.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description
+ "The running configuration is the config target.
+ This is optional-to-implement on the server.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config target.";
+ }
+ }
+ }
+
+ container source {
+ description
+ "Particular configuration to copy from.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration source for the copy operation.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config source.";
+ }
+ anyxml config {
+ description
+ "Inline Config content: <config> element. Represents
+ an entire configuration datastore, not
+ a subset of the running datastore.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc delete-config {
+ nacm:default-deny-all;
+ description
+ "Delete a configuration datastore.";
+
+ reference "RFC 6241, Section 7.4";
+
+ input {
+ container target {
+ description
+ "Particular configuration to delete.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to delete.";
+
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc lock {
+ description
+ "The lock operation allows the client to lock the configuration
+ system of a device.";
+
+ reference "RFC 6241, Section 7.5";
+
+ input {
+ container target {
+ description
+ "Particular configuration to lock.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to lock.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config target.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc unlock {
+ description
+ "The unlock operation is used to release a configuration lock,
+ previously obtained with the 'lock' operation.";
+
+ reference "RFC 6241, Section 7.6";
+
+ input {
+ container target {
+ description
+ "Particular configuration to unlock.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to unlock.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config target.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc get {
+ description
+ "Retrieve running configuration and device state information.";
+
+ reference "RFC 6241, Section 7.7";
+
+ input {
+ anyxml filter {
+ description
+ "This parameter specifies the portion of the system
+ configuration and state data to retrieve.";
+ nc:get-filter-element-attributes;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the running datastore subset and/or state
+ data that matched the filter criteria (if any).
+ An empty data container indicates that the request did not
+ produce any results.";
+ }
+ }
+ }
+
+ rpc close-session {
+ description
+ "Request graceful termination of a NETCONF session.";
+
+ reference "RFC 6241, Section 7.8";
+ }
+
+ rpc kill-session {
+ nacm:default-deny-all;
+ description
+ "Force the termination of a NETCONF session.";
+
+ reference "RFC 6241, Section 7.9";
+
+ input {
+ leaf session-id {
+ type session-id-type;
+ mandatory true;
+ description
+ "Particular session to kill.";
+ }
+ }
+ }
+
+ rpc commit {
+ if-feature candidate;
+
+ description
+ "Commit the candidate configuration as the device's new
+ current configuration.";
+
+ reference "RFC 6241, Section 8.3.4.1";
+
+ input {
+ leaf confirmed {
+ if-feature confirmed-commit;
+ type empty;
+ description
+ "Requests a confirmed commit.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf confirm-timeout {
+ if-feature confirmed-commit;
+ type uint32 {
+ range "1..max";
+ }
+ units "seconds";
+ default "600"; // 10 minutes
+ description
+ "The timeout interval for a confirmed commit.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf persist {
+ if-feature confirmed-commit;
+ type string;
+ description
+ "This parameter is used to make a confirmed commit
+ persistent. A persistent confirmed commit is not aborted
+ if the NETCONF session terminates. The only way to abort
+ a persistent confirmed commit is to let the timer expire,
+ or to use the <cancel-commit> operation.
+
+ The value of this parameter is a token that must be given
+ in the 'persist-id' parameter of <commit> or
+ <cancel-commit> operations in order to confirm or cancel
+ the persistent confirmed commit.
+
+ The token should be a random string.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf persist-id {
+ if-feature confirmed-commit;
+ type string;
+ description
+ "This parameter is given in order to commit a persistent
+ confirmed commit. The value must be equal to the value
+ given in the 'persist' parameter to the <commit> operation.
+ If it does not match, the operation fails with an
+ 'invalid-value' error.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ }
+ }
+
+ rpc discard-changes {
+ if-feature candidate;
+
+ description
+ "Revert the candidate configuration to the current
+ running configuration.";
+ reference "RFC 6241, Section 8.3.4.2";
+ }
+
+ rpc cancel-commit {
+ if-feature confirmed-commit;
+ description
+ "This operation is used to cancel an ongoing confirmed commit.
+ If the confirmed commit is persistent, the parameter
+ 'persist-id' must be given, and it must match the value of the
+ 'persist' parameter.";
+ reference "RFC 6241, Section 8.4.4.1";
+
+ input {
+ leaf persist-id {
+ type string;
+ description
+ "This parameter is given in order to cancel a persistent
+ confirmed commit. The value must be equal to the value
+ given in the 'persist' parameter to the <commit> operation.
+ If it does not match, the operation fails with an
+ 'invalid-value' error.";
+ }
+ }
+ }
+
+ rpc validate {
+ if-feature validate;
+
+ description
+ "Validates the contents of the specified configuration.";
+
+ reference "RFC 6241, Section 8.6.4.1";
+
+ input {
+ container source {
+ description
+ "Particular configuration to validate.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration source to validate.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config source.";
+ }
+ anyxml config {
+ description
+ "Inline Config content: <config> element. Represents
+ an entire configuration datastore, not
+ a subset of the running datastore.";
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/tests/src/test_validation.c b/tests/src/test_validation.c
index 660f856..6f0ef9f 100644
--- a/tests/src/test_validation.c
+++ b/tests/src/test_validation.c
@@ -20,8 +20,11 @@
#include <stdio.h>
#include <string.h>
+#include "tests/config.h"
+
#include "../../src/context.h"
#include "../../src/tree_data_internal.h"
+#include "../../src/printer_data.h"
#define BUFSIZE 1024
char logbuf[BUFSIZE] = {0};
@@ -177,16 +180,127 @@
"}"
"}"
"}";
+ const char *schema_e =
+ "module e {"
+ "namespace urn:tests:e;"
+ "prefix e;"
+ "yang-version 1.1;"
+
+ "choice choic {"
+ "leaf a {"
+ "type string;"
+ "}"
+ "case b {"
+ "leaf-list l {"
+ "type string;"
+ "}"
+ "}"
+ "}"
+ "list lt {"
+ "key \"k\";"
+ "leaf k {"
+ "type string;"
+ "}"
+ "}"
+ "leaf d {"
+ "type uint32;"
+ "}"
+ "leaf-list ll {"
+ "type string;"
+ "}"
+ "container cont {"
+ "list lt {"
+ "key \"k\";"
+ "leaf k {"
+ "type string;"
+ "}"
+ "}"
+ "leaf d {"
+ "type uint32;"
+ "}"
+ "leaf-list ll {"
+ "type string;"
+ "}"
+ "}"
+ "}";
+ const char *schema_f =
+ "module f {"
+ "namespace urn:tests:f;"
+ "prefix f;"
+ "yang-version 1.1;"
+
+ "choice choic {"
+ "default \"c\";"
+ "leaf a {"
+ "type string;"
+ "}"
+ "case b {"
+ "leaf l {"
+ "type string;"
+ "}"
+ "}"
+ "case c {"
+ "leaf-list ll1 {"
+ "type string;"
+ "default \"def1\";"
+ "default \"def2\";"
+ "default \"def3\";"
+ "}"
+ "}"
+ "}"
+ "leaf d {"
+ "type uint32;"
+ "default 15;"
+ "}"
+ "leaf-list ll2 {"
+ "type string;"
+ "default \"dflt1\";"
+ "default \"dflt2\";"
+ "}"
+ "container cont {"
+ "choice choic {"
+ "default \"c\";"
+ "leaf a {"
+ "type string;"
+ "}"
+ "case b {"
+ "leaf l {"
+ "type string;"
+ "}"
+ "}"
+ "case c {"
+ "leaf-list ll1 {"
+ "type string;"
+ "default \"def1\";"
+ "default \"def2\";"
+ "default \"def3\";"
+ "}"
+ "}"
+ "}"
+ "leaf d {"
+ "type uint32;"
+ "default 15;"
+ "}"
+ "leaf-list ll2 {"
+ "type string;"
+ "default \"dflt1\";"
+ "default \"dflt2\";"
+ "}"
+ "}"
+ "}";
#if ENABLE_LOGGER_CHECKING
ly_set_log_clb(logger, 1);
#endif
- assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &ctx));
+ assert_non_null(ly_ctx_load_module(ctx, "ietf-netconf-with-defaults", "2011-06-01"));
assert_non_null(lys_parse_mem(ctx, schema_a, LYS_IN_YANG));
assert_non_null(lys_parse_mem(ctx, schema_b, LYS_IN_YANG));
assert_non_null(lys_parse_mem(ctx, schema_c, LYS_IN_YANG));
assert_non_null(lys_parse_mem(ctx, schema_d, LYS_IN_YANG));
+ assert_non_null(lys_parse_mem(ctx, schema_e, LYS_IN_YANG));
+ assert_non_null(lys_parse_mem(ctx, schema_f, LYS_IN_YANG));
return 0;
}
@@ -285,7 +399,7 @@
data = "<a xmlns=\"urn:tests:b\">string</a><c xmlns=\"urn:tests:b\">string2</c>";
assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
assert_non_null(tree);
- lyd_free_withsiblings(tree);
+ lyd_free_siblings(tree);
*state = NULL;
}
@@ -316,7 +430,7 @@
"<l xmlns=\"urn:tests:c\">val3</l>";
assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
assert_non_null(tree);
- lyd_free_withsiblings(tree);
+ lyd_free_siblings(tree);
data =
"<l xmlns=\"urn:tests:c\">val1</l>"
@@ -352,7 +466,7 @@
"</lt>";
assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
assert_non_null(tree);
- lyd_free_withsiblings(tree);
+ lyd_free_siblings(tree);
data =
"<lt xmlns=\"urn:tests:d\">"
@@ -365,7 +479,7 @@
"</lt>";
assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
assert_non_null(tree);
- lyd_free_withsiblings(tree);
+ lyd_free_siblings(tree);
data =
"<lt xmlns=\"urn:tests:d\">"
@@ -416,7 +530,7 @@
"</lt>";
assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
assert_non_null(tree);
- lyd_free_withsiblings(tree);
+ lyd_free_siblings(tree);
data =
"<lt xmlns=\"urn:tests:d\">"
@@ -450,7 +564,7 @@
"</lt>";
assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
assert_non_null(tree);
- lyd_free_withsiblings(tree);
+ lyd_free_siblings(tree);
data =
"<lt xmlns=\"urn:tests:d\">"
@@ -556,7 +670,7 @@
"</lt2>";
assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY | LYD_OPT_STRICT, &tree));
assert_non_null(tree);
- lyd_free_withsiblings(tree);
+ lyd_free_siblings(tree);
data =
"<lt2 xmlns=\"urn:tests:d\">"
@@ -711,6 +825,193 @@
*state = NULL;
}
+static void
+test_dup(void **state)
+{
+ *state = test_dup;
+
+ const char *data;
+ struct lyd_node *tree;
+
+ data = "<d xmlns=\"urn:tests:e\">25</d><d xmlns=\"urn:tests:e\">50</d>";
+ assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
+ assert_null(tree);
+ logbuf_assert("Duplicate instance of \"d\". /e:d");
+
+ data = "<lt xmlns=\"urn:tests:e\"><k>A</k></lt><lt xmlns=\"urn:tests:e\"><k>B</k></lt><lt xmlns=\"urn:tests:e\"><k>A</k></lt>";
+ assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
+ assert_null(tree);
+ logbuf_assert("Duplicate instance of \"lt\". /e:lt[k='A']");
+
+ data = "<ll xmlns=\"urn:tests:e\">A</ll><ll xmlns=\"urn:tests:e\">B</ll><ll xmlns=\"urn:tests:e\">B</ll>";
+ assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
+ assert_null(tree);
+ logbuf_assert("Duplicate instance of \"ll\". /e:ll[.='B']");
+
+ data = "<cont xmlns=\"urn:tests:e\"></cont><cont xmlns=\"urn:tests:e\"/>";
+ assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
+ assert_null(tree);
+ logbuf_assert("Duplicate instance of \"cont\". /e:cont");
+
+ /* same tests again but using hashes */
+ data = "<cont xmlns=\"urn:tests:e\"><d>25</d><d>50</d><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll></cont>";
+ assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
+ assert_null(tree);
+ logbuf_assert("Duplicate instance of \"d\". /e:cont/d");
+
+ data = "<cont xmlns=\"urn:tests:e\"><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll>"
+ "<lt><k>a</k></lt><lt><k>b</k></lt><lt><k>c</k></lt><lt><k>d</k></lt><lt><k>c</k></lt></cont>";
+ assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
+ assert_null(tree);
+ logbuf_assert("Duplicate instance of \"lt\". /e:cont/lt[k='c']");
+
+ data = "<cont xmlns=\"urn:tests:e\"><ll>1</ll><ll>2</ll><ll>3</ll><ll>4</ll>"
+ "<ll>a</ll><ll>b</ll><ll>c</ll><ll>d</ll><ll>d</ll></cont>";
+ assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
+ assert_null(tree);
+ logbuf_assert("Duplicate instance of \"ll\". /e:cont/ll[.='d']");
+
+ /* cases */
+ data = "<l xmlns=\"urn:tests:e\">a</l><l xmlns=\"urn:tests:e\">b</l><l xmlns=\"urn:tests:e\">c</l><l xmlns=\"urn:tests:e\">b</l>";
+ assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
+ assert_null(tree);
+ logbuf_assert("Duplicate instance of \"l\". /e:l[.='b']");
+
+ data = "<l xmlns=\"urn:tests:e\">a</l><l xmlns=\"urn:tests:e\">b</l><l xmlns=\"urn:tests:e\">c</l><a xmlns=\"urn:tests:e\">aa</a>";
+ assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
+ assert_null(tree);
+ logbuf_assert("Data for both cases \"a\" and \"b\" exist. /e:choic");
+
+ *state = NULL;
+}
+
+static void
+test_defaults(void **state)
+{
+ *state = test_defaults;
+
+ const char *data;
+ char *str;
+ struct lyd_node *tree, *node;
+ const struct lys_module *mod = ly_ctx_get_module_latest(ctx, "f");
+
+ data = "<cont xmlns=\"urn:tests:f\"/>";
+ assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, LYD_VALOPT_DATA_ONLY, &tree));
+ assert_non_null(tree);
+
+ /* check all defaults exist */
+ lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+ assert_string_equal(str,
+ "<cont xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\">"
+ "<ll1 ncwd:default=\"true\">def1</ll1>"
+ "<ll1 ncwd:default=\"true\">def2</ll1>"
+ "<ll1 ncwd:default=\"true\">def3</ll1>"
+ "<d ncwd:default=\"true\">15</d>"
+ "<ll2 ncwd:default=\"true\">dflt1</ll2>"
+ "<ll2 ncwd:default=\"true\">dflt2</ll2>"
+ "</cont>"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def1</ll1>"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def2</ll1>"
+ "<ll1 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">def3</ll1>"
+ "<d xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>");
+ free(str);
+
+ /* create another explicit case and validate */
+ node = lyd_new_term(NULL, mod, "l", "value");
+ assert_non_null(node);
+ assert_int_equal(lyd_insert_after(tree->prev, node), LY_SUCCESS);
+ assert_int_equal(lyd_validate(ctx, &tree, LYD_VALOPT_DATA_ONLY), LY_SUCCESS);
+
+ /* check data tree */
+ lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+ assert_string_equal(str,
+ "<cont xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\">"
+ "<ll1 ncwd:default=\"true\">def1</ll1>"
+ "<ll1 ncwd:default=\"true\">def2</ll1>"
+ "<ll1 ncwd:default=\"true\">def3</ll1>"
+ "<d ncwd:default=\"true\">15</d>"
+ "<ll2 ncwd:default=\"true\">dflt1</ll2>"
+ "<ll2 ncwd:default=\"true\">dflt2</ll2>"
+ "</cont>"
+ "<d xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">15</d>"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt1</ll2>"
+ "<ll2 xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\" ncwd:default=\"true\">dflt2</ll2>"
+ "<l xmlns=\"urn:tests:f\">value</l>");
+ free(str);
+
+ /* create explicit leaf-list and leaf and validate */
+ node = lyd_new_term(NULL, mod, "d", "15");
+ assert_non_null(node);
+ assert_int_equal(lyd_insert_after(tree->prev, node), LY_SUCCESS);
+ node = lyd_new_term(NULL, mod, "ll2", "dflt2");
+ assert_non_null(node);
+ assert_int_equal(lyd_insert_after(tree->prev, node), LY_SUCCESS);
+ assert_int_equal(lyd_validate(ctx, &tree, LYD_VALOPT_DATA_ONLY), LY_SUCCESS);
+
+ /* check data tree */
+ lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+ assert_string_equal(str,
+ "<cont xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\">"
+ "<ll1 ncwd:default=\"true\">def1</ll1>"
+ "<ll1 ncwd:default=\"true\">def2</ll1>"
+ "<ll1 ncwd:default=\"true\">def3</ll1>"
+ "<d ncwd:default=\"true\">15</d>"
+ "<ll2 ncwd:default=\"true\">dflt1</ll2>"
+ "<ll2 ncwd:default=\"true\">dflt2</ll2>"
+ "</cont>"
+ "<l xmlns=\"urn:tests:f\">value</l>"
+ "<d xmlns=\"urn:tests:f\">15</d>"
+ "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
+ free(str);
+
+// /* create explicit container, which automatically becomes implicit */
+// node = lyd_new_inner(NULL, mod, "cont");
+// assert_non_null(node);
+// assert_int_equal(lyd_insert_after(tree->prev, node), LY_SUCCESS);
+// assert_int_equal(lyd_validate(ctx, &tree, LYD_VALOPT_DATA_ONLY), LY_SUCCESS);
+//
+// /* check data tree */
+// lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+// assert_string_equal(str,
+// "<cont xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\">"
+// "<ll1 ncwd:default=\"true\">def1</ll1>"
+// "<ll1 ncwd:default=\"true\">def2</ll1>"
+// "<ll1 ncwd:default=\"true\">def3</ll1>"
+// "<d ncwd:default=\"true\">15</d>"
+// "<ll2 ncwd:default=\"true\">dflt1</ll2>"
+// "<ll2 ncwd:default=\"true\">dflt2</ll2>"
+// "</cont>"
+// "<l xmlns=\"urn:tests:f\">value</l>"
+// "<d xmlns=\"urn:tests:f\">15</d>"
+// "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
+// free(str);
+
+ /* similar changes for nested defaults */
+ assert_non_null(lyd_new_term(tree, NULL, "ll1", "def3"));
+ assert_non_null(lyd_new_term(tree, NULL, "d", "5"));
+ assert_non_null(lyd_new_term(tree, NULL, "ll2", "non-dflt"));
+ assert_int_equal(lyd_validate(ctx, &tree, LYD_VALOPT_DATA_ONLY), LY_SUCCESS);
+
+ /* check data tree */
+ lyd_print_mem(&str, tree, LYD_XML, LYDP_WITHSIBLINGS | LYDP_WD_IMPL_TAG);
+ assert_string_equal(str,
+ "<cont xmlns=\"urn:tests:f\" xmlns:ncwd=\"urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults\">"
+ "<ll1>def3</ll1>"
+ "<d>5</d>"
+ "<ll2>non-dflt</ll2>"
+ "</cont>"
+ "<l xmlns=\"urn:tests:f\">value</l>"
+ "<d xmlns=\"urn:tests:f\">15</d>"
+ "<ll2 xmlns=\"urn:tests:f\">dflt2</ll2>");
+ free(str);
+
+ lyd_free_siblings(tree);
+
+ *state = NULL;
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
@@ -719,6 +1020,8 @@
cmocka_unit_test_teardown(test_minmax, teardown_s),
cmocka_unit_test_teardown(test_unique, teardown_s),
cmocka_unit_test_teardown(test_unique_nested, teardown_s),
+ cmocka_unit_test_teardown(test_dup, teardown_s),
+ cmocka_unit_test_teardown(test_defaults, teardown_s),
};
return cmocka_run_group_tests(tests, setup, teardown);
diff --git a/tools/lint/commands.c b/tools/lint/commands.c
index 67c0e53..b397950 100644
--- a/tools/lint/commands.c
+++ b/tools/lint/commands.c
@@ -545,7 +545,7 @@
}
static int
-parse_data(char *filepath, int *options, const struct lyd_node **trees, const char *rpc_act_file,
+parse_data(char *filepath, int *options, const struct lyd_node *tree, const char *rpc_act_file,
struct lyd_node **result)
{
LYD_FORMAT informat = LYD_UNKNOWN;
@@ -696,8 +696,8 @@
int options = 0, printopt = 0;
char **argv = NULL, *ptr;
const char *out_path = NULL;
- struct lyd_node *data = NULL, *val_tree = NULL;
- const struct lyd_node **trees = NULL;
+ struct lyd_node *data = NULL;
+ struct lyd_node *tree = NULL;
LYD_FORMAT outformat = LYD_UNKNOWN;
FILE *output = stdout;
static struct option long_options[] = {
@@ -829,7 +829,7 @@
goto cleanup;
}
- if (parse_data(argv[optind], &options, trees, argv[optind + 1], &data)) {
+ if (parse_data(argv[optind], &options, tree, argv[optind + 1], &data)) {
goto cleanup;
}
@@ -855,7 +855,6 @@
fclose(output);
}
- lyd_trees_free(trees, 1);
lyd_free_all(data);
return ret;
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index 60ecfe1..99e7ce2 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -319,8 +319,7 @@
struct lyxml_elem *iter, *elem;
struct *subroot, *next, *node;
#endif
- struct lyd_node *oper = NULL;
- const struct lyd_node **trees = NULL;
+ struct lyd_node *tree = NULL;
struct dataitem {
const char *filename;
struct lyd_node *tree;
@@ -797,12 +796,11 @@
fprintf(stderr, "yanglint error: The operational data are expected in XML or JSON format.\n");
goto cleanup;
}
- oper = lyd_parse_path(ctx, oper_file, informat_d, LYD_OPT_PARSE_ONLY);
- if (!oper) {
+ tree = lyd_parse_path(ctx, oper_file, informat_d, LYD_OPT_PARSE_ONLY);
+ if (!tree) {
fprintf(stderr, "yanglint error: Failed to parse the operational datastore file for RPC/Notification validation.\n");
goto cleanup;
}
- trees = lyd_trees_new(1, oper);
}
for (data_item = data, data_prev = NULL; data_item; data_prev = data_item, data_item = data_item->next) {
@@ -1101,7 +1099,6 @@
lyd_free_all(data->tree);
free(data);
}
- lyd_trees_free(trees, 1);
ly_ctx_destroy(ctx, NULL);
return ret;