#define _GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "compat.h"
#include "context.h"
#include "dict.h"
#include "diff.h"
#include "hash_table.h"
#include "in.h"
#include "in_internal.h"
#include "log.h"
#include "ly_common.h"
#include "parser_data.h"
#include "parser_internal.h"
#include "path.h"
#include "plugins.h"
#include "plugins_exts/metadata.h"
#include "plugins_internal.h"
#include "plugins_types.h"
#include "set.h"
#include "tree.h"
#include "tree_data_internal.h"
#include "tree_data_sorted.h"
#include "tree_edit.h"
#include "tree_schema.h"
#include "tree_schema_internal.h"
#include "validation.h"
#include "xml.h"
#include "xpath.h"
LY_ERR
lyd_create_term(const struct lysc_node *schema, const struct lyd_node *lnode, const void *value,
uint64_t value_size_bits, ly_bool is_utf8, ly_bool store_only, ly_bool *dynamic, LY_VALUE_FORMAT format,
void *prefix_data, uint32_t hints, ly_bool *incomplete, struct lyd_node **node)
{
LY_ERR ret;
struct lyd_node_term *term;
assert(schema->nodetype & LYD_NODE_TERM);
term = calloc(1, sizeof *term);
LY_CHECK_ERR_RET(!term, LOGMEM(schema->module->ctx), LY_EMEM);
term->schema = schema;
term->prev = &term->node;
term->flags = LYD_NEW;
ret = lyd_value_store(schema->module->ctx, lnode, &term->value, ((struct lysc_node_leaf *)term->schema)->type,
value, value_size_bits, is_utf8, store_only, dynamic, format, prefix_data, hints, schema, incomplete);
LY_CHECK_ERR_RET(ret, free(term), ret);
lyd_hash(&term->node);
*node = &term->node;
return ret;
}
LY_ERR
lyd_create_inner(const struct lysc_node *schema, struct lyd_node **node)
{
struct lyd_node_inner *in;
assert(schema->nodetype & LYD_NODE_INNER);
in = calloc(1, sizeof *in);
LY_CHECK_ERR_RET(!in, LOGMEM(schema->module->ctx), LY_EMEM);
in->schema = schema;
in->prev = &in->node;
in->flags = LYD_NEW;
if ((schema->nodetype == LYS_CONTAINER) && !(schema->flags & LYS_PRESENCE)) {
in->flags |= LYD_DEFAULT;
}
if ((schema->nodetype != LYS_LIST) || (schema->flags & LYS_KEYLESS)) {
lyd_hash(&in->node);
}
*node = &in->node;
return LY_SUCCESS;
}
LY_ERR
lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates, const struct lyxp_var *vars,
ly_bool store_only, struct lyd_node **node)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *list = NULL, *key;
struct lyxp_var *var;
const char *value;
LY_ARRAY_COUNT_TYPE u;
assert((schema->nodetype == LYS_LIST) && !(schema->flags & LYS_KEYLESS));
LY_CHECK_GOTO(ret = lyd_create_inner(schema, &list), cleanup);
LY_ARRAY_FOR(predicates, u) {
if (predicates[u].type == LY_PATH_PREDTYPE_LIST_VAR) {
if ((ret = lyxp_vars_find(schema->module->ctx, vars, predicates[u].variable, 0, &var))) {
goto cleanup;
}
value = var->value;
} else {
assert(predicates[u].type == LY_PATH_PREDTYPE_LIST);
value = predicates[u].value;
}
ret = lyd_create_term(predicates[u].key, NULL, value, strlen(value) * 8, 1, store_only, NULL, LY_VALUE_JSON,
NULL, LYD_HINT_DATA, NULL, &key);
LY_CHECK_GOTO(ret, cleanup);
lyd_insert_node(list, NULL, key, LYD_INSERT_NODE_DEFAULT);
}
lyd_hash(list);
*node = list;
list = NULL;
cleanup:
lyd_free_tree(list);
return ret;
}
LY_ERR
lyd_create_list2(const struct lysc_node *schema, const char *keys, size_t keys_len, ly_bool store_only,
struct lyd_node **node)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *expr = NULL;
uint32_t exp_idx = 0;
struct ly_path_predicate *predicates = NULL;
LY_CHECK_GOTO(ret = ly_path_parse_predicate(schema->module->ctx, schema, keys, keys_len, LY_PATH_PREFIX_OPTIONAL,
LY_PATH_PRED_KEYS, &expr), cleanup);
LY_CHECK_GOTO(ret = ly_path_compile_predicate(schema->module->ctx, NULL, schema, expr, &exp_idx, LY_VALUE_JSON,
NULL, &predicates), cleanup);
LY_CHECK_GOTO(ret = lyd_create_list(schema, predicates, NULL, store_only, node), cleanup);
cleanup:
lyxp_expr_free(expr);
ly_path_predicates_free(schema->module->ctx, predicates);
return ret;
}
static LY_ERR
lyd_create_any_datatree(const struct ly_ctx *ctx, const char *value, ly_bool log, struct lyd_node **tree)
{
LY_ERR rc = LY_SUCCESS;
struct lyd_ctx *lydctx = NULL;
struct ly_in *in = NULL;
uint32_t parse_opts, int_opts, *prev_lo = NULL, temp_lo = 0;
const char *ptr;
*tree = NULL;
parse_opts = LYD_PARSE_ONLY | LYD_PARSE_OPAQ;
int_opts = LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
if (!log) {
prev_lo = ly_temp_log_options(&temp_lo);
}
LY_CHECK_GOTO(rc = ly_in_new_memory(value, &in), cleanup);
for (ptr = value; isspace(ptr[0]); ++ptr) {}
if (ptr[0] == '<') {
rc = lyd_parse_xml(ctx, NULL, tree, in, parse_opts, 0, int_opts, NULL, &lydctx);
} else if (ptr[0] == '{') {
rc = lyd_parse_json(ctx, NULL, NULL, tree, in, parse_opts, 0, int_opts, NULL, &lydctx);
} else {
rc = LY_ENOT;
}
cleanup:
ly_in_free(in, 0);
if (lydctx) {
lydctx->free(lydctx);
}
if (!log) {
ly_temp_log_options(prev_lo);
}
if (rc && *tree) {
lyd_free_siblings(*tree);
*tree = NULL;
}
return rc;
}
LY_ERR
lyd_create_any(const struct lysc_node *schema, const struct lyd_node *child, const char *value, uint32_t hints,
ly_bool use_value, ly_bool try_parse, struct lyd_node **node)
{
LY_ERR rc = LY_SUCCESS, r;
struct lyd_node *tree;
struct lyd_node_any *any = NULL;
assert(schema->nodetype & LYD_NODE_ANY);
assert((schema->nodetype == LYS_ANYXML) || !value || try_parse);
assert(!child || !value);
any = calloc(1, sizeof *any);
LY_CHECK_ERR_RET(!any, LOGMEM(schema->module->ctx), LY_EMEM);
any->schema = schema;
any->prev = &any->node;
any->flags = LYD_NEW;
if (schema->nodetype == LYS_ANYDATA) {
if (value) {
if ((r = lyd_create_any_datatree(schema->module->ctx, value, 1, &tree))) {
LOGERR(schema->module->ctx, rc, "Failed to parse any content into a data tree.");
rc = r;
goto cleanup;
}
if (use_value) {
free((void *)value);
value = NULL;
}
use_value = 1;
child = tree;
}
} else {
if (hints & 0x0FFF) {
try_parse = 0;
}
if (value && try_parse) {
r = lyd_create_any_datatree(schema->module->ctx, value, 0, &tree);
if (!r) {
if (use_value) {
free((void *)value);
value = NULL;
}
use_value = 1;
child = tree;
}
}
}
if (use_value) {
if (child) {
any->child = (void *)child;
LY_LIST_FOR(any->child, tree) {
tree->parent = &any->node;
}
} else if (value) {
LY_CHECK_GOTO(rc = lydict_insert_zc(schema->module->ctx, (void *)value, &any->value), cleanup);
}
any->hints = hints;
} else {
LY_CHECK_GOTO(rc = lyd_any_copy_value(&any->node, child, value, hints), cleanup);
}
lyd_hash(&any->node);
cleanup:
if (rc) {
lyd_free_tree(&any->node);
} else {
*node = &any->node;
}
return rc;
}
LIBYANG_API_DEF LY_ERR
lyd_any_copy_value(struct lyd_node *trg, const struct lyd_node *child, const char *value, uint32_t hints)
{
struct lyd_node_any *t;
struct lyd_node *node;
ly_bool use_value = 0;
LY_CHECK_ARG_RET(NULL, trg, !child || !value, LY_EINVAL);
LY_CHECK_ARG_RET(NULL, trg->schema, trg->schema->nodetype & LYS_ANYDATA, LY_EINVAL);
t = (struct lyd_node_any *)trg;
lyd_free_siblings(t->child);
t->child = NULL;
lydict_remove(LYD_CTX(trg), t->value);
t->value = NULL;
if (!child && !value) {
return LY_SUCCESS;
}
if (value && ((trg->schema->nodetype == LYS_ANYDATA) || !(hints & 0x0FFF))) {
if (!lyd_create_any_datatree(LYD_CTX(trg), value, 0, &node)) {
use_value = 1;
child = node;
}
}
if (child) {
if (use_value) {
t->child = (struct lyd_node *)child;
} else {
LY_CHECK_RET(lyd_dup_siblings(child, NULL, LYD_DUP_RECURSIVE, &t->child));
}
LY_LIST_FOR(t->child, node) {
node->parent = &t->node;
}
} else {
LY_CHECK_RET(lydict_insert(LYD_CTX(trg), value, 0, &t->value));
}
t->hints = hints;
return LY_SUCCESS;
}
LY_ERR
lyd_create_opaq(const struct ly_ctx *ctx, const char *name, uint32_t name_len, const char *prefix, uint32_t pref_len,
const char *module_key, uint32_t module_key_len, const char *value, uint32_t value_len, ly_bool *dynamic,
LY_VALUE_FORMAT format, void *val_prefix_data, uint32_t hints, struct lyd_node **node)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node_opaq *opaq;
assert(ctx && name && name_len && format);
if (format == LY_VALUE_LYB) {
LOGERR(ctx, LY_EINVAL, "Cannot create opaque node \"%.*s\" with a LYB value.", (int)name_len, name);
return LY_EINVAL;
}
if (!value_len && (!dynamic || !*dynamic)) {
value = "";
}
opaq = calloc(1, sizeof *opaq);
LY_CHECK_ERR_GOTO(!opaq, LOGMEM(ctx); ret = LY_EMEM, finish);
opaq->prev = &opaq->node;
LY_CHECK_GOTO(ret = lydict_insert(ctx, name, name_len, &opaq->name.name), finish);
if (pref_len) {
LY_CHECK_GOTO(ret = lydict_insert(ctx, prefix, pref_len, &opaq->name.prefix), finish);
}
if (module_key_len) {
LY_CHECK_GOTO(ret = lydict_insert(ctx, module_key, module_key_len, &opaq->name.module_ns), finish);
}
if (dynamic && *dynamic) {
LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, (char *)value, &opaq->value), finish);
*dynamic = 0;
} else {
LY_CHECK_GOTO(ret = lydict_insert(ctx, value, value_len, &opaq->value), finish);
}
opaq->format = format;
opaq->val_prefix_data = val_prefix_data;
opaq->hints = hints;
opaq->ctx = ctx;
finish:
if (ret) {
lyd_free_tree(&opaq->node);
ly_free_prefix_data(format, val_prefix_data);
} else {
*node = &opaq->node;
}
return ret;
}
static LY_ERR
lyd_new_val_get_format(uint32_t options, LY_VALUE_FORMAT *format)
{
LY_CHECK_ARG_RET(NULL, format, LY_EVALID);
if (options & LYD_NEW_VAL_CANON) {
*format = LY_VALUE_CANON;
} else {
*format = LY_VALUE_JSON;
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyd_new_inner(struct lyd_node *parent, const struct lys_module *module, const char *name, ly_bool output,
struct lyd_node **node)
{
LY_ERR r;
struct lyd_node *ret = NULL;
const struct lysc_node *schema = NULL;
struct lysc_ext_instance *ext = NULL;
const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
if (!module) {
module = parent->schema->module;
}
r = lys_find_child_node(ctx, parent ? parent->schema : NULL, module, module->name, strlen(module->name),
LY_VALUE_JSON, NULL, name, 0, output ? LYS_GETNEXT_OUTPUT : 0, &schema, &ext);
if (!r && !(schema->nodetype & (LYS_CONTAINER | LYS_NOTIF | LYS_RPC | LYS_ACTION))) {
r = LY_ENOT;
}
LY_CHECK_ERR_RET(r, LOGERR(ctx, LY_EINVAL, "Inner node (container, notif, RPC, or action) \"%s\" not found.",
name), LY_ENOTFOUND);
LY_CHECK_RET(lyd_create_inner(schema, &ret));
if (ext) {
ret->flags |= LYD_EXT;
}
if (parent) {
lyd_insert_node(parent, NULL, ret, LYD_INSERT_NODE_DEFAULT);
}
if (node) {
*node = ret;
}
return LY_SUCCESS;
}
static LY_ERR
_lyd_new_list_node(const struct ly_ctx *ctx, const struct lyd_node *parent, const struct lys_module *module,
const char *name, uint32_t options, struct lyd_node **node)
{
LY_ERR r;
struct lyd_node *ret = NULL;
const struct lysc_node *schema = NULL;
struct lysc_ext_instance *ext = NULL;
uint32_t getnext_opts = (options & LYD_NEW_VAL_OUTPUT) ? LYS_GETNEXT_OUTPUT : 0;
if (!module) {
module = parent->schema->module;
}
r = lys_find_child_node(ctx, parent ? parent->schema : NULL, module, module->name, strlen(module->name),
LY_VALUE_JSON, NULL, name, 0, getnext_opts, &schema, &ext);
if (!r && (schema->nodetype != LYS_LIST)) {
r = LY_ENOT;
}
LY_CHECK_ERR_RET(r, LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found.", name), LY_ENOTFOUND);
LY_CHECK_RET(lyd_create_inner(schema, &ret));
if (ext) {
ret->flags |= LYD_EXT;
}
*node = ret;
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const char *name, uint32_t options,
struct lyd_node **node, ...)
{
struct lyd_node *ret = NULL, *key;
const struct lysc_node *key_s;
const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
const void *key_val;
uint32_t key_size_bits;
LY_ERR rc = LY_SUCCESS;
ly_bool store_only = (options & LYD_NEW_VAL_STORE_ONLY) ? 1 : 0;
LY_VALUE_FORMAT format;
va_list ap;
LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
LY_CHECK_RET(lyd_new_val_get_format(options, &format));
LY_CHECK_ARG_RET(ctx, !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)), LY_EINVAL);
LY_CHECK_RET(_lyd_new_list_node(ctx, parent, module, name, options, &ret));
va_start(ap, node);
for (key_s = lysc_node_child(ret->schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
if (format == LY_VALUE_LYB) {
key_val = va_arg(ap, const void *);
key_size_bits = va_arg(ap, uint32_t);
} else {
key_val = va_arg(ap, const char *);
key_size_bits = key_val ? strlen((char *)key_val) * 8 : 0;
}
rc = lyd_create_term(key_s, parent, key_val, key_size_bits, 0, store_only, NULL, format, NULL, LYD_HINT_DATA,
NULL, &key);
LY_CHECK_GOTO(rc, cleanup);
lyd_insert_node(ret, NULL, key, LYD_INSERT_NODE_LAST);
}
if (parent) {
lyd_insert_node(parent, NULL, ret, LYD_INSERT_NODE_DEFAULT);
}
cleanup:
va_end(ap);
if (rc) {
lyd_free_tree(ret);
ret = NULL;
} else if (node) {
*node = ret;
}
return rc;
}
LIBYANG_API_DEF LY_ERR
lyd_new_list2(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *keys,
uint32_t options, struct lyd_node **node)
{
LY_ERR r;
struct lyd_node *ret = NULL;
const struct lysc_node *schema = NULL;
struct lysc_ext_instance *ext = NULL;
const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
uint32_t getnext_opts = (options & LYD_NEW_VAL_OUTPUT) ? LYS_GETNEXT_OUTPUT : 0;
LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
if (!module) {
module = parent->schema->module;
}
if (!keys) {
keys = "";
}
r = lys_find_child_node(ctx, parent ? parent->schema : NULL, module, NULL, 0, 0, NULL, name, 0, getnext_opts,
&schema, &ext);
if (!r && (schema->nodetype != LYS_LIST)) {
r = LY_ENOT;
}
LY_CHECK_ERR_RET(r, LOGERR(ctx, LY_EINVAL, "List node \"%s\" not found.", name), LY_ENOTFOUND);
if ((schema->flags & LYS_KEYLESS) && !keys[0]) {
LY_CHECK_RET(lyd_create_inner(schema, &ret));
} else {
ly_bool store_only = (options & LYD_NEW_VAL_STORE_ONLY) ? 1 : 0;
LY_CHECK_RET(lyd_create_list2(schema, keys, strlen(keys), store_only, &ret));
}
if (ext) {
ret->flags |= LYD_EXT;
}
if (parent) {
lyd_insert_node(parent, NULL, ret, LYD_INSERT_NODE_DEFAULT);
}
if (node) {
*node = ret;
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyd_new_list3(struct lyd_node *parent, const struct lys_module *module, const char *name, const void **key_values,
uint32_t *value_sizes_bits, uint32_t options, struct lyd_node **node)
{
struct lyd_node *ret = NULL, *key;
const struct lysc_node *key_s;
const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
const void *key_val;
uint32_t key_size_bits, i;
LY_ERR rc = LY_SUCCESS;
ly_bool store_only = (options & LYD_NEW_VAL_STORE_ONLY) ? 1 : 0;
LY_VALUE_FORMAT format;
LY_CHECK_RET(lyd_new_val_get_format(options, &format));
LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, (format != LY_VALUE_LYB) || value_sizes_bits, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
LY_CHECK_ARG_RET(ctx, !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)), LY_EINVAL);
LY_CHECK_RET(_lyd_new_list_node(ctx, parent, module, name, options, &ret));
if (!(ret->schema->flags & LYS_KEYLESS) && !key_values) {
LOGERR(ctx, LY_EINVAL, "Missing list \"%s\" keys.", LYD_NAME(ret));
rc = LY_EINVAL;
goto cleanup;
}
i = 0;
for (key_s = lysc_node_child(ret->schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
key_val = key_values[i] ? key_values[i] : "";
key_size_bits = value_sizes_bits ? value_sizes_bits[i] : strlen(key_val) * 8;
rc = lyd_create_term(key_s, parent, key_val, key_size_bits, 0, store_only, NULL, format, NULL, LYD_HINT_DATA,
NULL, &key);
LY_CHECK_GOTO(rc, cleanup);
lyd_insert_node(ret, NULL, key, LYD_INSERT_NODE_LAST);
++i;
}
if (parent) {
lyd_insert_node(parent, NULL, ret, LYD_INSERT_NODE_DEFAULT);
}
cleanup:
if (rc) {
lyd_free_tree(ret);
ret = NULL;
} else if (node) {
*node = ret;
}
return rc;
}
static LY_ERR
_lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const char *name, const void *value,
uint64_t value_size_bits, uint32_t options, struct lyd_node **node)
{
LY_ERR r;
struct lyd_node *ret = NULL;
const struct lysc_node *schema = NULL;
struct lysc_ext_instance *ext = NULL;
const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
uint32_t getnext_opts = (options & LYD_NEW_VAL_OUTPUT) ? LYS_GETNEXT_OUTPUT : 0;
ly_bool store_only = (options & LYD_NEW_VAL_STORE_ONLY) ? 1 : 0;
LY_VALUE_FORMAT format;
LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
LY_CHECK_RET(lyd_new_val_get_format(options, &format));
LY_CHECK_ARG_RET(ctx, !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)), LY_EINVAL);
if (!module) {
module = parent->schema->module;
}
r = lys_find_child_node(ctx, parent ? parent->schema : NULL, module, module->name, strlen(module->name),
LY_VALUE_JSON, NULL, name, 0, getnext_opts, &schema, &ext);
if (!r && !(schema->nodetype & LYD_NODE_TERM)) {
r = LY_ENOT;
}
LY_CHECK_ERR_RET(r, LOGERR(ctx, LY_EINVAL, "Term node \"%s\" not found.", name), LY_ENOTFOUND);
LY_CHECK_RET(lyd_create_term(schema, parent, value, value_size_bits, 0, store_only, NULL, format, NULL,
LYD_HINT_DATA, NULL, &ret));
if (ext) {
ret->flags |= LYD_EXT;
}
if (parent) {
lyd_insert_node(parent, NULL, ret, LYD_INSERT_NODE_DEFAULT);
}
if (node) {
*node = ret;
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *value,
uint32_t options, struct lyd_node **node)
{
return _lyd_new_term(parent, module, name, value, value ? strlen(value) * 8 : 0, options, node);
}
LIBYANG_API_DEF LY_ERR
lyd_new_any(struct lyd_node *parent, const struct lys_module *module, const char *name, const struct lyd_node *child,
const char *value, uint32_t hints, uint32_t options, struct lyd_node **node)
{
LY_ERR r;
struct lyd_node *ret = NULL;
const struct lysc_node *schema = NULL;
struct lysc_ext_instance *ext = NULL;
const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
uint32_t getnext_opts = (options & LYD_NEW_VAL_OUTPUT) ? LYS_GETNEXT_OUTPUT : 0;
ly_bool use_value = (options & LYD_NEW_ANY_USE_VALUE) ? 1 : 0;
LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, !child || !value, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
if (!module) {
module = parent->schema->module;
}
r = lys_find_child_node(ctx, parent ? parent->schema : NULL, module, module->name, strlen(module->name),
LY_VALUE_JSON, NULL, name, 0, getnext_opts, &schema, &ext);
if (!r && !(schema->nodetype & LYD_NODE_ANY)) {
r = LY_ENOT;
}
LY_CHECK_ERR_RET(r, LOGERR(ctx, LY_EINVAL, "Any node \"%s\" not found.", name), LY_ENOTFOUND);
LY_CHECK_RET(lyd_create_any(schema, child, value, hints, use_value, 1, &ret));
if (ext) {
ret->flags |= LYD_EXT;
}
if (parent) {
lyd_insert_node(parent, NULL, ret, LYD_INSERT_NODE_DEFAULT);
}
if (node) {
*node = ret;
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyd_new_meta(const struct ly_ctx *ctx, struct lyd_node *parent, const struct lys_module *module, const char *name,
const char *val_str, uint32_t options, struct lyd_meta **meta)
{
const char *prefix, *tmp;
uint32_t pref_len, name_len;
ly_bool clear_dflt = options & LYD_NEW_META_CLEAR_DFLT;
ly_bool store_only = options & LYD_NEW_VAL_STORE_ONLY;
LY_CHECK_ARG_RET(ctx, ctx || parent, name, module || strchr(name, ':'), parent || meta, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, ctx, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
if (!ctx) {
ctx = module ? module->ctx : LYD_CTX(parent);
}
if (parent && !parent->schema) {
LOGERR(ctx, LY_EINVAL, "Cannot add metadata \"%s\" to an opaque node \"%s\".", name, LYD_NAME(parent));
return LY_EINVAL;
}
if (meta) {
*meta = NULL;
}
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 LY_EINVAL;
}
if (prefix) {
module = ly_ctx_get_module_implemented2(ctx, prefix, pref_len);
LY_CHECK_ERR_RET(!module, LOGERR(ctx, LY_EINVAL, "Module \"%.*s\" not found.", (int)pref_len, prefix), LY_ENOTFOUND);
}
if (!val_str) {
val_str = "";
}
return lyd_create_meta(parent, meta, module, name, name_len, val_str, strlen(val_str) * 8, 0, store_only, NULL,
LY_VALUE_JSON, NULL, LYD_HINT_DATA, parent ? parent->schema : NULL, NULL, clear_dflt, NULL);
}
LIBYANG_API_DEF LY_ERR
lyd_new_meta2(const struct ly_ctx *ctx, struct lyd_node *parent, uint32_t options, const struct lyd_attr *attr,
struct lyd_meta **meta)
{
const struct lys_module *mod;
ly_bool clear_dflt = options & LYD_NEW_META_CLEAR_DFLT;
ly_bool store_only = options & LYD_NEW_VAL_STORE_ONLY;
LY_CHECK_ARG_RET(NULL, ctx, attr, parent || meta, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL);
if (parent && !parent->schema) {
LOGERR(ctx, LY_EINVAL, "Cannot add metadata to an opaque node \"%s\".",
((struct lyd_node_opaq *)parent)->name.name);
return LY_EINVAL;
}
if (meta) {
*meta = NULL;
}
switch (attr->format) {
case LY_VALUE_XML:
mod = ly_ctx_get_module_implemented_ns(ctx, attr->name.module_ns);
if (!mod) {
LOGERR(ctx, LY_EINVAL, "Module with namespace \"%s\" not found.", attr->name.module_ns);
return LY_ENOTFOUND;
}
break;
case LY_VALUE_JSON:
mod = ly_ctx_get_module_implemented(ctx, attr->name.module_name);
if (!mod) {
LOGERR(ctx, LY_EINVAL, "Module \"%s\" not found.", attr->name.module_name);
return LY_ENOTFOUND;
}
break;
default:
LOGINT_RET(ctx);
}
return lyd_create_meta(parent, meta, mod, attr->name.name, strlen(attr->name.name), attr->value,
strlen(attr->value) * 8, 0, store_only, NULL, attr->format, attr->val_prefix_data, attr->hints,
parent ? parent->schema : NULL, NULL, clear_dflt, NULL);
}
LIBYANG_API_DEF LY_ERR
lyd_new_opaq(struct lyd_node *parent, const struct ly_ctx *ctx, const char *name, const char *value,
const char *prefix, const char *module_name, struct lyd_node **node)
{
struct lyd_node *ret = NULL;
uint32_t hints = 0;
LY_CHECK_ARG_RET(ctx, parent || ctx, parent || node, name, module_name, !prefix || !strcmp(prefix, module_name), LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL);
if (!ctx) {
ctx = LYD_CTX(parent);
}
if (!value) {
value = "";
} else if (!strcmp(value, "[null]")) {
hints |= LYD_VALHINT_EMPTY;
}
LY_CHECK_RET(lyd_create_opaq(ctx, name, strlen(name), prefix, prefix ? strlen(prefix) : 0, module_name,
strlen(module_name), value, strlen(value), NULL, LY_VALUE_JSON, NULL, hints, &ret));
if (parent) {
lyd_insert_node(parent, NULL, ret, LYD_INSERT_NODE_LAST);
}
if (node) {
*node = ret;
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyd_new_opaq2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *name, const char *value,
const char *prefix, const char *module_ns, struct lyd_node **node)
{
struct lyd_node *ret = NULL;
LY_CHECK_ARG_RET(ctx, parent || ctx, parent || node, name, module_ns, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL);
if (!ctx) {
ctx = LYD_CTX(parent);
}
if (!value) {
value = "";
}
LY_CHECK_RET(lyd_create_opaq(ctx, name, strlen(name), prefix, prefix ? strlen(prefix) : 0, module_ns,
strlen(module_ns), value, strlen(value), NULL, LY_VALUE_XML, NULL, 0, &ret));
if (parent) {
lyd_insert_node(parent, NULL, ret, LYD_INSERT_NODE_LAST);
}
if (node) {
*node = ret;
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyd_new_attr(struct lyd_node *parent, const char *module_name, const char *name, const char *value,
struct lyd_attr **attr)
{
struct lyd_attr *ret = NULL;
const struct ly_ctx *ctx;
const char *prefix, *tmp;
uint32_t pref_len, name_len, mod_len;
LY_CHECK_ARG_RET(NULL, parent, !parent->schema, name, LY_EINVAL);
ctx = LYD_CTX(parent);
tmp = name;
if (ly_parse_nodeid(&tmp, &prefix, &pref_len, &name, &name_len) || tmp[0]) {
LOGERR(ctx, LY_EINVAL, "Attribute name \"%s\" is not valid.", name);
return LY_EVALID;
}
if ((pref_len == 3) && !strncmp(prefix, "xml", 3)) {
name = prefix;
name_len += 1 + pref_len;
prefix = NULL;
pref_len = 0;
}
if (module_name) {
mod_len = strlen(module_name);
} else {
module_name = prefix;
mod_len = pref_len;
}
if (!value) {
value = "";
}
LY_CHECK_RET(lyd_create_attr(parent, &ret, ctx, name, name_len, prefix, pref_len, module_name, mod_len, value,
strlen(value), NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA));
if (attr) {
*attr = ret;
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyd_new_attr2(struct lyd_node *parent, const char *module_ns, const char *name, const char *value,
struct lyd_attr **attr)
{
struct lyd_attr *ret = NULL;
const struct ly_ctx *ctx;
const char *prefix, *tmp;
uint32_t pref_len, name_len;
LY_CHECK_ARG_RET(NULL, parent, !parent->schema, name, LY_EINVAL);
ctx = LYD_CTX(parent);
tmp = name;
if (ly_parse_nodeid(&tmp, &prefix, &pref_len, &name, &name_len) || tmp[0]) {
LOGERR(ctx, LY_EINVAL, "Attribute name \"%s\" is not valid.", name);
return LY_EVALID;
}
if ((pref_len == 3) && !strncmp(prefix, "xml", 3)) {
name = prefix;
name_len += 1 + pref_len;
prefix = NULL;
pref_len = 0;
}
if (!value) {
value = "";
}
if (strchr(value, ':')) {
LOGWRN(ctx, "Value \"%s\" prefix will never be interpreted as an XML prefix.", value);
}
LY_CHECK_RET(lyd_create_attr(parent, &ret, ctx, name, name_len, prefix, pref_len, module_ns,
module_ns ? strlen(module_ns) : 0, value, strlen(value), NULL, LY_VALUE_XML, NULL, LYD_HINT_DATA));
if (attr) {
*attr = ret;
}
return LY_SUCCESS;
}
static LY_ERR
lyd_change_node_value(struct lyd_node_term *term, struct lyd_value *val, ly_bool use_val)
{
LY_ERR rc = LY_SUCCESS;
struct lyd_node *target, *first;
struct lyplg_type *realtype_plg, *schema_type_plg;
realtype_plg = LYSC_GET_TYPE_PLG(term->value.realtype->plugin_ref);
schema_type_plg = LYSC_GET_TYPE_PLG(((struct lysc_node_leaf *)term->schema)->type->plugin_ref);
if (term->schema->nodetype == LYS_LEAFLIST) {
target = (struct lyd_node *)term;
} else if ((term->schema->flags & LYS_KEY) && term->parent) {
target = (struct lyd_node *)term->parent;
} else {
realtype_plg->free(LYD_CTX(term), &term->value);
if (use_val) {
term->value = *val;
} else {
rc = schema_type_plg->duplicate(LYD_CTX(term), val, &term->value);
}
return rc;
}
if (!LYD_NODE_IS_ALONE(target) && lyds_is_supported(target)) {
first = lyd_first_sibling(target);
first = first == target ? first->next : first;
lyd_unlink_tree(target);
realtype_plg->free(LYD_CTX(term), &term->value);
if (use_val) {
term->value = *val;
} else {
rc = schema_type_plg->duplicate(LYD_CTX(term), val, &term->value);
}
lyd_hash(target);
lyd_insert_node(NULL, &first, target, LYD_INSERT_NODE_DEFAULT);
} else {
lyd_unlink_hash(target);
realtype_plg->free(LYD_CTX(term), &term->value);
if (use_val) {
term->value = *val;
} else {
LY_CHECK_RET(schema_type_plg->duplicate(LYD_CTX(term), val, &term->value));
}
lyd_hash(target);
LY_CHECK_RET(lyd_insert_hash(target));
}
return rc;
}
LY_ERR
lyd_change_term_val(struct lyd_node *term, struct lyd_value *val, ly_bool use_val, ly_bool is_dflt)
{
LY_ERR rc = LY_SUCCESS;
struct lysc_type *type;
struct lyd_node_term *t;
ly_bool dflt_change, val_change;
struct lyplg_type *type_plg;
t = (struct lyd_node_term *)term;
type = ((struct lysc_node_leaf *)term->schema)->type;
type_plg = LYSC_GET_TYPE_PLG(type->plugin_ref);
if (type_plg->compare(LYD_CTX(term), &t->value, val)) {
assert(!(term->flags & LYD_DEFAULT) || !is_dflt);
LY_CHECK_RET(lyd_change_node_value(t, val, use_val));
val_change = 1;
} else {
if (use_val) {
type_plg->free(LYD_CTX(term), val);
}
val_change = 0;
}
if (val_change && (ly_ctx_get_options(LYD_CTX(term)) & LY_CTX_LEAFREF_LINKING)) {
lyd_free_leafref_nodes(t);
}
if (val_change) {
term->flags |= LYD_NEW;
}
if ((term->flags & LYD_DEFAULT) && !is_dflt) {
term->flags &= ~LYD_DEFAULT;
lyd_np_cont_dflt_del(term->parent);
dflt_change = 1;
} else if (!(term->flags & LYD_DEFAULT) && is_dflt) {
term->flags |= LYD_DEFAULT;
lyd_np_cont_dflt_set(term->parent);
dflt_change = 1;
} else {
dflt_change = 0;
}
if (!val_change) {
rc = dflt_change ? LY_EEXIST : LY_ENOT;
}
return rc;
}
static LY_ERR
_lyd_change_term(struct lyd_node *term, const void *value, uint64_t value_size_bits, LY_VALUE_FORMAT format)
{
struct lyd_value val;
assert(term && term->schema && (term->schema->nodetype & LYD_NODE_TERM));
LY_CHECK_RET(lyd_value_store(LYD_CTX(term), term, &val, ((struct lysc_node_leaf *)term->schema)->type, value,
value_size_bits, 0, 0, NULL, format, NULL, LYD_HINT_DATA, term->schema, NULL));
return lyd_change_term_val(term, &val, 1, 0);
}
LIBYANG_API_DEF LY_ERR
lyd_change_term(struct lyd_node *term, const char *val_str)
{
LY_CHECK_ARG_RET(NULL, term, term->schema, term->schema->nodetype & LYD_NODE_TERM, LY_EINVAL);
return _lyd_change_term(term, val_str, val_str ? strlen(val_str) * 8 : 0, LY_VALUE_JSON);
}
LIBYANG_API_DEF LY_ERR
lyd_change_term_canon(struct lyd_node *term, const char *val_str)
{
LY_CHECK_ARG_RET(NULL, term, term->schema, term->schema->nodetype & LYD_NODE_TERM, LY_EINVAL);
return _lyd_change_term(term, val_str, val_str ? strlen(val_str) * 8 : 0, LY_VALUE_CANON);
}
LIBYANG_API_DEF LY_ERR
lyd_change_meta(struct lyd_meta *meta, const char *val_str)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_meta *m2 = NULL;
struct lyd_value val;
ly_bool val_change;
LY_CHECK_ARG_RET(NULL, meta, LY_EINVAL);
if (!val_str) {
val_str = "";
}
ret = lyd_create_meta(NULL, &m2, meta->annotation->module, meta->name, strlen(meta->name), val_str, strlen(val_str) * 8,
0, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, meta->parent ? meta->parent->schema : NULL, NULL, 0, NULL);
LY_CHECK_GOTO(ret, cleanup);
if (lyd_compare_meta(meta, m2)) {
val = meta->value;
meta->value = m2->value;
m2->value = val;
val_change = 1;
} else {
val_change = 0;
}
if (!val_change) {
ret = LY_ENOT;
}
cleanup:
lyd_free_meta_single(m2);
return ret;
}
static void
lyd_anydata_switch_value(struct lyd_node *node1, struct lyd_node *node2)
{
struct lyd_node_any *any1, *any2;
const char *value;
struct lyd_node *child, *iter;
uint32_t hints;
assert((node1->schema->nodetype & LYD_NODE_ANY) && (node2->schema->nodetype & LYD_NODE_ANY));
any1 = (struct lyd_node_any *)node1;
any2 = (struct lyd_node_any *)node2;
child = any1->child;
value = any1->value;
hints = any1->hints;
any1->child = any2->child;
LY_LIST_FOR(any1->child, iter) {
iter->parent = &any1->node;
}
any1->value = any2->value;
any1->hints = any2->hints;
any2->child = child;
LY_LIST_FOR(any2->child, iter) {
iter->parent = &any2->node;
}
any2->value = value;
any2->hints = hints;
}
static LY_ERR
lyd_new_path_update(struct lyd_node *node, const void *value, uint64_t value_size_bits, uint32_t any_hints, uint32_t options,
LY_VALUE_FORMAT format, ly_bool any_use_value, struct lyd_node **new_parent, struct lyd_node **new_node)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *new_any;
const struct lyd_node *child;
const char *val_str;
switch (node->schema->nodetype) {
case LYS_CONTAINER:
case LYS_NOTIF:
case LYS_RPC:
case LYS_ACTION:
case LYS_LIST:
*new_parent = NULL;
*new_node = NULL;
break;
case LYS_LEAFLIST:
if (!lysc_is_dup_inst_list(node->schema)) {
*new_parent = NULL;
*new_node = NULL;
break;
}
case LYS_LEAF:
ret = _lyd_change_term(node, value, value_size_bits, format);
if ((ret == LY_SUCCESS) || (ret == LY_EEXIST)) {
*new_parent = node;
*new_node = node;
ret = LY_SUCCESS;
} else if (ret == LY_ENOT) {
*new_parent = NULL;
*new_node = NULL;
ret = LY_SUCCESS;
}
break;
case LYS_ANYDATA:
case LYS_ANYXML:
if (options & LYD_NEW_PATH_ANY_DATATREE) {
child = value;
val_str = NULL;
} else {
child = NULL;
val_str = value;
}
LY_CHECK_RET(lyd_create_any(node->schema, child, val_str, any_hints, any_use_value, 1, &new_any));
if (lyd_compare_single(node, new_any, 0)) {
lyd_anydata_switch_value(new_any, node);
*new_parent = node;
*new_node = node;
} else {
*new_parent = NULL;
*new_node = NULL;
}
lyd_free_tree(new_any);
break;
default:
LOGINT(LYD_CTX(node));
ret = LY_EINT;
break;
}
return ret;
}
static LY_ERR
lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const void *value, uint64_t value_size_bits,
LY_VALUE_FORMAT format, uint32_t options)
{
LY_ERR r;
struct ly_path_predicate *pred;
const struct lysc_node *schema = NULL;
LY_ARRAY_COUNT_TYPE u, new_count;
const char *canon;
int create = 0;
assert(path);
LY_ARRAY_FOR(path, u) {
schema = path[u].node;
if (lysc_is_dup_inst_list(schema)) {
if (!path[u].predicates ||
((schema->nodetype == LYS_LEAFLIST) && (path[u].predicates[0].type == LY_PATH_PREDTYPE_LEAFLIST))) {
create = 1;
new_count = u;
} else if (path[u].predicates[0].type != LY_PATH_PREDTYPE_POSITION) {
LOG_LOCSET(schema);
LOGVAL(schema->module->ctx, NULL, LYVE_XPATH, "Invalid predicate for state %s \"%s\" in path \"%s\".",
lys_nodetype2str(schema->nodetype), schema->name, str_path);
LOG_LOCBACK(1);
return LY_EINVAL;
}
} else if ((schema->nodetype == LYS_LIST) &&
(!path[u].predicates || (path[u].predicates[0].type != LY_PATH_PREDTYPE_LIST))) {
if ((u < LY_ARRAY_COUNT(path) - 1) || !(options & LYD_NEW_PATH_OPAQ)) {
LOG_LOCSET(schema);
LOGVAL(schema->module->ctx, NULL, LYVE_XPATH, "Predicate missing for %s \"%s\" in path \"%s\".",
lys_nodetype2str(schema->nodetype), schema->name, str_path);
LOG_LOCBACK(1);
return LY_EINVAL;
}
} else if ((schema->nodetype == LYS_LEAFLIST) &&
(!path[u].predicates || (path[u].predicates[0].type != LY_PATH_PREDTYPE_LEAFLIST))) {
r = LY_SUCCESS;
if (options & LYD_NEW_PATH_OPAQ) {
r = ly_value_validate(NULL, schema, value, value_size_bits, format, NULL, 0);
}
if (!r) {
r = lyd_value_validate3(schema, value, LYPLG_BITS2BYTES(value_size_bits), format, NULL, LYD_HINT_DATA,
NULL, 0, NULL, &canon);
LY_CHECK_RET(r && r != LY_EINCOMPLETE, r);
LY_ARRAY_NEW_RET(schema->module->ctx, path[u].predicates, pred, LY_EMEM);
pred->type = LY_PATH_PREDTYPE_LEAFLIST;
pred->value = canon;
}
}
}
if (create) {
while (new_count < LY_ARRAY_COUNT(path)) {
LY_ARRAY_DECREMENT(path);
}
}
return LY_SUCCESS;
}
LY_ERR
lyd_new_path_create(struct lyd_node *parent, const struct ly_ctx *ctx, struct ly_path *p, const char *path,
const void *value, uint64_t value_size_bits, uint32_t any_hints, uint32_t options, struct lyd_node **new_parent,
struct lyd_node **new_node)
{
LY_ERR ret = LY_SUCCESS, r;
struct lyd_node *nparent = NULL, *nnode = NULL, *node = NULL, *cur_parent, *iter, *tree = NULL;
const struct lysc_node *schema;
const char *val = NULL;
ly_bool store_only = (options & LYD_NEW_VAL_STORE_ONLY) ? 1 : 0;
ly_bool any_use_value = (options & LYD_NEW_ANY_USE_VALUE) ? 1 : 0;
LY_ARRAY_COUNT_TYPE path_idx = 0, orig_count = 0;
LY_VALUE_FORMAT format;
uint32_t value_size, hints, count;
LY_CHECK_GOTO(ret = lyd_new_val_get_format(options, &format), cleanup);
value_size = LYPLG_BITS2BYTES(value_size_bits);
orig_count = LY_ARRAY_COUNT(p);
LY_CHECK_GOTO(ret = lyd_new_path_check_find_lypath(p, path, value, value_size_bits, format, options), cleanup);
if (parent) {
if (p[0].doc_root) {
for (tree = parent; tree->parent; tree = tree->parent) {}
}
r = ly_path_eval_partial(p, tree ? NULL : parent, tree, NULL, options & LYD_NEW_PATH_WITH_OPAQ, &path_idx, &node);
if (r == LY_SUCCESS) {
if (orig_count == LY_ARRAY_COUNT(p)) {
if (!(options & LYD_NEW_PATH_UPDATE) && !(node->flags & LYD_DEFAULT)) {
LOGVAL(ctx, node, LYVE_REFERENCE, "Path \"%s\" already exists.", path);
ret = LY_EEXIST;
goto cleanup;
} else if ((options & LYD_NEW_PATH_UPDATE) && lysc_is_key(node->schema)) {
goto cleanup;
}
ret = lyd_new_path_update(node, value, value_size_bits, any_hints, options, format, any_use_value,
&nparent, &nnode);
goto cleanup;
}
} else if (r == LY_EINCOMPLETE) {
++path_idx;
} else if (r == LY_ENOTFOUND) {
if (lysc_data_parent(p[0].node)) {
node = parent;
}
} else {
ret = r;
goto cleanup;
}
}
while (orig_count > LY_ARRAY_COUNT(p)) {
LY_ARRAY_INCREMENT(p);
}
if ((path_idx < LY_ARRAY_COUNT(p)) && lysc_is_dup_inst_list(p[path_idx].node) && p[path_idx].predicates &&
(p[path_idx].predicates[0].type == LY_PATH_PREDTYPE_POSITION)) {
count = 0;
LYD_LIST_FOR_INST(node ? lyd_child(node) : parent, p[path_idx].node, iter) {
++count;
}
if (count + 1 < p[path_idx].predicates[0].position) {
if (count) {
LOGVAL(ctx, NULL, LYVE_REFERENCE,
"Cannot create \"%s\" on position %" PRIu64 ", only %" PRIu32 " instance%s exist%s.",
p[path_idx].node->name, p[path_idx].predicates[0].position, count, (count > 1) ? "s" : "",
(count > 1) ? "" : "s");
} else {
LOGVAL(ctx, NULL, LYVE_REFERENCE, "Cannot create \"%s\" on position %" PRIu64 ", no instances exist.",
p[path_idx].node->name, p[path_idx].predicates[0].position);
}
ret = LY_EINVAL;
goto cleanup;
}
}
for ( ; path_idx < LY_ARRAY_COUNT(p); ++path_idx) {
cur_parent = node;
schema = p[path_idx].node;
switch (schema->nodetype) {
case LYS_LIST:
if (lysc_is_dup_inst_list(schema)) {
LY_CHECK_GOTO(ret = lyd_create_inner(schema, &node), cleanup);
} else if ((options & LYD_NEW_PATH_OPAQ) && !p[path_idx].predicates) {
LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0,
schema->module->name, strlen(schema->module->name), NULL, 0, NULL, LY_VALUE_JSON, NULL,
LYD_NODEHINT_LIST, &node), cleanup);
} else {
LY_CHECK_GOTO(ret = lyd_create_list(schema, p[path_idx].predicates, NULL, store_only, &node), cleanup);
}
break;
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_NEW_PATH_OPAQ) &&
(!p[path_idx].predicates || (p[path_idx].predicates[0].type != LY_PATH_PREDTYPE_LEAFLIST))) {
r = LY_EVALID;
if (lysc_is_dup_inst_list(schema)) {
r = ly_value_validate(NULL, schema, value, value_size_bits, format, NULL, 0);
}
if (r && (r != LY_EINCOMPLETE)) {
hints = LYD_NODEHINT_LEAFLIST;
if (value && (format == LY_VALUE_JSON) && !ly_strncmp("[null]", value, value_size)) {
hints |= LYD_VALHINT_EMPTY;
}
LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0,
schema->module->name, strlen(schema->module->name), value, value_size, NULL, format, NULL,
hints, &node), cleanup);
break;
}
}
if (p[path_idx].predicates && (p[path_idx].predicates[0].type == LY_PATH_PREDTYPE_LEAFLIST)) {
val = p[path_idx].predicates[0].value;
}
if (val) {
LY_CHECK_GOTO(ret = lyd_create_term(schema, nnode, val, strlen(val) * 8, 0, store_only, NULL, format,
NULL, LYD_HINT_DATA, NULL, &node), cleanup);
} else {
LY_CHECK_GOTO(ret = lyd_create_term(schema, nnode, value, value_size_bits, 0, store_only, NULL, format,
NULL, LYD_HINT_DATA, NULL, &node), cleanup);
}
break;
case LYS_LEAF:
if (lysc_is_key(schema) && cur_parent->schema) {
lyd_find_sibling_schema(lyd_child(cur_parent), schema, &node);
assert(node);
goto next_iter;
}
if (options & LYD_NEW_PATH_OPAQ) {
if (cur_parent && !cur_parent->schema) {
r = LY_ENOT;
} else {
r = ly_value_validate(NULL, schema, value ? value : "", value_size_bits, format, NULL, 0);
}
if (r && (r != LY_EINCOMPLETE)) {
hints = 0;
if (value && (format == LY_VALUE_JSON) && !ly_strncmp("[null]", value, value_size)) {
hints |= LYD_VALHINT_EMPTY;
}
ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, schema->module->name,
strlen(schema->module->name), value, value_size, NULL, format, NULL, hints, &node);
LY_CHECK_GOTO(ret, cleanup);
break;
}
}
LY_CHECK_GOTO(ret = lyd_create_term(schema, nnode, value, value_size_bits, 0, store_only, NULL, format, NULL,
LYD_HINT_DATA, NULL, &node), cleanup);
break;
case LYS_ANYDATA:
case LYS_ANYXML:
if (path_idx < LY_ARRAY_COUNT(p) - 1) {
LY_CHECK_GOTO(ret = lyd_create_any(schema, NULL, NULL, any_hints, 1, 0, &node), cleanup);
} else {
LY_CHECK_GOTO(ret = lyd_create_any(schema, (options & LYD_NEW_PATH_ANY_DATATREE) ? value : NULL,
(options & LYD_NEW_PATH_ANY_DATATREE) ? NULL : value, any_hints, any_use_value, 1, &node), cleanup);
}
break;
default:
LOGINT(ctx);
ret = LY_EINT;
goto cleanup;
}
if (p[path_idx].ext) {
node->flags |= LYD_EXT;
}
if (cur_parent) {
lyd_insert_node(cur_parent, NULL, node, LYD_INSERT_NODE_DEFAULT);
} else if (parent) {
lyd_insert_node(NULL, &parent, node, LYD_INSERT_NODE_DEFAULT);
}
next_iter:
if (!nparent) {
nparent = node;
}
nnode = node;
}
cleanup:
if (p) {
while (orig_count > LY_ARRAY_COUNT(p)) {
LY_ARRAY_INCREMENT(p);
}
}
if (!ret) {
if (new_parent) {
*new_parent = nparent;
}
if (new_node) {
*new_node = nnode;
}
} else {
lyd_free_tree(nparent);
}
return ret;
}
static LY_ERR
lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
uint64_t value_size_bits, uint32_t any_hints, uint32_t options, struct lyd_node **new_parent,
struct lyd_node **new_node)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *exp = NULL;
struct ly_path *p = NULL;
assert(parent || ctx);
assert(path && ((path[0] == '/') || parent));
if (!ctx) {
ctx = LYD_CTX(parent);
}
LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, 0, 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST,
LY_PATH_PRED_SIMPLE, &exp), cleanup);
LY_CHECK_GOTO(ret = ly_path_compile(ctx, lyd_node_schema(parent), exp, options & LYD_NEW_VAL_OUTPUT ?
LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p), cleanup);
LY_CHECK_GOTO(ret = lyd_new_path_create(parent, ctx, p, path, value, value_size_bits, any_hints, options, new_parent,
new_node), cleanup);
cleanup:
lyxp_expr_free(exp);
ly_path_free(p);
return ret;
}
LIBYANG_API_DEF LY_ERR
lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const char *value, uint32_t options,
struct lyd_node **node)
{
LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, ctx, LY_EINVAL);
return lyd_new_path_(parent, ctx, path, value, value ? strlen(value) * 8 : 0, 0, options, node, NULL);
}
LIBYANG_API_DEF LY_ERR
lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
uint64_t value_size_bits, uint32_t any_hints, uint32_t options, struct lyd_node **new_parent,
struct lyd_node **new_node)
{
LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, ctx, LY_EINVAL);
if (!value_size_bits && value) {
value_size_bits = strlen(value) * 8;
}
return lyd_new_path_(parent, ctx, path, value, value_size_bits, any_hints, options, new_parent, new_node);
}
LY_ERR
lyd_new_implicit(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *ext_val,
uint32_t impl_opts, struct ly_ht *getnext_ht, struct lyd_node **diff)
{
const struct lysc_node *snode, **choices, **snodes;
struct lyd_node *node = NULL;
struct lysc_value *dflts;
LY_ARRAY_COUNT_TYPE u;
uint32_t i;
ly_bool incomplete;
assert(first && (parent || sparent || mod));
if (!sparent && parent) {
sparent = parent->schema;
}
LY_CHECK_RET(lyd_val_getnext_get(NULL, sparent, mod, impl_opts & LYD_IMPLICIT_OUTPUT, getnext_ht, &choices, &snodes));
for (i = 0; choices && choices[i]; ++i) {
snode = choices[i];
if ((impl_opts & LYD_IMPLICIT_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
continue;
} else if ((impl_opts & LYD_IMPLICIT_NO_CONFIG) && (snode->flags & LYS_CONFIG_W)) {
continue;
} else if (snode->flags & LYS_STATUS_OBSLT) {
continue;
}
node = lys_getnext_data(NULL, *first, NULL, snode, NULL);
if (!node && ((struct lysc_node_choice *)snode)->dflt) {
LY_CHECK_RET(lyd_new_implicit(parent, first, &((struct lysc_node_choice *)snode)->dflt->node,
NULL, node_when, node_types, ext_val, impl_opts, getnext_ht, diff));
} else if (node) {
assert(node->schema->parent->nodetype == LYS_CASE);
LY_CHECK_RET(lyd_new_implicit(parent, first, node->schema->parent, NULL, node_when, node_types,
ext_val, impl_opts, getnext_ht, diff));
}
}
for (i = 0; snodes && snodes[i]; ++i) {
snode = snodes[i];
if ((impl_opts & LYD_IMPLICIT_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
continue;
} else if ((impl_opts & LYD_IMPLICIT_NO_CONFIG) && (snode->flags & LYS_CONFIG_W)) {
continue;
} else if (snode->flags & LYS_STATUS_OBSLT) {
continue;
}
switch (snode->nodetype) {
case LYS_CONTAINER:
if (!(snode->flags & LYS_PRESENCE) && lyd_find_sibling_val(*first, snode, NULL, 0, NULL)) {
LY_CHECK_RET(lyd_create_inner(snode, &node));
node->flags = LYD_DEFAULT | (lysc_has_when(snode) ? LYD_WHEN_TRUE : 0);
lyd_insert_node(parent, first, node, LYD_INSERT_NODE_DEFAULT);
if (lysc_has_when(snode) && node_when) {
LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
}
if (ext_val) {
LY_CHECK_RET(lyd_validate_node_ext(node, ext_val));
}
if (diff) {
LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
}
}
break;
case LYS_LEAF:
if (!(impl_opts & LYD_IMPLICIT_NO_DEFAULTS) && ((struct lysc_node_leaf *)snode)->dflt.str &&
lyd_find_sibling_val(*first, snode, NULL, 0, NULL)) {
LY_CHECK_RET(lyd_create_term(snode, parent, ((struct lysc_node_leaf *)snode)->dflt.str,
strlen(((struct lysc_node_leaf *)snode)->dflt.str) * 8, 1, 1, NULL, LY_VALUE_SCHEMA_RESOLVED,
((struct lysc_node_leaf *)snode)->dflt.prefixes, LYD_HINT_SCHEMA, &incomplete, &node));
if (incomplete && node_types) {
LY_CHECK_RET(ly_set_add(node_types, node, 1, NULL));
}
node->flags = LYD_DEFAULT | (lysc_has_when(snode) ? LYD_WHEN_TRUE : 0);
lyd_insert_node(parent, first, node, LYD_INSERT_NODE_DEFAULT);
if (lysc_has_when(snode) && node_when) {
LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
}
if (ext_val) {
LY_CHECK_RET(lyd_validate_node_ext(node, ext_val));
}
if (diff) {
LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
}
}
break;
case LYS_LEAFLIST:
if (!(impl_opts & LYD_IMPLICIT_NO_DEFAULTS) && ((struct lysc_node_leaflist *)snode)->dflts &&
lyd_find_sibling_val(*first, snode, NULL, 0, NULL)) {
dflts = ((struct lysc_node_leaflist *)snode)->dflts;
LY_ARRAY_FOR(dflts, u) {
LY_CHECK_RET(lyd_create_term(snode, parent, dflts[u].str, strlen(dflts[u].str) * 8, 1, 1, NULL,
LY_VALUE_SCHEMA_RESOLVED, dflts[u].prefixes, LYD_HINT_SCHEMA, &incomplete, &node));
if (incomplete && node_types) {
LY_CHECK_RET(ly_set_add(node_types, node, 1, NULL));
}
node->flags = LYD_DEFAULT | (lysc_has_when(snode) ? LYD_WHEN_TRUE : 0);
lyd_insert_node(parent, first, node, LYD_INSERT_NODE_DEFAULT);
if (lysc_has_when(snode) && node_when) {
LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
}
if (ext_val) {
LY_CHECK_RET(lyd_validate_node_ext(node, ext_val));
}
if (diff) {
LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
}
}
}
break;
default:
break;
}
}
return LY_SUCCESS;
}
LY_ERR
lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *ext_val,
uint32_t impl_opts, struct ly_ht *getnext_ht, struct lyd_node **diff)
{
struct lyd_node *child;
LY_CHECK_RET(lyd_new_implicit(parent, first, sparent, mod, node_when, node_types, ext_val, impl_opts,
getnext_ht, diff));
LY_LIST_FOR(parent ? lyd_child_no_keys(parent) : *first, child) {
if ((child->flags & LYD_DEFAULT) && (child->schema->nodetype == LYS_CONTAINER)) {
LY_CHECK_RET(lyd_new_implicit_r(child, lyd_node_child_p(child), NULL, mod, node_when, node_types,
ext_val, impl_opts, getnext_ht, diff));
}
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyd_new_implicit_tree(struct lyd_node *tree, uint32_t implicit_options, struct lyd_node **diff)
{
LY_ERR rc = LY_SUCCESS;
struct lyd_node *node;
struct ly_set node_when = {0};
struct ly_ht *getnext_ht = NULL;
LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL);
if (diff) {
*diff = NULL;
}
LY_CHECK_GOTO(rc = lyd_val_getnext_ht_new(&getnext_ht), cleanup);
LYD_TREE_DFS_BEGIN(tree, node) {
if (node->schema && (node->schema->nodetype & LYD_NODE_INNER)) {
LY_CHECK_GOTO(rc = lyd_new_implicit(node, lyd_node_child_p(node), NULL, NULL, &node_when, NULL, NULL,
implicit_options, getnext_ht, diff), cleanup);
}
LYD_TREE_DFS_END(tree, node);
}
rc = lyd_validate_unres(&tree, NULL, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, 0, diff);
LY_CHECK_GOTO(rc, cleanup);
cleanup:
ly_set_erase(&node_when, NULL);
lyd_val_getnext_ht_free(getnext_ht);
if (rc && diff) {
lyd_free_all(*diff);
*diff = NULL;
}
return rc;
}
LIBYANG_API_DEF LY_ERR
lyd_new_implicit_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t implicit_options, struct lyd_node **diff)
{
const struct lys_module *mod;
struct lyd_node *d = NULL;
uint32_t i = 0;
LY_ERR rc = LY_SUCCESS;
LY_CHECK_ARG_RET(ctx, tree, *tree || ctx, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, *tree ? LYD_CTX(*tree) : NULL, ctx, LY_EINVAL);
if (diff) {
*diff = NULL;
}
if (!ctx) {
ctx = LYD_CTX(*tree);
}
while ((mod = ly_ctx_get_module_iter(ctx, &i))) {
if (!mod->implemented) {
continue;
}
LY_CHECK_GOTO(rc = lyd_new_implicit_module(tree, mod, implicit_options, diff ? &d : NULL), cleanup);
if (d) {
lyd_insert_sibling(*diff, d, diff);
d = NULL;
}
}
cleanup:
if (rc && diff) {
lyd_free_all(*diff);
*diff = NULL;
}
return rc;
}
LIBYANG_API_DEF LY_ERR
lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module, uint32_t implicit_options,
struct lyd_node **diff)
{
LY_ERR rc = LY_SUCCESS;
struct lyd_node *root, *d = NULL;
struct ly_set node_when = {0};
struct ly_ht *getnext_ht = NULL;
LY_CHECK_ARG_RET(NULL, tree, module, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, *tree ? LYD_CTX(*tree) : NULL, module ? module->ctx : NULL, LY_EINVAL);
if (diff) {
*diff = NULL;
}
LY_CHECK_GOTO(rc = lyd_val_getnext_ht_new(&getnext_ht), cleanup);
rc = lyd_new_implicit(NULL, tree, NULL, module, &node_when, NULL, NULL, implicit_options, getnext_ht, diff);
LY_CHECK_GOTO(rc, cleanup);
LY_CHECK_GOTO(rc = lyd_validate_unres(tree, module, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL,
0, diff), cleanup);
LY_LIST_FOR(*tree, root) {
LY_CHECK_GOTO(rc = lyd_new_implicit_tree(root, implicit_options, diff ? &d : NULL), cleanup);
if (d) {
lyd_insert_sibling(*diff, d, diff);
d = NULL;
}
}
cleanup:
ly_set_erase(&node_when, NULL);
lyd_val_getnext_ht_free(getnext_ht);
if (rc && diff) {
lyd_free_all(*diff);
*diff = NULL;
}
return rc;
}