blob: 7f51bca15d24f64b1c37b1e1d165f90394c7e11d [file] [log] [blame]
/**
* @file tree.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief Manipulation with libyang data structures
*
* Copyright (c) 2015 CESNET, z.s.p.o.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name of the Company nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*/
#define _GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>
#include "common.h"
#include "context.h"
#include "parse.h"
#include "parser.h"
#include "resolve.h"
#include "xml.h"
#include "tree_internal.h"
void ly_submodule_free(struct ly_submodule *submodule);
void
ly_mnode_unlink(struct ly_mnode *node)
{
struct ly_mnode *parent, *first;
if (!node) {
return;
}
/* unlink from data model if necessary */
if (node->module) {
if (node->module->data == node) {
node->module->data = node->next;
} else if (node->module->rpc == node) {
node->module->rpc = node->next;
} else if (node->module->notif == node) {
node->module->notif = node->next;
}
}
/* store pointers to important nodes */
parent = node->parent;
if (parent && !parent->nodetype) {
/* handle augments - first, unlink it from the augment parent ... */
if (parent->child == node) {
parent->child = node->next;
}
/* and then continue with the target parent */
parent = ((struct ly_augment *)parent)->target;
}
/* unlink from parent */
if (parent) {
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) {
first = parent->child;
} else {
first = node;
while (node->prev->next) {
first = node->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;
}
/*
* Add child model node at the end of the parent's child list.
* If the child is connected somewhere (has a parent), it is completely
* unlinked and none of the following conditions applies.
* If the child has prev sibling(s), they are ignored (child is added at the
* end of the child list).
* If the child has next sibling(s), all of them are connected with the parent.
*/
int
ly_mnode_addchild(struct ly_mnode *parent, struct ly_mnode *child)
{
struct ly_mnode *last;
assert(parent);
assert(child);
/* checks */
switch (parent->nodetype) {
case LY_NODE_CONTAINER:
case LY_NODE_LIST:
case LY_NODE_GROUPING:
case LY_NODE_USES:
case LY_NODE_INPUT:
case LY_NODE_OUTPUT:
case LY_NODE_NOTIF:
if (!(child->nodetype &
(LY_NODE_ANYXML | LY_NODE_CHOICE | LY_NODE_CONTAINER | LY_NODE_GROUPING | LY_NODE_LEAF |
LY_NODE_LEAFLIST | LY_NODE_LIST | LY_NODE_USES))) {
LOGVAL(VE_SPEC, 0, "Unexpected substatement \"%s\" in \"%s\" (%s).",
strnodetype(child->nodetype), strnodetype(parent->nodetype), parent->name);
return EXIT_FAILURE;
}
break;
case LY_NODE_CHOICE:
if (!(child->nodetype &
(LY_NODE_ANYXML | LY_NODE_CASE | LY_NODE_CONTAINER | LY_NODE_LEAF | LY_NODE_LEAFLIST | LY_NODE_LIST))) {
LOGVAL(VE_SPEC, 0, "Unexpected substatement \"%s\" in \"choice\" %s.",
strnodetype(child->nodetype), parent->name);
return EXIT_FAILURE;
}
break;
case LY_NODE_CASE:
if (!(child->nodetype &
(LY_NODE_ANYXML | LY_NODE_CHOICE | LY_NODE_CONTAINER | LY_NODE_LEAF | LY_NODE_LEAFLIST | LY_NODE_LIST | LY_NODE_USES))) {
LOGVAL(VE_SPEC, 0, "Unexpected substatement \"%s\" in \"case\" %s.",
strnodetype(child->nodetype), parent->name);
return EXIT_FAILURE;
}
break;
case LY_NODE_RPC:
if (!(child->nodetype & (LY_NODE_INPUT | LY_NODE_OUTPUT | LY_NODE_GROUPING))) {
LOGVAL(VE_SPEC, 0, "Unexpected substatement \"%s\" in \"rpc\" %s.",
strnodetype(child->nodetype), parent->name);
return EXIT_FAILURE;
}
break;
case LY_NODE_LEAF:
case LY_NODE_LEAFLIST:
case LY_NODE_ANYXML:
LOGVAL(VE_SPEC, 0, "The \"%s\" statement (%s) cannot have any substatement.",
strnodetype(parent->nodetype), parent->name);
return EXIT_FAILURE;
case LY_NODE_AUGMENT:
if (!(child->nodetype &
(LY_NODE_ANYXML | LY_NODE_CASE | LY_NODE_CHOICE | LY_NODE_CONTAINER | LY_NODE_LEAF
| LY_NODE_LEAFLIST | LY_NODE_LIST | LY_NODE_USES))) {
LOGVAL(VE_SPEC, 0, "Unexpected substatement \"%s\" in \"%s\" (%s).",
strnodetype(child->nodetype), strnodetype(parent->nodetype), parent->name);
return EXIT_FAILURE;
}
}
if (child->parent) {
ly_mnode_unlink(child);
}
if (!parent->child) {
/* the only/first child of the parent */
parent->child = child;
child->parent = parent;
last = child;
} else {
/* add a new child at the end of parent's child list */
last = parent->child->prev;
last->next = child;
child->prev = last;
}
while (last->next) {
last = last->next;
last->parent = parent;
}
parent->child->prev = last;
return EXIT_SUCCESS;
}
API struct ly_module *
lys_parse(struct ly_ctx *ctx, const char *data, LY_MINFORMAT format)
{
struct unres_item *unres;
struct ly_module *mod;
if (!ctx || !data) {
LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
return NULL;
}
unres = calloc(1, sizeof *unres);
switch (format) {
case LY_IN_YIN:
mod = yin_read_module(ctx, data, 1, unres);
break;
case LY_IN_YANG:
/* TODO */
mod = NULL;
break;
default:
mod = NULL;
break;
}
if (resolve_unres(mod, unres)) {
LOGERR(LY_EVALID, "There are unresolved items left.");
/* TODO print unresolved items */
lys_free(mod);
mod = NULL;
}
free(unres->item);
free(unres->type);
free(unres->str_mnode);
free(unres->line);
free(unres);
return mod;
}
struct ly_submodule *
ly_submodule_read(struct ly_module *module, const char *data, LY_MINFORMAT format, int implement)
{
struct unres_item *unres;
struct ly_submodule *submod;
assert(module);
assert(data);
unres = calloc(1, sizeof *unres);
switch (format) {
case LY_IN_YIN:
submod = yin_read_submodule(module, data, implement, unres);
break;
case LY_IN_YANG:
/* TODO */
submod = NULL;
break;
default:
submod = NULL;
break;
}
if (resolve_unres((struct ly_module *)submod, unres)) {
LOGERR(LY_EVALID, "There are unresolved items left.");
/* TODO print unresolved items */
ly_submodule_free(submod);
submod = NULL;
}
free(unres->item);
free(unres->type);
free(unres->str_mnode);
free(unres->line);
free(unres);
return submod;
}
struct ly_module *
lys_read_import(struct ly_ctx *ctx, int fd, LY_MINFORMAT format)
{
struct unres_item *unres;
struct ly_module *module;
struct stat sb;
char *addr;
if (!ctx || fd < 0) {
LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
return NULL;
}
unres = calloc(1, sizeof *unres);
/*
* TODO
* This is just a temporary solution to make working automatic search for
* imported modules. This doesn't work e.g. for streams (stdin)
*/
fstat(fd, &sb);
addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
switch (format) {
case LY_IN_YIN:
module = yin_read_module(ctx, addr, 0, unres);
break;
case LY_IN_YANG:
default:
/* TODO */
munmap(addr, sb.st_size);
return NULL;
}
munmap(addr, sb.st_size);
if (resolve_unres(module, unres)) {
LOGERR(LY_EVALID, "There are unresolved items left.");
/* TODO print unresolved items */
lys_free(module);
module = NULL;
}
free(unres->item);
free(unres->type);
free(unres->str_mnode);
free(unres->line);
free(unres);
return module;
}
API struct ly_module *
lys_read(struct ly_ctx *ctx, int fd, LY_MINFORMAT format)
{
struct ly_module *module;
struct stat sb;
char *addr;
if (!ctx || fd < 0) {
LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
return NULL;
}
/*
* TODO
* This is just a temporary solution to make working automatic search for
* imported modules. This doesn't work e.g. for streams (stdin)
*/
fstat(fd, &sb);
addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
module = lys_parse(ctx, addr, format);
munmap(addr, sb.st_size);
return module;
}
struct ly_submodule *
ly_submodule_read_fd(struct ly_module *module, int fd, LY_MINFORMAT format, int implement)
{
struct ly_submodule *submodule;
struct stat sb;
char *addr;
assert(module);
assert(fd >= 0);
/*
* TODO
* This is just a temporary solution to make working automatic search for
* imported modules. This doesn't work e.g. for streams (stdin)
*/
fstat(fd, &sb);
addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
/* TODO addr error check */
submodule = ly_submodule_read(module, addr, format, implement);
munmap(addr, sb.st_size);
return submodule;
}
struct ly_restr *
ly_restr_dup(struct ly_ctx *ctx, struct ly_restr *old, int size)
{
struct ly_restr *result;
int i;
if (!size) {
return NULL;
}
result = calloc(size, sizeof *result);
for (i = 0; i < size; i++) {
result[i].expr = lydict_insert(ctx, old[i].expr, 0);
result[i].dsc = lydict_insert(ctx, old[i].dsc, 0);
result[i].ref = lydict_insert(ctx, old[i].ref, 0);
result[i].eapptag = lydict_insert(ctx, old[i].eapptag, 0);
result[i].emsg = lydict_insert(ctx, old[i].emsg, 0);
}
return result;
}
void
ly_restr_free(struct ly_ctx *ctx, struct ly_restr *restr)
{
assert(ctx);
if (!restr) {
return;
}
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
ly_type_dup(struct ly_module *mod, struct ly_mnode *parent, struct ly_type *new, struct ly_type *old,
struct unres_item *unres)
{
int i;
new->prefix = lydict_insert(mod->ctx, old->prefix, 0);
new->base = old->base;
new->der = old->der;
i = find_unres(unres, old, UNRES_TYPE_DER);
if (i != -1) {
/* HACK for unres */
new->der = (struct ly_tpdf *)parent;
add_unres_str(mod, unres, new, UNRES_TYPE_DER, unres->str_mnode[i], 0);
return;
}
switch (new->base) {
case LY_TYPE_BINARY:
if (old->info.binary.length) {
new->info.binary.length = ly_restr_dup(mod->ctx, old->info.binary.length, 1);
}
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);
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].status = old->info.bits.bit[i].status;
new->info.bits.bit[i].pos = old->info.bits.bit[i].pos;
}
}
break;
case LY_TYPE_DEC64:
new->info.dec64.dig = old->info.dec64.dig;
if (old->info.dec64.range) {
new->info.dec64.range = ly_restr_dup(mod->ctx, old->info.dec64.range, 1);
}
break;
case LY_TYPE_ENUM:
new->info.enums.count = old->info.enums.count;
if (new->info.enums.count) {
new->info.enums.list = calloc(new->info.enums.count, sizeof *new->info.enums.list);
for (i = 0; i < new->info.enums.count; i++) {
new->info.enums.list[i].name = lydict_insert(mod->ctx, old->info.enums.list[i].name, 0);
new->info.enums.list[i].dsc = lydict_insert(mod->ctx, old->info.enums.list[i].dsc, 0);
new->info.enums.list[i].ref = lydict_insert(mod->ctx, old->info.enums.list[i].ref, 0);
new->info.enums.list[i].status = old->info.enums.list[i].status;
new->info.enums.list[i].value = old->info.enums.list[i].value;
}
}
break;
case LY_TYPE_IDENT:
if (old->info.ident.ref) {
new->info.ident.ref = old->info.ident.ref;
} else {
i = find_unres(unres, old, UNRES_TYPE_IDENTREF);
assert(i != -1);
add_unres_str(mod, unres, new, UNRES_TYPE_IDENTREF, unres->str_mnode[i], 0);
}
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 = ly_restr_dup(mod->ctx, old->info.num.range, 1);
}
break;
case LY_TYPE_LEAFREF:
new->info.lref.path = lydict_insert(mod->ctx, old->info.lref.path, 0);
add_unres_mnode(mod, unres, new, UNRES_TYPE_LEAFREF, parent, 0);
break;
case LY_TYPE_STRING:
if (old->info.str.length) {
new->info.str.length = ly_restr_dup(mod->ctx, old->info.str.length, 1);
}
new->info.str.patterns = ly_restr_dup(mod->ctx, old->info.str.patterns, 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.type = calloc(new->info.uni.count, sizeof *new->info.uni.type);
for (i = 0; i < new->info.uni.count; i++) {
ly_type_dup(mod, parent, &(new->info.uni.type[i]), &(old->info.uni.type[i]), unres);
}
}
break;
default:
/* nothing to do for LY_TYPE_BOOL, LY_TYPE_EMPTY */
break;
}
}
void
ly_type_free(struct ly_ctx *ctx, struct ly_type *type)
{
int i;
assert(ctx);
if (!type) {
return;
}
lydict_remove(ctx, type->prefix);
switch (type->base) {
case LY_TYPE_BINARY:
ly_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);
}
free(type->info.bits.bit);
break;
case LY_TYPE_DEC64:
ly_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.list[i].name);
lydict_remove(ctx, type->info.enums.list[i].dsc);
lydict_remove(ctx, type->info.enums.list[i].ref);
}
free(type->info.enums.list);
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:
ly_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:
ly_restr_free(ctx, type->info.str.length);
free(type->info.str.length);
for (i = 0; i < type->info.str.pat_count; i++) {
ly_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++) {
ly_type_free(ctx, &type->info.uni.type[i]);
}
free(type->info.uni.type);
break;
default:
/* nothing to do for LY_TYPE_IDENT, LY_TYPE_INST, LY_TYPE_BOOL, LY_TYPE_EMPTY */
break;
}
}
struct ly_tpdf *
ly_tpdf_dup(struct ly_module *mod, struct ly_mnode *parent, struct ly_tpdf *old, int size, struct unres_item *unres)
{
struct ly_tpdf *result;
int i;
if (!size) {
return NULL;
}
result = calloc(size, sizeof *result);
for (i = 0; i < size; i++) {
result[i].name = lydict_insert(mod->ctx, old[i].name, 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].flags = old[i].flags;
result[i].module = old[i].module;
ly_type_dup(mod, parent, &(result[i].type), &(old[i].type), unres);
result[i].dflt = lydict_insert(mod->ctx, old[i].dflt, 0);
result[i].units = lydict_insert(mod->ctx, old[i].units, 0);
}
return result;
}
void
ly_tpdf_free(struct ly_ctx *ctx, struct ly_tpdf *tpdf)
{
assert(ctx);
if (!tpdf) {
return;
}
lydict_remove(ctx, tpdf->name);
lydict_remove(ctx, tpdf->dsc);
lydict_remove(ctx, tpdf->ref);
ly_type_free(ctx, &tpdf->type);
lydict_remove(ctx, tpdf->units);
lydict_remove(ctx, tpdf->dflt);
}
struct ly_when *
ly_when_dup(struct ly_ctx *ctx, struct ly_when *old)
{
struct ly_when *new;
if (!old) {
return NULL;
}
new = calloc(1, sizeof *new);
new->cond = lydict_insert(ctx, old->cond, 0);
new->dsc = lydict_insert(ctx, old->dsc, 0);
new->ref = lydict_insert(ctx, old->ref, 0);
return new;
}
void
ly_when_free(struct ly_ctx *ctx, struct ly_when *w)
{
if (!w) {
return;
}
lydict_remove(ctx, w->cond);
lydict_remove(ctx, w->dsc);
lydict_remove(ctx, w->ref);
free(w);
}
static struct ly_augment *
ly_augment_dup(struct ly_module *module, struct ly_mnode *parent, struct ly_augment *old, int size)
{
struct ly_augment *new = NULL;
int i = -1;
if (!size) {
return NULL;
}
new = calloc(size, sizeof *new);
for (i = 0; i < size; i++) {
new[i].target_name = lydict_insert(module->ctx, old[i].target_name, 0);
new[i].dsc = lydict_insert(module->ctx, old[i].dsc, 0);
new[i].ref = lydict_insert(module->ctx, old[i].ref, 0);
new[i].flags = old[i].flags;
/* .target = NULL; .nodetype = 0 */
new[i].parent = parent;
/* copy the definition of augment nodes */
if (old[i].child) {
new[i].child = (struct ly_mnode *)lyxml_dup_elem(module->ctx, (struct lyxml_elem *)old[i].child, NULL, 1);
}
}
return new;
}
static struct ly_refine *
ly_refine_dup(struct ly_module *mod, struct ly_refine *old, int size, struct ly_mnode_uses *uses,
struct unres_item *unres)
{
struct ly_refine *result;
int i, j;
if (!size) {
return NULL;
}
result = calloc(size, sizeof *result);
for (i = 0; i < size; i++) {
result[i].target = lydict_insert(mod->ctx, old[i].target, 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].flags = old[i].flags;
result[i].target_type = old[i].target_type;
result[i].must_size = old[i].must_size;
result[i].must = ly_restr_dup(mod->ctx, old[i].must, old[i].must_size);
for (j = 0; j < result[i].must_size; ++j) {
add_unres_mnode(mod, unres, &result[i].must[j], UNRES_MUST, (struct ly_mnode *)uses, 0);
}
if (result[i].target_type & (LY_NODE_LEAF | LY_NODE_CHOICE)) {
result[i].mod.dflt = lydict_insert(mod->ctx, old[i].mod.dflt, 0);
} else if (result[i].target_type == LY_NODE_CONTAINER) {
result[i].mod.presence = lydict_insert(mod->ctx, old[i].mod.presence, 0);
} else if (result[i].target_type & (LY_NODE_LIST | LY_NODE_LEAFLIST)) {
result[i].mod.list = old[i].mod.list;
}
}
return result;
}
void
ly_ident_free(struct ly_ctx *ctx, struct ly_ident *ident)
{
struct ly_ident_der *der;
assert(ctx);
if (!ident) {
return;
}
/*
* if caller free only a single data model which is used (its identity is
* reference from identity in another module), this silly freeing can lead
* to segmentation fault. But without noting if the module is used by some
* other, it cannot be solved.
*
* Possible solution is to not allow caller to remove particular schema
* from the context.
*/
while (ident->der) {
der = ident->der;
ident->der = der->next;
free(der);
}
lydict_remove(ctx, ident->name);
lydict_remove(ctx, ident->dsc);
lydict_remove(ctx, ident->ref);
}
void
ly_grp_free(struct ly_ctx *ctx, struct ly_mnode_grp *grp)
{
int i;
/* handle only specific parts for LY_NODE_GROUPING */
for (i = 0; i < grp->tpdf_size; i++) {
ly_tpdf_free(ctx, &grp->tpdf[i]);
}
free(grp->tpdf);
}
void
ly_anyxml_free(struct ly_ctx *ctx, struct ly_mnode_anyxml *anyxml)
{
int i;
for (i = 0; i < anyxml->must_size; i++) {
ly_restr_free(ctx, &anyxml->must[i]);
}
free(anyxml->must);
ly_when_free(ctx, anyxml->when);
}
void
ly_leaf_free(struct ly_ctx *ctx, struct ly_mnode_leaf *leaf)
{
int i;
for (i = 0; i < leaf->must_size; i++) {
ly_restr_free(ctx, &leaf->must[i]);
}
free(leaf->must);
ly_when_free(ctx, leaf->when);
ly_type_free(ctx, &leaf->type);
lydict_remove(ctx, leaf->units);
lydict_remove(ctx, leaf->dflt);
}
void
ly_leaflist_free(struct ly_ctx *ctx, struct ly_mnode_leaflist *llist)
{
int i;
for (i = 0; i < llist->must_size; i++) {
ly_restr_free(ctx, &llist->must[i]);
}
free(llist->must);
ly_when_free(ctx, llist->when);
ly_type_free(ctx, &llist->type);
lydict_remove(ctx, llist->units);
}
void
ly_list_free(struct ly_ctx *ctx, struct ly_mnode_list *list)
{
int i;
/* handle only specific parts for LY_NODE_LIST */
for (i = 0; i < list->tpdf_size; i++) {
ly_tpdf_free(ctx, &list->tpdf[i]);
}
free(list->tpdf);
for (i = 0; i < list->must_size; i++) {
ly_restr_free(ctx, &list->must[i]);
}
free(list->must);
ly_when_free(ctx, list->when);
for (i = 0; i < list->unique_size; i++) {
free(list->unique[i].leafs);
}
free(list->unique);
free(list->keys);
}
void
ly_container_free(struct ly_ctx *ctx, struct ly_mnode_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++) {
ly_tpdf_free(ctx, &cont->tpdf[i]);
}
free(cont->tpdf);
for (i = 0; i < cont->must_size; i++) {
ly_restr_free(ctx, &cont->must[i]);
}
free(cont->must);
ly_when_free(ctx, cont->when);
}
void
ly_feature_free(struct ly_ctx *ctx, struct ly_feature *f)
{
lydict_remove(ctx, f->name);
lydict_remove(ctx, f->dsc);
lydict_remove(ctx, f->ref);
free(f->features);
}
void
ly_deviation_free(struct ly_ctx *ctx, struct ly_deviation *dev)
{
int i, j;
lydict_remove(ctx, dev->target_name);
lydict_remove(ctx, dev->dsc);
lydict_remove(ctx, dev->ref);
for (i = 0; i < dev->deviate_size; i++) {
lydict_remove(ctx, 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++) {
ly_restr_free(ctx, &dev->deviate[i].must[j]);
}
free(dev->deviate[i].must);
for (j = 0; j < dev->deviate[i].unique_size; j++) {
free(dev->deviate[j].unique[j].leafs);
}
free(dev->deviate[i].unique);
}
}
free(dev->deviate);
}
void
ly_augment_free(struct ly_ctx *ctx, struct ly_augment *aug)
{
lydict_remove(ctx, aug->target_name);
lydict_remove(ctx, aug->dsc);
lydict_remove(ctx, aug->ref);
free(aug->features);
ly_when_free(ctx, aug->when);
lyxml_free_elem(ctx, (struct lyxml_elem *)aug->child);
}
void
ly_uses_free(struct ly_ctx *ctx, struct ly_mnode_uses *uses)
{
int i, j;
for (i = 0; i < uses->refine_size; i++) {
lydict_remove(ctx, uses->refine[i].target);
lydict_remove(ctx, uses->refine[i].dsc);
lydict_remove(ctx, uses->refine[i].ref);
for (j = 0; j < uses->refine[j].must_size; j++) {
ly_restr_free(ctx, &uses->refine[i].must[j]);
}
free(uses->refine[i].must);
if (uses->refine[i].target_type & (LY_NODE_LEAF | LY_NODE_CHOICE)) {
lydict_remove(ctx, uses->refine[i].mod.dflt);
} else if (uses->refine[i].target_type & LY_NODE_CONTAINER) {
lydict_remove(ctx, uses->refine[i].mod.presence);
}
}
free(uses->refine);
for (i = 0; i < uses->augment_size; i++) {
ly_augment_free(ctx, &uses->augment[i]);
}
free(uses->augment);
ly_when_free(ctx, uses->when);
}
void
ly_mnode_free(struct ly_mnode *node)
{
struct ly_ctx *ctx;
struct ly_mnode *sub, *next;
if (!node) {
return;
}
assert(node->module);
assert(node->module->ctx);
ctx = node->module->ctx;
/* common part */
LY_TREE_FOR_SAFE(node->child, next, sub) {
ly_mnode_free(sub);
}
free(node->features);
lydict_remove(ctx, node->name);
lydict_remove(ctx, node->dsc);
lydict_remove(ctx, node->ref);
/* specific part */
switch (node->nodetype) {
case LY_NODE_CONTAINER:
ly_container_free(ctx, (struct ly_mnode_container *)node);
break;
case LY_NODE_CHOICE:
ly_when_free(ctx, ((struct ly_mnode_choice *)node)->when);
break;
case LY_NODE_LEAF:
ly_leaf_free(ctx, (struct ly_mnode_leaf *)node);
break;
case LY_NODE_LEAFLIST:
ly_leaflist_free(ctx, (struct ly_mnode_leaflist *)node);
break;
case LY_NODE_LIST:
ly_list_free(ctx, (struct ly_mnode_list *)node);
break;
case LY_NODE_ANYXML:
ly_anyxml_free(ctx, (struct ly_mnode_anyxml *)node);
break;
case LY_NODE_USES:
ly_uses_free(ctx, (struct ly_mnode_uses *)node);
break;
case LY_NODE_CASE:
ly_when_free(ctx, ((struct ly_mnode_case *)node)->when);
break;
case LY_NODE_AUGMENT:
/* do nothing */
break;
case LY_NODE_GROUPING:
case LY_NODE_RPC:
case LY_NODE_INPUT:
case LY_NODE_OUTPUT:
case LY_NODE_NOTIF:
ly_grp_free(ctx, (struct ly_mnode_grp *)node);
break;
}
/* again common part */
ly_mnode_unlink(node);
free(node);
}
static void
module_free_common(struct ly_module *module)
{
struct ly_ctx *ctx;
unsigned int i;
int j, l;
assert(module->ctx);
ctx = module->ctx;
/* as first step, free the imported modules */
for (i = 0; i < module->imp_size; i++) {
/* get the imported module from the context and then free,
* this check is necessary because the imported module can
* be already removed
*/
l = ctx->models.used;
for (j = 0; j < l; j++) {
if (ctx->models.list[j] == module->imp[i].module) {
lys_free(module->imp[i].module);
break;
}
}
}
free(module->imp);
while (module->data) {
ly_mnode_free(module->data);
}
while (module->rpc) {
ly_mnode_free(module->rpc);
}
while (module->notif) {
ly_mnode_free(module->notif);
}
lydict_remove(ctx, module->dsc);
lydict_remove(ctx, module->ref);
lydict_remove(ctx, module->org);
lydict_remove(ctx, module->contact);
/* revisions */
for (i = 0; i < module->rev_size; i++) {
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++) {
ly_ident_free(ctx, &module->ident[i]);
}
module->ident_size = 0;
free(module->ident);
/* typedefs */
for (i = 0; i < module->tpdf_size; i++) {
ly_tpdf_free(ctx, &module->tpdf[i]);
}
free(module->tpdf);
/* include */
for (i = 0; i < module->inc_size; i++) {
ly_submodule_free(module->inc[i].submodule);
}
free(module->inc);
/* augment */
for (i = 0; i < module->augment_size; i++) {
ly_augment_free(ctx, &module->augment[i]);
}
free(module->augment);
/* features */
for (i = 0; i < module->features_size; i++) {
ly_feature_free(ctx, &module->features[i]);
}
free(module->features);
/* deviations */
for (i = 0; i < module->deviation_size; i++) {
ly_deviation_free(ctx, &module->deviation[i]);
}
free(module->deviation);
lydict_remove(ctx, module->name);
}
void
ly_submodule_free(struct ly_submodule *submodule)
{
if (!submodule) {
return;
}
submodule->inc_size = 0;
free(submodule->inc);
submodule->inc = NULL;
/* common part with struct ly_module */
module_free_common((struct ly_module *)submodule);
/* no specific items to free */
free(submodule);
}
static struct ly_mnode_leaf *
ly_uniq_find(struct ly_mnode_list *list, struct ly_mnode_leaf *orig_leaf)
{
struct ly_mnode *mnode, *mnode2, *ret = NULL, *parent1, *parent2;
int depth = 1, i;
/* find the correct direct descendant of list in orig_leaf */
mnode = (struct ly_mnode *)orig_leaf;
while (1) {
if (!mnode->parent) {
return NULL;
}
if (!strcmp(mnode->parent->name, list->name)) {
break;
}
mnode = mnode->parent;
++depth;
}
/* make sure the nodes are equal */
parent1 = mnode->parent->parent;
parent2 = list->parent;
while (1) {
if ((parent1 && !parent2) || (!parent1 && parent2)) {
return NULL;
}
if (parent1 == parent2) {
break;
}
parent1 = parent1->parent;
parent2 = parent2->parent;
}
/* find the descendant in the list */
LY_TREE_FOR(list->child, mnode2) {
if (!strcmp(mnode2->name, mnode->name)) {
ret = mnode2;
break;
}
}
if (!ret) {
return NULL;
}
/* continue traversing both trees, the nodes are always truly equal */
while (1) {
--depth;
if (!depth) {
if (ret->nodetype != LY_NODE_LEAF) {
return NULL;
}
return (struct ly_mnode_leaf *)ret;
}
mnode = (struct ly_mnode *)orig_leaf;
for (i = 0; i < depth-1; ++i) {
mnode = mnode->parent;
}
LY_TREE_FOR(ret->child, mnode2) {
if (!strcmp(mnode2->name, mnode->name)) {
ret = mnode2;
break;
}
}
if (!mnode2) {
return NULL;
}
}
}
struct ly_mnode *
ly_mnode_dup(struct ly_module *module, struct ly_mnode *mnode, uint8_t flags, int recursive, unsigned int line,
struct unres_item *unres)
{
struct ly_mnode *retval = NULL, *aux, *child;
struct ly_ctx *ctx = module->ctx;
int i, j;
struct ly_mnode_container *cont;
struct ly_mnode_container *cont_orig = (struct ly_mnode_container *)mnode;
struct ly_mnode_choice *choice;
struct ly_mnode_choice *choice_orig = (struct ly_mnode_choice *)mnode;
struct ly_mnode_leaf *leaf;
struct ly_mnode_leaf *leaf_orig = (struct ly_mnode_leaf *)mnode;
struct ly_mnode_leaflist *llist;
struct ly_mnode_leaflist *llist_orig = (struct ly_mnode_leaflist *)mnode;
struct ly_mnode_list *list;
struct ly_mnode_list *list_orig = (struct ly_mnode_list *)mnode;
struct ly_mnode_anyxml *anyxml;
struct ly_mnode_anyxml *anyxml_orig = (struct ly_mnode_anyxml *)mnode;
struct ly_mnode_uses *uses;
struct ly_mnode_uses *uses_orig = (struct ly_mnode_uses *)mnode;
struct ly_mnode_grp *mix;
struct ly_mnode_grp *mix_orig = (struct ly_mnode_grp *)mnode;
struct ly_mnode_case *cs;
struct ly_mnode_case *cs_orig = (struct ly_mnode_case *)mnode;
/* we cannot just duplicate memory since the strings are stored in
* dictionary and we need to update dictionary counters.
*/
switch (mnode->nodetype) {
case LY_NODE_CONTAINER:
cont = calloc(1, sizeof *cont);
retval = (struct ly_mnode *)cont;
break;
case LY_NODE_CHOICE:
choice = calloc(1, sizeof *choice);
retval = (struct ly_mnode *)choice;
break;
case LY_NODE_LEAF:
leaf = calloc(1, sizeof *leaf);
retval = (struct ly_mnode *)leaf;
break;
case LY_NODE_LEAFLIST:
llist = calloc(1, sizeof *llist);
retval = (struct ly_mnode *)llist;
break;
case LY_NODE_LIST:
list = calloc(1, sizeof *list);
retval = (struct ly_mnode *)list;
break;
case LY_NODE_ANYXML:
anyxml = calloc(1, sizeof *anyxml);
retval = (struct ly_mnode *)anyxml;
break;
case LY_NODE_USES:
uses = calloc(1, sizeof *uses);
retval = (struct ly_mnode *)uses;
break;
case LY_NODE_CASE:
cs = calloc(1, sizeof *cs);
retval = (struct ly_mnode *)cs;
break;
/* exact same structure */
case LY_NODE_GROUPING:
case LY_NODE_RPC:
case LY_NODE_INPUT:
case LY_NODE_OUTPUT:
case LY_NODE_NOTIF:
mix = calloc(1, sizeof *mix);
retval = (struct ly_mnode *)mix;
break;
default:
goto error;
}
/*
* duplicate generic part of the structure
*/
retval->name = lydict_insert(ctx, mnode->name, 0);
retval->dsc = lydict_insert(ctx, mnode->dsc, 0);
retval->ref = lydict_insert(ctx, mnode->ref, 0);
retval->flags = mnode->flags;
if (!(retval->flags & LY_NODE_CONFIG_MASK)) {
/* set parent's config flag */
retval->flags |= flags & LY_NODE_CONFIG_MASK;
}
retval->module = module;
retval->nodetype = mnode->nodetype;
retval->prev = retval;
retval->features_size = mnode->features_size;
retval->features = calloc(retval->features_size, sizeof *retval->features);
for (i = 0; i < mnode->features_size; ++i) {
dup_unres(module, unres, &mnode->features[i], UNRES_IFFEAT, &retval->features[i]);
}
if (recursive) {
/* go recursively */
LY_TREE_FOR(mnode->child, child) {
aux = ly_mnode_dup(module, child, retval->flags, 1, line, unres);
if (!aux || ly_mnode_addchild(retval, aux)) {
goto error;
}
}
}
/*
* duplicate specific part of the structure
*/
switch (mnode->nodetype) {
case LY_NODE_CONTAINER:
if (cont_orig->when) {
cont->when = ly_when_dup(ctx, cont_orig->when);
add_unres_mnode(module, unres, cont->when, UNRES_WHEN, retval, 0);
}
cont->presence = lydict_insert(ctx, cont_orig->presence, 0);
cont->must_size = cont_orig->must_size;
cont->tpdf_size = cont_orig->tpdf_size;
cont->must = ly_restr_dup(ctx, cont_orig->must, cont->must_size);
for (i = 0; i < cont->must_size; ++i) {
add_unres_mnode(module, unres, &cont->must[i], UNRES_MUST, retval, 0);
}
cont->tpdf = ly_tpdf_dup(module, mnode->parent, cont_orig->tpdf, cont->tpdf_size, unres);
break;
case LY_NODE_CHOICE:
if (choice_orig->when) {
choice->when = ly_when_dup(ctx, choice_orig->when);
add_unres_mnode(module, unres, choice->when, UNRES_WHEN, retval, 0);
}
if (choice_orig->dflt) {
choice->dflt = resolve_child((struct ly_mnode *)choice, choice_orig->dflt->name, 0, LY_NODE_ANYXML
| LY_NODE_CASE | LY_NODE_CONTAINER | LY_NODE_LEAF | LY_NODE_LEAFLIST
| LY_NODE_LIST);
assert(choice->dflt);
} else {
dup_unres(module, unres, choice_orig, UNRES_CHOICE_DFLT, choice);
}
break;
case LY_NODE_LEAF:
ly_type_dup(module, mnode->parent, &(leaf->type), &(leaf_orig->type), unres);
leaf->units = lydict_insert(module->ctx, leaf_orig->units, 0);
if (leaf_orig->dflt) {
leaf->dflt = lydict_insert(ctx, leaf_orig->dflt, 0);
add_unres_str(module, unres, &leaf->type, UNRES_TYPE_DFLT, leaf->dflt, 0);
}
leaf->must_size = leaf_orig->must_size;
leaf->must = ly_restr_dup(ctx, leaf_orig->must, leaf->must_size);
for (i = 0; i < leaf->must_size; ++i) {
add_unres_mnode(module, unres, &leaf->must[i], UNRES_MUST, retval, 0);
}
if (leaf_orig->when) {
leaf->when = ly_when_dup(ctx, leaf_orig->when);
add_unres_mnode(module, unres, leaf->when, UNRES_WHEN, retval, 0);
}
break;
case LY_NODE_LEAFLIST:
ly_type_dup(module, mnode->parent, &(llist->type), &(llist_orig->type), unres);
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 = ly_restr_dup(ctx, llist_orig->must, llist->must_size);
for (i = 0; i < llist->must_size; ++i) {
add_unres_mnode(module, unres, &llist->must[i], UNRES_MUST, retval, 0);
}
if (llist_orig->when) {
llist->when = ly_when_dup(ctx, llist_orig->when);
add_unres_mnode(module, unres, llist->when, UNRES_WHEN, retval, 0);
}
break;
case LY_NODE_LIST:
list->min = list_orig->min;
list->max = list_orig->max;
list->must_size = list_orig->must_size;
list->tpdf_size = list_orig->tpdf_size;
list->keys_size = list_orig->keys_size;
list->unique_size = list_orig->unique_size;
list->must = ly_restr_dup(ctx, list_orig->must, list->must_size);
for (i = 0; i < list->must_size; ++i) {
add_unres_mnode(module, unres, &list->must[i], UNRES_MUST, retval, 0);
}
list->tpdf = ly_tpdf_dup(module, mnode->parent, list_orig->tpdf, list->tpdf_size, unres);
if (list->keys_size) {
list->keys = calloc(list->keys_size, sizeof *list->keys);
/* we managed to resolve it before, resolve it again manually */
if (list_orig->keys[0]) {
for (i = 0; i < list->keys_size; ++i) {
list->keys[i] = (struct ly_mnode_leaf *)resolve_child((struct ly_mnode *)list,
list_orig->keys[i]->name, 0, LY_NODE_LEAF);
assert(list->keys[i]);
}
/* it was not resolved yet, add unres copy */
} else {
dup_unres(module, unres, list_orig, UNRES_LIST_KEYS, list);
}
}
list->unique = calloc(list->unique_size, sizeof *list->unique);
if (list_orig->unique) {
for (i = 0; i < list->unique_size; ++i) {
list->unique[i].leafs = calloc(list->unique[i].leafs_size, sizeof *list->unique[i].leafs);
for (j = 0; j < list->unique[i].leafs_size; j++) {
list->unique[i].leafs[j] = ly_uniq_find(list, list_orig->unique[i].leafs[j]);
}
}
} else {
for (i = 0; i < list->unique_size; ++i) {
/* HACK for unres */
list->unique[i].leafs = (struct ly_mnode_leaf **)list;
dup_unres(module, unres, &list_orig->unique[i], UNRES_LIST_UNIQ, &list->unique[i]);
}
}
if (list_orig->when) {
list->when = ly_when_dup(ctx, list_orig->when);
add_unres_mnode(module, unres, list->when, UNRES_WHEN, retval, 0);
}
break;
case LY_NODE_ANYXML:
anyxml->must_size = anyxml_orig->must_size;
anyxml->must = ly_restr_dup(ctx, anyxml_orig->must, anyxml->must_size);
for (i = 0; i < anyxml->must_size; ++i) {
add_unres_mnode(module, unres, &anyxml->must[i], UNRES_MUST, retval, 0);
}
if (anyxml_orig->when) {
anyxml->when = ly_when_dup(ctx, anyxml_orig->when);
add_unres_mnode(module, unres, anyxml->when, UNRES_WHEN, retval, 0);
}
break;
case LY_NODE_USES:
uses->grp = uses_orig->grp;
if (uses_orig->when) {
uses->when = ly_when_dup(ctx, uses_orig->when);
add_unres_mnode(module, unres, uses->when, UNRES_WHEN, (struct ly_mnode *)uses, 0);
}
uses->refine_size = uses_orig->refine_size;
uses->refine = ly_refine_dup(module, uses_orig->refine, uses_orig->refine_size, uses, unres);
uses->augment_size = uses_orig->augment_size;
uses->augment = ly_augment_dup(module, (struct ly_mnode *)uses, uses_orig->augment, uses_orig->augment_size);
add_unres_mnode(module, unres, uses, UNRES_USES, NULL, line);
break;
case LY_NODE_CASE:
if (cs_orig->when) {
cs->when = ly_when_dup(ctx, cs_orig->when);
add_unres_mnode(module, unres, cs->when, UNRES_WHEN, retval, 0);
}
break;
case LY_NODE_GROUPING:
case LY_NODE_RPC:
case LY_NODE_INPUT:
case LY_NODE_OUTPUT:
case LY_NODE_NOTIF:
mix->tpdf_size = mix_orig->tpdf_size;
mix->tpdf = ly_tpdf_dup(module, mnode->parent, mix_orig->tpdf, mix->tpdf_size, unres);
break;
default:
/* LY_NODE_AUGMENT */
goto error;
}
return retval;
error:
LOGDBG("error: %s:%d", __FILE__, __LINE__);
ly_mnode_free(retval);
return NULL;
}
API void
lys_free(struct ly_module *module)
{
struct ly_ctx *ctx;
int i;
if (!module) {
return;
}
/* remove schema from the context */
ctx = module->ctx;
if (ctx->models.used) {
for (i = 0; i < ctx->models.used; i++) {
if (ctx->models.list[i] == module) {
/* replace the position in the list by the last module in the list */
ctx->models.used--;
ctx->models.list[i] = ctx->models.list[ctx->models.used];
ctx->models.list[ctx->models.used] = NULL;
/* we are done */
break;
}
}
}
/* common part with struct ly_submodule */
module_free_common(module);
/* specific items to free */
lydict_remove(module->ctx, module->ns);
lydict_remove(module->ctx, module->prefix);
free(module);
}
/*
* op: 1 - enable, 0 - disable
*/
static int
ly_features_change(struct ly_module *module, const char *name, int op)
{
int all = 0;
int i, j, k;
if (!module || !name || !strlen(name)) {
return EXIT_FAILURE;
}
if (!strcmp(name, "*")) {
/* enable all */
all = 1;
}
/* module itself */
for (i = 0; i < module->features_size; i++) {
if (all || !strcmp(module->features[i].name, name)) {
if (op) {
module->features[i].flags |= LY_NODE_FENABLED;
/* enable referenced features (recursion) */
for (k = 0; k < module->features[i].features_size; k++) {
ly_features_change(module->features[i].features[k]->module,
module->features[i].features[k]->name, op);
}
} else {
module->features[i].flags &= ~LY_NODE_FENABLED;
}
if (!all) {
return EXIT_SUCCESS;
}
}
}
/* submodules */
for (j = 0; j < module->inc_size; j++) {
for (i = 0; i < module->inc[j].submodule->features_size; i++) {
if (all || !strcmp(module->inc[j].submodule->features[i].name, name)) {
if (op) {
module->inc[j].submodule->features[i].flags |= LY_NODE_FENABLED;
} else {
module->inc[j].submodule->features[i].flags &= ~LY_NODE_FENABLED;
}
if (!all) {
return EXIT_SUCCESS;
}
}
}
}
/* TODO submodules of submodules ... */
if (all) {
return EXIT_SUCCESS;
} else {
return EXIT_FAILURE;
}
}
API int
lys_features_enable(struct ly_module *module, const char *feature)
{
return ly_features_change(module, feature, 1);
}
API int
lys_features_disable(struct ly_module *module, const char *feature)
{
return ly_features_change(module, feature, 0);
}
API int
lys_features_state(struct ly_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 & LY_NODE_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 & LY_NODE_FENABLED) {
return 1;
} else {
return 0;
}
}
}
}
/* TODO submodules of submodules ... */
/* feature definition not found */
return -1;
}
API const char **
lys_features_list(struct ly_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 (states) {
*states = malloc((count + 1) * sizeof **states);
}
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 & LY_NODE_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 (module->inc[j].submodule->features[i].flags & LY_NODE_FENABLED) {
(*states)[count] = 1;
} else {
(*states)[count] = 0;
}
count++;
}
}
/* TODO submodules of submodules ... */
/* terminating NULL byte */
result[count] = NULL;
return result;
}
API struct lyd_node *
lyd_parse(struct ly_ctx *ctx, const char *data, LY_DFORMAT format)
{
if (!ctx || !data) {
LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
return NULL;
}
switch (format) {
case LY_DATA_XML:
return xml_read_data(ctx, data);
case LY_DATA_JSON:
default:
/* TODO */
return NULL;
}
return NULL;
}
API void
lyd_free(struct lyd_node *node)
{
struct lyd_node *next, *child;
if (!node) {
return;
}
if (!(node->schema->nodetype & (LY_NODE_LEAF | LY_NODE_LEAFLIST | LY_NODE_ANYXML))) {
/* free children */
LY_TREE_FOR_SAFE(node->child, next, child) {
lyd_free(child);
}
} else if (node->schema->nodetype == LY_NODE_ANYXML) {
lyxml_free_elem(node->schema->module->ctx, ((struct lyd_node_anyxml *)node)->value);
} else {
/* free value */
switch(((struct lyd_node_leaf *)node)->value_type) {
case LY_TYPE_BINARY:
case LY_TYPE_STRING:
lydict_remove(node->schema->module->ctx, ((struct lyd_node_leaf *)node)->value.string);
break;
case LY_TYPE_BITS:
if (((struct lyd_node_leaf *)node)->value.bit) {
free(((struct lyd_node_leaf *)node)->value.bit);
}
break;
default:
/* TODO nothing needed : LY_TYPE_BOOL, LY_TYPE_DEC64*/
break;
}
}
if (node->prev->next) {
node->prev->next = node->next;
} else if (node->parent) {
/* first node */
node->parent->child = node->next;
}
if (node->next) {
node->next->prev = node->prev;
}
free(node);
}
API int
lyd_is_last(struct lyd_node *node)
{
struct lyd_node *n;
if (!node->next) {
return 1;
}
for (n = node->next; n; n = n->next) {
switch (n->schema->nodetype) {
case LY_NODE_LIST:
if (!((struct lyd_node_list *)n)->lprev) {
return 0;
}
break;
case LY_NODE_LEAFLIST:
if (!((struct lyd_node_leaflist *)n)->lprev) {
return 0;
}
break;
default:
return 0;
}
}
return 1;
}