blob: a54346e057a1ea2b05f7f573f6803339eed97a6f [file] [log] [blame]
/**
* @file parser_yang.c
* @author Pavol Vican
* @brief YANG 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 "parser_yang.h"
#include "parser_yang_bis.h"
#include "parser.h"
#include "xpath.h"
static int
yang_check_string(struct lys_module *module, const char **target, char *what, char *where, char *value, int line)
{
if (*target) {
LOGVAL(LYE_TOOMANY, line, what, where);
free(value);
return 1;
} else {
*target = lydict_insert_zc(module->ctx, value);
return 0;
}
}
int
yang_read_common(struct lys_module *module, char *value, int type, int line)
{
int ret = 0;
switch (type) {
case MODULE_KEYWORD:
module->name = lydict_insert_zc(module->ctx, value);
break;
case NAMESPACE_KEYWORD:
ret = yang_check_string(module, &module->ns, "namespace", "module", value, line);
break;
case ORGANIZATION_KEYWORD:
ret = yang_check_string(module, &module->org, "organization", "module", value, line);
break;
case CONTACT_KEYWORD:
ret = yang_check_string(module, &module->contact, "contact", "module", value, line);
break;
}
return ret;
}
int
yang_read_prefix(struct lys_module *module, void *save, char *value, int type, int line)
{
int ret = 0;
if (lyp_check_identifier(value, LY_IDENT_PREFIX, line, module, NULL)) {
free(value);
return EXIT_FAILURE;
}
switch (type){
case MODULE_KEYWORD:
ret = yang_check_string(module, &module->prefix, "prefix", "module", value, line);
break;
case IMPORT_KEYWORD:
((struct lys_import *)save)->prefix = lydict_insert_zc(module->ctx, value);
break;
}
return ret;
}
void *
yang_elem_of_array(void **ptr, uint8_t *act_size, int type, int sizeof_struct)
{
void *retval;
if (!(*act_size % LY_ARRAY_SIZE) && !(*ptr = ly_realloc(*ptr, (*act_size + LY_ARRAY_SIZE) * sizeof_struct))) {
LOGMEM;
return NULL;
}
switch (type) {
case IMPORT_KEYWORD:
retval = &((struct lys_import *)(*ptr))[*act_size];
break;
case REVISION_KEYWORD:
retval = &((struct lys_revision *)(*ptr))[*act_size];
break;
}
(*act_size)++;
memset(retval,0,sizeof_struct);
return retval;
}
int
yang_fill_import(struct lys_module *module, struct lys_import *imp, char *value, int line)
{
int count, i;
/* check for circular import, store it if passed */
if (!module->ctx->models.parsing) {
count = 0;
} else {
for (count = 0; module->ctx->models.parsing[count]; ++count) {
if (value == module->ctx->models.parsing[count]) {
LOGERR(LY_EVALID, "Circular import dependency on the module \"%s\".", value);
goto error;
}
}
}
++count;
module->ctx->models.parsing =
ly_realloc(module->ctx->models.parsing, (count + 1) * sizeof *module->ctx->models.parsing);
if (!module->ctx->models.parsing) {
LOGMEM;
goto error;
}
module->ctx->models.parsing[count - 1] = value;
module->ctx->models.parsing[count] = NULL;
/* try to load the module */
imp->module = (struct lys_module *)ly_ctx_get_module(module->ctx, value, imp->rev[0] ? imp->rev : NULL);
if (!imp->module) {
/* whether to use a user callback is decided in the function */
imp->module = (struct lys_module *)ly_ctx_load_module(module->ctx, value, imp->rev[0] ? imp->rev : NULL);
}
/* remove the new module name now that its parsing is finished (even if failed) */
if (module->ctx->models.parsing[count] || (module->ctx->models.parsing[count - 1] != value)) {
LOGINT;
}
--count;
if (count) {
module->ctx->models.parsing[count] = NULL;
} else {
free(module->ctx->models.parsing);
module->ctx->models.parsing = NULL;
}
/* check the result */
if (!imp->module) {
LOGERR(LY_EVALID, "Importing \"%s\" module into \"%s\" failed.", value, module->name);
goto error;
}
module->imp_size++;
/* 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(LYE_SPEC, line, "Importing module \"%s\" repeatedly.", module->imp[i].module->name);
goto error;
}
}
free(value);
return EXIT_SUCCESS;
error:
free(value);
return EXIT_FAILURE;
}
int
yang_read_description(struct lys_module *module, void *node, char *value, int type, int line)
{
int ret;
if (!node) {
ret = yang_check_string(module, &module->dsc, "description", "module", value, line);
} else {
switch (type) {
case REVISION_KEYWORD:
ret = yang_check_string(module, &((struct lys_revision *) node)->dsc, "description", "revision", value, line);
break;
case FEATURE_KEYWORD:
ret = yang_check_string(module, &((struct lys_feature *) node)->dsc, "description", "feature", value, line);
break;
case IDENTITY_KEYWORD:
ret = yang_check_string(module, &((struct lys_ident *) node)->dsc, "description", "identity", value, line);
break;
case MUST_KEYWORD:
ret = yang_check_string(module, &((struct lys_restr *) node)->dsc, "description", "must", value, line);
break;
}
}
return ret;
}
int
yang_read_reference(struct lys_module *module, void *node, char *value, int type, int line)
{
int ret;
if (!node) {
ret = yang_check_string(module, &module->ref, "reference", "module", value, line);
} else {
switch (type) {
case REVISION_KEYWORD:
ret = yang_check_string(module, &((struct lys_revision *) node)->ref, "reference", "revision", value, line);
break;
case FEATURE_KEYWORD:
ret = yang_check_string(module, &((struct lys_feature *) node)->ref, "reference", "feature", value, line);
break;
case IDENTITY_KEYWORD:
ret = yang_check_string(module, &((struct lys_ident *) node)->ref, "reference", "identity", value, line);
break;
case MUST_KEYWORD:
ret = yang_check_string(module,&((struct lys_restr *) node)->ref, "reference", "must", value, line);
break;
}
}
return ret;
}
void *
yang_read_revision(struct lys_module *module, char *value)
{
struct lys_revision *retval;
retval = &module->rev[module->rev_size];
/* first member of array is last revision */
if (module->rev_size && strcmp(module->rev[0].date, value) < 0) {
memcpy(retval->date, module->rev[0].date, LY_REV_SIZE);
memcpy(module->rev[0].date, value, LY_REV_SIZE);
retval->dsc = module->rev[0].dsc;
retval->ref = module->rev[0].ref;
retval = module->rev;
retval->dsc = NULL;
retval->ref = NULL;
} else {
memcpy(retval->date, value, LY_REV_SIZE);
}
module->rev_size++;
free(value);
return retval;
}
int
yang_add_elem(struct lys_node_array **node, int *size)
{
if (!*size % LY_ARRAY_SIZE) {
if (!(*node = ly_realloc(*node, (*size + LY_ARRAY_SIZE) * sizeof **node))) {
LOGMEM;
return EXIT_FAILURE;
} else {
memset(*node+*size,0,LY_ARRAY_SIZE*sizeof **node);
}
}
(*size)++;
return EXIT_SUCCESS;
}
void *
yang_read_feature(struct lys_module *module, char *value, int line)
{
struct lys_feature *retval;
/* check uniqueness of feature's names */
if (lyp_check_identifier(value, LY_IDENT_FEATURE, line, module, NULL)) {
goto error;
}
retval = &module->features[module->features_size];
retval->name = lydict_insert_zc(module->ctx, value);
retval->module = module;
module->features_size++;
return retval;
error:
free(value);
return NULL;
}
int
yang_read_if_feature(struct lys_module *module, void *ptr, char *value, struct unres_schema *unres, int type, int line)
{
const char *exp;
int ret;
struct lys_feature *f;
struct lys_node *n;
if (!(exp = transform_schema2json(module, value, line))) {
free(value);
return EXIT_FAILURE;
}
free(value);
/* hack - store pointer to the parent node for later status check */
if (type == FEATURE_KEYWORD) {
f = (struct lys_feature *) ptr;
f->features[f->features_size] = f;
ret = unres_schema_add_str(module, unres, &f->features[f->features_size], UNRES_IFFEAT, exp, line);
f->features_size++;
} else {
n = (struct lys_node *) ptr;
n->features[n->features_size] = (struct lys_feature *) n;
ret = unres_schema_add_str(module, unres, &n->features[n->features_size], UNRES_IFFEAT, exp, line);
n->features_size++;
}
lydict_remove(module->ctx, exp);
if (ret == -1) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
static int
yang_check_flags(uint8_t *flags, uint8_t mask, char *what, char *where, int value, int line)
{
if (*flags & mask) {
LOGVAL(LYE_TOOMANY, line, what, where);
return EXIT_FAILURE;
} else {
*flags = value;
return EXIT_SUCCESS;
}
}
int
yang_read_status(void *node, int value, int type, int line)
{
int retval;
switch (type) {
case FEATURE_KEYWORD:
retval = yang_check_flags(&((struct lys_feature *) node)->flags, LYS_STATUS_MASK, "status", "feature", value, line);
break;
case IDENTITY_KEYWORD:
retval = yang_check_flags(&((struct lys_ident *) node)->flags, LYS_STATUS_MASK, "status", "identity", value, line);
break;
}
return retval;
}
void *
yang_read_identity(struct lys_module *module, char *value)
{
struct lys_ident *ret;
ret = &module->ident[module->ident_size];
ret->name = lydict_insert_zc(module->ctx, value);
ret->module = module;
module->ident_size++;
return ret;
}
int
yang_read_base(struct lys_module *module, struct lys_ident *ident, char *value, struct unres_schema *unres, int line)
{
const char *exp;
if (ident->base) {
LOGVAL(LYE_TOOMANY, line, "base", "identity");
return EXIT_FAILURE;
}
exp = transform_schema2json(module, value, line);
free(value);
if (!exp) {
return EXIT_FAILURE;
}
if (unres_schema_add_str(module, unres, ident, UNRES_IDENT, exp, line) == -1) {
lydict_remove(module->ctx, exp);
return EXIT_FAILURE;
}
lydict_remove(module->ctx, exp);
return EXIT_SUCCESS;
}
void *
yang_read_cont(struct lys_module *module, struct lys_node *parent, char *value)
{
struct lys_node_container *cont;
cont = calloc(1, sizeof *cont);
if (!cont) {
LOGMEM;
return NULL;
}
cont->module = module;
cont->name = lydict_insert_zc(module->ctx, value);
cont->nodetype = LYS_CONTAINER;
cont->prev = (struct lys_node *)cont;
if (lys_node_addchild(parent, module->type ? ((struct lys_submodule *)module)->belongsto: module, (struct lys_node *)cont)) {
lydict_remove(module->ctx, cont->name);
free(cont);
return NULL;
}
return cont;
}
void *
yang_read_must(struct lys_module *module, struct lys_node *node, char *value, int type, int line)
{
struct lys_restr *retval;
switch (type) {
case CONTAINER_KEYWORD:
retval = &((struct lys_node_container *)node)->must[((struct lys_node_container *)node)->must_size];
break;
}
retval->expr = transform_schema2json(module, value, line);
if (!retval->expr || lyxp_syntax_check(retval->expr, line)) {
goto error;
}
free(value);
return retval;
error:
free(value);
return NULL;
}
int
yang_read_message(struct lys_module *module,struct lys_restr *save,char *value, int type, int message, int line)
{
int ret;
char *exp;
switch (type) {
case MUST_KEYWORD:
exp = "must";
break;
}
if (message==ERROR_APP_TAG_KEYWORD) {
ret = yang_check_string(module, &save->eapptag, "error_app_tag", exp, value, line);
} else {
ret = yang_check_string(module, &save->emsg, "error_app_tag", exp, value, line);
}
return ret;
}