diff options
Diffstat (limited to 'src/tree_data.c')
-rw-r--r-- | src/tree_data.c | 914 |
1 files changed, 620 insertions, 294 deletions
diff --git a/src/tree_data.c b/src/tree_data.c index d6a04ff..82a1ae8 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -52,6 +52,9 @@ #include "xml.h" #include "xpath.h" +static LY_ERR lyd_compare_siblings_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options, + ly_bool parental_schemas_checked); + static LYD_FORMAT lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format) { @@ -96,10 +99,9 @@ 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; + LY_ERR r = LY_SUCCESS, 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; @@ -121,36 +123,40 @@ lyd_parse(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct /* 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, + r = 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, + r = 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, + r = 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; + r = LY_EINVAL; break; } - LY_CHECK_GOTO(rc, cleanup); + if (r) { + rc = r; + if ((r != LY_EVALID) || !lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || + (ly_vecode(ctx) == LYVE_SYNTAX)) { + goto 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 (parent && parsed.count) { + /* use the first parsed node */ + first_p = &parsed.dnodes[0]; } 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, + r = 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); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); } /* set the operation node */ @@ -252,28 +258,7 @@ lyd_parse_data_path(const struct ly_ctx *ctx, const char *path, LYD_FORMAT forma * * 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. + * Specific @p data_type values have different parameter meaning as mentioned for ::lyd_parse_op(). * * @param[in] ctx libyang context. * @param[in] ext Extension instance providing the specific schema tree to match with the data being parsed. @@ -295,6 +280,7 @@ lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str struct ly_set parsed = {0}; struct lyd_node *first = NULL, *envp = NULL; uint32_t i, parse_opts, val_opts, int_opts = 0; + ly_bool proto_msg = 0; if (!ctx) { ctx = LYD_CTX(parent); @@ -316,20 +302,51 @@ lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str 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 */ + proto_msg = 1; + break; 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); - } + LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), + tree, !op, LY_EINVAL); + proto_msg = 1; + break; + case LYD_TYPE_RPC_RESTCONF: + case LYD_TYPE_REPLY_RESTCONF: + LY_CHECK_ARG_RET(ctx, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op, LY_EINVAL); + proto_msg = 1; + break; + case LYD_TYPE_NOTIF_RESTCONF: + LY_CHECK_ARG_RET(ctx, format == LYD_JSON, !parent, tree, op, LY_EINVAL); + proto_msg = 1; + break; - /* parse the NETCONF message */ - rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx); + /* set internal opts */ + case LYD_TYPE_RPC_YANG: + int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS); + break; + case LYD_TYPE_NOTIF_YANG: + int_opts = LYD_INTOPT_NOTIF | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS); + break; + case LYD_TYPE_REPLY_YANG: + int_opts = LYD_INTOPT_REPLY | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS); + break; + case LYD_TYPE_DATA_YANG: + LOGINT(ctx); + rc = LY_EINT; + goto cleanup; + } + + /* parse a full protocol message */ + if (proto_msg) { + if (format == LYD_XML) { + /* parse the NETCONF (or RESTCONF XML) message */ + rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx); + } else { + /* parse the RESTCONF message */ + rc = lyd_parse_json_restconf(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 */ @@ -349,21 +366,6 @@ lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str *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 */ @@ -405,7 +407,7 @@ cleanup: lyd_free_tree(parsed.dnodes[i]); } while (i); } - if (tree && ((format != LYD_XML) || !envp)) { + if (tree && !envp) { *tree = NULL; } if (op) { @@ -484,6 +486,10 @@ lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct ly /* get the first schema sibling */ schema = lys_getnext(NULL, sparent, new_node->schema->module->compiled, getnext_opts); + if (!schema) { + /* must be a top-level extension instance data, no anchor */ + return NULL; + } found = 0; LY_LIST_FOR(match, match) { @@ -506,8 +512,12 @@ lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct ly /* 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 (!schema) { + /* must be a top-level extension instance data, no anchor */ + return NULL; + } } if (found && (match->schema != new_node->schema)) { @@ -660,6 +670,14 @@ lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling_p, stru } else { /* find the anchor, our next node, so we can insert before it */ anchor = lyd_insert_get_next_anchor(first_sibling, node); + + /* cannot insert data node after opaque nodes */ + if (!anchor && node->schema && first_sibling && !first_sibling->prev->schema) { + anchor = first_sibling->prev; + while ((anchor != first_sibling) && !anchor->prev->schema) { + anchor = anchor->prev; + } + } } if (anchor) { @@ -684,7 +702,7 @@ lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling_p, stru 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)) { + if (node->schema && (node->schema->flags & LYS_KEY) && parent && parent->schema && lyd_insert_has_keys(parent)) { lyd_hash(parent); /* now we can insert even the list into its parent HT */ @@ -756,12 +774,12 @@ lyd_insert_child(struct lyd_node *parent, struct lyd_node *node) } if (node->parent || node->prev->next) { - lyd_unlink_tree(node); + lyd_unlink(node); } while (node) { iter = node->next; - lyd_unlink_tree(node); + lyd_unlink(node); lyd_insert_node(parent, NULL, node, 0); node = iter; } @@ -783,7 +801,7 @@ lyplg_ext_insert(struct lyd_node *parent, struct lyd_node *first) while (first) { iter = first->next; - lyd_unlink_tree(first); + lyd_unlink(first); lyd_insert_node(parent, NULL, first, 1); first = iter; } @@ -807,7 +825,7 @@ lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_n } if (node->parent || node->prev->next) { - lyd_unlink_tree(node); + lyd_unlink(node); } while (node) { @@ -817,7 +835,7 @@ lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_n } iter = node->next; - lyd_unlink_tree(node); + lyd_unlink(node); lyd_insert_node(NULL, &sibling, node, 0); node = iter; } @@ -850,7 +868,7 @@ lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node) return LY_EINVAL; } - lyd_unlink_tree(node); + lyd_unlink(node); lyd_insert_before_node(sibling, node); lyd_insert_hash(node); @@ -874,26 +892,15 @@ lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node) return LY_EINVAL; } - lyd_unlink_tree(node); + lyd_unlink(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) +void +lyd_unlink(struct lyd_node *node) { struct lyd_node *iter; @@ -941,6 +948,35 @@ lyd_unlink_tree(struct lyd_node *node) node->prev = node; } +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) { + if (lysc_is_key(elem->schema) && elem->parent) { + LOGERR(LYD_CTX(elem), LY_EINVAL, "Cannot unlink a list key \"%s\", unlink the list instance instead.", + LYD_NAME(elem)); + return; + } + + lyd_unlink(elem); + lyd_insert_node(NULL, &first, elem, 1); + } +} + +LIBYANG_API_DEF void +lyd_unlink_tree(struct lyd_node *node) +{ + if (node && lysc_is_key(node->schema) && node->parent) { + LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot unlink a list key \"%s\", unlink the list instance instead.", + LYD_NAME(node)); + return; + } + + lyd_unlink(node); +} + void lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_dflt) { @@ -973,7 +1009,7 @@ lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_df 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, + size_t name_len, const char *value, size_t value_len, ly_bool is_utf8, 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; @@ -1005,7 +1041,7 @@ lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct ly 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, + ret = lyd_value_store(mod->ctx, &mt->value, ant_type, value, value_len, is_utf8, 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); @@ -1110,11 +1146,9 @@ finish: LIBYANG_API_DEF const struct lyd_node_term * lyd_target(const struct ly_path *path, const struct lyd_node *tree) { - struct lyd_node *target; + struct lyd_node *target = NULL; - if (ly_path_eval(path, tree, &target)) { - return NULL; - } + lyd_find_target(path, tree, &target); return (struct lyd_node_term *)target; } @@ -1149,15 +1183,6 @@ lyd_compare_schema_equal(const struct lysc_node *schema1, const struct lysc_node 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; } @@ -1269,30 +1294,21 @@ lyd_compare_single_value(const struct lyd_node *node1, const struct lyd_node *no } /** - * @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. + * @brief Compare 2 data nodes if they are equivalent regarding the schema tree. + * + * Works correctly even if @p node1 and @p node2 have different contexts. + * + * @param[in] node1 The first node to compare. + * @param[in] node2 The second node to compare. + * @param[in] options Various @ref datacompareoptions. + * @param[in] parental_schemas_checked Flag set if parent schemas were checked for match. + * @return LY_SUCCESS if the nodes are equivalent. + * @return LY_ENOT if the nodes are not equivalent. */ static LY_ERR -lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options, +lyd_compare_single_schema(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) { @@ -1317,6 +1333,28 @@ lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2, } } + return LY_SUCCESS; +} + +/** + * @brief Compare 2 data nodes if they are equivalent regarding the data they contain. + * + * Works correctly even if @p node1 and @p node2 have different contexts. + * + * @param[in] node1 The first node to compare. + * @param[in] node2 The second node to compare. + * @param[in] options Various @ref datacompareoptions. + * @return LY_SUCCESS if the nodes are equivalent. + * @return LY_ENOT if the nodes are not equivalent. + */ +static LY_ERR +lyd_compare_single_data(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options) +{ + const struct lyd_node *iter1, *iter2; + struct lyd_node_any *any1, *any2; + int len1, len2; + LY_ERR r; + if (!(options & LYD_COMPARE_OPAQ) && (node1->hash != node2->hash)) { return LY_ENOT; } @@ -1326,14 +1364,16 @@ lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2, 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 ((!node1->schema && !node2->schema) || (node1->schema && (node1->schema->nodetype & LYD_NODE_TERM)) || + (node2->schema && (node2->schema->nodetype & LYD_NODE_TERM))) { + /* compare values only if there are any to compare */ + 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 lyd_compare_siblings_(lyd_child(node1), lyd_child(node2), options, 1); } return LY_SUCCESS; } else { @@ -1354,50 +1394,38 @@ lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2, 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; - } - } + /* implicit container is always equal to a container with non-default descendants */ if (options & LYD_COMPARE_FULL_RECURSION) { - iter1 = lyd_child(node1); - iter2 = lyd_child(node2); - goto all_children_compare; + return lyd_compare_siblings_(lyd_child(node1), lyd_child(node2), options, 1); } 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) */ + if (options & LYD_COMPARE_FULL_RECURSION) { + return lyd_compare_siblings_(iter1, iter2, options, 1); + } else if (node1->schema->flags & LYS_KEYLESS) { + /* always equal */ + return LY_SUCCESS; + } -all_children_compare: - if (!iter1 && !iter2) { - /* no children, nothing to compare */ - return LY_SUCCESS; + /* 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 (!iter1 || !iter2) { + return (iter1 == iter2) ? LY_SUCCESS : LY_ENOT; } + r = lyd_compare_single_schema(iter1, iter2, options, 1); + LY_CHECK_RET(r); + r = lyd_compare_single_data(iter1, iter2, options); + LY_CHECK_RET(r); - 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; - } + iter1 = iter1->next; + iter2 = iter2->next; } + return LY_SUCCESS; case LYS_ANYXML: case LYS_ANYDATA: @@ -1409,9 +1437,7 @@ all_children_compare: } switch (any1->value_type) { case LYD_ANYDATA_DATATREE: - iter1 = any1->value.tree; - iter2 = any2->value.tree; - goto all_children_compare; + return lyd_compare_siblings_(any1->value.tree, any2->value.tree, options, 1); case LYD_ANYDATA_STRING: case LYD_ANYDATA_XML: case LYD_ANYDATA_JSON: @@ -1441,23 +1467,77 @@ all_children_compare: return LY_EINT; } -LIBYANG_API_DEF LY_ERR -lyd_compare_single(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options) +/** + * @brief Compare all siblings at a node level. + * + * @param[in] node1 First sibling list. + * @param[in] node2 Second sibling list. + * @param[in] options Various @ref datacompareoptions. + * @param[in] parental_schemas_checked Flag set if parent schemas were checked for match. + * @return LY_SUCCESS if equal. + * @return LY_ENOT if not equal. + * @return LY_ERR on error. + */ +static LY_ERR +lyd_compare_siblings_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options, + ly_bool parental_schemas_checked) { - return lyd_compare_single_(node1, node2, options, 0); + LY_ERR r; + const struct lyd_node *iter2; + + while (node1 && node2) { + /* schema match */ + r = lyd_compare_single_schema(node1, node2, options, parental_schemas_checked); + LY_CHECK_RET(r); + + if (node1->schema && (((node1->schema->nodetype == LYS_LIST) && !(node1->schema->flags & LYS_KEYLESS)) || + ((node1->schema->nodetype == LYS_LEAFLIST) && (node1->schema->flags & LYS_CONFIG_W))) && + (node1->schema->flags & LYS_ORDBY_SYSTEM)) { + /* find a matching instance in case they are ordered differently */ + r = lyd_find_sibling_first(node2, node1, (struct lyd_node **)&iter2); + if (r == LY_ENOTFOUND) { + /* no matching instance, data not equal */ + r = LY_ENOT; + } + LY_CHECK_RET(r); + } else { + /* compare with the current node */ + iter2 = node2; + } + + /* data match */ + r = lyd_compare_single_data(node1, iter2, options | LYD_COMPARE_FULL_RECURSION); + LY_CHECK_RET(r); + + node1 = node1->next; + node2 = node2->next; + } + + return (node1 || node2) ? LY_ENOT : LY_SUCCESS; } LIBYANG_API_DEF LY_ERR -lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options) +lyd_compare_single(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)); + LY_ERR r; + + if (!node1 || !node2) { + return (node1 == node2) ? LY_SUCCESS : LY_ENOT; } - if (node1 == node2) { - return LY_SUCCESS; + /* schema match */ + if ((r = lyd_compare_single_schema(node1, node2, options, 0))) { + return r; } - return LY_ENOT; + + /* data match */ + return lyd_compare_single_data(node1, node2, options); +} + +LIBYANG_API_DEF LY_ERR +lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options) +{ + return lyd_compare_siblings_(node1, node2, options, 0); } LIBYANG_API_DEF LY_ERR @@ -1676,6 +1756,9 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_ } else { dup->flags = (node->flags & (LYD_DEFAULT | LYD_EXT)) | LYD_NEW; } + if (options & LYD_DUP_WITH_PRIV) { + dup->priv = node->priv; + } if (trg_ctx == LYD_CTX(node)) { dup->schema = node->schema; } else { @@ -1736,7 +1819,7 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_ /* 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, + ret = lyd_value_store(trg_ctx, &term->value, type, val_can, strlen(val_can), 1, NULL, LY_VALUE_CANON, NULL, LYD_HINT_DATA, term->schema, NULL); LY_CHECK_GOTO(ret, error); } @@ -1787,10 +1870,11 @@ error: * @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) +lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, + uint32_t options, struct lyd_node **dup_parent, struct lyd_node **local_parent) { - const struct lyd_node_inner *orig_parent, *iter; + const struct lyd_node *orig_parent; + struct lyd_node *iter = NULL; ly_bool repeat = 1, ext_parent = 0; *dup_parent = NULL; @@ -1799,38 +1883,38 @@ lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_c if (node->flags & LYD_EXT) { ext_parent = 1; } - for (orig_parent = node->parent; repeat && orig_parent; orig_parent = orig_parent->parent) { + for (orig_parent = lyd_parent(node); repeat && orig_parent; orig_parent = lyd_parent(orig_parent)) { if (ext_parent) { /* use the standard context */ trg_ctx = LYD_CTX(orig_parent); } - if (parent && (parent->schema == orig_parent->schema)) { + if (parent && (LYD_CTX(parent) == LYD_CTX(orig_parent)) && (parent->schema == orig_parent->schema)) { /* stop creating parents, connect what we have into the provided parent */ iter = parent; repeat = 0; + } else if (parent && (LYD_CTX(parent) != LYD_CTX(orig_parent)) && + lyd_compare_schema_equal(parent->schema, orig_parent->schema) && + lyd_compare_schema_parents_equal(parent, orig_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; + LY_CHECK_RET(lyd_dup_r(orig_parent, trg_ctx, NULL, 0, &iter, options, &iter)); + + /* insert into the previous duplicated parent */ if (*dup_parent) { - (*dup_parent)->prev = iter->child->prev; - iter->child->prev = *dup_parent; + lyd_insert_node(iter, NULL, *dup_parent, 0); } - } else { - ((struct lyd_node_inner *)iter)->child = *dup_parent; + + /* update the last duplicated parent */ + *dup_parent = iter; } - if (*dup_parent) { - (*dup_parent)->parent = (struct lyd_node_inner *)iter; + + /* set the first parent */ + if (!*local_parent) { + *local_parent = iter; } - *dup_parent = (struct lyd_node *)iter; + if (orig_parent->flags & LYD_EXT) { ext_parent = 1; } @@ -1838,23 +1922,27 @@ lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_c 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__); + LOGERR(trg_ctx, LY_EINVAL, "None of the duplicated node \"%s\" schema parents match the provided parent \"%s\".", + LYD_NAME(node), LYD_NAME(parent)); return LY_EINVAL; } + if (*dup_parent && parent) { + /* last insert into a prevously-existing parent */ + lyd_insert_node(parent, NULL, *dup_parent, 0); + } 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, +lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *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) */ + struct lyd_node *local_parent = NULL; /* the direct parent node for the duplicated node(s) */ assert(node && trg_ctx); @@ -1869,7 +1957,7 @@ lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_no 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); + rc = lyd_find_sibling_schema(lyd_child(local_parent), orig->schema, first ? NULL : &first); LY_CHECK_ERR_GOTO(rc, LOGINT(trg_ctx), error); } else { assert(!(options & LYD_DUP_WITH_PARENTS)); @@ -1879,8 +1967,7 @@ lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_no } } 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); + rc = lyd_dup_r(orig, trg_ctx, local_parent, 0, &first, options, first ? NULL : &first); LY_CHECK_GOTO(rc, error); } if (nosiblings) { @@ -1923,7 +2010,7 @@ lyd_dup_ctx_check(const struct lyd_node *node, const struct lyd_node_inner *pare 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."); + LOGERR(LYD_CTX(node), LY_EINVAL, "Different contexts used in node duplication."); return LY_EINVAL; } } @@ -1937,7 +2024,7 @@ lyd_dup_single(const struct lyd_node *node, struct lyd_node_inner *parent, uint3 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); + return lyd_dup(node, LYD_CTX(node), (struct lyd_node *)parent, options, 1, dup); } LIBYANG_API_DEF LY_ERR @@ -1946,7 +2033,7 @@ lyd_dup_single_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx, { LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL); - return lyd_dup(node, trg_ctx, parent, options, 1, dup); + return lyd_dup(node, trg_ctx, (struct lyd_node *)parent, options, 1, dup); } LIBYANG_API_DEF LY_ERR @@ -1955,7 +2042,7 @@ lyd_dup_siblings(const struct lyd_node *node, struct lyd_node_inner *parent, uin 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); + return lyd_dup(node, LYD_CTX(node), (struct lyd_node *)parent, options, 0, dup); } LIBYANG_API_DEF LY_ERR @@ -1964,7 +2051,7 @@ lyd_dup_siblings_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ct { LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL); - return lyd_dup(node, trg_ctx, parent, options, 0, dup); + return lyd_dup(node, trg_ctx, (struct lyd_node *)parent, options, 0, dup); } LIBYANG_API_DEF LY_ERR @@ -2019,13 +2106,13 @@ finish: */ 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) + lyd_merge_cb merge_cb, void *cb_data, uint16_t options, struct ly_ht **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; + struct ly_ht *child_dup_inst = NULL; LY_ERR ret; ly_bool first_inst = 0; @@ -2110,7 +2197,7 @@ lyd_merge_sibling_r(struct lyd_node **first_trg, struct lyd_node *parent_trg, co /* node not found, merge it */ if (options & LYD_MERGE_DESTRUCT) { dup_src = (struct lyd_node *)sibling_src; - lyd_unlink_tree(dup_src); + lyd_unlink(dup_src); /* spend it */ *sibling_src_p = NULL; } else { @@ -2147,7 +2234,7 @@ lyd_merge(struct lyd_node **target, const struct lyd_node *source, const struct 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; + struct ly_ht *dup_inst = NULL; ly_bool first; LY_ERR ret = LY_SUCCESS; @@ -2357,9 +2444,9 @@ lyd_path(const struct lyd_node *node, LYD_PATH_TYPE pathtype, char *buffer, size 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); + mod = lyd_node_module(iter); parent = lyd_parent(iter); - prev_mod = (parent && parent->schema) ? parent->schema->module : lyd_owner_module(parent); + prev_mod = lyd_node_module(parent); if (prev_mod == mod) { mod = NULL; } @@ -2429,15 +2516,17 @@ lyd_path_set(const struct ly_set *dnodes, LYD_PATH_TYPE pathtype) for (depth = 1; depth <= dnodes->count; ++depth) { /* current node */ iter = dnodes->dnodes[depth - 1]; - mod = iter->schema ? iter->schema->module : lyd_owner_module(iter); + mod = lyd_node_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)))); + assert(!parent || !iter->schema || !parent->schema || (parent->schema->nodetype & LYD_NODE_ANY) || + (lysc_data_parent(iter->schema) == parent->schema) || + (!lysc_data_parent(iter->schema) && (LYD_CTX(iter) != LYD_CTX(parent))) || + (parent->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))); /* get module to print, if any */ - prev_mod = (parent && parent->schema) ? parent->schema->module : lyd_owner_module(parent); + prev_mod = lyd_node_module(parent); if (prev_mod == mod) { mod = NULL; } @@ -2567,14 +2656,14 @@ lyd_find_sibling_first(const struct lyd_node *siblings, const struct lyd_node *t siblings = lyd_first_sibling(siblings); parent = siblings->parent; - if (parent && parent->schema && parent->children_ht) { + if (target->schema && 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)) { + if (!lyd_compare_single(target, iter, LYD_COMPARE_FULL_RECURSION)) { found = 1; break; } @@ -2594,10 +2683,16 @@ lyd_find_sibling_first(const struct lyd_node *siblings, const struct lyd_node *t } } } else { - /* no children hash table */ + /* no children hash table or cannot be used */ for ( ; siblings; siblings = siblings->next) { - if (!lyd_compare_single(siblings, target, LYD_COMPARE_OPAQ)) { - break; + if (lysc_is_dup_inst_list(target->schema)) { + if (!lyd_compare_single(siblings, target, LYD_COMPARE_FULL_RECURSION)) { + break; + } + } else { + if (!lyd_compare_single(siblings, target, 0)) { + break; + } } } } @@ -2661,7 +2756,7 @@ lyd_find_sibling_val(const struct lyd_node *siblings, const struct lysc_node *sc /* 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); + rc = lyd_create_term(schema, key_or_value, val_len, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &target); LY_CHECK_RET(rc); } else { /* target used attributes: schema, hash, child (all keys) */ @@ -2684,6 +2779,7 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_ { struct lyd_node **match_p, *first, *iter; struct lyd_node_inner *parent; + uint32_t comp_opts; LY_CHECK_ARG_RET(NULL, target, set, LY_EINVAL); LY_CHECK_CTX_EQUAL_RET(siblings ? LYD_CTX(siblings) : NULL, LYD_CTX(target), LY_EINVAL); @@ -2695,6 +2791,9 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_ return LY_ENOTFOUND; } + /* set options */ + comp_opts = (lysc_is_dup_inst_list(target->schema) ? LYD_COMPARE_FULL_RECURSION : 0); + /* get first sibling */ siblings = lyd_first_sibling(siblings); @@ -2719,7 +2818,7 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_ } 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)) { + if ((iter != first) && !lyd_compare_single(iter, target, comp_opts) && ly_set_add(*set, iter, 1, NULL)) { goto error; } @@ -2734,7 +2833,7 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_ } else { /* no children hash table */ LY_LIST_FOR(siblings, siblings) { - if (!lyd_compare_single(target, siblings, LYD_COMPARE_OPAQ)) { + if (!lyd_compare_single(target, siblings, comp_opts)) { ly_set_add(*set, (void *)siblings, 1, NULL); } } @@ -2756,8 +2855,22 @@ lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struc { LY_CHECK_ARG_RET(NULL, name, LY_EINVAL); + if (first && first->schema) { + first = first->prev; + if (first->schema) { + /* no opaque nodes */ + first = NULL; + } else { + /* opaque nodes are at the end, find quickly the first */ + while (!first->prev->schema) { + first = first->prev; + } + } + } + for ( ; first; first = first->next) { - if (!first->schema && !strcmp(LYD_NAME(first), name)) { + assert(!first->schema); + if (!strcmp(LYD_NAME(first), name)) { break; } } @@ -2769,58 +2882,19 @@ lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struc } 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) +lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, 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); + LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL); - /* 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; + return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set); +} - 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); - } - } +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); -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; + return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set); } LIBYANG_API_DEF LY_ERR @@ -2833,63 +2907,263 @@ lyd_find_xpath3(const struct lyd_node *ctx_node, const struct lyd_node *tree, co } 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) +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_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL); + LY_CHECK_ARG_RET(NULL, tree, xpath, set, LY_EINVAL); - return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set); + *set = NULL; + + return lyd_eval_xpath4(ctx_node, tree, NULL, xpath, format, prefix_data, vars, NULL, set, NULL, NULL, NULL); } LIBYANG_API_DEF LY_ERR -lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, struct ly_set **set) +lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result) { - LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL); + return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result); +} - return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set); +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_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) { + return lyd_eval_xpath4(ctx_node, ctx_node, cur_mod, xpath, format, prefix_data, vars, NULL, NULL, NULL, NULL, result); +} + +LIBYANG_API_DEF LY_ERR +lyd_eval_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const struct lys_module *cur_mod, + const char *xpath, LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, LY_XPATH_TYPE *ret_type, + struct ly_set **node_set, char **string, long double *number, ly_bool *boolean) +{ LY_ERR ret = LY_SUCCESS; struct lyxp_set xp_set = {0}; struct lyxp_expr *exp = NULL; + uint32_t i; - LY_CHECK_ARG_RET(NULL, ctx_node, xpath, result, LY_EINVAL); + LY_CHECK_ARG_RET(NULL, tree, xpath, ((ret_type && node_set && string && number && boolean) || + (node_set && !string && !number && !boolean) || (!node_set && string && !number && !boolean) || + (!node_set && !string && number && !boolean) || (!node_set && !string && !number && boolean)), LY_EINVAL); - /* compile expression */ - ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(ctx_node), xpath, 0, 1, &exp); + /* 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(ctx_node), exp, cur_mod, format, prefix_data, ctx_node, ctx_node, ctx_node, vars, &xp_set, + ret = lyxp_eval(LYD_CTX(tree), exp, cur_mod, format, prefix_data, ctx_node, ctx_node, tree, 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); + /* return expected result type without or with casting */ + if (node_set) { + /* node set */ + if (xp_set.type == LYXP_SET_NODE_SET) { + /* transform into a set */ + LY_CHECK_GOTO(ret = ly_set_new(node_set), cleanup); + (*node_set)->objs = malloc(xp_set.used * sizeof *(*node_set)->objs); + LY_CHECK_ERR_GOTO(!(*node_set)->objs, LOGMEM(LYD_CTX(tree)); ret = LY_EMEM, cleanup); + (*node_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(*node_set, xp_set.val.nodes[i].node, 1, NULL); + LY_CHECK_GOTO(ret, cleanup); + } + } + if (ret_type) { + *ret_type = LY_XPATH_NODE_SET; + } + } else if (!string && !number && !boolean) { + LOGERR(LYD_CTX(tree), LY_EINVAL, "XPath \"%s\" result is not a node set.", xpath); + ret = LY_EINVAL; + goto cleanup; + } + } - /* set result */ - *result = xp_set.val.bln; + if (string) { + if ((xp_set.type != LYXP_SET_STRING) && !node_set) { + /* cast into string */ + LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_STRING), cleanup); + } + if (xp_set.type == LYXP_SET_STRING) { + /* string */ + *string = xp_set.val.str; + xp_set.val.str = NULL; + if (ret_type) { + *ret_type = LY_XPATH_STRING; + } + } + } + + if (number) { + if ((xp_set.type != LYXP_SET_NUMBER) && !node_set) { + /* cast into number */ + LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_NUMBER), cleanup); + } + if (xp_set.type == LYXP_SET_NUMBER) { + /* number */ + *number = xp_set.val.num; + if (ret_type) { + *ret_type = LY_XPATH_NUMBER; + } + } + } + + if (boolean) { + if ((xp_set.type != LYXP_SET_BOOLEAN) && !node_set) { + /* cast into boolean */ + LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN), cleanup); + } + if (xp_set.type == LYXP_SET_BOOLEAN) { + /* boolean */ + *boolean = xp_set.val.bln; + if (ret_type) { + *ret_type = LY_XPATH_BOOLEAN; + } + } + } cleanup: lyxp_set_free_content(&xp_set); - lyxp_expr_free((struct ly_ctx *)LYD_CTX(ctx_node), exp); + lyxp_expr_free((struct ly_ctx *)LYD_CTX(tree), 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) +/** + * @brief Hash table node equal callback. + */ +static ly_bool +lyd_trim_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data)) { - return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, vars, result); + struct lyd_node *node1, *node2; + + node1 = *(struct lyd_node **)val1_p; + node2 = *(struct lyd_node **)val2_p; + + return node1 == node2; } LIBYANG_API_DEF LY_ERR -lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result) +lyd_trim_xpath(struct lyd_node **tree, const char *xpath, const struct lyxp_var *vars) { - return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result); + LY_ERR ret = LY_SUCCESS; + struct ly_ctx *ctx; + struct lyxp_set xp_set = {0}; + struct lyxp_expr *exp = NULL; + struct lyd_node *node, *parent; + struct lyxp_set_hash_node hnode; + struct ly_ht *parent_ht = NULL; + struct ly_set free_set = {0}; + uint32_t i, hash; + ly_bool is_result; + + LY_CHECK_ARG_RET(NULL, tree, xpath, LY_EINVAL); + + if (!*tree) { + /* nothing to do */ + goto cleanup; + } + + *tree = lyd_first_sibling(*tree); + ctx = (struct ly_ctx *)LYD_CTX(*tree); + + /* parse expression */ + ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp); + LY_CHECK_GOTO(ret, cleanup); + + /* evaluate expression */ + ret = lyxp_eval(ctx, exp, NULL, LY_VALUE_JSON, NULL, *tree, *tree, *tree, vars, &xp_set, LYXP_IGNORE_WHEN); + LY_CHECK_GOTO(ret, cleanup); + + /* create hash table for all the parents of results */ + parent_ht = lyht_new(32, sizeof node, lyd_trim_equal_cb, NULL, 1); + LY_CHECK_GOTO(!parent_ht, cleanup); + + for (i = 0; i < xp_set.used; ++i) { + if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) { + /* ignore */ + continue; + } + + for (parent = lyd_parent(xp_set.val.nodes[i].node); parent; parent = lyd_parent(parent)) { + /* add the parent into parent_ht */ + ret = lyht_insert(parent_ht, &parent, parent->hash, NULL); + if (ret == LY_EEXIST) { + /* shared parent, we are done */ + break; + } + LY_CHECK_GOTO(ret, cleanup); + } + } + + hnode.type = LYXP_NODE_ELEM; + LY_LIST_FOR(*tree, parent) { + LYD_TREE_DFS_BEGIN(parent, node) { + if (lysc_is_key(node->schema)) { + /* ignore */ + goto next_iter; + } + + /* check the results */ + is_result = 0; + if (xp_set.ht) { + hnode.node = node; + hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node); + hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type); + hash = lyht_hash_multi(hash, NULL, 0); + + if (!lyht_find(xp_set.ht, &hnode, hash, NULL)) { + is_result = 1; + } + } else { + /* not enough elements for a hash table */ + for (i = 0; i < xp_set.used; ++i) { + if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) { + /* ignore */ + continue; + } + + if (xp_set.val.nodes[i].node == node) { + is_result = 1; + break; + } + } + } + + if (is_result) { + /* keep the whole subtree if the node is in the results */ + LYD_TREE_DFS_continue = 1; + } else if (lyht_find(parent_ht, &node, node->hash, NULL)) { + /* free the whole subtree if the node is not even among the selected parents */ + ret = ly_set_add(&free_set, node, 1, NULL); + LY_CHECK_GOTO(ret, cleanup); + LYD_TREE_DFS_continue = 1; + } /* else keep the parent node because a subtree is in the results */ + +next_iter: + LYD_TREE_DFS_END(parent, node); + } + } + + /* free */ + for (i = 0; i < free_set.count; ++i) { + node = free_set.dnodes[i]; + if (*tree == node) { + *tree = (*tree)->next; + } + lyd_free_tree(node); + } + +cleanup: + lyxp_set_free_content(&xp_set); + lyxp_expr_free(ctx, exp); + lyht_free(parent_ht, NULL); + ly_set_erase(&free_set, NULL); + return ret; } LIBYANG_API_DEF LY_ERR @@ -2903,7 +3177,7 @@ lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output, /* 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_PATH_PREFIX_FIRST, LY_PATH_PRED_SIMPLE, &expr); LY_CHECK_GOTO(ret, cleanup); /* compile the path */ @@ -2912,7 +3186,7 @@ lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output, LY_CHECK_GOTO(ret, cleanup); /* evaluate the path */ - ret = ly_path_eval_partial(lypath, ctx_node, NULL, match); + ret = ly_path_eval_partial(lypath, ctx_node, NULL, 0, NULL, match); cleanup: lyxp_expr_free(LYD_CTX(ctx_node), expr); @@ -2928,7 +3202,7 @@ lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct LY_CHECK_ARG_RET(NULL, path, LY_EINVAL); - ret = ly_path_eval(path, tree, &m); + ret = ly_path_eval(path, tree, NULL, &m); if (ret) { if (match) { *match = NULL; @@ -2941,3 +3215,55 @@ lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct } return LY_SUCCESS; } + +LIBYANG_API_DEF struct lyd_node * +lyd_parent(const struct lyd_node *node) +{ + if (!node || !node->parent) { + return NULL; + } + + return &node->parent->node; +} + +LIBYANG_API_DEF struct lyd_node * +lyd_child(const struct lyd_node *node) +{ + if (!node) { + return NULL; + } + + if (!node->schema) { + /* opaq node */ + return ((const struct lyd_node_opaq *)node)->child; + } + + switch (node->schema->nodetype) { + case LYS_CONTAINER: + case LYS_LIST: + case LYS_RPC: + case LYS_ACTION: + case LYS_NOTIF: + return ((const struct lyd_node_inner *)node)->child; + default: + return NULL; + } +} + +LIBYANG_API_DEF const char * +lyd_get_value(const struct lyd_node *node) +{ + if (!node) { + return NULL; + } + + if (!node->schema) { + return ((const struct lyd_node_opaq *)node)->value; + } else if (node->schema->nodetype & LYD_NODE_TERM) { + const struct lyd_value *value = &((const struct lyd_node_term *)node)->value; + + return value->_canonical ? value->_canonical : lyd_value_get_canonical(LYD_CTX(node), value); + } + + return NULL; +} |