| /** |
| * @file tree_data.c |
| * @author Radek Krejci <rkrejci@cesnet.cz> |
| * @brief Manipulation with libyang data structures |
| * |
| * Copyright (c) 2015 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 <assert.h> |
| #include <ctype.h> |
| #include <limits.h> |
| #include <stdarg.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include "libyang.h" |
| #include "common.h" |
| #include "context.h" |
| #include "tree_data.h" |
| #include "parser.h" |
| #include "resolve.h" |
| #include "xml_internal.h" |
| #include "tree_internal.h" |
| #include "validation.h" |
| #include "xpath.h" |
| |
| static struct lyd_node * |
| lyd_parse_(struct ly_ctx *ctx, const struct lys_node *parent, const char *data, LYD_FORMAT format, int options) |
| { |
| struct lyxml_elem *xml, *xmlnext; |
| struct lyd_node *result = NULL; |
| int xmlopt = LYXML_PARSE_MULTIROOT; |
| |
| if (!ctx || !data) { |
| LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__); |
| return NULL; |
| } |
| |
| if (options & LYD_OPT_NOSIBLINGS) { |
| xmlopt = 0; |
| } |
| |
| switch (format) { |
| case LYD_XML: |
| case LYD_XML_FORMAT: |
| xml = lyxml_parse_mem(ctx, data, xmlopt); |
| if (ly_errno) { |
| return NULL; |
| } |
| result = lyd_parse_xml(ctx, &xml, options, parent); |
| LY_TREE_FOR_SAFE(xml, xmlnext, xml) { |
| lyxml_free(ctx, xml); |
| } |
| break; |
| case LYD_JSON: |
| result = lyd_parse_json(ctx, parent, data, options); |
| break; |
| default: |
| /* error */ |
| return NULL; |
| } |
| |
| if (!result && !ly_errno) { |
| /* is empty data tree really valid ? */ |
| lyd_validate(NULL, options, ctx); |
| } |
| return result; |
| } |
| |
| static struct lyd_node * |
| lyd_parse_data_(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, va_list ap) |
| { |
| const struct lys_node *rpc = NULL; |
| |
| if (lyp_check_options(options)) { |
| LOGERR(LY_EINVAL, "%s: Invalid options (multiple data type flags set).", __func__); |
| return NULL; |
| } |
| |
| if (options & LYD_OPT_RPCREPLY) { |
| rpc = va_arg(ap, struct lys_node*); |
| if (!rpc || (rpc->nodetype != LYS_RPC)) { |
| LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__); |
| return NULL; |
| } |
| } |
| |
| return lyd_parse_(ctx, rpc, data, format, options); |
| } |
| |
| API struct lyd_node * |
| lyd_parse_mem(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, ...) |
| { |
| va_list ap; |
| struct lyd_node *result; |
| |
| va_start(ap, options); |
| result = lyd_parse_data_(ctx, data, format, options, ap); |
| va_end(ap); |
| |
| return result; |
| } |
| |
| API struct lyd_node * |
| lyd_parse_fd(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, ...) |
| { |
| struct lyd_node *ret; |
| struct stat sb; |
| char *data; |
| va_list ap; |
| |
| if (!ctx || (fd == -1)) { |
| LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__); |
| return NULL; |
| } |
| |
| if (fstat(fd, &sb) == -1) { |
| LOGERR(LY_ESYS, "Failed to stat the file descriptor (%s).", strerror(errno)); |
| return NULL; |
| } |
| |
| data = mmap(NULL, sb.st_size + 1, PROT_READ, MAP_PRIVATE, fd, 0); |
| if (data == MAP_FAILED) { |
| LOGERR(LY_ESYS, "Mapping file descriptor into memory failed."); |
| return NULL; |
| } |
| |
| va_start(ap, options); |
| ret = lyd_parse_data_(ctx, data, format, options, ap); |
| |
| va_end(ap); |
| munmap(data, sb.st_size); |
| |
| return ret; |
| } |
| |
| API struct lyd_node * |
| lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options, ...) |
| { |
| int fd; |
| struct lyd_node *ret; |
| va_list ap; |
| |
| if (!ctx || !path) { |
| LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__); |
| return NULL; |
| } |
| |
| fd = open(path, O_RDONLY); |
| if (fd == -1) { |
| LOGERR(LY_ESYS, "Failed to open data file \"%s\" (%s).", path, strerror(errno)); |
| return NULL; |
| } |
| |
| va_start(ap, options); |
| ret = lyd_parse_fd(ctx, fd, format, options); |
| |
| va_end(ap); |
| close(fd); |
| |
| return ret; |
| } |
| |
| static struct lyd_node * |
| _lyd_new(struct lyd_node *parent, const struct lys_node *schema) |
| { |
| struct lyd_node *ret; |
| |
| ret = calloc(1, sizeof *ret); |
| if (!ret) { |
| LOGMEM; |
| return NULL; |
| } |
| ret->schema = (struct lys_node *)schema; |
| ret->validity = LYD_VAL_NOT; |
| ret->prev = ret; |
| if (parent) { |
| if (lyd_insert(parent, ret)) { |
| lyd_free(ret); |
| return NULL; |
| } |
| } |
| |
| return ret; |
| } |
| |
| API struct lyd_node * |
| lyd_new(struct lyd_node *parent, const struct lys_module *module, const char *name) |
| { |
| const struct lys_node *snode = NULL, *siblings; |
| |
| if ((!parent && !module) || !name) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| if (!parent) { |
| siblings = module->data; |
| } else { |
| if (!parent->schema) { |
| return NULL; |
| } |
| siblings = parent->schema->child; |
| } |
| |
| if (lys_get_data_sibling(module, siblings, name, LYS_CONTAINER | LYS_LIST | LYS_NOTIF | LYS_RPC, &snode) |
| || !snode) { |
| return NULL; |
| } |
| |
| return _lyd_new(parent, snode); |
| } |
| |
| static struct lyd_node * |
| lyd_create_leaf(const struct lys_node *schema, const char *val_str) |
| { |
| struct lyd_node_leaf_list *ret; |
| |
| ret = calloc(1, sizeof *ret); |
| if (!ret) { |
| LOGMEM; |
| return NULL; |
| } |
| ret->schema = (struct lys_node *)schema; |
| ret->validity = LYD_VAL_NOT; |
| ret->prev = (struct lyd_node *)ret; |
| ret->value_type = ((struct lys_node_leaf *)schema)->type.base; |
| ret->value_str = lydict_insert(schema->module->ctx, val_str, 0); |
| |
| /* resolve the type correctly */ |
| if (lyp_parse_value(ret, NULL, 1, NULL, 0)) { |
| lyd_free((struct lyd_node *)ret); |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| return (struct lyd_node *)ret; |
| } |
| |
| static struct lyd_node * |
| _lyd_new_leaf(struct lyd_node *parent, const struct lys_node *schema, const char *val_str) |
| { |
| struct lyd_node *ret; |
| |
| ret = lyd_create_leaf(schema, val_str); |
| if (!ret) { |
| return NULL; |
| } |
| |
| /* connect to parent */ |
| if (parent) { |
| if (lyd_insert(parent, ret)) { |
| lyd_free(ret); |
| return NULL; |
| } |
| } |
| |
| if (ret->schema->flags & LYS_UNIQUE) { |
| /* locate the first parent list */ |
| for (parent = ret->parent; parent && parent->schema->nodetype != LYS_LIST; parent = parent->parent); |
| |
| /* set flag for future validation */ |
| if (parent) { |
| parent->validity |= LYD_VAL_UNIQUE; |
| } |
| } |
| |
| return ret; |
| } |
| |
| API struct lyd_node * |
| lyd_new_leaf(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *val_str) |
| { |
| const struct lys_node *snode = NULL, *siblings; |
| |
| if ((!parent && !module) || !name) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| if (!parent) { |
| siblings = module->data; |
| } else { |
| if (!parent->schema) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| siblings = parent->schema->child; |
| } |
| |
| if (lys_get_data_sibling(module, siblings, name, LYS_LEAFLIST | LYS_LEAF, &snode) || !snode) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| return _lyd_new_leaf(parent, snode, val_str); |
| |
| } |
| |
| API int |
| lyd_change_leaf(struct lyd_node_leaf_list *leaf, const char *val_str) |
| { |
| const char *backup; |
| struct lyd_node *parent; |
| |
| if (!leaf) { |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| backup = leaf->value_str; |
| leaf->value_str = val_str; |
| |
| /* resolve the type correctly */ |
| if (lyp_parse_value(leaf, NULL, 1, NULL, 0)) { |
| leaf->value_str = backup; |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| /* value is correct, finish the changes in leaf */ |
| lydict_remove(leaf->schema->module->ctx, backup); |
| leaf->value_str = lydict_insert(leaf->schema->module->ctx, val_str, 0); |
| |
| if (leaf->schema->flags & LYS_UNIQUE) { |
| /* locate the first parent list */ |
| for (parent = leaf->parent; parent && parent->schema->nodetype != LYS_LIST; parent = parent->parent); |
| |
| /* set flag for future validation */ |
| if (parent) { |
| parent->validity |= LYD_VAL_UNIQUE; |
| } |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| static struct lyd_node * |
| lyd_create_anyxml(const struct lys_node *schema, const char *val_xml) |
| { |
| struct lyd_node_anyxml *ret; |
| struct lyxml_elem *root; |
| char *xml; |
| |
| ret = calloc(1, sizeof *ret); |
| if (!ret) { |
| LOGMEM; |
| return NULL; |
| } |
| ret->schema = (struct lys_node *)schema; |
| ret->validity = LYD_VAL_NOT; |
| ret->prev = (struct lyd_node *)ret; |
| |
| /* store the anyxml data together with the anyxml element */ |
| if (asprintf(&xml, "<%s>%s</%s>", schema->name, (val_xml ? val_xml : ""), schema->name) == -1) { |
| LOGMEM; |
| lyd_free((struct lyd_node *)ret); |
| return NULL; |
| } |
| root = lyxml_parse_mem(schema->module->ctx, xml, 0); |
| free(xml); |
| if (!root) { |
| lyd_free((struct lyd_node *)ret); |
| return NULL; |
| } |
| ret->value = root; |
| |
| return (struct lyd_node *)ret; |
| } |
| |
| static struct lyd_node * |
| _lyd_new_anyxml(struct lyd_node *parent, const struct lys_node *schema, const char *val_xml) |
| { |
| struct lyd_node *ret; |
| |
| ret = lyd_create_anyxml(schema, val_xml); |
| if (!ret) { |
| return NULL; |
| } |
| |
| /* connect to parent */ |
| if (parent) { |
| if (lyd_insert(parent, ret)) { |
| lyd_free(ret); |
| return NULL; |
| } |
| } |
| |
| return ret; |
| } |
| |
| API struct lyd_node * |
| lyd_new_anyxml(struct lyd_node *parent, const struct lys_module *module, const char *name, const char *val_xml) |
| { |
| const struct lys_node *siblings, *snode; |
| |
| if ((!parent && !module) || !name) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| if (!parent) { |
| siblings = module->data; |
| } else { |
| if (!parent->schema) { |
| return NULL; |
| } |
| siblings = parent->schema->child; |
| } |
| |
| if (lys_get_data_sibling(module, siblings, name, LYS_ANYXML, &snode) || !snode) { |
| return NULL; |
| } |
| |
| return _lyd_new_anyxml(parent, snode, val_xml); |
| } |
| |
| API struct lyd_node * |
| lyd_output_new(const struct lys_node *schema) |
| { |
| struct lyd_node *ret; |
| |
| if (!schema || !(schema->nodetype & (LYS_CONTAINER | LYS_LIST)) |
| || !lys_parent(schema) || (lys_parent(schema)->nodetype != LYS_OUTPUT)) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| ret = calloc(1, sizeof *ret); |
| if (!ret) { |
| LOGMEM; |
| return NULL; |
| } |
| ret->schema = (struct lys_node *)schema; |
| ret->validity = LYD_VAL_NOT; |
| ret->prev = ret; |
| |
| return ret; |
| } |
| |
| API struct lyd_node * |
| lyd_output_new_leaf(const struct lys_node *schema, const char *val_str) |
| { |
| if (!schema || (schema->nodetype != LYS_LEAF) |
| || !lys_parent(schema) || (lys_parent(schema)->nodetype != LYS_OUTPUT)) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| return lyd_create_leaf(schema, val_str); |
| } |
| |
| API struct lyd_node * |
| lyd_output_new_anyxml(const struct lys_node *schema, const char *val_xml) |
| { |
| if (!schema || (schema->nodetype != LYS_ANYXML) |
| || !lys_parent(schema) || (lys_parent(schema)->nodetype != LYS_OUTPUT)) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| return lyd_create_anyxml(schema, val_xml); |
| } |
| |
| static int |
| lyd_new_path_list_keys(struct lyd_node *list, const char *list_name, const char *predicate, int *parsed) |
| { |
| const char *name, *value; |
| char *key_val; |
| int r, i, nam_len, val_len, has_predicate = 1; |
| struct lys_node_list *slist; |
| |
| slist = (struct lys_node_list *)list->schema; |
| |
| for (i = 0; i < slist->keys_size; ++i) { |
| if (!has_predicate) { |
| LOGVAL(LYE_PATH_MISSKEY, 0, LY_VLOG_NONE, NULL, list_name); |
| return -1; |
| } |
| |
| if ((r = parse_schema_list_predicate(predicate, &name, &nam_len, &value, &val_len, &has_predicate)) < 1) { |
| LOGVAL(LYE_PATH_INCHAR, 0, LY_VLOG_NONE, NULL, predicate[-r], &predicate[-r]); |
| return -1; |
| } |
| *parsed += r; |
| predicate += r; |
| |
| if (strncmp(slist->keys[i]->name, name, nam_len) || slist->keys[i]->name[nam_len]) { |
| LOGVAL(LYE_PATH_INKEY, 0, LY_VLOG_NONE, NULL, name[0], name); |
| return -1; |
| } |
| |
| key_val = malloc((val_len + 1) * sizeof(char)); |
| if (!key_val) { |
| LOGMEM; |
| return -1; |
| } |
| strncpy(key_val, value, val_len); |
| key_val[val_len] = '\0'; |
| |
| if (!_lyd_new_leaf(list, (const struct lys_node *)slist->keys[i], key_val)) { |
| free(key_val); |
| return -1; |
| } |
| free(key_val); |
| } |
| |
| return 0; |
| } |
| |
| API struct lyd_node * |
| lyd_new_path(struct lyd_node *data_tree, struct ly_ctx *ctx, const char *path, const char *value, int options) |
| { |
| char module_name[LY_MODULE_NAME_MAX_LEN + 1]; |
| const char *mod_name, *name, *node_mod_name; |
| struct lyd_node *ret = NULL, *node, *parent = NULL; |
| const struct lys_node *schild, *sparent; |
| const struct lys_node_list *slist; |
| const struct lys_module *module, *prev_mod; |
| int r, i, parsed = 0, mod_name_len, nam_len, is_relative = -1; |
| |
| if (!path || (!data_tree && !ctx) || ((options & LYD_PATH_OPT_UPDATE) && !value) |
| || (data_tree && (data_tree->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML)))) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| if (data_tree) { |
| parent = resolve_partial_json_data_nodeid(path, data_tree, &parsed); |
| if (!parent) { |
| return NULL; |
| } |
| path += parsed; |
| if (parsed) { |
| /* if we parsed something we have a relative path for sure, otherwise we don't know */ |
| is_relative = 1; |
| } |
| |
| if (!path[0]) { |
| /* the node exists */ |
| if (!(options & LYD_PATH_OPT_UPDATE) || (parent->schema->nodetype != LYS_LEAF)) { |
| LOGVAL(LYE_PATH_EXISTS, 0, LY_VLOG_NONE, NULL); |
| return NULL; |
| } |
| |
| /* update leaf value */ |
| r = lyd_change_leaf((struct lyd_node_leaf_list *)parent, value); |
| if (r) { |
| return NULL; |
| } |
| return parent; |
| } |
| |
| sparent = parent->schema; |
| module = lys_node_module(sparent); |
| prev_mod = module; |
| } |
| |
| if ((r = parse_schema_nodeid(path, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL)) < 1) { |
| LOGVAL(LYE_PATH_INCHAR, 0, LY_VLOG_NONE, NULL, path[-r], &path[-r]); |
| return NULL; |
| } |
| |
| path += r; |
| |
| /* we are starting from scratch, absolute path */ |
| if (!data_tree) { |
| if (!mod_name) { |
| LOGVAL(LYE_PATH_MISSMOD, 0, LY_VLOG_NONE, NULL, name); |
| return NULL; |
| } else if (mod_name_len > LY_MODULE_NAME_MAX_LEN) { |
| LOGINT; |
| return NULL; |
| } |
| |
| strncpy(module_name, mod_name, mod_name_len); |
| module_name[mod_name_len] = '\0'; |
| module = ly_ctx_get_module(ctx, module_name, NULL); |
| if (!module) { |
| LOGVAL(LYE_PATH_INMOD, 0, LY_VLOG_NONE, NULL, mod_name); |
| return NULL; |
| } |
| mod_name = NULL; |
| mod_name_len = 0; |
| prev_mod = module; |
| |
| sparent = NULL; |
| } |
| |
| /* create nodes in a loop */ |
| while (1) { |
| /* find the schema node */ |
| schild = NULL; |
| while ((schild = lys_getnext(schild, sparent, module, 0))) { |
| if (schild->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_NOTIF | LYS_RPC)) { |
| /* module comparison */ |
| if (mod_name) { |
| node_mod_name = lys_node_module(schild)->name; |
| if (strncmp(node_mod_name, mod_name, mod_name_len) || node_mod_name[mod_name_len]) { |
| continue; |
| } |
| } else if (lys_node_module(schild) != prev_mod) { |
| continue; |
| } |
| |
| /* name check */ |
| if (!strncmp(schild->name, name, nam_len) && !schild->name[nam_len]) { |
| break; |
| } |
| } |
| } |
| |
| if (!schild) { |
| LOGVAL(LYE_PATH_INNODE, 0, LY_VLOG_NONE, NULL, name); |
| lyd_free(ret); |
| return NULL; |
| } |
| |
| /* we have the right schema node */ |
| switch (schild->nodetype) { |
| case LYS_CONTAINER: |
| case LYS_LIST: |
| case LYS_NOTIF: |
| case LYS_RPC: |
| node = _lyd_new(is_relative ? parent : NULL, schild); |
| break; |
| case LYS_LEAF: |
| case LYS_LEAFLIST: |
| if (path[0]) { |
| LOGVAL(LYE_PATH_INCHAR, 0, LY_VLOG_NONE, NULL, path[0], path); |
| return NULL; |
| } |
| node = _lyd_new_leaf(is_relative ? parent : NULL, schild, value); |
| break; |
| case LYS_ANYXML: |
| if (path[0]) { |
| LOGVAL(LYE_PATH_INCHAR, 0, LY_VLOG_NONE, NULL, path[0], path); |
| return NULL; |
| } |
| node = _lyd_new_anyxml(is_relative ? parent : NULL, schild, value); |
| break; |
| default: |
| LOGINT; |
| node = NULL; |
| break; |
| } |
| |
| if (!node) { |
| lyd_free(ret); |
| return NULL; |
| } |
| if (!ret) { |
| ret = node; |
| } |
| /* special case when we are creating a sibling of a top-level data node */ |
| if (!is_relative) { |
| if (data_tree) { |
| assert(data_tree == parent); |
| if (lyd_insert_before(data_tree, node)) { |
| lyd_free(ret); |
| return NULL; |
| } |
| } |
| is_relative = 1; |
| } |
| |
| parsed = 0; |
| if ((schild->nodetype == LYS_LIST) && lyd_new_path_list_keys(node, name, path, &parsed)) { |
| lyd_free(ret); |
| return NULL; |
| } |
| path += parsed; |
| |
| if (!path[0]) { |
| /* we are done */ |
| return ret; |
| } else if (!(options & LYD_PATH_OPT_RECURSIVE)) { |
| /* we were supposed to be done */ |
| LOGVAL(LYE_PATH_MISSPAR, 0, LY_VLOG_NONE, NULL, (mod_name ? mod_name : name)); |
| return NULL; |
| } |
| |
| /* prepare for another iteration */ |
| parent = node; |
| sparent = schild; |
| prev_mod = lys_node_module(schild); |
| |
| /* parse another node */ |
| if ((r = parse_schema_nodeid(path, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL)) < 1) { |
| LOGVAL(LYE_PATH_INCHAR, 0, LY_VLOG_NONE, NULL, path[-r], &path[-r]); |
| lyd_free(ret); |
| return NULL; |
| } |
| path += r; |
| |
| /* if a key of a list was supposed to be created, it is created as a part of the list instance |
| * creation and can cause several corner cases that can simply be avoided by forbidding this */ |
| if ((schild->nodetype == LYS_LIST) && !mod_name) { |
| slist = (const struct lys_node_list *)schild; |
| for (i = 0; i < slist->keys_size; ++i) { |
| if (!strncmp(slist->keys[i]->name, name, nam_len) && !slist->keys[i]->name[nam_len]) { |
| LOGVAL(LYE_PATH_EXISTS, 0, LY_VLOG_NONE, NULL); |
| lyd_free(ret); |
| return NULL; |
| } |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| static void |
| lyd_insert_setinvalid(struct lyd_node *node) |
| { |
| struct lyd_node *next, *elem, *parent_list; |
| |
| assert(node); |
| |
| /* overall validity of the node itself */ |
| node->validity = LYD_VAL_NOT; |
| |
| /* explore changed unique leafs */ |
| /* first, get know if there is a list in parents chain */ |
| for (parent_list = node->parent; |
| parent_list && parent_list->schema->nodetype != LYS_LIST; |
| parent_list = parent_list->parent); |
| if (parent_list && !(parent_list->validity & LYD_VAL_UNIQUE)) { |
| /* there is a list, so check if we inserted a leaf supposed to be unique */ |
| for (elem = node; elem; elem = next) { |
| if (elem->schema->nodetype == LYS_LIST) { |
| /* stop searching to the depth, children would be unique to a list in subtree */ |
| goto nextsibling; |
| } |
| |
| if (elem->schema->nodetype == LYS_LEAF && (elem->schema->flags & LYS_UNIQUE)) { |
| /* set flag to list for future validation */ |
| parent_list->validity |= LYD_VAL_UNIQUE; |
| break; |
| } |
| |
| if (elem->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML)) { |
| goto nextsibling; |
| } |
| |
| /* select next elem to process */ |
| /* go into children */ |
| next = elem->child; |
| /* got through siblings */ |
| if (!next) { |
| nextsibling: |
| next = elem->next; |
| if (!next) { |
| /* no children */ |
| if (elem == node) { |
| /* we are done, back in start node */ |
| break; |
| } |
| /* try siblings */ |
| next = elem->next; |
| } |
| } |
| /* go back to parents */ |
| while (!next) { |
| if (elem->parent == node) { |
| /* we are done, back in start node */ |
| break; |
| } |
| /* parent was actually already processed, so go to the parent's sibling */ |
| next = elem->parent->next; |
| } |
| } |
| } |
| } |
| |
| API int |
| lyd_insert(struct lyd_node *parent, struct lyd_node *node) |
| { |
| struct lys_node *sparent; |
| struct lyd_node *iter; |
| int invalid = 0; |
| |
| if (!node || !parent) { |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| /* check placing the node to the appropriate place according to the schema */ |
| for (sparent = lys_parent(node->schema); |
| sparent && !(sparent->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_RPC | LYS_OUTPUT | LYS_NOTIF)); |
| sparent = lys_parent(sparent)); |
| if (sparent != parent->schema) { |
| return EXIT_FAILURE; |
| } |
| |
| if (node->parent != parent || lyp_is_rpc(node->schema)) { |
| /* it is not just moving under a parent node or it is in an RPC where |
| * nodes order matters, so the validation will be necessary */ |
| invalid = 1; |
| } |
| |
| if (node->parent || node->prev->next) { |
| lyd_unlink(node); |
| } |
| |
| if (!parent->child) { |
| /* add as the only child of the parent */ |
| parent->child = node; |
| } else { |
| /* add as the last child of the parent */ |
| parent->child->prev->next = node; |
| node->prev = parent->child->prev; |
| for (iter = node; iter->next; iter = iter->next); |
| parent->child->prev = iter; |
| } |
| |
| LY_TREE_FOR(node, iter) { |
| iter->parent = parent; |
| if (invalid) { |
| lyd_insert_setinvalid(iter); |
| } |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| static int |
| lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, int before) |
| { |
| struct lys_node *par1, *par2; |
| struct lyd_node *iter; |
| int invalid = 0; |
| |
| if (sibling == node) { |
| return EXIT_SUCCESS; |
| } |
| |
| /* check placing the node to the appropriate place according to the schema */ |
| for (par1 = lys_parent(sibling->schema); |
| par1 && !(par1->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_INPUT | LYS_OUTPUT | LYS_NOTIF)); |
| par1 = lys_parent(par1)); |
| for (par2 = lys_parent(node->schema); |
| par2 && !(par2->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_INPUT | LYS_OUTPUT | LYS_NOTIF)); |
| par2 = lys_parent(par2)); |
| if (par1 != par2) { |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| if (node->parent != sibling->parent || !node->parent || lyp_is_rpc(node->schema)) { |
| /* a) it is not just moving under a parent node or |
| * b) it is top-level where we don't know if it is the same tree, or |
| * c) it is in an RPC where nodes order matters, |
| * so the validation will be necessary */ |
| if (!node->parent) { |
| /* b) search in siblings */ |
| for (iter = node->prev; iter != node; iter = iter->prev) { |
| if (iter == sibling) { |
| break; |
| } |
| } |
| if (iter == node) { |
| /* node and siblings are not currently in the same data tree */ |
| invalid = 1; |
| } |
| } else { /* a) and c) */ |
| invalid = 1; |
| } |
| } |
| |
| if (node->parent || node->next || node->prev->next) { |
| lyd_unlink(node); |
| } |
| |
| node->parent = sibling->parent; |
| if (invalid) { |
| lyd_insert_setinvalid(node); |
| } |
| |
| if (before) { |
| if (sibling->prev->next) { |
| /* adding into the list */ |
| sibling->prev->next = node; |
| } else if (sibling->parent) { |
| /* at the beginning */ |
| sibling->parent->child = node; |
| } |
| node->prev = sibling->prev; |
| sibling->prev = node; |
| node->next = sibling; |
| } else { |
| if (sibling->next) { |
| /* adding into a middle - fix the prev pointer of the node after inserted nodes */ |
| node->next = sibling->next; |
| sibling->next->prev = node; |
| } else { |
| /* at the end - fix the prev pointer of the first node */ |
| if (sibling->parent) { |
| sibling->parent->child->prev = node; |
| } else { |
| for (iter = sibling; iter->prev->next; iter = iter->prev); |
| iter->prev = node; |
| } |
| } |
| sibling->next = node; |
| node->prev = sibling; |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| API int |
| lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node) |
| { |
| if (!node || !sibling || lyd_insert_sibling(sibling, node, 1)) { |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| API int |
| lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node) |
| { |
| if (!node || !sibling || lyd_insert_sibling(sibling, node, 0)) { |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| API int |
| lyd_validate(struct lyd_node *node, int options, ...) |
| { |
| struct lyd_node *root, *next1, *next2, *iter, *to_free = NULL; |
| const struct lys_node *schema; |
| struct ly_ctx *ctx; |
| int i; |
| va_list ap; |
| |
| ly_errno = 0; |
| |
| if (!node) { |
| /* TODO what about LYD_OPT_NOTIF, LYD_OPT_RPC and LYD_OPT_RPCREPLY ? */ |
| if (options & (LYD_OPT_FILTER | LYD_OPT_EDIT | LYD_OPT_GET | LYD_OPT_GETCONFIG)) { |
| return EXIT_SUCCESS; |
| } |
| /* LYD_OPT_DATA || LYD_OPT_CONFIG */ |
| |
| /* get context with schemas from the variable arguments */ |
| va_start(ap, options); |
| ctx = va_arg(ap, struct ly_ctx*); |
| if (!ctx) { |
| LOGERR(LY_EINVAL, "%s: Invalid variable argument.", __func__); |
| va_end(ap); |
| return EXIT_FAILURE; |
| } |
| |
| /* check for missing mandatory elements according to schemas in context */ |
| for (i = 0; i < ctx->models.used; i++) { |
| if (!ctx->models.list[i]->data) { |
| continue; |
| } |
| schema = ly_check_mandatory(NULL, ctx->models.list[i]->data); |
| if (schema) { |
| if (schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) { |
| LOGVAL(LYE_TOOMANY, 0, LY_VLOG_LYS, schema, schema->name, schema->parent ? schema->parent->name : "module"); |
| LOGVAL(LYE_SPEC, 0, 0, NULL, |
| "Number of \"%s\" instances in \"%s\" does not follow min-elements constraint.", |
| schema->name, schema->parent ? schema->parent->name : ctx->models.list[i]->name); |
| } else { |
| LOGVAL(LYE_MISSELEM, 0, LY_VLOG_LYS, schema, |
| schema->name, schema->parent ? schema->parent->name : ctx->models.list[i]->name); |
| } |
| va_end(ap); |
| return EXIT_FAILURE; |
| |
| } |
| } |
| |
| va_end(ap); |
| return EXIT_SUCCESS; |
| } |
| |
| if (!(options & LYD_OPT_NOSIBLINGS)) { |
| /* check that the node is the first sibling */ |
| while(node->prev->next) { |
| node = node->prev; |
| } |
| } |
| |
| LY_TREE_FOR_SAFE(node, next1, root) { |
| LY_TREE_DFS_BEGIN(root, next2, iter) { |
| if (to_free) { |
| lyd_free(to_free); |
| to_free = NULL; |
| } |
| |
| if (lyv_data_context(iter, options, 0, NULL)) { |
| return EXIT_FAILURE; |
| } |
| if (lyv_data_value(iter, options)) { |
| return EXIT_FAILURE; |
| } |
| if (lyv_data_content(iter, options, 0, NULL)) { |
| if (ly_errno) { |
| return EXIT_FAILURE; |
| } else { |
| /* safe deferred removal */ |
| to_free = iter; |
| next2 = NULL; |
| goto nextsiblings; |
| } |
| } |
| |
| /* validation successful */ |
| iter->validity = LYD_VAL_OK; |
| |
| /* where go next? - modified LY_TREE_DFS_END */ |
| if (iter->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML)) { |
| next2 = NULL; |
| } else { |
| next2 = iter->child; |
| } |
| nextsiblings: |
| if (!next2) { |
| /* no children */ |
| if (iter == root) { |
| /* we are done */ |
| break; |
| } |
| /* try siblings */ |
| next2 = iter->next; |
| } |
| while (!next2) { |
| iter = iter->parent; |
| /* parent is already processed, go to its sibling */ |
| if (iter->parent == root->parent) { |
| /* we are done */ |
| break; |
| } |
| next2 = iter->next; |
| } /* end of modified LY_TREE_DFS_END */ |
| } |
| |
| if (to_free) { |
| if (node == to_free) { |
| /* we shouldn't be here */ |
| assert(0); |
| } |
| lyd_free(to_free); |
| to_free = NULL; |
| } |
| |
| if (options & LYD_OPT_NOSIBLINGS) { |
| break; |
| } |
| } |
| |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /* create an attribute copy */ |
| static struct lyd_attr * |
| lyd_dup_attr(struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_attr *attr) |
| { |
| struct lyd_attr *ret; |
| |
| /* allocate new attr */ |
| if (!parent->attr) { |
| parent->attr = malloc(sizeof *parent->attr); |
| ret = parent->attr; |
| } else { |
| for (ret = parent->attr; ret->next; ret = ret->next); |
| ret->next = malloc(sizeof *ret); |
| ret = ret->next; |
| } |
| if (!ret) { |
| LOGMEM; |
| return NULL; |
| } |
| |
| /* fill new attr except */ |
| ret->next = NULL; |
| ret->module = attr->module; |
| ret->name = lydict_insert(ctx, attr->name, 0); |
| ret->value = lydict_insert(ctx, attr->value, 0); |
| |
| return ret; |
| } |
| |
| API int |
| lyd_unlink(struct lyd_node *node) |
| { |
| struct lyd_node *iter, *next; |
| struct ly_set *set, *data; |
| unsigned int i, j; |
| |
| if (!node) { |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| /* fix leafrefs */ |
| LY_TREE_DFS_BEGIN(node, next, iter) { |
| /* the node is target of a leafref */ |
| if ((iter->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) && iter->schema->child) { |
| set = (struct ly_set *)iter->schema->child; |
| for (i = 0; i < set->number; i++) { |
| data = lyd_get_node2(iter, set->set.s[i]); |
| if (data) { |
| for (j = 0; j < data->number; j++) { |
| if (((struct lyd_node_leaf_list *)data->set.d[j])->value.leafref == iter) { |
| /* remove reference to the node we are going to replace */ |
| ((struct lyd_node_leaf_list *)data->set.d[j])->value.leafref = NULL; |
| } |
| } |
| ly_set_free(data); |
| } |
| } |
| } |
| LY_TREE_DFS_END(node, next, iter) |
| } |
| |
| /* 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; |
| } |
| node->parent = NULL; |
| } |
| |
| node->next = NULL; |
| node->prev = node; |
| |
| return EXIT_SUCCESS; |
| } |
| |
| API struct lyd_node * |
| lyd_dup(const struct lyd_node *node, int recursive) |
| { |
| const struct lyd_node *next, *elem; |
| struct lyd_node *ret, *parent, *new_node; |
| struct lyd_attr *attr; |
| struct lyd_node_leaf_list *new_leaf; |
| struct lyd_node_anyxml *new_axml; |
| struct lys_type *type; |
| |
| if (!node) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| ret = NULL; |
| parent = NULL; |
| |
| /* LY_TREE_DFS */ |
| for (elem = next = node; elem; elem = next) { |
| |
| /* fill specific part */ |
| switch (elem->schema->nodetype) { |
| case LYS_LEAF: |
| case LYS_LEAFLIST: |
| new_leaf = malloc(sizeof *new_leaf); |
| new_node = (struct lyd_node *)new_leaf; |
| if (!new_node) { |
| LOGMEM; |
| return NULL; |
| } |
| |
| new_leaf->value = ((struct lyd_node_leaf_list *)elem)->value; |
| new_leaf->value_str = lydict_insert(elem->schema->module->ctx, |
| ((struct lyd_node_leaf_list *)elem)->value_str, 0); |
| new_leaf->value_type = ((struct lyd_node_leaf_list *)elem)->value_type; |
| /* bits type must be treated specially */ |
| if (new_leaf->value_type == LY_TYPE_BITS) { |
| for (type = &((struct lys_node_leaf *)elem->schema)->type; type->der->module; type = &type->der->type) { |
| if (type->base != LY_TYPE_BITS) { |
| LOGINT; |
| lyd_free(new_node); |
| lyd_free(ret); |
| return NULL; |
| } |
| } |
| |
| new_leaf->value.bit = malloc(type->info.bits.count * sizeof *new_leaf->value.bit); |
| if (!new_leaf->value.bit) { |
| LOGMEM; |
| lyd_free(new_node); |
| lyd_free(ret); |
| return NULL; |
| } |
| memcpy(new_leaf->value.bit, ((struct lyd_node_leaf_list *)elem)->value.bit, |
| type->info.bits.count * sizeof *new_leaf->value.bit); |
| } |
| break; |
| case LYS_ANYXML: |
| new_axml = malloc(sizeof *new_axml); |
| new_node = (struct lyd_node *)new_axml; |
| if (!new_node) { |
| LOGMEM; |
| return NULL; |
| } |
| |
| new_axml->value = lyxml_dup_elem(elem->schema->module->ctx, ((struct lyd_node_anyxml *)elem)->value, |
| NULL, 1); |
| break; |
| case LYS_CONTAINER: |
| case LYS_LIST: |
| case LYS_NOTIF: |
| case LYS_RPC: |
| new_node = malloc(sizeof *new_node); |
| if (!new_node) { |
| LOGMEM; |
| return NULL; |
| } |
| new_node->child = NULL; |
| break; |
| default: |
| lyd_free(ret); |
| LOGINT; |
| return NULL; |
| } |
| |
| /* fill common part */ |
| new_node->schema = elem->schema; |
| new_node->attr = NULL; |
| LY_TREE_FOR(elem->attr, attr) { |
| lyd_dup_attr(elem->schema->module->ctx, new_node, attr); |
| } |
| new_node->next = NULL; |
| new_node->prev = new_node; |
| new_node->parent = NULL; |
| new_node->validity = LYD_VAL_NOT; |
| |
| if (!ret) { |
| ret = new_node; |
| } |
| if (parent) { |
| if (lyd_insert(parent, new_node)) { |
| lyd_free(ret); |
| LOGINT; |
| return NULL; |
| } |
| } |
| |
| if (!recursive) { |
| break; |
| } |
| |
| /* LY_TREE_DFS_END */ |
| /* select element for the next run - children first */ |
| next = elem->child; |
| /* child exception for lyd_node_leaf and lyd_node_leaflist */ |
| if (elem->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML)) { |
| next = NULL; |
| } |
| if (!next) { |
| /* no children, so try siblings */ |
| next = elem->next; |
| } else { |
| parent = new_node; |
| } |
| while (!next) { |
| /* no siblings, go back through parents */ |
| elem = elem->parent; |
| if (elem->parent == node->parent) { |
| break; |
| } |
| if (!parent) { |
| lyd_free(ret); |
| LOGINT; |
| return NULL; |
| } |
| parent = parent->parent; |
| /* parent is already processed, go to its sibling */ |
| next = elem->next; |
| } |
| } |
| |
| return ret; |
| } |
| |
| API void |
| lyd_free_attr(struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_attr *attr, int recursive) |
| { |
| struct lyd_attr *iter; |
| |
| if (!ctx || !attr) { |
| return; |
| } |
| |
| if (parent) { |
| if (parent->attr == attr) { |
| if (recursive) { |
| parent->attr = NULL; |
| } else { |
| parent->attr = attr->next; |
| } |
| } else { |
| for (iter = parent->attr; iter->next != attr; iter = iter->next); |
| if (iter->next) { |
| if (recursive) { |
| iter->next = NULL; |
| } else { |
| iter->next = attr->next; |
| } |
| } |
| } |
| } |
| |
| if (!recursive) { |
| attr->next = NULL; |
| } |
| |
| for(iter = attr; iter; ) { |
| attr = iter; |
| iter = iter->next; |
| |
| lydict_remove(ctx, attr->name); |
| lydict_remove(ctx, attr->value); |
| free(attr); |
| } |
| } |
| |
| struct lyd_node * |
| lyd_attr_parent(struct lyd_node *root, struct lyd_attr *attr) |
| { |
| struct lyd_node *next, *elem; |
| struct lyd_attr *node_attr; |
| |
| LY_TREE_DFS_BEGIN(root, next, elem) { |
| for (node_attr = elem->attr; node_attr; node_attr = node_attr->next) { |
| if (node_attr == attr) { |
| return elem; |
| } |
| } |
| LY_TREE_DFS_END(root, next, elem) |
| } |
| |
| return NULL; |
| } |
| |
| API struct lyd_attr * |
| lyd_insert_attr(struct lyd_node *parent, const char *name, const char *value) |
| { |
| struct lyd_attr *a, *iter; |
| struct ly_ctx *ctx; |
| const struct lys_module *module; |
| const char *p; |
| char *aux; |
| |
| if (!parent || !name || !value) { |
| return NULL; |
| } |
| ctx = parent->schema->module->ctx; |
| |
| if ((p = strchr(name, ':'))) { |
| /* search for the namespace */ |
| aux = strndup(name, p - name); |
| if (!aux) { |
| LOGMEM; |
| return NULL; |
| } |
| module = ly_ctx_get_module(ctx, aux, NULL); |
| free(aux); |
| name = p + 1; |
| |
| if (!module) { |
| /* module not found */ |
| LOGERR(LY_EINVAL, "Attribute prefix does not match any schema in the context."); |
| return NULL; |
| } |
| } else { |
| /* no prefix -> module is the same as for the parent */ |
| module = parent->schema->module; |
| } |
| |
| a = malloc(sizeof *a); |
| if (!a) { |
| LOGMEM; |
| return NULL; |
| } |
| a->module = (struct lys_module *)module; |
| a->next = NULL; |
| a->name = lydict_insert(ctx, name, 0); |
| a->value = lydict_insert(ctx, value, 0); |
| |
| if (!parent->attr) { |
| parent->attr = a; |
| } else { |
| for (iter = parent->attr; iter->next; iter = iter->next); |
| iter->next = a; |
| } |
| |
| return a; |
| } |
| |
| API void |
| lyd_free(struct lyd_node *node) |
| { |
| struct lyd_node *next, *iter; |
| |
| if (!node) { |
| return; |
| } |
| |
| if (!(node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) { |
| /* free children */ |
| LY_TREE_FOR_SAFE(node->child, next, iter) { |
| lyd_free(iter); |
| } |
| } else if (node->schema->nodetype == LYS_ANYXML) { |
| lyxml_free(node->schema->module->ctx, ((struct lyd_node_anyxml *)node)->value); |
| } else { /* LYS_LEAF | LYS_LEAFLIST */ |
| /* free value */ |
| switch (((struct lyd_node_leaf_list *)node)->value_type) { |
| case LY_TYPE_BINARY: |
| case LY_TYPE_STRING: |
| lydict_remove(node->schema->module->ctx, ((struct lyd_node_leaf_list *)node)->value.string); |
| break; |
| case LY_TYPE_BITS: |
| if (((struct lyd_node_leaf_list *)node)->value.bit) { |
| free(((struct lyd_node_leaf_list *)node)->value.bit); |
| } |
| break; |
| default: |
| lydict_remove(node->schema->module->ctx, ((struct lyd_node_leaf_list *)node)->value_str); |
| break; |
| } |
| } |
| |
| lyd_unlink(node); |
| lyd_free_attr(node->schema->module->ctx, node, node->attr, 1); |
| free(node); |
| } |
| |
| API void |
| lyd_free_withsiblings(struct lyd_node *node) |
| { |
| struct lyd_node *iter, *aux; |
| |
| if (!node) { |
| return; |
| } |
| |
| /* optimization - avoid freeing (unlinking) the last node of the siblings list */ |
| /* so, first, free the node's predecessors to the beginning of the list ... */ |
| for(iter = node->prev; iter->next; iter = aux) { |
| aux = iter->prev; |
| lyd_free(iter); |
| } |
| /* ... then, the node is the first in the siblings list, so free them all */ |
| LY_TREE_FOR_SAFE(node, aux, iter) { |
| lyd_free(iter); |
| } |
| } |
| |
| int |
| lyd_compare(struct lyd_node *first, struct lyd_node *second, int unique) |
| { |
| struct lys_node_list *slist; |
| const struct lys_node *snode = NULL; |
| struct lyd_node *diter; |
| const char *val1, *val2; |
| int i, j; |
| |
| assert(first); |
| assert(second); |
| |
| if (first->schema != second->schema) { |
| return 1; |
| } |
| |
| switch (first->schema->nodetype) { |
| case LYS_LEAFLIST: |
| /* compare values */ |
| if (ly_strequal(((struct lyd_node_leaf_list *)first)->value_str, |
| ((struct lyd_node_leaf_list *)second)->value_str, 1)) { |
| return 0; |
| } |
| return 1; |
| case LYS_LIST: |
| slist = (struct lys_node_list *)first->schema; |
| |
| if (unique) { |
| /* compare unique leafs */ |
| for (i = 0; i < slist->unique_size; i++) { |
| for (j = 0; j < slist->unique[i].expr_size; j++) { |
| /* first */ |
| diter = resolve_data_descendant_schema_nodeid(slist->unique[i].expr[j], first->child); |
| if (diter) { |
| val1 = ((struct lyd_node_leaf_list *)diter)->value_str; |
| } else { |
| /* use default value */ |
| if (resolve_descendant_schema_nodeid(slist->unique[i].expr[j], first->schema->child, LYS_LEAF, &snode)) { |
| /* error, but unique expression was checked when the schema was parsed */ |
| return -1; |
| } |
| val1 = ((struct lys_node_leaf *)snode)->dflt; |
| } |
| |
| /* second */ |
| diter = resolve_data_descendant_schema_nodeid(slist->unique[i].expr[j], second->child); |
| if (diter) { |
| val2 = ((struct lyd_node_leaf_list *)diter)->value_str; |
| } else { |
| /* use default value */ |
| if (resolve_descendant_schema_nodeid(slist->unique[i].expr[j], second->schema->child, LYS_LEAF, &snode)) { |
| /* error, but unique expression was checked when the schema was parsed */ |
| return -1; |
| } |
| val2 = ((struct lys_node_leaf *)snode)->dflt; |
| } |
| |
| if (!ly_strequal(val1, val2, 1)) { |
| break; |
| } |
| } |
| if (j && j == slist->unique[i].expr_size) { |
| /* all unique leafs are the same in this set */ |
| return 0; |
| } |
| } |
| } |
| |
| if (second->validity == LYD_VAL_UNIQUE) { |
| /* only unique part changed somewhere, so it is no need to check keys */ |
| return 0; |
| } |
| |
| /* compare keys */ |
| for (i = 0; i < slist->keys_size; i++) { |
| snode = (struct lys_node *)slist->keys[i]; |
| val1 = val2 = NULL; |
| LY_TREE_FOR(first->child, diter) { |
| if (diter->schema == snode) { |
| val1 = ((struct lyd_node_leaf_list *)diter)->value_str; |
| break; |
| } |
| } |
| LY_TREE_FOR(second->child, diter) { |
| if (diter->schema == snode) { |
| val2 = ((struct lyd_node_leaf_list *)diter)->value_str; |
| break; |
| } |
| } |
| if (!ly_strequal(val1, val2, 1)) { |
| return 1; |
| } |
| } |
| |
| return 0; |
| default: |
| /* no additional check is needed */ |
| return 0; |
| } |
| } |
| |
| API struct ly_set * |
| lyd_get_node(const struct lyd_node *data, const char *expr) |
| { |
| struct lyxp_set xp_set; |
| struct ly_set *set; |
| uint16_t i; |
| |
| if (!data || !expr) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| memset(&xp_set, 0, sizeof xp_set); |
| |
| if (lyxp_eval(expr, data, &xp_set, 0, 0) != EXIT_SUCCESS) { |
| return NULL; |
| } |
| |
| set = ly_set_new(); |
| if (!set) { |
| LOGMEM; |
| return NULL; |
| } |
| |
| if (xp_set.type == LYXP_SET_NODE_SET) { |
| for (i = 0; i < xp_set.used; ++i) { |
| if ((xp_set.node_type[i] == LYXP_NODE_ELEM) || (xp_set.node_type[i] == LYXP_NODE_TEXT)) { |
| if (ly_set_add(set, xp_set.value.nodes[i])) { |
| ly_set_free(set); |
| set = NULL; |
| break; |
| } |
| } |
| } |
| } |
| lyxp_set_cast(&xp_set, LYXP_SET_EMPTY, data, 0); |
| |
| return set; |
| } |
| |
| API struct ly_set * |
| lyd_get_node2(const struct lyd_node *data, const struct lys_node *schema) |
| { |
| struct ly_set *ret, *ret_aux, *spath; |
| const struct lys_node *siter; |
| struct lyd_node *iter; |
| unsigned int i, j; |
| |
| if (!data || !schema || |
| !(schema->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LIST | LYS_ANYXML | LYS_NOTIF | LYS_RPC))) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| ret = ly_set_new(); |
| spath = ly_set_new(); |
| if (!ret || !spath) { |
| LOGMEM; |
| goto error; |
| } |
| |
| /* find data root */ |
| while (data->parent) { |
| /* vertical move (up) */ |
| data = data->parent; |
| } |
| while (data->prev->next) { |
| /* horizontal move (left) */ |
| data = data->prev; |
| } |
| |
| /* build schema path */ |
| for (siter = schema; siter; ) { |
| if (siter->nodetype == LYS_AUGMENT) { |
| siter = ((struct lys_node_augment *)siter)->target; |
| continue; |
| } else if (siter->nodetype == LYS_OUTPUT) { |
| /* done for RPC reply */ |
| break; |
| } else if (siter->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LIST | LYS_ANYXML | LYS_NOTIF | LYS_RPC)) { |
| /* standard data node */ |
| ly_set_add(spath, (void*)siter); |
| |
| } /* else skip the rest node types */ |
| siter = siter->parent; |
| } |
| if (!spath->number) { |
| /* no valid path */ |
| goto error; |
| } |
| |
| /* start searching */ |
| LY_TREE_FOR((struct lyd_node *)data, iter) { |
| if (iter->schema == spath->set.s[spath->number - 1]) { |
| ly_set_add(ret, iter); |
| } |
| } |
| for (i = spath->number - 1; i; i--) { |
| if (!ret->number) { |
| /* nothing found */ |
| break; |
| } |
| |
| ret_aux = ly_set_new(); |
| if (!ret_aux) { |
| LOGMEM; |
| goto error; |
| } |
| for (j = 0; j < ret->number; j++) { |
| LY_TREE_FOR(ret->set.d[j]->child, iter) { |
| if (iter->schema == spath->set.s[i - 1]) { |
| ly_set_add(ret_aux, iter); |
| } |
| } |
| } |
| ly_set_free(ret); |
| ret = ret_aux; |
| } |
| |
| ly_set_free(spath); |
| return ret; |
| |
| error: |
| ly_set_free(ret); |
| ly_set_free(spath); |
| |
| return NULL; |
| } |
| |
| API struct ly_set * |
| lyd_get_list_keys(const struct lyd_node *list) |
| { |
| struct lyd_node *key; |
| struct lys_node_list *slist; |
| struct ly_set *set; |
| unsigned int i; |
| |
| if (!list || (list->schema->nodetype != LYS_LIST)) { |
| ly_errno = LY_EINVAL; |
| return NULL; |
| } |
| |
| slist = (struct lys_node_list *)list->schema; |
| |
| set = ly_set_new(); |
| if (!set) { |
| LOGMEM; |
| return NULL; |
| } |
| |
| for (i = 0; i < slist->keys_size; ++i) { |
| key = resolve_data_descendant_schema_nodeid(slist->keys[i]->name, list->child); |
| if (key) { |
| ly_set_add(set, key); |
| } |
| } |
| |
| return set; |
| } |
| |
| API struct ly_set * |
| ly_set_new(void) |
| { |
| return calloc(1, sizeof(struct ly_set)); |
| } |
| |
| API void |
| ly_set_free(struct ly_set *set) |
| { |
| if (!set) { |
| return; |
| } |
| |
| free(set->set.g); |
| free(set); |
| } |
| |
| API int |
| ly_set_add(struct ly_set *set, void *node) |
| { |
| unsigned int i; |
| void **new; |
| |
| if (!set || !node) { |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| /* search for duplication */ |
| for (i = 0; i < set->number; i++) { |
| if (set->set.g[i] == node) { |
| /* already in set */ |
| return EXIT_SUCCESS; |
| } |
| } |
| |
| if (set->size == set->number) { |
| new = realloc(set->set.g, (set->size + 8) * sizeof *(set->set.g)); |
| if (!new) { |
| LOGMEM; |
| return EXIT_FAILURE; |
| } |
| set->size += 8; |
| set->set.g = new; |
| } |
| |
| set->set.g[set->number++] = node; |
| |
| return EXIT_SUCCESS; |
| } |
| |
| API int |
| ly_set_rm_index(struct ly_set *set, unsigned int index) |
| { |
| if (!set || (index + 1) > set->number) { |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| if (index == set->number - 1) { |
| /* removing last item in set */ |
| set->set.g[index] = NULL; |
| } else { |
| /* removing item somewhere in a middle, so put there the last item */ |
| set->set.g[index] = set->set.g[set->number - 1]; |
| set->set.g[set->number - 1] = NULL; |
| } |
| set->number--; |
| |
| return EXIT_SUCCESS; |
| } |
| |
| API int |
| ly_set_rm(struct ly_set *set, void *node) |
| { |
| unsigned int i; |
| |
| if (!set || !node) { |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| /* get index */ |
| for (i = 0; i < set->number; i++) { |
| if (set->set.g[i] == node) { |
| break; |
| } |
| } |
| if (i == set->number) { |
| /* node is not in set */ |
| ly_errno = LY_EINVAL; |
| return EXIT_FAILURE; |
| } |
| |
| return ly_set_rm_index(set, i); |
| } |