#include "plugins_exts.h"
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include "dict.h"
#include "ly_common.h"
#include "parser_internal.h"
#include "printer_internal.h"
#include "schema_compile.h"
#include "schema_compile_amend.h"
#include "schema_compile_node.h"
#include "schema_features.h"
#include "tree_schema_internal.h"
LIBYANG_API_DEF const struct lysp_module *
lyplg_ext_parse_get_cur_pmod(const struct lysp_ctx *pctx)
{
return PARSER_CUR_PMOD(pctx);
}
LIBYANG_API_DEF LY_ERR
lyplg_ext_parse_extension_instance(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
{
LY_ERR rc = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u;
struct lysp_stmt *stmt;
LY_LIST_FOR(ext->child, stmt) {
if (stmt->flags & (LYS_YIN_ATTR | LYS_YIN_ARGUMENT)) {
continue;
}
LY_ARRAY_FOR(ext->substmts, u) {
if (ext->substmts[u].stmt == stmt->kw) {
break;
}
}
if (u == LY_ARRAY_COUNT(ext->substmts)) {
LOGVAL_PARSER(pctx, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s%s%s\" extension instance.",
stmt->stmt, ext->name, ext->argument ? " " : "", ext->argument ? ext->argument : "");
rc = LY_EVALID;
goto cleanup;
}
}
LY_ARRAY_FOR(ext->substmts, u) {
LY_LIST_FOR(ext->child, stmt) {
if (ext->substmts[u].stmt != stmt->kw) {
continue;
}
if ((rc = lys_parser_ext_instance_stmt(pctx, &ext->substmts[u], stmt))) {
goto cleanup;
}
}
}
cleanup:
return rc;
}
LIBYANG_API_DEF struct ly_ctx *
lyplg_ext_compile_get_ctx(const struct lysc_ctx *ctx)
{
return ctx->ctx;
}
LIBYANG_API_DEF uint32_t *
lyplg_ext_compile_get_options(const struct lysc_ctx *ctx)
{
return &((struct lysc_ctx *)ctx)->compile_opts;
}
LIBYANG_API_DEF const struct lys_module *
lyplg_ext_compile_get_cur_mod(const struct lysc_ctx *ctx)
{
return ctx->cur_mod;
}
LIBYANG_API_DEF struct lysp_module *
lyplg_ext_compile_get_pmod(const struct lysc_ctx *ctx)
{
return ctx->pmod;
}
static LY_ERR
lys_compile_ext_instance_stmt(struct lysc_ctx *ctx, void **parsed_p, struct lysc_ext_instance *ext,
struct lysc_node *parent, struct lysc_ext_substmt *substmt)
{
LY_ERR rc = LY_SUCCESS;
ly_bool length_restr = 0;
LY_DATA_TYPE basetype;
assert(*parsed_p);
if (substmt->stmt == LY_STMT_IF_FEATURE) {
ly_bool enabled;
LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, *parsed_p, &enabled), cleanup);
if (!enabled) {
rc = LY_ENOT;
}
}
if (!substmt->storage_p) {
goto cleanup;
}
switch (substmt->stmt) {
case LY_STMT_NOTIFICATION:
case LY_STMT_INPUT:
case LY_STMT_OUTPUT:
case LY_STMT_ACTION:
case LY_STMT_RPC:
case LY_STMT_ANYDATA:
case LY_STMT_ANYXML:
case LY_STMT_CASE:
case LY_STMT_CHOICE:
case LY_STMT_CONTAINER:
case LY_STMT_LEAF:
case LY_STMT_LEAF_LIST:
case LY_STMT_LIST:
case LY_STMT_USES: {
uint16_t flags;
struct lysp_node *pnodes, *pnode;
struct lysc_node *node;
lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags);
pnodes = *parsed_p;
LY_LIST_FOR(pnodes, pnode) {
if (pnode->nodetype & (LYS_INPUT | LYS_OUTPUT)) {
node = calloc(1, sizeof(struct lysc_node_action_inout));
LY_CHECK_ERR_GOTO(!node, LOGMEM(ctx->ctx); rc = LY_EMEM, cleanup);
LY_CHECK_GOTO(rc = lys_compile_node_action_inout(ctx, pnode, node), cleanup);
LY_CHECK_GOTO(rc = lys_compile_node_connect(ctx, NULL, node), cleanup);
} else {
LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, parent, flags, NULL), cleanup);
}
}
break;
}
case LY_STMT_ARGUMENT:
case LY_STMT_CONTACT:
case LY_STMT_DESCRIPTION:
case LY_STMT_ERROR_APP_TAG:
case LY_STMT_ERROR_MESSAGE:
case LY_STMT_KEY:
case LY_STMT_MODIFIER:
case LY_STMT_NAMESPACE:
case LY_STMT_ORGANIZATION:
case LY_STMT_PRESENCE:
case LY_STMT_REFERENCE:
case LY_STMT_UNITS:
LY_CHECK_GOTO(rc = lysdict_insert(ctx->ctx, *parsed_p, 0, (const char **)substmt->storage_p), cleanup);
break;
case LY_STMT_BIT:
basetype = LY_TYPE_BITS;
case LY_STMT_ENUM:
if (substmt->stmt == LY_STMT_ENUM) {
basetype = LY_TYPE_ENUM;
}
rc = lys_compile_type_enums(ctx, *parsed_p, basetype, NULL, (struct lysc_type_bitenum_item **)substmt->storage_p);
LY_CHECK_GOTO(rc, cleanup);
break;
case LY_STMT_CONFIG: {
uint16_t flags;
if (!(ctx->compile_opts & LYS_COMPILE_NO_CONFIG)) {
memcpy(&flags, parsed_p, 2);
if (flags & LYS_CONFIG_MASK) {
flags |= LYS_SET_CONFIG;
} else if (ext->parent_stmt & LY_STMT_DATA_NODE_MASK) {
flags = ((struct lysc_node *)ext->parent)->flags & LYS_CONFIG_MASK;
} else {
flags = LYS_CONFIG_W;
}
memcpy(substmt->storage_p, &flags, 2);
}
break;
}
case LY_STMT_MUST: {
const struct lysp_restr *restrs = *parsed_p;
COMPILE_ARRAY_GOTO(ctx, restrs, *(struct lysc_must **)substmt->storage_p, lys_compile_must, rc, cleanup);
break;
}
case LY_STMT_WHEN: {
uint16_t flags;
const struct lysp_when *when = *parsed_p;
lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags);
LY_CHECK_GOTO(rc = lys_compile_when(ctx, when, flags, NULL, NULL, NULL, (struct lysc_when **)substmt->storage_p), cleanup);
break;
}
case LY_STMT_FRACTION_DIGITS:
case LY_STMT_REQUIRE_INSTANCE:
memcpy(substmt->storage_p, parsed_p, 1);
break;
case LY_STMT_MANDATORY:
case LY_STMT_ORDERED_BY:
case LY_STMT_STATUS:
memcpy(substmt->storage_p, parsed_p, 2);
break;
case LY_STMT_MAX_ELEMENTS:
case LY_STMT_MIN_ELEMENTS:
memcpy(substmt->storage_p, parsed_p, 4);
break;
case LY_STMT_POSITION:
case LY_STMT_VALUE:
memcpy(substmt->storage_p, parsed_p, 8);
break;
case LY_STMT_IDENTITY:
rc = lys_identity_precompile(ctx, NULL, NULL, *parsed_p, (struct lysc_ident **)substmt->storage_p);
LY_CHECK_GOTO(rc, cleanup);
break;
case LY_STMT_LENGTH:
length_restr = 1;
case LY_STMT_RANGE:
rc = lys_compile_type_range(ctx, *parsed_p, LY_TYPE_UINT64, length_restr, 0, NULL, (struct lysc_range **)substmt->storage_p);
LY_CHECK_GOTO(rc, cleanup);
break;
case LY_STMT_PATTERN:
rc = lys_compile_type_patterns(ctx, *parsed_p, NULL, (struct lysc_pattern ***)substmt->storage_p);
LY_CHECK_GOTO(rc, cleanup);
break;
case LY_STMT_TYPE: {
uint16_t flags;
const char *units;
const struct lysp_type *ptype = *parsed_p;
lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags);
lyplg_ext_get_storage(ext, LY_STMT_UNITS, sizeof units, (const void **)&units);
rc = lys_compile_type(ctx, NULL, flags, ext->def->name, ptype, (struct lysc_type **)substmt->storage_p, &units, NULL);
LY_CHECK_GOTO(rc, cleanup);
LY_ATOMIC_INC_BARRIER((*(struct lysc_type **)substmt->storage_p)->refcount);
break;
}
case LY_STMT_EXTENSION_INSTANCE: {
struct lysp_ext_instance *extps = *parsed_p;
COMPILE_EXTS_GOTO(ctx, extps, *(struct lysc_ext_instance **)substmt->storage_p, ext, rc, cleanup);
break;
}
case LY_STMT_AUGMENT:
case LY_STMT_GROUPING:
case LY_STMT_BASE:
case LY_STMT_BELONGS_TO:
case LY_STMT_DEFAULT:
case LY_STMT_DEVIATE:
case LY_STMT_DEVIATION:
case LY_STMT_EXTENSION:
case LY_STMT_FEATURE:
case LY_STMT_IF_FEATURE:
case LY_STMT_IMPORT:
case LY_STMT_INCLUDE:
case LY_STMT_MODULE:
case LY_STMT_PATH:
case LY_STMT_PREFIX:
case LY_STMT_REFINE:
case LY_STMT_REVISION:
case LY_STMT_REVISION_DATE:
case LY_STMT_SUBMODULE:
case LY_STMT_TYPEDEF:
case LY_STMT_UNIQUE:
case LY_STMT_YANG_VERSION:
case LY_STMT_YIN_ELEMENT:
LOGVAL(ctx->ctx, NULL, LYVE_SYNTAX_YANG, "Statement \"%s\" compilation is not supported.",
lyplg_ext_stmt2str(substmt->stmt));
rc = LY_EVALID;
goto cleanup;
default:
LOGVAL(ctx->ctx, NULL, LYVE_SYNTAX_YANG, "Statement \"%s\" is not supported as an extension "
"(found in \"%s%s%s\") substatement.", lyplg_ext_stmt2str(substmt->stmt), ext->def->name,
ext->argument ? " " : "", ext->argument ? ext->argument : "");
rc = LY_EVALID;
goto cleanup;
}
cleanup:
return rc;
}
LIBYANG_API_DEF LY_ERR
lyplg_ext_compile_extension_instance(struct lysc_ctx *ctx, const struct lysp_ext_instance *extp,
struct lysc_ext_instance *ext, struct lysc_node *parent)
{
LY_ERR rc = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u, v;
enum ly_stmt stmtp;
void **storagep;
struct ly_set storagep_compiled = {0};
LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, ctx, extp, ext, LY_EINVAL);
ctx->ext = ext;
LY_ARRAY_FOR(extp->substmts, u) {
stmtp = extp->substmts[u].stmt;
storagep = extp->substmts[u].storage_p;
if (!*storagep || ly_set_contains(&storagep_compiled, storagep, NULL)) {
continue;
}
LY_ARRAY_FOR(ext->substmts, v) {
if (stmtp != ext->substmts[v].stmt) {
continue;
}
if ((rc = lys_compile_ext_instance_stmt(ctx, storagep, ext, parent, &ext->substmts[v]))) {
goto cleanup;
}
break;
}
ly_set_add(&storagep_compiled, storagep, 1, NULL);
}
cleanup:
ctx->ext = NULL;
ly_set_erase(&storagep_compiled, NULL);
return rc;
}
LIBYANG_API_DEF LY_ERR
lyplg_ext_compiled_node_augments(struct lysc_ctx *ctx, struct lysc_ext_instance *ext, struct lysc_node *node)
{
LY_ERR rc = LY_SUCCESS;
LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, ctx, ext, node, LY_EINVAL);
assert(!ctx->ext);
ctx->ext = ext;
LY_CHECK_GOTO(rc = lys_compile_node_augments(ctx, node), cleanup);
cleanup:
ctx->ext = NULL;
return rc;
}
LIBYANG_API_DEF struct ly_out **
lyplg_ext_print_get_out(const struct lyspr_ctx *ctx)
{
return &((struct lyspr_ctx *)ctx)->out;
}
LIBYANG_API_DEF uint32_t *
lyplg_ext_print_get_options(const struct lyspr_ctx *ctx)
{
return &((struct lyspr_ctx *)ctx)->options;
}
LIBYANG_API_DEF uint16_t *
lyplg_ext_print_get_level(const struct lyspr_ctx *ctx)
{
return &((struct lyspr_ctx *)ctx)->level;
}
LIBYANG_API_DEF int
lyplg_ext_compiled_stmts_storage_size(const struct lysc_ext_substmt *substmts, struct ly_ht *addr_ht)
{
LY_CHECK_ARG_RET(NULL, substmts, addr_ht, -1);
return ly_ctx_compiled_ext_stmts_storage_size(substmts, addr_ht);
}
LIBYANG_API_DEF LY_ERR
lyplg_ext_compiled_stmts_storage_print(const struct lysc_ext_substmt *orig_substmts, struct lysc_ext_substmt *substmts,
struct ly_ht *addr_ht, struct ly_set *ptr_set, void **mem)
{
LY_CHECK_ARG_RET(NULL, orig_substmts, substmts, addr_ht, ptr_set, mem, LY_EINVAL);
return ly_ctx_compiled_ext_stmts_storage_print(orig_substmts, substmts, addr_ht, ptr_set, mem);
}
LIBYANG_API_DEF void *
lyplg_ext_compiled_print_get_addr(const struct ly_ht *addr_ht, const void *addr)
{
LY_CHECK_ARG_RET(NULL, addr_ht, addr, NULL);
return ly_ctx_compiled_addr_ht_get(addr_ht, addr, 1);
}
LIBYANG_API_DEF LY_ERR
lyplg_ext_compiled_print_add_addr(struct ly_ht *addr_ht, const void *orig_addr, const void *addr)
{
LY_CHECK_ARG_RET(NULL, addr_ht, orig_addr, addr, LY_EINVAL);
return ly_ctx_compiled_addr_ht_add(addr_ht, orig_addr, addr);
}
LIBYANG_API_DEF const char *
lyplg_ext_stmt2str(enum ly_stmt stmt)
{
if (stmt == LY_STMT_EXTENSION_INSTANCE) {
return "extension instance";
} else {
return lys_stmt_str(stmt);
}
}
LIBYANG_API_DEF enum ly_stmt
lyplg_ext_nodetype2stmt(uint16_t nodetype)
{
switch (nodetype) {
case LYS_CONTAINER:
return LY_STMT_CONTAINER;
case LYS_CHOICE:
return LY_STMT_CHOICE;
case LYS_LEAF:
return LY_STMT_LEAF;
case LYS_LEAFLIST:
return LY_STMT_LEAF_LIST;
case LYS_LIST:
return LY_STMT_LIST;
case LYS_ANYXML:
return LY_STMT_ANYXML;
case LYS_ANYDATA:
return LY_STMT_ANYDATA;
case LYS_CASE:
return LY_STMT_CASE;
case LYS_RPC:
return LY_STMT_RPC;
case LYS_ACTION:
return LY_STMT_ACTION;
case LYS_NOTIF:
return LY_STMT_NOTIFICATION;
case LYS_USES:
return LY_STMT_USES;
case LYS_INPUT:
return LY_STMT_INPUT;
case LYS_OUTPUT:
return LY_STMT_OUTPUT;
default:
return LY_STMT_NONE;
}
}
LY_ERR
lyplg_ext_get_storage_p(const struct lysc_ext_instance *ext, int stmt, void ***storage_pp)
{
LY_ARRAY_COUNT_TYPE u;
enum ly_stmt match = 0;
*storage_pp = NULL;
if (!(stmt & LY_STMT_NODE_MASK)) {
match = stmt;
}
LY_ARRAY_FOR(ext->substmts, u) {
if ((match && (ext->substmts[u].stmt == match)) || (!match && (ext->substmts[u].stmt & stmt))) {
*storage_pp = ext->substmts[u].storage_p;
return LY_SUCCESS;
}
}
return LY_ENOT;
}
LIBYANG_API_DEF LY_ERR
lyplg_ext_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t storage_size, const void **storage)
{
LY_ERR rc = LY_SUCCESS;
void **s_p;
rc = lyplg_ext_get_storage_p(ext, stmt, &s_p);
if (s_p) {
memcpy(storage, s_p, storage_size);
} else {
memset(storage, 0, storage_size);
}
return rc;
}
LIBYANG_API_DEF LY_ERR
lyplg_ext_parsed_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t storage_size, const void **storage)
{
LY_ARRAY_COUNT_TYPE u;
const struct lysp_ext_instance *extp = NULL;
const char *extp_name;
enum ly_stmt match = 0;
void **s_p = NULL;
LY_CHECK_ARG_RET(NULL, ext, ext->module->parsed, LY_EINVAL);
LY_ARRAY_FOR(ext->module->parsed->exts, u) {
extp = &ext->module->parsed->exts[u];
extp_name = strchr(extp->name, ':') + 1;
if (!strcmp(ext->def->name, extp_name)) {
break;
}
extp = NULL;
}
assert(extp);
if (!(stmt & LY_STMT_NODE_MASK)) {
match = stmt;
}
LY_ARRAY_FOR(extp->substmts, u) {
if ((match && (extp->substmts[u].stmt == match)) || (!match && (extp->substmts[u].stmt & stmt))) {
s_p = extp->substmts[u].storage_p;
break;
}
}
if (s_p) {
memcpy(storage, s_p, storage_size);
} else {
memset(storage, 0, storage_size);
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyplg_ext_get_data(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, const struct lyd_node *parent,
void **ext_data, ly_bool *ext_data_free)
{
LY_ERR rc;
struct ly_ctx_shared_data *ctx_data;
LY_CHECK_ARG_RET(ctx, ctx, ext, ext_data, LY_EINVAL);
ctx_data = ly_ctx_shared_data_get(ctx);
pthread_mutex_lock(&ctx_data->ext_clb_lock);
if (!ctx_data->ext_clb) {
lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Failed to get extension data, no callback set.");
rc = LY_EINVAL;
goto cleanup;
}
if ((rc = ctx_data->ext_clb(ext, parent, ctx_data->ext_clb_data, ext_data, ext_data_free))) {
lyplg_ext_compile_log(NULL, ext, LY_LLERR, rc, "Callback for getting ext data failed.");
goto cleanup;
}
cleanup:
pthread_mutex_unlock(&ctx_data->ext_clb_lock);
return rc;
}
LIBYANG_API_DEF LY_ERR
lyplg_ext_set_parent_ctx(struct ly_ctx *ctx, const struct ly_ctx *parent_ctx)
{
const struct ly_ctx *c;
LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
if (ctx->parent_ctx == parent_ctx) {
return LY_SUCCESS;
}
c = parent_ctx;
while (c) {
if (c->parent_ctx == ctx) {
LOGERR(ctx, LY_EDENIED, "Circular references of parent contexts.");
return LY_EDENIED;
}
c = c->parent_ctx;
}
if (!ctx->parent_ctx) {
if (ly_ctx_is_printed(ctx) && ly_ctx_is_printed(parent_ctx)) {
ly_ctx_destroy(ctx);
} else {
ly_ctx_pattern_ht_erase(ctx);
ly_ctx_data_del(ctx);
}
} else if (!parent_ctx) {
LY_CHECK_RET(ly_ctx_data_add(ctx));
}
ctx->parent_ctx = (struct ly_ctx *)parent_ctx;
return LY_SUCCESS;
}