blob: 122f9e9fe02bbf9183c4d1e81ca64e36e44d54fc [file] [log] [blame]
/**
* @file tree_schema.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief Manipulation with libyang schema 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 <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "common.h"
#include "context.h"
#include "parser.h"
#include "resolve.h"
#include "xml.h"
#include "xpath.h"
#include "xml_internal.h"
#include "tree_internal.h"
#include "validation.h"
#include "parser_yang.h"
static int lys_type_dup(struct lys_module *mod, struct lys_node *parent, struct lys_type *new, struct lys_type *old,
int tpdftype, struct unres_schema *unres);
API const struct lys_node *
lys_is_disabled(const struct lys_node *node, int recursive)
{
int i;
if (!node) {
return NULL;
}
check:
if (node->nodetype != LYS_INPUT && node->nodetype != LYS_OUTPUT) {
/* input/output does not have if-feature, so skip them */
/* check local if-features */
for (i = 0; i < node->iffeature_size; i++) {
if (!resolve_iffeature(&node->iffeature[i])) {
return node;
}
}
}
if (!recursive) {
return NULL;
}
/* go through parents */
if (node->nodetype == LYS_AUGMENT) {
/* go to parent actually means go to the target node */
node = ((struct lys_node_augment *)node)->target;
} else if (node->parent) {
node = node->parent;
} else {
return NULL;
}
if (recursive == 2) {
/* continue only if the node cannot have a data instance */
if (node->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST)) {
return NULL;
}
}
goto check;
}
int
lys_get_sibling(const struct lys_node *siblings, const char *mod_name, int mod_name_len, const char *name,
int nam_len, LYS_NODE type, const struct lys_node **ret)
{
const struct lys_node *node, *parent = NULL;
const struct lys_module *mod = NULL;
const char *node_mod_name;
assert(siblings && mod_name && name);
assert(!(type & (LYS_USES | LYS_GROUPING)));
/* fill the lengths in case the caller is so indifferent */
if (!mod_name_len) {
mod_name_len = strlen(mod_name);
}
if (!nam_len) {
nam_len = strlen(name);
}
while (siblings && (siblings->nodetype == LYS_USES)) {
siblings = siblings->child;
}
if (!siblings) {
/* unresolved uses */
return EXIT_FAILURE;
}
if (siblings->nodetype == LYS_GROUPING) {
for (node = siblings; (node->nodetype == LYS_GROUPING) && (node->prev != siblings); node = node->prev);
if (node->nodetype == LYS_GROUPING) {
/* we went through all the siblings, only groupings there - no valid sibling */
return EXIT_FAILURE;
}
/* update siblings to be valid */
siblings = node;
}
/* set parent correctly */
parent = lys_parent(siblings);
/* go up all uses */
while (parent && (parent->nodetype == LYS_USES)) {
parent = lys_parent(parent);
}
if (!parent) {
/* handle situation when there is a top-level uses referencing a foreign grouping */
for (node = siblings; lys_parent(node) && (node->nodetype == LYS_USES); node = lys_parent(node));
mod = lys_node_module(node);
}
/* try to find the node */
node = NULL;
while ((node = lys_getnext(node, parent, mod, LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHINOUT))) {
if (!type || (node->nodetype & type)) {
/* module name comparison */
node_mod_name = lys_node_module(node)->name;
if (!ly_strequal(node_mod_name, mod_name, 1) && (strncmp(node_mod_name, mod_name, mod_name_len) || node_mod_name[mod_name_len])) {
continue;
}
/* direct name check */
if (ly_strequal(node->name, name, 1) || (!strncmp(node->name, name, nam_len) && !node->name[nam_len])) {
if (ret) {
*ret = node;
}
return EXIT_SUCCESS;
}
}
}
return EXIT_FAILURE;
}
int
lys_get_data_sibling(const struct lys_module *mod, const struct lys_node *siblings, const char *name, int nam_len,
LYS_NODE type, const struct lys_node **ret)
{
const struct lys_node *node;
assert(siblings && name);
assert(!(type & (LYS_AUGMENT | LYS_USES | LYS_GROUPING | LYS_CHOICE | LYS_CASE | LYS_INPUT | LYS_OUTPUT)));
/* find the beginning */
while (siblings->prev->next) {
siblings = siblings->prev;
}
if (!mod) {
mod = siblings->module;
}
/* try to find the node */
node = NULL;
while ((node = lys_getnext(node, lys_parent(siblings), mod, 0))) {
if (!type || (node->nodetype & type)) {
/* module check */
if (lys_node_module(node) != lys_main_module(mod)) {
continue;
}
/* direct name check */
if (!strncmp(node->name, name, nam_len) && !node->name[nam_len]) {
if (ret) {
*ret = node;
}
return EXIT_SUCCESS;
}
}
}
return EXIT_FAILURE;
}
API const struct lys_node *
lys_getnext(const struct lys_node *last, const struct lys_node *parent, const struct lys_module *module, int options)
{
const struct lys_node *next;
struct lys_node **snode;
if (!last) {
/* first call */
/* get know where to start */
if (parent) {
/* schema subtree */
snode = lys_child(parent, LYS_UNKNOWN);
if (!snode) {
return NULL;
}
next = last = *snode;
} else {
/* top level data */
assert(module);
next = last = module->data;
}
} else if ((last->nodetype == LYS_USES) && (options & LYS_GETNEXT_INTOUSES) && last->child) {
/* continue with uses content */
next = last->child;
} else {
/* continue after the last returned value */
next = last->next;
}
repeat:
while (next && (next->nodetype == LYS_GROUPING)) {
if (options & LYS_GETNEXT_WITHGROUPING) {
return next;
}
next = next->next;
}
if (!next) { /* cover case when parent is augment */
if (!last || last->parent == parent || lys_parent(last) == parent) {
/* no next element */
return NULL;
}
last = lys_parent(last);
next = last->next;
goto repeat;
} else {
last = next;
}
switch (next->nodetype) {
case LYS_INPUT:
case LYS_OUTPUT:
if (options & LYS_GETNEXT_WITHINOUT) {
return next;
} else if (next->child) {
next = next->child;
} else {
next = next->next;
}
goto repeat;
case LYS_CASE:
if (options & LYS_GETNEXT_WITHCASE) {
return next;
} else if (next->child) {
next = next->child;
} else {
next = next->next;
}
goto repeat;
case LYS_USES:
/* go into */
if (options & LYS_GETNEXT_WITHUSES) {
return next;
} else if (next->child) {
next = next->child;
} else {
next = next->next;
}
goto repeat;
case LYS_RPC:
case LYS_ACTION:
case LYS_NOTIF:
case LYS_LEAF:
case LYS_ANYXML:
case LYS_ANYDATA:
case LYS_LIST:
case LYS_LEAFLIST:
return next;
case LYS_CONTAINER:
if (!((struct lys_node_container *)next)->presence && (options & LYS_GETNEXT_INTONPCONT)) {
if (next->child) {
/* go into */
next = next->child;
} else {
next = next->next;
}
goto repeat;
} else {
return next;
}
case LYS_CHOICE:
if (options & LYS_GETNEXT_WITHCHOICE) {
return next;
} else if (next->child) {
/* go into */
next = next->child;
} else {
next = next->next;
}
goto repeat;
default:
/* we should not be here */
return NULL;
}
}
void
lys_node_unlink(struct lys_node *node)
{
struct lys_node *parent, *first, **pp;
struct lys_module *main_module;
if (!node) {
return;
}
/* unlink from data model if necessary */
if (node->module) {
/* get main module with data tree */
main_module = lys_node_module(node);
if (main_module->data == node) {
main_module->data = node->next;
}
}
/* store pointers to important nodes */
parent = node->parent;
if (parent && (parent->nodetype == LYS_AUGMENT)) {
/* handle augments - first, unlink it from the augment parent ... */
if (parent->child == node) {
parent->child = node->next;
}
if (parent->flags & LYS_NOTAPPLIED) {
/* data are not connected in the target, so we cannot continue with the target as a parent */
parent = NULL;
} else {
/* data are connected in target, so we will continue with the target as a parent */
parent = ((struct lys_node_augment *)parent)->target;
}
}
/* unlink from parent */
if (parent) {
if (parent->nodetype == LYS_EXT) {
pp = (struct lys_node **)lys_ext_complex_get_substmt(lys_snode2stmt(node->nodetype),
(struct lys_ext_instance_complex*)parent, NULL);
if (*pp == node) {
*pp = node->next;
}
} else if (parent->child == node) {
parent->child = node->next;
}
node->parent = NULL;
}
/* unlink from siblings */
if (node->prev == node) {
/* there are no more siblings */
return;
}
if (node->next) {
node->next->prev = node->prev;
} else {
/* unlinking the last element */
if (parent) {
if (parent->nodetype == LYS_EXT) {
first = *(struct lys_node **)pp;
} else {
first = parent->child;
}
} else {
first = node;
while (first->prev->next) {
first = first->prev;
}
}
first->prev = node->prev;
}
if (node->prev->next) {
node->prev->next = node->next;
}
/* clean up the unlinked element */
node->next = NULL;
node->prev = node;
}
struct lys_node_grp *
lys_find_grouping_up(const char *name, struct lys_node *start)
{
struct lys_node *par_iter, *iter, *stop;
for (par_iter = start; par_iter; par_iter = par_iter->parent) {
/* top-level augment, look into module (uses augment is handled correctly below) */
if (par_iter->parent && !par_iter->parent->parent && (par_iter->parent->nodetype == LYS_AUGMENT)) {
par_iter = par_iter->parent->module->data;
if (!par_iter) {
break;
}
}
if (par_iter->parent && (par_iter->parent->nodetype & (LYS_CHOICE | LYS_CASE | LYS_AUGMENT | LYS_USES))) {
continue;
}
for (iter = par_iter, stop = NULL; iter; iter = iter->prev) {
if (!stop) {
stop = par_iter;
} else if (iter == stop) {
break;
}
if (iter->nodetype != LYS_GROUPING) {
continue;
}
if (!strcmp(name, iter->name)) {
return (struct lys_node_grp *)iter;
}
}
}
return NULL;
}
/*
* get next grouping in the root's subtree, in the
* first call, tha last is NULL
*/
static struct lys_node_grp *
lys_get_next_grouping(struct lys_node_grp *lastgrp, struct lys_node *root)
{
struct lys_node *last = (struct lys_node *)lastgrp;
struct lys_node *next;
assert(root);
if (!last) {
last = root;
}
while (1) {
if ((last->nodetype & (LYS_CONTAINER | LYS_CHOICE | LYS_LIST | LYS_GROUPING | LYS_INPUT | LYS_OUTPUT))) {
next = last->child;
} else {
next = NULL;
}
if (!next) {
if (last == root) {
/* we are done */
return NULL;
}
/* no children, go to siblings */
next = last->next;
}
while (!next) {
/* go back through parents */
if (lys_parent(last) == root) {
/* we are done */
return NULL;
}
next = last->next;
last = lys_parent(last);
}
if (next->nodetype == LYS_GROUPING) {
return (struct lys_node_grp *)next;
}
last = next;
}
}
/* logs directly */
int
lys_check_id(struct lys_node *node, struct lys_node *parent, struct lys_module *module)
{
struct lys_node *start, *stop, *iter;
struct lys_node_grp *grp;
int down, up;
assert(node);
if (!parent) {
assert(module);
} else {
module = parent->module;
}
switch (node->nodetype) {
case LYS_GROUPING:
/* 6.2.1, rule 6 */
if (parent) {
start = *lys_child(parent, LYS_GROUPING);
if (!start) {
down = 0;
start = parent;
} else {
down = 1;
}
if (parent->nodetype == LYS_EXT) {
up = 0;
} else {
up = 1;
}
} else {
down = up = 1;
start = module->data;
}
/* go up */
if (up && lys_find_grouping_up(node->name, start)) {
LOGVAL(LYE_DUPID, LY_VLOG_LYS, node, "grouping", node->name);
return EXIT_FAILURE;
}
/* go down, because grouping can be defined after e.g. container in which is collision */
if (down) {
for (iter = start, stop = NULL; iter; iter = iter->prev) {
if (!stop) {
stop = start;
} else if (iter == stop) {
break;
}
if (!(iter->nodetype & (LYS_CONTAINER | LYS_CHOICE | LYS_LIST | LYS_GROUPING | LYS_INPUT | LYS_OUTPUT))) {
continue;
}
grp = NULL;
while ((grp = lys_get_next_grouping(grp, iter))) {
if (ly_strequal(node->name, grp->name, 1)) {
LOGVAL(LYE_DUPID,LY_VLOG_LYS, node, "grouping", node->name);
return EXIT_FAILURE;
}
}
}
}
break;
case LYS_LEAF:
case LYS_LEAFLIST:
case LYS_LIST:
case LYS_CONTAINER:
case LYS_CHOICE:
case LYS_ANYDATA:
/* 6.2.1, rule 7 */
if (parent) {
iter = parent;
while (iter && (iter->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE | LYS_AUGMENT))) {
if (iter->nodetype == LYS_AUGMENT) {
if (((struct lys_node_augment *)iter)->target) {
/* augment is resolved, go up */
iter = ((struct lys_node_augment *)iter)->target;
continue;
}
/* augment is not resolved, this is the final parent */
break;
}
iter = iter->parent;
}
if (!iter) {
stop = NULL;
iter = module->data;
} else if (iter->nodetype == LYS_EXT) {
stop = iter;
iter = *lys_child(iter, node->nodetype);
} else {
stop = iter;
iter = iter->child;
}
} else {
stop = NULL;
iter = module->data;
}
while (iter) {
if (iter->nodetype & (LYS_USES | LYS_CASE)) {
iter = iter->child;
continue;
}
if (iter->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CONTAINER | LYS_CHOICE | LYS_ANYDATA)) {
if (iter->module == node->module && ly_strequal(iter->name, node->name, 1)) {
LOGVAL(LYE_DUPID, LY_VLOG_LYS, node, strnodetype(node->nodetype), node->name);
return EXIT_FAILURE;
}
}
/* special case for choice - we must check the choice's name as
* well as the names of nodes under the choice
*/
if (iter->nodetype == LYS_CHOICE) {
iter = iter->child;
continue;
}
/* go to siblings */
if (!iter->next) {
/* no sibling, go to parent's sibling */
do {
/* for parent LYS_AUGMENT */
if (iter->parent == stop) {
iter = stop;
break;
}
iter = lys_parent(iter);
if (iter && iter->next) {
break;
}
} while (iter != stop);
if (iter == stop) {
break;
}
}
iter = iter->next;
}
break;
case LYS_CASE:
/* 6.2.1, rule 8 */
if (parent) {
start = *lys_child(parent, LYS_CASE);
} else {
start = module->data;
}
LY_TREE_FOR(start, iter) {
if (!(iter->nodetype & (LYS_ANYDATA | LYS_CASE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST))) {
continue;
}
if (iter->module == node->module && ly_strequal(iter->name, node->name, 1)) {
LOGVAL(LYE_DUPID, LY_VLOG_LYS, node, "case", node->name);
return EXIT_FAILURE;
}
}
break;
default:
/* no check needed */
break;
}
return EXIT_SUCCESS;
}
/* logs directly */
int
lys_node_addchild(struct lys_node *parent, struct lys_module *module, struct lys_node *child)
{
struct lys_node *iter, *next, **pchild;
struct lys_node_inout *in, *out, *inout;
int type;
void *p;
struct lyext_substmt *info = NULL;
assert(child);
if (parent) {
type = parent->nodetype;
module = parent->module;
} else {
assert(module);
assert(!(child->nodetype & (LYS_INPUT | LYS_OUTPUT)));
type = 0;
}
/* checks */
switch (type) {
case LYS_CONTAINER:
case LYS_LIST:
case LYS_GROUPING:
case LYS_USES:
if (!(child->nodetype &
(LYS_ANYDATA | LYS_CHOICE | LYS_CONTAINER | LYS_GROUPING | LYS_LEAF |
LYS_LEAFLIST | LYS_LIST | LYS_USES | LYS_ACTION | LYS_NOTIF))) {
LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, parent, strnodetype(child->nodetype), strnodetype(parent->nodetype));
return EXIT_FAILURE;
}
break;
case LYS_INPUT:
case LYS_OUTPUT:
case LYS_NOTIF:
if (!(child->nodetype &
(LYS_ANYDATA | LYS_CHOICE | LYS_CONTAINER | LYS_GROUPING | LYS_LEAF |
LYS_LEAFLIST | LYS_LIST | LYS_USES))) {
LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, parent, strnodetype(child->nodetype), strnodetype(parent->nodetype));
return EXIT_FAILURE;
}
break;
case LYS_CHOICE:
if (!(child->nodetype &
(LYS_ANYDATA | LYS_CASE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CHOICE))) {
LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, parent, strnodetype(child->nodetype), "choice");
return EXIT_FAILURE;
}
break;
case LYS_CASE:
if (!(child->nodetype &
(LYS_ANYDATA | LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_USES))) {
LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, parent, strnodetype(child->nodetype), "case");
return EXIT_FAILURE;
}
break;
case LYS_RPC:
case LYS_ACTION:
if (!(child->nodetype & (LYS_INPUT | LYS_OUTPUT | LYS_GROUPING))) {
LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, parent, strnodetype(child->nodetype), "rpc");
return EXIT_FAILURE;
}
break;
case LYS_LEAF:
case LYS_LEAFLIST:
case LYS_ANYXML:
case LYS_ANYDATA:
LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, parent, strnodetype(child->nodetype), strnodetype(parent->nodetype));
LOGVAL(LYE_SPEC, LY_VLOG_PREV, NULL, "The \"%s\" statement cannot have any data substatement.",
strnodetype(parent->nodetype));
return EXIT_FAILURE;
case LYS_AUGMENT:
if (!(child->nodetype &
(LYS_ANYDATA | LYS_CASE | LYS_CHOICE | LYS_CONTAINER | LYS_LEAF
| LYS_LEAFLIST | LYS_LIST | LYS_USES | LYS_ACTION | LYS_NOTIF))) {
LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, parent, strnodetype(child->nodetype), strnodetype(parent->nodetype));
return EXIT_FAILURE;
}
break;
case LYS_UNKNOWN:
/* top level */
if (!(child->nodetype &
(LYS_ANYDATA | LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_GROUPING
| LYS_LEAFLIST | LYS_LIST | LYS_USES | LYS_RPC | LYS_NOTIF | LYS_AUGMENT))) {
LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, parent, strnodetype(child->nodetype), "(sub)module");
return EXIT_FAILURE;
}
break;
case LYS_EXT:
/* plugin-defined */
p = lys_ext_complex_get_substmt(lys_snode2stmt(child->nodetype), (struct lys_ext_instance_complex*)parent, &info);
if (!p) {
LOGVAL(LYE_INCHILDSTMT, LY_VLOG_LYS, parent, strnodetype(child->nodetype),
((struct lys_ext_instance_complex*)parent)->def->name);
return EXIT_FAILURE;
}
/* TODO check cardinality */
break;
}
/* check identifier uniqueness */
if (lys_check_id(child, parent, module)) {
return EXIT_FAILURE;
}
if (child->parent) {
lys_node_unlink(child);
}
if ((child->nodetype & (LYS_INPUT | LYS_OUTPUT)) && parent->nodetype != LYS_EXT) {
/* replace the implicit input/output node */
if (child->nodetype == LYS_OUTPUT) {
inout = (struct lys_node_inout *)parent->child->next;
} else { /* LYS_INPUT */
inout = (struct lys_node_inout *)parent->child;
parent->child = child;
}
if (inout->next) {
child->next = inout->next;
inout->next->prev = child;
inout->next = NULL;
} else {
parent->child->prev = child;
}
child->prev = inout->prev;
if (inout->prev->next) {
inout->prev->next = child;
}
inout->prev = (struct lys_node *)inout;
child->parent = parent;
inout->parent = NULL;
lys_node_free((struct lys_node *)inout, NULL, 0);
} else {
/* connect the child correctly */
if (!parent) {
if (module->data) {
module->data->prev->next = child;
child->prev = module->data->prev;
module->data->prev = child;
} else {
module->data = child;
}
} else {
next = NULL;
pchild = lys_child(parent, child->nodetype);
assert(pchild);
if (!(*pchild)) {
/* the only/first child of the parent */
*pchild = child;
child->parent = parent;
iter = child;
} else if (type == LYS_AUGMENT) {
/* add a new child as a last child of the augment (no matter if applied or not) */
for (iter = (*pchild)->prev; iter->parent != parent; iter = iter->prev);
next = iter->next;
iter->next = child;
child->prev = iter;
} else {
/* add a new child at the end of parent's child list */
iter = (*pchild)->prev;
iter->next = child;
child->prev = iter;
}
while (iter->next) {
iter = iter->next;
iter->parent = parent;
}
if (next) {
/* we are in applied augment, its target has some additional nodes after the nodes from this augment */
iter->next = next;
next->prev = iter;
} else {
(*pchild)->prev = iter;
}
}
}
/* check config value (but ignore them in groupings and augments) */
for (iter = parent; iter && !(iter->nodetype & (LYS_GROUPING | LYS_AUGMENT | LYS_EXT)); iter = iter->parent);
if (parent && !iter) {
for (iter = child; iter && !(iter->nodetype & (LYS_NOTIF | LYS_INPUT | LYS_OUTPUT | LYS_RPC)); iter = iter->parent);
if (!iter && (parent->flags & LYS_CONFIG_R) && (child->flags & LYS_CONFIG_W)) {
LOGVAL(LYE_INARG, LY_VLOG_LYS, child, "true", "config");
LOGVAL(LYE_SPEC, LY_VLOG_PREV, NULL, "State nodes cannot have configuration nodes as children.");
return EXIT_FAILURE;
}
}
/* propagate information about status data presence */
if ((child->nodetype & (LYS_CONTAINER | LYS_CHOICE | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA)) &&
(child->flags & LYS_INCL_STATUS)) {
for(iter = parent; iter; iter = lys_parent(iter)) {
/* store it only into container or list - the only data inner nodes */
if (iter->nodetype & (LYS_CONTAINER | LYS_LIST)) {
if (iter->flags & LYS_INCL_STATUS) {
/* done, someone else set it already from here */
break;
}
/* set flag about including status data */
iter->flags |= LYS_INCL_STATUS;
}
}
}
/* create implicit input/output nodes to have available them as possible target for augment */
if (child->nodetype & (LYS_RPC | LYS_ACTION)) {
in = calloc(1, sizeof *in);
in->nodetype = LYS_INPUT;
in->name = lydict_insert(child->module->ctx, "input", 5);
out = calloc(1, sizeof *out);
out->name = lydict_insert(child->module->ctx, "output", 6);
out->nodetype = LYS_OUTPUT;
in->module = out->module = child->module;
in->parent = out->parent = child;
in->flags = out->flags = LYS_IMPLICIT;
in->next = (struct lys_node *)out;
in->prev = (struct lys_node *)out;
out->prev = (struct lys_node *)in;
child->child = (struct lys_node *)in;
}
return EXIT_SUCCESS;
}
static const struct lys_module *
lys_parse_mem_(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, int internal)
{
char *enlarged_data = NULL;
struct lys_module *mod = NULL;
unsigned int len;
ly_err_clean(1);
if (!ctx || !data) {
LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
return NULL;
}
if (!internal && format == LYS_IN_YANG) {
/* enlarge data by 2 bytes for flex */
len = strlen(data);
enlarged_data = malloc((len + 2) * sizeof *enlarged_data);
if (!enlarged_data) {
LOGMEM;
return NULL;
}
memcpy(enlarged_data, data, len);
enlarged_data[len] = enlarged_data[len + 1] = '\0';
data = enlarged_data;
}
switch (format) {
case LYS_IN_YIN:
mod = yin_read_module(ctx, data, NULL, 1);
break;
case LYS_IN_YANG:
mod = yang_read_module(ctx, data, 0, NULL, 1);
break;
default:
LOGERR(LY_EINVAL, "Invalid schema input format.");
break;
}
free(enlarged_data);
return mod;
}
API const struct lys_module *
lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format)
{
return lys_parse_mem_(ctx, data, format, 0);
}
struct lys_submodule *
lys_sub_parse_mem(struct lys_module *module, const char *data, LYS_INFORMAT format, struct unres_schema *unres)
{
char *enlarged_data = NULL;
struct lys_submodule *submod = NULL;
unsigned int len;
assert(module);
assert(data);
if (format == LYS_IN_YANG) {
/* enlarge data by 2 bytes for flex */
len = strlen(data);
enlarged_data = malloc((len + 2) * sizeof *enlarged_data);
if (!enlarged_data) {
LOGMEM;
return NULL;
}
memcpy(enlarged_data, data, len);
enlarged_data[len] = enlarged_data[len + 1] = '\0';
data = enlarged_data;
}
/* get the main module */
module = lys_main_module(module);
switch (format) {
case LYS_IN_YIN:
submod = yin_read_submodule(module, data, unres);
break;
case LYS_IN_YANG:
submod = yang_read_submodule(module, data, 0, unres);
break;
default:
assert(0);
break;
}
free(enlarged_data);
return submod;
}
API const struct lys_module *
lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format)
{
int fd;
const struct lys_module *ret;
const char *rev, *dot, *filename;
size_t len;
if (!ctx || !path) {
LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
return NULL;
}
fd = open(path, O_RDONLY);
if (fd == -1) {
LOGERR(LY_ESYS, "Opening file \"%s\" failed (%s).", path, strerror(errno));
return NULL;
}
ret = lys_parse_fd(ctx, fd, format);
close(fd);
if (!ret) {
/* error */
return NULL;
}
/* check that name and revision match filename */
filename = strrchr(path, '/');
if (!filename) {
filename = path;
} else {
filename++;
}
rev = strchr(filename, '@');
dot = strrchr(filename, '.');
/* name */
len = strlen(ret->name);
if (strncmp(filename, ret->name, len) ||
((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
LOGWRN("File name \"%s\" does not match module name \"%s\".", filename, ret->name);
}
if (rev) {
len = dot - ++rev;
if (!ret->rev_size || len != 10 || strncmp(ret->rev[0].date, rev, len)) {
LOGWRN("File name \"%s\" does not match module revision \"%s\".", filename,
ret->rev_size ? ret->rev[0].date : "none");
}
}
if (!ret->filepath) {
/* store URI */
((struct lys_module *)ret)->filepath = lydict_insert(ctx, path, 0);
}
return ret;
}
API const struct lys_module *
lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format)
{
const struct lys_module *module;
size_t length;
char *addr;
char buf[PATH_MAX];
int len;
if (!ctx || fd < 0) {
LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
return NULL;
}
addr = lyp_mmap(fd, format == LYS_IN_YANG ? 1 : 0, &length);
if (addr == MAP_FAILED) {
LOGERR(LY_ESYS, "Mapping file descriptor into memory failed (%s()).", __func__);
return NULL;
} else if (!addr) {
LOGERR(LY_EINVAL, "Empty schema file.");
return NULL;
}
module = lys_parse_mem_(ctx, addr, format, 1);
lyp_munmap(addr, length);
if (module && !module->filepath) {
/* get URI if there is /proc */
addr = NULL;
if (asprintf(&addr, "/proc/self/fd/%d", fd) != -1) {
if ((len = readlink(addr, buf, PATH_MAX - 1)) > 0) {
((struct lys_module *)module)->filepath = lydict_insert(ctx, buf, len);
}
free(addr);
}
}
return module;
}
struct lys_submodule *
lys_sub_parse_fd(struct lys_module *module, int fd, LYS_INFORMAT format, struct unres_schema *unres)
{
struct lys_submodule *submodule;
size_t length;
char *addr;
assert(module);
assert(fd >= 0);
addr = lyp_mmap(fd, format == LYS_IN_YANG ? 1 : 0, &length);
if (addr == MAP_FAILED) {
LOGERR(LY_ESYS, "Mapping file descriptor into memory failed (%s()).", __func__);
return NULL;
} else if (!addr) {
LOGERR(LY_EINVAL, "Empty submodule schema file.");
return NULL;
}
/* get the main module */
module = lys_main_module(module);
switch (format) {
case LYS_IN_YIN:
submodule = yin_read_submodule(module, addr, unres);
break;
case LYS_IN_YANG:
submodule = yang_read_submodule(module, addr, 0, unres);
break;
default:
assert(0);
break;
}
lyp_munmap(addr, length);
return submodule;
}
int
lys_ext_iter(struct lys_ext_instance **ext, uint8_t ext_size, uint8_t start, LYEXT_SUBSTMT substmt)
{
unsigned int u;
for (u = start; u < ext_size; u++) {
if (ext[u]->insubstmt == substmt) {
return u;
}
}
return -1;
}
/*
* duplicate extension instance
*/
int
lys_ext_dup(struct lys_module *mod, struct lys_ext_instance **orig, uint8_t size,
void *parent, LYEXT_PAR parent_type, struct lys_ext_instance ***new, struct unres_schema *unres)
{
int i;
uint8_t u = 0;
struct lys_ext_instance **result;
struct unres_ext *info, *info_orig;
assert(new);
if (!size) {
if (orig) {
LOGINT;
return EXIT_FAILURE;
}
(*new) = NULL;
return EXIT_SUCCESS;
}
(*new) = result = calloc(size, sizeof *result);
for (u = 0; u < size; u++) {
/* TODO cover complex extension instances */
if (orig[u]) {
/* resolved extension instance, just duplicate it */
switch(lys_ext_instance_type(orig[u])) {
case LYEXT_FLAG:
result[u] = malloc(sizeof(struct lys_ext_instance));
break;
case LYEXT_COMPLEX:
result[u] = calloc(1, ((struct lyext_plugin_complex*)orig[u]->def->plugin)->instance_size);
((struct lys_ext_instance_complex*)result[u])->nodetype = LYS_EXT;
((struct lys_ext_instance_complex*)result[u])->module = mod;
((struct lys_ext_instance_complex*)result[u])->substmt = ((struct lyext_plugin_complex*)orig[u]->def->plugin)->substmt;
/* TODO duplicate data in extension instance content */
break;
case LYEXT_ERR:
LOGINT;
break;
}
/* generic part */
result[u]->def = orig[u]->def;
result[u]->flags = 0;
result[u]->arg_value = lydict_insert(mod->ctx, orig[u]->arg_value, 0);
result[u]->parent = parent;
result[u]->parent_type = parent_type;
result[u]->insubstmt = orig[u]->insubstmt;
result[u]->insubstmt_index = orig[u]->insubstmt_index;
/* extensions */
orig[u]->ext = NULL;
result[u]->ext_size = orig[u]->ext_size;
if (lys_ext_dup(mod, orig[u]->ext, orig[u]->ext_size, result[u],
LYEXT_PAR_EXTINST, &result[u]->ext, unres)) {
goto error;
}
} else {
/* original extension is not yet resolved, so duplicate it in unres */
i = unres_schema_find(unres, -1, &orig, UNRES_EXT);
if (i == -1) {
/* extension not found in unres */
LOGINT;
goto error;
}
info_orig = unres->str_snode[i];
info = malloc(sizeof *info);
info->datatype = info_orig->datatype;
if (info->datatype == LYS_IN_YIN) {
info->data.yin = lyxml_dup_elem(mod->ctx, info_orig->data.yin, NULL, 1);
} /* else TODO YANG */
info->parent = parent;
info->mod = mod;
info->parent_type = parent_type;
info->ext_index = u;
if (unres_schema_add_node(info->mod, unres, new, UNRES_EXT, (struct lys_node *)info) == -1) {
goto error;
}
}
}
return EXIT_SUCCESS;
error:
(*new) = NULL;
lys_extension_instances_free(mod->ctx, result, u);
return EXIT_FAILURE;
}
static struct lys_restr *
lys_restr_dup(struct lys_module *mod, struct lys_restr *old, int size, struct unres_schema *unres)
{
struct lys_restr *result;
int i;
if (!size) {
return NULL;
}
result = calloc(size, sizeof *result);
if (!result) {
LOGMEM;
return NULL;
}
for (i = 0; i < size; i++) {
result[i].ext_size = old[i].ext_size;
lys_ext_dup(mod, old[i].ext, old[i].ext_size, &result[i], LYEXT_PAR_RESTR, &result[i].ext, unres);
result[i].expr = lydict_insert(mod->ctx, old[i].expr, 0);
result[i].dsc = lydict_insert(mod->ctx, old[i].dsc, 0);
result[i].ref = lydict_insert(mod->ctx, old[i].ref, 0);
result[i].eapptag = lydict_insert(mod->ctx, old[i].eapptag, 0);
result[i].emsg = lydict_insert(mod->ctx, old[i].emsg, 0);
}
return result;
}
void
lys_restr_free(struct ly_ctx *ctx, struct lys_restr *restr)
{
assert(ctx);
if (!restr) {
return;
}
lys_extension_instances_free(ctx, restr->ext, restr->ext_size);
lydict_remove(ctx, restr->expr);
lydict_remove(ctx, restr->dsc);
lydict_remove(ctx, restr->ref);
lydict_remove(ctx, restr->eapptag);
lydict_remove(ctx, restr->emsg);
}
void
lys_iffeature_free(struct ly_ctx *ctx, struct lys_iffeature *iffeature, uint8_t iffeature_size)
{
uint8_t i;
for (i = 0; i < iffeature_size; ++i) {
lys_extension_instances_free(ctx, iffeature[i].ext, iffeature[i].ext_size);
free(iffeature[i].expr);
free(iffeature[i].features);
}
free(iffeature);
}
static int
type_dup(struct lys_module *mod, struct lys_node *parent, struct lys_type *new, struct lys_type *old,
LY_DATA_TYPE base, int tpdftype, struct unres_schema *unres)
{
int i;
switch (base) {
case LY_TYPE_BINARY:
if (old->info.binary.length) {
new->info.binary.length = lys_restr_dup(mod, old->info.binary.length, 1, unres);
}
break;
case LY_TYPE_BITS:
new->info.bits.count = old->info.bits.count;
if (new->info.bits.count) {
new->info.bits.bit = calloc(new->info.bits.count, sizeof *new->info.bits.bit);
if (!new->info.bits.bit) {
LOGMEM;
return -1;
}
for (i = 0; i < new->info.bits.count; i++) {
new->info.bits.bit[i].name = lydict_insert(mod->ctx, old->info.bits.bit[i].name, 0);
new->info.bits.bit[i].dsc = lydict_insert(mod->ctx, old->info.bits.bit[i].dsc, 0);
new->info.bits.bit[i].ref = lydict_insert(mod->ctx, old->info.bits.bit[i].ref, 0);
new->info.bits.bit[i].flags = old->info.bits.bit[i].flags;
new->info.bits.bit[i].pos = old->info.bits.bit[i].pos;
new->info.bits.bit[i].ext_size = old->info.bits.bit[i].ext_size;
if (lys_ext_dup(mod, old->info.bits.bit[i].ext, old->info.bits.bit[i].ext_size,
&new->info.bits.bit[i], LYEXT_PAR_TYPE_BIT,
&new->info.bits.bit[i].ext, unres)) {
return -1;
}
}
}
break;
case LY_TYPE_DEC64:
new->info.dec64.dig = old->info.dec64.dig;
new->info.dec64.div = old->info.dec64.div;
if (old->info.dec64.range) {
new->info.dec64.range = lys_restr_dup(mod, old->info.dec64.range, 1, unres);
}
break;
case LY_TYPE_ENUM:
new->info.enums.count = old->info.enums.count;
if (new->info.enums.count) {
new->info.enums.enm = calloc(new->info.enums.count, sizeof *new->info.enums.enm);
if (!new->info.enums.enm) {
LOGMEM;
return -1;
}
for (i = 0; i < new->info.enums.count; i++) {
new->info.enums.enm[i].name = lydict_insert(mod->ctx, old->info.enums.enm[i].name, 0);
new->info.enums.enm[i].dsc = lydict_insert(mod->ctx, old->info.enums.enm[i].dsc, 0);
new->info.enums.enm[i].ref = lydict_insert(mod->ctx, old->info.enums.enm[i].ref, 0);
new->info.enums.enm[i].flags = old->info.enums.enm[i].flags;
new->info.enums.enm[i].value = old->info.enums.enm[i].value;
new->info.enums.enm[i].ext_size = old->info.enums.enm[i].ext_size;
if (lys_ext_dup(mod, old->info.enums.enm[i].ext, old->info.enums.enm[i].ext_size,
&new->info.enums.enm[i], LYEXT_PAR_TYPE_ENUM,
&new->info.enums.enm[i].ext, unres)) {
return -1;
}
}
}
break;
case LY_TYPE_IDENT:
new->info.ident.count = old->info.ident.count;
if (old->info.ident.count) {
new->info.ident.ref = malloc(old->info.ident.count * sizeof *new->info.ident.ref);
if (!new->info.ident.ref) {
LOGMEM;
return -1;
}
memcpy(new->info.ident.ref, old->info.ident.ref, old->info.ident.count * sizeof *new->info.ident.ref);
} else {
/* there can be several unresolved base identities, duplicate them all */
i = -1;
do {
i = unres_schema_find(unres, i, old, UNRES_TYPE_IDENTREF);
if (i != -1) {
if (unres_schema_add_str(mod, unres, new, UNRES_TYPE_IDENTREF, unres->str_snode[i]) == -1) {
return -1;
}
}
--i;
} while (i > -1);
}
break;
case LY_TYPE_INST:
new->info.inst.req = old->info.inst.req;
break;
case LY_TYPE_INT8:
case LY_TYPE_INT16:
case LY_TYPE_INT32:
case LY_TYPE_INT64:
case LY_TYPE_UINT8:
case LY_TYPE_UINT16:
case LY_TYPE_UINT32:
case LY_TYPE_UINT64:
if (old->info.num.range) {
new->info.num.range = lys_restr_dup(mod, old->info.num.range, 1, unres);
}
break;
case LY_TYPE_LEAFREF:
if (old->info.lref.path) {
new->info.lref.path = lydict_insert(mod->ctx, old->info.lref.path, 0);
if (!tpdftype && unres_schema_add_node(mod, unres, new, UNRES_TYPE_LEAFREF, parent) == -1) {
return -1;
}
}
break;
case LY_TYPE_STRING:
if (old->info.str.length) {
new->info.str.length = lys_restr_dup(mod, old->info.str.length, 1, unres);
}
new->info.str.patterns = lys_restr_dup(mod, old->info.str.patterns, old->info.str.pat_count, unres);
new->info.str.pat_count = old->info.str.pat_count;
break;
case LY_TYPE_UNION:
new->info.uni.count = old->info.uni.count;
if (new->info.uni.count) {
new->info.uni.types = calloc(new->info.uni.count, sizeof *new->info.uni.types);
if (!new->info.uni.types) {
LOGMEM;
return -1;
}
for (i = 0; i < new->info.uni.count; i++) {
if (lys_type_dup(mod, parent, &(new->info.uni.types[i]), &(old->info.uni.types[i]), tpdftype, unres)) {
return -1;
}
}
}
break;
default:
/* nothing to do for LY_TYPE_BOOL, LY_TYPE_EMPTY */
break;
}
return EXIT_SUCCESS;
}
struct yang_type *
lys_yang_type_dup(struct lys_module *module, struct lys_node *parent, struct yang_type *old, struct lys_type *type,
int tpdftype, struct unres_schema *unres)
{
struct yang_type *new;
new = calloc(1, sizeof *new);
if (!new) {
LOGMEM;
return NULL;
}
new->flags = old->flags;
new->base = old->base;
new->name = lydict_insert(module->ctx, old->name, 0);
new->type = type;
if (!new->name) {
LOGMEM;
goto error;
}
if (type_dup(module, parent, type, old->type, new->base, tpdftype, unres)) {
new->type->base = new->base;
lys_type_free(module->ctx, new->type);
memset(&new->type->info, 0, sizeof new->type->info);
goto error;
}
return new;
error:
free(new);
return NULL;
}
API const void *
lys_ext_instance_substmt(const struct lys_ext_instance *ext)
{
if (!ext) {
return NULL;
}
switch (ext->insubstmt) {
case LYEXT_SUBSTMT_SELF:
case LYEXT_SUBSTMT_MODIFIER:
case LYEXT_SUBSTMT_VERSION:
return NULL;
case LYEXT_SUBSTMT_ARGUMENT:
if (ext->parent_type == LYEXT_PAR_EXT) {
return ((struct lys_ext_instance*)ext->parent)->arg_value;
}
break;
case LYEXT_SUBSTMT_BASE:
if (ext->parent_type == LYEXT_PAR_TYPE) {
return ((struct lys_type*)ext->parent)->info.ident.ref[ext->insubstmt_index];
} else if (ext->parent_type == LYEXT_PAR_IDENT) {
return ((struct lys_ident*)ext->parent)->base[ext->insubstmt_index];
}
break;
case LYEXT_SUBSTMT_BELONGSTO:
if (ext->parent_type == LYEXT_PAR_MODULE && ((struct lys_module*)ext->parent)->type) {
return ((struct lys_submodule*)ext->parent)->belongsto;
}
break;
case LYEXT_SUBSTMT_CONFIG:
case LYEXT_SUBSTMT_MANDATORY:
if (ext->parent_type == LYEXT_PAR_NODE) {
return &((struct lys_node*)ext->parent)->flags;
} else if (ext->parent_type == LYEXT_PAR_DEVIATE) {
return &((struct lys_deviate*)ext->parent)->flags;
} else if (ext->parent_type == LYEXT_PAR_REFINE) {
return &((struct lys_refine*)ext->parent)->flags;
}
break;
case LYEXT_SUBSTMT_CONTACT:
if (ext->parent_type == LYEXT_PAR_MODULE) {
return ((struct lys_module*)ext->parent)->contact;
}
break;
case LYEXT_SUBSTMT_DEFAULT:
if (ext->parent_type == LYEXT_PAR_NODE) {
switch (((struct lys_node*)ext->parent)->nodetype) {
case LYS_LEAF:
case LYS_LEAFLIST:
/* in case of leaf, the index is supposed to be 0, so it will return the
* correct pointer despite the leaf structure does not have dflt as array */
return ((struct lys_node_leaflist*)ext->parent)->dflt[ext->insubstmt_index];
case LYS_CHOICE:
return ((struct lys_node_choice*)ext->parent)->dflt;
default:
/* internal error */
break;
}
} else if (ext->parent_type == LYEXT_PAR_TPDF) {
return ((struct lys_tpdf*)ext->parent)->dflt;
} else if (ext->parent_type == LYEXT_PAR_DEVIATE) {
return ((struct lys_deviate*)ext->parent)->dflt[ext->insubstmt_index];
} else if (ext->parent_type == LYEXT_PAR_REFINE) {
return &((struct lys_refine*)ext->parent)->dflt[ext->insubstmt_index];
}
break;
case LYEXT_SUBSTMT_DESCRIPTION:
switch (ext->parent_type) {
case LYEXT_PAR_NODE:
return ((struct lys_node*)ext->parent)->dsc;
case LYEXT_PAR_MODULE:
return ((struct lys_module*)ext->parent)->dsc;
case LYEXT_PAR_IMPORT:
return ((struct lys_import*)ext->parent)->dsc;
case LYEXT_PAR_INCLUDE:
return ((struct lys_include*)ext->parent)->dsc;
case LYEXT_PAR_EXT:
return ((struct lys_ext*)ext->parent)->dsc;
case LYEXT_PAR_FEATURE:
return ((struct lys_feature*)ext->parent)->dsc;
case LYEXT_PAR_TPDF:
return ((struct lys_tpdf*)ext->parent)->dsc;
case LYEXT_PAR_TYPE_BIT:
return ((struct lys_type_bit*)ext->parent)->dsc;
case LYEXT_PAR_TYPE_ENUM:
return ((struct lys_type_enum*)ext->parent)->dsc;
case LYEXT_PAR_RESTR:
return ((struct lys_restr*)ext->parent)->dsc;
case LYEXT_PAR_WHEN:
return ((struct lys_when*)ext->parent)->dsc;
case LYEXT_PAR_IDENT:
return ((struct lys_ident*)ext->parent)->dsc;
case LYEXT_PAR_DEVIATION:
return ((struct lys_deviation*)ext->parent)->dsc;
case LYEXT_PAR_REVISION:
return ((struct lys_revision*)ext->parent)->dsc;
case LYEXT_PAR_REFINE:
return ((struct lys_refine*)ext->parent)->dsc;
default:
break;
}
break;
case LYEXT_SUBSTMT_ERRTAG:
if (ext->parent_type == LYEXT_PAR_RESTR) {
return ((struct lys_restr*)ext->parent)->eapptag;
}
break;
case LYEXT_SUBSTMT_ERRMSG:
if (ext->parent_type == LYEXT_PAR_RESTR) {
return ((struct lys_restr*)ext->parent)->emsg;
}
break;
case LYEXT_SUBSTMT_DIGITS:
if (ext->parent_type == LYEXT_PAR_TYPE && ((struct lys_type*)ext->parent)->base == LY_TYPE_DEC64) {
return &((struct lys_type*)ext->parent)->info.dec64.dig;
}
break;
case LYEXT_SUBSTMT_KEY:
if (ext->parent_type == LYEXT_PAR_NODE && ((struct lys_node*)ext->parent)->nodetype == LYS_LIST) {
return ((struct lys_node_list*)ext->parent)->keys;
}
break;
case LYEXT_SUBSTMT_MAX:
if (ext->parent_type == LYEXT_PAR_NODE) {
if (((struct lys_node*)ext->parent)->nodetype == LYS_LIST) {
return &((struct lys_node_list*)ext->parent)->max;
} else if (((struct lys_node*)ext->parent)->nodetype == LYS_LEAFLIST) {
return &((struct lys_node_leaflist*)ext->parent)->max;
}
} else if (ext->parent_type == LYEXT_PAR_REFINE) {
return &((struct lys_refine*)ext->parent)->mod.list.max;
}
break;
case LYEXT_SUBSTMT_MIN:
if (ext->parent_type == LYEXT_PAR_NODE) {
if (((struct lys_node*)ext->parent)->nodetype == LYS_LIST) {
return &((struct lys_node_list*)ext->parent)->min;
} else if (((struct lys_node*)ext->parent)->nodetype == LYS_LEAFLIST) {
return &((struct lys_node_leaflist*)ext->parent)->min;
}
} else if (ext->parent_type == LYEXT_PAR_REFINE) {
return &((struct lys_refine*)ext->parent)->mod.list.min;
}
break;
case LYEXT_SUBSTMT_NAMESPACE:
if (ext->parent_type == LYEXT_PAR_MODULE && !((struct lys_module*)ext->parent)->type) {
return ((struct lys_module*)ext->parent)->ns;
}
break;
case LYEXT_SUBSTMT_ORDEREDBY:
if (ext->parent_type == LYEXT_PAR_NODE &&
(((struct lys_node*)ext->parent)->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
return &((struct lys_node_list*)ext->parent)->flags;
}
break;
case LYEXT_SUBSTMT_ORGANIZATION:
if (ext->parent_type == LYEXT_PAR_MODULE) {
return ((struct lys_module*)ext->parent)->org;
}
break;
case LYEXT_SUBSTMT_PATH:
if (ext->parent_type == LYEXT_PAR_TYPE && ((struct lys_type*)ext->parent)->base == LY_TYPE_LEAFREF) {
return ((struct lys_type*)ext->parent)->info.lref.path;
}
break;
case LYEXT_SUBSTMT_POSITION:
if (ext->parent_type == LYEXT_PAR_TYPE_BIT) {
return &((struct lys_type_bit*)ext->parent)->pos;
}
break;
case LYEXT_SUBSTMT_PREFIX:
if (ext->parent_type == LYEXT_PAR_MODULE) {
/* covers also lys_submodule */
return ((struct lys_module*)ext->parent)->prefix;
} else if (ext->parent_type == LYEXT_PAR_IMPORT) {
return ((struct lys_import*)ext->parent)->prefix;
}
break;
case LYEXT_SUBSTMT_PRESENCE:
if (ext->parent_type == LYEXT_PAR_NODE && ((struct lys_node*)ext->parent)->nodetype == LYS_CONTAINER) {
return ((struct lys_node_container*)ext->parent)->presence;
} else if (ext->parent_type == LYEXT_PAR_REFINE) {
return ((struct lys_refine*)ext->parent)->mod.presence;
}
break;
case LYEXT_SUBSTMT_REFERENCE:
switch (ext->parent_type) {
case LYEXT_PAR_NODE:
return ((struct lys_node*)ext->parent)->ref;
case LYEXT_PAR_MODULE:
return ((struct lys_module*)ext->parent)->ref;
case LYEXT_PAR_IMPORT:
return ((struct lys_import*)ext->parent)->ref;
case LYEXT_PAR_INCLUDE:
return ((struct lys_include*)ext->parent)->ref;
case LYEXT_PAR_EXT:
return ((struct lys_ext*)ext->parent)->ref;
case LYEXT_PAR_FEATURE:
return ((struct lys_feature*)ext->parent)->ref;
case LYEXT_PAR_TPDF:
return ((struct lys_tpdf*)ext->parent)->ref;
case LYEXT_PAR_TYPE_BIT:
return ((struct lys_type_bit*)ext->parent)->ref;
case LYEXT_PAR_TYPE_ENUM:
return ((struct lys_type_enum*)ext->parent)->ref;
case LYEXT_PAR_RESTR:
return ((struct lys_restr*)ext->parent)->ref;
case LYEXT_PAR_WHEN:
return ((struct lys_when*)ext->parent)->ref;
case LYEXT_PAR_IDENT:
return ((struct lys_ident*)ext->parent)->ref;
case LYEXT_PAR_DEVIATION:
return ((struct lys_deviation*)ext->parent)->ref;
case LYEXT_PAR_REVISION:
return ((struct lys_revision*)ext->parent)->ref;
case LYEXT_PAR_REFINE:
return ((struct lys_refine*)ext->parent)->ref;
default:
break;
}
break;
case LYEXT_SUBSTMT_REQINSTANCE:
if (ext->parent_type == LYEXT_PAR_TYPE) {
if (((struct lys_type*)ext->parent)->base == LY_TYPE_LEAFREF) {
return &((struct lys_type*)ext->parent)->info.lref.req;
} else if (((struct lys_type*)ext->parent)->base == LY_TYPE_INST) {
return &((struct lys_type*)ext->parent)->info.inst.req;
}
}
break;
case LYEXT_SUBSTMT_REVISIONDATE:
if (ext->parent_type == LYEXT_PAR_IMPORT) {
return ((struct lys_import*)ext->parent)->rev;
} else if (ext->parent_type == LYEXT_PAR_INCLUDE) {
return ((struct lys_include*)ext->parent)->rev;
}
break;
case LYEXT_SUBSTMT_STATUS:
switch (ext->parent_type) {
case LYEXT_PAR_NODE:
case LYEXT_PAR_IDENT:
case LYEXT_PAR_TPDF:
case LYEXT_PAR_EXT:
case LYEXT_PAR_FEATURE:
case LYEXT_PAR_TYPE_ENUM:
case LYEXT_PAR_TYPE_BIT:
/* in all structures the flags member is at the same offset */
return &((struct lys_node*)ext->parent)->flags;
default:
break;
}
break;
case LYEXT_SUBSTMT_UNIQUE:
if (ext->parent_type == LYEXT_PAR_DEVIATE) {
return &((struct lys_deviate*)ext->parent)->unique[ext->insubstmt_index];
} else if (ext->parent_type == LYEXT_PAR_NODE && ((struct lys_node*)ext->parent)->nodetype == LYS_LIST) {
return &((struct lys_node_list*)ext->parent)->unique[ext->insubstmt_index];
}
break;
case LYEXT_SUBSTMT_UNITS:
if (ext->parent_type == LYEXT_PAR_NODE &&
(((struct lys_node*)ext->parent)->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
/* units is at the same offset in both lys_node_leaf and lys_node_leaflist */
return ((struct lys_node_leaf*)ext->parent)->units;
} else if (ext->parent_type == LYEXT_PAR_TPDF) {
return ((struct lys_tpdf*)ext->parent)->units;
} else if (ext->parent_type == LYEXT_PAR_DEVIATE) {
return ((struct lys_deviate*)ext->parent)->units;
}
break;
case LYEXT_SUBSTMT_VALUE:
if (ext->parent_type == LYEXT_PAR_TYPE_ENUM) {
return &((struct lys_type_enum*)ext->parent)->value;
}
break;
case LYEXT_SUBSTMT_YINELEM:
if (ext->parent_type == LYEXT_PAR_EXT) {
return &((struct lys_ext*)ext->parent)->flags;
}
break;
}
LOGINT;
return NULL;
}
static int
lys_type_dup(struct lys_module *mod, struct lys_node *parent, struct lys_type *new, struct lys_type *old,
int tpdftype, struct unres_schema *unres)
{
int i;
new->module_name = lydict_insert(mod->ctx, old->module_name, 0);
new->base = old->base;
new->der = old->der;
new->parent = (struct lys_tpdf *)parent;
new->ext_size = old->ext_size;
if (lys_ext_dup(mod, old->ext, old->ext_size, new, LYEXT_PAR_TYPE, &new->ext, unres)) {
return -1;
}
i = unres_schema_find(unres, -1, old, tpdftype ? UNRES_TYPE_DER_TPDF : UNRES_TYPE_DER);
if (i != -1) {
/* HACK (serious one) for unres */
/* nothing else we can do but duplicate it immediately */
if (((struct lyxml_elem *)old->der)->flags & LY_YANG_STRUCTURE_FLAG) {
new->der = (struct lys_tpdf *)lys_yang_type_dup(mod, parent, (struct yang_type *)old->der, new, tpdftype, unres);
} else {
new->der = (struct lys_tpdf *)lyxml_dup_elem(mod->ctx, (struct lyxml_elem *)old->der, NULL, 1);
}
/* all these unres additions can fail even though they did not before */
if (!new->der || unres_schema_add_node(mod, unres, new,
tpdftype ? UNRES_TYPE_DER_TPDF : UNRES_TYPE_DER, parent) == -1) {
return -1;
}
return EXIT_SUCCESS;
}
return type_dup(mod, parent, new, old, new->base, tpdftype, unres);
}
void
lys_type_free(struct ly_ctx *ctx, struct lys_type *type)
{
int i;
assert(ctx);
if (!type) {
return;
}
lydict_remove(ctx, type->module_name);
lys_extension_instances_free(ctx, type->ext, type->ext_size);
switch (type->base) {
case LY_TYPE_BINARY:
lys_restr_free(ctx, type->info.binary.length);
free(type->info.binary.length);
break;
case LY_TYPE_BITS:
for (i = 0; i < type->info.bits.count; i++) {
lydict_remove(ctx, type->info.bits.bit[i].name);
lydict_remove(ctx, type->info.bits.bit[i].dsc);
lydict_remove(ctx, type->info.bits.bit[i].ref);
lys_iffeature_free(ctx, type->info.bits.bit[i].iffeature, type->info.bits.bit[i].iffeature_size);
lys_extension_instances_free(ctx, type->info.bits.bit[i].ext, type->info.bits.bit[i].ext_size);
}
free(type->info.bits.bit);
break;
case LY_TYPE_DEC64:
lys_restr_free(ctx, type->info.dec64.range);
free(type->info.dec64.range);
break;
case LY_TYPE_ENUM:
for (i = 0; i < type->info.enums.count; i++) {
lydict_remove(ctx, type->info.enums.enm[i].name);
lydict_remove(ctx, type->info.enums.enm[i].dsc);
lydict_remove(ctx, type->info.enums.enm[i].ref);
lys_iffeature_free(ctx, type->info.enums.enm[i].iffeature, type->info.enums.enm[i].iffeature_size);
lys_extension_instances_free(ctx, type->info.enums.enm[i].ext, type->info.enums.enm[i].ext_size);
}
free(type->info.enums.enm);
break;
case LY_TYPE_INT8:
case LY_TYPE_INT16:
case LY_TYPE_INT32:
case LY_TYPE_INT64:
case LY_TYPE_UINT8:
case LY_TYPE_UINT16:
case LY_TYPE_UINT32:
case LY_TYPE_UINT64:
lys_restr_free(ctx, type->info.num.range);
free(type->info.num.range);
break;
case LY_TYPE_LEAFREF:
lydict_remove(ctx, type->info.lref.path);
break;
case LY_TYPE_STRING:
lys_restr_free(ctx, type->info.str.length);
free(type->info.str.length);
for (i = 0; i < type->info.str.pat_count; i++) {
lys_restr_free(ctx, &type->info.str.patterns[i]);
}
free(type->info.str.patterns);
break;
case LY_TYPE_UNION:
for (i = 0; i < type->info.uni.count; i++) {
lys_type_free(ctx, &type->info.uni.types[i]);
}
free(type->info.uni.types);
break;
case LY_TYPE_IDENT:
free(type->info.ident.ref);
break;
default:
/* nothing to do for LY_TYPE_INST, LY_TYPE_BOOL, LY_TYPE_EMPTY */
break;
}
}
static void
lys_tpdf_free(struct ly_ctx *ctx, struct lys_tpdf *tpdf)
{
assert(ctx);
if (!tpdf) {
return;
}
lydict_remove(ctx, tpdf->name);
lydict_remove(ctx, tpdf->dsc);
lydict_remove(ctx, tpdf->ref);
lys_type_free(ctx, &tpdf->type);
lydict_remove(ctx, tpdf->units);
lydict_remove(ctx, tpdf->dflt);
lys_extension_instances_free(ctx, tpdf->ext, tpdf->ext_size);
}
static struct lys_when *
lys_when_dup(struct lys_module *mod, struct lys_when *old, struct unres_schema *unres)
{
struct lys_when *new;
if (!old) {
return NULL;
}
new = calloc(1, sizeof *new);
if (!new) {
LOGMEM;
return NULL;
}
new->cond = lydict_insert(mod->ctx, old->cond, 0);
new->dsc = lydict_insert(mod->ctx, old->dsc, 0);
new->ref = lydict_insert(mod->ctx, old->ref, 0);
new->ext_size = old->ext_size;
lys_ext_dup(mod, old->ext, old->ext_size, new, LYEXT_PAR_WHEN, &new->ext, unres);
return new;
}
void
lys_when_free(struct ly_ctx *ctx, struct lys_when *w)
{
if (!w) {
return;
}
lys_extension_instances_free(ctx, w->ext, w->ext_size);
lydict_remove(ctx, w->cond);
lydict_remove(ctx, w->dsc);
lydict_remove(ctx, w->ref);
free(w);
}
static void
lys_augment_free(struct ly_ctx *ctx, struct lys_node_augment *aug, void (*private_destructor)(const struct lys_node *node, void *priv))
{
struct lys_node *next, *sub;
/* children from a resolved augment are freed under the target node */
if (!aug->target || (aug->flags & LYS_NOTAPPLIED)) {
LY_TREE_FOR_SAFE(aug->child, next, sub) {
lys_node_free(sub, private_destructor, 0);
}
}
lydict_remove(ctx, aug->target_name);
lydict_remove(ctx, aug->dsc);
lydict_remove(ctx, aug->ref);
lys_iffeature_free(ctx, aug->iffeature, aug->iffeature_size);
lys_extension_instances_free(ctx, aug->ext, aug->ext_size);
lys_when_free(ctx, aug->when);
}
static void
lys_ident_free(struct ly_ctx *ctx, struct lys_ident *ident)
{
assert(ctx);
if (!ident) {
return;
}
free(ident->base);
ly_set_free(ident->der);
lydict_remove(ctx, ident->name);
lydict_remove(ctx, ident->dsc);
lydict_remove(ctx, ident->ref);
lys_iffeature_free(ctx, ident->iffeature, ident->iffeature_size);
lys_extension_instances_free(ctx, ident->ext, ident->ext_size);
}
static void
lys_grp_free(struct ly_ctx *ctx, struct lys_node_grp *grp)
{
int i;
/* handle only specific parts for LYS_GROUPING */
for (i = 0; i < grp->tpdf_size; i++) {
lys_tpdf_free(ctx, &grp->tpdf[i]);
}
free(grp->tpdf);
}
static void
lys_inout_free(struct ly_ctx *ctx, struct lys_node_inout *io)
{
int i;
/* handle only specific parts for LYS_INPUT and LYS_OUTPUT */
for (i = 0; i < io->tpdf_size; i++) {
lys_tpdf_free(ctx, &io->tpdf[i]);
}
free(io->tpdf);
for (i = 0; i < io->must_size; i++) {
lys_restr_free(ctx, &io->must[i]);
}
free(io->must);
}
static void
lys_notif_free(struct ly_ctx *ctx, struct lys_node_notif *notif)
{
int i;
for (i = 0; i < notif->must_size; i++) {
lys_restr_free(ctx, &notif->must[i]);
}
free(notif->must);
for (i = 0; i < notif->tpdf_size; i++) {
lys_tpdf_free(ctx, &notif->tpdf[i]);
}
free(notif->tpdf);
}
static void
lys_anydata_free(struct ly_ctx *ctx, struct lys_node_anydata *anyxml)
{
int i;
for (i = 0; i < anyxml->must_size; i++) {
lys_restr_free(ctx, &anyxml->must[i]);
}
free(anyxml->must);
lys_when_free(ctx, anyxml->when);
}
static void
lys_leaf_free(struct ly_ctx *ctx, struct lys_node_leaf *leaf)
{
int i;
/* leafref backlinks */
ly_set_free((struct ly_set *)leaf->backlinks);
for (i = 0; i < leaf->must_size; i++) {
lys_restr_free(ctx, &leaf->must[i]);
}
free(leaf->must);
lys_when_free(ctx, leaf->when);
lys_type_free(ctx, &leaf->type);
lydict_remove(ctx, leaf->units);
lydict_remove(ctx, leaf->dflt);
}
static void
lys_leaflist_free(struct ly_ctx *ctx, struct lys_node_leaflist *llist)
{
int i;
if (llist->backlinks) {
/* leafref backlinks */
ly_set_free(llist->backlinks);
}
for (i = 0; i < llist->must_size; i++) {
lys_restr_free(ctx, &llist->must[i]);
}
free(llist->must);
for (i = 0; i < llist->dflt_size; i++) {
lydict_remove(ctx, llist->dflt[i]);
}
free(llist->dflt);
lys_when_free(ctx, llist->when);
lys_type_free(ctx, &llist->type);
lydict_remove(ctx, llist->units);
}
static void
lys_list_free(struct ly_ctx *ctx, struct lys_node_list *list)
{
int i, j;
/* handle only specific parts for LY_NODE_LIST */
for (i = 0; i < list->tpdf_size; i++) {
lys_tpdf_free(ctx, &list->tpdf[i]);
}
free(list->tpdf);
for (i = 0; i < list->must_size; i++) {
lys_restr_free(ctx, &list->must[i]);
}
free(list->must);
lys_when_free(ctx, list->when);
for (i = 0; i < list->unique_size; i++) {
for (j = 0; j < list->unique[i].expr_size; j++) {
lydict_remove(ctx, list->unique[i].expr[j]);
}
free(list->unique[i].expr);
}
free(list->unique);
free(list->keys);
}
static void
lys_container_free(struct ly_ctx *ctx, struct lys_node_container *cont)
{
int i;
/* handle only specific parts for LY_NODE_CONTAINER */
lydict_remove(ctx, cont->presence);
for (i = 0; i < cont->tpdf_size; i++) {
lys_tpdf_free(ctx, &cont->tpdf[i]);
}
free(cont->tpdf);
for (i = 0; i < cont->must_size; i++) {
lys_restr_free(ctx, &cont->must[i]);
}
free(cont->must);
lys_when_free(ctx, cont->when);
}
static void
lys_feature_free(struct ly_ctx *ctx, struct lys_feature *f)
{
lydict_remove(ctx, f->name);
lydict_remove(ctx, f->dsc);
lydict_remove(ctx, f->ref);
lys_iffeature_free(ctx, f->iffeature, f->iffeature_size);
ly_set_free(f->depfeatures);
lys_extension_instances_free(ctx, f->ext, f->ext_size);
}
static void
lys_extension_free(struct ly_ctx *ctx, struct lys_ext *e)
{
lydict_remove(ctx, e->name);
lydict_remove(ctx, e->dsc);
lydict_remove(ctx, e->ref);
lydict_remove(ctx, e->argument);
lys_extension_instances_free(ctx, e->ext, e->ext_size);
}
static void
lys_deviation_free(struct lys_module *module, struct lys_deviation *dev)
{
int i, j, k;
struct ly_ctx *ctx;
struct lys_node *next, *elem;
ctx = module->ctx;
lydict_remove(ctx, dev->target_name);
lydict_remove(ctx, dev->dsc);
lydict_remove(ctx, dev->ref);
lys_extension_instances_free(ctx, dev->ext, dev->ext_size);
if (!dev->deviate) {
return ;
}
/* the module was freed, but we only need the context from orig_node, use ours */
if (dev->deviate[0].mod == LY_DEVIATE_NO) {
/* it's actually a node subtree, we need to update modules on all the nodes :-/ */
LY_TREE_DFS_BEGIN(dev->orig_node, next, elem) {
elem->module = module;
LY_TREE_DFS_END(dev->orig_node, next, elem);
}
lys_node_free(dev->orig_node, NULL, 0);
} else {
/* it's just a shallow copy, freeing one node */
dev->orig_node->module = module;
lys_node_free(dev->orig_node, NULL, 1);
}
for (i = 0; i < dev->deviate_size; i++) {
lys_extension_instances_free(ctx, dev->deviate[i].ext, dev->deviate[i].ext_size);
for (j = 0; j < dev->deviate[i].dflt_size; j++) {
lydict_remove(ctx, dev->deviate[i].dflt[j]);
}
free(dev->deviate[i].dflt);
lydict_remove(ctx, dev->deviate[i].units);
if (dev->deviate[i].mod == LY_DEVIATE_DEL) {
for (j = 0; j < dev->deviate[i].must_size; j++) {
lys_restr_free(ctx, &dev->deviate[i].must[j]);
}
free(dev->deviate[i].must);
for (j = 0; j < dev->deviate[i].unique_size; j++) {
for (k = 0; k < dev->deviate[i].unique[j].expr_size; k++) {
lydict_remove(ctx, dev->deviate[i].unique[j].expr[k]);
}
free(dev->deviate[i].unique[j].expr);
}
free(dev->deviate[i].unique);
}
}
free(dev->deviate);
}
static void
lys_uses_free(struct ly_ctx *ctx, struct lys_node_uses *uses, void (*private_destructor)(const struct lys_node *node, void *priv))
{
int i, j;
for (i = 0; i < uses->refine_size; i++) {
lydict_remove(ctx, uses->refine[i].target_name);
lydict_remove(ctx, uses->refine[i].dsc);
lydict_remove(ctx, uses->refine[i].ref);
for (j = 0; j < uses->refine[i].must_size; j++) {
lys_restr_free(ctx, &uses->refine[i].must[j]);
}
free(uses->refine[i].must);
for (j = 0; j < uses->refine[i].dflt_size; j++) {
lydict_remove(ctx, uses->refine[i].dflt[j]);
}
free(uses->refine[i].dflt);
lys_extension_instances_free(ctx, uses->refine[i].ext, uses->refine[i].ext_size);
if (uses->refine[i].target_type & LYS_CONTAINER) {
lydict_remove(ctx, uses->refine[i].mod.presence);
}
}
free(uses->refine);
for (i = 0; i < uses->augment_size; i++) {
lys_augment_free(ctx, &uses->augment[i], private_destructor);
}
free(uses->augment);
lys_when_free(ctx, uses->when);
}
void
lys_node_free(struct lys_node *node, void (*private_destructor)(const struct lys_node *node, void *priv), int shallow)
{
struct ly_ctx *ctx;
struct lys_node *sub, *next;
if (!node) {
return;
}
assert(node->module);
assert(node->module->ctx);
ctx = node->module->ctx;
/* remove private object */
if (node->priv && private_destructor) {
private_destructor(node, node->priv);
}
/* common part */
lydict_remove(ctx, node->name);
if (!(node->nodetype & (LYS_INPUT | LYS_OUTPUT))) {
lys_iffeature_free(ctx, node->iffeature, node->iffeature_size);
lydict_remove(ctx, node->dsc);
lydict_remove(ctx, node->ref);
}
if (!shallow && !(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
LY_TREE_FOR_SAFE(node->child, next, sub) {
lys_node_free(sub, private_destructor, 0);
}
}
lys_extension_instances_free(ctx, node->ext, node->ext_size);
/* specific part */
switch (node->nodetype) {
case LYS_CONTAINER:
lys_container_free(ctx, (struct lys_node_container *)node);
break;
case LYS_CHOICE:
lys_when_free(ctx, ((struct lys_node_choice *)node)->when);
break;
case LYS_LEAF:
lys_leaf_free(ctx, (struct lys_node_leaf *)node);
break;
case LYS_LEAFLIST:
lys_leaflist_free(ctx, (struct lys_node_leaflist *)node);
break;
case LYS_LIST:
lys_list_free(ctx, (struct lys_node_list *)node);
break;
case LYS_ANYXML:
case LYS_ANYDATA:
lys_anydata_free(ctx, (struct lys_node_anydata *)node);
break;
case LYS_USES:
lys_uses_free(ctx, (struct lys_node_uses *)node, private_destructor);
break;
case LYS_CASE:
lys_when_free(ctx, ((struct lys_node_case *)node)->when);
break;
case LYS_AUGMENT:
/* do nothing */
break;
case LYS_GROUPING:
case LYS_RPC:
case LYS_ACTION:
lys_grp_free(ctx, (struct lys_node_grp *)node);
break;
case LYS_NOTIF:
lys_notif_free(ctx, (struct lys_node_notif *)node);
break;
case LYS_INPUT:
case LYS_OUTPUT:
lys_inout_free(ctx, (struct lys_node_inout *)node);
break;
case LYS_EXT:
case LYS_UNKNOWN:
LOGINT;
break;
}
/* again common part */
lys_node_unlink(node);
free(node);
}
API struct lys_module *
lys_implemented_module(const struct lys_module *mod)
{
struct ly_ctx *ctx;
int i;
if (!mod || mod->implemented) {
/* invalid argument or the module itself is implemented */
return (struct lys_module *)mod;
}
ctx = mod->ctx;
for (i = 0; i < ctx->models.used; i++) {
if (!ctx->models.list[i]->implemented) {
continue;
}
if (ly_strequal(mod->name, ctx->models.list[i]->name, 1)) {
/* we have some revision of the module implemented */
return ctx->models.list[i];
}
}
/* we have no revision of the module implemented, return the module itself,
* it is up to the caller to set the module implemented when needed */
return (struct lys_module *)mod;
}
const struct lys_module *
lys_get_import_module_ns(const struct lys_module *module, const char *ns)
{
int i;
assert(module && ns);
if (module->type) {
/* the module is actually submodule and to get the namespace, we need the main module */
if (ly_strequal(((struct lys_submodule *)module)->belongsto->ns, ns, 0)) {
return ((struct lys_submodule *)module)->belongsto;
}
} else {
/* modul's own namespace */
if (ly_strequal(module->ns, ns, 0)) {
return module;
}
}
/* imported modules */
for (i = 0; i < module->imp_size; ++i) {
if (ly_strequal(module->imp[i].module->ns, ns, 0)) {
return module->imp[i].module;
}
}
return NULL;
}
const struct lys_module *
lys_get_import_module(const struct lys_module *module, const char *prefix, int pref_len, const char *name, int name_len)
{
const struct lys_module *main_module;
char *str;
int i;
assert(!prefix || !name);
if (prefix && !pref_len) {
pref_len = strlen(prefix);
}
if (name && !name_len) {
name_len = strlen(name);
}
main_module = lys_main_module(module);
/* module own prefix, submodule own prefix, (sub)module own name */
if ((!prefix || (!module->type && !strncmp(main_module->prefix, prefix, pref_len) && !main_module->prefix[pref_len])
|| (module->type && !strncmp(module->prefix, prefix, pref_len) && !module->prefix[pref_len]))
&& (!name || (!strncmp(main_module->name, name, name_len) && !main_module->name[name_len]))) {
return main_module;
}
/* standard import */
for (i = 0; i < module->imp_size; ++i) {
if ((!prefix || (!strncmp(module->imp[i].prefix, prefix, pref_len) && !module->imp[i].prefix[pref_len]))
&& (!name || (!strncmp(module->imp[i].module->name, name, name_len) && !module->imp[i].module->name[name_len]))) {
return module->imp[i].module;
}
}
/* module required by a foreign grouping, deviation, or submodule */
if (name) {
str = strndup(name, name_len);
if (!str) {
LOGMEM;
return NULL;
}
main_module = ly_ctx_get_module(module->ctx, str, NULL);
free(str);
return main_module;
}
return NULL;
}
/* free_int_mods - flag whether to free the internal modules as well */
static void
module_free_common(struct lys_module *module, void (*private_destructor)(const struct lys_node *node, void *priv))
{
struct ly_ctx *ctx;
struct lys_node *next, *iter;
unsigned int i;
assert(module->ctx);
ctx = module->ctx;
/* just free the import array, imported modules will stay in the context */
for (i = 0; i < module->imp_size; i++) {
lydict_remove(ctx, module->imp[i].prefix);
lydict_remove(ctx, module->imp[i].dsc);
lydict_remove(ctx, module->imp[i].ref);
lys_extension_instances_free(ctx, module->imp[i].ext, module->imp[i].ext_size);
}
free(module->imp);
/* submodules don't have data tree, the data nodes
* are placed in the main module altogether */
if (!module->type) {
LY_TREE_FOR_SAFE(module->data, next, iter) {
lys_node_free(iter, private_destructor, 0);
}
}
lydict_remove(ctx, module->dsc);
lydict_remove(ctx, module->ref);
lydict_remove(ctx, module->org);
lydict_remove(ctx, module->contact);
lydict_remove(ctx, module->filepath);
/* revisions */
for (i = 0; i < module->rev_size; i++) {
lys_extension_instances_free(ctx, module->rev[i].ext, module->rev[i].ext_size);
lydict_remove(ctx, module->rev[i].dsc);
lydict_remove(ctx, module->rev[i].ref);
}
free(module->rev);
/* identities */
for (i = 0; i < module->ident_size; i++) {
lys_ident_free(ctx, &module->ident[i]);
}
module->ident_size = 0;
free(module->ident);
/* typedefs */
for (i = 0; i < module->tpdf_size; i++) {
lys_tpdf_free(ctx, &module->tpdf[i]);
}
free(module->tpdf);
/* extension instances */
lys_extension_instances_free(ctx, module->ext, module->ext_size);
/* include */
for (i = 0; i < module->inc_size; i++) {
lydict_remove(ctx, module->inc[i].dsc);
lydict_remove(ctx, module->inc[i].ref);
lys_extension_instances_free(ctx, module->inc[i].ext, module->inc[i].ext_size);
/* complete submodule free is done only from main module since
* submodules propagate their includes to the main module */
if (!module->type) {
lys_submodule_free(module->inc[i].submodule, private_destructor);
}
}
free(module->inc);
/* augment */
for (i = 0; i < module->augment_size; i++) {
lys_augment_free(ctx, &module->augment[i], private_destructor);
}
free(module->augment);
/* features */
for (i = 0; i < module->features_size; i++) {
lys_feature_free(ctx, &module->features[i]);
}
free(module->features);
/* deviations */
for (i = 0; i < module->deviation_size; i++) {
lys_deviation_free(module, &module->deviation[i]);
}
free(module->deviation);
/* extensions */
for (i = 0; i < module->extensions_size; i++) {
lys_extension_free(ctx, &module->extensions[i]);
}
free(module->extensions);
lydict_remove(ctx, module->name);
lydict_remove(ctx, module->prefix);
}
void
lys_submodule_free(struct lys_submodule *submodule, void (*private_destructor)(const struct lys_node *node, void *priv))
{
if (!submodule) {
return;
}
/* common part with struct ly_module */
module_free_common((struct lys_module *)submodule, private_destructor);
/* no specific items to free */
free(submodule);
}
static int
ingrouping(const struct lys_node *node)
{
const struct lys_node *iter = node;
assert(node);
for(iter = node; iter && iter->nodetype != LYS_GROUPING; iter = lys_parent(iter));
if (!iter) {
return 0;
} else {
return 1;
}
}
/*
* final: 0 - do not change config flags; 1 - inherit config flags from the parent; 2 - remove config flags
*/
static struct lys_node *
lys_node_dup_recursion(struct lys_module *module, struct lys_node *parent, const struct lys_node *node,
struct unres_schema *unres, int shallow, int finalize)
{
struct lys_node *retval = NULL, *iter, *p;
struct ly_ctx *ctx = module->ctx;
int i, j, rc;
unsigned int size, size1, size2;
struct unres_list_uniq *unique_info;
uint16_t flags;
struct lys_node_container *cont = NULL;
struct lys_node_container *cont_orig = (struct lys_node_container *)node;
struct lys_node_choice *choice = NULL;
struct lys_node_choice *choice_orig = (struct lys_node_choice *)node;
struct lys_node_leaf *leaf = NULL;
struct lys_node_leaf *leaf_orig = (struct lys_node_leaf *)node;
struct lys_node_leaflist *llist = NULL;
struct lys_node_leaflist *llist_orig = (struct lys_node_leaflist *)node;
struct lys_node_list *list = NULL;
struct lys_node_list *list_orig = (struct lys_node_list *)node;
struct lys_node_anydata *any = NULL;
struct lys_node_anydata *any_orig = (struct lys_node_anydata *)node;
struct lys_node_uses *uses = NULL;
struct lys_node_uses *uses_orig = (struct lys_node_uses *)node;
struct lys_node_rpc_action *rpc = NULL;
struct lys_node_inout *io = NULL;
struct lys_node_rpc_action *ntf = NULL;
struct lys_node_case *cs = NULL;
struct lys_node_case *cs_orig = (struct lys_node_case *)node;
/* we cannot just duplicate memory since the strings are stored in
* dictionary and we need to update dictionary counters.
*/
switch (node->nodetype) {
case LYS_CONTAINER:
cont = calloc(1, sizeof *cont);
retval = (struct lys_node *)cont;
break;
case LYS_CHOICE:
choice = calloc(1, sizeof *choice);
retval = (struct lys_node *)choice;
break;
case LYS_LEAF:
leaf = calloc(1, sizeof *leaf);
retval = (struct lys_node *)leaf;
break;
case LYS_LEAFLIST:
llist = calloc(1, sizeof *llist);
retval = (struct lys_node *)llist;
break;
case LYS_LIST:
list = calloc(1, sizeof *list);
retval = (struct lys_node *)list;
break;
case LYS_ANYXML:
case LYS_ANYDATA:
any = calloc(1, sizeof *any);
retval = (struct lys_node *)any;
break;
case LYS_USES:
uses = calloc(1, sizeof *uses);
retval = (struct lys_node *)uses;
break;
case LYS_CASE:
cs = calloc(1, sizeof *cs);
retval = (struct lys_node *)cs;
break;
case LYS_RPC:
case LYS_ACTION:
rpc = calloc(1, sizeof *rpc);
retval = (struct lys_node *)rpc;
break;
case LYS_INPUT:
case LYS_OUTPUT:
io = calloc(1, sizeof *io);
retval = (struct lys_node *)io;
break;
case LYS_NOTIF:
ntf = calloc(1, sizeof *ntf);
retval = (struct lys_node *)ntf;
break;
default:
LOGINT;
goto error;
}
if (!retval) {
LOGMEM;
return NULL;
}
/*
* duplicate generic part of the structure
*/
retval->name = lydict_insert(ctx, node->name, 0);
retval->dsc = lydict_insert(ctx, node->dsc, 0);
retval->ref = lydict_insert(ctx, node->ref, 0);
retval->flags = node->flags;
retval->module = module;
retval->nodetype = node->nodetype;
retval->prev = retval;
retval->ext_size = node->ext_size;
if (lys_ext_dup(module, node->ext, node->ext_size, retval, LYEXT_PAR_NODE, &retval->ext, unres)) {
goto error;
}
if (node->iffeature_size) {
retval->iffeature_size = node->iffeature_size;
retval->iffeature = calloc(retval->iffeature_size, sizeof *retval->iffeature);
if (!retval->iffeature) {
LOGMEM;
goto error;
}
}
if (!shallow) {
for (i = 0; i < node->iffeature_size; ++i) {
resolve_iffeature_getsizes(&node->iffeature[i], &size1, &size2);
if (size1) {
/* there is something to duplicate */
/* duplicate compiled expression */
size = (size1 / 4) + (size1 % 4) ? 1 : 0;
retval->iffeature[i].expr = malloc(size * sizeof *retval->iffeature[i].expr);
memcpy(retval->iffeature[i].expr, node->iffeature[i].expr, size * sizeof *retval->iffeature[i].expr);
/* list of feature pointer must be updated to point to the resulting tree */
retval->iffeature[i].features = calloc(size2, sizeof *retval->iffeature[i].features);
for (j = 0; (unsigned int)j < size2; j++) {
rc = unres_schema_dup(module, unres, &node->iffeature[i].features[j], UNRES_IFFEAT,
&retval->iffeature[i].features[j]);
if (rc == EXIT_FAILURE) {
/* feature is resolved in origin, so copy it
* - duplication is used for instantiating groupings
* and if-feature inside grouping is supposed to be
* resolved inside the original grouping, so we want
* to keep pointers to features from the grouping
* context */
retval->iffeature[i].features[j] = node->iffeature[i].features[j];
} else if (rc == -1) {
goto error;
} /* else unres was duplicated */
}
}
/* duplicate if-feature's extensions */
retval->iffeature[i].ext_size = node->iffeature[i].ext_size;
if (lys_ext_dup(module, node->iffeature[i].ext, node->iffeature[i].ext_size,
&retval->iffeature[i], LYEXT_PAR_IFFEATURE, &retval->iffeature[i].ext, unres)) {
goto error;
}
}
/* inherit config flags */
p = parent;
do {
for (iter = p; iter && (iter->nodetype == LYS_USES); iter = iter->parent);
} while (iter && iter->nodetype == LYS_AUGMENT && (p = lys_parent(iter)));
if (iter) {
flags = iter->flags & LYS_CONFIG_MASK;
} else {
/* default */
flags = LYS_CONFIG_W;
}
switch (finalize) {
case 1:
/* inherit config flags */
if (retval->flags & LYS_CONFIG_SET) {
/* skip nodes with an explicit config value */
if ((flags & LYS_CONFIG_R) && (retval->flags & LYS_CONFIG_W)) {
LOGVAL(LYE_INARG, LY_VLOG_LYS, retval, "true", "config");
LOGVAL(LYE_SPEC, LY_VLOG_PREV, NULL, "State nodes cannot have configuration nodes as children.");
goto error;
}
break;
}
if (retval->nodetype != LYS_USES) {
retval->flags = (retval->flags & ~LYS_CONFIG_MASK) | flags;
}
break;
case 2:
/* erase config flags */
retval->flags &= ~LYS_CONFIG_MASK;
retval->flags &= ~LYS_CONFIG_SET;
break;
}
/* connect it to the parent */
if (lys_node_addchild(parent, retval->module, retval)) {
goto error;
}
/* go recursively */
if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
LY_TREE_FOR(node->child, iter) {
if (iter->nodetype & LYS_GROUPING) {
/* do not instantiate groupings */
continue;
}
if (!lys_node_dup_recursion(module, retval, iter, unres, 0, finalize)) {
goto error;
}
}
}
if (finalize == 1) {
/* check that configuration lists have keys
* - we really want to check keys_size in original node, because the keys are
* not yet resolved here, it is done below in nodetype specific part */
if ((retval->nodetype == LYS_LIST) && (retval->flags & LYS_CONFIG_W)
&& !((struct lys_node_list *)node)->keys_size) {
LOGVAL(LYE_MISSCHILDSTMT, LY_VLOG_LYS, retval, "key", "list");
goto error;
}
}
} else {
memcpy(retval->iffeature, node->iffeature, retval->iffeature_size * sizeof *retval->iffeature);
}
/*
* duplicate specific part of the structure
*/
switch (node->nodetype) {
case LYS_CONTAINER:
if (cont_orig->when) {
cont->when = lys_when_dup(module, cont_orig->when, unres);
}
cont->presence = lydict_insert(ctx, cont_orig->presence, 0);
cont->must_size = cont_orig->must_size;
cont->must = lys_restr_dup(module, cont_orig->must, cont->must_size, unres);
/* typedefs are not needed in instantiated grouping, nor the deviation's shallow copy */
break;
case LYS_CHOICE:
if (choice_orig->when) {
choice->when = lys_when_dup(module, choice_orig->when, unres);
}
if (!shallow) {
if (choice_orig->dflt) {
rc = lys_get_sibling(choice->child, lys_node_module(retval)->name, 0, choice_orig->dflt->name, 0,
LYS_ANYDATA | LYS_CASE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST,
(const struct lys_node **)&choice->dflt);
if (rc) {
if (rc == EXIT_FAILURE) {
LOGINT;
}
goto error;
}
} else {
/* useless to check return value, we don't know whether
* there really wasn't any default defined or it just hasn't
* been resolved, we just hope for the best :)
*/
unres_schema_dup(module, unres, choice_orig, UNRES_CHOICE_DFLT, choice);
}
} else {
choice->dflt = choice_orig->dflt;
}
break;
case LYS_LEAF:
if (lys_type_dup(module, retval, &(leaf->type), &(leaf_orig->type), ingrouping(retval), unres)) {
goto error;
}
leaf->units = lydict_insert(module->ctx, leaf_orig->units, 0);
if (leaf_orig->dflt) {
leaf->dflt = lydict_insert(ctx, leaf_orig->dflt, 0);
if (unres_schema_add_node(module, unres, &leaf->type, UNRES_TYPE_DFLT,
(struct lys_node *)(&leaf->dflt)) == -1) {
goto error;
}
}
leaf->must_size = leaf_orig->must_size;
leaf->must = lys_restr_dup(module, leaf_orig->must, leaf->must_size, unres);
if (leaf_orig->when) {
leaf->when = lys_when_dup(module, leaf_orig->when, unres);
}
break;
case LYS_LEAFLIST:
if (lys_type_dup(module, retval, &(llist->type), &(llist_orig->type), ingrouping(retval), unres)) {
goto error;
}
llist->units = lydict_insert(module->ctx, llist_orig->units, 0);
llist->min = llist_orig->min;
llist->max = llist_orig->max;
llist->must_size = llist_orig->must_size;
llist->must = lys_restr_dup(module, llist_orig->must, llist->must_size, unres);
llist->dflt_size = llist_orig->dflt_size;
llist->dflt = malloc(llist->dflt_size * sizeof *llist->dflt);
for (i = 0; i < llist->dflt_size; i++) {
llist->dflt[i] = lydict_insert(ctx, llist_orig->dflt[i], 0);
if (unres_schema_add_node(module, unres, &llist->type, UNRES_TYPE_DFLT,
(struct lys_node *)(&llist->dflt[i])) == -1) {
goto error;
}
}
if (llist_orig->when) {
llist->when = lys_when_dup(module, llist_orig->when, unres);
}
break;
case LYS_LIST:
list->min = list_orig->min;
list->max = list_orig->max;
list->must_size = list_orig->must_size;
list->must = lys_restr_dup(module, list_orig->must, list->must_size, unres);
/* typedefs are not needed in instantiated grouping, nor the deviation's shallow copy */
list->keys_size = list_orig->keys_size;
if (list->keys_size) {
list->keys = calloc(list->keys_size, sizeof *list->keys);
list->keys_str = lydict_insert(ctx, list_orig->keys_str, 0);
if (!list->keys) {
LOGMEM;
goto error;
}
if (!shallow) {
/* the keys are going to be resolved only if the list is instantiated in data tree, not just
* in another grouping */
for (iter = parent; iter && iter->nodetype != LYS_GROUPING; iter = iter->parent);
if (!iter && unres_schema_add_node(module, unres, list, UNRES_LIST_KEYS, NULL) == -1) {
goto error;
}
} else {
memcpy(list->keys, list_orig->keys, list->keys_size * sizeof *list->keys);
}
}
list->unique_size = list_orig->unique_size;
list->unique = malloc(list->unique_size * sizeof *list->unique);
if (!list->unique) {
LOGMEM;
goto error;
}
for (i = 0; i < list->unique_size; ++i) {
list->unique[i].expr_size = list_orig->unique[i].expr_size;
list->unique[i].expr = malloc(list->unique[i].expr_size * sizeof *list->unique[i].expr);
if (!list->unique[i].expr) {
LOGMEM;
goto error;
}
for (j = 0; j < list->unique[i].expr_size; j++) {
list->unique[i].expr[j] = lydict_insert(ctx, list_orig->unique[i].expr[j], 0);
/* if it stays in unres list, duplicate it also there */
unique_info = malloc(sizeof *unique_info);
unique_info->list = (struct lys_node *)list;
unique_info->expr = list->unique[i].expr[j];
unique_info->trg_type = &list->unique[i].trg_type;
unres_schema_dup(module, unres, &list_orig, UNRES_LIST_UNIQ, unique_info);
}
}
if (list_orig->when) {
list->when = lys_when_dup(module, list_orig->when, unres);
}
break;
case LYS_ANYXML:
case LYS_ANYDATA:
any->must_size = any_orig->must_size;
any->must = lys_restr_dup(module, any_orig->must, any->must_size, unres);
if (any_orig->when) {
any->when = lys_when_dup(module, any_orig->when, unres);
}
break;
case LYS_USES:
uses->grp = uses_orig->grp;
if (uses_orig->when) {
uses->when = lys_when_dup(module, uses_orig->when, unres);
}
/* it is not needed to duplicate refine, nor augment. They are already applied to the uses children */
break;
case LYS_CASE:
if (cs_orig->when) {
cs->when = lys_when_dup(module, cs_orig->when, unres);
}
break;
case LYS_ACTION:
case LYS_RPC:
case LYS_INPUT:
case LYS_OUTPUT:
case LYS_NOTIF:
/* typedefs are not needed in instantiated grouping, nor the deviation's shallow copy */
break;
default:
/* LY_NODE_AUGMENT */
LOGINT;
goto error;
}
return retval;
error:
lys_node_free(retval, NULL, 0);
return NULL;
}
int
lys_has_xpath(const struct lys_node *node)
{
assert(node);
switch (node->nodetype) {
case LYS_AUGMENT:
if (((struct lys_node_augment *)node)->when) {
return 1;
}
break;
case LYS_CASE:
if (((struct lys_node_case *)node)->when) {
return 1;
}
break;
case LYS_CHOICE:
if (((struct lys_node_choice *)node)->when) {
return 1;
}
break;
case LYS_ANYDATA:
if (((struct lys_node_anydata *)node)->when || ((struct lys_node_anydata *)node)->must_size) {
return 1;
}
break;
case LYS_LEAF:
if (((struct lys_node_leaf *)node)->when || ((struct lys_node_leaf *)node)->must_size) {
return 1;
}
break;
case LYS_LEAFLIST:
if (((struct lys_node_leaflist *)node)->when || ((struct lys_node_leaflist *)node)->must_size) {
return 1;
}
break;
case LYS_LIST:
if (((struct lys_node_list *)node)->when || ((struct lys_node_list *)node)->must_size) {
return 1;
}
break;
case LYS_CONTAINER:
if (((struct lys_node_container *)node)->when || ((struct lys_node_container *)node)->must_size) {
return 1;
}
break;
case LYS_INPUT:
case LYS_OUTPUT:
if (((struct lys_node_inout *)node)->must_size) {
return 1;
}
break;
case LYS_NOTIF:
if (((struct lys_node_notif *)node)->must_size) {
return 1;
}
break;
case LYS_USES:
if (((struct lys_node_uses *)node)->when) {
return 1;
}
break;
default:
/* does not have XPath */
break;
}
return 0;
}
struct lys_node *
lys_node_dup(struct lys_module *module, struct lys_node *parent, const struct lys_node *node,
struct unres_schema *unres, int shallow)
{
struct lys_node *p = NULL;
int finalize = 0;
struct lys_node *result, *iter, *next;
if (!shallow) {
/* get know where in schema tree we are to know what should be done during instantiation of the grouping */
for (p = parent;
p && !(p->nodetype & (LYS_NOTIF | LYS_INPUT | LYS_OUTPUT | LYS_RPC | LYS_ACTION | LYS_GROUPING));
p = lys_parent(p));
finalize = p ? ((p->nodetype == LYS_GROUPING) ? 0 : 2) : 1;
}
result = lys_node_dup_recursion(module, parent, node, unres, shallow, finalize);
if (finalize) {
/* check xpath expressions in the instantiated tree */
for (iter = next = parent->child; iter; iter = next) {
if (lys_has_xpath(iter) && unres_schema_add_node(module, unres, iter, UNRES_XPATH, NULL) == -1) {
/* invalid xpath */
return NULL;
}
/* select next item */
if (iter->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA | LYS_GROUPING)) {
/* child exception for leafs, leaflists and anyxml without children, ignore groupings */
next = NULL;
} else {
next = iter->child;
}
if (!next) {
/* no children, try siblings */
next = iter->next;
}
while (!next) {
/* parent is already processed, go to its sibling */
iter = lys_parent(iter);
if (iter == parent) {
/* we are done, no next element to process */
break;
}
next = iter->next;
}
}
}
return result;
}
void
lys_node_switch(struct lys_node *dst, struct lys_node *src)
{
struct lys_node *child;
assert((dst->module == src->module) && ly_strequal(dst->name, src->name, 1) && (dst->nodetype == src->nodetype));
/* sibling next */
if (dst->prev->next) {
dst->prev->next = src;
}
/* sibling prev */
if (dst->next) {
dst->next->prev = src;
} else {
for (child = dst->prev; child->prev->next; child = child->prev);
child->prev = src;
}
/* next */
src->next = dst->next;
dst->next = NULL;
/* prev */
if (dst->prev != dst) {
src->prev = dst->prev;
}
dst->prev = dst;
/* parent child */
if (dst->parent) {
if (dst->parent->child == dst) {
dst->parent->child = src;
}
} else if (dst->module->data == dst) {
dst->module->data = src;
}
/* parent */
src->parent = dst->parent; dst->parent = NULL;
/* child parent */
LY_TREE_FOR(dst->child, child) {
if (child->parent == dst) {
child->parent = src;
}
}
/* child */
src->child = dst->child;
dst->child = NULL;
/* node-specific data */
switch (dst->nodetype) {
case LYS_CONTAINER:
((struct lys_node_container *)src)->tpdf_size = ((struct lys_node_container *)dst)->tpdf_size;
((struct lys_node_container *)src)->tpdf = ((struct lys_node_container *)dst)->tpdf;
((struct lys_node_container *)dst)->tpdf_size = 0;
((struct lys_node_container *)dst)->tpdf = NULL;
break;
case LYS_LIST:
((struct lys_node_list *)src)->tpdf_size = ((struct lys_node_list *)dst)->tpdf_size;
((struct lys_node_list *)src)->tpdf = ((struct lys_node_list *)dst)->tpdf;
((struct lys_node_list *)dst)->tpdf_size = 0;
((struct lys_node_list *)dst)->tpdf = NULL;
break;
case LYS_RPC:
case LYS_ACTION:
((struct lys_node_rpc_action *)src)->tpdf_size = ((struct lys_node_rpc_action *)dst)->tpdf_size;
((struct lys_node_rpc_action *)src)->tpdf = ((struct lys_node_rpc_action *)dst)->tpdf;
((struct lys_node_rpc_action *)dst)->tpdf_size = 0;
((struct lys_node_rpc_action *)dst)->tpdf = NULL;
break;
case LYS_NOTIF:
((struct lys_node_notif *)src)->tpdf_size = ((struct lys_node_notif *)dst)->tpdf_size;
((struct lys_node_notif *)src)->tpdf = ((struct lys_node_notif *)dst)->tpdf;
((struct lys_node_notif *)dst)->tpdf_size = 0;
((struct lys_node_notif *)dst)->tpdf = NULL;
break;
case LYS_INPUT:
case LYS_OUTPUT:
((struct lys_node_inout *)src)->tpdf_size = ((struct lys_node_inout *)dst)->tpdf_size;
((struct lys_node_inout *)src)->tpdf = ((struct lys_node_inout *)dst)->tpdf;
((struct lys_node_inout *)dst)->tpdf_size = 0;
((struct lys_node_inout *)dst)->tpdf = NULL;
break;
default:
/* nothing special */
break;
}
}
void
lys_free(struct lys_module *module, void (*private_destructor)(const struct lys_node *node, void *priv), int remove_from_ctx)
{
struct ly_ctx *ctx;
int i;
if (!module) {
return;
}
/* remove schema from the context */
ctx = module->ctx;
if (remove_from_ctx && ctx->models.used) {
for (i = 0; i < ctx->models.used; i++) {
if (ctx->models.list[i] == module) {
/* move all the models to not change the order in the list */
ctx->models.used--;
memmove(&ctx->models.list[i], ctx->models.list[i + 1], (ctx->models.used - i) * sizeof *ctx->models.list);
ctx->models.list[ctx->models.used] = NULL;
/* we are done */
break;
}
}
}
/* common part with struct ly_submodule */
module_free_common(module, private_destructor);
/* specific items to free */
lydict_remove(ctx, module->ns);
free(module);
}
static void
lys_features_disable_recursive(struct lys_feature *f)
{
unsigned int i;
struct lys_feature *depf;
/* disable the feature */
f->flags &= ~LYS_FENABLED;
/* by disabling feature we have to disable also all features that depends on this feature */
if (f->depfeatures) {
for (i = 0; i < f->depfeatures->number; i++) {
depf = (struct lys_feature *)f->depfeatures->set.g[i];
if (depf->flags & LYS_FENABLED) {
lys_features_disable_recursive(depf);
}
}
}
}
/*
* op: 1 - enable, 0 - disable
*/
static int
lys_features_change(const struct lys_module *module, const char *name, int op)
{
int all = 0;
int i, j, k;
int progress, faili, failj, failk;
uint8_t fsize;
struct lys_feature *f;
if (!module || !name || !strlen(name)) {
return EXIT_FAILURE;
}
if (!strcmp(name, "*")) {
/* enable all */
all = 1;
}
progress = failk = 1;
while (progress && failk) {
for (i = -1, failk = progress = 0; i < module->inc_size; i++) {
if (i == -1) {
fsize = module->features_size;
f = module->features;
} else {
fsize = module->inc[i].submodule->features_size;
f = module->inc[i].submodule->features;
}
for (j = 0; j < fsize; j++) {
if (all || !strcmp(f[j].name, name)) {
if ((op && (f[j].flags & LYS_FENABLED)) || (!op && !(f[j].flags & LYS_FENABLED))) {
if (all) {
/* skip already set features */
continue;
} else {
/* feature already set correctly */
return EXIT_SUCCESS;
}
}
if (op) {
/* check referenced features if they are enabled */
for (k = 0; k < f[j].iffeature_size; k++) {
if (!resolve_iffeature(&f[j].iffeature[k])) {
if (all) {
faili = i;
failj = j;
failk = k + 1;
break;
} else {
LOGERR(LY_EINVAL, "Feature \"%s\" is disabled by its %d. if-feature condition.",
f[j].name, k + 1);
return EXIT_FAILURE;
}
}
}
if (k == f[j].iffeature_size) {
/* the last check passed, do the change */
f[j].flags |= LYS_FENABLED;
progress++;
}
} else {
lys_features_disable_recursive(&f[j]);
progress++;
}
if (!all) {
/* stop in case changing a single feature */
return EXIT_SUCCESS;
}
}
}
}
}
if (failk) {
/* print info about the last failing feature */
LOGERR(LY_EINVAL, "Feature \"%s\" is disabled by its %d. if-feature condition.",
faili == -1 ? module->features[failj].name : module->inc[faili].submodule->features[failj].name, failk);
return EXIT_FAILURE;
}
if (all) {
return EXIT_SUCCESS;
} else {
/* the specified feature not found */
return EXIT_FAILURE;
}
}
API int
lys_features_enable(const struct lys_module *module, const char *feature)
{
return lys_features_change(module, feature, 1);
}
API int
lys_features_disable(const struct lys_module *module, const char *feature)
{
return lys_features_change(module, feature, 0);
}
API int
lys_features_state(const struct lys_module *module, const char *feature)
{
int i, j;
if (!module || !feature) {
return -1;
}
/* search for the specified feature */
/* module itself */
for (i = 0; i < module->features_size; i++) {
if (!strcmp(feature, module->features[i].name)) {
if (module->features[i].flags & LYS_FENABLED) {
return 1;
} else {
return 0;
}
}
}
/* submodules */
for (j = 0; j < module->inc_size; j++) {
for (i = 0; i < module->inc[j].submodule->features_size; i++) {
if (!strcmp(feature, module->inc[j].submodule->features[i].name)) {
if (module->inc[j].submodule->features[i].flags & LYS_FENABLED) {
return 1;
} else {
return 0;
}
}
}
}
/* feature definition not found */
return -1;
}
API const char **
lys_features_list(const struct lys_module *module, uint8_t **states)
{
const char **result = NULL;
int i, j;
unsigned int count;
if (!module) {
return NULL;
}
count = module->features_size;
for (i = 0; i < module->inc_size; i++) {
count += module->inc[i].submodule->features_size;
}
result = malloc((count + 1) * sizeof *result);
if (!result) {
LOGMEM;
return NULL;
}
if (states) {
*states = malloc((count + 1) * sizeof **states);
if (!(*states)) {
LOGMEM;
free(result);
return NULL;
}
}
count = 0;
/* module itself */
for (i = 0; i < module->features_size; i++) {
result[count] = module->features[i].name;
if (states) {
if (module->features[i].flags & LYS_FENABLED) {
(*states)[count] = 1;
} else {
(*states)[count] = 0;
}
}
count++;
}
/* submodules */
for (j = 0; j < module->inc_size; j++) {
for (i = 0; i < module->inc[j].submodule->features_size; i++) {
result[count] = module->inc[j].submodule->features[i].name;
if (states) {
if (module->inc[j].submodule->features[i].flags & LYS_FENABLED) {
(*states)[count] = 1;
} else {
(*states)[count] = 0;
}
}
count++;
}
}
/* terminating NULL byte */
result[count] = NULL;
return result;
}
API struct lys_module *
lys_node_module(const struct lys_node *node)
{
if (!node) {
return NULL;
}
return node->module->type ? ((struct lys_submodule *)node->module)->belongsto : node->module;
}
API struct lys_module *
lys_main_module(const struct lys_module *module)
{
if (!module) {
return NULL;
}
return (module->type ? ((struct lys_submodule *)module)->belongsto : (struct lys_module *)module);
}
API struct lys_node *
lys_parent(const struct lys_node *node)
{
struct lys_node *parent;
if (!node) {
return NULL;
}
if (node->nodetype == LYS_EXT) {
if (((struct lys_ext_instance_complex*)node)->parent_type != LYEXT_PAR_NODE) {
return NULL;
}
parent = (struct lys_node*)((struct lys_ext_instance_complex*)node)->parent;
} else if (!node->parent) {
return NULL;
} else {
parent = node->parent;
}
if (parent->nodetype == LYS_AUGMENT) {
return ((struct lys_node_augment *)parent)->target;
} else {
return parent;
}
}
struct lys_node **
lys_child(const struct lys_node *node, LYS_NODE nodetype)
{
void *pp;
assert(node);
if (node->nodetype == LYS_EXT) {
pp = lys_ext_complex_get_substmt(lys_snode2stmt(nodetype), (struct lys_ext_instance_complex*)node, NULL);
if (!pp) {
return NULL;
}
return (struct lys_node **)pp;
} else {
return (struct lys_node **)&node->child;
}
}
API void *
lys_set_private(const struct lys_node *node, void *priv)
{
void *prev;
if (!node) {
LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
return NULL;
}
prev = node->priv;
((struct lys_node *)node)->priv = priv;
return prev;
}
int
lys_leaf_add_leafref_target(struct lys_node_leaf *leafref_target, struct lys_node *leafref)
{
struct lys_node_leaf *iter = leafref_target;
if (!(leafref_target->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
LOGINT;
return -1;
}
/* check for config flag */
if ((leafref->flags & LYS_CONFIG_W) && (leafref_target->flags & LYS_CONFIG_R)) {
LOGVAL(LYE_SPEC, LY_VLOG_LYS, leafref,
"The %s is config but refers to a non-config %s.",
strnodetype(leafref->nodetype), strnodetype(leafref_target->nodetype));
return -1;
}
/* check for cycles */
while (iter && iter->type.base == LY_TYPE_LEAFREF) {
if ((void *)iter == (void *)leafref) {
/* cycle detected */
LOGVAL(LYE_CIRC_LEAFREFS, LY_VLOG_LYS, leafref);
return -1;
}
iter = iter->type.info.lref.target;
}
/* create fake child - the ly_set structure to hold the list of
* leafrefs referencing the leaf(-list) */
if (!leafref_target->backlinks) {
leafref_target->backlinks = (void*)ly_set_new();
if (!leafref_target->backlinks) {
LOGMEM;
return -1;
}
}
ly_set_add(leafref_target->backlinks, leafref, 0);
return 0;
}
/* not needed currently */
#if 0
static const char *
lys_data_path_reverse(const struct lys_node *node, char * const buf, uint32_t buf_len)
{
struct lys_module *prev_mod;
uint32_t str_len, mod_len, buf_idx;
if (!(node->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA))) {
LOGINT;
return NULL;
}
buf_idx = buf_len - 1;
buf[buf_idx] = '\0';
while (node) {
if (lys_parent(node)) {
prev_mod = lys_node_module(lys_parent(node));
} else {
prev_mod = NULL;
}
if (node->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
str_len = strlen(node->name);
if (prev_mod != node->module) {
mod_len = strlen(node->module->name);
} else {
mod_len = 0;
}
if (buf_idx < 1 + (mod_len ? mod_len + 1 : 0) + str_len) {
LOGINT;
return NULL;
}
buf_idx -= 1 + (mod_len ? mod_len + 1 : 0) + str_len;
buf[buf_idx] = '/';
if (mod_len) {
memcpy(buf + buf_idx + 1, node->module->name, mod_len);
buf[buf_idx + 1 + mod_len] = ':';
}
memcpy(buf + buf_idx + 1 + (mod_len ? mod_len + 1 : 0), node->name, str_len);
}
node = lys_parent(node);
}
return buf + buf_idx;
}
#endif
API struct ly_set *
lys_find_xpath(struct ly_ctx *ctx, const struct lys_node *node, const char *expr, int options)
{
struct lyxp_set set;
struct ly_set *ret_set;
uint32_t i;
int opts;
if ((!ctx && !node) || !expr) {
ly_errno = LY_EINVAL;
return NULL;
}
if (!node) {
node = ly_ctx_get_node(ctx, NULL, "/ietf-yang-library:modules-state");
if (!node) {
ly_errno = LY_EINT;
return NULL;
}
}
memset(&set, 0, sizeof set);
opts = LYXP_SNODE;
if (options & LYS_FIND_OUTPUT) {
opts |= LYXP_SNODE_OUTPUT;
}
if (lyxp_atomize(expr, node, LYXP_NODE_ELEM, &set, opts)) {
/* just find a relevant node to put in path, if it fails, use the original one */
for (i = 0; i < set.used; ++i) {
if (set.val.snodes[i].in_ctx == 1) {
node = set.val.snodes[i].snode;
break;
}
}
free(set.val.snodes);
LOGVAL(LYE_SPEC, LY_VLOG_LYS, node, "Resolving XPath expression \"%s\" failed.", expr);
return NULL;
}
ret_set = ly_set_new();
for (i = 0; i < set.used; ++i) {
if (!set.val.snodes[i].in_ctx) {
continue;
}
assert(set.val.snodes[i].in_ctx == 1);
switch (set.val.snodes[i].type) {
case LYXP_NODE_ELEM:
if (ly_set_add(ret_set, set.val.snodes[i].snode, LY_SET_OPT_USEASLIST) == -1) {
ly_set_free(ret_set);
free(set.val.snodes);
return NULL;
}
break;
default:
/* ignore roots, text and attr should not ever appear */
break;
}
}
free(set.val.snodes);
return ret_set;
}
API struct ly_set *
lys_xpath_atomize(const struct lys_node *cur_snode, enum lyxp_node_type cur_snode_type, const char *expr, int options)
{
struct lyxp_set set;
struct ly_set *ret_set;
uint32_t i;
if (!cur_snode || !expr) {
return NULL;
}
/* adjust the root */
if ((cur_snode_type == LYXP_NODE_ROOT) || (cur_snode_type == LYXP_NODE_ROOT_CONFIG)) {
do {
cur_snode = lys_getnext(NULL, NULL, lys_node_module(cur_snode), 0);
} while ((cur_snode_type == LYXP_NODE_ROOT_CONFIG) && (cur_snode->flags & LYS_CONFIG_R));
}
memset(&set, 0, sizeof set);
if (options & LYXP_MUST) {
options &= ~LYXP_MUST;
options |= LYXP_SNODE_MUST;
} else if (options & LYXP_WHEN) {
options &= ~LYXP_WHEN;
options |= LYXP_SNODE_WHEN;
} else {
options |= LYXP_SNODE;
}
if (lyxp_atomize(expr, cur_snode, cur_snode_type, &set, options)) {
free(set.val.snodes);
LOGVAL(LYE_SPEC, LY_VLOG_LYS, cur_snode, "Resolving XPath expression \"%s\" failed.", expr);
return NULL;
}
ret_set = ly_set_new();
for (i = 0; i < set.used; ++i) {
switch (set.val.snodes[i].type) {
case LYXP_NODE_ELEM:
if (ly_set_add(ret_set, set.val.snodes[i].snode, LY_SET_OPT_USEASLIST) == -1) {
ly_set_free(ret_set);
free(set.val.snodes);
return NULL;
}
break;
default:
/* ignore roots, text and attr should not ever appear */
break;
}
}
free(set.val.snodes);
return ret_set;
}
API struct ly_set *
lys_node_xpath_atomize(const struct lys_node *node, int options)
{
const struct lys_node *next, *elem, *parent, *tmp;
struct lyxp_set set;
struct ly_set *ret_set;
uint16_t i;
if (!node) {
return NULL;
}
for (parent = node; parent && !(parent->nodetype & (LYS_NOTIF | LYS_INPUT | LYS_OUTPUT)); parent = lys_parent(parent));
if (!parent) {
/* not in input, output, or notification */
return NULL;
}
ret_set = ly_set_new();
if (!ret_set) {
return NULL;
}
LY_TREE_DFS_BEGIN(node, next, elem) {
if ((options & LYXP_NO_LOCAL) && !(elem->flags & LYS_XPATH_DEP)) {
/* elem has no dependencies from other subtrees and local nodes get discarded */
goto next_iter;
}
if (lyxp_node_atomize(elem, &set, 0)) {
ly_set_free(ret_set);
free(set.val.snodes);
return NULL;
}
for (i = 0; i < set.used; ++i) {
switch (set.val.snodes[i].type) {
case LYXP_NODE_ELEM:
if (options & LYXP_NO_LOCAL) {
for (tmp = set.val.snodes[i].snode; tmp && (tmp != parent); tmp = lys_parent(tmp));
if (tmp) {
/* in local subtree, discard */
break;
}
}
if (ly_set_add(ret_set, set.val.snodes[i].snode, 0) == -1) {
ly_set_free(ret_set);
free(set.val.snodes);
return NULL;
}
break;
default:
/* ignore roots, text and attr should not ever appear */
break;
}
}
free(set.val.snodes);
if (!(options & LYXP_RECURSIVE)) {
break;
}
next_iter:
LY_TREE_DFS_END(node, next, elem);
}
return ret_set;
}
static void
apply_aug(struct lys_node_augment *augment)
{
struct lys_node *last;
assert(augment->target && (augment->flags & LYS_NOTAPPLIED));
/* reconnect augmenting data into the target - add them to the target child list */
if (augment->target->child) {
last = augment->target->child->prev;
last->next = augment->child;
augment->target->child->prev = augment->child->prev;
augment->child->prev = last;
} else {
augment->target->child = augment->child;
}
/* remove the flag about not applicability */
augment->flags &= ~LYS_NOTAPPLIED;
}
static void
remove_aug(struct lys_node_augment *augment)
{
struct lys_node *last, *elem;
if (!augment->target) {
/* skip not resolved augments */
return;
}
elem = augment->child;
if (elem) {
LY_TREE_FOR(elem, last) {
if (!last->next || (last->next->parent != (struct lys_node *)augment)) {
break;
}
}
/* elem is first augment child, last is the last child */
/* parent child ptr */
if (augment->target->child == elem) {
augment->target->child = last->next;
}
/* parent child next ptr */
if (elem->prev->next) {
elem->prev->next = last->next;
}
/* parent child prev ptr */
if (last->next) {
last->next->prev = elem->prev;
} else if (augment->target->child) {
augment->target->child->prev = elem->prev;
}
/* update augment children themselves */
elem->prev = last;
last->next = NULL;
}
/* augment->target still keeps the resolved target, but for lys_augment_free()
* we have to keep information that this augment is not applied to free its data */
augment->flags |= LYS_NOTAPPLIED;
}
/*
* @param[in] module - the module where the deviation is defined
*/
static void
lys_switch_deviation(struct lys_deviation *dev, const struct lys_module *module)
{
int ret;
char *parent_path;
struct lys_node *target = NULL, *parent;
if (!dev->deviate) {
return ;
}
if (dev->deviate[0].mod == LY_DEVIATE_NO) {
if (dev->orig_node) {
/* removing not-supported deviation ... */
if (strrchr(dev->target_name, '/') != dev->target_name) {
/* ... from a parent */
/* reconnect to its previous position */
parent = dev->orig_node->parent;
if (parent) {
/* the original node was actually from augment, we have to get know if the augment is
* applied (its module is enabled and implemented). If yes, the node will be connected
* to the augment and the linkage with the target will be fixed if needed, otherwise
* it will be connected only to the augment */
/* first, connect it into the augment */
lys_node_addchild(parent, NULL, dev->orig_node);
if (!parent->module->disabled && parent->module->implemented) {
/* augment is supposed to be applied, so fix pointers in target and the status of the original node */
if (parent->child == dev->orig_node) {
/* the only node in augment */
dev->orig_node->flags |= LYS_NOTAPPLIED;
apply_aug((struct lys_node_augment *)parent);
} else {
/* other nodes from augment applied, nothing more needed in target, everything was done
* by lys_node_addchild() */
dev->orig_node->flags |= parent->child->flags & LYS_NOTAPPLIED;
}
} else {
/* augment is not supposed to be applied */
dev->orig_node->flags |= LYS_NOTAPPLIED;
}
} else {
/* non-augment, non-toplevel */
parent_path = strndup(dev->target_name, strrchr(dev->target_name, '/') - dev->target_name);
ret = resolve_augment_schema_nodeid(parent_path, NULL, module, 1,
(const struct lys_node **)&target);
free(parent_path);
if (ret || !target) {
LOGINT;
return;
}
lys_node_addchild(target, NULL, dev->orig_node);
}
} else {
/* ... from top-level data */
lys_node_addchild(NULL, (struct lys_module *)dev->orig_node->module, dev->orig_node);
}
dev->orig_node = NULL;
} else {
/* adding not-supported deviation */
ret = resolve_augment_schema_nodeid(dev->target_name, NULL, module, 1,
(const struct lys_node **)&target);
if (ret || !target) {
LOGINT;
return;
}
/* unlink and store the original node */
parent = target->parent;
lys_node_unlink(target);
if (parent && parent->nodetype == LYS_AUGMENT) {
/* hack for augment, because when the original will be sometime reconnected back, we actually need
* to reconnect it to both - the augment and its target (which is deduced from the deviations target
* path), so we need to remember the augment as an addition */
target->parent = parent;
}
dev->orig_node = target;
}
} else {
ret = resolve_augment_schema_nodeid(dev->target_name, NULL, module, 1,
(const struct lys_node **)&target);
if (ret || !target) {
LOGINT;
return;
}
lys_node_switch(target, dev->orig_node);
dev->orig_node = target;
}
}
/* temporarily removes or applies deviations, updates module deviation flag accordingly */
void
lys_switch_deviations(struct lys_module *module)
{
uint32_t i = 0, j;
const struct lys_module *mod;
const char *ptr;
if (module->deviated) {
while ((mod = ly_ctx_get_module_iter(module->ctx, &i))) {
if (mod == module) {
continue;
}
for (j = 0; j < mod->deviation_size; ++j) {
ptr = strstr(mod->deviation[j].target_name, module->name);
if (ptr && ptr[strlen(module->name)] == ':') {
lys_switch_deviation(&mod->deviation[j], mod);
}
}
}
if (module->deviated == 2) {
module->deviated = 1;
} else {
module->deviated = 2;
}
}
}
static void
apply_dev(struct lys_deviation *dev, const struct lys_module *module)
{
lys_switch_deviation(dev, module);
assert(dev->orig_node);
lys_node_module(dev->orig_node)->deviated = 1;
}
static void
remove_dev(struct lys_deviation *dev, const struct lys_module *module)
{
uint32_t idx = 0, j;
const struct lys_module *mod;
struct lys_module *target_mod;
const char *ptr;
if (dev->orig_node) {
target_mod = lys_node_module(dev->orig_node);
} else {
LOGINT;
return;
}
lys_switch_deviation(dev, module);
/* clear the deviation flag if possible */
while ((mod = ly_ctx_get_module_iter(module->ctx, &idx))) {
if ((mod == module) || (mod == target_mod)) {
continue;
}
for (j = 0; j < mod->deviation_size; ++j) {
ptr = strstr(mod->deviation[j].target_name, target_mod->name);
if (ptr && (ptr[strlen(target_mod->name)] == ':')) {
/* some other module deviation targets the inspected module, flag remains */
break;
}
}
if (j < mod->deviation_size) {
break;
}
}
if (!mod) {
target_mod->deviated = 0;
}
}
void
lys_sub_module_apply_devs_augs(struct lys_module *module)
{
uint8_t u, v;
/* remove applied deviations */
for (u = 0; u < module->deviation_size; ++u) {
apply_dev(&module->deviation[u], module);
}
/* remove applied augments */
for (u = 0; u < module->augment_size; ++u) {
apply_aug(&module->augment[u]);
}
/* remove deviation and augments defined in submodules */
for (v = 0; v < module->inc_size; ++v) {
for (u = 0; u < module->inc[v].submodule->deviation_size; ++u) {
apply_dev(&module->inc[v].submodule->deviation[u], module);
}
for (u = 0; u < module->inc[v].submodule->augment_size; ++u) {
apply_aug(&module->inc[v].submodule->augment[u]);
}
}
}
void
lys_sub_module_remove_devs_augs(struct lys_module *module)
{
uint8_t u, v;
/* remove applied deviations */
for (u = 0; u < module->deviation_size; ++u) {
remove_dev(&module->deviation[u], module);
}
/* remove applied augments */
for (u = 0; u < module->augment_size; ++u) {
remove_aug(&module->augment[u]);
}
/* remove deviation and augments defined in submodules */
for (v = 0; v < module->inc_size && module->inc[v].submodule; ++v) {
for (u = 0; u < module->inc[v].submodule->deviation_size; ++u) {
remove_dev(&module->inc[v].submodule->deviation[u], module);
}
for (u = 0; u < module->inc[v].submodule->augment_size; ++u) {
remove_aug(&module->inc[v].submodule->augment[u]);
}
}
}
static int
lys_set_implemented_recursion(struct lys_module *module, struct unres_schema *unres)
{
struct lys_node *root, *next, *node;
uint8_t i;
for (i = 0; i < module->augment_size; i++) {
/* apply augment */
if (!module->augment[i].target
&& (unres_schema_add_node(module, unres, &module->augment[i], UNRES_AUGMENT, NULL) == -1)) {
return -1;
}
}
LY_TREE_FOR(module->data, root) {
/* handle leafrefs and recursively change the implemented flags in the leafref targets */
LY_TREE_DFS_BEGIN(root, next, node) {
if (node->nodetype == LYS_GROUPING) {
goto nextsibling;
}
if (node->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
if (((struct lys_node_leaf *)node)->type.base == LY_TYPE_LEAFREF) {
if (unres_schema_add_node(module, unres, &((struct lys_node_leaf *)node)->type,
UNRES_TYPE_LEAFREF, node) == -1) {
return EXIT_FAILURE;
}
}
}
/* modified LY_TREE_DFS_END */
next = node->child;
/* child exception for leafs, leaflists and anyxml without children */
if (node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
next = NULL;
}
if (!next) {
nextsibling:
/* no children */
if (node == root) {
/* we are done, root has no children */
break;
}
/* try siblings */
next = node->next;
}
while (!next) {
/* parent is already processed, go to its sibling */
node = lys_parent(node);
/* no siblings, go back through parents */
if (lys_parent(node) == lys_parent(root)) {
/* we are done, no next element to process */
break;
}
next = node->next;
}
}
}
return EXIT_SUCCESS;
}
API int
lys_set_implemented(const struct lys_module *module)
{
struct ly_ctx *ctx;
struct unres_schema *unres;
int i, j, disabled = 0;
if (!module) {
ly_errno = LY_EINVAL;
return EXIT_FAILURE;
}
module = lys_main_module(module);
if (module->disabled) {
disabled = 1;
lys_set_enabled(module);
}
if (module->implemented) {
return EXIT_SUCCESS;
}
ctx = module->ctx;
for (i = 0; i < ctx->models.used; ++i) {
if (module == ctx->models.list[i]) {
continue;
}
if (!strcmp(module->name, ctx->models.list[i]->name) && ctx->models.list[i]->implemented) {
LOGERR(LY_EINVAL, "Module \"%s\" in another revision already implemented.", module->name);
if (disabled) {
/* set it back disabled */
lys_set_disabled(module);
}
return EXIT_FAILURE;
}
}
unres = calloc(1, sizeof *unres);
if (!unres) {
LOGMEM;
if (disabled) {
/* set it back disabled */
lys_set_disabled(module);
}
return EXIT_FAILURE;
}
/* recursively make the module implemented */
((struct lys_module *)module)->implemented = 1;
if (lys_set_implemented_recursion((struct lys_module *)module, unres)) {
goto error;
}
/* process augments in submodules */
for (i = 0; i < module->inc_size && module->inc[i].submodule; ++i) {
for (j = 0; j < module->inc[i].submodule->augment_size; j++) {
/* apply augment */
if (!module->inc[i].submodule->augment[j].target
&& (unres_schema_add_node((struct lys_module *)module->inc[j].submodule, unres,
&module->inc[i].submodule->augment[j], UNRES_AUGMENT, NULL) == -1)) {
goto error;
}
}
}
/* try again resolve augments in other modules possibly augmenting this one,
* since we have just enabled it
*/
/* resolve rest of unres items */
if (unres->count && resolve_unres_schema((struct lys_module *)module, unres)) {
goto error;
}
unres_schema_free((struct lys_module *)module, &unres);
return EXIT_SUCCESS;
error:
if (disabled) {
/* set it back disabled */
lys_set_disabled(module);
}
((struct lys_module *)module)->implemented = 0;
unres_schema_free((struct lys_module *)module, &unres);
return EXIT_FAILURE;
}
void
lys_submodule_module_data_free(struct lys_submodule *submodule)
{
struct lys_node *next, *elem;
/* remove parsed data */
LY_TREE_FOR_SAFE(submodule->belongsto->data, next, elem) {
if (elem->module == (struct lys_module *)submodule) {
lys_node_free(elem, NULL, 0);
}
}
}
int
lys_is_key(struct lys_node_list *list, struct lys_node_leaf *leaf)
{
uint8_t i;
for (i = 0; i < list->keys_size; i++) {
if (list->keys[i] == leaf) {
return i + 1;
}
}
return 0;
}
API char *
lys_path(const struct lys_node *node)
{
char *buf_backup = NULL, *buf = ly_buf(), *result = NULL;
uint16_t index = LY_BUF_SIZE - 1;
if (!node) {
LOGERR(LY_EINVAL, "%s: NULL node parameter", __func__);
return NULL;
}
/* backup the shared internal buffer */
if (ly_buf_used && buf[0]) {
buf_backup = strndup(buf, LY_BUF_SIZE - 1);
}
ly_buf_used++;
/* build the path */
buf[index] = '\0';
ly_vlog_build_path_reverse(LY_VLOG_LYS, node, buf, &index, 0);
result = strdup(&buf[index]);
/* restore the shared internal buffer */
if (buf_backup) {
strcpy(buf, buf_backup);
free(buf_backup);
}
ly_buf_used--;
return result;
}
static void
lys_extcomplex_free_str(struct ly_ctx *ctx, struct lys_ext_instance_complex *ext, LY_STMT stmt)
{
struct lyext_substmt *info;
const char **str, ***a;
int c;
str = lys_ext_complex_get_substmt(stmt, ext, &info);
if (!str || !(*str)) {
return;
}
if (info->cardinality >= LY_STMT_CARD_SOME) {
/* we have array */
a = (const char ***)str;
for (str = (*(const char ***)str), c = 0; str[c]; c++) {
lydict_remove(ctx, str[c]);
}
free(a[0]);
if (stmt == LY_STMT_BELONGSTO) {
for (str = a[1], c = 0; str[c]; c++) {
lydict_remove(ctx, str[c]);
}
free(a[1]);
}
} else {
lydict_remove(ctx, str[0]);
if (stmt == LY_STMT_BELONGSTO) {
lydict_remove(ctx, str[1]);
}
}
}
void
lys_extension_instances_free(struct ly_ctx *ctx, struct lys_ext_instance **e, unsigned int size)
{
unsigned int i, j, k;
struct lyext_substmt *substmt;
void **pp, **start;
struct lys_node *siter, *snext;
#define EXTCOMPLEX_FREE_STRUCT(STMT, TYPE, FUNC, FREE, ARGS...) \
pp = lys_ext_complex_get_substmt(STMT, (struct lys_ext_instance_complex *)e[i], NULL); \
if (!pp || !(*pp)) { break; } \
if (substmt[j].cardinality >= LY_STMT_CARD_SOME) { /* process array */ \
for (start = pp = *pp; *pp; pp++) { \
FUNC(ctx, (TYPE *)(*pp), ##ARGS); \
if (FREE) { free(*pp); } \
} \
free(start); \
} else { /* single item */ \
FUNC(ctx, (TYPE *)(*pp), ##ARGS); \
if (FREE) { free(*pp); } \
}
if (!size || !e || !(*e)) {
return;
}
for (i = 0; i < size; i++) {
if (!e[i]) {
continue;
}
if (e[i]->flags & (LYEXT_OPT_INHERIT)) {
/* no free, this is just a shadow copy of the original extension instance */
} else {
if (e[i]->flags & (LYEXT_OPT_YANG)) {
free(e[i]->def); /* remove name of instance extension */
e[i]->def = NULL;
free(e[i]->parent); /* remove backup part of yang file */
}
lys_extension_instances_free(ctx, e[i]->ext, e[i]->ext_size);
lydict_remove(ctx, e[i]->arg_value);
}
if (e[i]->def && e[i]->def->plugin && e[i]->def->plugin->type == LYEXT_COMPLEX) {
substmt = ((struct lys_ext_instance_complex *)e[i])->substmt;
for (j = 0; substmt[j].stmt; j++) {
switch(substmt[j].stmt) {
case LY_STMT_DESCRIPTION:
case LY_STMT_REFERENCE:
case LY_STMT_UNITS:
case LY_STMT_ARGUMENT:
case LY_STMT_DEFAULT:
case LY_STMT_ERRTAG:
case LY_STMT_ERRMSG:
case LY_STMT_PREFIX:
case LY_STMT_NAMESPACE:
case LY_STMT_PRESENCE:
case LY_STMT_REVISIONDATE:
case LY_STMT_KEY:
case LY_STMT_BASE:
case LY_STMT_BELONGSTO:
case LY_STMT_CONTACT:
case LY_STMT_ORGANIZATION:
case LY_STMT_PATH:
lys_extcomplex_free_str(ctx, (struct lys_ext_instance_complex *)e[i], substmt[j].stmt);
break;
case LY_STMT_TYPE:
EXTCOMPLEX_FREE_STRUCT(LY_STMT_TYPE, struct lys_type, lys_type_free, 1);
break;
case LY_STMT_TYPEDEF:
EXTCOMPLEX_FREE_STRUCT(LY_STMT_TYPEDEF, struct lys_tpdf, lys_tpdf_free, 1);
break;
case LY_STMT_IFFEATURE:
EXTCOMPLEX_FREE_STRUCT(LY_STMT_IFFEATURE, struct lys_iffeature, lys_iffeature_free, 0, 1);
break;
case LY_STMT_MAX:
case LY_STMT_MIN:
case LY_STMT_POSITION:
case LY_STMT_VALUE:
pp = (void**)&((struct lys_ext_instance_complex *)e[i])->content[substmt[j].offset];
if (substmt[j].cardinality >= LY_STMT_CARD_SOME && *pp) {
for(k = 0; ((uint32_t**)(*pp))[k]; k++) {
free(((uint32_t**)(*pp))[k]);
}
}
free(*pp);
break;
case LY_STMT_DIGITS:
if (substmt[j].cardinality >= LY_STMT_CARD_SOME) {
/* free the array */
pp = (void**)&((struct lys_ext_instance_complex *)e[i])->content[substmt[j].offset];
free(*pp);
}
break;
case LY_STMT_MODULE:
/* modules are part of the context, so they will be freed there */
if (substmt[j].cardinality >= LY_STMT_CARD_SOME) {
/* free the array */
pp = (void**)&((struct lys_ext_instance_complex *)e[i])->content[substmt[j].offset];
free(*pp);
}
break;
case LY_STMT_ACTION:
case LY_STMT_ANYDATA:
case LY_STMT_ANYXML:
case LY_STMT_CASE:
case LY_STMT_CHOICE:
case LY_STMT_CONTAINER:
case LY_STMT_GROUPING:
case LY_STMT_INPUT:
case LY_STMT_LEAF:
case LY_STMT_LEAFLIST:
case LY_STMT_LIST:
case LY_STMT_NOTIFICATION:
case LY_STMT_OUTPUT:
case LY_STMT_RPC:
case LY_STMT_USES:
pp = (void**)&((struct lys_ext_instance_complex *)e[i])->content[substmt[j].offset];
LY_TREE_FOR_SAFE((struct lys_node *)(*pp), snext, siter) {
lys_node_free(siter, NULL, 0);
}
*pp = NULL;
break;
case LY_STMT_UNIQUE:
pp = lys_ext_complex_get_substmt(LY_STMT_UNIQUE, (struct lys_ext_instance_complex *)e[i], NULL);
if (!pp || !(*pp)) {
break;
}
if (substmt[j].cardinality >= LY_STMT_CARD_SOME) { /* process array */
for (start = pp = *pp; *pp; pp++) {
for (k = 0; k < (*(struct lys_unique**)pp)->expr_size; k++) {
lydict_remove(ctx, (*(struct lys_unique**)pp)->expr[k]);
}
free((*(struct lys_unique**)pp)->expr);
free(*pp);
}
free(start);
} else { /* single item */
for (k = 0; k < (*(struct lys_unique**)pp)->expr_size; k++) {
lydict_remove(ctx, (*(struct lys_unique**)pp)->expr[k]);
}
free((*(struct lys_unique**)pp)->expr);
free(*pp);
}
break;
case LY_STMT_LENGTH:
case LY_STMT_MUST:
case LY_STMT_PATTERN:
case LY_STMT_RANGE:
EXTCOMPLEX_FREE_STRUCT(substmt[j].stmt, struct lys_restr, lys_restr_free, 1);
break;
case LY_STMT_WHEN:
EXTCOMPLEX_FREE_STRUCT(LY_STMT_WHEN, struct lys_when, lys_when_free, 0);
break;
case LY_STMT_REVISION:
pp = lys_ext_complex_get_substmt(LY_STMT_REVISION, (struct lys_ext_instance_complex *)e[i], NULL);
if (!pp || !(*pp)) {
break;
}
if (substmt[j].cardinality >= LY_STMT_CARD_SOME) { /* process array */
for (start = pp = *pp; *pp; pp++) {
lydict_remove(ctx, (*(struct lys_revision**)pp)->dsc);
lydict_remove(ctx, (*(struct lys_revision**)pp)->ref);
lys_extension_instances_free(ctx, (*(struct lys_revision**)pp)->ext,
(*(struct lys_revision**)pp)->ext_size);
free(*pp);
}
free(start);
} else { /* single item */
lydict_remove(ctx, (*(struct lys_revision**)pp)->dsc);
lydict_remove(ctx, (*(struct lys_revision**)pp)->ref);
lys_extension_instances_free(ctx, (*(struct lys_revision**)pp)->ext,
(*(struct lys_revision**)pp)->ext_size);
free(*pp);
}
break;
default:
/* nothing to free */
break;
}
}
}
free(e[i]);
}
free(e);
#undef EXTCOMPLEX_FREE_STRUCT
}