#define _GNU_SOURCE
#include "tree_schema.h"
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "compat.h"
#include "context.h"
#include "dict.h"
#include "in.h"
#include "in_internal.h"
#include "log.h"
#include "ly_common.h"
#include "parser_internal.h"
#include "parser_schema.h"
#include "path.h"
#include "plugins_exts.h"
#include "plugins_internal.h"
#include "schema_compile.h"
#include "schema_compile_amend.h"
#include "schema_features.h"
#include "set.h"
#include "tree.h"
#include "tree_edit.h"
#include "tree_schema_free.h"
#include "tree_schema_internal.h"
#include "xml.h"
#include "xpath.h"
const char * const ly_devmod_list[] = {
[LYS_DEV_NOT_SUPPORTED] = "not-supported",
[LYS_DEV_ADD] = "add",
[LYS_DEV_DELETE] = "delete",
[LYS_DEV_REPLACE] = "replace",
};
LIBYANG_API_DEF LY_ERR
lysc_tree_dfs_full(const struct lysc_node *root, lysc_dfs_clb dfs_clb, void *data)
{
struct lysc_node *elem, *elem2;
const struct lysc_node_action *action;
const struct lysc_node_notif *notif;
LY_CHECK_ARG_RET(NULL, root, dfs_clb, LY_EINVAL);
LYSC_TREE_DFS_BEGIN(root, elem) {
LY_CHECK_RET(dfs_clb(elem, data, &LYSC_TREE_DFS_continue));
LY_LIST_FOR(lysc_node_actions(elem), action) {
LYSC_TREE_DFS_BEGIN(action, elem2) {
LY_CHECK_RET(dfs_clb(elem2, data, &LYSC_TREE_DFS_continue));
LYSC_TREE_DFS_END(action, elem2);
}
}
LY_LIST_FOR(lysc_node_notifs(elem), notif) {
LYSC_TREE_DFS_BEGIN(notif, elem2) {
LY_CHECK_RET(dfs_clb(elem2, data, &LYSC_TREE_DFS_continue));
LYSC_TREE_DFS_END(notif, elem2);
}
}
LYSC_TREE_DFS_END(root, elem);
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lysc_module_dfs_full(const struct lys_module *mod, lysc_dfs_clb dfs_clb, void *data)
{
const struct lysc_node *root;
LY_CHECK_ARG_RET(NULL, mod, mod->compiled, dfs_clb, LY_EINVAL);
LY_LIST_FOR(mod->compiled->data, root) {
LY_CHECK_RET(lysc_tree_dfs_full(root, dfs_clb, data));
}
LY_LIST_FOR((const struct lysc_node *)mod->compiled->rpcs, root) {
LY_CHECK_RET(lysc_tree_dfs_full(root, dfs_clb, data));
}
LY_LIST_FOR((const struct lysc_node *)mod->compiled->notifs, root) {
LY_CHECK_RET(lysc_tree_dfs_full(root, dfs_clb, data));
}
return LY_SUCCESS;
}
static const struct lys_module *
ly_schema_resolve_prefix(const struct ly_ctx *UNUSED(ctx), const char *prefix, uint32_t prefix_len, const void *prefix_data)
{
const struct lysp_module *prefix_mod = prefix_data;
LY_ARRAY_COUNT_TYPE u;
const char *local_prefix;
local_prefix = prefix_mod->is_submod ? ((struct lysp_submodule *)prefix_mod)->prefix : prefix_mod->mod->prefix;
if (!prefix_len || !ly_strncmp(local_prefix, prefix, prefix_len)) {
return prefix_mod->mod;
}
LY_ARRAY_FOR(prefix_mod->imports, u) {
if (!ly_strncmp(prefix_mod->imports[u].prefix, prefix, prefix_len)) {
return prefix_mod->imports[u].module;
}
}
return NULL;
}
static const struct lys_module *
ly_schema_resolved_resolve_prefix(const struct ly_ctx *UNUSED(ctx), const char *prefix, uint32_t prefix_len,
const void *prefix_data)
{
const struct lysc_prefix *prefixes = prefix_data;
LY_ARRAY_COUNT_TYPE u;
LY_ARRAY_FOR(prefixes, u) {
if ((!prefixes[u].prefix && !prefix_len) || (prefixes[u].prefix && !ly_strncmp(prefixes[u].prefix, prefix, prefix_len))) {
return prefixes[u].mod;
}
}
return NULL;
}
static const struct lys_module *
ly_xml_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, uint32_t prefix_len, const void *prefix_data)
{
const struct lys_module *mod;
const struct lyxml_ns *ns;
const struct ly_set *ns_set = prefix_data;
ns = lyxml_ns_get(ns_set, prefix, prefix_len);
if (!ns) {
return NULL;
}
mod = ly_ctx_get_module_implemented_ns(ctx, ns->uri);
if (!mod) {
mod = ly_ctx_get_module_latest_ns(ctx, ns->uri);
}
return mod;
}
static const struct lys_module *
ly_json_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, uint32_t prefix_len, const void *UNUSED(prefix_data))
{
return ly_ctx_get_module_implemented2(ctx, prefix, prefix_len);
}
const struct lys_module *
ly_resolve_prefix(const struct ly_ctx *ctx, const void *prefix, uint32_t prefix_len, LY_VALUE_FORMAT format,
const void *prefix_data)
{
const struct lys_module *mod = NULL;
LY_CHECK_ARG_RET(ctx, prefix, prefix_len, NULL);
switch (format) {
case LY_VALUE_SCHEMA:
mod = ly_schema_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
break;
case LY_VALUE_SCHEMA_RESOLVED:
mod = ly_schema_resolved_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
break;
case LY_VALUE_XML:
case LY_VALUE_STR_NS:
mod = ly_xml_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
break;
case LY_VALUE_CANON:
case LY_VALUE_JSON:
case LY_VALUE_LYB:
mod = ly_json_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
break;
}
return mod;
}
LIBYANG_API_DEF const struct lys_module *
lys_find_module(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *prefix, uint32_t prefix_len,
LY_VALUE_FORMAT format, const void *prefix_data)
{
if (prefix_len) {
return ly_resolve_prefix(ctx, prefix, prefix_len, format, prefix_data);
} else {
switch (format) {
case LY_VALUE_SCHEMA:
return ly_schema_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
case LY_VALUE_SCHEMA_RESOLVED:
return ly_schema_resolved_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
case LY_VALUE_CANON:
case LY_VALUE_JSON:
case LY_VALUE_LYB:
case LY_VALUE_STR_NS:
return ctx_node ? ctx_node->module : NULL;
case LY_VALUE_XML:
return ly_xml_resolve_prefix(ctx, NULL, 0, prefix_data);
}
}
return NULL;
}
static void
lys_getnext_into_case(const struct lysc_node_case *first_case, const struct lysc_node **last, const struct lysc_node **next)
{
for ( ; first_case; first_case = (const struct lysc_node_case *)first_case->next) {
if (first_case->child) {
(*next) = first_case->child;
return;
}
}
(*last) = (*next);
(*next) = (*next)->next;
}
LIBYANG_API_DEF const struct lysc_node *
lys_getnext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module, uint32_t options)
{
const struct lysc_node *next = NULL;
ly_bool action_flag = 0, notif_flag = 0;
LY_CHECK_ARG_RET(NULL, last || parent || module, NULL);
next:
if (!last) {
if (parent) {
next = last = lysc_node_child(parent);
} else {
next = last = module->data;
}
if (!next) {
goto repeat;
}
goto check;
} else if (last->nodetype & (LYS_RPC | LYS_ACTION)) {
action_flag = 1;
next = last->next;
} else if (last->nodetype == LYS_NOTIF) {
action_flag = notif_flag = 1;
next = last->next;
} else {
next = last->next;
}
repeat:
if (!next) {
if (last && (last->parent != parent)) {
last = last->parent;
goto next;
} else if (!action_flag) {
action_flag = 1;
if (parent) {
next = (struct lysc_node *)lysc_node_actions(parent);
} else if (module) {
next = (struct lysc_node *)module->rpcs;
}
} else if (!notif_flag) {
notif_flag = 1;
if (parent) {
next = (struct lysc_node *)lysc_node_notifs(parent);
} else if (module) {
next = (struct lysc_node *)module->notifs;
}
} else {
return NULL;
}
goto repeat;
}
check:
switch (next->nodetype) {
case LYS_RPC:
case LYS_ACTION:
case LYS_NOTIF:
case LYS_LEAF:
case LYS_ANYXML:
case LYS_ANYDATA:
case LYS_LIST:
case LYS_LEAFLIST:
break;
case LYS_CASE:
if (options & LYS_GETNEXT_WITHCASE) {
break;
} else {
lys_getnext_into_case((const struct lysc_node_case *)next, &last, &next);
}
goto repeat;
case LYS_CONTAINER:
if (!(next->flags & LYS_PRESENCE) && (options & LYS_GETNEXT_INTONPCONT)) {
if (lysc_node_child(next)) {
next = lysc_node_child(next);
} else {
last = next;
next = next->next;
}
goto repeat;
}
break;
case LYS_CHOICE:
if (options & LYS_GETNEXT_WITHCHOICE) {
break;
} else if ((options & LYS_GETNEXT_NOCHOICE) || !lysc_node_child(next)) {
next = next->next;
} else {
if (options & LYS_GETNEXT_WITHCASE) {
next = lysc_node_child(next);
} else {
lys_getnext_into_case(((struct lysc_node_choice *)next)->cases, &last, &next);
}
}
goto repeat;
case LYS_INPUT:
if (options & LYS_GETNEXT_OUTPUT) {
next = next->next;
} else {
next = lysc_node_child(next);
}
goto repeat;
case LYS_OUTPUT:
if (!(options & LYS_GETNEXT_OUTPUT)) {
next = next->next;
} else {
next = lysc_node_child(next);
}
goto repeat;
default:
LOGINT(NULL);
return NULL;
}
return next;
}
static LY_ERR
lys_ext_find_node(const struct lysc_ext_instance *ext, const struct lyd_node *parent, const struct lysc_node *sparent,
const char *prefix, uint32_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name,
uint32_t name_len, ly_bool is_xpath, const struct lysc_node **snode)
{
LY_ERR rc = LY_SUCCESS;
struct lyplg_ext *plg_ext;
const struct lys_module *mod;
const struct lysc_node *node;
plg_ext = LYSC_GET_EXT_PLG(ext->def->plugin_ref);
if (!plg_ext) {
return LY_ENOT;
}
if (is_xpath && plg_ext->snode_xpath) {
rc = plg_ext->snode_xpath((struct lysc_ext_instance *)ext, prefix, prefix_len, format, prefix_data, name,
name_len, snode);
} else if (!is_xpath && plg_ext->snode) {
rc = plg_ext->snode((struct lysc_ext_instance *)ext, parent, sparent, prefix, prefix_len, format,
prefix_data, name, name_len, snode);
} else {
lyplg_ext_get_storage(ext, LY_STMT_DATA_NODE_MASK, sizeof node, (const void **)&node);
if (node) {
mod = lys_find_module((*snode)->module->ctx, parent ? parent->schema : sparent, prefix, prefix_len, format,
prefix_data);
}
if (node && mod && mod->implemented) {
while ((node = lys_getnext(node, node->parent, NULL, 0))) {
if (node->module != mod) {
continue;
}
if (ly_strncmp(node->name, name, name_len)) {
continue;
}
break;
}
if (node) {
*snode = node;
}
}
if (!*snode) {
rc = LY_ENOT;
}
}
return rc;
}
LY_ERR
lys_find_child_node_ext(const struct ly_ctx *ctx, const struct lys_module *mod, const struct lyd_node *parent,
const struct lysc_node *sparent, const char *prefix, uint32_t prefix_len, LY_VALUE_FORMAT format,
void *prefix_data, const char *name, uint32_t name_len, ly_bool is_xpath, const struct lysc_node **snode,
struct lysc_ext_instance **ext)
{
LY_ERR r;
LY_ARRAY_COUNT_TYPE u;
struct lysc_ext_instance *exts;
*snode = NULL;
if (ext) {
*ext = NULL;
}
if (parent && parent->schema) {
exts = parent->schema->exts;
} else if (sparent) {
exts = sparent->exts;
} else {
exts = NULL;
}
LY_ARRAY_FOR(exts, u) {
r = lys_ext_find_node(&exts[u], parent, sparent, prefix, prefix_len, format, prefix_data, name, name_len,
is_xpath, snode);
if (!r) {
if (ext) {
*ext = &exts[u];
}
return LY_SUCCESS;
} else if (r != LY_ENOT) {
return r;
}
}
if (!mod) {
mod = lys_find_module(ctx, parent ? parent->schema : sparent, prefix, prefix_len, format, prefix_data);
}
if (mod && mod->implemented) {
exts = mod->compiled->exts;
} else {
exts = NULL;
}
LY_ARRAY_FOR(exts, u) {
r = lys_ext_find_node(&exts[u], parent, sparent, prefix, prefix_len, format, prefix_data, name, name_len,
is_xpath, snode);
if (!r) {
if (ext) {
*ext = &exts[u];
}
return LY_SUCCESS;
} else if (r != LY_ENOT) {
return r;
}
}
return LY_ENOT;
}
LY_ERR
lys_find_child_node(const struct ly_ctx *ctx, const struct lysc_node *parent, const struct lys_module *mod,
const char *prefix, uint32_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name,
uint32_t name_len, uint32_t options, const struct lysc_node **snode, struct lysc_ext_instance **ext)
{
const struct lysc_node *node = NULL;
assert(name);
*snode = NULL;
if (ext) {
*ext = NULL;
}
if (prefix && !prefix_len) {
prefix_len = strlen(prefix);
}
if (!name_len) {
name_len = strlen(name);
}
if (!mod) {
mod = lys_find_module(ctx, parent, prefix, prefix_len, format, prefix_data);
}
if (mod && mod->implemented) {
while ((node = lys_getnext(node, parent, mod->compiled, options))) {
if (node->module != mod) {
continue;
}
if (!ly_strncmp(node->name, name, name_len)) {
*snode = node;
return LY_SUCCESS;
}
}
}
return lys_find_child_node_ext(ctx, mod, NULL, parent, prefix, prefix_len, format, prefix_data, name, name_len,
0, snode, ext);
}
LIBYANG_API_DEF const struct lysc_node *
lys_find_child(const struct ly_ctx *ctx, const struct lysc_node *parent, const struct lys_module *mod,
const char *mod_name, uint32_t mod_len, const char *name, uint32_t name_len, uint32_t options)
{
const struct lysc_node *snode = NULL;
LY_CHECK_ARG_RET(NULL, ctx || parent || mod, (mod || mod_name) && (!mod || !mod_name), name, NULL);
if (parent) {
ctx = parent->module->ctx;
} else if (mod) {
ctx = mod->ctx;
}
if (mod) {
mod_name = mod->name;
}
if (mod_name && !mod_len) {
mod_len = strlen(mod_name);
}
if (!name_len) {
name_len = strlen(name);
}
lys_find_child_node(ctx, parent, mod, mod_name, mod_len, LY_VALUE_JSON, NULL, name, name_len, options, &snode, NULL);
return snode;
}
LIBYANG_API_DEF LY_ERR
lys_find_xpath_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *xpath, uint32_t options,
struct ly_set **set)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_set xp_set = {0};
struct lyxp_expr *exp = NULL;
uint32_t i;
LY_CHECK_ARG_RET(NULL, ctx || ctx_node, xpath, set, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, ctx, ctx_node ? ctx_node->module->ctx : NULL, LY_EINVAL);
if (!(options & LYXP_SCNODE_ALL)) {
options |= LYXP_SCNODE;
}
if (!ctx) {
ctx = ctx_node->module->ctx;
}
ret = ly_set_new(set);
LY_CHECK_GOTO(ret, cleanup);
ret = lyxp_expr_parse(ctx, NULL, xpath, 0, 1, &exp);
LY_CHECK_GOTO(ret, cleanup);
ret = lyxp_atomize(ctx, exp, NULL, LY_VALUE_JSON, NULL, ctx_node, ctx_node, &xp_set, options);
LY_CHECK_GOTO(ret, cleanup);
(*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(ctx); ret = LY_EMEM, cleanup);
(*set)->size = xp_set.used;
for (i = 0; i < xp_set.used; ++i) {
if (xp_set.val.scnodes[i].type == LYXP_NODE_ELEM) {
ret = ly_set_add(*set, xp_set.val.scnodes[i].scnode, 1, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
}
cleanup:
lyxp_set_free_content(&xp_set);
lyxp_expr_free(exp);
return ret;
}
LIBYANG_API_DEF LY_ERR
lys_find_expr_atoms(const struct lysc_node *ctx_node, const struct lys_module *cur_mod, const struct lyxp_expr *expr,
const struct lysc_prefix *prefixes, uint32_t options, struct ly_set **set)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_set xp_set = {0};
uint32_t i;
LY_CHECK_ARG_RET(NULL, cur_mod, expr, prefixes, set, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, ctx_node ? ctx_node->module->ctx : NULL, cur_mod->ctx, LY_EINVAL);
if (!(options & LYXP_SCNODE_ALL)) {
options = LYXP_SCNODE;
}
ret = ly_set_new(set);
LY_CHECK_GOTO(ret, cleanup);
ret = lyxp_atomize(cur_mod->ctx, expr, cur_mod, LY_VALUE_SCHEMA_RESOLVED, (void *)prefixes, ctx_node, ctx_node,
&xp_set, options);
LY_CHECK_GOTO(ret, cleanup);
(*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(cur_mod->ctx); ret = LY_EMEM, cleanup);
(*set)->size = xp_set.used;
for (i = 0; i < xp_set.used; ++i) {
if ((xp_set.val.scnodes[i].type == LYXP_NODE_ELEM) && (xp_set.val.scnodes[i].in_ctx >= LYXP_SET_SCNODE_ATOM_NODE)) {
assert((xp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_NODE) ||
(xp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL) ||
(xp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX));
ret = ly_set_add(*set, xp_set.val.scnodes[i].scnode, 1, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
}
cleanup:
lyxp_set_free_content(&xp_set);
if (ret) {
ly_set_free(*set, NULL);
*set = NULL;
}
return ret;
}
LIBYANG_API_DEF LY_ERR
lys_find_xpath(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *xpath, uint32_t options,
struct ly_set **set)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_set xp_set = {0};
struct lyxp_expr *exp = NULL;
uint32_t i;
LY_CHECK_ARG_RET(NULL, ctx || ctx_node, xpath, set, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, ctx, ctx_node ? ctx_node->module->ctx : NULL, LY_EINVAL);
if (!(options & LYXP_SCNODE_ALL)) {
options |= LYXP_SCNODE;
}
if (!ctx) {
ctx = ctx_node->module->ctx;
}
ret = ly_set_new(set);
LY_CHECK_GOTO(ret, cleanup);
ret = lyxp_expr_parse(ctx, NULL, xpath, 0, 1, &exp);
LY_CHECK_GOTO(ret, cleanup);
ret = lyxp_atomize(ctx, exp, NULL, LY_VALUE_JSON, NULL, ctx_node, ctx_node, &xp_set, options);
LY_CHECK_GOTO(ret, cleanup);
(*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(ctx); ret = LY_EMEM, cleanup);
(*set)->size = xp_set.used;
for (i = 0; i < xp_set.used; ++i) {
if ((xp_set.val.scnodes[i].type == LYXP_NODE_ELEM) && (xp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_CTX)) {
ret = ly_set_add(*set, xp_set.val.scnodes[i].scnode, 1, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
}
cleanup:
lyxp_set_free_content(&xp_set);
lyxp_expr_free(exp);
if (ret) {
ly_set_free(*set, NULL);
*set = NULL;
}
return ret;
}
LIBYANG_API_DEF LY_ERR
lys_find_lypath_atoms(const struct ly_path *path, struct ly_set **set)
{
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u, v;
LY_CHECK_ARG_RET(NULL, path, set, LY_EINVAL);
LY_CHECK_RET(ly_set_new(set));
LY_ARRAY_FOR(path, u) {
LY_CHECK_GOTO(ret = ly_set_add(*set, (void *)path[u].node, 0, NULL), cleanup);
LY_ARRAY_FOR(path[u].predicates, v) {
if ((path[u].predicates[v].type == LY_PATH_PREDTYPE_LIST) || (path[u].predicates[v].type == LY_PATH_PREDTYPE_LIST_VAR)) {
LY_CHECK_GOTO(ret = ly_set_add(*set, (void *)path[u].predicates[v].key, 0, NULL), cleanup);
}
}
}
cleanup:
if (ret) {
ly_set_free(*set, NULL);
*set = NULL;
}
return ret;
}
LIBYANG_API_DEF LY_ERR
lys_find_path_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *path, ly_bool output,
struct ly_set **set)
{
LY_ERR ret = LY_SUCCESS;
uint8_t oper;
struct lyxp_expr *expr = NULL;
struct ly_path *p = NULL;
LY_CHECK_ARG_RET(ctx, ctx || ctx_node, path, set, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(__func__, ctx, ctx_node ? ctx_node->module->ctx : NULL, LY_EINVAL);
if (!ctx) {
ctx = ctx_node->module->ctx;
}
ret = ly_path_parse(ctx, ctx_node, path, 0, 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST,
LY_PATH_PRED_SIMPLE, &expr);
LY_CHECK_GOTO(ret, cleanup);
oper = output ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
ret = ly_path_compile(ctx, ctx_node, expr, oper, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p);
LY_CHECK_GOTO(ret, cleanup);
ret = lys_find_lypath_atoms(p, set);
cleanup:
ly_path_free(p);
lyxp_expr_free(expr);
return ret;
}
LIBYANG_API_DEF const struct lysc_node *
lys_find_path(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *path, ly_bool output)
{
const struct lysc_node *snode = NULL;
struct lyxp_expr *expr = NULL;
struct ly_path *p = NULL;
LY_ERR ret;
uint8_t oper;
LY_CHECK_ARG_RET(ctx, ctx || ctx_node, path, NULL);
LY_CHECK_CTX_EQUAL_RET(__func__, ctx, ctx_node ? ctx_node->module->ctx : NULL, NULL);
if (!ctx) {
ctx = ctx_node->module->ctx;
}
ret = ly_path_parse(ctx, ctx_node, path, 0, 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST,
LY_PATH_PRED_SIMPLE, &expr);
LY_CHECK_GOTO(ret, cleanup);
oper = output ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
ret = ly_path_compile(ctx, ctx_node, expr, oper, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p);
LY_CHECK_GOTO(ret, cleanup);
snode = p[LY_ARRAY_COUNT(p) - 1].node;
cleanup:
ly_path_free(p);
lyxp_expr_free(expr);
return snode;
}
char *
lysc_path_until(const struct lysc_node *node, const struct lysc_node *parent, LYSC_PATH_TYPE pathtype, char *buffer,
size_t buflen)
{
const struct lysc_node *iter, *par, *key;
char *path = NULL;
int len = 0;
ly_bool skip_schema;
if (buffer) {
LY_CHECK_ARG_RET(node->module->ctx, buflen > 1, NULL);
buffer[0] = '\0';
}
if ((pathtype == LYSC_PATH_DATA) || (pathtype == LYSC_PATH_DATA_PATTERN)) {
skip_schema = 1;
} else {
skip_schema = 0;
}
for (iter = node; iter && (iter != parent) && (len >= 0); iter = iter->parent) {
char *s;
const char *slash;
if (skip_schema && (iter->nodetype & (LYS_CHOICE | LYS_CASE | LYS_INPUT | LYS_OUTPUT))) {
continue;
}
if ((pathtype == LYSC_PATH_DATA_PATTERN) && (iter->nodetype == LYS_LIST)) {
char *predicates = NULL;
key = NULL;
while ((key = lys_getnext(key, iter, NULL, 0)) && lysc_is_key(key)) {
s = predicates;
if (asprintf(&predicates, "%s[%s='%%s']", s ? s : "", key->name) == -1) {
free(s);
free(path);
return NULL;
}
free(s);
}
s = buffer ? strdup(buffer) : path;
if (buffer) {
len = snprintf(buffer, buflen, "%s%s", predicates ? predicates : "", s ? s : "");
} else {
len = asprintf(&path, "%s%s", predicates ? predicates : "", s ? s : "");
}
free(predicates);
free(s);
if (buffer && (buflen <= (size_t)len)) {
break;
}
}
s = buffer ? strdup(buffer) : path;
if (parent && (iter->parent == parent)) {
slash = "";
} else {
slash = "/";
}
if (skip_schema) {
par = lysc_data_parent(iter);
} else {
par = iter->parent;
}
if (!par || (par->module != iter->module)) {
if (buffer) {
len = snprintf(buffer, buflen, "%s%s:%s%s", slash, iter->module->name, iter->name, s ? s : "");
} else {
len = asprintf(&path, "%s%s:%s%s", slash, iter->module->name, iter->name, s ? s : "");
}
} else {
if (buffer) {
len = snprintf(buffer, buflen, "%s%s%s", slash, iter->name, s ? s : "");
} else {
len = asprintf(&path, "%s%s%s", slash, iter->name, s ? s : "");
}
}
free(s);
if (buffer && (buflen <= (size_t)len)) {
break;
}
}
if (len < 0) {
free(path);
path = NULL;
} else if (len == 0) {
if (buffer) {
strcpy(buffer, "/");
} else {
free(path);
path = strdup("/");
}
}
if (buffer) {
return buffer;
} else {
return path;
}
}
LIBYANG_API_DEF char *
lysc_path(const struct lysc_node *node, LYSC_PATH_TYPE pathtype, char *buffer, size_t buflen)
{
return lysc_path_until(node, NULL, pathtype, buffer, buflen);
}
LY_ERR
_lys_set_implemented(struct lys_module *mod, const char **features, struct lys_glob_unres *unres)
{
LY_ERR ret = LY_SUCCESS, r;
struct lys_module *mod_iter;
const char **imp_f, *all_f[] = {"*", NULL};
uint32_t i;
if (mod->implemented) {
r = lys_set_features(mod->parsed, features);
if (r == LY_EEXIST) {
return LY_SUCCESS;
} else if (!r) {
mod->to_compile = 1;
}
return r;
}
r = lys_implement(mod, features, unres);
LY_CHECK_ERR_GOTO(r && (r != LY_ERECOMPILE), ret = r, cleanup);
if (mod->ctx->opts & LY_CTX_ALL_IMPLEMENTED) {
for (i = 0; i < unres->creating.count; ++i) {
mod = unres->creating.objs[i];
if (mod->implemented) {
continue;
}
imp_f = (mod->ctx->opts & LY_CTX_ENABLE_IMP_FEATURES) ? all_f : NULL;
r = lys_implement(mod, imp_f, unres);
LY_CHECK_ERR_GOTO(r && (r != LY_ERECOMPILE), ret = r, cleanup);
}
}
i = 0;
while ((mod_iter = ly_ctx_get_module_iter(mod->ctx, &i))) {
if (!strcmp(mod_iter->name, mod->name) && (mod_iter != mod) && (mod_iter->latest_revision & LYS_MOD_IMPORTED_REV)) {
LOGVRB("Implemented module \"%s@%s\" was not and will not be imported if the revision-date is missing"
" in the import statement. Instead, the revision \"%s\" is imported.", mod->name, mod->revision,
mod_iter->revision);
break;
}
}
cleanup:
return ret;
}
static LY_ERR
lys_unres_dep_sets_create_mod_r(struct lys_module *mod, struct ly_set *ctx_set, struct ly_set *dep_set,
struct ly_set *aux_set)
{
struct lys_module *mod2;
struct lysp_import *imports;
uint32_t i;
LY_ARRAY_COUNT_TYPE u, v;
ly_bool found;
if (LYS_IS_SINGLE_DEP_SET(mod)) {
if (!lys_has_dep_mods(mod)) {
return LY_SUCCESS;
}
if (ly_set_contains(aux_set, mod, NULL)) {
return LY_SUCCESS;
}
LY_CHECK_RET(ly_set_add(aux_set, mod, 1, NULL));
} else {
if (!ly_set_contains(ctx_set, mod, &i)) {
return LY_SUCCESS;
}
ly_set_rm_index(ctx_set, i, NULL);
LY_CHECK_RET(ly_set_add(dep_set, mod, 1, NULL));
}
imports = mod->parsed->imports;
LY_ARRAY_FOR(imports, u) {
mod2 = imports[u].module;
LY_CHECK_RET(lys_unres_dep_sets_create_mod_r(mod2, ctx_set, dep_set, aux_set));
}
LY_ARRAY_FOR(mod->parsed->includes, v) {
imports = mod->parsed->includes[v].submodule->imports;
LY_ARRAY_FOR(imports, u) {
mod2 = imports[u].module;
if (LYS_IS_SINGLE_DEP_SET(mod2) && !lys_has_dep_mods(mod2)) {
continue;
}
LY_CHECK_RET(lys_unres_dep_sets_create_mod_r(imports[u].module, ctx_set, dep_set, aux_set));
}
}
for (i = 0; i < mod->ctx->modules.count; ++i) {
mod2 = mod->ctx->modules.objs[i];
found = 0;
imports = mod2->parsed->imports;
LY_ARRAY_FOR(imports, u) {
if (imports[u].module == mod) {
found = 1;
break;
}
}
if (!found) {
LY_ARRAY_FOR(mod2->parsed->includes, v) {
imports = mod2->parsed->includes[v].submodule->imports;
LY_ARRAY_FOR(imports, u) {
if (imports[u].module == mod) {
found = 1;
break;
}
}
if (found) {
break;
}
}
}
if (found) {
LY_CHECK_RET(lys_unres_dep_sets_create_mod_r(mod2, ctx_set, dep_set, aux_set));
}
}
return LY_SUCCESS;
}
static LY_ERR
lys_unres_dep_sets_create_single(struct ly_set *ctx_set, struct ly_set *main_set)
{
LY_ERR ret = LY_SUCCESS;
struct lys_module *m;
uint32_t i = 0;
struct ly_set *dep_set = NULL;
while (i < ctx_set->count) {
m = ctx_set->objs[i];
if (LYS_IS_SINGLE_DEP_SET(m)) {
ly_set_rm_index(ctx_set, i, NULL);
LY_CHECK_GOTO(ret = ly_set_new(&dep_set), cleanup);
LY_CHECK_GOTO(ret = ly_set_add(dep_set, m, 1, NULL), cleanup);
LY_CHECK_GOTO(ret = ly_set_add(main_set, dep_set, 1, NULL), cleanup);
dep_set = NULL;
} else {
++i;
}
}
cleanup:
ly_set_free(dep_set, NULL);
return ret;
}
LY_ERR
lys_unres_dep_sets_create(struct ly_ctx *ctx, struct ly_set *main_set, struct lys_module *mod)
{
LY_ERR ret = LY_SUCCESS;
struct lys_module *m;
struct ly_set *dep_set = NULL, *ctx_set = NULL, aux_set = {0};
uint32_t i;
ly_bool found;
assert(!main_set->count);
LY_CHECK_GOTO(ret = ly_set_dup(&ctx->modules, NULL, &ctx_set), cleanup);
LY_CHECK_GOTO(ret = lys_unres_dep_sets_create_single(ctx_set, main_set), cleanup);
if (mod && !ly_set_contains(ctx_set, mod, NULL)) {
goto cleanup;
}
while (ctx_set->count) {
LY_CHECK_GOTO(ret = ly_set_new(&dep_set), cleanup);
if (mod) {
LY_CHECK_GOTO(ret = lys_unres_dep_sets_create_mod_r(mod, ctx_set, dep_set, &aux_set), cleanup);
} else {
LY_CHECK_GOTO(ret = lys_unres_dep_sets_create_mod_r(ctx_set->objs[0], ctx_set, dep_set, &aux_set), cleanup);
}
ly_set_erase(&aux_set, NULL);
assert(dep_set->count);
found = 0;
for (i = 0; i < dep_set->count; ++i) {
m = dep_set->objs[i];
if (m->to_compile) {
found = 1;
break;
}
}
if (found) {
for (i = 0; i < dep_set->count; ++i) {
m = dep_set->objs[i];
if (m->implemented) {
m->to_compile = 1;
}
}
}
LY_CHECK_GOTO(ret = ly_set_add(main_set, dep_set, 1, NULL), cleanup);
dep_set = NULL;
if (mod) {
break;
}
}
#ifndef NDEBUG
LOGDBG(LY_LDGDEPSETS, "dep sets created (%" PRIu32 "):", main_set->count);
for (i = 0; i < main_set->count; ++i) {
struct ly_set *iter_set = main_set->objs[i];
LOGDBG(LY_LDGDEPSETS, "dep set #%" PRIu32 ":", i);
for (uint32_t j = 0; j < iter_set->count; ++j) {
m = iter_set->objs[j];
LOGDBG(LY_LDGDEPSETS, "\t%s", m->name);
}
}
#endif
cleanup:
assert(ret || main_set->objs);
ly_set_erase(&aux_set, NULL);
ly_set_free(dep_set, NULL);
ly_set_free(ctx_set, NULL);
return ret;
}
void
lys_unres_glob_revert(struct ly_ctx *ctx, struct lys_glob_unres *unres)
{
uint32_t i, j, idx, *prev_lo, temp_lo = 0;
struct lys_module *mod;
struct ly_set *dep_set;
LY_ERR ret;
for (i = 0; i < unres->implementing.count; ++i) {
mod = unres->implementing.objs[i];
assert(mod->implemented);
mod->implemented = 0;
lys_precompile_augments_deviations_revert(ctx, mod);
lysc_module_free(ctx, mod->compiled);
mod->compiled = NULL;
mod->to_compile = 0;
}
for (i = 0; i < unres->creating.count; ++i) {
mod = unres->creating.objs[i];
ly_set_rm(&ctx->modules, mod, NULL);
for (j = 0; j < unres->dep_sets.count; ++j) {
dep_set = unres->dep_sets.objs[j];
if (ly_set_contains(dep_set, mod, &idx)) {
ly_set_rm_index(dep_set, idx, NULL);
break;
}
}
lys_module_free(ctx, mod, 1);
}
if (unres->implementing.count) {
prev_lo = ly_temp_log_options(&temp_lo);
ret = lys_compile_depset_all(ctx, &ctx->unres);
ly_temp_log_options(prev_lo);
if (ret) {
LOGINT(ctx);
}
}
}
void
lys_unres_glob_erase(struct lys_glob_unres *unres)
{
uint32_t i;
for (i = 0; i < unres->dep_sets.count; ++i) {
ly_set_free(unres->dep_sets.objs[i], NULL);
}
ly_set_erase(&unres->dep_sets, NULL);
ly_set_erase(&unres->implementing, NULL);
ly_set_erase(&unres->creating, NULL);
assert(!unres->ds_unres.whens.count);
assert(!unres->ds_unres.musts.count);
assert(!unres->ds_unres.leafrefs.count);
assert(!unres->ds_unres.disabled_leafrefs.count);
assert(!unres->ds_unres.dflts.count);
assert(!unres->ds_unres.disabled.count);
}
LIBYANG_API_DEF LY_ERR
lys_set_implemented(struct lys_module *mod, const char **features)
{
LY_ERR ret = LY_SUCCESS;
struct lys_glob_unres *unres = &mod->ctx->unres;
LY_CHECK_ARG_RET(NULL, mod, mod->parsed, !(mod->ctx->opts & LY_CTX_INT_IMMUTABLE), LY_EINVAL);
ret = _lys_set_implemented(mod, features, unres);
LY_CHECK_GOTO(ret, cleanup);
if (!(mod->ctx->opts & LY_CTX_EXPLICIT_COMPILE)) {
LY_CHECK_GOTO(ret = lys_unres_dep_sets_create(mod->ctx, &unres->dep_sets, mod), cleanup);
LY_CHECK_GOTO(ret = lys_compile_depset_all(mod->ctx, unres), cleanup);
lys_unres_glob_erase(unres);
ly_ctx_new_change(mod->ctx);
}
cleanup:
if (ret) {
lys_unres_glob_revert(mod->ctx, unres);
lys_unres_glob_erase(unres);
}
return ret;
}
static LY_ERR
lysp_resolve_import_include(struct lysp_ctx *pctx, struct lysp_module *pmod, struct ly_set *new_mods)
{
struct lysp_import *imp;
LY_ARRAY_COUNT_TYPE u, v;
pmod->parsing = 1;
LY_ARRAY_FOR(pmod->imports, u) {
imp = &pmod->imports[u];
if (!imp->module) {
LY_CHECK_RET(lys_parse_load(PARSER_CTX(pctx), imp->name, imp->rev[0] ? imp->rev : NULL, new_mods, &imp->module));
if (!imp->rev[0]) {
imp->module->latest_revision |= LYS_MOD_IMPORTED_REV;
}
}
for (v = 0; v < u; ++v) {
if (imp->module == pmod->imports[v].module) {
LOGWRN(PARSER_CTX(pctx), "Single revision of the module \"%s\" imported twice.", imp->name);
}
}
}
LY_CHECK_RET(lysp_load_submodules(pctx, pmod, new_mods));
pmod->parsing = 0;
return LY_SUCCESS;
}
static char *
lysp_path_until(const struct lysp_node *node, const struct lysp_node *parent, const struct lysp_module *pmod)
{
const struct lysp_node *iter;
char *path = NULL, *s;
const char *slash, *mod_colon, *mod_prefix, *node_start, *node_end;
int len = 0;
for (iter = node; iter && (iter != parent) && (len >= 0); iter = iter->parent) {
if (parent && (iter->parent == parent)) {
slash = "";
} else {
slash = "/";
}
if (iter->parent) {
mod_colon = "";
mod_prefix = "";
} else {
mod_colon = ":";
mod_prefix = pmod->mod->name;
}
switch (iter->nodetype) {
case LYS_CONTAINER:
case LYS_CHOICE:
case LYS_LEAF:
case LYS_LEAFLIST:
case LYS_LIST:
case LYS_ANYXML:
case LYS_ANYDATA:
case LYS_CASE:
case LYS_RPC:
case LYS_ACTION:
case LYS_NOTIF:
case LYS_INPUT:
case LYS_OUTPUT:
node_start = "";
node_end = "";
break;
case LYS_USES:
node_start = "{uses='";
node_end = "'}";
break;
case LYS_GROUPING:
node_start = "{grouping='";
node_end = "'}";
break;
case LYS_AUGMENT:
node_start = "{augment='";
node_end = "'}";
break;
default:
len = -1;
goto cleanup;
}
s = path;
len = asprintf(&path, "%s%s%s%s%s%s%s", slash, mod_prefix, mod_colon, node_start, iter->name, node_end, s ? s : "");
free(s);
}
if (len == 0) {
path = strdup("/");
}
cleanup:
if (len < 0) {
free(path);
path = NULL;
}
return path;
}
static LY_ERR
lysp_ext_instance_path_append(char **buf, uint32_t *size, const char *format, ...)
{
va_list ap;
uint32_t len;
va_start(ap, format);
len = vsnprintf(*buf ? *buf + *size : NULL, 0, format, ap);
va_end(ap);
*buf = ly_realloc(*buf, (*size) + len + 1);
LY_CHECK_ERR_RET(!*buf, LOGMEM(NULL), LY_EMEM);
va_start(ap, format);
*size += vsnprintf(*buf + *size, len + 1, format, ap);
va_end(ap);
return LY_SUCCESS;
}
static LY_ERR
lysp_ext_instance_path_stmt_append_r(const struct ly_ctx *ctx, enum ly_stmt stmt, LY_ARRAY_COUNT_TYPE stmt_idx,
const void *stmt_p, const struct lysp_module *pmod, char **buf, uint32_t *size)
{
if (*size && ((*buf)[*size - 1] != ':')) {
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "/"));
}
switch (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_AUGMENT:
case LY_STMT_CASE:
case LY_STMT_CHOICE:
case LY_STMT_CONTAINER:
case LY_STMT_GROUPING:
case LY_STMT_LEAF:
case LY_STMT_LEAF_LIST:
case LY_STMT_LIST:
case LY_STMT_USES:
*buf = lysp_path_until(stmt_p, NULL, pmod);
LY_CHECK_ERR_RET(!buf, LOGMEM(ctx), LY_EMEM);
*size = strlen(*buf);
break;
case LY_STMT_ARGUMENT:
case LY_STMT_YIN_ELEMENT:
LY_CHECK_RET(lysp_ext_instance_path_stmt_append_r(ctx, LY_STMT_EXTENSION, 0, stmt_p, pmod, buf, size));
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "/{%s}", lyplg_ext_stmt2str(stmt)));
break;
case LY_STMT_BASE: {
const char * const *bases = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), bases[stmt_idx]));
break;
}
case LY_STMT_BELONGS_TO: {
const struct lysp_submodule *submod = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), submod->name));
break;
}
case LY_STMT_BIT:
case LY_STMT_ENUM: {
const struct lysp_type_enum *be = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), be->name));
break;
}
case LY_STMT_CONFIG:
case LY_STMT_CONTACT:
case LY_STMT_DESCRIPTION:
case LY_STMT_MANDATORY:
case LY_STMT_MAX_ELEMENTS:
case LY_STMT_MIN_ELEMENTS:
case LY_STMT_NAMESPACE:
case LY_STMT_ORGANIZATION:
case LY_STMT_PREFIX:
case LY_STMT_YANG_VERSION:
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s}", lyplg_ext_stmt2str(stmt)));
break;
case LY_STMT_DEFAULT: {
const struct lysp_qname *qn = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), qn->str));
break;
}
case LY_STMT_DEVIATE: {
const struct lysp_deviate *d = stmt_p;
switch (d->mod) {
case LYS_DEV_NOT_SUPPORTED:
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='not-supported'}", lyplg_ext_stmt2str(stmt)));
break;
case LYS_DEV_ADD:
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='add'}", lyplg_ext_stmt2str(stmt)));
break;
case LYS_DEV_DELETE:
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='delete'}", lyplg_ext_stmt2str(stmt)));
break;
case LYS_DEV_REPLACE:
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='replace'}", lyplg_ext_stmt2str(stmt)));
break;
}
break;
}
case LY_STMT_DEVIATION: {
const struct lysp_deviation *dev = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), dev->nodeid));
break;
}
case LY_STMT_ERROR_APP_TAG:
case LY_STMT_ERROR_MESSAGE:
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{restriction}/{%s}", lyplg_ext_stmt2str(stmt)));
break;
case LY_STMT_EXTENSION: {
const struct lysp_ext *ext = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), ext->name));
break;
}
case LY_STMT_EXTENSION_INSTANCE: {
const struct lysp_ext_instance *ext = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{ext-inst='%s'}", ext->name));
if (ext->argument) {
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "/%s", ext->argument));
}
break;
}
case LY_STMT_FEATURE: {
const struct lysp_feature *f = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), f->name));
break;
}
case LY_STMT_FRACTION_DIGITS:
case LY_STMT_PATH:
case LY_STMT_REQUIRE_INSTANCE:
LY_CHECK_RET(lysp_ext_instance_path_stmt_append_r(ctx, LY_STMT_TYPE, 0, stmt_p, pmod, buf, size));
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "/{%s}", lyplg_ext_stmt2str(stmt)));
break;
case LY_STMT_IDENTITY: {
const struct lysp_ident *id = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), id->name));
break;
}
case LY_STMT_IF_FEATURE:
case LY_STMT_UNIQUE: {
const struct lysp_qname *qns = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), qns[stmt_idx].str));
break;
}
case LY_STMT_IMPORT: {
const struct lysp_import *imp = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), imp->name));
break;
}
case LY_STMT_INCLUDE: {
const struct lysp_include *inc = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), inc->name));
break;
}
case LY_STMT_KEY:
case LY_STMT_ORDERED_BY:
LY_CHECK_RET(lysp_ext_instance_path_stmt_append_r(ctx, LY_STMT_LIST, 0, stmt_p, pmod, buf, size));
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "/{%s}", lyplg_ext_stmt2str(stmt)));
break;
case LY_STMT_LENGTH:
case LY_STMT_MUST:
case LY_STMT_RANGE: {
const struct lysp_restr *res = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), res->arg.str));
break;
}
case LY_STMT_MODIFIER:
LY_CHECK_RET(lysp_ext_instance_path_stmt_append_r(ctx, LY_STMT_PATTERN, 0, stmt_p, pmod, buf, size));
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "/{%s}", lyplg_ext_stmt2str(stmt)));
break;
case LY_STMT_PATTERN: {
const struct lysp_restr *res = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), res->arg.str + 1));
break;
}
case LY_STMT_POSITION:
LY_CHECK_RET(lysp_ext_instance_path_stmt_append_r(ctx, LY_STMT_BIT, 0, stmt_p, pmod, buf, size));
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "/{%s}", lyplg_ext_stmt2str(stmt)));
break;
case LY_STMT_PRESENCE:
case LY_STMT_REFERENCE:
case LY_STMT_REVISION_DATE:
case LY_STMT_UNITS: {
const char *str = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), str));
break;
}
case LY_STMT_REFINE: {
const struct lysp_refine *rf = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), rf->nodeid));
break;
}
case LY_STMT_REVISION: {
const struct lysp_revision *rev = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), rev->date));
break;
}
case LY_STMT_STATUS: {
const uint16_t *flags = stmt_p;
if (*flags & LYS_STATUS_OBSLT) {
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='obsolete'}", lyplg_ext_stmt2str(stmt)));
} else if (*flags & LYS_STATUS_DEPRC) {
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='deprecated'}", lyplg_ext_stmt2str(stmt)));
} else {
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='current'}", lyplg_ext_stmt2str(stmt)));
}
break;
}
case LY_STMT_TYPE: {
const struct lysp_type *typ = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), typ->name));
break;
}
case LY_STMT_TYPEDEF: {
const struct lysp_tpdf *tpdf = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), tpdf->name));
break;
}
case LY_STMT_VALUE:
LY_CHECK_RET(lysp_ext_instance_path_stmt_append_r(ctx, LY_STMT_ENUM, 0, stmt_p, pmod, buf, size));
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "/{%s}", lyplg_ext_stmt2str(stmt)));
break;
case LY_STMT_WHEN: {
const struct lysp_when *wh = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}", lyplg_ext_stmt2str(stmt), wh->cond));
break;
}
case LY_STMT_NONE:
case LY_STMT_MODULE:
case LY_STMT_SUBMODULE:
case LY_STMT_SYNTAX_SEMICOLON:
case LY_STMT_SYNTAX_LEFT_BRACE:
case LY_STMT_SYNTAX_RIGHT_BRACE:
case LY_STMT_ARG_TEXT:
case LY_STMT_ARG_VALUE:
LOGINT_RET(ctx);
}
return LY_SUCCESS;
}
LY_ERR
lysp_ext_instance_path(const struct ly_ctx *ctx, const struct lysp_module *pmod, const struct lysp_ext_instance *ext,
char **path)
{
char *buf = NULL;
uint32_t size = 0;
if (ext->parent_stmt == LY_STMT_MODULE) {
LY_CHECK_RET(lysp_ext_instance_path_append(&buf, &size, "/%s:", ((struct lysp_module *)ext->parent)->mod->name));
} else if (ext->parent_stmt == LY_STMT_SUBMODULE) {
LY_CHECK_RET(lysp_ext_instance_path_append(&buf, &size, "/%s:", ((struct lysp_submodule *)ext->parent)->name));
} else {
if (!(ext->parent_stmt & LY_STMT_NODE_MASK)) {
LY_CHECK_RET(lysp_ext_instance_path_append(&buf, &size, "/%s:", pmod->mod->name));
}
LY_CHECK_RET(lysp_ext_instance_path_stmt_append_r(ctx, ext->parent_stmt, ext->parent_stmt_index, ext->parent, pmod,
&buf, &size));
}
LY_CHECK_RET(lysp_ext_instance_path_stmt_append_r(ctx, LY_STMT_EXTENSION_INSTANCE, 0, ext, pmod, &buf, &size));
*path = buf;
return LY_SUCCESS;
}
static void
lysp_resolve_ext_instance_plugins(struct lys_module *mod)
{
LY_ARRAY_COUNT_TYPE u, v;
const struct lysp_include *inc;
LY_ARRAY_FOR(mod->parsed->extensions, u) {
mod->parsed->extensions[u].plugin_ref = lyplg_ext_plugin_find(mod->ctx, mod->name,
mod->revision, mod->parsed->extensions[u].name);
}
LY_ARRAY_FOR(mod->parsed->includes, v) {
inc = &mod->parsed->includes[v];
LY_ARRAY_FOR(inc->submodule->extensions, u) {
inc->submodule->extensions[u].plugin_ref = lyplg_ext_plugin_find(mod->ctx, mod->name,
mod->revision, inc->submodule->extensions[u].name);
}
}
}
static LY_ERR
lysp_resolve_ext_instance_records(struct lysp_ctx *pctx)
{
LY_ERR r;
struct lysp_ext_instance *ext, *exts;
struct lysp_ext *ext_def;
const struct lys_module *mod;
uint32_t i;
LY_ARRAY_COUNT_TYPE u;
char *path = NULL;
struct lyplg_ext *ext_plg;
for (i = 0; i < pctx->ext_inst.count; ++i) {
exts = pctx->ext_inst.objs[i];
LY_ARRAY_FOR(exts, u) {
ext = &exts[u];
LY_CHECK_RET(lysp_ext_find_definition(PARSER_CTX(pctx), PARSER_CUR_PMOD(pctx), ext, &mod, &ext_def));
ext->plugin_ref = ext_def->plugin_ref;
LY_CHECK_RET(lysp_ext_instance_resolve_argument(PARSER_CTX(pctx), ext_def, ext));
}
}
for (i = 0; i < pctx->ext_inst.count; ++i) {
exts = pctx->ext_inst.objs[i];
u = 0;
while (u < LY_ARRAY_COUNT(exts)) {
ext = &exts[u];
if (!ext->plugin_ref || !(ext_plg = LYSC_GET_EXT_PLG(ext->plugin_ref))->parse) {
goto next_iter;
}
LY_CHECK_RET(lysp_ext_instance_path(PARSER_CTX(pctx), PARSER_CUR_PMOD(pctx), ext, &path));
ly_log_location(NULL, path, NULL);
r = ext_plg->parse(pctx, ext);
ly_log_location_revert(0, 1, 0);
free(path);
if (r == LY_ENOT) {
lysp_ext_instance_free(PARSER_CTX(pctx), ext);
LY_ARRAY_DECREMENT(exts);
if (u < LY_ARRAY_COUNT(exts)) {
*ext = exts[LY_ARRAY_COUNT(exts)];
}
continue;
} else if (r) {
return r;
}
next_iter:
++u;
}
}
return LY_SUCCESS;
}
static void
ly_check_module_filename(const struct ly_ctx *ctx, const char *name, const char *revision, const char *filename)
{
const char *basename, *rev, *dot;
size_t len;
basename = strrchr(filename, '/');
#ifdef _WIN32
const char *backslash = strrchr(filename, '\\');
if (!basename || (basename && backslash && (backslash > basename))) {
basename = backslash;
}
#endif
if (!basename) {
basename = filename;
} else {
basename++;
}
rev = strchr(basename, '@');
dot = strrchr(basename, '.');
len = strlen(name);
if (strncmp(basename, name, len) ||
((rev && (rev != &basename[len])) || (!rev && (dot != &basename[len])))) {
LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", basename, name);
}
if (rev) {
len = dot - ++rev;
if (!revision || (len != LY_REV_SIZE - 1) || strncmp(revision, rev, len)) {
LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", basename,
revision ? revision : "none");
}
}
}
static LY_ERR
lysp_load_module_data_check(const struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod,
const struct lysp_load_module_data *mod_data)
{
const char *name;
uint8_t latest_revision;
struct lysp_revision *revs;
name = mod ? mod->mod->name : submod->name;
revs = mod ? mod->revs : submod->revs;
latest_revision = mod ? mod->mod->latest_revision : submod->latest_revision;
if (mod_data->name) {
if (strcmp(mod_data->name, name)) {
LOGERR(ctx, LY_EINVAL, "Unexpected module \"%s\" parsed instead of \"%s\".", name, mod_data->name);
return LY_EINVAL;
}
}
if (mod_data->revision) {
if (!revs || strcmp(mod_data->revision, revs[0].date)) {
LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").", name,
revs ? revs[0].date : "none", mod_data->revision);
return LY_EINVAL;
}
} else if (!latest_revision) {
return LY_EEXIST;
}
if (submod) {
assert(mod_data->submoduleof);
if (strcmp(mod_data->submoduleof, submod->mod->name)) {
LOGVAL(ctx, NULL, LYVE_REFERENCE, "Included \"%s\" submodule from \"%s\" belongs-to a different module \"%s\".",
submod->name, mod_data->submoduleof, submod->mod->name);
return LY_EVALID;
}
if (submod->parsing) {
LOGVAL(ctx, NULL, LYVE_REFERENCE, "A circular dependency (include) for module \"%s\".", submod->name);
return LY_EVALID;
}
}
if (mod_data->path) {
ly_check_module_filename(ctx, name, revs ? revs[0].date : NULL, mod_data->path);
}
return LY_SUCCESS;
}
LY_ERR
lys_parse_submodule(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, struct lysp_ctx *main_ctx,
const struct lysp_load_module_data *mod_data, ly_bool in_searchdirs, struct ly_set *new_mods,
struct lysp_submodule **submodule)
{
LY_ERR rc = LY_SUCCESS, r;
struct lysp_submodule *submod = NULL, *latest_sp;
struct lysp_yang_ctx *yangctx = NULL;
struct lysp_yin_ctx *yinctx = NULL;
struct lysp_ctx *pctx = NULL;
LY_CHECK_ARG_RET(ctx, ctx, in, LY_EINVAL);
*submodule = NULL;
switch (format) {
case LYS_IN_YIN:
rc = yin_parse_submodule(&yinctx, ctx, main_ctx, in, &submod);
pctx = (struct lysp_ctx *)yinctx;
break;
case LYS_IN_YANG:
rc = yang_parse_submodule(&yangctx, ctx, main_ctx, in, &submod);
pctx = (struct lysp_ctx *)yangctx;
break;
default:
LOGERR(ctx, LY_EINVAL, "Invalid schema input format.");
rc = LY_EINVAL;
break;
}
LY_CHECK_GOTO(rc, cleanup);
assert(submod);
lysp_sort_revisions(submod->revs);
latest_sp = (struct lysp_submodule *)ly_ctx_get_submodule2_latest(submod->mod, submod->name);
if (latest_sp) {
if (submod->revs) {
if (!latest_sp->revs) {
submod->latest_revision = latest_sp->latest_revision;
} else if (strcmp(submod->revs[0].date, latest_sp->revs[0].date) > 0) {
submod->latest_revision = latest_sp->latest_revision;
} else {
latest_sp = NULL;
}
} else {
latest_sp = NULL;
}
} else {
submod->latest_revision = (in_searchdirs && !mod_data->revision) ? 2 : 1;
}
r = lysp_load_module_data_check(ctx, NULL, submod, mod_data);
if (r == LY_EEXIST) {
ly_set_erase(&pctx->tpdfs_nodes, NULL);
ly_set_erase(&pctx->grps_nodes, NULL);
ly_set_erase(&pctx->ext_inst, NULL);
lysp_module_free(ctx, (struct lysp_module *)submod);
submod = NULL;
goto cleanup;
} else if (r) {
rc = r;
goto cleanup;
}
if (latest_sp) {
latest_sp->latest_revision = 0;
}
LY_CHECK_GOTO(rc = lys_parser_fill_filepath(ctx, in, &submod->filepath), cleanup);
LY_CHECK_GOTO(rc = lysp_resolve_import_include(pctx, (struct lysp_module *)submod, new_mods), cleanup);
cleanup:
if (rc) {
LOGERR(ctx, rc, "Parsing submodule \"%s\" failed.", mod_data->name);
if (pctx) {
ly_set_erase(&pctx->tpdfs_nodes, NULL);
ly_set_erase(&pctx->grps_nodes, NULL);
ly_set_erase(&pctx->ext_inst, NULL);
}
lysp_module_free(ctx, (struct lysp_module *)submod);
} else if (submod) {
*submodule = submod;
ly_set_merge(&pctx->main_ctx->tpdfs_nodes, &pctx->tpdfs_nodes, 1, NULL);
ly_set_erase(&pctx->tpdfs_nodes, NULL);
ly_set_merge(&pctx->main_ctx->grps_nodes, &pctx->grps_nodes, 1, NULL);
ly_set_erase(&pctx->grps_nodes, NULL);
ly_set_merge(&pctx->main_ctx->ext_inst, &pctx->ext_inst, 1, NULL);
ly_set_erase(&pctx->ext_inst, NULL);
}
if (format == LYS_IN_YANG) {
lysp_yang_ctx_free(yangctx);
} else {
lysp_yin_ctx_free(yinctx);
}
return rc;
}
static LY_ERR
lysp_add_internal_ietf_netconf(struct lysp_ctx *pctx, struct lysp_module *mod)
{
struct lysp_ext_instance *extp, *prev_exts = mod->exts;
struct lysp_stmt *stmt;
struct lysp_node_leaf *leaf;
struct lysp_node_container *cont;
struct lysp_type_enum *enm;
struct lysp_import *imp;
uint32_t idx;
LY_ARRAY_NEW_RET(mod->mod->ctx, mod->exts, extp, LY_EMEM);
LY_CHECK_ERR_RET(!extp, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "md_:annotation", 0, &extp->name));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "operation", 0, &extp->argument));
extp->format = LY_VALUE_SCHEMA;
extp->prefix_data = mod;
extp->parent = mod;
extp->parent_stmt = LY_STMT_MODULE;
extp->flags = LYS_INTERNAL;
extp->child = stmt = calloc(1, sizeof *extp->child);
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "type", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enumeration", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_TYPE;
stmt->child = calloc(1, sizeof *stmt->child);
stmt = stmt->child;
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "merge", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_ENUM;
stmt->next = calloc(1, sizeof *stmt->child);
stmt = stmt->next;
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "replace", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_ENUM;
stmt->next = calloc(1, sizeof *stmt->child);
stmt = stmt->next;
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "create", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_ENUM;
stmt->next = calloc(1, sizeof *stmt->child);
stmt = stmt->next;
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "delete", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_ENUM;
stmt->next = calloc(1, sizeof *stmt->child);
stmt = stmt->next;
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "remove", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_ENUM;
LY_ARRAY_NEW_RET(mod->mod->ctx, mod->exts, extp, LY_EMEM);
LY_CHECK_ERR_RET(!extp, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "md_:annotation", 0, &extp->name));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "type", 0, &extp->argument));
extp->format = LY_VALUE_SCHEMA;
extp->prefix_data = mod;
extp->parent = mod;
extp->parent_stmt = LY_STMT_MODULE;
extp->flags = LYS_INTERNAL;
extp->child = stmt = calloc(1, sizeof *extp->child);
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "type", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enumeration", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_TYPE;
stmt->child = calloc(1, sizeof *stmt->child);
stmt = stmt->child;
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "subtree", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_ENUM;
stmt->next = calloc(1, sizeof *stmt->child);
stmt = stmt->next;
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enum", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "xpath", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_ENUM;
if (mod->version >= LYS_VERSION_1_1) {
stmt->child = calloc(1, sizeof *stmt->child);
stmt = stmt->child;
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "if-feature", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "xpath", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_IF_FEATURE;
}
LY_ARRAY_NEW_RET(mod->mod->ctx, mod->exts, extp, LY_EMEM);
LY_CHECK_ERR_RET(!extp, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "md_:annotation", 0, &extp->name));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "select", 0, &extp->argument));
extp->format = LY_VALUE_SCHEMA;
extp->prefix_data = mod;
extp->parent = mod;
extp->parent_stmt = LY_STMT_MODULE;
extp->flags = LYS_INTERNAL;
extp->child = stmt = calloc(1, sizeof *extp->child);
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "type", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "yang_:xpath1.0", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_TYPE;
if (!prev_exts) {
assert(pctx->main_ctx == pctx);
LY_CHECK_RET(ly_set_add(&pctx->ext_inst, mod->exts, 1, NULL));
} else {
if (!ly_set_contains(&pctx->ext_inst, prev_exts, &idx)) {
LOGINT_RET(mod->mod->ctx);
}
pctx->ext_inst.objs[idx] = mod->exts;
}
LY_LIST_NEW_RET(mod->mod->ctx, &mod->data, cont, next, LY_EMEM);
cont->nodetype = LYS_CONTAINER;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "rpc-error", 0, &cont->name));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "presence", 0, &cont->presence));
cont->flags = LYS_INTERNAL;
LY_LIST_NEW_RET(mod->mod->ctx, &cont->child, leaf, next, LY_EMEM);
leaf->nodetype = LYS_LEAF;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "error-type", 0, &leaf->name));
leaf->flags = LYS_INTERNAL;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enumeration", 0, &leaf->type.name));
leaf->type.pmod = mod;
leaf->type.flags = LYS_SET_ENUM;
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "transport", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "rpc", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "protocol", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "application", 0, &enm->name));
LY_LIST_NEW_RET(mod->mod->ctx, &cont->child, leaf, next, LY_EMEM);
leaf->nodetype = LYS_LEAF;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "error-tag", 0, &leaf->name));
leaf->flags = LYS_INTERNAL;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enumeration", 0, &leaf->type.name));
leaf->type.pmod = mod;
leaf->type.flags = LYS_SET_ENUM;
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "in-use", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "invalid-value", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "too-big", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "missing-attribute", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "bad-attribute", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "unknown-attribute", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "missing-element", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "bad-element", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "unknown-element", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "unknown-namespace", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "access-denied", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "lock-denied", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "resource-denied", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "rollback-failed", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "data-exists", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "data-missing", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "operation-not-supported", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "operation-failed", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "partial-operation", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "malformed-message", 0, &enm->name));
LY_LIST_NEW_RET(mod->mod->ctx, &cont->child, leaf, next, LY_EMEM);
leaf->nodetype = LYS_LEAF;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "error-severity", 0, &leaf->name));
leaf->flags = LYS_INTERNAL;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "enumeration", 0, &leaf->type.name));
leaf->type.pmod = mod;
leaf->type.flags = LYS_SET_ENUM;
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "error", 0, &enm->name));
LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "warning", 0, &enm->name));
LY_LIST_NEW_RET(mod->mod->ctx, &cont->child, leaf, next, LY_EMEM);
leaf->nodetype = LYS_LEAF;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "error-app-tag", 0, &leaf->name));
leaf->flags = LYS_INTERNAL;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "string", 0, &leaf->type.name));
leaf->type.pmod = mod;
LY_LIST_NEW_RET(mod->mod->ctx, &cont->child, leaf, next, LY_EMEM);
leaf->nodetype = LYS_LEAF;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "error-path", 0, &leaf->name));
leaf->flags = LYS_INTERNAL;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "yang_:xpath1.0", 0, &leaf->type.name));
leaf->type.pmod = mod;
LY_ARRAY_NEW_RET(mod->mod->ctx, mod->imports, imp, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "ietf-yang-metadata", 0, &imp->name));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "md_", 0, &imp->prefix));
imp->flags = LYS_INTERNAL;
LY_ARRAY_NEW_RET(mod->mod->ctx, mod->imports, imp, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "ietf-yang-types", 0, &imp->name));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "yang_", 0, &imp->prefix));
imp->flags = LYS_INTERNAL;
return LY_SUCCESS;
}
static LY_ERR
lysp_add_internal_ietf_netconf_with_defaults(struct lysp_ctx *pctx, struct lysp_module *mod)
{
struct lysp_ext_instance *extp, *prev_exts = mod->exts;
struct lysp_stmt *stmt;
struct lysp_import *imp;
uint32_t idx;
LY_ARRAY_NEW_RET(mod->mod->ctx, mod->exts, extp, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "md_:annotation", 0, &extp->name));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "default", 0, &extp->argument));
extp->format = LY_VALUE_SCHEMA;
extp->prefix_data = mod;
extp->parent = mod;
extp->parent_stmt = LY_STMT_MODULE;
extp->flags = LYS_INTERNAL;
extp->child = stmt = calloc(1, sizeof *extp->child);
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "type", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "boolean", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_TYPE;
if (!prev_exts) {
assert(pctx->main_ctx == pctx);
LY_CHECK_RET(ly_set_add(&pctx->ext_inst, mod->exts, 1, NULL));
} else {
if (!ly_set_contains(&pctx->ext_inst, prev_exts, &idx)) {
LOGINT_RET(mod->mod->ctx);
}
pctx->ext_inst.objs[idx] = mod->exts;
}
LY_ARRAY_NEW_RET(mod->mod->ctx, mod->imports, imp, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "ietf-yang-metadata", 0, &imp->name));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "md_", 0, &imp->prefix));
imp->flags = LYS_INTERNAL;
return LY_SUCCESS;
}
static LY_ERR
lysp_add_internal_yang(struct lysp_ctx *pctx, struct lysp_module *mod)
{
struct lysp_ext_instance *extp, *prev_exts = mod->exts;
struct lysp_stmt *stmt;
struct lysp_tpdf *tpdf;
struct lysp_node_leaf *leaf;
struct lysp_import *imp;
uint32_t idx;
LY_ARRAY_NEW_RET(PARSER_CTX(pctx), mod->typedefs, tpdf, LY_EMEM);
LY_CHECK_RET(lysdict_insert(PARSER_CTX(pctx), "lyds_tree", 0, &tpdf->name));
LY_CHECK_RET(lysdict_insert(PARSER_CTX(pctx), "uint64", 0, &tpdf->type.name));
tpdf->type.pmod = mod;
LY_ARRAY_NEW_RET(PARSER_CTX(pctx), mod->exts, extp, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "md:annotation", 0, &extp->name));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "lyds_tree", 0, &extp->argument));
extp->format = LY_VALUE_SCHEMA;
extp->prefix_data = mod;
extp->parent = mod;
extp->parent_stmt = LY_STMT_MODULE;
extp->flags = LYS_INTERNAL;
extp->child = stmt = calloc(1, sizeof *extp->child);
LY_CHECK_ERR_RET(!stmt, LOGMEM(mod->mod->ctx), LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "type", 0, &stmt->stmt));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "lyds_tree", 0, &stmt->arg));
stmt->format = LY_VALUE_SCHEMA;
stmt->prefix_data = mod;
stmt->kw = LY_STMT_TYPE;
if (!prev_exts) {
assert(pctx->main_ctx == pctx);
LY_CHECK_RET(ly_set_add(&pctx->ext_inst, mod->exts, 1, NULL));
} else {
if (!ly_set_contains(&pctx->ext_inst, prev_exts, &idx)) {
LOGINT_RET(mod->mod->ctx);
}
pctx->ext_inst.objs[idx] = mod->exts;
}
LY_LIST_NEW_RET(mod->mod->ctx, &mod->data, leaf, next, LY_EMEM);
leaf->nodetype = LYS_LEAF;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "date-and-time", 0, &leaf->name));
leaf->flags = LYS_INTERNAL;
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "yang_:date-and-time", 0, &leaf->type.name));
leaf->type.pmod = mod;
LY_ARRAY_NEW_RET(mod->mod->ctx, mod->imports, imp, LY_EMEM);
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "ietf-yang-types", 0, &imp->name));
LY_CHECK_RET(lysdict_insert(mod->mod->ctx, "yang_", 0, &imp->prefix));
imp->flags = LYS_INTERNAL;
return LY_SUCCESS;
}
static LY_ERR
lys_compile_submodules(struct lys_module *mod)
{
LY_ERR rc = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u;
const struct lysp_submodule *submodp;
struct lysc_submodule *submod;
LY_ARRAY_FOR(mod->parsed->includes, u) {
submodp = mod->parsed->includes[u].submodule;
LY_ARRAY_NEW_GOTO(mod->ctx, mod->submodules, submod, rc, cleanup);
DUP_STRING_GOTO(mod->ctx, submodp->name, submod->name, rc, cleanup);
if (submodp->revs) {
LY_CHECK_GOTO(rc = lysdict_insert(mod->ctx, submodp->revs[0].date, 0, &submod->revision), cleanup);
}
DUP_STRING_GOTO(mod->ctx, submodp->filepath, submod->filepath, rc, cleanup);
}
cleanup:
return rc;
}
LY_ERR
lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const struct lysp_load_module_data *mod_data,
struct ly_set *new_mods, struct lys_module **module)
{
LY_ERR rc = LY_SUCCESS, r;
struct lys_module *mod = NULL, *latest, *mod_dup = NULL;
struct lysp_yang_ctx *yangctx = NULL;
struct lysp_yin_ctx *yinctx = NULL;
struct lysp_ctx *pctx = NULL;
ly_bool mod_created = 0, mod_exists = 0;
assert(ctx && in && new_mods);
if (module) {
*module = NULL;
}
mod = calloc(1, sizeof *mod);
LY_CHECK_ERR_RET(!mod, LOGMEM(ctx), LY_EMEM);
mod->ctx = ctx;
switch (format) {
case LYS_IN_YIN:
rc = yin_parse_module(&yinctx, in, mod);
pctx = (struct lysp_ctx *)yinctx;
break;
case LYS_IN_YANG:
rc = yang_parse_module(&yangctx, in, mod);
pctx = (struct lysp_ctx *)yangctx;
break;
default:
LOGERR(ctx, LY_EINVAL, "Invalid schema input format.");
rc = LY_EINVAL;
break;
}
LY_CHECK_GOTO(rc, cleanup);
lysp_sort_revisions(mod->parsed->revs);
if (mod->parsed->revs) {
LY_CHECK_GOTO(rc = lysdict_insert(ctx, mod->parsed->revs[0].date, 0, &mod->revision), cleanup);
}
switch (mod->parsed->version) {
case LYS_VERSION_UNDEF:
case LYS_VERSION_1_0:
mod->version = LYS_VERSION_1_0;
break;
case LYS_VERSION_1_1:
mod->version = LYS_VERSION_1_1;
break;
}
latest = ly_ctx_get_module_latest(ctx, mod->name);
if (latest) {
if (mod->revision) {
if (!latest->revision) {
mod->latest_revision = latest->latest_revision & (LYS_MOD_LATEST_REV | LYS_MOD_LATEST_SEARCHDIRS);
} else if (strcmp(mod->revision, latest->revision) > 0) {
mod->latest_revision = latest->latest_revision & (LYS_MOD_LATEST_REV | LYS_MOD_LATEST_SEARCHDIRS);
} else {
latest = NULL;
}
} else {
latest = NULL;
}
} else {
mod->latest_revision = LYS_MOD_LATEST_REV;
}
if (mod_data) {
r = lysp_load_module_data_check(ctx, mod->parsed, NULL, mod_data);
if (r == LY_EEXIST) {
mod_exists = 1;
goto cleanup;
} else if (r) {
rc = r;
goto cleanup;
}
}
mod_dup = ly_ctx_get_module(ctx, mod->name, mod->revision);
if (mod_dup) {
LOGVRB("Module \"%s@%s\" is already present in the context.", mod_dup->name,
mod_dup->revision ? mod_dup->revision : "<none>");
goto cleanup;
}
mod_dup = ly_ctx_get_module_latest_ns(ctx, mod->ns);
if (mod_dup && (mod_dup->revision == mod->revision)) {
LOGERR(ctx, LY_EINVAL, "Two different modules (\"%s\" and \"%s\") have the same namespace \"%s\".",
mod_dup->name, mod->name, mod->ns);
rc = LY_EINVAL;
goto cleanup;
}
switch (in->type) {
case LY_IN_FILEPATH:
ly_check_module_filename(ctx, mod->name, mod->parsed->revs ? mod->parsed->revs[0].date : NULL, in->method.fpath.filepath);
break;
case LY_IN_FD:
case LY_IN_FILE:
case LY_IN_MEMORY:
break;
case LY_IN_ERROR:
LOGINT(ctx);
rc = LY_EINT;
goto cleanup;
}
LY_CHECK_GOTO(rc = lys_parser_fill_filepath(ctx, in, &mod->filepath), cleanup);
if (latest) {
latest->latest_revision &= ~(LYS_MOD_LATEST_REV | LYS_MOD_LATEST_SEARCHDIRS);
}
if (!strcmp(mod->name, "ietf-netconf")) {
LY_CHECK_GOTO(rc = lysp_add_internal_ietf_netconf(pctx, mod->parsed), cleanup);
} else if (!strcmp(mod->name, "ietf-netconf-with-defaults")) {
LY_CHECK_GOTO(rc = lysp_add_internal_ietf_netconf_with_defaults(pctx, mod->parsed), cleanup);
} else if (!strcmp(mod->name, "yang")) {
LY_CHECK_GOTO(rc = lysp_add_internal_yang(pctx, mod->parsed), cleanup);
}
LY_CHECK_GOTO(rc = ly_set_add(new_mods, mod, 1, NULL), cleanup);
mod_created = 1;
rc = ly_set_add(&ctx->modules, mod, 1, NULL);
LY_CHECK_GOTO(rc, cleanup);
LY_CHECK_GOTO(rc = lysp_resolve_import_include(pctx, mod->parsed, new_mods), cleanup);
lysp_resolve_ext_instance_plugins(mod);
LY_CHECK_GOTO(rc = lysp_resolve_ext_instance_records(pctx), cleanup);
LY_CHECK_GOTO(rc = lysp_check_dup_typedefs(pctx, mod->parsed), cleanup);
LY_CHECK_GOTO(rc = lysp_check_dup_groupings(pctx, mod->parsed), cleanup);
LY_CHECK_GOTO(rc = lysp_check_dup_features(pctx, mod->parsed), cleanup);
LY_CHECK_GOTO(rc = lysp_check_dup_identities(pctx, mod->parsed), cleanup);
LY_CHECK_GOTO(rc = lys_compile_feature_iffeatures(mod->parsed), cleanup);
LY_CHECK_GOTO(rc = lys_compile_extensions(mod), cleanup);
LY_CHECK_GOTO(rc = lys_compile_identities(mod), cleanup);
LY_CHECK_GOTO(rc = lys_compile_submodules(mod), cleanup);
cleanup:
if (rc && mod && mod->name) {
const struct ly_err_item *e = ly_err_last(ctx);
if (e && (!e->schema_path || e->line)) {
LOGERR(ctx, LY_EOTHER, "Parsing module \"%s\" failed.", mod->name);
}
}
if (!mod_created) {
lys_module_free(ctx, mod, 0);
mod = mod_dup;
}
if (format == LYS_IN_YANG) {
lysp_yang_ctx_free(yangctx);
} else {
lysp_yin_ctx_free(yinctx);
}
if (!rc && !mod_exists && module) {
*module = mod;
}
return rc;
}
static LYS_INFORMAT
lys_parse_get_format(const struct ly_in *in, LYS_INFORMAT format)
{
if (!format && (in->type == LY_IN_FILEPATH)) {
const char *path = in->method.fpath.filepath;
size_t len = strlen(path);
for ( ; len > 0 && isspace(path[len - 1]); len--) {}
if ((len >= LY_YANG_SUFFIX_LEN + 1) &&
!strncmp(&path[len - LY_YANG_SUFFIX_LEN], LY_YANG_SUFFIX, LY_YANG_SUFFIX_LEN)) {
format = LYS_IN_YANG;
} else if ((len >= LY_YIN_SUFFIX_LEN + 1) &&
!strncmp(&path[len - LY_YIN_SUFFIX_LEN], LY_YIN_SUFFIX, LY_YIN_SUFFIX_LEN)) {
format = LYS_IN_YIN;
}
}
return format;
}
LIBYANG_API_DEF LY_ERR
lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const char **features, struct lys_module **module)
{
LY_ERR ret = LY_SUCCESS;
struct lys_module *mod;
if (module) {
*module = NULL;
}
LY_CHECK_ARG_RET(NULL, ctx, !(ctx->opts & LY_CTX_INT_IMMUTABLE), in, LY_EINVAL);
format = lys_parse_get_format(in, format);
LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
in->func_start = in->current;
ret = lys_parse_in(ctx, in, format, NULL, &ctx->unres.creating, &mod);
LY_CHECK_GOTO(ret, cleanup);
ret = _lys_set_implemented(mod, features, &ctx->unres);
LY_CHECK_GOTO(ret, cleanup);
if (!(ctx->opts & LY_CTX_EXPLICIT_COMPILE)) {
LY_CHECK_GOTO(ret = lys_unres_dep_sets_create(ctx, &ctx->unres.dep_sets, mod), cleanup);
LY_CHECK_GOTO(ret = lys_compile_depset_all(ctx, &ctx->unres), cleanup);
lys_unres_glob_erase(&ctx->unres);
ly_ctx_new_change(ctx);
}
cleanup:
if (ret) {
lys_unres_glob_revert(ctx, &ctx->unres);
lys_unres_glob_erase(&ctx->unres);
} else if (module) {
*module = mod;
}
return ret;
}
LIBYANG_API_DEF LY_ERR
lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, struct lys_module **module)
{
LY_ERR ret;
struct ly_in *in = NULL;
LY_CHECK_ARG_RET(ctx, ctx, !(ctx->opts & LY_CTX_INT_IMMUTABLE), data, format != LYS_IN_UNKNOWN, LY_EINVAL);
LY_CHECK_ERR_RET(ret = ly_in_new_memory(data, &in), LOGERR(ctx, ret, "Unable to create input handler."), ret);
ret = lys_parse(ctx, in, format, NULL, module);
ly_in_free(in, 0);
return ret;
}
LIBYANG_API_DEF LY_ERR
lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct lys_module **module)
{
LY_ERR ret;
struct ly_in *in = NULL;
LY_CHECK_ARG_RET(ctx, ctx, !(ctx->opts & LY_CTX_INT_IMMUTABLE), fd > -1, format != LYS_IN_UNKNOWN, LY_EINVAL);
LY_CHECK_ERR_RET(ret = ly_in_new_fd(fd, &in), LOGERR(ctx, ret, "Unable to create input handler."), ret);
ret = lys_parse(ctx, in, format, NULL, module);
ly_in_free(in, 0);
return ret;
}
LIBYANG_API_DEF LY_ERR
lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, struct lys_module **module)
{
LY_ERR ret;
struct ly_in *in = NULL;
LY_CHECK_ARG_RET(ctx, ctx, !(ctx->opts & LY_CTX_INT_IMMUTABLE), path, format != LYS_IN_UNKNOWN, LY_EINVAL);
LY_CHECK_ERR_RET(ret = ly_in_new_filepath(path, 0, &in),
LOGERR(ctx, ret, "Unable to create input handler for filepath %s.", path), ret);
ret = lys_parse(ctx, in, format, NULL, module);
ly_in_free(in, 0);
return ret;
}
static LY_ERR
lys_search_localfile_file_type(const struct dirent *file, const char *wd, struct ly_set *dirs, ly_bool implicit_cwd,
ly_bool *skip)
{
LY_ERR rc = LY_SUCCESS;
char *str = NULL;
ly_bool is_dir = 0, is_reg = 0, need_stat = 1;
struct stat st;
*skip = 0;
#ifdef HAVE_DIRENT_D_TYPE
if (file->d_type == DT_DIR) {
is_dir = 1;
need_stat = 0;
} else if (file->d_type == DT_REG) {
is_reg = 1;
need_stat = 0;
} else if ((file->d_type != DT_UNKNOWN) && (file->d_type != DT_LNK)) {
need_stat = 0;
}
#endif
if (need_stat) {
if (asprintf(&str, "%s/%s", wd, file->d_name) == -1) {
LOGMEM(NULL);
rc = LY_EMEM;
goto cleanup;
}
if (stat(str, &st)) {
LOGWRN(NULL, "Unable to get information about \"%s\" file in \"%s\" when searching for (sub)modules (%s)",
file->d_name, wd, strerror(errno));
} else if (S_ISDIR(st.st_mode)) {
is_dir = 1;
} else if (S_ISREG(st.st_mode)) {
is_reg = 1;
}
}
if (is_dir && (dirs->count || !implicit_cwd)) {
if (!str && (asprintf(&str, "%s/%s", wd, file->d_name) == -1)) {
LOGMEM(NULL);
rc = LY_EMEM;
goto cleanup;
}
if ((rc = ly_set_add(dirs, str, 0, NULL))) {
goto cleanup;
}
str = NULL;
*skip = 1;
} else if (!is_reg) {
*skip = 1;
}
cleanup:
free(str);
return rc;
}
LIBYANG_API_DEF LY_ERR
lys_search_localfile(const char * const *searchpaths, ly_bool cwd, const char *name, const char *revision,
char **localfile, LYS_INFORMAT *format)
{
LY_ERR ret = LY_EMEM;
size_t len, flen, match_len = 0, dir_len;
ly_bool implicit_cwd = 0, skip;
char *wd;
DIR *dir = NULL;
struct dirent *file;
char *match_name = NULL;
LYS_INFORMAT format_aux, match_format = 0;
struct ly_set *dirs;
LY_CHECK_ARG_RET(NULL, localfile, LY_EINVAL);
LY_CHECK_RET(ly_set_new(&dirs));
len = strlen(name);
if (cwd) {
wd = get_current_dir_name();
if (!wd) {
LOGMEM(NULL);
goto cleanup;
} else {
ret = ly_set_add(dirs, wd, 0, NULL);
LY_CHECK_GOTO(ret, cleanup);
implicit_cwd = 1;
}
}
if (searchpaths) {
for (uint64_t i = 0; searchpaths[i]; i++) {
if (implicit_cwd && !strcmp(dirs->objs[0], searchpaths[i])) {
implicit_cwd = 0;
continue;
}
wd = strdup(searchpaths[i]);
if (!wd) {
LOGMEM(NULL);
goto cleanup;
} else {
ret = ly_set_add(dirs, wd, 0, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
}
}
wd = NULL;
while (dirs->count) {
free(wd);
dirs->count--;
wd = (char *)dirs->objs[dirs->count];
dirs->objs[dirs->count] = NULL;
LOGVRB("Searching for \"%s\" in \"%s\".", name, wd);
if (dir) {
closedir(dir);
}
dir = opendir(wd);
dir_len = strlen(wd);
if (!dir) {
LOGWRN(NULL, "Unable to open directory \"%s\" for searching (sub)modules (%s).", wd, strerror(errno));
continue;
}
while ((file = readdir(dir))) {
if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
continue;
}
if ((ret = lys_search_localfile_file_type(file, wd, dirs, implicit_cwd, &skip))) {
goto cleanup;
}
if (strncmp(name, file->d_name, len) || ((file->d_name[len] != '.') && (file->d_name[len] != '@'))) {
continue;
}
flen = strlen(file->d_name);
if ((flen >= LY_YANG_SUFFIX_LEN + 1) && !strcmp(&file->d_name[flen - LY_YANG_SUFFIX_LEN], LY_YANG_SUFFIX)) {
format_aux = LYS_IN_YANG;
} else if ((flen >= LY_YIN_SUFFIX_LEN + 1) && !strcmp(&file->d_name[flen - LY_YIN_SUFFIX_LEN], LY_YIN_SUFFIX)) {
format_aux = LYS_IN_YIN;
} else {
continue;
}
if (revision) {
if (file->d_name[len] == '@') {
if (strncmp(revision, &file->d_name[len + 1], strlen(revision))) {
continue;
} else {
free(match_name);
if (asprintf(&match_name, "%s/%s", wd, file->d_name) == -1) {
LOGMEM(NULL);
goto cleanup;
}
match_len = dir_len + 1 + len;
match_format = format_aux;
goto success;
}
} else {
free(match_name);
if (asprintf(&match_name, "%s/%s", wd, file->d_name) == -1) {
LOGMEM(NULL);
goto cleanup;
}
match_len = dir_len + 1 + len;
match_format = format_aux;
continue;
}
} else {
if (match_name) {
if ((file->d_name[len] != '@') ||
lys_check_date(NULL, &file->d_name[len + 1],
flen - ((format_aux == LYS_IN_YANG) ? LY_YANG_SUFFIX_LEN : LY_YIN_SUFFIX_LEN) - len - 1, "revision")) {
continue;
} else if ((match_name[match_len] == '@') &&
(strncmp(&match_name[match_len + 1], &file->d_name[len + 1], LY_REV_SIZE - 1) >= 0)) {
continue;
}
free(match_name);
}
if (asprintf(&match_name, "%s/%s", wd, file->d_name) == -1) {
LOGMEM(NULL);
goto cleanup;
}
match_len = dir_len + 1 + len;
match_format = format_aux;
continue;
}
}
}
success:
(*localfile) = match_name;
match_name = NULL;
if (format) {
(*format) = match_format;
}
ret = LY_SUCCESS;
cleanup:
free(wd);
if (dir) {
closedir(dir);
}
free(match_name);
ly_set_free(dirs, free);
return ret;
}