data tree FEATURE lyd_new_path() and lyd_change_term()
With tests included. Also, another missing API
function lyd_new_opaq() added.
diff --git a/src/context.c b/src/context.c
index 52a34a5..9f18510 100644
--- a/src/context.c
+++ b/src/context.c
@@ -771,10 +771,9 @@
root_bis = 0;
}
- /* TODO uncomment once lefref validation works
if (lyd_validate(&root, NULL, LYD_VALOPT_DATA_ONLY)) {
goto error;
- }*/
+ }
return root;
diff --git a/src/path.c b/src/path.c
index 5138fa7..bcbfe01 100644
--- a/src/path.c
+++ b/src/path.c
@@ -308,6 +308,7 @@
/**
* @brief Parse prefix from a NameTest, if any, and node name, and return expected module of the node.
*
+ * @param[in] ctx libyang context.
* @param[in] cur_mod Module of the current (original context) node. Needed for ::LYD_SCHEMA.
* @param[in] prev_ctx_node Previous context node. Needed for ::LYD_JSON.
* @param[in] expr Parsed path.
@@ -322,9 +323,9 @@
* @return LY_ERR value.
*/
static LY_ERR
-ly_path_compile_prefix(const struct lys_module *cur_mod, const struct lysc_node *prev_ctx_node, const struct lyxp_expr *expr,
- uint16_t tok_idx, uint8_t lref, ly_clb_resolve_prefix resolve_prefix, void *prefix_data,
- LYD_FORMAT format, const struct lys_module **mod, const char **name, size_t *name_len)
+ly_path_compile_prefix(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *prev_ctx_node,
+ const struct lyxp_expr *expr, uint16_t tok_idx, uint8_t lref, ly_clb_resolve_prefix resolve_prefix,
+ void *prefix_data, LYD_FORMAT format, const struct lys_module **mod, const char **name, size_t *name_len)
{
const char *ptr;
size_t len;
@@ -337,14 +338,14 @@
/* find next node module */
if (ptr) {
- *mod = resolve_prefix(cur_mod->ctx, expr->expr + expr->tok_pos[tok_idx], len, prefix_data);
+ *mod = resolve_prefix(ctx, expr->expr + expr->tok_pos[tok_idx], len, prefix_data);
if (!*mod) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Prefix \"%.*s\" not found of a module in path.",
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Prefix \"%.*s\" not found of a module in path.",
len, expr->expr + expr->tok_pos[tok_idx]);
return LY_EINVAL;
} else if (!(*mod)->implemented) {
if (lref == LY_PATH_LREF_FALSE) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not implemented module \"%s\" in path.", (*mod)->name);
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not implemented module \"%s\" in path.", (*mod)->name);
return LY_EINVAL;
}
lys_set_implemented_internal((struct lys_module *)*mod, 2);
@@ -356,13 +357,13 @@
break;
case LYD_JSON:
if (!prev_ctx_node) {
- LOGINT_RET(cur_mod->ctx);
+ LOGINT_RET(ctx);
}
*mod = prev_ctx_node->module;
break;
case LYD_XML:
/* not really defined */
- LOGINT_RET(cur_mod->ctx);
+ LOGINT_RET(ctx);
}
}
@@ -379,9 +380,10 @@
}
LY_ERR
-ly_path_compile_predicate(const struct lys_module *cur_mod, const struct lysc_node *ctx_node, const struct lyxp_expr *expr,
- uint16_t *tok_idx, ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format,
- struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
+ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
+ const struct lyxp_expr *expr, uint16_t *tok_idx, ly_clb_resolve_prefix resolve_prefix,
+ void *prefix_data, LYD_FORMAT format, struct ly_path_predicate **predicates,
+ enum ly_path_pred_type *pred_type)
{
struct ly_path_predicate *p;
const struct lysc_node *key;
@@ -389,6 +391,8 @@
const char *name;
size_t name_len, key_count;
+ assert(ctx && ctx_node);
+
*pred_type = 0;
/* '[' */
@@ -399,25 +403,25 @@
if (expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST) {
if (ctx_node->nodetype != LYS_LIST) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.",
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.",
lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
return LY_EINVAL;
} else if (ctx_node->flags & LYS_KEYLESS) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.",
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.",
lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
return LY_EINVAL;
}
do {
/* NameTest, find the key */
- LY_CHECK_RET(ly_path_compile_prefix(cur_mod, ctx_node, expr, *tok_idx, LY_PATH_LREF_FALSE, resolve_prefix,
+ LY_CHECK_RET(ly_path_compile_prefix(ctx, cur_mod, ctx_node, expr, *tok_idx, LY_PATH_LREF_FALSE, resolve_prefix,
prefix_data, format, &mod, &name, &name_len));
key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
if (!key) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
return LY_EINVAL;
} else if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) {
- LOGVAL(cur_mod->ctx, LY_VLOG_LYSC, key, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.",
+ LOGVAL(ctx, LY_VLOG_LYSC, key, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.",
lys_nodetype2str(key->nodetype), key->name);
return LY_EINVAL;
}
@@ -428,7 +432,7 @@
*pred_type = LY_PATH_PREDTYPE_LIST;
}
assert(*pred_type == LY_PATH_PREDTYPE_LIST);
- LY_ARRAY_NEW_RET(cur_mod->ctx, *predicates, p, LY_EMEM);
+ LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
p->key = key;
/* '=' */
@@ -455,16 +459,16 @@
}
if (LY_ARRAY_SIZE(*predicates) != key_count) {
/* names (keys) are unique - it was checked when parsing */
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for a key of %s \"%s\" in path.",
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for a key of %s \"%s\" in path.",
lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
- ly_path_predicates_free(cur_mod->ctx, LY_PATH_PREDTYPE_LIST, NULL, *predicates);
+ ly_path_predicates_free(ctx, LY_PATH_PREDTYPE_LIST, NULL, *predicates);
*predicates = NULL;
return LY_EINVAL;
}
} else if (expr->tokens[*tok_idx] == LYXP_TOKEN_DOT) {
if (ctx_node->nodetype != LYS_LEAFLIST) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Leaf-list predicate defined for %s \"%s\" in path.",
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Leaf-list predicate defined for %s \"%s\" in path.",
lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
return LY_EINVAL;
}
@@ -472,7 +476,7 @@
/* new predicate */
*pred_type = LY_PATH_PREDTYPE_LEAFLIST;
- LY_ARRAY_NEW_RET(cur_mod->ctx, *predicates, p, LY_EMEM);
+ LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
/* '=' */
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
@@ -490,22 +494,22 @@
} else {
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER);
if (!(ctx_node->nodetype & (LYS_LEAFLIST | LYS_LIST))) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Positional predicate defined for %s \"%s\" in path.",
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Positional predicate defined for %s \"%s\" in path.",
lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
return LY_EINVAL;
} else if (ctx_node->flags & LYS_CONFIG_W) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Positional predicate defined for configuration"
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Positional predicate defined for configuration"
" %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
return LY_EINVAL;
}
- ++(*tok_idx);
/* new predicate */
*pred_type = LY_PATH_PREDTYPE_POSITION;
- LY_ARRAY_NEW_RET(cur_mod->ctx, *predicates, p, LY_EMEM);
+ LY_ARRAY_NEW_RET(ctx, *predicates, p, LY_EMEM);
/* syntax was already checked */
p->position = strtoull(expr->expr + expr->tok_pos[*tok_idx], (char **)&name, 10);
+ ++(*tok_idx);
/* ']' */
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
@@ -555,8 +559,8 @@
do {
/* NameTest, find the key */
- LY_CHECK_RET(ly_path_compile_prefix(cur_node->module, ctx_node, expr, *tok_idx, LY_PATH_LREF_TRUE, resolve_prefix,
- prefix_data, format, &mod, &name, &name_len));
+ LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node->module, ctx_node, expr, *tok_idx,
+ LY_PATH_LREF_TRUE, resolve_prefix, prefix_data, format, &mod, &name, &name_len));
key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
if (!key) {
LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
@@ -614,8 +618,8 @@
/* NameTest */
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST);
- LY_CHECK_RET(ly_path_compile_prefix(cur_node->module, node, expr, *tok_idx, LY_PATH_LREF_TRUE, resolve_prefix,
- prefix_data, format, &mod, &name, &name_len));
+ LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node->module, node, expr, *tok_idx,
+ LY_PATH_LREF_TRUE, resolve_prefix, prefix_data, format, &mod, &name, &name_len));
node2 = lys_find_child(node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
if (!node2) {
LOGVAL(cur_node->module->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.",
@@ -647,21 +651,24 @@
}
LY_ERR
-ly_path_compile(const struct lys_module *cur_mod, const struct lysc_node *ctx_node, const struct lyxp_expr *expr,
- uint8_t lref, ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format,
- struct ly_path **path)
+ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
+ const struct lyxp_expr *expr, uint8_t lref, uint8_t oper, uint8_t target,
+ ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format, struct ly_path **path)
{
LY_ERR ret = LY_SUCCESS;
uint16_t tok_idx = 0;
const struct lys_module *mod;
const struct lysc_node *node2, *cur_node;
- struct ly_path *p;
+ struct ly_path *p = NULL;
+ int getnext_opts;
const char *name;
size_t name_len;
- assert(cur_mod);
- assert((expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) || ctx_node);
+ assert(ctx);
+ assert((expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) || (lref == LY_PATH_LREF_FALSE) || ctx_node);
assert((lref == LY_PATH_LREF_TRUE) || (lref == LY_PATH_LREF_FALSE));
+ assert((oper == LY_PATH_OPER_INPUT) || (oper == LY_PATH_OPER_OUTPUT));
+ assert((target == LY_PATH_TARGET_SINGLE) || (target == LY_PATH_TARGET_MANY));
if (lref == LY_PATH_LREF_TRUE) {
/* remember original context node */
@@ -669,6 +676,12 @@
}
*path = NULL;
+ if (oper == LY_PATH_OPER_OUTPUT) {
+ getnext_opts = LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_OUTPUT;
+ } else {
+ getnext_opts = LYS_GETNEXT_NOSTATECHECK;
+ }
+
if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
/* absolute path */
ctx_node = NULL;
@@ -678,7 +691,7 @@
/* relative path */
while ((lref == LY_PATH_LREF_TRUE) && (expr->tokens[tok_idx] == LYXP_TOKEN_DDOT)) {
if (!ctx_node) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Too many parent references in path.");
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Too many parent references in path.");
return LY_EINVAL;
}
@@ -691,64 +704,68 @@
++tok_idx;
}
- if (ctx_node) {
- /* store the parent correctly */
- LY_ARRAY_NEW_GOTO(cur_mod->ctx, *path, p, ret, cleanup);
- p->node = lysc_data_parent(ctx_node) ? ctx_node : NULL;
- }
- /* if there is no node meaning the relative path went up to the document root, the path meaning is exactly
- * the same as an absolute path and we will even store it that way */
+ /* we are not storing the parent */
+ (void)ctx_node;
}
do {
+ /* check last compiled inner node, whether it is uniquely identified (even key-less list) */
+ if (p && (lref == LY_PATH_LREF_FALSE) && (p->node->nodetype == LYS_LIST) && !p->predicates) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.",
+ lys_nodetype2str(p->node->nodetype), p->node->name);
+ return LY_EINVAL;
+ }
+
/* get module and node name */
- LY_CHECK_GOTO(ret = ly_path_compile_prefix(cur_mod, ctx_node, expr, tok_idx, lref, resolve_prefix, prefix_data,
- format, &mod, &name, &name_len), cleanup);
+ LY_CHECK_GOTO(ret = ly_path_compile_prefix(ctx, cur_mod, ctx_node, expr, tok_idx, lref, resolve_prefix,
+ prefix_data, format, &mod, &name, &name_len), cleanup);
++tok_idx;
/* find the next node */
- node2 = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+ node2 = lys_find_child(ctx_node, mod, name, name_len, 0, getnext_opts);
if (!node2) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
ret = LY_EINVAL;
goto cleanup;
}
ctx_node = node2;
/* new path segment */
- LY_ARRAY_NEW_GOTO(cur_mod->ctx, *path, p, ret, cleanup);
+ LY_ARRAY_NEW_GOTO(ctx, *path, p, ret, cleanup);
p->node = ctx_node;
/* compile any predicates */
if (lref == LY_PATH_LREF_TRUE) {
ret = ly_path_compile_predicate_leafref(ctx_node, cur_node, expr, &tok_idx, resolve_prefix, prefix_data, format);
} else {
- ret = ly_path_compile_predicate(cur_mod, ctx_node, expr, &tok_idx, resolve_prefix, prefix_data, format,
+ ret = ly_path_compile_predicate(ctx, cur_mod, ctx_node, expr, &tok_idx, resolve_prefix, prefix_data, format,
&p->predicates, &p->pred_type);
}
LY_CHECK_GOTO(ret, cleanup);
-
- /* check whether node is uniquely identified */
- if ((lref == LY_PATH_LREF_FALSE) && (ctx_node->nodetype & (LYS_LEAFLIST | LYS_LIST)) && !p->predicates) {
- LOGVAL(cur_mod->ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.",
- lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
- return LY_EINVAL;
- }
} while (!lyxp_next_token(NULL, expr, &tok_idx, LYXP_TOKEN_OPER_PATH));
+ /* check last compiled node */
+ if ((lref == LY_PATH_LREF_FALSE) && (target == LY_PATH_TARGET_SINGLE)
+ && (p->node->nodetype & (LYS_LIST | LYS_LEAFLIST)) && !p->predicates) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.",
+ lys_nodetype2str(p->node->nodetype), p->node->name);
+ return LY_EINVAL;
+ }
+
cleanup:
if (ret) {
- ly_path_free(cur_mod->ctx, *path);
+ ly_path_free(ctx, *path);
*path = NULL;
}
return ret;
}
LY_ERR
-ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match)
+ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_SIZE_TYPE *path_idx,
+ struct lyd_node **match)
{
LY_ARRAY_SIZE_TYPE u;
- struct lyd_node *node, *target;
+ struct lyd_node *prev_node = NULL, *node, *target;
uint64_t pos;
assert(path && start);
@@ -802,16 +819,63 @@
break;
}
+ /* rememeber previous node */
+ prev_node = node;
+
/* next path segment, if any */
start = lyd_node_children(node);
}
- /* result */
- if (match) {
- *match = node;
- }
if (node) {
+ /* we have found the full path */
+ if (path_idx) {
+ *path_idx = u;
+ }
+ if (match) {
+ *match = node;
+ }
return LY_SUCCESS;
+
+ } else if (prev_node) {
+ /* we have found only some partial match */
+ if (path_idx) {
+ *path_idx = u - 1;
+ }
+ if (match) {
+ *match = prev_node;
+ }
+ return LY_EINCOMPLETE;
+ }
+
+ /* we have not found any nodes */
+ if (path_idx) {
+ *path_idx = 0;
+ }
+ if (match) {
+ *match = NULL;
+ }
+ return LY_ENOTFOUND;
+}
+
+LY_ERR
+ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match)
+{
+ LY_ERR ret;
+ struct lyd_node *m;
+
+ ret = ly_path_eval_partial(path, start, NULL, &m);
+
+ if (ret == LY_SUCCESS) {
+ /* last node was found */
+ if (match) {
+ *match = m;
+ }
+ return LY_SUCCESS;
+ }
+
+ /* not a full match */
+ if (match) {
+ *match = NULL;
}
return LY_ENOTFOUND;
}
diff --git a/src/path.h b/src/path.h
index c973add..c75387c 100644
--- a/src/path.h
+++ b/src/path.h
@@ -18,6 +18,7 @@
#include <stdint.h>
#include "log.h"
+#include "tree.h"
#include "tree_data.h"
struct lysc_node;
@@ -116,25 +117,47 @@
uint8_t pred, struct lyxp_expr **expr);
/**
+ * @defgroup path_oper_options Path operation options.
+ * @{
+ */
+#define LY_PATH_OPER_INPUT 0x01 /**< if any RPC/action is traversed, its input nodes are used */
+#define LY_PATH_OPER_OUTPUT 0x02 /**< if any RPC/action is traversed, its output nodes are used */
+/** @} */
+
+/* lref */
+
+/**
+ * @defgroup path_target_options Path target options.
+ * @{
+ */
+#define LY_PATH_TARGET_SINGLE 0x10 /**< last (target) node must identify an exact instance */
+#define LY_PATH_TARGET_MANY 0x20 /**< last (target) node may identify all instances (of leaf-list/list) */
+/** @} */
+
+/**
* @brief Compile path into ly_path structure. Any predicates of a leafref are only checked, not compiled.
*
+ * @param[in] ctx libyang context.
* @param[in] cur_mod Module of the current (original context) node. Used for nodes without prefix for ::LYD_SCHEMA format.
* @param[in] ctx_node Context node. Can be NULL for absolute paths.
* @param[in] expr Parsed path.
* @param[in] lref Lref option (@ref path_lref_options).
+ * @param[in] oper Oper option (@ref path_oper_options).
+ * @param[in] target Target option (@ref path_target_options).
* @param[in] resolve_prefix Callback for prefix resolution.
* @param[in] prefix_data Data for @p resolve_prefix.
* @param[in] format Format of the path.
* @param[out] path Compiled path.
* @return LY_ERR value.
*/
-LY_ERR ly_path_compile(const struct lys_module *cur_mod, const struct lysc_node *ctx_node, const struct lyxp_expr *expr,
- uint8_t lref, ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format,
- struct ly_path **path);
+LY_ERR ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
+ const struct lyxp_expr *expr, uint8_t lref, uint8_t oper, uint8_t target,
+ ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format, struct ly_path **path);
/**
* @brief Compile predicate into ly_path_predicate structure. Only simple predicates (not leafref) are supported.
*
+ * @param[in] ctx libyang context.
* @param[in] cur_mod Module of the current (original context) node. Used for nodes without prefix for ::LYD_SCHEMA format.
* @param[in] ctx_node Context node, node for which the predicate is defined.
* @param[in] expr Parsed path.
@@ -146,10 +169,25 @@
* @param[out] pred_type Type of the compiled predicate(s).
* @return LY_ERR value.
*/
-LY_ERR ly_path_compile_predicate(const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
- const struct lyxp_expr *expr, uint16_t *tok_idx, ly_clb_resolve_prefix resolve_prefix,
- void *prefix_data, LYD_FORMAT format, struct ly_path_predicate **predicates,
- enum ly_path_pred_type *pred_type);
+LY_ERR ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lys_module *cur_mod,
+ const struct lysc_node *ctx_node, const struct lyxp_expr *expr, uint16_t *tok_idx,
+ ly_clb_resolve_prefix resolve_prefix, void *prefix_data, LYD_FORMAT format,
+ struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type);
+
+/**
+ * @brief Resolve at least partially the target defined by ly_path structure. Not supported for leafref!
+ *
+ * @param[in] path Path structure specifying the target.
+ * @param[in] start Starting node for relative paths, can be any for absolute paths.
+ * @param[out] path_idx Last found path segment index, can be NULL, set to 0 if not found.
+ * @param[out] match Last found matching node, can be NULL, set to NULL if not found.
+ * @return LY_ENOTFOUND if no nodes were found,
+ * @return LY_EINCOMPLETE if some node was found but not the last one,
+ * @return LY_SUCCESS when the last node in the path was found,
+ * @return LY_ERR on another error.
+ */
+LY_ERR ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_SIZE_TYPE *path_idx,
+ struct lyd_node **match);
/**
* @brief Resolve the target defined by ly_path structure. Not supported for leafref!
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 894c6b6..2ec1d2a 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -1420,7 +1420,7 @@
struct ly_path *path = NULL;
struct ly_set predicates = {0};
struct lyxp_expr *exp = NULL;
- const struct lys_module *cur_mod;
+ const struct lysc_node *ctx_scnode;
/* init */
*err = NULL;
@@ -1464,9 +1464,11 @@
}
/* resolve it on schema tree */
- cur_mod = (options & (LY_TYPE_OPTS_SCHEMA | LY_TYPE_OPTS_INCOMPLETE_DATA)) ?
- ((struct lysc_node *)context_node)->module : ((struct lyd_node *)context_node)->schema->module;
- ret = ly_path_compile(cur_mod, NULL, exp, LY_PATH_LREF_FALSE, ly_type_stored_prefixes_clb, prefixes, format, &path);
+ ctx_scnode = (options & (LY_TYPE_OPTS_SCHEMA | LY_TYPE_OPTS_INCOMPLETE_DATA)) ?
+ (struct lysc_node *)context_node : ((struct lyd_node *)context_node)->schema;
+ ret = ly_path_compile(ctx, ctx_scnode->module, NULL, exp, LY_PATH_LREF_FALSE, lysc_is_output(ctx_scnode) ?
+ LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, ly_type_stored_prefixes_clb,
+ prefixes, format, &path);
if (ret) {
asprintf(&errmsg, "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, value);
goto error;
diff --git a/src/tree_data.c b/src/tree_data.c
index eb6a5fe..351bb9d 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -46,16 +46,6 @@
#include "xml.h"
#include "xpath.h"
-struct ly_keys {
- char *str;
- struct {
- const struct lysc_node_leaf *schema;
- char *value;
- struct lyd_value val;
- } *keys;
- size_t key_count;
-};
-
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 *tree)
@@ -95,7 +85,7 @@
return ret;
}
-/* similar to lyd_value_parse except can be used just to store the value, hence does also not support a second call */
+/* similar to lyd_value_parse except can be used just to store the value, hence also does not support a second call */
LY_ERR
lyd_value_store(struct lyd_value *val, const struct lysc_node *schema, const char *value, size_t value_len, int *dynamic,
ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format)
@@ -434,6 +424,7 @@
struct lysc_type *type;
assert(schema->nodetype & LYD_NODE_TERM);
+ assert(val && val->realtype);
term = calloc(1, sizeof *term);
LY_CHECK_ERR_RET(!term, LOGMEM(schema->module->ctx), LY_EMEM);
@@ -449,6 +440,7 @@
free(term);
return ret;
}
+ term->value.realtype = val->realtype;
lyd_hash((struct lyd_node *)term);
*node = (struct lyd_node *)term;
@@ -478,174 +470,6 @@
return LY_SUCCESS;
}
-static void
-ly_keys_clean(struct ly_keys *keys)
-{
- size_t i;
-
- for (i = 0; i < keys->key_count; ++i) {
- keys->keys[i].schema->type->plugin->free(keys->keys[i].schema->module->ctx, &keys->keys[i].val);
- }
- free(keys->str);
- free(keys->keys);
-}
-
-static char *
-ly_keys_parse_next(char **next_key, char **key_name)
-{
- char *ptr, *ptr2, *val, quot;
- const char *pref;
- size_t pref_len, key_len;
- int have_equal = 0;
-
- ptr = *next_key;
-
- /* "[" */
- LY_CHECK_GOTO(ptr[0] != '[', error);
- ++ptr;
-
- /* skip WS */
- while (isspace(ptr[0])) {
- ++ptr;
- }
-
- /* key name without prefix */
- LY_CHECK_GOTO(ly_parse_nodeid((const char **)&ptr, &pref, &pref_len, (const char **)key_name, &key_len), error);
- if (pref) {
- goto error;
- }
-
- /* terminate it */
- LY_CHECK_GOTO((ptr[0] != '=') && !isspace(ptr[0]), error);
- if (ptr[0] == '=') {
- have_equal = 1;
- }
- ptr[0] = '\0';
- ++ptr;
-
- if (!have_equal) {
- /* skip WS */
- while (isspace(ptr[0])) {
- ++ptr;
- }
-
- /* '=' */
- LY_CHECK_GOTO(ptr[0] != '=', error);
- ++ptr;
- }
-
- /* skip WS */
- while (isspace(ptr[0])) {
- ++ptr;
- }
-
- /* quote */
- LY_CHECK_GOTO((ptr[0] != '\'') && (ptr[0] != '\"'), error);
- quot = ptr[0];
- ++ptr;
-
- /* value, terminate it */
- val = ptr;
- ptr2 = strchr(ptr, quot);
- LY_CHECK_GOTO(!ptr2, error);
- ptr2[0] = '\0';
-
- /* \0, was quote */
- ptr = ptr2 + 1;
-
- /* skip WS */
- while (isspace(ptr[0])) {
- ++ptr;
- }
-
- /* "]" */
- LY_CHECK_GOTO(ptr[0] != ']', error);
- ++ptr;
-
- *next_key = ptr;
- return val;
-
-error:
- *next_key = ptr;
- return NULL;
-}
-
-/* fill keys structure that is expected to be zeroed and must always be cleaned (even on error);
- * if store is set, fill also each val */
-static LY_ERR
-ly_keys_parse(const struct lysc_node *list, const char *keys_str, size_t keys_len, int store, int log,
- struct ly_keys *keys)
-{
- LY_ERR ret = LY_SUCCESS;
- char *next_key, *name;
- const struct lysc_node *key;
- size_t i;
-
- assert(list->nodetype == LYS_LIST);
-
- 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;
- while (next_key[0]) {
- /* new key */
- keys->keys = ly_realloc(keys->keys, (keys->key_count + 1) * sizeof *keys->keys);
- LY_CHECK_ERR_GOTO(!keys->keys, LOGMEM(list->module->ctx); ret = LY_EMEM, cleanup);
-
- /* fill */
- keys->keys[keys->key_count].value = ly_keys_parse_next(&next_key, &name);
- if (!keys->keys[keys->key_count].value) {
- if (log) {
- LOGERR(list->module->ctx, LY_EINVAL, "Invalid keys string (at \"%s\").", next_key);
- }
- ret = LY_EINVAL;
- goto cleanup;
- }
-
- /* find schema node */
- key = lys_find_child(list, list->module, name, 0, LYS_LEAF, 0);
- if (!key) {
- if (log) {
- LOGERR(list->module->ctx, LY_EINVAL, "List \"%s\" has no key \"%s\".", list->name, name);
- }
- ret = LY_EINVAL;
- goto cleanup;
- }
- keys->keys[keys->key_count].schema = (const struct lysc_node_leaf *)key;
-
- /* check that we do not have it already */
- for (i = 0; i < keys->key_count; ++i) {
- if (keys->keys[i].schema == keys->keys[keys->key_count].schema) {
- if (log) {
- LOGERR(list->module->ctx, LY_EINVAL, "Duplicit key \"%s\" value.", name);
- }
- ret = LY_EINVAL;
- goto cleanup;
- }
- }
-
- if (store) {
- /* store the value */
- ret = lyd_value_store(&keys->keys[keys->key_count].val, key, keys->keys[keys->key_count].value, 0, 0,
- lydjson_resolve_prefix, NULL, LYD_JSON);
- LY_CHECK_GOTO(ret, cleanup);
- } else {
- memset(&keys->keys[keys->key_count].val, 0, sizeof keys->keys[keys->key_count].val);
- }
-
- /* another valid key */
- ++keys->key_count;
- }
-
-cleanup:
- return ret;
-}
-
LY_ERR
lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates, struct lyd_node **node)
{
@@ -690,8 +514,8 @@
LY_PATH_PRED_KEYS, &expr), cleanup);
/* compile them */
- LY_CHECK_GOTO(ret = ly_path_compile_predicate(schema->module, schema, expr, &exp_idx, lydjson_resolve_prefix, NULL,
- LYD_JSON, &predicates, &pred_type), cleanup);
+ LY_CHECK_GOTO(ret = ly_path_compile_predicate(schema->module->ctx, NULL, schema, expr, &exp_idx, lydjson_resolve_prefix,
+ NULL, LYD_JSON, &predicates, &pred_type), cleanup);
/* create the list node */
LY_CHECK_GOTO(ret = lyd_create_list(schema, predicates, node), cleanup);
@@ -916,16 +740,81 @@
return ret;
}
+static LY_ERR
+lyd_new_path_update(struct lyd_node *node, const void *value, LYD_ANYDATA_VALUETYPE value_type,
+ struct lyd_node **new_parent, struct lyd_node **new_node)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *new_any;
+
+ switch (node->schema->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_NOTIF:
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_LIST:
+ case LYS_LEAFLIST:
+ /* if it exists, there is nothing to update */
+ *new_parent = NULL;
+ *new_node = NULL;
+ break;
+ case LYS_LEAF:
+ ret = lyd_change_term(node, value);
+ if ((ret == LY_SUCCESS) || (ret == LY_EEXIST)) {
+ /* there was an actual change (at least of the default flag) */
+ *new_parent = node;
+ *new_node = node;
+ ret = LY_SUCCESS;
+ } else if (ret == LY_ENOT) {
+ /* no change */
+ *new_parent = NULL;
+ *new_node = NULL;
+ ret = LY_SUCCESS;
+ } /* else error */
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ /* create a new any node */
+ LY_CHECK_RET(lyd_create_any(node->schema, value, value_type, &new_any));
+
+ /* compare with the existing one */
+ if (lyd_compare(node, new_any, 0)) {
+ /* not equal, switch values (so that we can use generic node free) */
+ ((struct lyd_node_any *)new_any)->value = ((struct lyd_node_any *)node)->value;
+ ((struct lyd_node_any *)new_any)->value_type = ((struct lyd_node_any *)node)->value_type;
+ ((struct lyd_node_any *)node)->value.str = value;
+ ((struct lyd_node_any *)node)->value_type = value_type;
+
+ *new_parent = node;
+ *new_node = node;
+ } else {
+ /* they are equal */
+ *new_parent = NULL;
+ *new_node = NULL;
+ }
+ lyd_free_tree(new_any);
+ break;
+ default:
+ LOGINT(LYD_NODE_CTX(node));
+ ret = LY_EINT;
+ break;
+ }
+
+ return ret;
+}
+
API struct lyd_meta *
lyd_new_meta(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *val_str)
{
struct lyd_meta *ret = NULL;
- struct ly_ctx *ctx = parent->schema->module->ctx;
+ const struct ly_ctx *ctx;
const char *prefix, *tmp;
char *str;
size_t pref_len, name_len;
- LY_CHECK_ARG_RET(ctx, parent, name, module || strchr(name, ':'), NULL);
+ LY_CHECK_ARG_RET(NULL, parent, name, module || strchr(name, ':'), NULL);
+
+ ctx = LYD_NODE_CTX(parent);
/* parse the name */
tmp = name;
@@ -952,6 +841,330 @@
return ret;
}
+API struct lyd_node *
+lyd_new_opaq(struct lyd_node *parent, const struct ly_ctx *ctx, const char *name, const char *value,
+ const char *module_name)
+{
+ struct lyd_node *ret = NULL;
+
+ LY_CHECK_ARG_RET(ctx, parent || ctx, name, module_name, NULL);
+
+ if (!ctx) {
+ ctx = LYD_NODE_CTX(parent);
+ }
+ if (!value) {
+ value = "";
+ }
+
+ if (!lyd_create_opaq(ctx, name, strlen(name), value, strlen(value), NULL, LYD_JSON, NULL, NULL, 0, module_name, &ret)
+ && parent) {
+ lyd_insert_node(parent, NULL, ret);
+ }
+ return ret;
+}
+
+API struct ly_attr *
+lyd_new_attr(struct lyd_node *parent, const char *module_name, const char *name, const char *val_str)
+{
+ struct ly_attr *ret = NULL;
+ const struct ly_ctx *ctx;
+ const char *prefix, *tmp;
+ size_t pref_len, name_len;
+
+ LY_CHECK_ARG_RET(NULL, parent, !parent->schema, name, NULL);
+
+ ctx = LYD_NODE_CTX(parent);
+
+ /* parse the name */
+ tmp = name;
+ if (ly_parse_nodeid(&tmp, &prefix, &pref_len, &name, &name_len) || tmp[0]) {
+ LOGERR(ctx, LY_EINVAL, "Metadata name \"%s\" is not valid.", name);
+ return NULL;
+ }
+
+ /* set value if none */
+ if (!val_str) {
+ val_str = "";
+ }
+
+ ly_create_attr(parent, &ret, ctx, name, name_len, val_str, strlen(val_str), NULL, LYD_JSON, NULL, prefix,
+ pref_len, module_name);
+ return ret;
+}
+
+API LY_ERR
+lyd_change_term(struct lyd_node *term, const char *val_str)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type *type;
+ struct lyd_node_term *t;
+ struct lyd_node *parent;
+ struct lyd_value val = {0};
+ int dflt_change, val_change;
+
+ LY_CHECK_ARG_RET(NULL, term, term->schema, term->schema->nodetype & LYD_NODE_TERM, LY_EINVAL);
+
+ if (!val_str) {
+ val_str = "";
+ }
+ t = (struct lyd_node_term *)term;
+ type = ((struct lysc_node_leaf *)term->schema)->type;
+
+ /* parse the new value */
+ LY_CHECK_GOTO(ret = lyd_value_store(&val, term->schema, val_str, strlen(val_str), NULL, lydjson_resolve_prefix, NULL,
+ LYD_JSON), cleanup);
+
+ /* compare original and new value */
+ if (type->plugin->compare(&t->value, &val)) {
+ /* values differ, switch them */
+ type->plugin->free(LYD_NODE_CTX(term), &t->value);
+ t->value = val;
+ memset(&val, 0, sizeof val);
+ val_change = 1;
+ } else {
+ val_change = 0;
+ }
+
+ /* always clear the default flag */
+ if (term->flags & LYD_DEFAULT) {
+ for (parent = term; parent; parent = (struct lyd_node *)parent->parent) {
+ parent->flags &= ~LYD_DEFAULT;
+ }
+ dflt_change = 1;
+ } else {
+ dflt_change = 0;
+ }
+
+ if (val_change || dflt_change) {
+ /* make the node non-validated */
+ term->flags &= LYD_NEW;
+ }
+
+ if (val_change) {
+ if (term->schema->nodetype == LYS_LEAFLIST) {
+ /* leaf-list needs to be hashed again and re-inserted into parent */
+ lyd_unlink_hash(term);
+ lyd_hash(term);
+ LY_CHECK_GOTO(ret = lyd_insert_hash(term), cleanup);
+ } else if ((term->schema->flags & LYS_KEY) && term->parent) {
+ /* list needs to be updated if its key was changed */
+ assert(term->parent->schema->nodetype == LYS_LIST);
+ lyd_unlink_hash((struct lyd_node *)term->parent);
+ lyd_hash((struct lyd_node *)term->parent);
+ LY_CHECK_GOTO(ret = lyd_insert_hash((struct lyd_node *)term->parent), cleanup);
+ } /* else leaf that is not a key, its value is not used for its hash so it does not change */
+ }
+
+ /* retrun value */
+ if (!val_change) {
+ if (dflt_change) {
+ /* only default flag change */
+ ret = LY_EEXIST;
+ } else {
+ /* no change */
+ ret = LY_ENOT;
+ }
+ } /* else value changed, LY_SUCCESS */
+
+cleanup:
+ type->plugin->free(LYD_NODE_CTX(term), &val);
+ return ret;
+}
+
+API struct lyd_node *
+lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const char *value, int options)
+{
+ struct lyd_node *new_node = NULL;
+
+ lyd_new_path2(parent, ctx, path, value, 0, options, NULL, &new_node);
+ return new_node;
+}
+
+API struct lyd_node *
+lyd_new_path_any(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
+ LYD_ANYDATA_VALUETYPE value_type, int options)
+{
+ struct lyd_node *new_node = NULL;
+
+ lyd_new_path2(parent, ctx, path, value, value_type, options, NULL, &new_node);
+ return new_node;
+}
+
+API LY_ERR
+lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
+ LYD_ANYDATA_VALUETYPE value_type, int options, struct lyd_node **new_parent, struct lyd_node **new_node)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ struct lyxp_expr *exp = NULL;
+ struct ly_path *p = NULL;
+ struct lyd_node *nparent = NULL, *nnode = NULL, *node = NULL, *cur_parent;
+ const struct lysc_node *schema;
+ LY_ARRAY_SIZE_TYPE path_idx = 0;
+ struct ly_path_predicate *pred;
+
+ LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, LY_EINVAL);
+
+ if (!ctx) {
+ ctx = LYD_NODE_CTX(parent);
+ }
+
+ /* parse path */
+ LY_CHECK_GOTO(ret = ly_path_parse(ctx, path, strlen(path), LY_PATH_BEGIN_EITHER, LY_PATH_LREF_FALSE,
+ LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_SIMPLE, &exp), cleanup);
+
+ /* compile path */
+ LY_CHECK_GOTO(ret = ly_path_compile(ctx, NULL, parent ? parent->schema : NULL, exp, LY_PATH_LREF_FALSE,
+ options & LYD_NEWOPT_OUTPUT ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT,
+ LY_PATH_TARGET_MANY, lydjson_resolve_prefix, NULL, LYD_JSON, &p), cleanup);
+
+ schema = p[LY_ARRAY_SIZE(p) - 1].node;
+ if ((schema->nodetype == LYS_LIST) && (p[LY_ARRAY_SIZE(p) - 1].pred_type == LY_PATH_PREDTYPE_NONE)
+ && !(options & LYD_NEWOPT_OPAQ)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.",
+ lys_nodetype2str(schema->nodetype), schema->name);
+ ret = LY_EINVAL;
+ goto cleanup;
+ } else if ((schema->nodetype == LYS_LEAFLIST) && (p[LY_ARRAY_SIZE(p) - 1].pred_type == LY_PATH_PREDTYPE_NONE)) {
+ /* parse leafref value into a predicate, if not defined in the path */
+ p[LY_ARRAY_SIZE(p) - 1].pred_type = LY_PATH_PREDTYPE_LEAFLIST;
+ LY_ARRAY_NEW_GOTO(ctx, p[LY_ARRAY_SIZE(p) - 1].predicates, pred, ret, cleanup);
+
+ if (!value) {
+ value = "";
+ }
+
+ r = LY_SUCCESS;
+ if (options & LYD_NEWOPT_OPAQ) {
+ r = lys_value_validate(NULL, schema, value, strlen(value), lydjson_resolve_prefix, NULL, LYD_JSON);
+ }
+ if (!r) {
+ LY_CHECK_GOTO(ret = lyd_value_store(&pred->value, schema, value, strlen(value), NULL, lydjson_resolve_prefix,
+ NULL, LYD_JSON), cleanup);
+ } /* else we have opaq flag and the value is not valid, leavne no predicate and then create an opaque node */
+ }
+
+ /* try to find any existing nodes in the path */
+ if (parent) {
+ ret = ly_path_eval_partial(p, parent, &path_idx, &node);
+ if (ret == LY_SUCCESS) {
+ /* the node exists, are we supposed to update it or is it just a default? */
+ if (!(options & LYD_NEWOPT_UPDATE) && !(node->flags & LYD_DEFAULT)) {
+ LOGERR(ctx, LY_EEXIST, "Path \"%s\" already exists", path);
+ ret = LY_EEXIST;
+ goto cleanup;
+ }
+
+ /* update the existing node */
+ ret = lyd_new_path_update(node, value, value_type, &nparent, &nnode);
+ goto cleanup;
+ } else if (ret == LY_EINCOMPLETE) {
+ /* some nodes were found, adjust the iterator to the next segment */
+ ++path_idx;
+ } else if (ret == LY_ENOTFOUND) {
+ /* we will create the nodes from top-level, default behavior (absolute path), or from the parent (relative path) */
+ if (lysc_data_parent(p[LY_ARRAY_SIZE(p) - 1].node)) {
+ node = parent;
+ }
+ } else {
+ /* error */
+ goto cleanup;
+ }
+ }
+
+ /* create all the non-existing nodes in a loop */
+ for (; path_idx < LY_ARRAY_SIZE(p); ++path_idx) {
+ cur_parent = node;
+ schema = p[path_idx].node;
+
+ switch (schema->nodetype) {
+ case LYS_LIST:
+ if (!(schema->flags & LYS_KEYLESS)) {
+ if ((options & LYD_NEWOPT_OPAQ) && (p[path_idx].pred_type == LY_PATH_PREDTYPE_NONE)) {
+ /* creating opaque list without keys */
+ LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL,
+ LYD_JSON, NULL, NULL, 0, schema->module->name, &node), cleanup);
+ } else {
+ assert(p[path_idx].pred_type == LY_PATH_PREDTYPE_LIST);
+ LY_CHECK_GOTO(ret = lyd_create_list(schema, p[path_idx].predicates, &node), cleanup);
+ }
+ break;
+ }
+ /* fallthrough */
+ case LYS_CONTAINER:
+ case LYS_NOTIF:
+ case LYS_RPC:
+ case LYS_ACTION:
+ LY_CHECK_GOTO(ret = lyd_create_inner(schema, &node), cleanup);
+ break;
+ case LYS_LEAFLIST:
+ if ((options & LYD_NEWOPT_OPAQ) && (p[path_idx].pred_type == LY_PATH_PREDTYPE_NONE)) {
+ /* creating opaque leaf-list without value */
+ LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL,
+ LYD_JSON, NULL, NULL, 0, schema->module->name, &node), cleanup);
+ } else {
+ assert(p[path_idx].pred_type == LY_PATH_PREDTYPE_LEAFLIST);
+ LY_CHECK_GOTO(ret = lyd_create_term2(schema, &p[path_idx].predicates[0].value, &node), cleanup);
+ }
+ break;
+ case LYS_LEAF:
+ /* make there is some value */
+ if (!value) {
+ value = "";
+ }
+
+ r = LY_SUCCESS;
+ if (options & LYD_NEWOPT_OPAQ) {
+ r = lys_value_validate(NULL, schema, value, strlen(value), lydjson_resolve_prefix, NULL, LYD_JSON);
+ }
+ if (!r) {
+ LY_CHECK_GOTO(ret = lyd_create_term(schema, value, strlen(value), NULL, lydjson_resolve_prefix, NULL,
+ LYD_JSON, &node), cleanup);
+ } else {
+ /* creating opaque leaf without value */
+ LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, NULL,
+ LYD_JSON, NULL, NULL, 0, schema->module->name, &node), cleanup);
+ }
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ LY_CHECK_GOTO(ret = lyd_create_any(schema, value, value_type, &node), cleanup);
+ break;
+ default:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ goto cleanup;
+ }
+
+ if (cur_parent) {
+ /* connect to the parent */
+ lyd_insert_node(cur_parent, NULL, node);
+ } else if (parent) {
+ /* connect to top-level siblings */
+ lyd_insert_node(NULL, &parent, node);
+ }
+
+ /* update remembered nodes */
+ if (!nparent) {
+ nparent = node;
+ }
+ nnode = node;
+ }
+
+cleanup:
+ lyxp_expr_free(ctx, exp);
+ ly_path_free(ctx, p);
+ if (!ret) {
+ /* set out params only on success */
+ if (new_parent) {
+ *new_parent = nparent;
+ }
+ if (new_node) {
+ *new_node = nnode;
+ }
+ }
+ return ret;
+}
+
struct lyd_node *
lyd_get_prev_key_anchor(const struct lyd_node *first_sibling, const struct lysc_node *new_key)
{
@@ -2214,9 +2427,12 @@
LY_ERR rc;
const struct lyd_node *node = NULL;
struct lyd_node_term *term;
- struct ly_keys keys = {0};
+ struct lyxp_expr *expr = NULL;
+ uint16_t exp_idx = 0;
+ struct ly_path_predicate *predicates = NULL;
+ enum ly_path_pred_type pred_type = 0;
struct lyd_value val = {0};
- size_t i;
+ LY_ARRAY_SIZE_TYPE u;
LY_CHECK_ARG_RET(NULL, schema, LY_EINVAL);
@@ -2236,8 +2452,13 @@
/* store the value */
LY_CHECK_GOTO(rc = lyd_value_store(&val, schema, key_or_value, val_len, 0, lydjson_resolve_prefix, NULL, LYD_JSON), cleanup);
} else if (key_or_value && (schema->nodetype == LYS_LIST)) {
- /* parse keys into canonical values */
- LY_CHECK_GOTO(rc = ly_keys_parse(schema, key_or_value, val_len, 1, 1, &keys), cleanup);
+ /* parse keys */
+ LY_CHECK_GOTO(rc = ly_path_parse_predicate(schema->module->ctx, key_or_value, val_len, LY_PATH_PREFIX_OPTIONAL,
+ LY_PATH_PRED_KEYS, &expr), cleanup);
+
+ /* compile them */
+ LY_CHECK_GOTO(rc = ly_path_compile_predicate(schema->module->ctx, NULL, schema, expr, &exp_idx, lydjson_resolve_prefix,
+ NULL, LYD_JSON, &predicates, &pred_type), cleanup);
}
/* find first matching value */
@@ -2246,12 +2467,11 @@
continue;
}
- if ((schema->nodetype == LYS_LIST) && keys.str) {
+ if ((schema->nodetype == LYS_LIST) && predicates) {
/* compare all set keys */
- for (i = 0; i < keys.key_count; ++i) {
+ LY_ARRAY_FOR(predicates, u) {
/* find key */
- rc = lyd_find_sibling_val(lyd_node_children(node), (struct lysc_node *)keys.keys[i].schema, NULL, 0,
- (struct lyd_node **)&term);
+ rc = lyd_find_sibling_val(lyd_node_children(node), predicates[u].key, NULL, 0, (struct lyd_node **)&term);
if (rc == LY_ENOTFOUND) {
/* all keys must always exist */
LOGINT_RET(schema->module->ctx);
@@ -2259,12 +2479,12 @@
LY_CHECK_GOTO(rc, cleanup);
/* compare values */
- if (!term->value.realtype->plugin->compare(&term->value, &keys.keys[i].val)) {
+ if (!term->value.realtype->plugin->compare(&term->value, &predicates[u].value)) {
break;
}
}
- if (i < keys.key_count) {
+ if (u < LY_ARRAY_SIZE(predicates)) {
/* not a match */
continue;
}
@@ -2297,7 +2517,8 @@
rc = LY_SUCCESS;
cleanup:
- ly_keys_clean(&keys);
+ ly_path_predicates_free(schema->module->ctx, pred_type, NULL, predicates);
+ lyxp_expr_free(schema->module->ctx, expr);
if (val.realtype) {
val.realtype->plugin->free(schema->module->ctx, &val);
}
diff --git a/src/tree_data.h b/src/tree_data.h
index 34524dc..803b610 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -401,7 +401,7 @@
void *priv; /**< private user data, not used by libyang */
#endif
- union {
+ union lyd_any_value {
struct lyd_node *tree; /**< data tree */
const char *str; /**< Generic string data */
const char *xml; /**< Serialized XML data */
@@ -629,7 +629,7 @@
LY_ERR lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *tree, int val_opts);
/**
- * @brief Create a new inner node in a data tree.
+ * @brief Create a new inner node in the 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 of the node being created. If NULL, @p parent module will be used.
@@ -640,7 +640,7 @@
struct lyd_node *lyd_new_inner(struct lyd_node *parent, const struct lys_module *module, const char *name);
/**
- * @brief Create a new list node in a data tree.
+ * @brief Create a new list node in the 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 of the node being created. If NULL, @p parent module will be used.
@@ -654,7 +654,7 @@
struct lyd_node *lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const char *name, ...);
/**
- * @brief Create a new list node in a data tree.
+ * @brief Create a new list node in the 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 of the node being created. If NULL, @p parent module will be used.
@@ -668,7 +668,7 @@
struct lyd_node *lyd_new_list2(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *keys);
/**
- * @brief Create a new term node in a data tree.
+ * @brief Create a new term node in the 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 of the node being created. If NULL, @p parent module will be used.
@@ -681,7 +681,7 @@
struct lyd_node *lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *val_str);
/**
- * @brief Create a new any node in a data tree.
+ * @brief Create a new any node in the 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 of the node being created. If NULL, @p parent module will be used.
@@ -698,18 +698,142 @@
* @brief Create new metadata for a data node.
*
* @param[in] parent Parent node for the metadata being created.
- * @param[in] module Module of the metdata being created. If NULL, @p name must include module name as the prefix.
+ * @param[in] module Module of the metadata being created. If NULL, @p name must include module name as the prefix.
* @param[in] name Annotation name of the new metadata. It can include the annotation module as the prefix.
* If the prefix is specified it is always used but if not specified, @p module must be set.
- * @param[in] val_str String form of the value of the metadata being created. In case of an instance-identifier or identityref
+ * @param[in] val_str String form of the value of the metadata. In case of an instance-identifier or identityref
* value, the JSON format is expected (module names instead of prefixes).
- * @return New created metadata in the @p parent.
+ * @return New created metadata of @p parent.
* @return NULL on error.
*/
struct lyd_meta *lyd_new_meta(struct lyd_node *parent, const struct lys_module *module, const char *name,
const char *val_str);
/**
+ * @brief Create a new opaque node in the data tree.
+ *
+ * @param[in] parent Parent node for the node beaing created. NULL in case of creating a top level element.
+ * @param[in] ctx libyang context. If NULL, @p parent context will be used.
+ * @param[in] name Node name.
+ * @param[in] value Node value, may be NULL.
+ * @param[in] module_name Node module name.
+ * @return New created node.
+ * @return NULL on error.
+ */
+struct lyd_node *lyd_new_opaq(struct lyd_node *parent, const struct ly_ctx *ctx, const char *name, const char *value,
+ const char *module_name);
+
+/**
+ * @brief Create new attribute for an opaque data node.
+ *
+ * @param[in] parent Parent opaque node for the attribute being created.
+ * @param[in] module Module name of the attribute being created. There may be none.
+ * @param[in] name Attribute name. It can include the module name as the prefix.
+ * @param[in] val_str String value of the attribute. Is stored directly.
+ * @return New created attribute of @p parent.
+ * @return NULL on error.
+ */
+struct ly_attr *lyd_new_attr(struct lyd_node *parent, const char *module_name, const char *name, const char *val_str);
+
+/**
+ * @defgroup pathoptions Data path creation options
+ * @ingroup datatree
+ *
+ * Various options to change lyd_new_path*() behavior.
+ *
+ * Default behavior:
+ * - if the target node already exists (and is not default), an error is returned.
+ * - the whole path to the target node is created (with any missing parents) if necessary.
+ * - RPC output schema children are completely ignored in all modules. Input is searched and nodes created normally.
+ * @{
+ */
+
+#define LYD_NEWOPT_UPDATE 0x01 /**< If the target node exists, is a leaf, and it is updated with a new value or its
+ default flag is changed, it is returned. If the target node exists and is not
+ a leaf or generally no change occurs in the @p parent tree, NULL is returned and
+ no error set. */
+#define LYD_NEWOPT_OUTPUT 0x02 /**< Changes the behavior to ignoring RPC/action input schema nodes and using only
+ output ones. */
+#define LYD_NEWOPT_OPAQ 0x04 /**< Enables the creation of opaque nodes with some specific rules. If the __last node__
+ in the path is not uniquely defined ((leaf-)list without a predicate) or has an
+ invalid value (leaf/leaf-list), it is created as opaque. */
+
+/** @} pathoptions */
+
+/**
+ * @brief Create a new node in the data tree based on a path. Cannot be used for anyxml/anydata nodes,
+ * for those use ::lyd_new_path_any.
+ *
+ * If @p path points to a list key and the list instance does not exist, the key value from the predicate is used
+ * and @p value is ignored. Also, if a leaf-list is being created and both a predicate is defined in @p path
+ * and @p value is set, the predicate is preferred.
+ *
+ * @param[in] parent Data parent to add to/modify, can be NULL.
+ * @param[in] ctx libyang context, must be set if @p parent is NULL.
+ * @param[in] path Path to create (TODO ref path).
+ * @param[in] value Value of the new leaf/leaf-list. For other node types, it is ignored.
+ * @param[in] options Bitmask of options, see @ref pathoptions.
+ * @return (Last) created node.
+ * @return NULL on error.
+ */
+struct lyd_node *lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const char *value,
+ int options);
+
+/**
+ * @brief Create a new node in the data tree based on a path. All node types can be created.
+ *
+ * If @p path points to a list key and the list instance does not exist, the key value from the predicate is used
+ * and @p value is ignored. Also, if a leaf-list is being created and both a predicate is defined in @p path
+ * and @p value is set, the predicate is preferred.
+ *
+ * @param[in] parent Data parent to add to/modify, can be NULL.
+ * @param[in] ctx libyang context, must be set if @p parent is NULL.
+ * @param[in] path Path to create (TODO ref path).
+ * @param[in] value Value of the new leaf/leaf-list/anyxml/anydata. For other node types, it is ignored.
+ * @param[in] value_type Anyxml/anydata node @p value type.
+ * @param[in] options Bitmask of options, see @ref pathoptions.
+ * @return (Last) created node.
+ * @return NULL on error.
+ */
+struct lyd_node *lyd_new_path_any(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
+ LYD_ANYDATA_VALUETYPE value_type, int options);
+
+/**
+ * @brief Create a new node in the data tree based on a path. All node types can be created.
+ *
+ * If @p path points to a list key and the list instance does not exist, the key value from the predicate is used
+ * and @p value is ignored. Also, if a leaf-list is being created and both a predicate is defined in @p path
+ * and @p value is set, the predicate is preferred.
+ *
+ * @param[in] parent Data parent to add to/modify, can be NULL.
+ * @param[in] ctx libyang context, must be set if @p parent is NULL.
+ * @param[in] path Path to create (TODO ref path).
+ * @param[in] value Value of the new leaf/leaf-list/anyxml/anydata. For other node types, it is ignored.
+ * @param[in] value_type Anyxml/anydata node @p value type.
+ * @param[in] options Bitmask of options, see @ref pathoptions.
+ * @param[out] new_parent First parent node created, can be NULL. If only one node was created, equals to @p new_node.
+ * @param[out] new_node Last node created, can be NULL.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
+ LYD_ANYDATA_VALUETYPE value_type, int options, struct lyd_node **new_parent, struct lyd_node **new_node);
+
+/**
+ * @brief Change the value of a term (leaf or leaf-list) node.
+ *
+ * Node changed this way is always considered explicitly set, meaning its default flag
+ * is always cleared.
+ *
+ * @param[in] term Term node to change.
+ * @param[in] val_str New value to set, any prefixes are expected in JSON format.
+ * @return LY_SUCCESS if value was changed,
+ * @return LY_EEXIST if value was the same and only the default flag was cleared,
+ * @return LY_ENOT if the values were equal and no change occured,
+ * @return LY_ERR value on other errors.
+ */
+LY_ERR lyd_change_term(struct lyd_node *term, const char *val_str);
+
+/**
* @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.
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 73849b8..bb70f31 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -6862,8 +6862,9 @@
assert(node->nodetype & (LYS_LEAF | LYS_LEAFLIST));
/* try to find the target */
- LY_CHECK_RET(ly_path_compile(node->module, node, lref->path, LY_PATH_LREF_TRUE, lys_resolve_prefix, lref->path_context,
- LYD_SCHEMA, &p));
+ LY_CHECK_RET(ly_path_compile(ctx->ctx, node->module, node, lref->path, LY_PATH_LREF_TRUE,
+ lysc_is_output(node) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY,
+ lys_resolve_prefix, lref->path_context, LYD_SCHEMA, &p));
/* get the target node */
target = p[LY_ARRAY_SIZE(p) - 1].node;
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index f1ad8b9..16bc09e 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -1693,3 +1693,17 @@
return parent;
}
+
+int
+lysc_is_output(const struct lysc_node *schema)
+{
+ const struct lysc_node *parent;
+
+ assert(schema);
+
+ for (parent = schema->parent; parent && !(parent->nodetype & (LYS_RPC | LYS_ACTION)); parent = parent->parent);
+ if (parent && (schema->flags & LYS_CONFIG_R)) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index ddc7966..ed07a25 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -802,4 +802,13 @@
*/
const struct lysc_node *lysc_data_parent(const struct lysc_node *schema);
+/**
+ * @brief Learn whether a node is in an operation output.
+ *
+ * @param[in] schema Schema node to examine.
+ * @return non-zero is the node is in output,
+ * @return 0 if it is not.
+ */
+int lysc_is_output(const struct lysc_node *schema);
+
#endif /* LY_TREE_SCHEMA_INTERNAL_H_ */
diff --git a/src/xpath.c b/src/xpath.c
index 459c78e..39c7365 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -3611,6 +3611,7 @@
char *errmsg = NULL;
const char *val;
int dynamic;
+ uint8_t oper;
LY_ERR rc = LY_SUCCESS;
if (options & LYXP_SCNODE_ALL) {
@@ -3625,15 +3626,16 @@
set_scnode_clear_ctx(set);
if (sleaf && (sleaf->type->basetype == LY_TYPE_LEAFREF)) {
lref = (struct lysc_type_leafref *)sleaf->type;
+ oper = lysc_is_output((struct lysc_node *)sleaf) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
/* it was already evaluated on schema, it must succeed */
if (set->format == LYD_JSON) {
- rc = ly_path_compile(sleaf->module, (struct lysc_node *)sleaf, lref->path, LY_PATH_LREF_TRUE,
- lydjson_resolve_prefix, NULL, LYD_JSON, &p);
+ rc = ly_path_compile(set->ctx, sleaf->module, (struct lysc_node *)sleaf, lref->path, LY_PATH_LREF_TRUE,
+ oper, LY_PATH_TARGET_MANY, lydjson_resolve_prefix, NULL, LYD_JSON, &p);
} else {
assert(set->format == LYD_SCHEMA);
- rc = ly_path_compile(sleaf->module, (struct lysc_node *)sleaf, lref->path, LY_PATH_LREF_TRUE,
- lys_resolve_prefix, lref->path_context, LYD_SCHEMA, &p);
+ rc = ly_path_compile(set->ctx, sleaf->module, (struct lysc_node *)sleaf, lref->path, LY_PATH_LREF_TRUE,
+ oper, LY_PATH_TARGET_MANY, lys_resolve_prefix, lref->path_context, LYD_SCHEMA, &p);
}
assert(!rc);
@@ -6906,12 +6908,12 @@
/* compile */
switch (format) {
case LYD_SCHEMA:
- ret = ly_path_compile_predicate(scnode->module, scnode, exp2, &pred_idx, lys_resolve_prefix, scnode->module,
- LYD_SCHEMA, predicates, pred_type);
+ ret = ly_path_compile_predicate(scnode->module->ctx, scnode->module, scnode, exp2, &pred_idx, lys_resolve_prefix,
+ scnode->module, LYD_SCHEMA, predicates, pred_type);
break;
case LYD_JSON:
- ret = ly_path_compile_predicate(scnode->module, scnode, exp2, &pred_idx, lydjson_resolve_prefix, NULL, LYD_JSON,
- predicates, pred_type);
+ ret = ly_path_compile_predicate(scnode->module->ctx, scnode->module, scnode, exp2, &pred_idx,
+ lydjson_resolve_prefix, NULL, LYD_JSON, predicates, pred_type);
break;
case LYD_XML:
ret = LY_EINT;
diff --git a/tests/utests/data/test_new.c b/tests/utests/data/test_new.c
index 8d96df1..ee42a0b 100644
--- a/tests/utests/data/test_new.c
+++ b/tests/utests/data/test_new.c
@@ -204,10 +204,129 @@
*state = NULL;
}
+static void
+test_opaq(void **state)
+{
+ *state = test_opaq;
+
+ struct lyd_node *root, *node;
+ struct lyd_node_opaq *opq;
+
+ root = lyd_new_opaq(NULL, ctx, "node1", NULL, "my-module");
+ assert_non_null(root);
+ assert_null(root->schema);
+ opq = (struct lyd_node_opaq *)root;
+ assert_string_equal(opq->name, "node1");
+ assert_string_equal(opq->value, "");
+ assert_string_equal(opq->prefix.ns, "my-module");
+
+ node = lyd_new_opaq(root, NULL, "node2", "value", "my-module2");
+ assert_non_null(node);
+ assert_null(node->schema);
+ opq = (struct lyd_node_opaq *)node;
+ assert_string_equal(opq->name, "node2");
+ assert_string_equal(opq->value, "value");
+ assert_string_equal(opq->prefix.ns, "my-module2");
+ assert_ptr_equal(opq->parent, root);
+
+ lyd_free_tree(root);
+
+ *state = NULL;
+}
+
+static void
+test_path(void **state)
+{
+ *state = test_path;
+
+ LY_ERR ret;
+ struct lyd_node *root, *node, *parent;
+ int dynamic;
+
+ /* create 2 nodes */
+ ret = lyd_new_path2(NULL, ctx, "/a:c/x[.='val']", "vvv", 0, 0, &root, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_string_equal(root->schema->name, "c");
+ assert_non_null(node);
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("val", lyd_value2str((struct lyd_node_term *)node, &dynamic));
+ assert_int_equal(dynamic, 0);
+
+ /* append another */
+ ret = lyd_new_path2(root, NULL, "/a:c/x", "val2", 0, 0, &parent, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_ptr_equal(parent, node);
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("val2", lyd_value2str((struct lyd_node_term *)node, &dynamic));
+ assert_int_equal(dynamic, 0);
+
+ /* and a last one */
+ ret = lyd_new_path2(root, NULL, "x", "val3", 0, 0, &parent, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_ptr_equal(parent, node);
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("val3", lyd_value2str((struct lyd_node_term *)node, &dynamic));
+ assert_int_equal(dynamic, 0);
+
+ lyd_free_tree(root);
+
+ /* try LYD_NEWOPT_OPAQ */
+ ret = lyd_new_path2(NULL, ctx, "/a:l1", NULL, 0, 0, NULL, NULL);
+ assert_int_equal(ret, LY_EINVAL);
+ logbuf_assert("Predicate missing for list \"l1\" in path.");
+
+ ret = lyd_new_path2(NULL, ctx, "/a:l1", NULL, 0, LYD_NEWOPT_OPAQ, NULL, &root);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_null(root->schema);
+
+ lyd_free_tree(root);
+
+ ret = lyd_new_path2(NULL, ctx, "/a:foo", NULL, 0, 0, NULL, NULL);
+ assert_int_equal(ret, LY_EVALID);
+ logbuf_assert("Invalid empty uint16 value. /a:foo");
+
+ ret = lyd_new_path2(NULL, ctx, "/a:foo", NULL, 0, LYD_NEWOPT_OPAQ, NULL, &root);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_null(root->schema);
+
+ lyd_free_tree(root);
+
+ /* try LYD_NEWOPT_UPDATE */
+ ret = lyd_new_path2(NULL, ctx, "/a:l2[1]/c/x", "val", 0, 0, &root, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("val", lyd_value2str((struct lyd_node_term *)node, &dynamic));
+ assert_int_equal(dynamic, 0);
+
+ ret = lyd_new_path2(root, NULL, "/a:l2[1]/c/x", "val", 0, 0, NULL, &node);
+ assert_int_equal(ret, LY_EEXIST);
+
+ ret = lyd_new_path2(root, NULL, "/a:l2[1]/c/x", "val", 0, LYD_NEWOPT_UPDATE, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_null(node);
+
+ ret = lyd_new_path2(root, NULL, "/a:l2[1]/c/x", "val2", 0, LYD_NEWOPT_UPDATE, NULL, &node);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(node);
+ assert_string_equal(node->schema->name, "x");
+ assert_string_equal("val2", lyd_value2str((struct lyd_node_term *)node, &dynamic));
+ assert_int_equal(dynamic, 0);
+
+ lyd_free_tree(root);
+
+ *state = NULL;
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_top_level, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_opaq, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_path, setup, teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/test_inout.c b/tests/utests/test_inout.c
index 1542d0f..c621022 100644
--- a/tests/utests/test_inout.c
+++ b/tests/utests/test_inout.c
@@ -1,9 +1,9 @@
-/*
+/**
* @file test_inout.c
* @author: Radek Krejci <rkrejci@cesnet.cz>
* @brief unit tests for input and output handlers functions
*
- * Copyright (c) 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2020 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.