diff options
Diffstat (limited to '')
-rw-r--r-- | src/tree_data.c | 2943 |
1 files changed, 2943 insertions, 0 deletions
diff --git a/src/tree_data.c b/src/tree_data.c new file mode 100644 index 0000000..d6a04ff --- /dev/null +++ b/src/tree_data.c @@ -0,0 +1,2943 @@ +/** + * @file tree_data.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> + * @brief Data tree functions + * + * Copyright (c) 2015 - 2022 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. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ + +#define _GNU_SOURCE + +#include "tree_data.h" + +#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 "common.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 "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_edit.h" +#include "tree_schema.h" +#include "tree_schema_internal.h" +#include "validation.h" +#include "xml.h" +#include "xpath.h" + +static LYD_FORMAT +lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format) +{ + if (!format && (in->type == LY_IN_FILEPATH)) { + /* unknown format - try to detect it from filename's suffix */ + const char *path = in->method.fpath.filepath; + size_t len = strlen(path); + + /* ignore trailing whitespaces */ + for ( ; len > 0 && isspace(path[len - 1]); len--) {} + + if ((len >= LY_XML_SUFFIX_LEN + 1) && + !strncmp(&path[len - LY_XML_SUFFIX_LEN], LY_XML_SUFFIX, LY_XML_SUFFIX_LEN)) { + format = LYD_XML; + } else if ((len >= LY_JSON_SUFFIX_LEN + 1) && + !strncmp(&path[len - LY_JSON_SUFFIX_LEN], LY_JSON_SUFFIX, LY_JSON_SUFFIX_LEN)) { + format = LYD_JSON; + } else if ((len >= LY_LYB_SUFFIX_LEN + 1) && + !strncmp(&path[len - LY_LYB_SUFFIX_LEN], LY_LYB_SUFFIX, LY_LYB_SUFFIX_LEN)) { + format = LYD_LYB; + } /* else still unknown */ + } + + return format; +} + +/** + * @brief Parse YANG data into a data tree. + * + * @param[in] ctx libyang context. + * @param[in] ext Optional extenion instance to parse data following the schema tree specified in the extension instance + * @param[in] parent Parent to connect the parsed nodes to, if any. + * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL. + * @param[in] in Input handle to read the input from. + * @param[in] format Expected format of the data in @p in. + * @param[in] parse_opts Options for parser. + * @param[in] val_opts Options for validation. + * @param[out] op Optional pointer to the parsed operation, if any. + * @return LY_ERR value. + */ +static LY_ERR +lyd_parse(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, struct lyd_node **first_p, + struct ly_in *in, LYD_FORMAT format, uint32_t parse_opts, uint32_t val_opts, struct lyd_node **op) +{ + LY_ERR rc = LY_SUCCESS; + struct lyd_ctx *lydctx = NULL; + struct ly_set parsed = {0}; + struct lyd_node *first; + uint32_t i, int_opts = 0; + ly_bool subtree_sibling = 0; + + assert(ctx && (parent || first_p)); + + format = lyd_parse_get_format(in, format); + if (first_p) { + *first_p = NULL; + } + + /* remember input position */ + in->func_start = in->current; + + /* set internal options */ + if (!(parse_opts & LYD_PARSE_SUBTREE)) { + int_opts = LYD_INTOPT_WITH_SIBLINGS; + } + + /* parse the data */ + switch (format) { + case LYD_XML: + rc = lyd_parse_xml(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, + &subtree_sibling, &lydctx); + break; + case LYD_JSON: + rc = lyd_parse_json(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, + &subtree_sibling, &lydctx); + break; + case LYD_LYB: + rc = lyd_parse_lyb(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, + &subtree_sibling, &lydctx); + break; + case LYD_UNKNOWN: + LOGARG(ctx, format); + rc = LY_EINVAL; + break; + } + LY_CHECK_GOTO(rc, cleanup); + + if (parent) { + /* get first top-level sibling */ + for (first = parent; first->parent; first = lyd_parent(first)) {} + first = lyd_first_sibling(first); + first_p = &first; + } + + if (!(parse_opts & LYD_PARSE_ONLY)) { + /* validate data */ + rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types, + &lydctx->ext_node, &lydctx->ext_val, NULL); + LY_CHECK_GOTO(rc, cleanup); + } + + /* set the operation node */ + if (op) { + *op = lydctx->op_node; + } + +cleanup: + if (lydctx) { + lydctx->free(lydctx); + } + if (rc) { + if (parent) { + /* free all the parsed subtrees */ + for (i = 0; i < parsed.count; ++i) { + lyd_free_tree(parsed.dnodes[i]); + } + } else { + /* free everything */ + lyd_free_all(*first_p); + *first_p = NULL; + } + } else if (subtree_sibling) { + rc = LY_ENOT; + } + ly_set_erase(&parsed, NULL); + return rc; +} + +LIBYANG_API_DEF LY_ERR +lyd_parse_ext_data(const struct lysc_ext_instance *ext, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format, + uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree) +{ + const struct ly_ctx *ctx = ext ? ext->module->ctx : NULL; + + LY_CHECK_ARG_RET(ctx, ext, in, parent || tree, LY_EINVAL); + LY_CHECK_ARG_RET(ctx, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL); + LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL); + + return lyd_parse(ctx, ext, parent, tree, in, format, parse_options, validate_options, NULL); +} + +LIBYANG_API_DEF LY_ERR +lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format, + uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree) +{ + LY_CHECK_ARG_RET(ctx, ctx, in, parent || tree, LY_EINVAL); + LY_CHECK_ARG_RET(ctx, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL); + LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL); + + return lyd_parse(ctx, NULL, parent, tree, in, format, parse_options, validate_options, NULL); +} + +LIBYANG_API_DEF LY_ERR +lyd_parse_data_mem(const struct ly_ctx *ctx, const char *data, LYD_FORMAT format, uint32_t parse_options, + uint32_t validate_options, struct lyd_node **tree) +{ + LY_ERR ret; + struct ly_in *in; + + LY_CHECK_RET(ly_in_new_memory(data, &in)); + ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree); + + ly_in_free(in, 0); + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyd_parse_data_fd(const struct ly_ctx *ctx, int fd, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options, + struct lyd_node **tree) +{ + LY_ERR ret; + struct ly_in *in; + + LY_CHECK_RET(ly_in_new_fd(fd, &in)); + ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree); + + ly_in_free(in, 0); + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyd_parse_data_path(const struct ly_ctx *ctx, const char *path, LYD_FORMAT format, uint32_t parse_options, + uint32_t validate_options, struct lyd_node **tree) +{ + LY_ERR ret; + struct ly_in *in; + + LY_CHECK_RET(ly_in_new_filepath(path, 0, &in)); + ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree); + + ly_in_free(in, 0); + return ret; +} + +/** + * @brief Parse YANG data into an operation data tree, in case the extension instance is specified, keep the searching + * for schema nodes locked inside the extension instance. + * + * At least one of @p parent, @p tree, or @p op must always be set. + * + * Specific @p data_type values have different parameter meaning as follows: + * - ::LYD_TYPE_RPC_NETCONF: + * - @p parent - must be NULL, the whole RPC is expected; + * - @p format - must be ::LYD_XML, NETCONF supports only this format; + * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as + * a separate opaque data tree, even if the function fails, this may be returned; + * - @p op - must be provided, the RPC/action data tree itself will be returned here, pointing to the operation; + * + * - ::LYD_TYPE_NOTIF_NETCONF: + * - @p parent - must be NULL, the whole notification is expected; + * - @p format - must be ::LYD_XML, NETCONF supports only this format; + * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as + * a separate opaque data tree, even if the function fails, this may be returned; + * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation; + * + * - ::LYD_TYPE_REPLY_NETCONF: + * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node; + * - @p format - must be ::LYD_XML, NETCONF supports only this format; + * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as + * a separate opaque data tree, even if the function fails, this may be returned; + * - @p op - must be NULL, the reply is appended to the RPC; + * Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC. + * + * @param[in] ctx libyang context. + * @param[in] ext Extension instance providing the specific schema tree to match with the data being parsed. + * @param[in] parent Optional parent to connect the parsed nodes to. + * @param[in] in Input handle to read the input from. + * @param[in] format Expected format of the data in @p in. + * @param[in] data_type Expected operation to parse (@ref datatype). + * @param[out] tree Optional full parsed data tree. If @p parent is set, set to NULL. + * @param[out] op Optional parsed operation node. + * @return LY_ERR value. + * @return LY_ENOT if @p data_type is a NETCONF message and the root XML element is not the expected one. + */ +static LY_ERR +lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, + struct ly_in *in, LYD_FORMAT format, enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op) +{ + LY_ERR rc = LY_SUCCESS; + struct lyd_ctx *lydctx = NULL; + struct ly_set parsed = {0}; + struct lyd_node *first = NULL, *envp = NULL; + uint32_t i, parse_opts, val_opts, int_opts = 0; + + if (!ctx) { + ctx = LYD_CTX(parent); + } + if (tree) { + *tree = NULL; + } + if (op) { + *op = NULL; + } + + format = lyd_parse_get_format(in, format); + + /* remember input position */ + in->func_start = in->current; + + /* set parse and validation opts */ + parse_opts = LYD_PARSE_ONLY | LYD_PARSE_STRICT; + val_opts = 0; + + switch (data_type) { + + /* special XML NETCONF data types */ + case LYD_TYPE_RPC_NETCONF: + case LYD_TYPE_NOTIF_NETCONF: + LY_CHECK_ARG_RET(ctx, format == LYD_XML, !parent, tree, op, LY_EINVAL); + /* fallthrough */ + case LYD_TYPE_REPLY_NETCONF: + if (data_type == LYD_TYPE_REPLY_NETCONF) { + LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op, + LY_EINVAL); + } + + /* parse the NETCONF message */ + rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx); + if (rc) { + if (envp) { + /* special situation when the envelopes were parsed successfully */ + *tree = envp; + } + goto cleanup; + } + + /* set out params correctly */ + if (envp) { + /* special out param meaning */ + *tree = envp; + } else { + *tree = parent ? NULL : first; + } + if (op) { + *op = lydctx->op_node; + } + goto cleanup; + + /* set internal opts */ + case LYD_TYPE_RPC_YANG: + int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS; + break; + case LYD_TYPE_NOTIF_YANG: + int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS; + break; + case LYD_TYPE_REPLY_YANG: + int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS; + break; + case LYD_TYPE_DATA_YANG: + LOGINT(ctx); + rc = LY_EINT; + goto cleanup; + } + + /* parse the data */ + switch (format) { + case LYD_XML: + rc = lyd_parse_xml(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx); + break; + case LYD_JSON: + rc = lyd_parse_json(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx); + break; + case LYD_LYB: + rc = lyd_parse_lyb(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx); + break; + case LYD_UNKNOWN: + LOGARG(ctx, format); + rc = LY_EINVAL; + break; + } + LY_CHECK_GOTO(rc, cleanup); + + /* set out params correctly */ + if (tree) { + *tree = parent ? NULL : first; + } + if (op) { + *op = lydctx->op_node; + } + +cleanup: + if (lydctx) { + lydctx->free(lydctx); + } + if (rc) { + /* free all the parsed nodes */ + if (parsed.count) { + i = parsed.count; + do { + --i; + lyd_free_tree(parsed.dnodes[i]); + } while (i); + } + if (tree && ((format != LYD_XML) || !envp)) { + *tree = NULL; + } + if (op) { + *op = NULL; + } + } + ly_set_erase(&parsed, NULL); + return rc; +} + +LIBYANG_API_DEF LY_ERR +lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format, + enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op) +{ + LY_CHECK_ARG_RET(ctx, ctx || parent, in, data_type, parent || tree || op, LY_EINVAL); + + return lyd_parse_op_(ctx, NULL, parent, in, format, data_type, tree, op); +} + +LIBYANG_API_DEF LY_ERR +lyd_parse_ext_op(const struct lysc_ext_instance *ext, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format, + enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op) +{ + const struct ly_ctx *ctx = ext ? ext->module->ctx : NULL; + + LY_CHECK_ARG_RET(ctx, ext, in, data_type, parent || tree || op, LY_EINVAL); + + return lyd_parse_op_(ctx, ext, parent, in, format, data_type, tree, op); +} + +struct lyd_node * +lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct lyd_node *new_node) +{ + const struct lysc_node *schema, *sparent; + struct lyd_node *match = NULL; + ly_bool found; + uint32_t getnext_opts; + + assert(new_node); + + if (!first_sibling || !new_node->schema || (LYD_CTX(first_sibling) != LYD_CTX(new_node))) { + /* insert at the end, no next anchor */ + return NULL; + } + + getnext_opts = 0; + if (new_node->schema->flags & LYS_IS_OUTPUT) { + getnext_opts = LYS_GETNEXT_OUTPUT; + } + + if (first_sibling->parent && first_sibling->parent->schema && first_sibling->parent->children_ht) { + /* find the anchor using hashes */ + sparent = first_sibling->parent->schema; + schema = lys_getnext(new_node->schema, sparent, NULL, getnext_opts); + while (schema) { + /* keep trying to find the first existing instance of the closest following schema sibling, + * otherwise return NULL - inserting at the end */ + if (!lyd_find_sibling_schema(first_sibling, schema, &match)) { + break; + } + + schema = lys_getnext(schema, sparent, NULL, getnext_opts); + } + } else { + /* find the anchor without hashes */ + match = (struct lyd_node *)first_sibling; + sparent = lysc_data_parent(new_node->schema); + if (!sparent) { + /* we are in top-level, skip all the data from preceding modules */ + LY_LIST_FOR(match, match) { + if (!match->schema || (strcmp(lyd_owner_module(match)->name, lyd_owner_module(new_node)->name) >= 0)) { + break; + } + } + } + + /* get the first schema sibling */ + schema = lys_getnext(NULL, sparent, new_node->schema->module->compiled, getnext_opts); + + found = 0; + LY_LIST_FOR(match, match) { + if (!match->schema || (lyd_owner_module(match) != lyd_owner_module(new_node))) { + /* we have found an opaque node, which must be at the end, so use it OR + * modules do not match, so we must have traversed all the data from new_node module (if any), + * we have found the first node of the next module, that is what we want */ + break; + } + + /* skip schema nodes until we find the instantiated one */ + while (!found) { + if (new_node->schema == schema) { + /* we have found the schema of the new node, continue search to find the first + * data node with a different schema (after our schema) */ + found = 1; + break; + } + if (match->schema == schema) { + /* current node (match) is a data node still before the new node, continue search in data */ + break; + } + schema = lys_getnext(schema, sparent, new_node->schema->module->compiled, getnext_opts); + assert(schema); + } + + if (found && (match->schema != new_node->schema)) { + /* find the next node after we have found our node schema data instance */ + break; + } + } + } + + return match; +} + +void +lyd_insert_after_node(struct lyd_node *sibling, struct lyd_node *node) +{ + struct lyd_node_inner *par; + + assert(!node->next && (node->prev == node)); + + node->next = sibling->next; + node->prev = sibling; + sibling->next = node; + if (node->next) { + /* sibling had a succeeding node */ + node->next->prev = node; + } else { + /* sibling was last, find first sibling and change its prev */ + if (sibling->parent) { + sibling = sibling->parent->child; + } else { + for ( ; sibling->prev->next != node; sibling = sibling->prev) {} + } + sibling->prev = node; + } + node->parent = sibling->parent; + + for (par = node->parent; par; par = par->parent) { + if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) { + /* remove default flags from NP containers */ + par->flags &= ~LYD_DEFAULT; + } + } +} + +void +lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node) +{ + struct lyd_node_inner *par; + + assert(!node->next && (node->prev == node)); + + node->next = sibling; + /* covers situation of sibling being first */ + node->prev = sibling->prev; + sibling->prev = node; + if (node->prev->next) { + /* sibling had a preceding node */ + node->prev->next = node; + } else if (sibling->parent) { + /* sibling was first and we must also change parent child pointer */ + sibling->parent->child = node; + } + node->parent = sibling->parent; + + for (par = node->parent; par; par = par->parent) { + if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) { + /* remove default flags from NP containers */ + par->flags &= ~LYD_DEFAULT; + } + } +} + +/** + * @brief Insert node as the first and only child of a parent. + * + * Handles inserting into NP containers and key-less lists. + * + * @param[in] parent Parent to insert into. + * @param[in] node Node to insert. + */ +static void +lyd_insert_only_child(struct lyd_node *parent, struct lyd_node *node) +{ + struct lyd_node_inner *par; + + assert(parent && !lyd_child(parent) && !node->next && (node->prev == node)); + assert(!parent->schema || (parent->schema->nodetype & LYD_NODE_INNER)); + + par = (struct lyd_node_inner *)parent; + + par->child = node; + node->parent = par; + + for ( ; par; par = par->parent) { + if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) { + /* remove default flags from NP containers */ + par->flags &= ~LYD_DEFAULT; + } + } +} + +/** + * @brief Learn whether a list instance has all the keys. + * + * @param[in] list List instance to check. + * @return non-zero if all the keys were found, + * @return 0 otherwise. + */ +static int +lyd_insert_has_keys(const struct lyd_node *list) +{ + const struct lyd_node *key; + const struct lysc_node *skey = NULL; + + assert(list->schema->nodetype == LYS_LIST); + key = lyd_child(list); + while ((skey = lys_getnext(skey, list->schema, NULL, 0)) && (skey->flags & LYS_KEY)) { + if (!key || (key->schema != skey)) { + /* key missing */ + return 0; + } + + key = key->next; + } + + /* all keys found */ + return 1; +} + +void +lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling_p, struct lyd_node *node, ly_bool last) +{ + struct lyd_node *anchor, *first_sibling; + + /* inserting list without its keys is not supported */ + assert((parent || first_sibling_p) && node && (node->hash || !node->schema)); + assert(!parent || !parent->schema || + (parent->schema->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_RPC | LYS_ACTION | LYS_NOTIF))); + + if (!parent && first_sibling_p && (*first_sibling_p) && (*first_sibling_p)->parent) { + parent = lyd_parent(*first_sibling_p); + } + + /* get first sibling */ + first_sibling = parent ? lyd_child(parent) : *first_sibling_p; + + if (last || (first_sibling && (first_sibling->flags & LYD_EXT))) { + /* no next anchor */ + anchor = NULL; + } else { + /* find the anchor, our next node, so we can insert before it */ + anchor = lyd_insert_get_next_anchor(first_sibling, node); + } + + if (anchor) { + /* insert before the anchor */ + lyd_insert_before_node(anchor, node); + if (!parent && (*first_sibling_p == anchor)) { + /* move first sibling */ + *first_sibling_p = node; + } + } else if (first_sibling) { + /* insert as the last node */ + lyd_insert_after_node(first_sibling->prev, node); + } else if (parent) { + /* insert as the only child */ + lyd_insert_only_child(parent, node); + } else { + /* insert as the only sibling */ + *first_sibling_p = node; + } + + /* insert into parent HT */ + lyd_insert_hash(node); + + /* finish hashes for our parent, if needed and possible */ + if (node->schema && (node->schema->flags & LYS_KEY) && parent && lyd_insert_has_keys(parent)) { + lyd_hash(parent); + + /* now we can insert even the list into its parent HT */ + lyd_insert_hash(parent); + } +} + +/** + * @brief Check schema place of a node to be inserted. + * + * @param[in] parent Schema node of the parent data node. + * @param[in] sibling Schema node of a sibling data node. + * @param[in] schema Schema node if the data node to be inserted. + * @return LY_SUCCESS on success. + * @return LY_EINVAL if the place is invalid. + */ +static LY_ERR +lyd_insert_check_schema(const struct lysc_node *parent, const struct lysc_node *sibling, const struct lysc_node *schema) +{ + const struct lysc_node *par2; + + assert(!parent || !(parent->nodetype & (LYS_CASE | LYS_CHOICE))); + assert(!sibling || !(sibling->nodetype & (LYS_CASE | LYS_CHOICE))); + assert(!schema || !(schema->nodetype & (LYS_CASE | LYS_CHOICE))); + + if (!schema || (!parent && !sibling)) { + /* opaque nodes can be inserted wherever */ + return LY_SUCCESS; + } + + if (!parent) { + parent = lysc_data_parent(sibling); + } + + /* find schema parent */ + par2 = lysc_data_parent(schema); + + if (parent) { + /* inner node */ + if (par2 != parent) { + LOGERR(schema->module->ctx, LY_EINVAL, "Cannot insert, parent of \"%s\" is not \"%s\".", schema->name, + parent->name); + return LY_EINVAL; + } + } else { + /* top-level node */ + if (par2) { + LOGERR(schema->module->ctx, LY_EINVAL, "Cannot insert, node \"%s\" is not top-level.", schema->name); + return LY_EINVAL; + } + } + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyd_insert_child(struct lyd_node *parent, struct lyd_node *node) +{ + struct lyd_node *iter; + + LY_CHECK_ARG_RET(NULL, parent, node, !parent->schema || (parent->schema->nodetype & LYD_NODE_INNER), LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(LYD_CTX(parent), LYD_CTX(node), LY_EINVAL); + + LY_CHECK_RET(lyd_insert_check_schema(parent->schema, NULL, node->schema)); + + if (node->schema && (node->schema->flags & LYS_KEY)) { + LOGERR(LYD_CTX(parent), LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name); + return LY_EINVAL; + } + + if (node->parent || node->prev->next) { + lyd_unlink_tree(node); + } + + while (node) { + iter = node->next; + lyd_unlink_tree(node); + lyd_insert_node(parent, NULL, node, 0); + node = iter; + } + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyplg_ext_insert(struct lyd_node *parent, struct lyd_node *first) +{ + struct lyd_node *iter; + + LY_CHECK_ARG_RET(NULL, parent, first, !first->parent, !first->prev->next, + !parent->schema || (parent->schema->nodetype & LYD_NODE_INNER), LY_EINVAL); + + if (first->schema && (first->schema->flags & LYS_KEY)) { + LOGERR(LYD_CTX(parent), LY_EINVAL, "Cannot insert key \"%s\".", first->schema->name); + return LY_EINVAL; + } + + while (first) { + iter = first->next; + lyd_unlink_tree(first); + lyd_insert_node(parent, NULL, first, 1); + first = iter; + } + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_node **first) +{ + struct lyd_node *iter; + + LY_CHECK_ARG_RET(NULL, node, LY_EINVAL); + + if (sibling) { + LY_CHECK_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema)); + } + + if (sibling == node) { + /* we need to keep the connection to siblings so we can insert into them */ + sibling = sibling->prev; + } + + if (node->parent || node->prev->next) { + lyd_unlink_tree(node); + } + + while (node) { + if (lysc_is_key(node->schema)) { + LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot insert key \"%s\".", node->schema->name); + return LY_EINVAL; + } + + iter = node->next; + lyd_unlink_tree(node); + lyd_insert_node(NULL, &sibling, node, 0); + node = iter; + } + + if (first) { + /* find the first sibling */ + *first = sibling; + while ((*first)->prev->next) { + *first = (*first)->prev; + } + } + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node) +{ + LY_CHECK_ARG_RET(NULL, sibling, node, sibling != node, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(LYD_CTX(sibling), LYD_CTX(node), LY_EINVAL); + + LY_CHECK_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema)); + + if (node->schema && (!(node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) || !(node->schema->flags & LYS_ORDBY_USER))) { + LOGERR(LYD_CTX(sibling), LY_EINVAL, "Can be used only for user-ordered nodes."); + return LY_EINVAL; + } + if (node->schema && sibling->schema && (node->schema != sibling->schema)) { + LOGERR(LYD_CTX(sibling), LY_EINVAL, "Cannot insert before a different schema node instance."); + return LY_EINVAL; + } + + lyd_unlink_tree(node); + lyd_insert_before_node(sibling, node); + lyd_insert_hash(node); + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node) +{ + LY_CHECK_ARG_RET(NULL, sibling, node, sibling != node, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(LYD_CTX(sibling), LYD_CTX(node), LY_EINVAL); + + LY_CHECK_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema)); + + if (node->schema && (!(node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) || !(node->schema->flags & LYS_ORDBY_USER))) { + LOGERR(LYD_CTX(sibling), LY_EINVAL, "Can be used only for user-ordered nodes."); + return LY_EINVAL; + } + if (node->schema && sibling->schema && (node->schema != sibling->schema)) { + LOGERR(LYD_CTX(sibling), LY_EINVAL, "Cannot insert after a different schema node instance."); + return LY_EINVAL; + } + + lyd_unlink_tree(node); + lyd_insert_after_node(sibling, node); + lyd_insert_hash(node); + + return LY_SUCCESS; +} + +LIBYANG_API_DEF void +lyd_unlink_siblings(struct lyd_node *node) +{ + struct lyd_node *next, *elem, *first = NULL; + + LY_LIST_FOR_SAFE(node, next, elem) { + lyd_unlink_tree(elem); + lyd_insert_node(NULL, &first, elem, 1); + } +} + +LIBYANG_API_DEF void +lyd_unlink_tree(struct lyd_node *node) +{ + struct lyd_node *iter; + + if (!node) { + return; + } + + /* update hashes while still linked into the tree */ + lyd_unlink_hash(node); + + /* unlink from siblings */ + if (node->prev->next) { + node->prev->next = node->next; + } + if (node->next) { + node->next->prev = node->prev; + } else { + /* unlinking the last node */ + if (node->parent) { + iter = node->parent->child; + } else { + iter = node->prev; + while (iter->prev != node) { + iter = iter->prev; + } + } + /* update the "last" pointer from the first node */ + iter->prev = node->prev; + } + + /* unlink from parent */ + if (node->parent) { + if (node->parent->child == node) { + /* the node is the first child */ + node->parent->child = node->next; + } + + /* check for NP container whether its last non-default node is not being unlinked */ + lyd_cont_set_dflt(lyd_parent(node)); + + node->parent = NULL; + } + + node->next = NULL; + node->prev = node; +} + +void +lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_dflt) +{ + struct lyd_meta *last, *iter; + + assert(parent); + + if (!meta) { + return; + } + + for (iter = meta; iter; iter = iter->next) { + iter->parent = parent; + } + + /* insert as the last attribute */ + if (parent->meta) { + for (last = parent->meta; last->next; last = last->next) {} + last->next = meta; + } else { + parent->meta = meta; + } + + /* remove default flags from NP containers */ + while (clear_dflt && parent && (parent->schema->nodetype == LYS_CONTAINER) && (parent->flags & LYD_DEFAULT)) { + parent->flags &= ~LYD_DEFAULT; + parent = lyd_parent(parent); + } +} + +LY_ERR +lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name, + size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, + void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, ly_bool clear_dflt, ly_bool *incomplete) +{ + LY_ERR ret = LY_SUCCESS; + struct lysc_ext_instance *ant = NULL; + const struct lysc_type *ant_type; + struct lyd_meta *mt, *last; + LY_ARRAY_COUNT_TYPE u; + + assert((parent || meta) && mod); + + LY_ARRAY_FOR(mod->compiled->exts, u) { + if (!strncmp(mod->compiled->exts[u].def->plugin->id, "ly2 metadata", 12) && + !ly_strncmp(mod->compiled->exts[u].argument, name, name_len)) { + /* we have the annotation definition */ + ant = &mod->compiled->exts[u]; + break; + } + } + if (!ant) { + /* attribute is not defined as a metadata annotation (RFC 7952) */ + LOGVAL(mod->ctx, LYVE_REFERENCE, "Annotation definition for attribute \"%s:%.*s\" not found.", + mod->name, (int)name_len, name); + ret = LY_EINVAL; + goto cleanup; + } + + mt = calloc(1, sizeof *mt); + LY_CHECK_ERR_GOTO(!mt, LOGMEM(mod->ctx); ret = LY_EMEM, cleanup); + mt->parent = parent; + mt->annotation = ant; + lyplg_ext_get_storage(ant, LY_STMT_TYPE, sizeof ant_type, (const void **)&ant_type); + ret = lyd_value_store(mod->ctx, &mt->value, ant_type, value, value_len, dynamic, format, prefix_data, hints, + ctx_node, incomplete); + LY_CHECK_ERR_GOTO(ret, free(mt), cleanup); + ret = lydict_insert(mod->ctx, name, name_len, &mt->name); + LY_CHECK_ERR_GOTO(ret, free(mt), cleanup); + + /* insert as the last attribute */ + if (parent) { + lyd_insert_meta(parent, mt, clear_dflt); + } else if (*meta) { + for (last = *meta; last->next; last = last->next) {} + last->next = mt; + } + + if (meta) { + *meta = mt; + } + +cleanup: + return ret; +} + +void +lyd_insert_attr(struct lyd_node *parent, struct lyd_attr *attr) +{ + struct lyd_attr *last, *iter; + struct lyd_node_opaq *opaq; + + assert(parent && !parent->schema); + + if (!attr) { + return; + } + + opaq = (struct lyd_node_opaq *)parent; + for (iter = attr; iter; iter = iter->next) { + iter->parent = opaq; + } + + /* insert as the last attribute */ + if (opaq->attr) { + for (last = opaq->attr; last->next; last = last->next) {} + last->next = attr; + } else { + opaq->attr = attr; + } +} + +LY_ERR +lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct ly_ctx *ctx, const char *name, size_t name_len, + const char *prefix, size_t prefix_len, const char *module_key, size_t module_key_len, const char *value, + size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *val_prefix_data, uint32_t hints) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_attr *at, *last; + + assert(ctx && (parent || attr) && (!parent || !parent->schema)); + assert(name && name_len && format); + + if (!value_len && (!dynamic || !*dynamic)) { + value = ""; + } + + at = calloc(1, sizeof *at); + LY_CHECK_ERR_RET(!at, LOGMEM(ctx); ly_free_prefix_data(format, val_prefix_data), LY_EMEM); + + LY_CHECK_GOTO(ret = lydict_insert(ctx, name, name_len, &at->name.name), finish); + if (prefix_len) { + LY_CHECK_GOTO(ret = lydict_insert(ctx, prefix, prefix_len, &at->name.prefix), finish); + } + if (module_key_len) { + LY_CHECK_GOTO(ret = lydict_insert(ctx, module_key, module_key_len, &at->name.module_ns), finish); + } + + if (dynamic && *dynamic) { + ret = lydict_insert_zc(ctx, (char *)value, &at->value); + LY_CHECK_GOTO(ret, finish); + *dynamic = 0; + } else { + LY_CHECK_GOTO(ret = lydict_insert(ctx, value, value_len, &at->value), finish); + } + at->format = format; + at->val_prefix_data = val_prefix_data; + at->hints = hints; + + /* insert as the last attribute */ + if (parent) { + lyd_insert_attr(parent, at); + } else if (*attr) { + for (last = *attr; last->next; last = last->next) {} + last->next = at; + } + +finish: + if (ret) { + lyd_free_attr_single(ctx, at); + } else if (attr) { + *attr = at; + } + return LY_SUCCESS; +} + +LIBYANG_API_DEF const struct lyd_node_term * +lyd_target(const struct ly_path *path, const struct lyd_node *tree) +{ + struct lyd_node *target; + + if (ly_path_eval(path, tree, &target)) { + return NULL; + } + + return (struct lyd_node_term *)target; +} + +/** + * @brief Check the equality of the two schemas from different contexts. + * + * @param schema1 of first node. + * @param schema2 of second node. + * @return 1 if the schemas are equal otherwise 0. + */ +static ly_bool +lyd_compare_schema_equal(const struct lysc_node *schema1, const struct lysc_node *schema2) +{ + if (!schema1 && !schema2) { + return 1; + } else if (!schema1 || !schema2) { + return 0; + } + + assert(schema1->module->ctx != schema2->module->ctx); + + if (schema1->nodetype != schema2->nodetype) { + return 0; + } + + if (strcmp(schema1->name, schema2->name)) { + return 0; + } + + if (strcmp(schema1->module->name, schema2->module->name)) { + return 0; + } + + if (schema1->module->revision || schema2->module->revision) { + if (!schema1->module->revision || !schema2->module->revision) { + return 0; + } + if (strcmp(schema1->module->revision, schema2->module->revision)) { + return 0; + } + } + + return 1; +} + +/** + * @brief Check the equality of the schemas for all parent nodes. + * + * Both nodes must be from different contexts. + * + * @param node1 Data of first node. + * @param node2 Data of second node. + * @return 1 if the all related parental schemas are equal otherwise 0. + */ +static ly_bool +lyd_compare_schema_parents_equal(const struct lyd_node *node1, const struct lyd_node *node2) +{ + const struct lysc_node *parent1, *parent2; + + assert(node1 && node2); + + for (parent1 = node1->schema->parent, parent2 = node2->schema->parent; + parent1 && parent2; + parent1 = parent1->parent, parent2 = parent2->parent) { + if (!lyd_compare_schema_equal(parent1, parent2)) { + return 0; + } + } + + if (parent1 || parent2) { + return 0; + } + + return 1; +} + +/** + * @brief Compare 2 nodes values including opaque node values. + * + * @param[in] node1 First node to compare. + * @param[in] node2 Second node to compare. + * @return LY_SUCCESS if equal. + * @return LY_ENOT if not equal. + * @return LY_ERR on error. + */ +static LY_ERR +lyd_compare_single_value(const struct lyd_node *node1, const struct lyd_node *node2) +{ + const struct lyd_node_opaq *opaq1 = NULL, *opaq2 = NULL; + const char *val1, *val2, *col; + const struct lys_module *mod; + char *val_dyn = NULL; + LY_ERR rc = LY_SUCCESS; + + if (!node1->schema) { + opaq1 = (struct lyd_node_opaq *)node1; + } + if (!node2->schema) { + opaq2 = (struct lyd_node_opaq *)node2; + } + + if (opaq1 && opaq2 && (opaq1->format == LY_VALUE_XML) && (opaq2->format == LY_VALUE_XML)) { + /* opaque XML and opaque XML node */ + if (lyxml_value_compare(LYD_CTX(node1), opaq1->value, opaq1->val_prefix_data, LYD_CTX(node2), opaq2->value, + opaq2->val_prefix_data)) { + return LY_ENOT; + } + return LY_SUCCESS; + } + + /* get their values */ + if (opaq1 && ((opaq1->format == LY_VALUE_XML) || (opaq1->format == LY_VALUE_STR_NS)) && (col = strchr(opaq1->value, ':'))) { + /* XML value with a prefix, try to transform it into a JSON (canonical) value */ + mod = ly_resolve_prefix(LYD_CTX(node1), opaq1->value, col - opaq1->value, opaq1->format, opaq1->val_prefix_data); + if (!mod) { + /* unable to compare */ + return LY_ENOT; + } + + if (asprintf(&val_dyn, "%s%s", mod->name, col) == -1) { + LOGMEM(LYD_CTX(node1)); + return LY_EMEM; + } + val1 = val_dyn; + } else { + val1 = lyd_get_value(node1); + } + if (opaq2 && ((opaq2->format == LY_VALUE_XML) || (opaq2->format == LY_VALUE_STR_NS)) && (col = strchr(opaq2->value, ':'))) { + mod = ly_resolve_prefix(LYD_CTX(node2), opaq2->value, col - opaq2->value, opaq2->format, opaq2->val_prefix_data); + if (!mod) { + return LY_ENOT; + } + + assert(!val_dyn); + if (asprintf(&val_dyn, "%s%s", mod->name, col) == -1) { + LOGMEM(LYD_CTX(node2)); + return LY_EMEM; + } + val2 = val_dyn; + } else { + val2 = lyd_get_value(node2); + } + + /* compare values */ + if (strcmp(val1, val2)) { + rc = LY_ENOT; + } + + free(val_dyn); + return rc; +} + +/** + * @brief Internal implementation of @ref lyd_compare_single. + * @copydoc lyd_compare_single + * @param[in] parental_schemas_checked Flag used for optimization. + * When this function is called for the first time, the flag must be set to 0. + * The @ref lyd_compare_schema_parents_equal should be called only once during + * recursive calls, and this is accomplished by setting to 1 in the lyd_compare_single_ body. + */ +static LY_ERR +lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options, + ly_bool parental_schemas_checked) +{ + const struct lyd_node *iter1, *iter2; + struct lyd_node_any *any1, *any2; + int len1, len2; + LY_ERR r; + + if (!node1 || !node2) { + if (node1 == node2) { + return LY_SUCCESS; + } else { + return LY_ENOT; + } + } + + if (LYD_CTX(node1) == LYD_CTX(node2)) { + /* same contexts */ + if (options & LYD_COMPARE_OPAQ) { + if (lyd_node_schema(node1) != lyd_node_schema(node2)) { + return LY_ENOT; + } + } else { + if (node1->schema != node2->schema) { + return LY_ENOT; + } + } + } else { + /* different contexts */ + if (!lyd_compare_schema_equal(node1->schema, node2->schema)) { + return LY_ENOT; + } + if (!parental_schemas_checked) { + if (!lyd_compare_schema_parents_equal(node1, node2)) { + return LY_ENOT; + } + parental_schemas_checked = 1; + } + } + + if (!(options & LYD_COMPARE_OPAQ) && (node1->hash != node2->hash)) { + return LY_ENOT; + } + /* equal hashes do not mean equal nodes, they can be just in collision so the nodes must be checked explicitly */ + + if (!node1->schema || !node2->schema) { + if (!(options & LYD_COMPARE_OPAQ) && ((node1->schema && !node2->schema) || (!node1->schema && node2->schema))) { + return LY_ENOT; + } + if ((r = lyd_compare_single_value(node1, node2))) { + return r; + } + + if (options & LYD_COMPARE_FULL_RECURSION) { + iter1 = lyd_child(node1); + iter2 = lyd_child(node2); + goto all_children_compare; + } + return LY_SUCCESS; + } else { + switch (node1->schema->nodetype) { + case LYS_LEAF: + case LYS_LEAFLIST: + if (options & LYD_COMPARE_DEFAULTS) { + if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) { + return LY_ENOT; + } + } + if ((r = lyd_compare_single_value(node1, node2))) { + return r; + } + + return LY_SUCCESS; + case LYS_CONTAINER: + case LYS_RPC: + case LYS_ACTION: + case LYS_NOTIF: + if (options & LYD_COMPARE_DEFAULTS) { + if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) { + return LY_ENOT; + } + } + if (options & LYD_COMPARE_FULL_RECURSION) { + iter1 = lyd_child(node1); + iter2 = lyd_child(node2); + goto all_children_compare; + } + return LY_SUCCESS; + case LYS_LIST: + iter1 = lyd_child(node1); + iter2 = lyd_child(node2); + + if (!(node1->schema->flags & LYS_KEYLESS) && !(options & LYD_COMPARE_FULL_RECURSION)) { + /* lists with keys, their equivalence is based on their keys */ + for (const struct lysc_node *key = lysc_node_child(node1->schema); + key && (key->flags & LYS_KEY); + key = key->next) { + if (lyd_compare_single_(iter1, iter2, options, parental_schemas_checked)) { + return LY_ENOT; + } + iter1 = iter1->next; + iter2 = iter2->next; + } + } else { + /* lists without keys, their equivalence is based on equivalence of all the children (both direct and indirect) */ + +all_children_compare: + if (!iter1 && !iter2) { + /* no children, nothing to compare */ + return LY_SUCCESS; + } + + for ( ; iter1 && iter2; iter1 = iter1->next, iter2 = iter2->next) { + if (lyd_compare_single_(iter1, iter2, options | LYD_COMPARE_FULL_RECURSION, parental_schemas_checked)) { + return LY_ENOT; + } + } + if (iter1 || iter2) { + return LY_ENOT; + } + } + return LY_SUCCESS; + case LYS_ANYXML: + case LYS_ANYDATA: + any1 = (struct lyd_node_any *)node1; + any2 = (struct lyd_node_any *)node2; + + if (any1->value_type != any2->value_type) { + return LY_ENOT; + } + switch (any1->value_type) { + case LYD_ANYDATA_DATATREE: + iter1 = any1->value.tree; + iter2 = any2->value.tree; + goto all_children_compare; + case LYD_ANYDATA_STRING: + case LYD_ANYDATA_XML: + case LYD_ANYDATA_JSON: + if ((!any1->value.str && any2->value.str) || (any1->value.str && !any2->value.str)) { + return LY_ENOT; + } else if (!any1->value.str && !any2->value.str) { + return LY_SUCCESS; + } + len1 = strlen(any1->value.str); + len2 = strlen(any2->value.str); + if ((len1 != len2) || strcmp(any1->value.str, any2->value.str)) { + return LY_ENOT; + } + return LY_SUCCESS; + case LYD_ANYDATA_LYB: + len1 = lyd_lyb_data_length(any1->value.mem); + len2 = lyd_lyb_data_length(any2->value.mem); + if ((len1 == -1) || (len2 == -1) || (len1 != len2) || memcmp(any1->value.mem, any2->value.mem, len1)) { + return LY_ENOT; + } + return LY_SUCCESS; + } + } + } + + LOGINT(LYD_CTX(node1)); + return LY_EINT; +} + +LIBYANG_API_DEF LY_ERR +lyd_compare_single(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options) +{ + return lyd_compare_single_(node1, node2, options, 0); +} + +LIBYANG_API_DEF LY_ERR +lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options) +{ + for ( ; node1 && node2; node1 = node1->next, node2 = node2->next) { + LY_CHECK_RET(lyd_compare_single(node1, node2, options)); + } + + if (node1 == node2) { + return LY_SUCCESS; + } + return LY_ENOT; +} + +LIBYANG_API_DEF LY_ERR +lyd_compare_meta(const struct lyd_meta *meta1, const struct lyd_meta *meta2) +{ + if (!meta1 || !meta2) { + if (meta1 == meta2) { + return LY_SUCCESS; + } else { + return LY_ENOT; + } + } + + if ((meta1->annotation->module->ctx != meta2->annotation->module->ctx) || (meta1->annotation != meta2->annotation)) { + return LY_ENOT; + } + + return meta1->value.realtype->plugin->compare(&meta1->value, &meta2->value); +} + +/** + * @brief Create a copy of the attribute. + * + * @param[in] attr Attribute to copy. + * @param[in] node Opaque where to append the new attribute. + * @param[out] dup Optional created attribute copy. + * @return LY_ERR value. + */ +static LY_ERR +lyd_dup_attr_single(const struct lyd_attr *attr, struct lyd_node *node, struct lyd_attr **dup) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_attr *a, *last; + struct lyd_node_opaq *opaq = (struct lyd_node_opaq *)node; + + LY_CHECK_ARG_RET(NULL, attr, node, !node->schema, LY_EINVAL); + + /* create a copy */ + a = calloc(1, sizeof *attr); + LY_CHECK_ERR_RET(!a, LOGMEM(LYD_CTX(node)), LY_EMEM); + + LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->name.name, 0, &a->name.name), finish); + LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->name.prefix, 0, &a->name.prefix), finish); + LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->name.module_ns, 0, &a->name.module_ns), finish); + LY_CHECK_GOTO(ret = lydict_insert(LYD_CTX(node), attr->value, 0, &a->value), finish); + a->hints = attr->hints; + a->format = attr->format; + if (attr->val_prefix_data) { + ret = ly_dup_prefix_data(LYD_CTX(node), attr->format, attr->val_prefix_data, &a->val_prefix_data); + LY_CHECK_GOTO(ret, finish); + } + + /* insert as the last attribute */ + a->parent = opaq; + if (opaq->attr) { + for (last = opaq->attr; last->next; last = last->next) {} + last->next = a; + } else { + opaq->attr = a; + } + +finish: + if (ret) { + lyd_free_attr_single(LYD_CTX(node), a); + } else if (dup) { + *dup = a; + } + return LY_SUCCESS; +} + +/** + * @brief Find @p schema equivalent in @p trg_ctx. + * + * @param[in] schema Schema node to find. + * @param[in] trg_ctx Target context to search in. + * @param[in] parent Data parent of @p schema, if any. + * @param[in] log Whether to log directly. + * @param[out] trg_schema Found schema from @p trg_ctx to use. + * @return LY_RRR value. + */ +static LY_ERR +lyd_find_schema_ctx(const struct lysc_node *schema, const struct ly_ctx *trg_ctx, const struct lyd_node *parent, + ly_bool log, const struct lysc_node **trg_schema) +{ + const struct lysc_node *src_parent = NULL, *trg_parent = NULL, *sp, *tp; + const struct lys_module *trg_mod = NULL; + char *path; + + if (!schema) { + /* opaque node */ + *trg_schema = NULL; + return LY_SUCCESS; + } + + if (lysc_data_parent(schema) && parent && parent->schema) { + /* start from schema parent */ + trg_parent = parent->schema; + src_parent = lysc_data_parent(schema); + } + + do { + /* find the next parent */ + sp = schema; + while (lysc_data_parent(sp) != src_parent) { + sp = lysc_data_parent(sp); + } + src_parent = sp; + + if (!src_parent->parent) { + /* find the module first */ + trg_mod = ly_ctx_get_module_implemented(trg_ctx, src_parent->module->name); + if (!trg_mod) { + if (log) { + LOGERR(trg_ctx, LY_ENOTFOUND, "Module \"%s\" not present/implemented in the target context.", + src_parent->module->name); + } + return LY_ENOTFOUND; + } + } + + /* find the next parent */ + assert(trg_parent || trg_mod); + tp = NULL; + while ((tp = lys_getnext(tp, trg_parent, trg_mod ? trg_mod->compiled : NULL, 0))) { + if (!strcmp(tp->name, src_parent->name) && !strcmp(tp->module->name, src_parent->module->name)) { + break; + } + } + if (!tp) { + /* schema node not found */ + if (log) { + path = lysc_path(src_parent, LYSC_PATH_LOG, NULL, 0); + LOGERR(trg_ctx, LY_ENOTFOUND, "Schema node \"%s\" not found in the target context.", path); + free(path); + } + return LY_ENOTFOUND; + } + + trg_parent = tp; + } while (schema != src_parent); + + /* success */ + *trg_schema = trg_parent; + return LY_SUCCESS; +} + +/** + * @brief Duplicate a single node and connect it into @p parent (if present) or last of @p first siblings. + * + * Ignores ::LYD_DUP_WITH_PARENTS and ::LYD_DUP_WITH_SIBLINGS which are supposed to be handled by lyd_dup(). + * + * @param[in] node Node to duplicate. + * @param[in] trg_ctx Target context for duplicated nodes. + * @param[in] parent Parent to insert into, NULL for top-level sibling. + * @param[in] insert_last Whether the duplicated node can be inserted as the last child of @p parent. Set for + * recursive duplication as an optimization. + * @param[in,out] first First sibling, NULL if no top-level sibling exist yet. Can be also NULL if @p parent is set. + * @param[in] options Bitmask of options flags, see @ref dupoptions. + * @param[out] dup_p Pointer where the created duplicated node is placed (besides connecting it to @p parent / @p first). + * @return LY_ERR value. + */ +static LY_ERR +lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, ly_bool insert_last, + struct lyd_node **first, uint32_t options, struct lyd_node **dup_p) +{ + LY_ERR ret; + struct lyd_node *dup = NULL; + struct lyd_meta *meta; + struct lyd_attr *attr; + struct lyd_node_any *any; + const struct lysc_type *type; + const char *val_can; + + LY_CHECK_ARG_RET(NULL, node, LY_EINVAL); + + if (node->flags & LYD_EXT) { + if (options & LYD_DUP_NO_EXT) { + /* no not duplicate this subtree */ + return LY_SUCCESS; + } + + /* we need to use the same context */ + trg_ctx = LYD_CTX(node); + } + + if (!node->schema) { + dup = calloc(1, sizeof(struct lyd_node_opaq)); + ((struct lyd_node_opaq *)dup)->ctx = trg_ctx; + } else { + switch (node->schema->nodetype) { + case LYS_RPC: + case LYS_ACTION: + case LYS_NOTIF: + case LYS_CONTAINER: + case LYS_LIST: + dup = calloc(1, sizeof(struct lyd_node_inner)); + break; + case LYS_LEAF: + case LYS_LEAFLIST: + dup = calloc(1, sizeof(struct lyd_node_term)); + break; + case LYS_ANYDATA: + case LYS_ANYXML: + dup = calloc(1, sizeof(struct lyd_node_any)); + break; + default: + LOGINT(trg_ctx); + ret = LY_EINT; + goto error; + } + } + LY_CHECK_ERR_GOTO(!dup, LOGMEM(trg_ctx); ret = LY_EMEM, error); + + if (options & LYD_DUP_WITH_FLAGS) { + dup->flags = node->flags; + } else { + dup->flags = (node->flags & (LYD_DEFAULT | LYD_EXT)) | LYD_NEW; + } + if (trg_ctx == LYD_CTX(node)) { + dup->schema = node->schema; + } else { + ret = lyd_find_schema_ctx(node->schema, trg_ctx, parent, 1, &dup->schema); + if (ret) { + /* has no schema but is not an opaque node */ + free(dup); + dup = NULL; + goto error; + } + } + dup->prev = dup; + + /* duplicate metadata/attributes */ + if (!(options & LYD_DUP_NO_META)) { + if (!node->schema) { + LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, attr) { + LY_CHECK_GOTO(ret = lyd_dup_attr_single(attr, dup, NULL), error); + } + } else { + LY_LIST_FOR(node->meta, meta) { + LY_CHECK_GOTO(ret = lyd_dup_meta_single(meta, dup, NULL), error); + } + } + } + + /* nodetype-specific work */ + if (!dup->schema) { + struct lyd_node_opaq *opaq = (struct lyd_node_opaq *)dup; + struct lyd_node_opaq *orig = (struct lyd_node_opaq *)node; + struct lyd_node *child; + + if (options & LYD_DUP_RECURSIVE) { + /* duplicate all the children */ + LY_LIST_FOR(orig->child, child) { + LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, 1, NULL, options, NULL), error); + } + } + LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->name.name, 0, &opaq->name.name), error); + LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->name.prefix, 0, &opaq->name.prefix), error); + LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->name.module_ns, 0, &opaq->name.module_ns), error); + LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->value, 0, &opaq->value), error); + opaq->hints = orig->hints; + opaq->format = orig->format; + if (orig->val_prefix_data) { + ret = ly_dup_prefix_data(trg_ctx, opaq->format, orig->val_prefix_data, &opaq->val_prefix_data); + LY_CHECK_GOTO(ret, error); + } + } else if (dup->schema->nodetype & LYD_NODE_TERM) { + struct lyd_node_term *term = (struct lyd_node_term *)dup; + struct lyd_node_term *orig = (struct lyd_node_term *)node; + + term->hash = orig->hash; + if (trg_ctx == LYD_CTX(node)) { + ret = orig->value.realtype->plugin->duplicate(trg_ctx, &orig->value, &term->value); + LY_CHECK_ERR_GOTO(ret, LOGERR(trg_ctx, ret, "Value duplication failed."), error); + } else { + /* store canonical value in the target context */ + val_can = lyd_get_value(node); + type = ((struct lysc_node_leaf *)term->schema)->type; + ret = lyd_value_store(trg_ctx, &term->value, type, val_can, strlen(val_can), NULL, LY_VALUE_CANON, NULL, + LYD_HINT_DATA, term->schema, NULL); + LY_CHECK_GOTO(ret, error); + } + } else if (dup->schema->nodetype & LYD_NODE_INNER) { + struct lyd_node_inner *orig = (struct lyd_node_inner *)node; + struct lyd_node *child; + + if (options & LYD_DUP_RECURSIVE) { + /* duplicate all the children */ + LY_LIST_FOR(orig->child, child) { + LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, 1, NULL, options, NULL), error); + } + } else if ((dup->schema->nodetype == LYS_LIST) && !(dup->schema->flags & LYS_KEYLESS)) { + /* always duplicate keys of a list */ + for (child = orig->child; child && lysc_is_key(child->schema); child = child->next) { + LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, 1, NULL, options, NULL), error); + } + } + lyd_hash(dup); + } else if (dup->schema->nodetype & LYD_NODE_ANY) { + dup->hash = node->hash; + any = (struct lyd_node_any *)node; + LY_CHECK_GOTO(ret = lyd_any_copy_value(dup, &any->value, any->value_type), error); + } + + /* insert */ + lyd_insert_node(parent, first, dup, insert_last); + + if (dup_p) { + *dup_p = dup; + } + return LY_SUCCESS; + +error: + lyd_free_tree(dup); + return ret; +} + +/** + * @brief Get a parent node to connect duplicated subtree to. + * + * @param[in] node Node (subtree) to duplicate. + * @param[in] trg_ctx Target context for duplicated nodes. + * @param[in] parent Initial parent to connect to. + * @param[in] options Bitmask of options flags, see @ref dupoptions. + * @param[out] dup_parent First duplicated parent node, if any. + * @param[out] local_parent Correct parent to directly connect duplicated @p node to. + * @return LY_ERR value. + */ +static LY_ERR +lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_ctx, const struct lyd_node_inner *parent, + uint32_t options, struct lyd_node **dup_parent, struct lyd_node_inner **local_parent) +{ + const struct lyd_node_inner *orig_parent, *iter; + ly_bool repeat = 1, ext_parent = 0; + + *dup_parent = NULL; + *local_parent = NULL; + + if (node->flags & LYD_EXT) { + ext_parent = 1; + } + for (orig_parent = node->parent; repeat && orig_parent; orig_parent = orig_parent->parent) { + if (ext_parent) { + /* use the standard context */ + trg_ctx = LYD_CTX(orig_parent); + } + if (parent && (parent->schema == orig_parent->schema)) { + /* stop creating parents, connect what we have into the provided parent */ + iter = parent; + repeat = 0; + } else { + iter = NULL; + LY_CHECK_RET(lyd_dup_r((struct lyd_node *)orig_parent, trg_ctx, NULL, 0, (struct lyd_node **)&iter, options, + (struct lyd_node **)&iter)); + } + if (!*local_parent) { + *local_parent = (struct lyd_node_inner *)iter; + } + if (iter->child) { + /* 1) list - add after keys + * 2) provided parent with some children */ + iter->child->prev->next = *dup_parent; + if (*dup_parent) { + (*dup_parent)->prev = iter->child->prev; + iter->child->prev = *dup_parent; + } + } else { + ((struct lyd_node_inner *)iter)->child = *dup_parent; + } + if (*dup_parent) { + (*dup_parent)->parent = (struct lyd_node_inner *)iter; + } + *dup_parent = (struct lyd_node *)iter; + if (orig_parent->flags & LYD_EXT) { + ext_parent = 1; + } + } + + if (repeat && parent) { + /* given parent and created parents chain actually do not interconnect */ + LOGERR(trg_ctx, LY_EINVAL, + "Invalid argument parent (%s()) - does not interconnect with the created node's parents chain.", __func__); + return LY_EINVAL; + } + + return LY_SUCCESS; +} + +static LY_ERR +lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent, uint32_t options, + ly_bool nosiblings, struct lyd_node **dup) +{ + LY_ERR rc; + const struct lyd_node *orig; /* original node to be duplicated */ + struct lyd_node *first = NULL; /* the first duplicated node, this is returned */ + struct lyd_node *top = NULL; /* the most higher created node */ + struct lyd_node_inner *local_parent = NULL; /* the direct parent node for the duplicated node(s) */ + + assert(node && trg_ctx); + + if (options & LYD_DUP_WITH_PARENTS) { + LY_CHECK_GOTO(rc = lyd_dup_get_local_parent(node, trg_ctx, parent, options & (LYD_DUP_WITH_FLAGS | LYD_DUP_NO_META), + &top, &local_parent), error); + } else { + local_parent = parent; + } + + LY_LIST_FOR(node, orig) { + if (lysc_is_key(orig->schema)) { + if (local_parent) { + /* the key must already exist in the parent */ + rc = lyd_find_sibling_schema(local_parent->child, orig->schema, first ? NULL : &first); + LY_CHECK_ERR_GOTO(rc, LOGINT(trg_ctx), error); + } else { + assert(!(options & LYD_DUP_WITH_PARENTS)); + /* duplicating a single key, okay, I suppose... */ + rc = lyd_dup_r(orig, trg_ctx, NULL, 0, &first, options, first ? NULL : &first); + LY_CHECK_GOTO(rc, error); + } + } else { + /* if there is no local parent, it will be inserted into first */ + rc = lyd_dup_r(orig, trg_ctx, local_parent ? &local_parent->node : NULL, 0, &first, options, + first ? NULL : &first); + LY_CHECK_GOTO(rc, error); + } + if (nosiblings) { + break; + } + } + + if (dup) { + *dup = first; + } + return LY_SUCCESS; + +error: + if (top) { + lyd_free_tree(top); + } else { + lyd_free_siblings(first); + } + return rc; +} + +/** + * @brief Check the context of node and parent when duplicating nodes. + * + * @param[in] node Node to duplicate. + * @param[in] parent Parent of the duplicated node(s). + * @return LY_ERR value. + */ +static LY_ERR +lyd_dup_ctx_check(const struct lyd_node *node, const struct lyd_node_inner *parent) +{ + const struct lyd_node *iter; + + if (!node || !parent) { + return LY_SUCCESS; + } + + if ((LYD_CTX(node) != LYD_CTX(parent))) { + /* try to find top-level ext data parent */ + for (iter = node; iter && !(iter->flags & LYD_EXT); iter = lyd_parent(iter)) {} + + if (!iter || !lyd_parent(iter) || (LYD_CTX(lyd_parent(iter)) != LYD_CTX(parent))) { + LOGERR(NULL, LY_EINVAL, "Different contexts used in node duplication."); + return LY_EINVAL; + } + } + + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyd_dup_single(const struct lyd_node *node, struct lyd_node_inner *parent, uint32_t options, struct lyd_node **dup) +{ + LY_CHECK_ARG_RET(NULL, node, LY_EINVAL); + LY_CHECK_RET(lyd_dup_ctx_check(node, parent)); + + return lyd_dup(node, LYD_CTX(node), parent, options, 1, dup); +} + +LIBYANG_API_DEF LY_ERR +lyd_dup_single_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent, + uint32_t options, struct lyd_node **dup) +{ + LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL); + + return lyd_dup(node, trg_ctx, parent, options, 1, dup); +} + +LIBYANG_API_DEF LY_ERR +lyd_dup_siblings(const struct lyd_node *node, struct lyd_node_inner *parent, uint32_t options, struct lyd_node **dup) +{ + LY_CHECK_ARG_RET(NULL, node, LY_EINVAL); + LY_CHECK_RET(lyd_dup_ctx_check(node, parent)); + + return lyd_dup(node, LYD_CTX(node), parent, options, 0, dup); +} + +LIBYANG_API_DEF LY_ERR +lyd_dup_siblings_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent, + uint32_t options, struct lyd_node **dup) +{ + LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL); + + return lyd_dup(node, trg_ctx, parent, options, 0, dup); +} + +LIBYANG_API_DEF LY_ERR +lyd_dup_meta_single(const struct lyd_meta *meta, struct lyd_node *node, struct lyd_meta **dup) +{ + LY_ERR ret = LY_SUCCESS; + const struct ly_ctx *ctx; + struct lyd_meta *mt, *last; + + LY_CHECK_ARG_RET(NULL, meta, node, LY_EINVAL); + + /* log to node context but value must always use the annotation context */ + ctx = meta->annotation->module->ctx; + + /* create a copy */ + mt = calloc(1, sizeof *mt); + LY_CHECK_ERR_RET(!mt, LOGMEM(LYD_CTX(node)), LY_EMEM); + mt->annotation = meta->annotation; + ret = meta->value.realtype->plugin->duplicate(ctx, &meta->value, &mt->value); + LY_CHECK_ERR_GOTO(ret, LOGERR(LYD_CTX(node), LY_EINT, "Value duplication failed."), finish); + LY_CHECK_GOTO(ret = lydict_insert(ctx, meta->name, 0, &mt->name), finish); + + /* insert as the last attribute */ + mt->parent = node; + if (node->meta) { + for (last = node->meta; last->next; last = last->next) {} + last->next = mt; + } else { + node->meta = mt; + } + +finish: + if (ret) { + lyd_free_meta_single(mt); + } else if (dup) { + *dup = mt; + } + return LY_SUCCESS; +} + +/** + * @brief Merge a source sibling into target siblings. + * + * @param[in,out] first_trg First target sibling, is updated if top-level. + * @param[in] parent_trg Target parent. + * @param[in,out] sibling_src Source sibling to merge, set to NULL if spent. + * @param[in] merge_cb Optional merge callback. + * @param[in] cb_data Arbitrary callback data. + * @param[in] options Merge options. + * @param[in,out] dup_inst Duplicate instance cache for all @p first_trg siblings. + * @return LY_ERR value. + */ +static LY_ERR +lyd_merge_sibling_r(struct lyd_node **first_trg, struct lyd_node *parent_trg, const struct lyd_node **sibling_src_p, + lyd_merge_cb merge_cb, void *cb_data, uint16_t options, struct lyd_dup_inst **dup_inst) +{ + const struct lyd_node *child_src, *tmp, *sibling_src; + struct lyd_node *match_trg, *dup_src, *elem; + struct lyd_node_opaq *opaq_trg, *opaq_src; + struct lysc_type *type; + struct lyd_dup_inst *child_dup_inst = NULL; + LY_ERR ret; + ly_bool first_inst = 0; + + sibling_src = *sibling_src_p; + if (!sibling_src->schema) { + /* try to find the same opaque node */ + lyd_find_sibling_opaq_next(*first_trg, LYD_NAME(sibling_src), &match_trg); + } else if (sibling_src->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) { + /* try to find the exact instance */ + lyd_find_sibling_first(*first_trg, sibling_src, &match_trg); + } else { + /* try to simply find the node, there cannot be more instances */ + lyd_find_sibling_val(*first_trg, sibling_src->schema, NULL, 0, &match_trg); + } + + if (match_trg) { + /* update match as needed */ + LY_CHECK_RET(lyd_dup_inst_next(&match_trg, *first_trg, dup_inst)); + } else { + /* first instance of this node */ + first_inst = 1; + } + + if (match_trg) { + /* call callback */ + if (merge_cb) { + LY_CHECK_RET(merge_cb(match_trg, sibling_src, cb_data)); + } + + /* node found, make sure even value matches for all node types */ + if (!match_trg->schema) { + if (lyd_compare_single(sibling_src, match_trg, 0)) { + /* update value */ + opaq_trg = (struct lyd_node_opaq *)match_trg; + opaq_src = (struct lyd_node_opaq *)sibling_src; + + lydict_remove(LYD_CTX(opaq_trg), opaq_trg->value); + lydict_insert(LYD_CTX(opaq_trg), opaq_src->value, 0, &opaq_trg->value); + opaq_trg->hints = opaq_src->hints; + + ly_free_prefix_data(opaq_trg->format, opaq_trg->val_prefix_data); + opaq_trg->format = opaq_src->format; + ly_dup_prefix_data(LYD_CTX(opaq_trg), opaq_src->format, opaq_src->val_prefix_data, + &opaq_trg->val_prefix_data); + } + } else if ((match_trg->schema->nodetype == LYS_LEAF) && + lyd_compare_single(sibling_src, match_trg, LYD_COMPARE_DEFAULTS)) { + /* since they are different, they cannot both be default */ + assert(!(sibling_src->flags & LYD_DEFAULT) || !(match_trg->flags & LYD_DEFAULT)); + + /* update value (or only LYD_DEFAULT flag) only if flag set or the source node is not default */ + if ((options & LYD_MERGE_DEFAULTS) || !(sibling_src->flags & LYD_DEFAULT)) { + type = ((struct lysc_node_leaf *)match_trg->schema)->type; + type->plugin->free(LYD_CTX(match_trg), &((struct lyd_node_term *)match_trg)->value); + LY_CHECK_RET(type->plugin->duplicate(LYD_CTX(match_trg), &((struct lyd_node_term *)sibling_src)->value, + &((struct lyd_node_term *)match_trg)->value)); + + /* copy flags and add LYD_NEW */ + match_trg->flags = sibling_src->flags | ((options & LYD_MERGE_WITH_FLAGS) ? 0 : LYD_NEW); + } + } else if ((match_trg->schema->nodetype & LYS_ANYDATA) && lyd_compare_single(sibling_src, match_trg, 0)) { + /* update value */ + LY_CHECK_RET(lyd_any_copy_value(match_trg, &((struct lyd_node_any *)sibling_src)->value, + ((struct lyd_node_any *)sibling_src)->value_type)); + + /* copy flags and add LYD_NEW */ + match_trg->flags = sibling_src->flags | ((options & LYD_MERGE_WITH_FLAGS) ? 0 : LYD_NEW); + } + + /* check descendants, recursively */ + ret = LY_SUCCESS; + LY_LIST_FOR_SAFE(lyd_child_no_keys(sibling_src), tmp, child_src) { + ret = lyd_merge_sibling_r(lyd_node_child_p(match_trg), match_trg, &child_src, merge_cb, cb_data, options, + &child_dup_inst); + if (ret) { + break; + } + } + lyd_dup_inst_free(child_dup_inst); + LY_CHECK_RET(ret); + } else { + /* node not found, merge it */ + if (options & LYD_MERGE_DESTRUCT) { + dup_src = (struct lyd_node *)sibling_src; + lyd_unlink_tree(dup_src); + /* spend it */ + *sibling_src_p = NULL; + } else { + LY_CHECK_RET(lyd_dup_single(sibling_src, NULL, LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &dup_src)); + } + + if (!(options & LYD_MERGE_WITH_FLAGS)) { + /* set LYD_NEW for all the new nodes, required for validation */ + LYD_TREE_DFS_BEGIN(dup_src, elem) { + elem->flags |= LYD_NEW; + LYD_TREE_DFS_END(dup_src, elem); + } + } + + /* insert */ + lyd_insert_node(parent_trg, first_trg, dup_src, 0); + + if (first_inst) { + /* remember not to find this instance next time */ + LY_CHECK_RET(lyd_dup_inst_next(&dup_src, *first_trg, dup_inst)); + } + + /* call callback, no source node */ + if (merge_cb) { + LY_CHECK_RET(merge_cb(dup_src, NULL, cb_data)); + } + } + + return LY_SUCCESS; +} + +static LY_ERR +lyd_merge(struct lyd_node **target, const struct lyd_node *source, const struct lys_module *mod, + lyd_merge_cb merge_cb, void *cb_data, uint16_t options, ly_bool nosiblings) +{ + const struct lyd_node *sibling_src, *tmp; + struct lyd_dup_inst *dup_inst = NULL; + ly_bool first; + LY_ERR ret = LY_SUCCESS; + + LY_CHECK_ARG_RET(NULL, target, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(*target ? LYD_CTX(*target) : NULL, source ? LYD_CTX(source) : NULL, mod ? mod->ctx : NULL, + LY_EINVAL); + + if (!source) { + /* nothing to merge */ + return LY_SUCCESS; + } + + if ((*target && lysc_data_parent((*target)->schema)) || lysc_data_parent(source->schema)) { + LOGERR(LYD_CTX(source), LY_EINVAL, "Invalid arguments - can merge only 2 top-level subtrees (%s()).", __func__); + return LY_EINVAL; + } + + LY_LIST_FOR_SAFE(source, tmp, sibling_src) { + if (mod && (lyd_owner_module(sibling_src) != mod)) { + /* skip data nodes from different modules */ + continue; + } + + first = (sibling_src == source) ? 1 : 0; + ret = lyd_merge_sibling_r(target, NULL, &sibling_src, merge_cb, cb_data, options, &dup_inst); + if (ret) { + break; + } + if (first && !sibling_src) { + /* source was spent (unlinked), move to the next node */ + source = tmp; + } + + if (nosiblings) { + break; + } + } + + if (options & LYD_MERGE_DESTRUCT) { + /* free any leftover source data that were not merged */ + lyd_free_siblings((struct lyd_node *)source); + } + + lyd_dup_inst_free(dup_inst); + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyd_merge_tree(struct lyd_node **target, const struct lyd_node *source, uint16_t options) +{ + return lyd_merge(target, source, NULL, NULL, NULL, options, 1); +} + +LIBYANG_API_DEF LY_ERR +lyd_merge_siblings(struct lyd_node **target, const struct lyd_node *source, uint16_t options) +{ + return lyd_merge(target, source, NULL, NULL, NULL, options, 0); +} + +LIBYANG_API_DEF LY_ERR +lyd_merge_module(struct lyd_node **target, const struct lyd_node *source, const struct lys_module *mod, + lyd_merge_cb merge_cb, void *cb_data, uint16_t options) +{ + return lyd_merge(target, source, mod, merge_cb, cb_data, options, 0); +} + +static LY_ERR +lyd_path_str_enlarge(char **buffer, size_t *buflen, size_t reqlen, ly_bool is_static) +{ + /* ending \0 */ + ++reqlen; + + if (reqlen > *buflen) { + if (is_static) { + return LY_EINCOMPLETE; + } + + *buffer = ly_realloc(*buffer, reqlen * sizeof **buffer); + if (!*buffer) { + return LY_EMEM; + } + + *buflen = reqlen; + } + + return LY_SUCCESS; +} + +LY_ERR +lyd_path_list_predicate(const struct lyd_node *node, char **buffer, size_t *buflen, size_t *bufused, ly_bool is_static) +{ + const struct lyd_node *key; + size_t len; + const char *val; + char quot; + + for (key = lyd_child(node); key && key->schema && (key->schema->flags & LYS_KEY); key = key->next) { + val = lyd_get_value(key); + len = 1 + strlen(key->schema->name) + 2 + strlen(val) + 2; + LY_CHECK_RET(lyd_path_str_enlarge(buffer, buflen, *bufused + len, is_static)); + + quot = '\''; + if (strchr(val, '\'')) { + quot = '"'; + } + *bufused += sprintf(*buffer + *bufused, "[%s=%c%s%c]", key->schema->name, quot, val, quot); + } + + return LY_SUCCESS; +} + +/** + * @brief Append leaf-list value predicate to path. + * + * @param[in] node Node to print. + * @param[in,out] buffer Buffer to print to. + * @param[in,out] buflen Current buffer length. + * @param[in,out] bufused Current number of characters used in @p buffer. + * @param[in] is_static Whether buffer is static or can be reallocated. + * @return LY_ERR + */ +static LY_ERR +lyd_path_leaflist_predicate(const struct lyd_node *node, char **buffer, size_t *buflen, size_t *bufused, ly_bool is_static) +{ + size_t len; + const char *val; + char quot; + + val = lyd_get_value(node); + len = 4 + strlen(val) + 2; /* "[.='" + val + "']" */ + LY_CHECK_RET(lyd_path_str_enlarge(buffer, buflen, *bufused + len, is_static)); + + quot = '\''; + if (strchr(val, '\'')) { + quot = '"'; + } + *bufused += sprintf(*buffer + *bufused, "[.=%c%s%c]", quot, val, quot); + + return LY_SUCCESS; +} + +/** + * @brief Append node position (relative to its other instances) predicate to path. + * + * @param[in] node Node to print. + * @param[in,out] buffer Buffer to print to. + * @param[in,out] buflen Current buffer length. + * @param[in,out] bufused Current number of characters used in @p buffer. + * @param[in] is_static Whether buffer is static or can be reallocated. + * @return LY_ERR + */ +static LY_ERR +lyd_path_position_predicate(const struct lyd_node *node, char **buffer, size_t *buflen, size_t *bufused, ly_bool is_static) +{ + size_t len; + uint32_t pos; + char *val = NULL; + LY_ERR rc; + + pos = lyd_list_pos(node); + if (asprintf(&val, "%" PRIu32, pos) == -1) { + return LY_EMEM; + } + + len = 1 + strlen(val) + 1; + rc = lyd_path_str_enlarge(buffer, buflen, *bufused + len, is_static); + if (rc != LY_SUCCESS) { + goto cleanup; + } + + *bufused += sprintf(*buffer + *bufused, "[%s]", val); + +cleanup: + free(val); + return rc; +} + +LIBYANG_API_DEF char * +lyd_path(const struct lyd_node *node, LYD_PATH_TYPE pathtype, char *buffer, size_t buflen) +{ + ly_bool is_static = 0; + uint32_t i, depth; + size_t bufused = 0, len; + const struct lyd_node *iter, *parent; + const struct lys_module *mod, *prev_mod; + LY_ERR rc = LY_SUCCESS; + + LY_CHECK_ARG_RET(NULL, node, NULL); + if (buffer) { + LY_CHECK_ARG_RET(LYD_CTX(node), buflen > 1, NULL); + is_static = 1; + } else { + buflen = 0; + } + + switch (pathtype) { + case LYD_PATH_STD: + case LYD_PATH_STD_NO_LAST_PRED: + depth = 1; + for (iter = node; iter->parent; iter = lyd_parent(iter)) { + ++depth; + } + + goto iter_print; + while (depth) { + /* find the right node */ + for (iter = node, i = 1; i < depth; iter = lyd_parent(iter), ++i) {} +iter_print: + /* get the module */ + mod = iter->schema ? iter->schema->module : lyd_owner_module(iter); + parent = lyd_parent(iter); + prev_mod = (parent && parent->schema) ? parent->schema->module : lyd_owner_module(parent); + if (prev_mod == mod) { + mod = NULL; + } + + /* realloc string */ + len = 1 + (mod ? strlen(mod->name) + 1 : 0) + (iter->schema ? strlen(iter->schema->name) : + strlen(((struct lyd_node_opaq *)iter)->name.name)); + rc = lyd_path_str_enlarge(&buffer, &buflen, bufused + len, is_static); + if (rc != LY_SUCCESS) { + break; + } + + /* print next node */ + bufused += sprintf(buffer + bufused, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", LYD_NAME(iter)); + + /* do not always print the last (first) predicate */ + if (iter->schema && ((depth > 1) || (pathtype == LYD_PATH_STD))) { + switch (iter->schema->nodetype) { + case LYS_LIST: + if (iter->schema->flags & LYS_KEYLESS) { + /* print its position */ + rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, is_static); + } else { + /* print all list keys in predicates */ + rc = lyd_path_list_predicate(iter, &buffer, &buflen, &bufused, is_static); + } + break; + case LYS_LEAFLIST: + if (iter->schema->flags & LYS_CONFIG_W) { + /* print leaf-list value */ + rc = lyd_path_leaflist_predicate(iter, &buffer, &buflen, &bufused, is_static); + } else { + /* print its position */ + rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, is_static); + } + break; + default: + /* nothing to print more */ + break; + } + } + if (rc != LY_SUCCESS) { + break; + } + + --depth; + } + break; + } + + return buffer; +} + +char * +lyd_path_set(const struct ly_set *dnodes, LYD_PATH_TYPE pathtype) +{ + uint32_t depth; + size_t bufused = 0, buflen = 0, len; + char *buffer = NULL; + const struct lyd_node *iter, *parent; + const struct lys_module *mod, *prev_mod; + LY_ERR rc = LY_SUCCESS; + + switch (pathtype) { + case LYD_PATH_STD: + case LYD_PATH_STD_NO_LAST_PRED: + for (depth = 1; depth <= dnodes->count; ++depth) { + /* current node */ + iter = dnodes->dnodes[depth - 1]; + mod = iter->schema ? iter->schema->module : lyd_owner_module(iter); + + /* parent */ + parent = (depth > 1) ? dnodes->dnodes[depth - 2] : NULL; + assert(!parent || !iter->schema || !parent->schema || (lysc_data_parent(iter->schema) == parent->schema) || + (!lysc_data_parent(iter->schema) && (LYD_CTX(iter) != LYD_CTX(parent)))); + + /* get module to print, if any */ + prev_mod = (parent && parent->schema) ? parent->schema->module : lyd_owner_module(parent); + if (prev_mod == mod) { + mod = NULL; + } + + /* realloc string */ + len = 1 + (mod ? strlen(mod->name) + 1 : 0) + (iter->schema ? strlen(iter->schema->name) : + strlen(((struct lyd_node_opaq *)iter)->name.name)); + if ((rc = lyd_path_str_enlarge(&buffer, &buflen, bufused + len, 0))) { + break; + } + + /* print next node */ + bufused += sprintf(buffer + bufused, "/%s%s%s", mod ? mod->name : "", mod ? ":" : "", LYD_NAME(iter)); + + /* do not always print the last (first) predicate */ + if (iter->schema && ((depth > 1) || (pathtype == LYD_PATH_STD))) { + switch (iter->schema->nodetype) { + case LYS_LIST: + if (iter->schema->flags & LYS_KEYLESS) { + /* print its position */ + rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, 0); + } else { + /* print all list keys in predicates */ + rc = lyd_path_list_predicate(iter, &buffer, &buflen, &bufused, 0); + } + break; + case LYS_LEAFLIST: + if (iter->schema->flags & LYS_CONFIG_W) { + /* print leaf-list value */ + rc = lyd_path_leaflist_predicate(iter, &buffer, &buflen, &bufused, 0); + } else { + /* print its position */ + rc = lyd_path_position_predicate(iter, &buffer, &buflen, &bufused, 0); + } + break; + default: + /* nothing to print more */ + break; + } + } + if (rc) { + break; + } + } + break; + } + + return buffer; +} + +LIBYANG_API_DEF struct lyd_meta * +lyd_find_meta(const struct lyd_meta *first, const struct lys_module *module, const char *name) +{ + struct lyd_meta *ret = NULL; + const struct ly_ctx *ctx; + const char *prefix, *tmp; + char *str; + size_t pref_len, name_len; + + LY_CHECK_ARG_RET(NULL, module || strchr(name, ':'), name, NULL); + LY_CHECK_CTX_EQUAL_RET(first ? first->annotation->module->ctx : NULL, module ? module->ctx : NULL, NULL); + + if (!first) { + return NULL; + } + + ctx = first->annotation->module->ctx; + + /* 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; + } + + /* find the module */ + if (prefix) { + str = strndup(prefix, pref_len); + module = ly_ctx_get_module_latest(ctx, str); + free(str); + LY_CHECK_ERR_RET(!module, LOGERR(ctx, LY_EINVAL, "Module \"%.*s\" not found.", (int)pref_len, prefix), NULL); + } + + /* find the metadata */ + LY_LIST_FOR(first, first) { + if ((first->annotation->module == module) && !strcmp(first->name, name)) { + ret = (struct lyd_meta *)first; + break; + } + } + + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyd_find_sibling_first(const struct lyd_node *siblings, const struct lyd_node *target, struct lyd_node **match) +{ + struct lyd_node **match_p, *iter, *dup = NULL; + struct lyd_node_inner *parent; + ly_bool found; + + LY_CHECK_ARG_RET(NULL, target, LY_EINVAL); + if (!siblings) { + /* no data */ + if (match) { + *match = NULL; + } + return LY_ENOTFOUND; + } + + if (LYD_CTX(siblings) != LYD_CTX(target)) { + /* create a duplicate in this context */ + LY_CHECK_RET(lyd_dup_single_to_ctx(target, LYD_CTX(siblings), NULL, 0, &dup)); + target = dup; + } + + if ((siblings->schema && target->schema && (lysc_data_parent(siblings->schema) != lysc_data_parent(target->schema)))) { + /* schema mismatch */ + lyd_free_tree(dup); + if (match) { + *match = NULL; + } + return LY_ENOTFOUND; + } + + /* get first sibling */ + siblings = lyd_first_sibling(siblings); + + parent = siblings->parent; + if (parent && parent->schema && parent->children_ht) { + assert(target->hash); + + if (lysc_is_dup_inst_list(target->schema)) { + /* we must search the instances from beginning to find the first matching one */ + found = 0; + LYD_LIST_FOR_INST(siblings, target->schema, iter) { + if (!lyd_compare_single(target, iter, 0)) { + found = 1; + break; + } + } + if (found) { + siblings = iter; + } else { + siblings = NULL; + } + } else { + /* find by hash */ + if (!lyht_find(parent->children_ht, &target, target->hash, (void **)&match_p)) { + siblings = *match_p; + } else { + /* not found */ + siblings = NULL; + } + } + } else { + /* no children hash table */ + for ( ; siblings; siblings = siblings->next) { + if (!lyd_compare_single(siblings, target, LYD_COMPARE_OPAQ)) { + break; + } + } + } + + lyd_free_tree(dup); + if (!siblings) { + if (match) { + *match = NULL; + } + return LY_ENOTFOUND; + } + + if (match) { + *match = (struct lyd_node *)siblings; + } + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyd_find_sibling_val(const struct lyd_node *siblings, const struct lysc_node *schema, const char *key_or_value, + size_t val_len, struct lyd_node **match) +{ + LY_ERR rc; + struct lyd_node *target = NULL; + const struct lyd_node *parent; + + LY_CHECK_ARG_RET(NULL, schema, !(schema->nodetype & (LYS_CHOICE | LYS_CASE)), LY_EINVAL); + if (!siblings) { + /* no data */ + if (match) { + *match = NULL; + } + return LY_ENOTFOUND; + } + + if ((LYD_CTX(siblings) != schema->module->ctx)) { + /* parent of ext nodes is useless */ + parent = (siblings->flags & LYD_EXT) ? NULL : lyd_parent(siblings); + if (lyd_find_schema_ctx(schema, LYD_CTX(siblings), parent, 0, &schema)) { + /* no schema node in siblings so certainly no data node either */ + if (match) { + *match = NULL; + } + return LY_ENOTFOUND; + } + } + + if (siblings->schema && (lysc_data_parent(siblings->schema) != lysc_data_parent(schema))) { + /* schema mismatch */ + if (match) { + *match = NULL; + } + return LY_ENOTFOUND; + } + + if (key_or_value && !val_len) { + val_len = strlen(key_or_value); + } + + if ((schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && key_or_value) { + /* create a data node and find the instance */ + if (schema->nodetype == LYS_LEAFLIST) { + /* target used attributes: schema, hash, value */ + rc = lyd_create_term(schema, key_or_value, val_len, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &target); + LY_CHECK_RET(rc); + } else { + /* target used attributes: schema, hash, child (all keys) */ + LY_CHECK_RET(lyd_create_list2(schema, key_or_value, val_len, &target)); + } + + /* find it */ + rc = lyd_find_sibling_first(siblings, target, match); + } else { + /* find the first schema node instance */ + rc = lyd_find_sibling_schema(siblings, schema, match); + } + + lyd_free_tree(target); + return rc; +} + +LIBYANG_API_DEF LY_ERR +lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_node *target, struct ly_set **set) +{ + struct lyd_node **match_p, *first, *iter; + struct lyd_node_inner *parent; + + LY_CHECK_ARG_RET(NULL, target, set, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(siblings ? LYD_CTX(siblings) : NULL, LYD_CTX(target), LY_EINVAL); + + LY_CHECK_RET(ly_set_new(set)); + + if (!siblings || (siblings->schema && (lysc_data_parent(siblings->schema) != lysc_data_parent(target->schema)))) { + /* no data or schema mismatch */ + return LY_ENOTFOUND; + } + + /* get first sibling */ + siblings = lyd_first_sibling(siblings); + + parent = siblings->parent; + if (parent && parent->schema && parent->children_ht) { + assert(target->hash); + + /* find the first instance */ + lyd_find_sibling_first(siblings, target, &first); + if (first) { + /* add it so that it is the first in the set */ + if (ly_set_add(*set, first, 1, NULL)) { + goto error; + } + + /* find by hash */ + if (!lyht_find(parent->children_ht, &target, target->hash, (void **)&match_p)) { + iter = *match_p; + } else { + /* not found */ + iter = NULL; + } + while (iter) { + /* add all found nodes into the set */ + if ((iter != first) && !lyd_compare_single(iter, target, 0) && ly_set_add(*set, iter, 1, NULL)) { + goto error; + } + + /* find next instance */ + if (lyht_find_next(parent->children_ht, &iter, iter->hash, (void **)&match_p)) { + iter = NULL; + } else { + iter = *match_p; + } + } + } + } else { + /* no children hash table */ + LY_LIST_FOR(siblings, siblings) { + if (!lyd_compare_single(target, siblings, LYD_COMPARE_OPAQ)) { + ly_set_add(*set, (void *)siblings, 1, NULL); + } + } + } + + if (!(*set)->count) { + return LY_ENOTFOUND; + } + return LY_SUCCESS; + +error: + ly_set_free(*set, NULL); + *set = NULL; + return LY_EMEM; +} + +LIBYANG_API_DEF LY_ERR +lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struct lyd_node **match) +{ + LY_CHECK_ARG_RET(NULL, name, LY_EINVAL); + + for ( ; first; first = first->next) { + if (!first->schema && !strcmp(LYD_NAME(first), name)) { + break; + } + } + + if (match) { + *match = (struct lyd_node *)first; + } + return first ? LY_SUCCESS : LY_ENOTFOUND; +} + +LIBYANG_API_DEF LY_ERR +lyd_find_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath, LY_VALUE_FORMAT format, + void *prefix_data, const struct lyxp_var *vars, struct ly_set **set) +{ + LY_ERR ret = LY_SUCCESS; + struct lyxp_set xp_set = {0}; + struct lyxp_expr *exp = NULL; + uint32_t i; + + LY_CHECK_ARG_RET(NULL, tree, xpath, format, set, LY_EINVAL); + + *set = NULL; + + /* parse expression */ + ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(tree), xpath, 0, 1, &exp); + LY_CHECK_GOTO(ret, cleanup); + + /* evaluate expression */ + ret = lyxp_eval(LYD_CTX(tree), exp, NULL, format, prefix_data, ctx_node, ctx_node, tree, vars, &xp_set, + LYXP_IGNORE_WHEN); + LY_CHECK_GOTO(ret, cleanup); + + if (xp_set.type != LYXP_SET_NODE_SET) { + LOGERR(LYD_CTX(tree), LY_EINVAL, "XPath \"%s\" result is not a node set.", xpath); + ret = LY_EINVAL; + goto cleanup; + } + + /* allocate return set */ + ret = ly_set_new(set); + LY_CHECK_GOTO(ret, cleanup); + + /* transform into ly_set, allocate memory for all the elements once (even though not all items must be + * elements but most likely will be) */ + (*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs); + LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(LYD_CTX(tree)); ret = LY_EMEM, cleanup); + (*set)->size = xp_set.used; + + for (i = 0; i < xp_set.used; ++i) { + if (xp_set.val.nodes[i].type == LYXP_NODE_ELEM) { + ret = ly_set_add(*set, xp_set.val.nodes[i].node, 1, NULL); + LY_CHECK_GOTO(ret, cleanup); + } + } + +cleanup: + lyxp_set_free_content(&xp_set); + lyxp_expr_free((struct ly_ctx *)LYD_CTX(tree), exp); + if (ret) { + ly_set_free(*set, NULL); + *set = NULL; + } + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyd_find_xpath3(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath, + const struct lyxp_var *vars, struct ly_set **set) +{ + LY_CHECK_ARG_RET(NULL, tree, xpath, set, LY_EINVAL); + + return lyd_find_xpath4(ctx_node, tree, xpath, LY_VALUE_JSON, NULL, vars, set); +} + +LIBYANG_API_DEF LY_ERR +lyd_find_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, struct ly_set **set) +{ + LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL); + + return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set); +} + +LIBYANG_API_DEF LY_ERR +lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, struct ly_set **set) +{ + LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL); + + return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set); +} + +LIBYANG_API_DEF LY_ERR +lyd_eval_xpath3(const struct lyd_node *ctx_node, const struct lys_module *cur_mod, const char *xpath, + LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, ly_bool *result) +{ + LY_ERR ret = LY_SUCCESS; + struct lyxp_set xp_set = {0}; + struct lyxp_expr *exp = NULL; + + LY_CHECK_ARG_RET(NULL, ctx_node, xpath, result, LY_EINVAL); + + /* compile expression */ + ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(ctx_node), xpath, 0, 1, &exp); + LY_CHECK_GOTO(ret, cleanup); + + /* evaluate expression */ + ret = lyxp_eval(LYD_CTX(ctx_node), exp, cur_mod, format, prefix_data, ctx_node, ctx_node, ctx_node, vars, &xp_set, + LYXP_IGNORE_WHEN); + LY_CHECK_GOTO(ret, cleanup); + + /* transform into boolean */ + ret = lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN); + LY_CHECK_GOTO(ret, cleanup); + + /* set result */ + *result = xp_set.val.bln; + +cleanup: + lyxp_set_free_content(&xp_set); + lyxp_expr_free((struct ly_ctx *)LYD_CTX(ctx_node), exp); + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyd_eval_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, ly_bool *result) +{ + return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, vars, result); +} + +LIBYANG_API_DEF LY_ERR +lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result) +{ + return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result); +} + +LIBYANG_API_DEF LY_ERR +lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output, struct lyd_node **match) +{ + LY_ERR ret = LY_SUCCESS; + struct lyxp_expr *expr = NULL; + struct ly_path *lypath = NULL; + + LY_CHECK_ARG_RET(NULL, ctx_node, ctx_node->schema, path, LY_EINVAL); + + /* parse the path */ + ret = ly_path_parse(LYD_CTX(ctx_node), ctx_node->schema, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, + LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_SIMPLE, &expr); + LY_CHECK_GOTO(ret, cleanup); + + /* compile the path */ + ret = ly_path_compile(LYD_CTX(ctx_node), NULL, ctx_node->schema, NULL, expr, + output ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, 0, LY_VALUE_JSON, NULL, &lypath); + LY_CHECK_GOTO(ret, cleanup); + + /* evaluate the path */ + ret = ly_path_eval_partial(lypath, ctx_node, NULL, match); + +cleanup: + lyxp_expr_free(LYD_CTX(ctx_node), expr); + ly_path_free(LYD_CTX(ctx_node), lypath); + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct lyd_node **match) +{ + LY_ERR ret; + struct lyd_node *m; + + LY_CHECK_ARG_RET(NULL, path, LY_EINVAL); + + ret = ly_path_eval(path, tree, &m); + if (ret) { + if (match) { + *match = NULL; + } + return LY_ENOTFOUND; + } + + if (match) { + *match = m; + } + return LY_SUCCESS; +} |