blob: b966054f63e93dc201a87289fa790a1baa79fd63 [file] [log] [blame]
/**
* @file yin.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief YIN parser for libyang
*
* 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.
*/
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "../libyang.h"
#include "../common.h"
#include "../context.h"
#include "../dict.h"
#include "../parser.h"
#include "../tree_internal.h"
#include "../xml.h"
enum LY_IDENT {
LY_IDENT_SIMPLE, /* only syntax rules */
LY_IDENT_FEATURE,
LY_IDENT_IDENTITY,
LY_IDENT_TYPE,
LY_IDENT_NODE,
LY_IDENT_NAME, /* uniqueness across the siblings */
LY_IDENT_PREFIX
};
#define LY_NSYIN "urn:ietf:params:xml:ns:yang:yin:1"
#define GETVAL(value, node, arg) \
value = lyxml_get_attr(node, arg, NULL); \
if (!value) { \
LOGVAL(VE_MISSARG, LOGLINE(node), arg, node->name); \
goto error; \
}
#define OPT_IDENT 0x01
#define OPT_CONFIG 0x02
#define OPT_MODULE 0x04
#define OPT_INHERIT 0x08
static int read_yin_common(struct ly_module *, struct ly_mnode *, struct ly_mnode *, struct lyxml_elem *, int);
struct obj_list {
void *obj;
struct obj_list *next;
unsigned int line;
};
static struct ly_mnode *read_yin_choice(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin,
int resolve, struct obj_list **unres);
static struct ly_mnode *read_yin_case(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin,
int resolve, struct obj_list **unres);
static struct ly_mnode *read_yin_anyxml(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin,
int resolve);
static struct ly_mnode *read_yin_container(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin,
int resolve, struct obj_list **unres);
static struct ly_mnode *read_yin_leaf(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin,
int resolve);
static struct ly_mnode *read_yin_leaflist(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin,
int resolve);
static struct ly_mnode *read_yin_list(struct ly_module *module,struct ly_mnode *parent, struct lyxml_elem *yin,
int resolve, struct obj_list **unres);
static struct ly_mnode *read_yin_uses(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *node,
int resolve, struct obj_list **unres);
static struct ly_mnode *read_yin_grouping(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *node,
int resolve, struct obj_list **unres);
static struct ly_when *read_yin_when(struct ly_module *module,struct lyxml_elem *yin);
static int
dup_typedef_check(const char *type, struct ly_tpdf *tpdf, int size)
{
int i;
for (i = 0; i < size; i++) {
if (!strcmp(type, tpdf[i].name)) {
/* name collision */
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
static int
dup_feature_check(const char *id, struct ly_module *module)
{
int i;
for (i = 0; i < module->features_size; i++) {
if (!strcmp(id, module->features[i].name)) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
static int
dup_prefix_check(const char *prefix, struct ly_module *module)
{
int i;
if (!module->type && module->prefix && !strcmp(module->prefix, prefix)) {
return EXIT_FAILURE;
}
for (i = 0; i < module->imp_size; i++) {
if (!strcmp(module->imp[i].prefix, prefix)) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
static int
check_identifier(const char *id, enum LY_IDENT type, unsigned int line,
struct ly_module *module, struct ly_mnode *parent)
{
int i;
int size;
struct ly_tpdf *tpdf;
struct ly_mnode *mnode;
assert(id);
/* check id syntax */
if (!(id[0] >= 'A' && id[0] <= 'Z') && !(id[0] >= 'a' && id[0] <= 'z') && id[0] != '_') {
LOGVAL(VE_INID, line, id, "invalid start character");
return EXIT_FAILURE;
}
for (i = 1; id[i]; i++) {
if (!(id[i] >= 'A' && id[i] <= 'Z') && !(id[i] >= 'a' && id[i] <= 'z')
&& !(id[i] >= '0' && id[i] <= '9') && id[i] != '_' && id[i] != '-' && id[i] != '.') {
LOGVAL(VE_INID, line, id, "invalid character");
return EXIT_FAILURE;
}
}
if (i > 64) {
LOGWRN("Identifier \"%s\" is long, you should use something shorter.", id);
}
switch (type) {
case LY_IDENT_NAME:
/* check uniqueness of the node within its siblings */
if (!parent) {
break;
}
LY_TREE_FOR(parent->child, mnode) {
if (mnode->name == id) {
LOGVAL(VE_INID, line, id, "name duplication");
return EXIT_FAILURE;
}
}
break;
case LY_IDENT_TYPE:
assert(module);
/* check collision with the built-in types */
if (!strcmp(id, "binary") || !strcmp(id, "bits") ||
!strcmp(id, "boolean") || !strcmp(id, "decimal64") ||
!strcmp(id, "empty") || !strcmp(id, "enumeration") ||
!strcmp(id, "identityref") || !strcmp(id, "instance-identifier") ||
!strcmp(id, "int8") || !strcmp(id, "int16") ||
!strcmp(id, "int32") || !strcmp(id, "int64") ||
!strcmp(id, "leafref") || !strcmp(id, "string") ||
!strcmp(id, "uint8") || !strcmp(id, "uint16") ||
!strcmp(id, "uint32") || !strcmp(id, "uint64") || !strcmp(id, "union")) {
LOGVAL(VE_SPEC, line, "Typedef name duplicates built-in type.");
return EXIT_FAILURE;
}
/* check locally scoped typedefs (avoid name shadowing) */
for (; parent; parent = parent->parent) {
switch (parent->nodetype) {
case LY_NODE_CONTAINER:
size = ((struct ly_mnode_container *)parent)->tpdf_size;
tpdf = ((struct ly_mnode_container *)parent)->tpdf;
break;
case LY_NODE_LIST:
size = ((struct ly_mnode_list *)parent)->tpdf_size;
tpdf = ((struct ly_mnode_list *)parent)->tpdf;
break;
case LY_NODE_GROUPING:
size = ((struct ly_mnode_grp *)parent)->tpdf_size;
tpdf = ((struct ly_mnode_grp *)parent)->tpdf;
break;
default:
continue;
}
if (dup_typedef_check(id, tpdf, size)) {
LOGVAL(VE_DUPID, line, "typedef", id);
return EXIT_FAILURE;
}
}
/* check top-level names */
if (dup_typedef_check(id, module->tpdf, module->tpdf_size)) {
LOGVAL(VE_DUPID, line, "typedef", id);
return EXIT_FAILURE;
}
/* check submodule's top-level names */
for (i = 0; i < module->inc_size; i++) {
if (dup_typedef_check(id, module->inc[i].submodule->tpdf, module->inc[i].submodule->tpdf_size)) {
LOGVAL(VE_DUPID, line, "typedef", id);
return EXIT_FAILURE;
}
}
/* check top-level names in the main module */
if (module->type) {
if (dup_typedef_check(id, ((struct ly_submodule *)module)->belongsto->tpdf,
((struct ly_submodule *)module)->belongsto->tpdf_size)) {
LOGVAL(VE_DUPID, line, "typedef", id);
return EXIT_FAILURE;
}
}
break;
case LY_IDENT_PREFIX:
assert(module);
if (module->type) {
/* go to the main module */
module = ((struct ly_submodule *)module)->belongsto;
}
/* check the main module itself */
if (dup_prefix_check(id, module)) {
LOGVAL(VE_DUPID, line, "prefix", id);
return EXIT_FAILURE;
}
/* and all its submodules */
for (i = 0; i < module->inc_size; i++) {
if (dup_prefix_check(id, (struct ly_module *)module->inc[i].submodule)) {
LOGVAL(VE_DUPID, line, "prefix", id);
return EXIT_FAILURE;
}
}
break;
case LY_IDENT_FEATURE:
assert(module);
/* check feature name uniqness*/
/* check features in the current module */
if (dup_feature_check(id, module)) {
LOGVAL(VE_DUPID, line, "feature", id);
return EXIT_FAILURE;
}
/* and all its submodules */
for (i = 0; i < module->inc_size; i++) {
if (dup_feature_check(id, (struct ly_module *)module->inc[i].submodule)) {
LOGVAL(VE_DUPID, line, "feature", id);
return EXIT_FAILURE;
}
}
break;
default:
/* no check required */
break;
}
return EXIT_SUCCESS;
}
static int
check_key(struct ly_mnode_leaf *key, uint8_t flags, struct ly_mnode_leaf **list, int index, unsigned int line,
const char *name, int len)
{
char *dup = NULL;
int j;
/* existence */
if (!key) {
if (name[len] != '\0') {
dup = strdup(name);
dup[len] = '\0';
name = dup;
}
LOGVAL(VE_KEY_MISS, line, name);
free(dup);
return EXIT_FAILURE;
}
/* uniqueness */
for (j = index - 1; j >= 0; j--) {
if (list[index] == list[j]) {
LOGVAL(VE_KEY_DUP, line, key->name);
return EXIT_FAILURE;
}
}
/* key is a leaf */
if (key->nodetype != LY_NODE_LEAF) {
LOGVAL(VE_KEY_NLEAF, line, key->name);
return EXIT_FAILURE;
}
/* type of the leaf is not built-in empty */
if (key->type.base == LY_TYPE_EMPTY) {
LOGVAL(VE_KEY_TYPE, line, key->name);
return EXIT_FAILURE;
}
/* config attribute is the same as of the list */
if ((flags & LY_NODE_CONFIG_MASK) != (key->flags & LY_NODE_CONFIG_MASK)) {
LOGVAL(VE_KEY_CONFIG, line, key->name);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
static int
check_mandatory(struct ly_mnode *mnode)
{
struct ly_mnode *child;
assert(mnode);
if (mnode->flags & LY_NODE_MAND_TRUE) {
return EXIT_FAILURE;
}
if (mnode->nodetype == LY_NODE_CASE || mnode->nodetype == LY_NODE_CHOICE) {
LY_TREE_FOR(mnode->child, child) {
if (check_mandatory(child)) {
return EXIT_FAILURE;
}
}
}
return EXIT_SUCCESS;
}
static int
check_default(struct ly_type *type, const char *value)
{
/* TODO - RFC 6020, sec. 7.3.4 */
(void)type;
(void)value;
return EXIT_SUCCESS;
}
static int
check_date(const char *date, unsigned int line)
{
int i;
assert(date);
if (strlen(date) != LY_REV_SIZE - 1) {
goto error;
}
for (i = 0; i < LY_REV_SIZE - 1; i++) {
if (i == 4 || i == 7) {
if (date[i] != '-') {
goto error;
}
} else if (!isdigit(date[i])) {
goto error;
}
}
return EXIT_SUCCESS;
error:
LOGVAL(VE_INDATE, line, date);
return EXIT_FAILURE;
}
static int
check_length(const char *expr, struct ly_type *type, unsigned int line)
{
const char *c = expr;
char *tail;
uint64_t limit = 0, n;
int flg = 1; /* first run flag */
assert(expr);
/* TODO check compatibility with the restriction defined on type from which this type is derived,
* it will be the same function to check that the value from instance data respect the restriction */
(void)type;
lengthpart:
while (isspace(*c)) {
c++;
}
/* lower boundary or explicit number */
if (!strncmp(c, "max", 3)) {
max:
c += 3;
while (isspace(*c)) {
c++;
}
if (*c != '\0') {
goto error;
}
return EXIT_SUCCESS;
} else if (!strncmp(c, "min", 3)) {
if (!flg) {
/* min cannot be used elsewhere than in the first length-part */
goto error;
} else {
flg = 0;
/* remember value/lower boundary */
limit = 0;
}
c += 3;
while (isspace(*c)) {
c++;
}
if (*c == '|') {
c++;
/* process next length-parth */
goto lengthpart;
} else if (*c == '\0') {
return EXIT_SUCCESS;
} else if (!strncmp(c, "..", 2)) {
upper:
c += 2;
while (isspace(*c)) {
c++;
}
if (*c == '\0') {
goto error;
}
/* upper boundary */
if (!strncmp(c, "max", 3)) {
goto max;
}
if (!isdigit(*c)) {
goto error;
}
n = strtol(c, &tail, 10);
c = tail;
while (isspace(*c)) {
c++;
}
if (n <= limit) {
goto error;
}
if (*c == '\0') {
return EXIT_SUCCESS;
} else if (*c == '|') {
c++;
/* remember the uppre boundary for check in next part */
limit = n;
/* process next length-parth */
goto lengthpart;
} else {
goto error;
}
} else {
goto error;
}
} else if (isdigit(*c)) {
/* number */
n = strtol(c, &tail, 10);
c = tail;
while (isspace(*c)) {
c++;
}
/* skip limit check in first length-part check */
if (!flg && n <= limit) {
goto error;
}
flg = 0;
limit = n;
if (*c == '|') {
c++;
/* process next length-parth */
goto lengthpart;
} else if (*c == '\0') {
return EXIT_SUCCESS;
} else if (!strncmp(c, "..", 2)) {
goto upper;
}
} /* else error */
error:
LOGVAL(VE_INARG, line, expr, "length");
return EXIT_FAILURE;
}
static const char *
read_yin_subnode(struct ly_ctx *ctx, struct lyxml_elem *node, const char *name)
{
int len;
/* there should be <text> child */
if (!node->child || !node->child->name || strcmp(node->child->name, name)) {
LOGWRN("Expected \"%s\" element in \"%s\" element.", name, node->name);
} else if (node->child->content) {
len = strlen(node->child->content);
return lydict_insert(ctx, node->child->content, len);
}
LOGVAL(VE_INARG, LOGLINE(node), name, node->name);
return NULL;
}
static struct ly_tpdf *
find_superior_type(const char *name, struct ly_module *module, struct ly_mnode *parent)
{
int i, j, found = 0;
int prefix_len = 0;
const char *qname;
struct ly_tpdf *tpdf;
int tpdf_size;
qname = strchr(name, ':');
if (!qname) {
/* no prefix, try built-in types */
for (i = 1; i < LY_DATA_TYPE_COUNT; i++) {
if (!strcmp(ly_types[i].def->name, name)) {
return ly_types[i].def;
}
}
qname = name;
} else {
/* set qname to correct position after colon */
prefix_len = qname - name;
qname++;
if (!strncmp(name, module->prefix, prefix_len) && !module->prefix[prefix_len]) {
/* prefix refers to the current module, ignore it */
prefix_len = 0;
}
}
if (!prefix_len && parent) {
/* search in local typedefs */
while (parent) {
switch (parent->nodetype) {
case LY_NODE_CONTAINER:
tpdf_size = ((struct ly_mnode_container *)parent)->tpdf_size;
tpdf = ((struct ly_mnode_container *)parent)->tpdf;
break;
case LY_NODE_LIST:
tpdf_size = ((struct ly_mnode_list *)parent)->tpdf_size;
tpdf = ((struct ly_mnode_list *)parent)->tpdf;
break;
case LY_NODE_GROUPING:
tpdf_size = ((struct ly_mnode_grp *)parent)->tpdf_size;
tpdf = ((struct ly_mnode_grp *)parent)->tpdf;
break;
/* TODO add rpc, notification, input, output */
default:
parent = parent->parent;
continue;
}
for (i = 0; i < tpdf_size; i++) {
if (!strcmp(tpdf[i].name, qname)) {
return &tpdf[i];
}
}
parent = parent->parent;
}
} else if (prefix_len) {
/* get module where to search */
for (i = 0; i < module->imp_size; i++) {
if (!strncmp(module->imp[i].prefix, name, prefix_len) && !module->imp[i].prefix[prefix_len]) {
module = module->imp[i].module;
found = 1;
break;
}
}
if (!found) {
return NULL;
}
}
/* search in top level typedefs */
for (i = 0; i < module->tpdf_size; i++) {
if (!strcmp(module->tpdf[i].name, qname)) {
return &module->tpdf[i];
}
}
/* search in submodules */
for (i = 0; i < module->inc_size; i++) {
for (j = 0; j < module->inc[i].submodule->tpdf_size; j++) {
if (!strcmp(module->inc[i].submodule->tpdf[j].name, qname)) {
return &module->inc[i].submodule->tpdf[j];
}
}
}
return NULL;
}
static struct ly_ident *
find_base_ident_sub(struct ly_module *module, struct ly_ident *ident, const char *basename)
{
unsigned int i;
struct ly_ident *base_iter;
struct ly_ident_der *der;
for (i = 0; i < module->ident_size; i++) {
if (!strcmp(basename, module->ident[i].name)) {
/* we are done */
if (!ident) {
/* just search for type, so do not modify anything, just return
* the base identity pointer
*/
return &module->ident[i];
}
/* we are resolving identity definition, so now update structures */
ident->base = base_iter = &module->ident[i];
while (base_iter) {
for (der = base_iter->der; der && der->next; der = der->next);
if (der) {
der->next = malloc(sizeof *der);
der = der->next;
} else {
ident->base->der = der = malloc(sizeof *der);
}
der->next = NULL;
der->ident = ident;
base_iter = base_iter->base;
}
return ident->base;
}
}
return NULL;
}
static struct ly_ident *
find_base_ident(struct ly_module *module, struct ly_ident *ident, struct lyxml_elem *node)
{
const char *name;
int prefix_len = 0;
int i, found = 0;
struct ly_ident *result;
const char *basename;
basename = lyxml_get_attr(node, "name", NULL);
if (!basename) {
LOGVAL(VE_MISSARG, LOGLINE(node), "name", "base");
return NULL;
}
/* search for the base identity */
name = strchr(basename, ':');
if (name) {
/* set name to correct position after colon */
prefix_len = name - basename;
name++;
if (!strncmp(basename, module->prefix, prefix_len) && !module->prefix[prefix_len]) {
/* prefix refers to the current module, ignore it */
prefix_len = 0;
}
} else {
name = basename;
}
if (prefix_len) {
/* get module where to search */
for (i = 0; i < module->imp_size; i++) {
if (!strncmp(module->imp[i].prefix, basename, prefix_len)
&& !module->imp[i].prefix[prefix_len]) {
module = module->imp[i].module;
found = 1;
break;
}
}
if (!found) {
/* identity refers unknown data model */
LOGVAL(VE_INPREFIX, LOGLINE(node), basename);
return NULL;
}
} else {
/* search in submodules */
for (i = 0; i < module->inc_size; i++) {
result = find_base_ident_sub((struct ly_module *)module->inc[i].submodule, ident, name);
if (result) {
return result;
}
}
}
/* search in the identified module */
result = find_base_ident_sub(module, ident, name);
if (!result) {
LOGVAL(VE_INARG, LOGLINE(node), basename, ident ? "identity" : "type");
}
return result;
}
static int
fill_yin_identity(struct ly_module *module, struct lyxml_elem *yin, struct ly_ident *ident)
{
struct lyxml_elem *node, *next;
if (read_yin_common(module, NULL, (struct ly_mnode *)ident, yin, OPT_IDENT | OPT_MODULE)) {
return EXIT_FAILURE;
}
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!strcmp(node->name, "base")) {
if (ident->base) {
LOGVAL(VE_TOOMANY, LOGLINE(node), "base", "identity");
return EXIT_FAILURE;
}
if (!find_base_ident(module, ident, node)) {
return EXIT_FAILURE;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(node), node->name, "identity");
return EXIT_FAILURE;
}
lyxml_free_elem(module->ctx, node);
}
return EXIT_SUCCESS;
}
static int
read_restr_substmt(struct ly_ctx *ctx, struct ly_restr *restr, struct lyxml_elem *yin)
{
struct lyxml_elem *next, *child;
const char *value;
LY_TREE_FOR_SAFE(yin->child, next, child) {
if (!strcmp(child->name, "description")) {
if (restr->dsc) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
return EXIT_FAILURE;
}
restr->dsc = read_yin_subnode(ctx, child, "text");
if (!restr->dsc) {
return EXIT_FAILURE;
}
} else if (!strcmp(child->name, "reference")) {
if (restr->ref) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
return EXIT_FAILURE;
}
restr->ref = read_yin_subnode(ctx, child, "text");
if (!restr->ref) {
return EXIT_FAILURE;
}
} else if (!strcmp(child->name, "error-app-tag")) {
if (restr->eapptag) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
return EXIT_FAILURE;
}
GETVAL(value, yin, "value");
restr->eapptag = lydict_insert(ctx, value, 0);
} else if (!strcmp(child->name, "error-message")) {
if (restr->emsg) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
return EXIT_FAILURE;
}
restr->emsg = read_yin_subnode(ctx, child, "value");
if (!restr->emsg) {
return EXIT_FAILURE;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
return EXIT_FAILURE;
}
lyxml_free_elem(ctx, child);
}
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
static int
fill_yin_type(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin, struct ly_type *type, struct obj_list **unres)
{
const char *value, *delim, *name;
struct lyxml_elem *next, *node;
struct ly_restr **restr;
struct obj_list *unres_new;
int i, j;
int64_t v, v_;
int64_t p, p_;
GETVAL(value, yin, "name");
if (!type->prefix) {
/* if we are trying to resolve unresolved type,
* prefix is already stored
*/
delim = strchr(value, ':');
if (delim) {
type->prefix = lydict_insert(module->ctx, value, delim - value);
}
}
type->der = find_superior_type(value, module, parent);
if (!type->der) {
if (unres) {
/* store it for later resolving */
LOGVRB("Unresolved type of \"%s\" (line %d), trying to resolve it later", value, LOGLINE(yin));
unres_new = calloc(1, sizeof *unres_new);
if (*unres) {
unres_new->next = *unres;
}
/* keep XML data for later processing */
type->der = (struct ly_tpdf *)lyxml_dup_elem(module->ctx, yin, NULL, 1);
unres_new->obj = type;
unres_new->line = LOGLINE(yin);
/* put it at the beginning of the unresolved list */
*unres = unres_new;
return EXIT_SUCCESS;
}
LOGVAL(VE_INARG, LOGLINE(yin), value, yin->name);
goto error;
}
type->base = type->der->type.base;
switch (type->base) {
case LY_TYPE_BITS:
/* RFC 6020 9.7.4 - bit */
/* get bit specifications, at least one must be present */
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!strcmp(node->name, "bit")) {
type->info.bits.count++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(yin->child), yin->child->name);
goto error;
}
}
if (!type->info.bits.count) {
if (type->der->type.der) {
/* this is just a derived type with no bit specified/required */
break;
}
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "bit", "type");
goto error;
}
type->info.bits.bit = calloc(type->info.bits.count, sizeof *type->info.bits.bit);
for (i = p = 0; yin->child; i++) {
GETVAL(value, yin->child, "name");
if (check_identifier(value, LY_IDENT_SIMPLE, LOGLINE(yin->child), NULL, NULL)) {
goto error;
}
type->info.bits.bit[i].name = lydict_insert(module->ctx, value, strlen(value));
if (read_yin_common(module, NULL, (struct ly_mnode *)&type->info.bits.bit[i], yin->child, 0)) {
type->info.bits.count = i + 1;
goto error;
}
/* check the name uniqueness */
for (j = 0; j < i; j++) {
if (!strcmp(type->info.bits.bit[j].name, type->info.bits.bit[i].name)) {
LOGVAL(VE_BITS_DUPNAME, LOGLINE(yin->child), type->info.bits.bit[i].name);
type->info.bits.count = i + 1;
goto error;
}
}
node = yin->child->child;
if (node && !strcmp(node->name, "position")) {
GETVAL(value, node, "value");
p_ = strtol(value, NULL, 10);
/* range check */
if (p_ < 0 || p_ > UINT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(node), value, "bit/position");
type->info.bits.count = i + 1;
goto error;
}
type->info.bits.bit[i].pos = (uint32_t)p_;
/* keep the highest enum value for automatic increment */
if (type->info.bits.bit[i].pos > p) {
p = type->info.bits.bit[i].pos;
p++;
} else {
/* check that the value is unique */
for (j = 0; j < i; j++) {
if (type->info.bits.bit[j].pos == type->info.bits.bit[i].pos) {
LOGVAL(VE_BITS_DUPVAL, LOGLINE(node), type->info.bits.bit[i].pos, type->info.bits.bit[i].name);
type->info.bits.count = i + 1;
goto error;
}
}
}
} else {
/* assign value automatically */
if (p > UINT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(yin->child), "4294967295", "bit/position");
type->info.bits.count = i + 1;
goto error;
}
type->info.bits.bit[i].pos = (uint32_t)p;
p++;
}
lyxml_free_elem(module->ctx, yin->child);
}
break;
case LY_TYPE_DEC64:
/* RFC 6020 9.2.4 - range and 9.3.4 - fraction-digits */
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!strcmp(node->name, "range")) {
if (type->info.dec64.range) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "value");
if (check_length(value, type, LOGLINE(node))) {
goto error;
}
type->info.dec64.range = calloc(1, sizeof *type->info.dec64.range);
type->info.dec64.range->expr = lydict_insert(module->ctx, value, 0);
/* get possible substatements */
if (read_restr_substmt(module->ctx, type->info.dec64.range, node)) {
goto error;
}
} else if (!strcmp(node->name, "fraction-digits")) {
if (type->info.dec64.dig) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "value");
v = strtol(value, NULL, 10);
/* range check */
if (v < 1 || v > 18) {
LOGVAL(VE_INARG, LOGLINE(node), value, node->name);
goto error;
}
type->info.dec64.dig = (uint8_t)v;
} else {
LOGVAL(VE_INSTMT, LOGLINE(node), node->name);
goto error;
}
lyxml_free_elem(module->ctx, node);
}
/* mandatory sub-statement(s) check */
if (!type->info.dec64.dig && !type->der->type.der) {
/* decimal64 type directly derived from built-in type requires fraction-digits */
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "fraction-digits", "type");
goto error;
}
break;
case LY_TYPE_ENUM:
/* RFC 6020 9.6 - enum */
/* get enum specifications, at least one must be present */
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!strcmp(node->name, "enum")) {
type->info.enums.count++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(yin->child), yin->child->name);
goto error;
}
}
if (!type->info.enums.count) {
if (type->der->type.der) {
/* this is just a derived type with no enum specified/required */
break;
}
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "enum", "type");
goto error;
}
type->info.enums.list = calloc(type->info.enums.count, sizeof *type->info.enums.list);
for (i = v = 0; yin->child; i++) {
GETVAL(value, yin->child, "name");
if (check_identifier(value, LY_IDENT_SIMPLE, LOGLINE(yin->child), NULL, NULL)) {
goto error;
}
type->info.enums.list[i].name = lydict_insert(module->ctx, value, strlen(value));
if (read_yin_common(module, NULL, (struct ly_mnode *)&type->info.enums.list[i], yin->child, 0)) {
type->info.enums.count = i + 1;
goto error;
}
/* the assigned name MUST NOT have any leading or trailing whitespace characters */
value = type->info.enums.list[i].name;
if (isspace(value[0]) || isspace(value[strlen(value) - 1])) {
LOGVAL(VE_ENUM_WS, LOGLINE(yin->child), value);
type->info.enums.count = i + 1;
goto error;
}
/* check the name uniqueness */
for (j = 0; j < i; j++) {
if (!strcmp(type->info.enums.list[j].name, type->info.enums.list[i].name)) {
LOGVAL(VE_ENUM_DUPNAME, LOGLINE(yin->child), type->info.enums.list[i].name);
type->info.enums.count = i + 1;
goto error;
}
}
node = yin->child->child;
if (node && !strcmp(node->name, "value")) {
GETVAL(value, node, "value");
v_ = strtol(value, NULL, 10);
/* range check */
if (v_ < INT32_MIN || v_ > INT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(node), value, "enum/value");
type->info.enums.count = i + 1;
goto error;
}
type->info.enums.list[i].value = v_;
/* keep the highest enum value for automatic increment */
if (type->info.enums.list[i].value > v) {
v = type->info.enums.list[i].value;
v++;
} else {
/* check that the value is unique */
for (j = 0; j < i; j++) {
if (type->info.enums.list[j].value == type->info.enums.list[i].value) {
LOGVAL(VE_ENUM_DUPVAL, LOGLINE(node), type->info.enums.list[i].value, type->info.enums.list[i].name);
type->info.enums.count = i + 1;
goto error;
}
}
}
} else {
/* assign value automatically */
if (v > INT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(yin->child), "2147483648", "enum/value");
type->info.enums.count = i + 1;
goto error;
}
type->info.enums.list[i].value = v;
v++;
}
lyxml_free_elem(module->ctx, yin->child);
}
break;
case LY_TYPE_IDENT:
/* RFC 6020 9.10 - base */
/* get base specification, exactly one must be present */
if (!yin->child) {
if (type->der->type.der) {
/* this is just a derived type with no base specified/required */
break;
}
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "base", "type");
goto error;
}
if (strcmp(yin->child->name, "base")) {
LOGVAL(VE_INSTMT, LOGLINE(yin->child), yin->child->name);
goto error;
}
if (yin->child->next) {
LOGVAL(VE_INSTMT, LOGLINE(yin->child->next), yin->child->next->name);
goto error;
}
type->info.ident.ref = find_base_ident(module, NULL, yin->child);
if (!type->info.ident.ref) {
return EXIT_FAILURE;
}
break;
case LY_TYPE_INST:
/* RFC 6020 9.13.2 - require-instance */
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!strcmp(node->name, "require-instance")) {
if (type->info.inst.req) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "value");
if (strcmp(value, "true")) {
type->info.inst.req = 1;
} else if (strcmp(value, "false")) {
type->info.inst.req = -1;
} else {
LOGVAL(VE_INARG, LOGLINE(node), value, node->name);
goto error;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(node), node->name);
goto error;
}
lyxml_free_elem(module->ctx, node);
}
break;
case LY_TYPE_BINARY:
/* RFC 6020 9.8.1, 9.4.4 - length, number of octets it contains */
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:
/* RFC 6020 9.2.4 - range */
/* length and range are actually the same restriction, so process
* them by this common code, we just need to differ the name and
* structure where the information will be stored
*/
if (type->base == LY_TYPE_BINARY) {
restr = &type->info.binary.length;
name = "length";
} else {
restr = &type->info.num.range;
name = "range";
}
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!strcmp(node->name, name)) {
if (*restr) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "value");
if (check_length(value, type, LOGLINE(node))) {
goto error;
}
*restr = calloc(1, sizeof **restr);
(*restr)->expr = lydict_insert(module->ctx, value, 0);
/* get possible substatements */
if (read_restr_substmt(module->ctx, *restr, node)) {
goto error;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(node), node->name);
goto error;
}
lyxml_free_elem(module->ctx, node);
}
break;
case LY_TYPE_LEAFREF:
/* RFC 6020 9.9.2 - path */
if (!yin->child) {
if (type->der->type.der) {
/* this is just a derived type with no path specified/required */
break;
}
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "path", "type");
goto error;
}
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!strcmp(node->name, "path")) {
if (type->info.lref.path) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "value");
/* TODO
* it would be nice to perform here a check that target is leaf or leaf-list,
* but schema is not finished yet and path can point almost to anywhere, so
* we will have to check the path at the end of parsing the schema.
*/
type->info.lref.path = lydict_insert(module->ctx, value, 0);
} else {
LOGVAL(VE_INSTMT, LOGLINE(node), node->name);
goto error;
}
lyxml_free_elem(module->ctx, node);
}
break;
case LY_TYPE_STRING:
/* RFC 6020 9.4.4 - length */
/* RFC 6020 9.4.6 - pattern */
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!strcmp(node->name, "length")) {
if (type->info.str.length) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "value");
if (check_length(value, type, LOGLINE(node))) {
goto error;
}
type->info.str.length = calloc(1, sizeof *type->info.str.length);
type->info.str.length->expr = lydict_insert(module->ctx, value, 0);
/* get possible sub-statements */
if (read_restr_substmt(module->ctx, type->info.str.length, node)) {
goto error;
}
lyxml_free_elem(module->ctx, node);
} else if (!strcmp(node->name, "pattern")) {
type->info.str.pat_count++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(node), node->name);
goto error;
}
}
/* store patterns in array */
if (type->info.str.pat_count) {
type->info.str.patterns = calloc(type->info.str.pat_count, sizeof *type->info.str.patterns);
for (i = 0; yin->child; i++) {
GETVAL(value, yin->child, "value");
type->info.str.patterns[i].expr = lydict_insert(module->ctx, value, 0);
/* get possible sub-statements */
if (read_restr_substmt(module->ctx, &type->info.str.patterns[i], yin->child)) {
goto error;
}
lyxml_free_elem(module->ctx, yin->child);
}
}
break;
case LY_TYPE_UNION:
/* RFC 6020 7.4 - type */
/* count number of types in union */
i = 0;
LY_TREE_FOR(yin->child, node) {
if (!strcmp(node->name, "type")) {
i++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(node), node->name);
goto error;
}
}
if (!i) {
if (type->der->type.der) {
/* this is just a derived type with no base specified/required */
break;
}
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "type", "(union) type");
goto error;
}
/* allocate array for union's types ... */
type->info.uni.type = calloc(i, sizeof *type->info.uni.type);
/* ... and fill the structures */
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (fill_yin_type(module, parent, node, &type->info.uni.type[type->info.uni.count], unres)) {
goto error;
}
type->info.uni.count++;
/* union's type cannot be empty or leafref */
if (type->info.uni.type[type->info.uni.count - 1].base == LY_TYPE_EMPTY) {
LOGVAL(VE_INARG, LOGLINE(node), "empty", node->name);
goto error;
} else if (type->info.uni.type[type->info.uni.count - 1].base == LY_TYPE_LEAFREF) {
LOGVAL(VE_INARG, LOGLINE(node), "leafref", node->name);
goto error;
}
lyxml_free_elem(module->ctx, node);
}
break;
default:
/* no sub-statement allowed in:
* LY_TYPE_BOOL, LY_TYPE_EMPTY
*/
if (yin->child) {
LOGVAL(VE_INSTMT, LOGLINE(yin->child), yin->child->name);
goto error;
}
break;
}
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
static int
fill_yin_typedef(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin, struct ly_tpdf *tpdf, struct obj_list **unres)
{
const char *value;
struct lyxml_elem *node, *next;
int r = 0;
GETVAL(value, yin, "name");
if (check_identifier(value, LY_IDENT_TYPE, LOGLINE(yin), module, parent)) {
goto error;
}
tpdf->name = lydict_insert(module->ctx, value, strlen(value));
/* generic part - status, description, reference */
if (read_yin_common(module, NULL, (struct ly_mnode *)tpdf, yin, OPT_IDENT)) {
goto error;
}
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!strcmp(node->name, "type")) {
if (tpdf->type.der) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
r = fill_yin_type(module, parent, node, &tpdf->type, unres);
} else if (!strcmp(node->name, "default")) {
if (tpdf->dflt) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "value");
tpdf->dflt = lydict_insert(module->ctx, value, strlen(value));
} else if (!strcmp(node->name, "units")) {
if (tpdf->units) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "name");
tpdf->units = lydict_insert(module->ctx, value, strlen(value));
} else {
LOGVAL(VE_INSTMT, LOGLINE(node), value);
r = 1;
}
lyxml_free_elem(module->ctx, node);
if (r) {
goto error;
}
}
/* check mandatory value */
if (!tpdf->type.der) {
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "type", yin->name);
goto error;
}
/* check default value */
if (check_default(&tpdf->type, tpdf->dflt)) {
goto error;
}
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
static struct ly_feature *
resolve_feature(const char *name, struct ly_module *module, unsigned int line)
{
const char *prefix;
unsigned int prefix_len = 0;
int i, j, found = 0;
assert(name);
assert(module);
/* check prefix */
prefix = name;
name = strchr(prefix, ':');
if (name) {
/* there is prefix */
prefix_len = name - prefix;
name++;
/* check whether the prefix points to the current module */
if (!strncmp(prefix, module->prefix, prefix_len) && !module->prefix[prefix_len]) {
/* then ignore prefix and works as there is no prefix */
prefix_len = 0;
}
} else {
/* no prefix, set pointers correctly */
name = prefix;
}
if (prefix_len) {
/* search in imported modules */
for (i = 0; i < module->imp_size; i++) {
if (!strncmp(module->imp[i].prefix, prefix, prefix_len) && !module->imp[i].prefix[prefix_len]) {
module = module->imp[i].module;
found = 1;
break;
}
}
if (!found) {
/* identity refers unknown data model */
LOGVAL(VE_INPREFIX, line, prefix);
return NULL;
}
} else {
/* search in submodules */
for (i = 0; i < module->inc_size; i++) {
for (j = 0; j < module->inc[i].submodule->features_size; j++) {
if (!strcmp(name, module->inc[i].submodule->features[j].name)) {
return &(module->inc[i].submodule->features[j]);
}
}
}
}
/* search in the identified module */
for (j = 0; j < module->features_size; j++) {
if (!strcmp(name, module->features[j].name)) {
return &module->features[j];
}
}
/* not found */
return NULL;
}
static int
fill_yin_feature(struct ly_module *module, struct lyxml_elem *yin, struct ly_feature *f)
{
const char *value;
struct lyxml_elem *child, *next;
int c = 0;
GETVAL(value, yin, "name");
if (check_identifier(value, LY_IDENT_FEATURE, LOGLINE(yin), module, NULL)) {
goto error;
}
f->name = lydict_insert(module->ctx, value, strlen(value));
if (read_yin_common(module, NULL, (struct ly_mnode *)f, yin, OPT_MODULE)) {
goto error;
}
LY_TREE_FOR_SAFE(yin->child, next, child) {
if (!strcmp(child->name, "if-feature")) {
c++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
goto error;
}
}
if (c) {
f->features = calloc(c, sizeof *f->features);
}
LY_TREE_FOR_SAFE(yin->child, next, child) {
GETVAL(value, child, "name");
f->features[f->features_size] = resolve_feature(value, module, LOGLINE(child));
if (!f->features[f->features_size]) {
goto error;
}
f->features_size++;
}
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
static int
fill_yin_must(struct ly_module *module, struct lyxml_elem *yin, struct ly_restr *must)
{
const char *value;
GETVAL(value, yin, "condition");
must->expr = lydict_insert(module->ctx, value, strlen(value));
return read_restr_substmt(module->ctx, must, yin);
error: /* GETVAL requires this label */
return EXIT_FAILURE;
}
static int
parse_unique(struct ly_mnode *parent, struct lyxml_elem *node, struct ly_unique *uniq_s)
{
const char *value;
char *uniq_str = NULL, *uniq_val, *start;
int i, j;
/* count the number of unique values */
GETVAL(value, node, "tag");
uniq_val = uniq_str = strdup(value);
uniq_s->leafs_size = 0;
while ((uniq_val = strpbrk(uniq_val, " \t\n"))) {
uniq_s->leafs_size++;
while (isspace(*uniq_val)) {
uniq_val++;
}
}
uniq_s->leafs_size++;
uniq_s->leafs = calloc(uniq_s->leafs_size, sizeof *uniq_s->leafs);
/* interconnect unique values with the leafs */
uniq_val = uniq_str;
for (i = 0; uniq_val && i < uniq_s->leafs_size; i++) {
start = uniq_val;
if ((uniq_val = strpbrk(start, " \t\n"))) {
*uniq_val = '\0'; /* add terminating NULL byte */
uniq_val++;
while (isspace(*uniq_val)) {
uniq_val++;
}
} /* else only one nodeid present/left already NULL byte terminated */
uniq_s->leafs[i] = (struct ly_mnode_leaf *)resolve_schema_nodeid(start, parent, parent->module, LY_NODE_USES);
if (!uniq_s->leafs[i] || uniq_s->leafs[i]->nodetype != LY_NODE_LEAF) {
LOGVAL(VE_INARG, LOGLINE(node), start, node->name);
if (!uniq_s->leafs[i]) {
LOGVAL(VE_SPEC, 0, "Target leaf not found.");
} else {
LOGVAL(VE_SPEC, 0, "Target is not a leaf.");
}
goto error;
}
for (j = 0; j < i; j++) {
if (uniq_s->leafs[j] == uniq_s->leafs[i]) {
LOGVAL(VE_INARG, LOGLINE(node), start, node->name);
LOGVAL(VE_SPEC, 0, "The identifier is not unique");
goto error;
}
}
}
free(uniq_str);
return EXIT_SUCCESS;
error:
free(uniq_s->leafs);
free(uniq_str);
return EXIT_FAILURE;
}
/*
* type: 0 - min, 1 - max
*/
static int
deviate_minmax(struct ly_mnode *target, struct lyxml_elem *node, struct ly_deviate *d, int type)
{
const char *value;
char *endptr;
unsigned long val;
uint32_t *ui32val;
/* check target node type */
if (target->nodetype == LY_NODE_LEAFLIST) {
if (type) {
ui32val = &((struct ly_mnode_leaflist *)target)->max;
} else {
ui32val = &((struct ly_mnode_leaflist *)target)->min;
}
} else if (target->nodetype == LY_NODE_LIST) {
if (type) {
ui32val = &((struct ly_mnode_list *)target)->max;
} else {
ui32val = &((struct ly_mnode_list *)target)->min;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(node), node->name);
LOGVAL(VE_SPEC, 0, "Target node does not allow \"%s\" property.", node->name);
goto error;
}
GETVAL(value, node, "value");
while (isspace(value[0])) {
value++;
}
/* convert it to uint32_t */
errno = 0;
endptr = NULL;
val = strtoul(value, &endptr, 10);
if (*endptr || value[0] == '-' || errno || val > UINT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(node), value, node->name);
goto error;
}
if (type) {
d->max = (uint32_t)val;
} else {
d->min = (uint32_t)val;
}
if (d->mod == LY_DEVIATE_ADD) {
/* check that there is no current value */
if (*ui32val) {
LOGVAL(VE_INSTMT, LOGLINE(node), node->name);
LOGVAL(VE_SPEC, 0, "Adding property that already exists.");
goto error;
}
}
if (d->mod == LY_DEVIATE_DEL) {
/* check values */
if ((uint32_t)val != *ui32val) {
LOGVAL(VE_INARG, LOGLINE(node), value, node->name);
LOGVAL(VE_SPEC, 0, "Value differs from the target being deleted.");
goto error;
}
/* remove current min-elements value of the target */
*ui32val = 0;
} else { /* add (already checked) and replace */
/* set new value specified in deviation */
*ui32val = (uint32_t)val;
}
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
static int
fill_yin_deviation(struct ly_module *module, struct lyxml_elem *yin, struct ly_deviation *dev)
{
const char *value, **stritem;
struct lyxml_elem *next, *child, *develem;
int c_dev = 0, c_must, c_uniq;
int f_min = 0; /* flags */
int i, j;
struct ly_deviate *d = NULL;
struct ly_mnode *mnode = NULL;
struct ly_mnode_choice *choice = NULL;
struct ly_mnode_leaf *leaf = NULL;
struct ly_mnode_list *list = NULL;
struct ly_type *t = NULL;
uint8_t *trg_must_size = NULL;
struct ly_restr **trg_must = NULL;
GETVAL(value, yin, "target-node");
dev->target_name = lydict_insert(module->ctx, value, 0);
/* resolve target node */
dev->target = resolve_schema_nodeid(dev->target_name, NULL, module, LY_NODE_AUGMENT);
if (!dev->target) {
LOGVAL(VE_INARG, LOGLINE(yin), dev->target_name, yin->name);
goto error;
}
if (dev->target->module == module) {
LOGVAL(VE_SPEC, LOGLINE(yin), "Deviating own module is not allowed.");
goto error;
}
/* mark the target module as deviated */
dev->target->module->deviated = 1;
LY_TREE_FOR_SAFE(yin->child, next, child) {
if (!strcmp(child->name, "description")) {
if (dev->dsc) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
dev->dsc = read_yin_subnode(module->ctx, child, "text");
if (!dev->dsc) {
goto error;
}
} else if (!strcmp(child->name, "reference")) {
if (dev->ref) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
dev->ref = read_yin_subnode(module->ctx, child, "text");
if (!dev->ref) {
goto error;
}
} else if (!strcmp(child->name, "deviate")) {
c_dev++;
/* skip lyxml_free_elem() at the end of the loop, node will be
* further processed later
*/
continue;
} else {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
goto error;
}
lyxml_free_elem(module->ctx, child);
}
if (c_dev) {
dev->deviate = calloc(c_dev, sizeof *dev->deviate);
}
LY_TREE_FOR(yin->child, develem) {
/* init */
f_min = 0;
c_must = 0;
c_uniq = 0;
/* get deviation type */
GETVAL(value, develem, "value");
if (!strcmp(value, "not-supported")) {
dev->deviate[dev->deviate_size].mod = LY_DEVIATE_NO;
/* no property expected in this case */
if (develem->child) {
LOGVAL(VE_INSTMT, LOGLINE(develem->child), develem->child->name);
goto error;
}
/* and neither any other deviate statement is expected,
* not-supported deviation must be the only deviation of the target
*/
if (dev->deviate_size || develem->next) {
LOGVAL(VE_INARG, LOGLINE(develem), value, develem->name);
LOGVAL(VE_SPEC, 0, "\"not-supported\" deviation cannot be combined with any other deviation.");
goto error;
}
/* remove target node */
ly_mnode_free(dev->target);
dev->target = NULL;
dev->deviate_size = 1;
return EXIT_SUCCESS;
} else if (!strcmp(value, "add")) {
dev->deviate[dev->deviate_size].mod = LY_DEVIATE_ADD;
} else if (!strcmp(value, "replace")) {
dev->deviate[dev->deviate_size].mod = LY_DEVIATE_RPL;
} else if (!strcmp(value, "delete")) {
dev->deviate[dev->deviate_size].mod = LY_DEVIATE_DEL;
} else {
LOGVAL(VE_INARG, LOGLINE(develem), value, develem->name);
goto error;
}
d = &dev->deviate[dev->deviate_size];
/* process deviation properties */
LY_TREE_FOR_SAFE(develem->child, next, child) {
if (!strcmp(child->name, "config")) {
if (d->flags & LY_NODE_CONFIG_MASK) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
/* for we deviate from RFC 6020 and allow config property even it is/is not
* specified in the target explicitly since config property inherits. So we expect
* that config is specified in every node. But for delete, we check that the value
* is the same as here in deviation
*/
GETVAL(value, child, "value");
if (!strcmp(value, "false")) {
d->flags |= LY_NODE_CONFIG_R;
} else if (!strcmp(value, "true")) {
d->flags |= LY_NODE_CONFIG_W;
} else {
LOGVAL(VE_INARG, LOGLINE(child), value, child->name);
goto error;
}
if (d->mod == LY_DEVIATE_DEL) {
/* check values */
if ((d->flags & LY_NODE_CONFIG_MASK) != (dev->target->flags & LY_NODE_CONFIG_MASK)) {
LOGVAL(VE_INARG, LOGLINE(child), value, child->name);
LOGVAL(VE_SPEC, 0, "Value differs from the target being deleted.");
goto error;
}
/* remove current config value of the target ... */
dev->target->flags &= ~LY_NODE_CONFIG_MASK;
/* ... and inherit config value from the target's parent */
if (dev->target->parent) {
dev->target->flags |= dev->target->parent->flags & LY_NODE_CONFIG_MASK;
} else {
/* default config is true */
dev->target->flags |= LY_NODE_CONFIG_W;
}
} else { /* add and replace are the same in this case */
/* remove current config value of the target ... */
dev->target->flags &= ~LY_NODE_CONFIG_MASK;
/* ... and replace it with the value specified in deviation */
dev->target->flags |= d->flags & LY_NODE_CONFIG_MASK;
}
} else if (!strcmp(child->name, "default")) {
if (d->dflt) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
GETVAL(value, child, "value");
d->dflt = lydict_insert(module->ctx, value, 0);
if (dev->target->nodetype == LY_NODE_CHOICE) {
choice = (struct ly_mnode_choice *)dev->target;
if (d->mod == LY_DEVIATE_ADD) {
/* check that there is no current value */
if (choice->dflt) {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Adding property that already exists.");
goto error;
}
}
mnode = resolve_schema_nodeid(d->dflt, (struct ly_mnode *)choice, choice->module, LY_NODE_CHOICE);
if (d->mod == LY_DEVIATE_DEL) {
if (!choice->dflt || choice->dflt != mnode) {
LOGVAL(VE_INARG, LOGLINE(child), value, child->name);
LOGVAL(VE_SPEC, 0, "Value differs from the target being deleted.");
goto error;
}
} else { /* add (already checked) and replace */
choice->dflt = mnode;
if (!choice->dflt) {
/* default branch not found */
LOGVAL(VE_INARG, LOGLINE(yin), value, "default");
goto error;
}
}
} else if (dev->target->nodetype == LY_NODE_LEAF) {
leaf = (struct ly_mnode_leaf *)dev->target;
if (d->mod == LY_DEVIATE_ADD) {
/* check that there is no current value */
if (leaf->dflt) {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Adding property that already exists.");
goto error;
}
}
if (d->mod == LY_DEVIATE_DEL) {
if (!leaf->dflt || leaf->dflt != d->dflt) {
LOGVAL(VE_INARG, LOGLINE(child), value, child->name);
LOGVAL(VE_SPEC, 0, "Value differs from the target being deleted.");
goto error;
}
/* remove value */
lydict_remove(leaf->module->ctx, leaf->dflt);
leaf->dflt = NULL;
} else { /* add (already checked) and replace */
/* remove value */
lydict_remove(leaf->module->ctx, leaf->dflt);
/* set new value */
leaf->dflt = lydict_insert(leaf->module->ctx, d->dflt, 0);
}
} else {
/* invalid target for default value */
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Target node does not allow \"%s\" property.", child->name);
goto error;
}
} else if (!strcmp(child->name, "mandatory")) {
if (d->flags & LY_NODE_MAND_MASK) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
/* check target node type */
if (!(dev->target->nodetype &= (LY_NODE_LEAF | LY_NODE_CHOICE | LY_NODE_ANYXML))) {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Target node does not allow \"%s\" property.", child->name);
goto error;
}
GETVAL(value, child, "value");
if (!strcmp(value, "false")) {
d->flags |= LY_NODE_MAND_FALSE;
} else if (!strcmp(value, "true")) {
d->flags |= LY_NODE_MAND_TRUE;
} else {
LOGVAL(VE_INARG, LOGLINE(child), value, child->name);
goto error;
}
if (d->mod == LY_DEVIATE_ADD) {
/* check that there is no current value */
if (dev->target->flags & LY_NODE_MAND_MASK) {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Adding property that already exists.");
goto error;
}
}
if (d->mod == LY_DEVIATE_DEL) {
/* check values */
if ((d->flags & LY_NODE_MAND_MASK) != (dev->target->flags & LY_NODE_MAND_MASK)) {
LOGVAL(VE_INARG, LOGLINE(child), value, child->name);
LOGVAL(VE_SPEC, 0, "Value differs from the target being deleted.");
goto error;
}
/* remove current mandatory value of the target */
dev->target->flags &= ~LY_NODE_MAND_MASK;
} else { /* add (already checked) and replace */
/* remove current mandatory value of the target ... */
dev->target->flags &= ~LY_NODE_MAND_MASK;
/* ... and replace it with the value specified in deviation */
dev->target->flags |= d->flags & LY_NODE_MAND_MASK;
}
} else if (!strcmp(child->name, "min-elements")) {
if (f_min) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
f_min = 1;
if (deviate_minmax(dev->target, child, d, 0)) {
goto error;
}
} else if (!strcmp(child->name, "max-elements")) {
if (d->max) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
if (deviate_minmax(dev->target, child, d, 1)) {
goto error;
}
} else if (!strcmp(child->name, "must")) {
c_must++;
/* skip lyxml_free_elem() at the end of the loop, this node will be processed later */
continue;
} else if (!strcmp(child->name, "type")) {
if (d->type) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
/* check target node type */
if (dev->target->nodetype == LY_NODE_LEAF) {
t = &((struct ly_mnode_leaf *)dev->target)->type;
} else if (dev->target->nodetype == LY_NODE_LEAFLIST) {
t = &((struct ly_mnode_leaflist *)dev->target)->type;
} else {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Target node does not allow \"%s\" property.", child->name);
goto error;
}
if (d->mod == LY_DEVIATE_ADD) {
/* not allowed, type is always present at the target */
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Adding property that already exists.");
goto error;
} else if (d->mod == LY_DEVIATE_DEL) {
/* not allowed, type cannot be deleted from the target */
LOGVAL(VE_INARG, LOGLINE(child), value, child->name);
LOGVAL(VE_SPEC, 0, "Deleteing type from the target is not allowed.");
goto error;
}
/* replace */
/* remove current units value of the target ... */
ly_type_free(dev->target->module->ctx, t);
/* ... and replace it with the value specified in deviation */
if (fill_yin_type(module, dev->target, child, t, NULL)) {
goto error;
}
d->type = t;
} else if (!strcmp(child->name, "unique")) {
c_uniq++;
/* skip lyxml_free_elem() at the end of the loop, this node will be processed later */
continue;
} else if (!strcmp(child->name, "units")) {
if (d->units) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
/* check target node type */
if (dev->target->nodetype == LY_NODE_LEAFLIST) {
stritem = &((struct ly_mnode_leaflist *)dev->target)->units;
} else if (dev->target->nodetype == LY_NODE_LEAF) {
stritem = &((struct ly_mnode_leaf *)dev->target)->units;
} else {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Target node does not allow \"%s\" property.", child->name);
goto error;
}
/* get units value */
GETVAL(value, child, "name");
d->units = lydict_insert(module->ctx, value, 0);
/* apply to target */
if (d->mod == LY_DEVIATE_ADD) {
/* check that there is no current value */
if (*stritem) {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Adding property that already exists.");
goto error;
}
}
if (d->mod == LY_DEVIATE_DEL) {
/* check values */
if (*stritem != d->units) {
LOGVAL(VE_INARG, LOGLINE(child), value, child->name);
LOGVAL(VE_SPEC, 0, "Value differs from the target being deleted.");
goto error;
}
/* remove current units value of the target */
lydict_remove(dev->target->module->ctx, *stritem);
} else { /* add (already checked) and replace */
/* remove current units value of the target ... */
lydict_remove(dev->target->module->ctx, *stritem);
/* ... and replace it with the value specified in deviation */
*stritem = lydict_insert(module->ctx, value, 0);
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
goto error;
}
lyxml_free_elem(module->ctx, child);
}
if (c_must) {
/* check target node type */
switch (dev->target->nodetype) {
case LY_NODE_LEAF:
trg_must = &((struct ly_mnode_leaf *)dev->target)->must;
trg_must_size = &((struct ly_mnode_leaf *)dev->target)->must_size;
break;
case LY_NODE_CONTAINER:
trg_must = &((struct ly_mnode_container *)dev->target)->must;
trg_must_size = &((struct ly_mnode_container *)dev->target)->must_size;
break;
case LY_NODE_LEAFLIST:
trg_must = &((struct ly_mnode_leaflist *)dev->target)->must;
trg_must_size = &((struct ly_mnode_leaflist *)dev->target)->must_size;
break;
case LY_NODE_LIST:
trg_must = &((struct ly_mnode_list *)dev->target)->must;
trg_must_size = &((struct ly_mnode_list *)dev->target)->must_size;
break;
case LY_NODE_ANYXML:
trg_must = &((struct ly_mnode_anyxml *)dev->target)->must;
trg_must_size = &((struct ly_mnode_anyxml *)dev->target)->must_size;
break;
default:
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Target node does not allow \"%s\" property.", child->name);
goto error;
}
if (d->mod == LY_DEVIATE_RPL) {
/* remove target's musts and allocate new array for it */
if (!*trg_must) {
LOGVAL(VE_INARG, LOGLINE(develem), "replace", "deviate");
LOGVAL(VE_SPEC, 0, "Property \"must\" to replace does not exists in target.");
goto error;
}
for (i = 0; i < list->must_size; i++) {
ly_restr_free(dev->target->module->ctx, &(*trg_must[i]));
}
free(*trg_must);
*trg_must = d->must = calloc(c_must, sizeof *d->must);
d->must_size = c_must;
*trg_must_size = 0;
} else if (d->mod == LY_DEVIATE_ADD) {
/* reallocate the must array of the target */
d->must = realloc(*trg_must, (c_must + *trg_must_size) * sizeof *d->must);
*trg_must = d->must;
d->must = &(*trg_must[*trg_must_size]);
d->must_size = c_must;
} else { /* LY_DEVIATE_DEL */
d->must = calloc(c_must, sizeof *d->must);
}
}
if (c_uniq) {
/* check target node type */
if (dev->target->nodetype != LY_NODE_LIST) {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
LOGVAL(VE_SPEC, 0, "Target node does not allow \"%s\" property.", child->name);
goto error;
}
list = (struct ly_mnode_list *)dev->target;
if (d->mod == LY_DEVIATE_RPL) {
/* remove target's unique and allocate new array for it */
if (!list->unique) {
LOGVAL(VE_INARG, LOGLINE(develem), "replace", "deviate");
LOGVAL(VE_SPEC, 0, "Property \"unique\" to replace does not exists in target.");
goto error;
}
for (i = 0; i < list->unique_size; i++) {
free(list->unique[i].leafs);
}
free(list->unique);
list->unique = d->unique = calloc(c_uniq, sizeof *d->unique);
d->unique_size = c_uniq;
list->unique_size = 0;
} else if (d->mod == LY_DEVIATE_ADD) {
/* reallocate the unique array of the target */
d->unique = realloc(list->unique, (c_uniq + list->unique_size) * sizeof *d->unique);
list->unique = d->unique;
d->unique = &list->unique[list->unique_size];
d->unique_size = c_uniq;
} else { /* LY_DEVIATE_DEL */
d->unique = calloc(c_uniq, sizeof *d->unique);
}
}
/* process deviation properties with 0..n cardinality */
LY_TREE_FOR_SAFE(develem->child, next, child) {
if (!strcmp(child->name, "must")) {
if (d->mod == LY_DEVIATE_DEL) {
if (fill_yin_must(module, child, &d->must[d->must_size])) {
goto error;
}
/* find must to delete, we are ok with just matching conditions */
for (i = 0; i < *trg_must_size; i++) {
if (d->must[d->must_size].expr == (*trg_must)[i].expr) {
/* we have a match, free the must structure ... */
ly_restr_free(dev->target->module->ctx, &(*trg_must[i]));
/* ... and maintain the array */
(*trg_must_size)--;
if (i != *trg_must_size) {
(*trg_must)[i].expr = (*trg_must)[*trg_must_size].expr;
(*trg_must)[i].dsc = (*trg_must)[*trg_must_size].dsc;
(*trg_must)[i].ref = (*trg_must)[*trg_must_size].ref;
(*trg_must)[i].eapptag = (*trg_must)[*trg_must_size].eapptag;
(*trg_must)[i].emsg = (*trg_must)[*trg_must_size].emsg;
}
if (!(*trg_must_size)) {
free(*trg_must);
*trg_must = NULL;
} else {
(*trg_must)[*trg_must_size].expr = NULL;
(*trg_must)[*trg_must_size].dsc = NULL;
(*trg_must)[*trg_must_size].ref = NULL;
(*trg_must)[*trg_must_size].eapptag = NULL;
(*trg_must)[*trg_must_size].emsg = NULL;
}
i = -1; /* set match flag */
break;
}
}
d->must_size++;
if (i != -1) {
/* no match found */
LOGVAL(VE_INARG, LOGLINE(child), d->must[d->must_size - 1].expr, child->name);
LOGVAL(VE_SPEC, 0, "Value does not match any must from the target.");
goto error;
}
} else { /* replace or add */
if (fill_yin_must(dev->target->module, child, &((*trg_must)[*trg_must_size]))) {
goto error;
}
(*trg_must_size)++;
}
} else if (!strcmp(child->name, "unique")) {
if (d->mod == LY_DEVIATE_DEL) {
if (parse_unique(dev->target, child, &d->unique[d->unique_size])) {
goto error;
}
/* find unique structures to delete */
for (i = 0; i < list->unique_size; i++) {
if (list->unique[i].leafs_size != d->unique[d->unique_size].leafs_size) {
continue;
}
for (j = 0; j < d->unique[d->unique_size].leafs_size; j++) {
if (list->unique[i].leafs[j] != d->unique[d->unique_size].leafs[j]) {
break;
}
}
if (j == d->unique[d->unique_size].leafs_size) {
/* we have a match, free the unique structure ... */
free(list->unique[i].leafs);
/* ... and maintain the array */
list->unique_size--;
if (i != list->unique_size) {
list->unique[i].leafs_size = list->unique[list->unique_size].leafs_size;
list->unique[i].leafs = list->unique[list->unique_size].leafs;
}
if (!list->unique_size) {
free(list->unique);
list->unique = NULL;
} else {
list->unique[list->unique_size].leafs_size = 0;
list->unique[list->unique_size].leafs = NULL;
}
i = -1; /* set match flag */
break;
}
}
d->unique_size++;
if (i != -1) {
/* no match found */
LOGVAL(VE_INARG, LOGLINE(child), lyxml_get_attr(child, "tag", NULL), child->name);
LOGVAL(VE_SPEC, 0, "Value differs from the target being deleted.");
goto error;
}
} else { /* replace or add */
if (parse_unique(dev->target, child, &list->unique[list->unique_size])) {
goto error;
}
list->unique_size++;
}
}
lyxml_free_elem(module->ctx, child);
}
dev->deviate_size++;
}
return EXIT_SUCCESS;
error:
if (dev->deviate) {
for (i = 0; i < dev->deviate_size; i++) {
lydict_remove(module->ctx, dev->deviate[i].dflt);
lydict_remove(module->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(module->ctx, &dev->deviate[i].must[j]);
}
free(dev->deviate[i].must);
for (j = 0; j < dev->deviate[i].unique_size; j++) {
free(dev->deviate[i].unique[j].leafs);
}
free(dev->deviate[i].unique);
}
}
free(dev->deviate);
}
return EXIT_FAILURE;
}
static int
fill_yin_augment(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin, struct ly_augment *aug)
{
const char *value;
struct lyxml_elem *next, *child;
int c = 0;
GETVAL(value, yin, "target-node");
aug->target_name = lydict_insert(module->ctx, value, 0);
aug->parent = parent;
if (read_yin_common(module, NULL, (struct ly_mnode *)aug, yin, 0)) {
goto error;
}
LY_TREE_FOR_SAFE(yin->child, next, child) {
if (!strcmp(child->name, "if-feature")) {
c++;
} else if (!strcmp(child->name, "when")) {
if (aug->when) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
aug->when = read_yin_when(module, child);
lyxml_free_elem(module->ctx, child);
if (!aug->when) {
goto error;
}
/* check allowed sub-statements */
} else if (strcmp(child->name, "anyxml") && strcmp(child->name, "case") && strcmp(child->name, "choice") &&
strcmp(child->name, "container") && strcmp(child->name, "leaf-list") && strcmp(child->name, "leaf") &&
strcmp(child->name, "list") && strcmp(child->name, "uses")) {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
goto error;
}
}
if (c) {
aug->features = calloc(c, sizeof *aug->features);
}
LY_TREE_FOR_SAFE(yin->child, next, child) {
if (!strcmp(child->name, "if-feature")) {
GETVAL(value, child, "name");
aug->features[aug->features_size] = resolve_feature(value, module, LOGLINE(child));
if (!aug->features[aug->features_size]) {
goto error;
}
aug->features_size++;
} else {
/* keep the data nodes */
continue;
}
lyxml_free_elem(module->ctx, child);
}
/* do not resolve data now, just keep the definition which will be parsed later
* when we will have the target node
*/
lyxml_unlink_elem(yin);
aug->child = (struct ly_mnode *)yin;
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
static int
fill_yin_refine(struct ly_module *module, struct lyxml_elem *yin, struct ly_refine *rfn)
{
struct lyxml_elem *sub, *next;
const char *value;
char *endptr;
int f_mand = 0, f_min = 0, f_max = 0;
int c_must = 0;
int r;
unsigned long int val;
GETVAL(value, yin, "target-node");
rfn->target = lydict_insert(module->ctx, value, strlen(value));
if (read_yin_common(module, NULL, (struct ly_mnode *)rfn, yin, OPT_CONFIG)) {
goto error;
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
/* limited applicability */
if (!strcmp(sub->name, "default")) {
/* leaf or choice */
if (rfn->mod.dflt) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
/* check possibility of statements combination */
if (rfn->target_type) {
rfn->target_type &= (LY_NODE_LEAF | LY_NODE_CHOICE);
if (!rfn->target_type) {
LOGVAL(VE_SPEC, LOGLINE(sub), "invalid combination of refine substatements");
goto error;
}
} else {
rfn->target_type = LY_NODE_LEAF | LY_NODE_CHOICE;
}
GETVAL(value, sub, "value");
rfn->mod.dflt = lydict_insert(module->ctx, value, strlen(value));
} else if (!strcmp(sub->name, "mandatory")) {
/* leaf, choice or anyxml */
if (f_mand) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
/* just checking the flags in leaf is not sufficient, we would allow
* multiple mandatory statements with the "false" value
*/
f_mand = 1;
/* check possibility of statements combination */
if (rfn->target_type) {
rfn->target_type &= (LY_NODE_LEAF | LY_NODE_CHOICE | LY_NODE_ANYXML);
if (!rfn->target_type) {
LOGVAL(VE_SPEC, LOGLINE(sub), "invalid combination of refine substatements");
goto error;
}
} else {
rfn->target_type = LY_NODE_LEAF | LY_NODE_CHOICE | LY_NODE_ANYXML;
}
GETVAL(value, sub, "value");
if (!strcmp(value, "true")) {
rfn->flags |= LY_NODE_MAND_TRUE;
} else if (!strcmp(value, "false")) {
rfn->flags |= LY_NODE_MAND_FALSE;
} else {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
}
} else if (!strcmp(sub->name, "min-elements")) {
/* list or leaf-list */
if (f_min) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
f_min = 1;
/* check possibility of statements combination */
if (rfn->target_type) {
rfn->target_type &= (LY_NODE_LIST | LY_NODE_LEAFLIST);
if (!rfn->target_type) {
LOGVAL(VE_SPEC, LOGLINE(sub), "invalid combination of refine substatements");
goto error;
}
} else {
rfn->target_type = LY_NODE_LIST | LY_NODE_LEAFLIST;
}
GETVAL(value, sub, "value");
while (isspace(value[0])) {
value++;
}
/* convert it to uint32_t */
errno = 0;
endptr = NULL;
val = strtoul(value, &endptr, 10);
if (*endptr || value[0] == '-' || errno || val > UINT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
}
rfn->mod.list.min = (uint32_t) val;
/* magic - bit 3 in flags means min set */
rfn->flags |= 0x04;
} else if (!strcmp(sub->name, "max-elements")) {
/* list or leaf-list */
if (f_max) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
f_max = 1;
/* check possibility of statements combination */
if (rfn->target_type) {
rfn->target_type &= (LY_NODE_LIST | LY_NODE_LEAFLIST);
if (!rfn->target_type) {
LOGVAL(VE_SPEC, LOGLINE(sub), "invalid combination of refine substatements");
goto error;
}
} else {
rfn->target_type = LY_NODE_LIST | LY_NODE_LEAFLIST;
}
GETVAL(value, sub, "value");
while (isspace(value[0])) {
value++;
}
/* convert it to uint32_t */
errno = 0;
endptr = NULL;
val = strtoul(value, &endptr, 10);
if (*endptr || value[0] == '-' || errno || val == 0 || val > UINT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
}
rfn->mod.list.max = (uint32_t) val;
/* magic - bit 4 in flags means min set */
rfn->flags |= 0x08;
} else if (!strcmp(sub->name, "presence")) {
/* container */
if (rfn->mod.presence) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
/* check possibility of statements combination */
if (rfn->target_type) {
rfn->target_type &= LY_NODE_CONTAINER;
if (!rfn->target_type) {
LOGVAL(VE_SPEC, LOGLINE(sub), "invalid combination of refine substatements");
goto error;
}
} else {
rfn->target_type = LY_NODE_CONTAINER;
}
GETVAL(value, sub, "value");
rfn->mod.presence = lydict_insert(module->ctx, value, strlen(value));
} else if (!strcmp(sub->name, "must")) {
/* leaf-list, list, container or anyxml */
/* check possibility of statements combination */
if (rfn->target_type) {
rfn->target_type &= (LY_NODE_LIST | LY_NODE_LEAFLIST | LY_NODE_CONTAINER | LY_NODE_ANYXML);
if (!rfn->target_type) {
LOGVAL(VE_SPEC, LOGLINE(sub), "invalid combination of refine substatements");
goto error;
}
} else {
rfn->target_type = LY_NODE_LIST | LY_NODE_LEAFLIST | LY_NODE_CONTAINER | LY_NODE_ANYXML;
}
c_must++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
lyxml_free_elem(module->ctx, sub);
}
/* process nodes with cardinality of 0..n */
if (c_must) {
rfn->must = calloc(c_must, sizeof *rfn->must);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "must")) {
r = fill_yin_must(module, sub, &rfn->must[rfn->must_size]);
rfn->must_size++;
if (r) {
goto error;
}
}
lyxml_free_elem(module->ctx, sub);
}
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
static int
fill_yin_import(struct ly_module *module, struct lyxml_elem *yin, struct ly_import *imp)
{
struct lyxml_elem *child;
const char *value;
LY_TREE_FOR(yin->child, child) {
if (!strcmp(child->name, "prefix")) {
GETVAL(value, child, "value");
if (check_identifier(value, LY_IDENT_PREFIX, LOGLINE(child), module, NULL)) {
goto error;
}
imp->prefix = lydict_insert(module->ctx, value, strlen(value));
} else if (!strcmp(child->name, "revision-date")) {
if (imp->rev[0]) {
LOGVAL(VE_TOOMANY, LOGLINE(child), "revision-date", yin->name);
goto error;
}
GETVAL(value, child, "date");
if (check_date(value, LOGLINE(child))) {
goto error;
}
memcpy(imp->rev, value, LY_REV_SIZE - 1);
} else {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
goto error;
}
}
/* check mandatory information */
if (!imp->prefix) {
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "prefix", yin->name);
goto error;
}
GETVAL(value, yin, "module");
imp->module = ly_ctx_get_module(module->ctx, value, imp->rev[0] ? imp->rev : NULL, 1);
if (!imp->module) {
LOGERR(LY_EVALID, "Importing \"%s\" module into \"%s\" failed.", value, module->name);
LOGVAL(VE_INARG, LOGLINE(yin), value, yin->name);
goto error;
}
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
static int
fill_yin_include(struct ly_module *module, struct lyxml_elem *yin, struct ly_include *inc)
{
struct lyxml_elem *child;
const char *value;
LY_TREE_FOR(yin->child, child) {
if (!strcmp(child->name, "revision-date")) {
if (inc->rev[0]) {
LOGVAL(VE_TOOMANY, LOGLINE(child), "revision-date", yin->name);
goto error;
}
GETVAL(value, child, "date");
if (check_date(value, LOGLINE(child))) {
goto error;
}
memcpy(inc->rev, value, LY_REV_SIZE - 1);
} else {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
goto error;
}
}
GETVAL(value, yin, "module");
inc->submodule = ly_ctx_get_submodule(module, value, inc->rev[0] ? inc->rev : NULL);
if (!inc->submodule) {
LOGERR(LY_EVALID, "Including \"%s\" module into \"%s\" failed.", value, module->name);
LOGVAL(VE_INARG, LOGLINE(yin), value, yin->name);
goto error;
}
/* check that belongs-to corresponds */
if (module->type) {
module = ((struct ly_submodule *)module)->belongsto;
}
if (inc->submodule->belongsto != module) {
LOGVAL(VE_INARG, LOGLINE(yin), value, yin->name);
LOGVAL(VE_SPEC, 0, "The included module does not belongs-to the \"%s\" module", module->name);
goto error;
}
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
/*
* Covers:
* description, reference, status, optionaly config
*
*/
static int
read_yin_common(struct ly_module *module, struct ly_mnode *parent,
struct ly_mnode *mnode, struct lyxml_elem *xmlnode, int opt)
{
const char *value;
struct lyxml_elem *sub, *next;
struct ly_ctx *const ctx = module->ctx;
int r = 0;
if (opt & OPT_MODULE) {
mnode->module = module;
}
if (opt & OPT_IDENT) {
GETVAL(value, xmlnode, "name");
if (check_identifier(value, LY_IDENT_NAME, LOGLINE(xmlnode), NULL, NULL)) {
goto error;
}
mnode->name = lydict_insert(ctx, value, strlen(value));
}
/* process local parameters */
LY_TREE_FOR_SAFE(xmlnode->child, next, sub) {
if (!strcmp(sub->name, "description")) {
if (mnode->dsc) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, xmlnode->name);
goto error;
}
mnode->dsc = read_yin_subnode(ctx, sub, "text");
if (!mnode->dsc) {
r = 1;
}
} else if (!strcmp(sub->name, "reference")) {
if (mnode->ref) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, xmlnode->name);
goto error;
}
mnode->ref = read_yin_subnode(ctx, sub, "text");
if (!mnode->ref) {
r = 1;
}
} else if (!strcmp(sub->name, "status")) {
if (mnode->flags & LY_NODE_STATUS_MASK) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, xmlnode->name);
goto error;
}
GETVAL(value, sub, "value");
if (!strcmp(value, "current")) {
mnode->flags |= LY_NODE_STATUS_CURR;
} else if (!strcmp(value, "deprecated")) {
mnode->flags |= LY_NODE_STATUS_DEPRC;
} else if (!strcmp(value, "obsolete")) {
mnode->flags |= LY_NODE_STATUS_OBSLT;
} else {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
r = 1;
}
} else if ((opt & OPT_CONFIG) && !strcmp(sub->name, "config")) {
if (mnode->flags & LY_NODE_CONFIG_MASK) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, xmlnode->name);
goto error;
}
GETVAL(value, sub, "value");
if (!strcmp(value, "false")) {
mnode->flags |= LY_NODE_CONFIG_R;
} else if (!strcmp(value, "true")) {
mnode->flags |= LY_NODE_CONFIG_W;
} else {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
r = 1;
}
} else {
/* skip the lyxml_free_elem */
continue;
}
lyxml_free_elem(ctx, sub);
if (r) {
goto error;
}
}
if ((opt & OPT_INHERIT) && !(mnode->flags & LY_NODE_CONFIG_MASK)) {
/* get config flag from parent */
if (parent) {
mnode->flags |= parent->flags & LY_NODE_CONFIG_MASK;
} else {
/* default config is true */
mnode->flags |= LY_NODE_CONFIG_W;
}
}
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
static struct ly_when *
read_yin_when(struct ly_module *module,struct lyxml_elem *yin)
{
struct ly_when *retval = NULL;
struct lyxml_elem *next, *child;
const char *value;
retval = calloc(1, sizeof *retval);
GETVAL(value, yin, "condition");
retval->cond = lydict_insert(module->ctx, value, 0);
LY_TREE_FOR_SAFE(yin->child, next, child) {
if (!strcmp(child->name, "description")) {
if (retval->dsc) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
retval->dsc = read_yin_subnode(module->ctx, child, "text");
if (!retval->dsc) {
goto error;
}
} else if (!strcmp(child->name, "reference")) {
if (retval->ref) {
LOGVAL(VE_TOOMANY, LOGLINE(child), child->name, yin->name);
goto error;
}
retval->ref = read_yin_subnode(module->ctx, child, "text");
if (!retval->ref) {
goto error;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
goto error;
}
lyxml_free_elem(module->ctx, child);
}
return retval;
error:
ly_mnode_free((struct ly_mnode *)retval);
return NULL;
}
/* additional check in case statement - the child must be unique across
* all other case names and its data children
*/
static int
check_branch_id(struct ly_mnode *parent, struct ly_mnode *new, struct ly_mnode *excl, int line)
{
struct ly_mnode *mnode, *submnode;
if (new->nodetype == LY_NODE_CHOICE) {
/* we have nested choice in case, so we need recursion */
LY_TREE_FOR(new->child, mnode) {
if (mnode->nodetype == LY_NODE_CASE) {
LY_TREE_FOR(mnode->child, submnode) {
if (check_branch_id(parent, submnode, new, line)) {
return EXIT_FAILURE;
}
}
} else if (check_branch_id(parent, mnode, new, line)) {
return EXIT_FAILURE;
}
}
} else {
LY_TREE_FOR(parent->child, mnode) {
if (mnode == excl) {
continue;
}
if (!strcmp(new->name, mnode->name)) {
LOGVAL(VE_INID, line, new->name, "duplicated identifier within a choice's cases");
return EXIT_FAILURE;
}
if (mnode->nodetype == LY_NODE_CASE) {
LY_TREE_FOR(mnode->child, submnode) {
if (!strcmp(new->name, submnode->name)) {
LOGVAL(VE_INID, line, new->name, "duplicated identifier within a choice's cases");
return EXIT_FAILURE;
}
}
}
}
}
return EXIT_SUCCESS;
}
static struct ly_mnode *
read_yin_case(struct ly_module *module,
struct ly_mnode *parent, struct lyxml_elem *yin, int resolve, struct obj_list **unres)
{
struct lyxml_elem *sub, *next;
struct ly_mnode_case *mcase;
struct ly_mnode *retval, *mnode = NULL;
int c_ftrs = 0;
const char *value;
mcase = calloc(1, sizeof *mcase);
mcase->nodetype = LY_NODE_CASE;
mcase->prev = (struct ly_mnode *)mcase;
retval = (struct ly_mnode *)mcase;
if (read_yin_common(module, parent, retval, yin, OPT_IDENT | OPT_MODULE | OPT_INHERIT)) {
goto error;
}
/* process choice's specific children */
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "container")) {
mnode = read_yin_container(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "leaf-list")) {
mnode = read_yin_leaflist(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "leaf")) {
mnode = read_yin_leaf(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "list")) {
mnode = read_yin_list(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "uses")) {
mnode = read_yin_uses(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "choice")) {
mnode = read_yin_choice(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "anyxml")) {
mnode = read_yin_anyxml(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "if-feature")) {
c_ftrs++;
/* skip lyxml_free_elem() at the end of the loop, sub is processed later */
continue;
} else if (!strcmp(sub->name, "when")) {
if (mcase->when) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
mcase->when = read_yin_when(module, sub);
if (!mcase->when) {
goto error;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
if (!mnode) {
goto error;
} else if (check_branch_id(parent, mnode, mnode, LOGLINE(sub))) {
goto error;
}
mnode = NULL;
lyxml_free_elem(module->ctx, sub);
}
if (c_ftrs) {
mcase->features = calloc(c_ftrs, sizeof *mcase->features);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
GETVAL(value, sub, "name");
mcase->features[mcase->features_size] = resolve_feature(value, module, LOGLINE(sub));
if (!mcase->features[mcase->features_size]) {
goto error;
}
mcase->features_size++;
lyxml_free_elem(module->ctx, sub);
}
/* inherit config flag */
if (parent) {
retval->flags |= parent->flags & LY_NODE_CONFIG_MASK;
} else {
/* default config is true */
retval->flags |= LY_NODE_CONFIG_W;
}
/* insert the node into the schema tree */
if (ly_mnode_addchild(parent, retval)) {
goto error;
}
return retval;
error:
ly_mnode_free(retval);
return NULL;
}
static struct ly_mnode *
read_yin_choice(struct ly_module *module,
struct ly_mnode *parent, struct lyxml_elem *yin, int resolve, struct obj_list **unres)
{
struct lyxml_elem *sub, *next;
struct ly_ctx *const ctx = module->ctx;
struct ly_mnode *retval, *mnode = NULL;
struct ly_mnode_choice *choice;
const char *value;
char *dflt_str = NULL;
int f_mand = 0, c_ftrs = 0;
choice = calloc(1, sizeof *choice);
choice->nodetype = LY_NODE_CHOICE;
choice->prev = (struct ly_mnode *)choice;
retval = (struct ly_mnode *)choice;
if (read_yin_common(module, parent, retval, yin, OPT_IDENT | OPT_MODULE | OPT_CONFIG | (resolve ? OPT_INHERIT : 0))) {
goto error;
}
/* process choice's specific children */
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "container")) {
if (!(mnode = read_yin_container(module, retval, sub, resolve, unres))) {
goto error;
}
} else if (!strcmp(sub->name, "leaf-list")) {
if (!(mnode = read_yin_leaflist(module, retval, sub, resolve))) {
goto error;
}
} else if (!strcmp(sub->name, "leaf")) {
if (!(mnode = read_yin_leaf(module, retval, sub, resolve))) {
goto error;
}
} else if (!strcmp(sub->name, "list")) {
if (!(mnode = read_yin_list(module, retval, sub, resolve, unres))) {
goto error;
}
} else if (!strcmp(sub->name, "case")) {
if (!(mnode = read_yin_case(module, retval, sub, resolve, unres))) {
goto error;
}
} else if (!strcmp(sub->name, "anyxml")) {
if (!(mnode = read_yin_anyxml(module, retval, sub, resolve))) {
goto error;
}
} else if (!strcmp(sub->name, "default")) {
if (dflt_str) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
GETVAL(value, sub, "value");
dflt_str = strdup(value);
} else if (!strcmp(sub->name, "mandatory")) {
if (f_mand) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
/* just checking the flags in leaf is not sufficient, we would allow
* multiple mandatory statements with the "false" value
*/
f_mand = 1;
GETVAL(value, sub, "value");
if (!strcmp(value, "true")) {
choice->flags |= LY_NODE_MAND_TRUE;
} else if (!strcmp(value, "false")) {
choice->flags |= LY_NODE_MAND_FALSE;
} else {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
} /* else false is the default value, so we can ignore it */
} else if (!strcmp(sub->name, "when")) {
if (choice->when) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
choice->when = read_yin_when(module, sub);
if (!choice->when) {
goto error;
}
} else if (!strcmp(sub->name, "if-feature")) {
c_ftrs++;
/* skip lyxml_free_elem() at the end of the loop, the sub node is processed later */
continue;
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
if (mnode && check_branch_id(retval, mnode, mnode, LOGLINE(sub))) {
goto error;
}
mnode = NULL;
lyxml_free_elem(ctx, sub);
}
if (c_ftrs) {
choice->features = calloc(c_ftrs, sizeof *choice->features);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
GETVAL(value, sub, "name");
choice->features[choice->features_size] = resolve_feature(value, module, LOGLINE(sub));
if (!choice->features[choice->features_size]) {
goto error;
}
choice->features_size++;
lyxml_free_elem(ctx, sub);
}
/* check - default is prohibited in combination with mandatory */
if (dflt_str && (choice->flags & LY_NODE_MAND_TRUE)) {
LOGVAL(VE_SPEC, LOGLINE(yin),
"The \"default\" statement MUST NOT be present on choices where \"mandatory\" is true.");
goto error;
}
/* link default with the case */
if (dflt_str) {
choice->dflt = resolve_schema_nodeid(dflt_str, retval, module, LY_NODE_CHOICE);
if (!choice->dflt) {
/* default branch not found */
LOGVAL(VE_INARG, LOGLINE(yin), dflt_str, "default");
goto error;
}
}
/* insert the node into the schema tree */
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
free(dflt_str);
return retval;
error:
ly_mnode_free(retval);
free(dflt_str);
return NULL;
}
static struct ly_mnode *
read_yin_anyxml(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin, int resolve)
{
struct ly_mnode *retval;
struct ly_mnode_leaf *anyxml;
struct lyxml_elem *sub, *next;
const char *value;
int r;
int f_mand = 0;
int c_must = 0, c_ftrs = 0;
anyxml = calloc(1, sizeof *anyxml);
anyxml->nodetype = LY_NODE_ANYXML;
anyxml->prev = (struct ly_mnode *)anyxml;
retval = (struct ly_mnode *)anyxml;
if (read_yin_common(module, parent, retval, yin, OPT_IDENT | OPT_MODULE | OPT_CONFIG | (resolve ? OPT_INHERIT : 0))) {
goto error;
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "mandatory")) {
if (f_mand) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
/* just checking the flags in leaf is not sufficient, we would allow
* multiple mandatory statements with the "false" value
*/
f_mand = 1;
GETVAL(value, sub, "value");
if (!strcmp(value, "true")) {
anyxml->flags |= LY_NODE_MAND_TRUE;
} else if (!strcmp(value, "false")) {
anyxml->flags |= LY_NODE_MAND_FALSE;
} else {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
}
/* else false is the default value, so we can ignore it */
lyxml_free_elem(module->ctx, sub);
} else if (!strcmp(sub->name, "when")) {
if (anyxml->when) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
anyxml->when = read_yin_when(module, sub);
lyxml_free_elem(module->ctx, sub);
if (!anyxml->when) {
goto error;
}
} else if (!strcmp(sub->name, "must")) {
c_must++;
} else if (!strcmp(sub->name, "if-feature")) {
c_ftrs++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
}
/* middle part - process nodes with cardinality of 0..n */
if (c_must) {
anyxml->must = calloc(c_must, sizeof *anyxml->must);
}
if (c_ftrs) {
anyxml->features = calloc(c_ftrs, sizeof *anyxml->features);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "must")) {
r = fill_yin_must(module, sub, &anyxml->must[anyxml->must_size]);
anyxml->must_size++;
if (r) {
goto error;
}
} else if (!strcmp(sub->name, "must")) {
GETVAL(value, sub, "name");
anyxml->features[anyxml->features_size] = resolve_feature(value, module, LOGLINE(sub));
if (!anyxml->features[anyxml->features_size]) {
goto error;
}
anyxml->features_size++;
}
lyxml_free_elem(module->ctx, sub);
}
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
return retval;
error:
ly_mnode_free(retval);
return NULL;
}
static struct ly_mnode *
read_yin_leaf(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin, int resolve)
{
struct ly_mnode *retval;
struct ly_mnode_leaf *leaf;
struct lyxml_elem *sub, *next;
const char *value;
int r;
int c_must = 0, c_ftrs = 0, f_mand = 0;
leaf = calloc(1, sizeof *leaf);
leaf->nodetype = LY_NODE_LEAF;
leaf->prev = (struct ly_mnode *)leaf;
retval = (struct ly_mnode *)leaf;
if (read_yin_common(module, parent, retval, yin, OPT_IDENT | OPT_MODULE | OPT_CONFIG | (resolve ? OPT_INHERIT : 0))) {
goto error;
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "type")) {
if (leaf->type.der) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
if (fill_yin_type(module, parent, sub, &leaf->type, NULL)) {
goto error;
}
} else if (!strcmp(sub->name, "default")) {
if (leaf->dflt) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
GETVAL(value, sub, "value");
leaf->dflt = lydict_insert(module->ctx, value, strlen(value));
} else if (!strcmp(sub->name, "units")) {
if (leaf->units) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
GETVAL(value, sub, "name");
leaf->units = lydict_insert(module->ctx, value, strlen(value));
} else if (!strcmp(sub->name, "mandatory")) {
if (f_mand) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
/* just checking the flags in leaf is not sufficient, we would allow
* multiple mandatory statements with the "false" value
*/
f_mand = 1;
GETVAL(value, sub, "value");
if (!strcmp(value, "true")) {
leaf->flags |= LY_NODE_MAND_TRUE;
} else if (!strcmp(value, "false")) {
leaf->flags |= LY_NODE_MAND_FALSE;
} else {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
} /* else false is the default value, so we can ignore it */
} else if (!strcmp(sub->name, "when")) {
if (leaf->when) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
leaf->when = read_yin_when(module, sub);
if (!leaf->when) {
goto error;
}
} else if (!strcmp(sub->name, "must")) {
c_must++; /* else false is the default value, so we can ignore it */
} else if (!strcmp(sub->name, "if-feature")) {
c_ftrs++;
/* skip element free at the end of the loop */
continue;
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
lyxml_free_elem(module->ctx, sub);
}
/* check mandatory parameters */
if (!leaf->type.der) {
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "type", yin->name);
goto error;
}
if (leaf->dflt && check_default(&leaf->type, leaf->dflt)) {
goto error;
}
/* middle part - process nodes with cardinality of 0..n */
if (c_must) {
leaf->must = calloc(c_must, sizeof *leaf->must);
}
if (c_ftrs) {
leaf->features = calloc(c_ftrs, sizeof *leaf->features);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "must")) {
r = fill_yin_must(module, sub, &leaf->must[leaf->must_size]);
leaf->must_size++;
if (r) {
goto error;
}
} else if (!strcmp(sub->name, "if-feature")) {
GETVAL(value, sub, "name");
leaf->features[leaf->features_size] = resolve_feature(value, module, LOGLINE(sub));
if (!leaf->features[leaf->features_size]) {
goto error;
}
leaf->features_size++;
}
lyxml_free_elem(module->ctx, sub);
}
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
return retval;
error:
ly_mnode_free(retval);
return NULL;
}
static struct ly_mnode *
read_yin_leaflist(struct ly_module *module, struct ly_mnode *parent, struct lyxml_elem *yin, int resolve)
{
struct ly_mnode *retval;
struct ly_mnode_leaflist *llist;
struct lyxml_elem *sub, *next;
const char *value;
char *endptr;
unsigned long val;
int r;
int c_must = 0, c_ftrs = 0;
int f_ordr = 0, f_min = 0, f_max = 0;
llist = calloc(1, sizeof *llist);
llist->nodetype = LY_NODE_LEAFLIST;
llist->prev = (struct ly_mnode *)llist;
retval = (struct ly_mnode *)llist;
if (read_yin_common(module, parent, retval, yin, OPT_IDENT | OPT_MODULE | OPT_CONFIG | (resolve ? OPT_INHERIT : 0))) {
goto error;
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "type")) {
if (llist->type.der) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
if (fill_yin_type(module, parent, sub, &llist->type, NULL)) {
goto error;
}
} else if (!strcmp(sub->name, "units")) {
if (llist->units) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
GETVAL(value, sub, "name");
llist->units = lydict_insert(module->ctx, value, strlen(value));
} else if (!strcmp(sub->name, "ordered-by")) {
if (f_ordr) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
/* just checking the flags in llist is not sufficient, we would
* allow multiple ordered-by statements with the "system" value
*/
f_ordr = 1;
if (llist->flags & LY_NODE_CONFIG_R) {
/* RFC 6020, 7.7.5 - ignore ordering when the list represents
* state data
*/
lyxml_free_elem(module->ctx, sub);
continue;
}
GETVAL(value, sub, "value");
if (!strcmp(value, "user")) {
llist->flags |= LY_NODE_USERORDERED;
} else if (strcmp(value, "system")) {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
} /* else system is the default value, so we can ignore it */
} else if (!strcmp(sub->name, "must")) {
c_must++;
} else if (!strcmp(sub->name, "if-feature")) {
c_ftrs++;
/* skip element free at the end of the loop */
continue;
} else if (!strcmp(sub->name, "min-elements")) {
if (f_min) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
f_min = 1;
GETVAL(value, sub, "value");
while (isspace(value[0])) {
value++;
}
/* convert it to uint32_t */
errno = 0;
endptr = NULL;
val = strtoul(value, &endptr, 10);
if (*endptr || value[0] == '-' || errno || val > UINT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
}
llist->min = (uint32_t) val;
} else if (!strcmp(sub->name, "max-elements")) {
if (f_max) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
f_max = 1;
GETVAL(value, sub, "value");
while (isspace(value[0])) {
value++;
}
/* convert it to uint32_t */
errno = 0;
endptr = NULL;
val = strtoul(value, &endptr, 10);
if (*endptr || value[0] == '-' || errno || val == 0 || val > UINT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
}
llist->max = (uint32_t) val;
} else if (!strcmp(sub->name, "when")) {
if (llist->when) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
llist->when = read_yin_when(module, sub);
if (!llist->when) {
goto error;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
lyxml_free_elem(module->ctx, sub);
}
/* check constraints */
if (!llist->type.der) {
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "type", yin->name);
goto error;
}
if (llist->max && llist->min > llist->max) {
LOGVAL(VE_SPEC, LOGLINE(yin), "\"min-elements\" is bigger than \"max-elements\".");
goto error;
}
/* middle part - process nodes with cardinality of 0..n */
if (c_must) {
llist->must = calloc(c_must, sizeof *llist->must);
}
if (c_ftrs) {
llist->features = calloc(c_ftrs, sizeof *llist->features);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "must")) {
r = fill_yin_must(module, sub, &llist->must[llist->must_size]);
llist->must_size++;
if (r) {
goto error;
}
} else if (!strcmp(sub->name, "if-feature")) {
GETVAL(value, sub, "name");
llist->features[llist->features_size] = resolve_feature(value, module, LOGLINE(sub));
if (!llist->features[llist->features_size]) {
goto error;
}
llist->features_size++;
}
lyxml_free_elem(module->ctx, sub);
}
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
return retval;
error:
ly_mnode_free(retval);
return NULL;
}
static struct ly_mnode *
read_yin_list(struct ly_module *module,
struct ly_mnode *parent, struct lyxml_elem *yin, int resolve, struct obj_list **unres)
{
struct ly_mnode *retval, *mnode;
struct ly_mnode_list *list;
struct lyxml_elem *sub, *next, root, uniq;
int i, r;
size_t len;
int c_tpdf = 0, c_must = 0, c_uniq = 0, c_ftrs = 0;
int f_ordr = 0, f_max = 0, f_min = 0;
const char *key_str = NULL, *value;
char *auxs;
unsigned long val;
/* init */
memset(&root, 0, sizeof root);
memset(&uniq, 0, sizeof uniq);
list = calloc(1, sizeof *list);
list->nodetype = LY_NODE_LIST;
list->prev = (struct ly_mnode *)list;
retval = (struct ly_mnode *)list;
if (read_yin_common(module, parent, retval, yin, OPT_IDENT | OPT_MODULE | OPT_CONFIG | (resolve ? OPT_INHERIT : 0))) {
goto error;
}
/* process list's specific children */
LY_TREE_FOR_SAFE(yin->child, next, sub) {
/* data statements */
if (!strcmp(sub->name, "container") ||
!strcmp(sub->name, "leaf-list") ||
!strcmp(sub->name, "leaf") ||
!strcmp(sub->name, "list") ||
!strcmp(sub->name, "choice") ||
!strcmp(sub->name, "uses") ||
!strcmp(sub->name, "grouping") ||
!strcmp(sub->name, "anyxml")) {
lyxml_unlink_elem(sub);
lyxml_add_child(&root, sub);
/* array counters */
} else if (!strcmp(sub->name, "key")) {
/* check cardinality 0..1 */
if (list->keys_size) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, list->name);
goto error;
}
/* count the number of keys */
GETVAL(value, sub, "value");
key_str = value;
while ((value = strpbrk(value, " \t\n"))) {
list->keys_size++;
while (isspace(*value)) {
value++;
}
}
list->keys_size++;
list->keys = calloc(list->keys_size, sizeof *list->keys);
} else if (!strcmp(sub->name, "unique")) {
c_uniq++;
lyxml_unlink_elem(sub);
lyxml_add_child(&uniq, sub);
} else if (!strcmp(sub->name, "typedef")) {
c_tpdf++;
} else if (!strcmp(sub->name, "must")) {
c_must++;
} else if (!strcmp(sub->name, "if-feature")) {
c_ftrs++;
/* optional stetments */
} else if (!strcmp(sub->name, "ordered-by")) {
if (f_ordr) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
/* just checking the flags in llist is not sufficient, we would
* allow multiple ordered-by statements with the "system" value
*/
f_ordr = 1;
if (list->flags & LY_NODE_CONFIG_R) {
/* RFC 6020, 7.7.5 - ignore ordering when the list represents
* state data
*/
lyxml_free_elem(module->ctx, sub);
continue;
}
GETVAL(value, sub, "value");
if (!strcmp(value, "user")) {
list->flags |= LY_NODE_USERORDERED;
} else if (strcmp(value, "system")) {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
}
/* else system is the default value, so we can ignore it */
lyxml_free_elem(module->ctx, sub);
} else if (!strcmp(sub->name, "min-elements")) {
if (f_min) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
f_min = 1;
GETVAL(value, sub, "value");
while (isspace(value[0])) {
value++;
}
/* convert it to uint32_t */
errno = 0;
auxs = NULL;
val = strtoul(value, &auxs, 10);
if (*auxs || value[0] == '-' || errno || val > UINT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
}
list->min = (uint32_t) val;
lyxml_free_elem(module->ctx, sub);
} else if (!strcmp(sub->name, "max-elements")) {
if (f_max) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
f_max = 1;
GETVAL(value, sub, "value");
while (isspace(value[0])) {
value++;
}
/* convert it to uint32_t */
errno = 0;
auxs = NULL;
val = strtoul(value, &auxs, 10);
if (*auxs || value[0] == '-' || errno || val == 0 || val > UINT32_MAX) {
LOGVAL(VE_INARG, LOGLINE(sub), value, sub->name);
goto error;
}
list->max = (uint32_t) val;
lyxml_free_elem(module->ctx, sub);
} else if (!strcmp(sub->name, "when")) {
if (list->when) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
list->when = read_yin_when(module, sub);
lyxml_free_elem(module->ctx, sub);
if (!list->when) {
goto error;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
}
/* check - if list is configuration, key statement is mandatory */
if ((list->flags & LY_NODE_CONFIG_W) && !key_str) {
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "key", "list");
goto error;
}
if (list->max && list->min > list->max) {
LOGVAL(VE_SPEC, LOGLINE(yin), "\"min-elements\" is bigger than \"max-elements\".");
goto error;
}
/* middle part - process nodes with cardinality of 0..n except the data nodes */
if (c_tpdf) {
list->tpdf = calloc(c_tpdf, sizeof *list->tpdf);
}
if (c_must) {
list->must = calloc(c_must, sizeof *list->must);
}
if (c_ftrs) {
list->features = calloc(c_ftrs, sizeof *list->features);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "typedef")) {
r = fill_yin_typedef(module, retval, sub, &list->tpdf[list->tpdf_size], NULL);
list->tpdf_size++;
if (r) {
goto error;
}
} else if (!strcmp(sub->name, "if-feature")) {
GETVAL(value, sub, "name");
list->features[list->features_size] = resolve_feature(value, module, LOGLINE(sub));
if (!list->features[list->features_size]) {
goto error;
}
list->features_size++;
} else if (!strcmp(sub->name, "must")) {
r = fill_yin_must(module, sub, &list->must[list->must_size]);
list->must_size++;
if (r) {
goto error;
}
}
lyxml_free_elem(module->ctx, sub);
}
/* last part - process data nodes */
LY_TREE_FOR_SAFE(root.child, next, sub) {
if (!strcmp(sub->name, "container")) {
mnode = read_yin_container(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "leaf-list")) {
mnode = read_yin_leaflist(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "leaf")) {
mnode = read_yin_leaf(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "list")) {
mnode = read_yin_list(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "choice")) {
mnode = read_yin_choice(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "uses")) {
mnode = read_yin_uses(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "grouping")) {
mnode = read_yin_grouping(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "anyxml")) {
mnode = read_yin_anyxml(module, retval, sub, resolve);
}
lyxml_free_elem(module->ctx, sub);
if (!mnode) {
goto error;
}
}
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
if (!key_str) {
/* config false list without a key */
return retval;
}
/* link key leafs into the list structure and check all constraints */
for (i = 0; i < list->keys_size; i++) {
/* get the key name */
if ((value = strpbrk(key_str, " \t\n"))) {
len = value - key_str;
while (isspace(*value)) {
value++;
}
} else {
len = strlen(key_str);
}
list->keys[i] = find_leaf(retval, key_str, len);
if (check_key(list->keys[i], list->flags, list->keys, i, LOGLINE(yin), key_str, len)) {
goto error;
}
/* prepare for next iteration */
while (value && isspace(*value)) {
value++;
}
key_str = value;
}
/* process unique statements */
if (c_uniq) {
list->unique = calloc(c_uniq, sizeof *list->unique);
}
LY_TREE_FOR_SAFE(uniq.child, next, sub) {
if (parse_unique(retval, sub, &list->unique[list->unique_size++])) {
goto error;
}
lyxml_free_elem(module->ctx, sub);
}
return retval;
error:
ly_mnode_free(retval);
while (root.child) {
lyxml_free_elem(module->ctx, root.child);
}
while (uniq.child) {
lyxml_free_elem(module->ctx, uniq.child);
}
return NULL;
}
static struct ly_mnode *
read_yin_container(struct ly_module *module,
struct ly_mnode *parent, struct lyxml_elem *yin, int resolve, struct obj_list **unres)
{
struct lyxml_elem *sub, *next, root;
struct ly_mnode *mnode = NULL;
struct ly_mnode *retval;
struct ly_mnode_container *cont;
const char *value;
int r;
int c_tpdf = 0, c_must = 0, c_ftrs = 0;
/* init */
memset(&root, 0, sizeof root);
cont = calloc(1, sizeof *cont);
cont->nodetype = LY_NODE_CONTAINER;
cont->prev = (struct ly_mnode *)cont;
retval = (struct ly_mnode *)cont;
if (read_yin_common(module, parent, retval, yin, OPT_IDENT | OPT_MODULE | OPT_CONFIG | (resolve ? OPT_INHERIT : 0))) {
goto error;
}
/* process container's specific children */
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "presence")) {
if (cont->presence) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
GETVAL(value, sub, "value");
cont->presence = lydict_insert(module->ctx, value, strlen(value));
lyxml_free_elem(module->ctx, sub);
} else if (!strcmp(sub->name, "when")) {
if (cont->when) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
cont->when = read_yin_when(module, sub);
lyxml_free_elem(module->ctx, sub);
if (!cont->when) {
goto error;
}
/* data statements */
} else if (!strcmp(sub->name, "container") ||
!strcmp(sub->name, "leaf-list") ||
!strcmp(sub->name, "leaf") ||
!strcmp(sub->name, "list") ||
!strcmp(sub->name, "choice") ||
!strcmp(sub->name, "uses") ||
!strcmp(sub->name, "grouping") ||
!strcmp(sub->name, "anyxml")) {
lyxml_unlink_elem(sub);
lyxml_add_child(&root, sub);
/* array counters */
} else if (!strcmp(sub->name, "typedef")) {
c_tpdf++;
} else if (!strcmp(sub->name, "must")) {
c_must++;
} else if (!strcmp(sub->name, "if-feature")) {
c_ftrs++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
}
/* middle part - process nodes with cardinality of 0..n except the data nodes */
if (c_tpdf) {
cont->tpdf = calloc(c_tpdf, sizeof *cont->tpdf);
}
if (c_must) {
cont->must = calloc(c_must, sizeof *cont->must);
}
if (c_ftrs) {
cont->features = calloc(c_ftrs, sizeof *cont->features);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "typedef")) {
r = fill_yin_typedef(module, retval, sub, &cont->tpdf[cont->tpdf_size], NULL);
cont->tpdf_size++;
if (r) {
goto error;
}
} else if (!strcmp(sub->name, "must")) {
r = fill_yin_must(module, sub, &cont->must[cont->must_size]);
cont->must_size++;
if (r) {
goto error;
}
} else if (!strcmp(sub->name, "if-feature")) {
GETVAL(value, sub, "name");
cont->features[cont->features_size] = resolve_feature(value, module, LOGLINE(sub));
if (!cont->features[cont->features_size]) {
goto error;
}
cont->features_size++;
}
lyxml_free_elem(module->ctx, sub);
}
/* last part - process data nodes */
LY_TREE_FOR_SAFE(root.child, next, sub) {
if (!strcmp(sub->name, "container")) {
mnode = read_yin_container(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "leaf-list")) {
mnode = read_yin_leaflist(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "leaf")) {
mnode = read_yin_leaf(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "list")) {
mnode = read_yin_list(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "choice")) {
mnode = read_yin_choice(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "uses")) {
mnode = read_yin_uses(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "grouping")) {
mnode = read_yin_grouping(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "anyxml")) {
mnode = read_yin_anyxml(module, retval, sub, resolve);
}
lyxml_free_elem(module->ctx, sub);
if (!mnode) {
goto error;
}
}
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
return retval;
error:
ly_mnode_free(retval);
while (root.child) {
lyxml_free_elem(module->ctx, root.child);
}
return NULL;
}
static struct ly_mnode *
read_yin_grouping(struct ly_module *module,
struct ly_mnode *parent, struct lyxml_elem *node, int resolve, struct obj_list **unres)
{
struct lyxml_elem *sub, *next, root;
struct ly_mnode *mnode = NULL;
struct ly_mnode *retval;
struct ly_mnode_grp *grp;
int r;
int c_tpdf = 0;
/* init */
memset(&root, 0, sizeof root);
grp = calloc(1, sizeof *grp);
grp->nodetype = LY_NODE_GROUPING;
grp->prev = (struct ly_mnode *)grp;
retval = (struct ly_mnode *)grp;
if (read_yin_common(module, parent, retval, node, OPT_IDENT | OPT_MODULE)) {
goto error;
}
LY_TREE_FOR_SAFE(node->child, next, sub) {
/* data statements */
if (!strcmp(sub->name, "container") ||
!strcmp(sub->name, "leaf-list") ||
!strcmp(sub->name, "leaf") ||
!strcmp(sub->name, "list") ||
!strcmp(sub->name, "choice") ||
!strcmp(sub->name, "uses") ||
!strcmp(sub->name, "grouping") ||
!strcmp(sub->name, "anyxml")) {
lyxml_unlink_elem(sub);
lyxml_add_child(&root, sub);
/* array counters */
} else if (!strcmp(sub->name, "typedef")) {
c_tpdf++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
}
/* middle part - process nodes with cardinality of 0..n except the data nodes */
if (c_tpdf) {
grp->tpdf = calloc(c_tpdf, sizeof *grp->tpdf);
}
LY_TREE_FOR_SAFE(node->child, next, sub) {
if (!strcmp(sub->name, "typedef")) {
r = fill_yin_typedef(module, retval, sub, &grp->tpdf[grp->tpdf_size], NULL);
grp->tpdf_size++;
if (r) {
goto error;
}
}
lyxml_free_elem(module->ctx, sub);
}
/* last part - process data nodes */
LY_TREE_FOR_SAFE(root.child, next, sub) {
if (!strcmp(sub->name, "container")) {
mnode = read_yin_container(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "leaf-list")) {
mnode = read_yin_leaflist(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "leaf")) {
mnode = read_yin_leaf(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "list")) {
mnode = read_yin_list(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "choice")) {
mnode = read_yin_choice(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "uses")) {
mnode = read_yin_uses(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "grouping")) {
mnode = read_yin_grouping(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "anyxml")) {
mnode = read_yin_anyxml(module, retval, sub, resolve);
}
lyxml_free_elem(module->ctx, sub);
if (!mnode) {
goto error;
}
}
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
return retval;
error:
ly_mnode_free(retval);
while (root.child) {
lyxml_free_elem(module->ctx, root.child);
}
return NULL;
}
static struct ly_mnode *
read_yin_input_output(struct ly_module *module,
struct ly_mnode *parent, struct lyxml_elem *yin, int resolve, struct obj_list **unres)
{
struct lyxml_elem *sub, *next, root;
struct ly_mnode *mnode = NULL;
struct ly_mnode *retval;
struct ly_mnode_input_output *inout;
int r;
int c_tpdf = 0;
/* init */
memset(&root, 0, sizeof root);
inout = calloc(1, sizeof *inout);
if (!strcmp(yin->name, "input")) {
inout->nodetype = LY_NODE_INPUT;
} else if (!strcmp(yin->name, "output")) {
inout->nodetype = LY_NODE_OUTPUT;
} else {
assert(0);
}
inout->prev = (struct ly_mnode *)inout;
retval = (struct ly_mnode *)inout;
if (read_yin_common(module, parent, retval, yin, OPT_MODULE)) {
goto error;
}
/* data statements */
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "container") ||
!strcmp(sub->name, "leaf-list") ||
!strcmp(sub->name, "leaf") ||
!strcmp(sub->name, "list") ||
!strcmp(sub->name, "choice") ||
!strcmp(sub->name, "uses") ||
!strcmp(sub->name, "grouping") ||
!strcmp(sub->name, "anyxml")) {
lyxml_unlink_elem(sub);
lyxml_add_child(&root, sub);
/* array counters */
} else if (!strcmp(sub->name, "typedef")) {
c_tpdf++;
#if 0
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
#else
} else {
continue;
#endif
}
}
/* middle part - process nodes with cardinality of 0..n except the data nodes */
if (c_tpdf) {
inout->tpdf = calloc(c_tpdf, sizeof *inout->tpdf);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "typedef")) {
r = fill_yin_typedef(module, retval, sub, &inout->tpdf[inout->tpdf_size], NULL);
inout->tpdf_size++;
if (r) {
goto error;
}
}
lyxml_free_elem(module->ctx, sub);
}
/* last part - process data nodes */
LY_TREE_FOR_SAFE(root.child, next, sub) {
if (!strcmp(sub->name, "container")) {
mnode = read_yin_container(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "leaf-list")) {
mnode = read_yin_leaflist(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "leaf")) {
mnode = read_yin_leaf(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "list")) {
mnode = read_yin_list(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "choice")) {
mnode = read_yin_choice(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "uses")) {
mnode = read_yin_uses(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "grouping")) {
mnode = read_yin_grouping(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "anyxml")) {
mnode = read_yin_anyxml(module, retval, sub, resolve);
}
lyxml_free_elem(module->ctx, sub);
if (!mnode) {
goto error;
}
}
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
return retval;
error:
ly_mnode_free(retval);
while (root.child) {
lyxml_free_elem(module->ctx, root.child);
}
return NULL;
}
static struct ly_mnode *
read_yin_notif(struct ly_module *module,
struct ly_mnode *parent, struct lyxml_elem *yin, int resolve, struct obj_list **unres)
{
struct lyxml_elem *sub, *next, root;
struct ly_mnode *mnode = NULL;
struct ly_mnode *retval;
struct ly_mnode_notif *notif;
const char *value;
int r;
int c_tpdf = 0, c_ftrs = 0;
memset(&root, 0, sizeof root);
notif = calloc(1, sizeof *notif);
notif->nodetype = LY_NODE_NOTIF;
notif->prev = (struct ly_mnode *)notif;
retval = (struct ly_mnode *)notif;
if (read_yin_common(module, parent, retval, yin, OPT_IDENT | OPT_MODULE)) {
goto error;
}
/* process rpc's specific children */
LY_TREE_FOR_SAFE(yin->child, next, sub) {
/* data statements */
if (!strcmp(sub->name, "container") ||
!strcmp(sub->name, "leaf-list") ||
!strcmp(sub->name, "leaf") ||
!strcmp(sub->name, "list") ||
!strcmp(sub->name, "choice") ||
!strcmp(sub->name, "uses") ||
!strcmp(sub->name, "grouping") ||
!strcmp(sub->name, "anyxml")) {
lyxml_unlink_elem(sub);
lyxml_add_child(&root, sub);
/* array counters */
} else if (!strcmp(sub->name, "typedef")) {
c_tpdf++;
} else if (!strcmp(sub->name, "if-feature")) {
c_ftrs++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
}
/* middle part - process nodes with cardinality of 0..n except the data nodes */
if (c_tpdf) {
notif->tpdf = calloc(c_tpdf, sizeof *notif->tpdf);
}
if (c_ftrs) {
notif->features = calloc(c_ftrs, sizeof *notif->features);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "typedef")) {
r = fill_yin_typedef(module, retval, sub, &notif->tpdf[notif->tpdf_size], NULL);
notif->tpdf_size++;
if (r) {
goto error;
}
} else if (!strcmp(sub->name, "typedef")) {
GETVAL(value, sub, "name");
notif->features[notif->features_size] = resolve_feature(value, module, LOGLINE(sub));
if (!notif->features[notif->features_size]) {
goto error;
}
notif->features_size++;
}
lyxml_free_elem(module->ctx, sub);
}
/* last part - process data nodes */
LY_TREE_FOR_SAFE(root.child, next, sub) {
if (!strcmp(sub->name, "container")) {
mnode = read_yin_container(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "leaf-list")) {
mnode = read_yin_leaflist(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "leaf")) {
mnode = read_yin_leaf(module, retval, sub, resolve);
} else if (!strcmp(sub->name, "list")) {
mnode = read_yin_list(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "choice")) {
mnode = read_yin_choice(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "uses")) {
mnode = read_yin_uses(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "grouping")) {
mnode = read_yin_grouping(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "anyxml")) {
mnode = read_yin_anyxml(module, retval, sub, resolve);
}
lyxml_free_elem(module->ctx, sub);
if (!mnode) {
goto error;
}
}
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
return retval;
error:
ly_mnode_free(retval);
while (root.child) {
lyxml_free_elem(module->ctx, root.child);
}
return NULL;
}
static struct ly_mnode *
read_yin_rpc(struct ly_module *module,
struct ly_mnode *parent, struct lyxml_elem *yin, int resolve, struct obj_list **unres)
{
struct lyxml_elem *sub, *next, root;
struct ly_mnode *mnode = NULL;
struct ly_mnode *retval;
struct ly_mnode_rpc *rpc;
const char *value;
int r;
int c_tpdf = 0, c_ftrs = 0;
/* init */
memset(&root, 0, sizeof root);
rpc = calloc(1, sizeof *rpc);
rpc->nodetype = LY_NODE_RPC;
rpc->prev = (struct ly_mnode *)rpc;
retval = (struct ly_mnode *)rpc;
if (read_yin_common(module, parent, retval, yin, OPT_IDENT | OPT_MODULE)) {
goto error;
}
/* process rpc's specific children */
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "input")) {
if (rpc->child
&& (rpc->child->nodetype == LY_NODE_INPUT
|| (rpc->child->next && rpc->child->next->nodetype == LY_NODE_INPUT))) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
lyxml_unlink_elem(sub);
lyxml_add_child(&root, sub);
} else if (!strcmp(sub->name, "output")) {
if (rpc->child
&& (rpc->child->nodetype == LY_NODE_INPUT
|| (rpc->child->next && rpc->child->next->nodetype == LY_NODE_INPUT))) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, yin->name);
goto error;
}
lyxml_unlink_elem(sub);
lyxml_add_child(&root, sub);
/* data statements */
} else if (!strcmp(sub->name, "grouping")) {
lyxml_unlink_elem(sub);
lyxml_add_child(&root, sub);
/* array counters */
} else if (!strcmp(sub->name, "typedef")) {
c_tpdf++;
} else if (!strcmp(sub->name, "if-feature")) {
c_ftrs++;
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
}
/* middle part - process nodes with cardinality of 0..n except the data nodes */
if (c_tpdf) {
rpc->tpdf = calloc(c_tpdf, sizeof *rpc->tpdf);
}
if (c_ftrs) {
rpc->features = calloc(c_ftrs, sizeof *rpc->features);
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "typedef")) {
r = fill_yin_typedef(module, retval, sub, &rpc->tpdf[rpc->tpdf_size], NULL);
rpc->tpdf_size++;
if (r) {
goto error;
}
} else if (!strcmp(sub->name, "if-feature")) {
GETVAL(value, sub, "name");
rpc->features[rpc->features_size] = resolve_feature(value, module, LOGLINE(sub));
if (!rpc->features[rpc->features_size]) {
goto error;
}
rpc->features_size++;
}
lyxml_free_elem(module->ctx, sub);
}
/* last part - process data nodes */
LY_TREE_FOR_SAFE(root.child, next, sub) {
if (!strcmp(sub->name, "grouping")) {
mnode = read_yin_grouping(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "input")) {
mnode = read_yin_input_output(module, retval, sub, resolve, unres);
} else if (!strcmp(sub->name, "output")) {
mnode = read_yin_input_output(module, retval, sub, resolve, unres);
}
lyxml_free_elem(module->ctx, sub);
if (!mnode) {
goto error;
}
}
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
return retval;
error:
ly_mnode_free(retval);
while (root.child) {
lyxml_free_elem(module->ctx, root.child);
}
return NULL;
}
static int
find_grouping(struct ly_mnode *parent, struct ly_mnode_uses *uses, int line)
{
struct ly_module *searchmod = NULL, *module = uses->module;
struct ly_mnode *mnode, *mnode_aux;
const char *name;
int prefix_len = 0;
int i;
/* get referenced grouping */
name = strchr(uses->name, ':');
if (!name) {
/* no prefix, search in local tree */
name = uses->name;
} else {
/* there is some prefix, check if it refer the same data model */
/* set name to correct position after colon */
prefix_len = name - uses->name;
name++;
if (!strncmp(uses->name, module->prefix, prefix_len) && !module->prefix[prefix_len]) {
/* prefix refers to the current module, ignore it */
prefix_len = 0;
}
}
/* search */
if (prefix_len) {
/* in top-level groupings of some other module */
for (i = 0; i < module->imp_size; i++) {
if (!strncmp(module->imp[i].prefix, uses->name, prefix_len)
&& !module->imp[i].prefix[prefix_len]) {
searchmod = module->imp[i].module;
break;
}
}
if (!searchmod) {
/* uses refers unknown data model */
LOGVAL(VE_INPREFIX, line, name);
return EXIT_FAILURE;
}
LY_TREE_FOR(searchmod->data, mnode) {
if (mnode->nodetype == LY_NODE_GROUPING && !strcmp(mnode->name, name)) {
uses->grp = (struct ly_mnode_grp *)mnode;
return EXIT_SUCCESS;
}
}
} else {
/* in local tree hierarchy */
for (mnode_aux = parent; mnode_aux; mnode_aux = mnode_aux->parent) {
LY_TREE_FOR(mnode_aux->child, mnode) {
if (mnode->nodetype == LY_NODE_GROUPING && !strcmp(mnode->name, name)) {
uses->grp = (struct ly_mnode_grp *)mnode;
return EXIT_SUCCESS;
}
}
}
/* search in top level of the current module */
LY_TREE_FOR(module->data, mnode) {
if (mnode->nodetype == LY_NODE_GROUPING && !strcmp(mnode->name, name)) {
uses->grp = (struct ly_mnode_grp *)mnode;
return EXIT_SUCCESS;
}
}
/* search in top-level of included modules */
for (i = 0; i < module->inc_size; i++) {
LY_TREE_FOR(module->inc[i].submodule->data, mnode) {
if (mnode->nodetype == LY_NODE_GROUPING && !strcmp(mnode->name, name)) {
uses->grp = (struct ly_mnode_grp *)mnode;
return EXIT_SUCCESS;
}
}
}
}
/* not found, but no explicit error occured */
return EXIT_SUCCESS;
}
static int
resolve_augment(struct ly_augment *aug, struct ly_mnode *parent, struct ly_module *module, unsigned int line)
{
struct lyxml_elem *yin, *next, *sub;
struct ly_mnode *mnode;
assert(module);
/* resolve target node */
aug->target = resolve_schema_nodeid(aug->target_name, parent, module, LY_NODE_AUGMENT);
if (!aug->target) {
LOGVAL(VE_INARG, line, aug->target, "uses");
return EXIT_FAILURE;
}
if (!aug->child) {
/* nothing to do */
return EXIT_SUCCESS;
}
yin = (struct lyxml_elem *)aug->child;
if (read_yin_common(module, aug->target, (struct ly_mnode *)aug, yin, OPT_CONFIG)) {
return EXIT_FAILURE;
}
LY_TREE_FOR_SAFE(yin->child, next, sub) {
if (!strcmp(sub->name, "container")) {
mnode = read_yin_container(module, aug->target, sub, 1, NULL);
} else if (!strcmp(sub->name, "leaf-list")) {
mnode = read_yin_leaflist(module, aug->target, sub, 1);
} else if (!strcmp(sub->name, "leaf")) {
mnode = read_yin_leaf(module, aug->target, sub, 1);
} else if (!strcmp(sub->name, "list")) {
mnode = read_yin_list(module, aug->target, sub, 1, NULL);
} else if (!strcmp(sub->name, "uses")) {
mnode = read_yin_uses(module, aug->target, sub, 1, NULL);
} else if (!strcmp(sub->name, "choice")) {
mnode = read_yin_case(module, aug->target, sub, 1, NULL);
} else if (!strcmp(sub->name, "case")) {
mnode = read_yin_case(module, aug->target, sub, 1, NULL);
} else if (!strcmp(sub->name, "anyxml")) {
mnode = read_yin_anyxml(module, aug->target, sub, 1);
#if 0
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
return EXIT_FAILURE;
#else
} else {
continue;
#endif
}
if (!mnode) {
return EXIT_FAILURE;
}
/* check for mandatory nodes - if the target node is in another module
* the added nodes cannot be mandatory
*/
if (check_mandatory(mnode)) {
LOGVAL(VE_SPEC, LOGLINE(sub), "When augmenting data in another module, mandatory statement is not allowed.");
return EXIT_FAILURE;
}
lyxml_free_elem(module->ctx, sub);
/* the parent pointer will point to the augment node, but all
* siblings pointers and possibly the child node in target does
* not know about the augment and follow the standard schema tree
* structure
*/
mnode->parent = (struct ly_mnode *)aug;
mnode = NULL;
}
lyxml_free_elem(module->ctx, yin);
aug->child = NULL;
return EXIT_SUCCESS;
}
int
resolve_uses(struct ly_mnode_uses *uses, unsigned int line)
{
struct ly_ctx *ctx;
struct ly_mnode *mnode = NULL, *mnode_aux;
struct ly_refine *rfn;
struct ly_restr *newmust;
int i, j;
uint8_t size;
/* copy the data nodes from grouping into the uses context */
LY_TREE_FOR(uses->grp->child, mnode) {
mnode_aux = ly_mnode_dup(uses->module, mnode, uses->flags, 1, line);
if (!mnode_aux) {
LOGVAL(VE_SPEC, line, "Copying data from grouping failed");
return EXIT_FAILURE;
}
if (ly_mnode_addchild((struct ly_mnode *)uses, mnode_aux)) {
ly_mnode_free(mnode_aux);
return EXIT_FAILURE;
}
}
ctx = uses->module->ctx;
/* apply refines */
for (i = 0; i < uses->refine_size; i++) {
rfn = &uses->refine[i];
mnode = resolve_schema_nodeid(rfn->target, (struct ly_mnode *)uses, uses->module, LY_NODE_USES);
if (!mnode) {
LOGVAL(VE_INARG, line, rfn->target, "uses");
return EXIT_FAILURE;
}
if (rfn->target_type && !(mnode->nodetype & rfn->target_type)) {
LOGVAL(VE_SPEC, line, "refine substatements not applicable to the target-node");
return EXIT_FAILURE;
}
/* description on any nodetype */
if (rfn->dsc) {
lydict_remove(ctx, mnode->dsc);
mnode->dsc = lydict_insert(ctx, rfn->dsc, 0);
}
/* reference on any nodetype */
if (rfn->ref) {
lydict_remove(ctx, mnode->ref);
mnode->ref = lydict_insert(ctx, rfn->ref, 0);
}
/* config on any nodetype */
if (rfn->flags & LY_NODE_CONFIG_MASK) {
mnode->flags &= ~LY_NODE_CONFIG_MASK;
mnode->flags |= (rfn->flags & LY_NODE_CONFIG_MASK);
}
/* default value ... */
if (rfn->mod.dflt) {
if (mnode->nodetype == LY_NODE_LEAF) {
/* leaf */
lydict_remove(ctx, ((struct ly_mnode_leaf *)mnode)->dflt);
((struct ly_mnode_leaf *)mnode)->dflt = lydict_insert(ctx, rfn->mod.dflt, 0);
} else if (mnode->nodetype == LY_NODE_CHOICE) {
/* choice */
((struct ly_mnode_choice *)mnode)->dflt = resolve_schema_nodeid(rfn->mod.dflt, mnode, mnode->module, LY_NODE_CHOICE);
if (!((struct ly_mnode_choice *)mnode)->dflt) {
LOGVAL(VE_INARG, line, rfn->mod.dflt, "default");
return EXIT_FAILURE;
}
}
}
/* mandatory on leaf, anyxml or choice */
if (rfn->flags & LY_NODE_MAND_MASK) {
if (mnode->nodetype & (LY_NODE_LEAF | LY_NODE_ANYXML | LY_NODE_CHOICE)) {
/* remove current value */
mnode->flags &= ~LY_NODE_MAND_MASK;
/* set new value */
mnode->flags |= (rfn->flags & LY_NODE_MAND_MASK);
}
}
/* presence on container */
if ((mnode->nodetype & LY_NODE_CONTAINER) && rfn->mod.presence) {
lydict_remove(ctx, ((struct ly_mnode_container *)mnode)->presence);
((struct ly_mnode_container *)mnode)->presence = lydict_insert(ctx, rfn->mod.presence, 0);
}
/* min/max-elements on list or leaf-list */
if (mnode->nodetype & (LY_NODE_LEAFLIST | LY_NODE_LIST)) {
/* magic - bit 3 in flags means min set, bit 4 says max set */
if (rfn->flags & 0x04) {
((struct ly_mnode_list *)mnode)->min = rfn->mod.list.min;
}
if (rfn->flags & 0x08) {
((struct ly_mnode_list *)mnode)->max = rfn->mod.list.max;
}
}
/* must in leaf, leaf-list, list, container or anyxml */
if (rfn->must_size) {
size = ((struct ly_mnode_leaf *)mnode)->must_size + rfn->must_size;
newmust = realloc(((struct ly_mnode_leaf *)mnode)->must, size * sizeof *rfn->must);
if (!newmust) {
LOGMEM;
return EXIT_FAILURE;
}
for (i = 0, j = ((struct ly_mnode_leaf *)mnode)->must_size; i < rfn->must_size; i++, j++) {
newmust[j].expr = lydict_insert(ctx, rfn->must[i].expr, 0);
newmust[j].dsc = lydict_insert(ctx, rfn->must[i].dsc, 0);
newmust[j].ref = lydict_insert(ctx, rfn->must[i].ref, 0);
newmust[j].eapptag = lydict_insert(ctx, rfn->must[i].eapptag, 0);
newmust[j].emsg = lydict_insert(ctx, rfn->must[i].emsg, 0);
}
((struct ly_mnode_leaf *)mnode)->must = newmust;
((struct ly_mnode_leaf *)mnode)->must_size = size;
}
}
/* apply augments */
for (i = 0; i < uses->augment_size; i++) {
if (resolve_augment(&uses->augment[i], (struct ly_mnode *)uses, uses->module, line)) {
goto error;
}
}
return EXIT_SUCCESS;
error:
return EXIT_FAILURE;
}
/*
* resolve - referenced grouping should be bounded to the namespace (resolved)
* only when uses does not appear in grouping. In a case of grouping's uses,
* we just get information but we do not apply augment or refine to it.
*/
static struct ly_mnode *
read_yin_uses(struct ly_module *module,
struct ly_mnode *parent, struct lyxml_elem *node, int resolve, struct obj_list **unres)
{
struct lyxml_elem *sub, *next;
struct ly_mnode *retval;
struct ly_mnode_uses *uses;
struct obj_list *unres_new;
const char *value;
int c_ref = 0, c_aug = 0, c_ftrs = 0;
int r;
uses = calloc(1, sizeof *uses);
uses->nodetype = LY_NODE_USES;
uses->prev = (struct ly_mnode *)uses;
retval = (struct ly_mnode *)uses;
GETVAL(value, node, "name");
uses->name = lydict_insert(module->ctx, value, 0);
if (read_yin_common(module, parent, retval, node, OPT_MODULE | (resolve ? OPT_INHERIT : 0))) {
goto error;
}
/* get other properties of uses */
LY_TREE_FOR_SAFE(node->child, next, sub) {
if (!strcmp(sub->name, "refine")) {
c_ref++;
} else if (!strcmp(sub->name, "augment")) {
c_aug++;
} else if (!strcmp(sub->name, "if-feature")) {
c_ftrs++;
} else if (!strcmp(sub->name, "when")) {
if (uses->when) {
LOGVAL(VE_TOOMANY, LOGLINE(sub), sub->name, node->name);
goto error;
}
uses->when = read_yin_when(module, sub);
lyxml_free_elem(module->ctx, sub);
if (!uses->when) {
goto error;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(sub), sub->name);
goto error;
}
}
/* process properties with cardinality 0..n */
if (c_ref) {
uses->refine = calloc(c_ref, sizeof *uses->refine);
}
if (c_aug) {
uses->augment = calloc(c_aug, sizeof *uses->augment);
}
if (c_ftrs) {
uses->features = calloc(c_ftrs, sizeof *uses->features);
}
LY_TREE_FOR_SAFE(node->child, next, sub) {
if (!strcmp(sub->name, "refine")) {
r = fill_yin_refine(module, sub, &uses->refine[uses->refine_size]);
uses->refine_size++;
lyxml_free_elem(module->ctx, sub);
} else if (!strcmp(sub->name, "augment")) {
r = fill_yin_augment(module, retval, sub, &uses->augment[uses->augment_size]);
uses->augment_size++;
} else if (!strcmp(sub->name, "if-feature")) {
GETVAL(value, sub, "name");
uses->features[uses->features_size] = resolve_feature(value, module, LOGLINE(sub));
if (!uses->features[uses->features_size]) {
goto error;
}
r = 0;
uses->features_size++;
lyxml_free_elem(module->ctx, sub);
}
if (r) {
goto error;
}
}
if (find_grouping(parent, uses, LOGLINE(node))) {
goto error;
}
if (!uses->grp) {
LOGVRB("Unresolved uses of \"%s\" (line %d), trying to resolve it later", uses->name, LOGLINE(node));
unres_new = calloc(1, sizeof *unres_new);
if (*unres) {
unres_new->next = *unres;
}
unres_new->obj = retval;
unres_new->line = LOGLINE(node);
/* put it at the beginning of the unresolved list */
*unres = unres_new;
}
if (parent && ly_mnode_addchild(parent, retval)) {
goto error;
}
if (resolve) {
/* inherit config flag */
if (parent) {
retval->flags |= parent->flags & LY_NODE_CONFIG_MASK;
} else {
/* default config is true */
retval->flags |= LY_NODE_CONFIG_W;
}
}
if (resolve && uses->grp) {
/* copy the data nodes from grouping into the uses context */
if (resolve_uses(uses, LOGLINE(node))) {
goto error;
}
}
return retval;
error:
ly_mnode_free(retval);
return NULL;
}
/* common code for yin_read_module() and yin_read_submodule() */
static int
read_sub_module(struct ly_module *module, struct lyxml_elem *yin)
{
struct ly_ctx *ctx = module->ctx;
struct ly_submodule *submodule = (struct ly_submodule *)module;
struct lyxml_elem *next, *node, *child, root, grps, rpcs, notifs;
struct ly_mnode *mnode = NULL;
struct obj_list *unres = NULL, *unres_next; /* unresolved objects */
const char *value;
int r;
int i;
int unres_flag = 0; /* 0 for uses, 1 for types */
int belongsto_flag = 0;
/* counters */
int c_imp = 0, c_rev = 0, c_tpdf = 0, c_ident = 0, c_inc = 0, c_aug = 0, c_ftrs = 0, c_dev = 0;
/* init */
memset(&root, 0, sizeof root);
memset(&grps, 0, sizeof grps);
memset(&rpcs, 0, sizeof rpcs);
memset(&notifs, 0, sizeof notifs);
/*
* in the first run, we process elements with cardinality of 1 or 0..1 and
* count elements with cardinality 0..n. Data elements (choices, containers,
* leafs, lists, leaf-lists) are moved aside to be processed last, since we
* need have all top-level and groupings already prepared at that time. In
* the middle loop, we process other elements with carinality of 0..n since
* we need to allocate arrays to store them.
*/
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!node->ns || strcmp(node->ns->value, LY_NSYIN)) {
lyxml_free_elem(ctx, node);
continue;
}
if (!module->type && !strcmp(node->name, "namespace")) {
if (module->ns) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "uri");
module->ns = lydict_insert(ctx, value, strlen(value));
lyxml_free_elem(ctx, node);
} else if (!module->type && !strcmp(node->name, "prefix")) {
if (module->prefix) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "value");
if (check_identifier(value, LY_IDENT_PREFIX, LOGLINE(node), module, NULL)) {
goto error;
}
module->prefix = lydict_insert(ctx, value, strlen(value));
lyxml_free_elem(ctx, node);
} else if (module->type && !strcmp(node->name, "belongs-to")) {
if (belongsto_flag) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
belongsto_flag = 1;
GETVAL(value, node, "module");
while (submodule->belongsto->type) {
submodule->belongsto = ((struct ly_submodule *)submodule->belongsto)->belongsto;
}
if (value != submodule->belongsto->name) {
LOGVAL(VE_INARG, LOGLINE(node), value, node->name);
goto error;
}
/* get the prefix substatement, start with checks */
if (!node->child) {
LOGVAL(VE_MISSSTMT2, LOGLINE(node), "prefix", node->name);
goto error;
} else if (strcmp(node->child->name, "prefix")) {
LOGVAL(VE_INSTMT, LOGLINE(node->child), node->child->name);
goto error;
} else if (node->child->next) {
LOGVAL(VE_INSTMT, LOGLINE(node->child->next), node->child->next->name);
goto error;
}
/* and now finally get the value */
GETVAL(value, node->child, "value");
/* check here differs from a generic prefix check, since this prefix
* don't have to be unique
*/
if (check_identifier(value, LY_IDENT_NAME, LOGLINE(node->child), NULL, NULL)) {
goto error;
}
module->prefix = lydict_insert(ctx, value, strlen(value));
/* we are done with belongs-to */
lyxml_free_elem(ctx, node);
/* counters (statements with n..1 cardinality) */
} else if (!strcmp(node->name, "import")) {
c_imp++;
} else if (!strcmp(node->name, "revision")) {
c_rev++;
} else if (!strcmp(node->name, "typedef")) {
c_tpdf++;
} else if (!strcmp(node->name, "identity")) {
c_ident++;
} else if (!strcmp(node->name, "include")) {
c_inc++;
} else if (!strcmp(node->name, "augment")) {
c_aug++;
} else if (!strcmp(node->name, "feature")) {
c_ftrs++;
} else if (!strcmp(node->name, "deviation")) {
c_dev++;
/* data statements */
} else if (!strcmp(node->name, "container") ||
!strcmp(node->name, "leaf-list") ||
!strcmp(node->name, "leaf") ||
!strcmp(node->name, "list") ||
!strcmp(node->name, "choice") ||
!strcmp(node->name, "uses") ||
!strcmp(node->name, "anyxml")) {
lyxml_unlink_elem(node);
lyxml_add_child(&root, node);
} else if (!strcmp(node->name, "grouping")) {
/* keep groupings separated and process them before other data statements */
lyxml_unlink_elem(node);
lyxml_add_child(&grps, node);
/* optional statements */
} else if (!strcmp(node->name, "description")) {
if (module->dsc) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
module->dsc = read_yin_subnode(ctx, node, "text");
lyxml_free_elem(ctx, node);
if (!module->dsc) {
goto error;
}
} else if (!strcmp(node->name, "reference")) {
if (module->ref) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
module->ref = read_yin_subnode(ctx, node, "text");
lyxml_free_elem(ctx, node);
if (!module->ref) {
goto error;
}
} else if (!strcmp(node->name, "organization")) {
if (module->org) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
module->org = read_yin_subnode(ctx, node, "text");
lyxml_free_elem(ctx, node);
if (!module->org) {
goto error;
}
} else if (!strcmp(node->name, "contact")) {
if (module->contact) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
module->contact = read_yin_subnode(ctx, node, "text");
lyxml_free_elem(ctx, node);
if (!module->contact) {
goto error;
}
} else if (!strcmp(node->name, "yang-version")) {
/* TODO: support YANG 1.1 ? */
if (module->version) {
LOGVAL(VE_TOOMANY, LOGLINE(node), node->name, yin->name);
goto error;
}
GETVAL(value, node, "value");
if (strcmp(value, "1")) {
LOGVAL(VE_INARG, LOGLINE(node), value, "yang-version");
goto error;
}
module->version = 1;
lyxml_free_elem(ctx, node);
/* rpcs & notifications */
} else if (!strcmp(node->name, "rpc")) {
lyxml_unlink_elem(node);
lyxml_add_child(&rpcs, node);
} else if (!strcmp(node->name, "notification")) {
lyxml_unlink_elem(node);
lyxml_add_child(&notifs, node);
#if 0
} else {
LOGVAL(VE_INSTMT, LOGLINE(node), node->name);
goto error;
#else
} else {
continue;
#endif
}
}
if (!submodule) {
/* check for mandatory statements */
if (!module->ns) {
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "namespace", "module");
goto error;
}
if (!module->prefix) {
LOGVAL(VE_MISSSTMT2, LOGLINE(yin), "prefix", "module");
goto error;
}
}
/* allocate arrays for elements with cardinality of 0..n */
if (c_imp) {
module->imp = calloc(c_imp, sizeof *module->imp);
}
if (c_rev) {
module->rev = calloc(c_rev, sizeof *module->rev);
}
if (c_tpdf) {
module->tpdf = calloc(c_tpdf, sizeof *module->tpdf);
}
if (c_ident) {
module->ident = calloc(c_ident, sizeof *module->ident);
}
if (c_inc) {
module->inc = calloc(c_inc, sizeof *module->inc);
}
if (c_aug) {
module->augment = calloc(c_aug, sizeof *module->augment);
}
if (c_ftrs) {
module->features = calloc(c_ftrs, sizeof *module->features);
}
if (c_dev) {
module->deviation = calloc(c_dev, sizeof *module->deviation);
}
/* now we are going to remember unresolved types, the flag is
* used in case of error to get know how to free the structures
* in unres list
*/
unres_flag = 1;
/* middle part - process nodes with cardinality of 0..n except the data nodes */
LY_TREE_FOR_SAFE(yin->child, next, node) {
if (!strcmp(node->name, "import")) {
r = fill_yin_import(module, node, &module->imp[module->imp_size]);
module->imp_size++;
if (r) {
goto error;
}
/* check duplicities in imported modules */
for (i = 0; i < module->imp_size - 1; i++) {
if (!strcmp(module->imp[i].module->name, module->imp[module->imp_size - 1].module->name)) {
LOGVAL(VE_SPEC, LOGLINE(node), "Importing module \"%s\" repeatedly.", module->imp[i].module->name);
goto error;
}
}
} else if (!strcmp(node->name, "include")) {
r = fill_yin_include(module, node, &module->inc[module->inc_size]);
module->inc_size++;
if (r) {
goto error;
}
/* check duplications in include submodules */
for (i = 0; i < module->inc_size - 1; i++) {
if (!strcmp(module->inc[i].submodule->name, module->inc[module->inc_size - 1].submodule->name)) {
LOGVAL(VE_SPEC, LOGLINE(node), "Importing module \"%s\" repeatedly.",
module->inc[i].submodule->name);
goto error;
}
}
} else if (!strcmp(node->name, "revision")) {
GETVAL(value, node, "date");
if (check_date(value, LOGLINE(node))) {
goto error;
}
memcpy(module->rev[module->rev_size].date, value, LY_REV_SIZE - 1);
/* check uniqueness of the revision date - not required by RFC */
for (i = 0; i < module->rev_size; i++) {
if (!strcmp(value, module->rev[i].date)) {
LOGVAL(VE_INARG, LOGLINE(node), value, node->name);
LOGVAL(VE_SPEC, 0, "Revision is not unique.");
}
}
LY_TREE_FOR(node->child, child) {
if (!strcmp(child->name, "description")) {
if (module->rev[module->rev_size].dsc) {
LOGVAL(VE_TOOMANY, LOGLINE(node), child->name, node->name);
goto error;
}
module->rev[module->rev_size].dsc = read_yin_subnode(ctx, child, "text");
if (!module->rev[module->rev_size].dsc) {
goto error;
}
} else if (!strcmp(child->name, "reference")) {
if (module->rev[module->rev_size].ref) {
LOGVAL(VE_TOOMANY, LOGLINE(node), child->name, node->name);
goto error;
}
module->rev[module->rev_size].ref = read_yin_subnode(ctx, child, "text");
if (!module->rev[module->rev_size].ref) {
goto error;
}
} else {
LOGVAL(VE_INSTMT, LOGLINE(child), child->name);
goto error;
}
}
/* keep the latest revision at position 0 */
if (module->rev_size && strcmp(module->rev[module->rev_size].date, module->rev[0].date) > 0) {
/* switch their position */
value = strdup(module->rev[0].date);
memcpy(module->rev[0].date, module->rev[module->rev_size].date, LY_REV_SIZE - 1);
memcpy(module->rev[module->rev_size].date, value, LY_REV_SIZE - 1);
free((char *)value);
if (module->rev[0].dsc != module->rev[module->rev_size].dsc) {
value = module->rev[0].dsc;
module->rev[0].dsc = module->rev[module->rev_size].dsc;
module->rev[module->rev_size].dsc = value;
}
if (module->rev[0].ref != module->rev[module->rev_size].ref) {
value = module->rev[0].ref;
module->rev[0].ref = module->rev[module->rev_size].ref;
module->rev[module->rev_size].ref = value;
}
}
module->rev_size++;
} else if (!strcmp(node->name, "typedef")) {
r = fill_yin_typedef(module, NULL, node, &module->tpdf[module->tpdf_size], &unres);
module->tpdf_size++;
if (r) {
goto error;
}
} else if (!strcmp(node->name, "identity")) {
r = fill_yin_identity(module, node, &module->ident[module->ident_size]);
module->ident_size++;
if (r) {
goto error;
}
} else if (!strcmp(node->name, "feature")) {
r = fill_yin_feature(module, node, &module->features[module->features_size]);
module->features_size++;
if (r) {
goto error;
}
} else if (!strcmp(node->name, "augment")) {
r = fill_yin_augment(module, NULL, node, &module->augment[module->augment_size]);
module->augment_size++;
if (r) {
goto error;
}
/* node is reconnected into the augment, so we have to skip its free at the end of the loop */
continue;
} else if (!strcmp(node->name, "deviation")) {
r = fill_yin_deviation(module, node, &module->deviation[module->deviation_size]);
module->deviation_size++;
if (r) {
goto error;
}
}
lyxml_free_elem(ctx, node);
}
/* resolve unresolved types (possible in typedef's with unions */
while (unres) {
node = (struct lyxml_elem *)((struct ly_type *)unres->obj)->der;
((struct ly_type *)unres->obj)->der = NULL;
r = fill_yin_type(module, NULL, node, (struct ly_type *)unres->obj, NULL);
lyxml_free_elem(ctx, node);
unres_next = unres->next;
free(unres);
unres = unres_next;
if (r) {
goto error;
}
}
unres_flag = 0;
/* process data nodes. Start with groupings to allow uses
* refer to them
*/
LY_TREE_FOR_SAFE(grps.child, next, node) {
mnode = read_yin_grouping(module, NULL, node, 0, &unres);
lyxml_free_elem(ctx, node);
if (!mnode) {
goto error;
}
/* include data element */
if (module->data) {
module->data->prev->next = mnode;
mnode->prev = module->data->prev;
module->data->prev = mnode;
} else {
module->data = mnode;
}
}
while (unres) {
if (find_grouping(((struct ly_mnode_uses *)unres->obj)->parent, (struct ly_mnode_uses *)unres->obj, unres->line)) {
goto error;
}
if (!((struct ly_mnode_uses *)unres->obj)->grp) {
LOGVAL(VE_INARG, unres->line, ((struct ly_mnode_uses *)unres->obj)->name, "uses");
goto error;
}
unres_next = unres->next;
free(unres);
unres = unres_next;
}
/* parse data nodes, ... */
LY_TREE_FOR_SAFE(root.child, next, node) {
if (!strcmp(node->name, "container")) {
mnode = read_yin_container(module, NULL, node, 1, &unres);
} else if (!strcmp(node->name, "leaf-list")) {
mnode = read_yin_leaflist(module, NULL, node, 1);
} else if (!strcmp(node->name, "leaf")) {
mnode = read_yin_leaf(module, NULL, node, 1);
} else if (!strcmp(node->name, "list")) {
mnode = read_yin_list(module, NULL, node, 1, &unres);
} else if (!strcmp(node->name, "choice")) {
mnode = read_yin_choice(module, NULL, node, 1, &unres);
} else if (!strcmp(node->name, "uses")) {
mnode = read_yin_uses(module, NULL, node, 1, &unres);
} else if (!strcmp(node->name, "anyxml")) {
mnode = read_yin_anyxml(module, NULL, node, 1);
}
lyxml_free_elem(ctx, node);
if (!mnode) {
goto error;
}
/* include data element */
if (module->data) {
module->data->prev->next = mnode;
mnode->prev = module->data->prev;
module->data->prev = mnode;
} else {
module->data = mnode;
}
}
/* ... rpcs ... */
LY_TREE_FOR_SAFE(rpcs.child, next, node) {
mnode = read_yin_rpc(module, NULL, node, 0, &unres);
lyxml_free_elem(ctx, node);
if (!mnode) {
goto error;
}
/* include rpc element */
if (module->rpc) {
module->rpc->prev->next = mnode;
mnode->prev = module->rpc->prev;
module->rpc->prev = mnode;
} else {
module->rpc = mnode;
}
}
/* ... and notifications */
LY_TREE_FOR_SAFE(notifs.child, next, node) {
mnode = read_yin_notif(module, NULL, node, 0, &unres);
lyxml_free_elem(ctx, node);
if (!mnode) {
goto error;
}
/* include notification element */
if (module->notif) {
module->notif->prev->next = mnode;
mnode->prev = module->notif->prev;
module->notif->prev = mnode;
} else {
module->notif = mnode;
}
}
/* and now try to resolve unresolved uses, if any */
while (unres) {
/* find referenced grouping */
if (find_grouping(((struct ly_mnode_uses *)unres->obj)->parent, (struct ly_mnode_uses *)unres->obj, unres->line)) {
goto error;
}
if (!((struct ly_mnode_uses *)unres->obj)->grp) {
LOGVAL(VE_INARG, unres->line, ((struct ly_mnode_uses *)unres->obj)->name, "uses");
goto error;
}
/* resolve uses by copying grouping content under the uses */
if (resolve_uses((struct ly_mnode_uses *)unres->obj, unres->line)) {
goto error;
}
unres_next = unres->next;
free(unres);
unres = unres_next;
}
/* and finally apply augments */
for (i = 0; i < module->augment_size; i++) {
if (resolve_augment(&module->augment[i], NULL, module, 0)) {
goto error;
}
}
return EXIT_SUCCESS;
error:
/* cleanup */
while (root.child) {
lyxml_free_elem(module->ctx, root.child);
}
while (grps.child) {
lyxml_free_elem(module->ctx, grps.child);
}
while (rpcs.child) {
lyxml_free_elem(module->ctx, rpcs.child);
}
while (unres) {
unres_next = unres->next;
if (unres_flag) {
/* free the XML subtrees kept in unresolved type structures */
node = (struct lyxml_elem *)((struct ly_type *)unres->obj)->der;
lyxml_free_elem(ctx, node);
}
free(unres);
unres = unres_next;
}
return EXIT_FAILURE;
}
struct ly_submodule *
yin_read_submodule(struct ly_module *module, const char *data)
{
struct lyxml_elem *yin;
struct ly_submodule *submodule = NULL;
const char *value;
assert(module->ctx);
yin = lyxml_read(module->ctx, data, 0);
if (!yin) {
return NULL;
}
/* check root element */
if (!yin->name || strcmp(yin->name, "submodule")) {
LOGVAL(VE_INSTMT, LOGLINE(yin), yin->name);
goto error;
}
GETVAL(value, yin, "name");
if (check_identifier(value, LY_IDENT_NAME, LOGLINE(yin), NULL, NULL)) {
goto error;
}
submodule = calloc(1, sizeof *submodule);
if (!submodule) {
LOGMEM;
goto error;
}
submodule->ctx = module->ctx;
submodule->name = lydict_insert(submodule->ctx, value, strlen(value));
submodule->type = 1;
submodule->belongsto = module;
LOGVRB("reading submodule %s", submodule->name);
if (read_sub_module((struct ly_module *)submodule, yin)) {
goto error;
}
/* cleanup */
lyxml_free_elem(module->ctx, yin);
LOGVRB("submodule %s successfully parsed", submodule->name);
return submodule;
error:
/* cleanup */
lyxml_free_elem(module->ctx, yin);
ly_submodule_free(submodule);
return NULL;
}
struct ly_module *
yin_read_module(struct ly_ctx *ctx, const char *data)
{
struct lyxml_elem *yin;
struct ly_module *module = NULL, **newlist = NULL;
const char *value;
int i;
yin = lyxml_read(ctx, data, 0);
if (!yin) {
return NULL;
}
/* check root element */
if (!yin->name || strcmp(yin->name, "module")) {
LOGVAL(VE_INSTMT, LOGLINE(yin), yin->name);
goto error;
}
GETVAL(value, yin, "name");
if (check_identifier(value, LY_IDENT_NAME, LOGLINE(yin), NULL, NULL)) {
goto error;
}
module = calloc(1, sizeof *module);
if (!module) {
LOGMEM;
goto error;
}
module->ctx = ctx;
module->name = lydict_insert(ctx, value, strlen(value));
module->type = 0;
LOGVRB("reading module %s", module->name);
if (read_sub_module(module, yin)) {
goto error;
}
/* add to the context's list of modules */
if (ctx->models.used == ctx->models.size) {
newlist = realloc(ctx->models.list, ctx->models.size * 2);
if (!newlist) {
LOGMEM;
goto error;
}
for (i = ctx->models.size; i < ctx->models.size * 2; i++) {
newlist[i] = NULL;
}
ctx->models.size *= 2;
ctx->models.list = newlist;
}
for (i = 0; ctx->models.list[i]; i++) {
/* check name (name/revision) and namespace uniqueness */
if (!strcmp(ctx->models.list[i]->name, module->name)) {
if (!ctx->models.list[i]->rev_size && !module->rev_size) {
/* both data models are same, with no revision specified */
LOGERR(LY_EINVAL, "Module \"%s\" (no revision in either of them specified) already in context.",
module->name);
goto error;
} else if (!ctx->models.list[i]->rev_size || !module->rev_size) {
/* one of the models does not have a revision, so they differs */
continue;
} else {
/* both models have a revision statement which we have to
* compare, revision at position 0 is the last revision
*/
if (!strcmp(ctx->models.list[i]->rev[0].date, module->rev[0].date)) {
/* we have the same modules */
LOGERR(LY_EINVAL, "Module \"%s\", revision %s already in context.", module->name,
module->rev[0].date);
goto error;
}
}
} else if (!strcmp(ctx->models.list[i]->ns, module->ns)) {
LOGERR(LY_EINVAL, "Two different modules (\"%s\" and \"%s\") have the same namespace \"%s\"",
ctx->models.list[i]->name, module->name, module->ns);
goto error;
}
}
ctx->models.list[i] = module;
ctx->models.used++;
/* cleanup */
lyxml_free_elem(ctx, yin);
LOGVRB("module %s successfully parsed", module->name);
return module;
error:
/* cleanup */
lyxml_free_elem(ctx, yin);
ly_module_free(module);
return NULL;
}