schema tree REFACTOR evaluate features during compilation
So that the compiled schema tree reflects the state
of all the features.
diff --git a/src/common.h b/src/common.h
index 7e3f23b..d5209c5 100644
--- a/src/common.h
+++ b/src/common.h
@@ -206,7 +206,6 @@
#define LY_VCODE_NOUNIQ LYVE_DATA, "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\"."
#define LY_VCODE_DUP LYVE_DATA, "Duplicate instance of \"%s\"."
#define LY_VCODE_DUPCASE LYVE_DATA, "Data for both cases \"%s\" and \"%s\" exist."
-#define LY_VCODE_NOIFF LYVE_DATA, "Data are disabled by \"%s\" schema node if-feature."
#define LY_VCODE_INNODE LYVE_DATA, "Invalid %s data node \"%s\" found."
#define LY_VCODE_NOKEY LYVE_DATA, "List instance is missing its key \"%s\"."
@@ -614,6 +613,19 @@
--(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))
/**
+ * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)) and free the whole array
+ * in case it was decremented to 0.
+ *
+ * @param[in] ARRAY Pointer to the array to affect.
+ */
+#define LY_ARRAY_DECREMENT_FREE(ARRAY) \
+ --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
+ if (!LY_ARRAY_COUNT(ARRAY)) { \
+ LY_ARRAY_FREE(ARRAY); \
+ (ARRAY) = NULL; \
+ }
+
+/**
* @brief Free the space allocated for the ([sized array](@ref sizedarrays)).
*
* The items inside the array are not freed.
diff --git a/src/context.c b/src/context.c
index 9457436..48bd11b 100644
--- a/src/context.c
+++ b/src/context.c
@@ -178,13 +178,13 @@
}
API const struct lys_module *
-ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision)
+ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision, const char **features)
{
struct lys_module *result = NULL;
LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
- LY_CHECK_RET(lysp_load_module(ctx, name, revision, 1, 0, &result), NULL);
+ LY_CHECK_RET(lysp_load_module(ctx, name, revision, 1, 0, features, &result), NULL);
return result;
}
@@ -253,7 +253,7 @@
for (i = 0; i < ((options & LY_CTX_NO_YANGLIBRARY) ? (LY_INTERNAL_MODS_COUNT - 2) : LY_INTERNAL_MODS_COUNT); i++) {
ly_in_memory(in, internal_modules[i].data);
LY_CHECK_GOTO(rc = lys_create_module(ctx, in, internal_modules[i].format, internal_modules[i].implemented,
- NULL, NULL, &module), error);
+ NULL, NULL, NULL, &module), error);
}
ly_in_free(in, 0);
@@ -612,21 +612,32 @@
}
static LY_ERR
-ylib_feature(struct lyd_node *parent, const struct lys_module *cur_mod)
+ylib_feature(struct lyd_node *parent, const struct lysp_module *pmod)
{
- LY_ARRAY_COUNT_TYPE i;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysp_feature *f;
- if (!cur_mod->implemented) {
+ if (!pmod->mod->implemented) {
/* no features can be enabled */
return LY_SUCCESS;
}
- LY_ARRAY_FOR(cur_mod->features, i) {
- if (!(cur_mod->features[i].flags & LYS_FENABLED)) {
+ LY_ARRAY_FOR(pmod->features, struct lysp_feature, f) {
+ if (!(f->flags & LYS_FENABLED)) {
continue;
}
- LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", cur_mod->features[i].name, NULL));
+ LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", f->name, NULL));
+ }
+
+ LY_ARRAY_FOR(pmod->includes, u) {
+ LY_ARRAY_FOR(pmod->includes[u].submodule->features, struct lysp_feature, f) {
+ if (!(f->flags & LYS_FENABLED)) {
+ continue;
+ }
+
+ LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", f->name, NULL));
+ }
}
return LY_SUCCESS;
@@ -658,7 +669,7 @@
}
static LY_ERR
-ylib_submodules(struct lyd_node *parent, const struct lys_module *cur_mod, ly_bool bis)
+ylib_submodules(struct lyd_node *parent, const struct lysp_module *pmod, ly_bool bis)
{
LY_ERR ret;
LY_ARRAY_COUNT_TYPE i;
@@ -667,8 +678,8 @@
int r;
char *str;
- LY_ARRAY_FOR(cur_mod->parsed->includes, i) {
- submod = cur_mod->parsed->includes[i].submodule;
+ LY_ARRAY_FOR(pmod->includes, i) {
+ submod = pmod->includes[i].submodule;
if (bis) {
LY_CHECK_RET(lyd_new_list(parent, NULL, "submodule", &cont, submod->name));
@@ -683,7 +694,7 @@
if (submod->filepath) {
r = asprintf(&str, "file://%s", submod->filepath);
- LY_CHECK_ERR_RET(r == -1, LOGMEM(cur_mod->ctx), LY_EMEM);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(pmod->mod->ctx), LY_EMEM);
ret = lyd_new_term(cont, NULL, bis ? "location" : "schema", str, NULL);
free(str);
@@ -734,6 +745,10 @@
for (i = 0; i < ctx->list.count; ++i) {
mod = ctx->list.objs[i];
+ if (!mod->parsed) {
+ LOGERR(ctx, LY_ENOTFOUND, "Parsed module \"%s\" missing in the context.", mod->name);
+ goto error;
+ }
/*
* deprecated legacy
@@ -755,7 +770,7 @@
LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "namespace", mod->ns, NULL), error);
/* feature leaf-list */
- LY_CHECK_GOTO(ret = ylib_feature(cont, mod), error);
+ LY_CHECK_GOTO(ret = ylib_feature(cont, mod->parsed), error);
/* deviation list */
LY_CHECK_GOTO(ret = ylib_deviation(cont, mod, 0), error);
@@ -765,7 +780,7 @@
NULL), error);
/* submodule list */
- LY_CHECK_GOTO(ret = ylib_submodules(cont, mod, 0), error);
+ LY_CHECK_GOTO(ret = ylib_submodules(cont, mod->parsed, 0), error);
/*
* current revision
@@ -797,10 +812,10 @@
}
/* submodule list */
- LY_CHECK_GOTO(ret = ylib_submodules(cont, mod, 1), error);
+ LY_CHECK_GOTO(ret = ylib_submodules(cont, mod->parsed, 1), error);
/* feature list */
- LY_CHECK_GOTO(ret = ylib_feature(cont, mod), error);
+ LY_CHECK_GOTO(ret = ylib_feature(cont, mod->parsed), error);
/* deviation */
LY_CHECK_GOTO(ret = ylib_deviation(cont, mod, 1), error);
diff --git a/src/context.h b/src/context.h
index 758b419..ac82a01 100644
--- a/src/context.h
+++ b/src/context.h
@@ -469,9 +469,12 @@
* @param[in] ctx Context to add to.
* @param[in] name Name of the module to load.
* @param[in] revision Optional revision date of the module. If not specified, the latest revision is loaded.
+ * @param[in] features Optional array of features ended with NULL to be enabled if the module is being implemented.
+ * NULL for all features disabled and '*' for all enabled.
* @return Pointer to the data model structure, NULL if not found or some error occurred.
*/
-const struct lys_module *ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision);
+const struct lys_module *ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision,
+ const char **features);
/**
* @brief Get current ID of the modules set. The value is available also
diff --git a/src/parser_json.c b/src/parser_json.c
index 02428af..9fa5334 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -188,9 +188,7 @@
size_t name_len, const struct lyd_node_inner *parent, const struct lysc_node **snode_p)
{
struct lys_module *mod = NULL;
-
- /* leave if-feature check for validation */
- uint32_t getnext_opts = LYS_GETNEXT_NOSTATECHECK | (lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
+ uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
/* init return value */
*snode_p = NULL;
@@ -362,7 +360,7 @@
/* get all keys into a set (keys do not have if-features or anything) */
snode = NULL;
- while ((snode = lys_getnext(snode, list, NULL, LYS_GETNEXT_NOSTATECHECK)) && (snode->flags & LYS_KEY)) {
+ while ((snode = lys_getnext(snode, list, NULL, 0)) && (snode->flags & LYS_KEY)) {
ret = ly_set_add(&key_set, (void *)snode, 1, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index 2f4a0c7..1076bdc 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -588,8 +588,7 @@
uint32_t getnext_opts;
*snode = NULL;
- /* leave if-feature check for validation */
- getnext_opts = LYS_GETNEXT_NOSTATECHECK | (lybctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
+ getnext_opts = lybctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
/* read the first hash */
lyb_read(&hash[0], sizeof *hash, lybctx->lybctx);
diff --git a/src/parser_schema.h b/src/parser_schema.h
index 57a3928..afef7fe 100644
--- a/src/parser_schema.h
+++ b/src/parser_schema.h
@@ -98,10 +98,12 @@
* @param[in] ctx libyang context where to process the data model.
* @param[in] in The input handle to provide the dumped data model in the specified format.
* @param[in] format Format of the schema to parse.
+ * @param[in] features Array of features to enable ended with NULL. If NULL, no features are enabled.
* @param[out] module Optional parsed module.
* @return LY_ERR value.
*/
-LY_ERR lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const struct lys_module **module);
+LY_ERR lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const char **features,
+ const struct lys_module **module);
/**
* @brief Load a schema into the specified context.
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 8647e69..f3abaa4 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -215,7 +215,7 @@
/* get all keys into a set (keys do not have if-features or anything) */
snode = NULL;
- while ((snode = lys_getnext(snode, list, NULL, LYS_GETNEXT_NOSTATECHECK)) && (snode->flags & LYS_KEY)) {
+ while ((snode = lys_getnext(snode, list, NULL, 0)) && (snode->flags & LYS_KEY)) {
ret = ly_set_add(&key_set, (void *)snode, 1, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
@@ -387,8 +387,7 @@
xmlctx = lydctx->xmlctx;
ctx = xmlctx->ctx;
- /* leave if-feature check for validation */
- getnext_opts = LYS_GETNEXT_NOSTATECHECK | (lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
+ getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
assert(xmlctx->status == LYXML_ELEMENT);
diff --git a/src/path.c b/src/path.c
index f590fbc..7086a97 100644
--- a/src/path.c
+++ b/src/path.c
@@ -407,7 +407,7 @@
LOGVAL_P(ctx, cur_node, LYVE_XPATH, "Not implemented module \"%s\" in path.", (*mod)->name);
return LY_EVALID;
}
- LY_CHECK_RET(lys_set_implemented((struct lys_module *)*mod));
+ LY_CHECK_RET(lys_set_implemented((struct lys_module *)*mod, NULL));
}
} else {
switch (format) {
@@ -479,7 +479,7 @@
/* NameTest, find the key */
LY_CHECK_RET(ly_path_compile_prefix(ctx, cur_node, cur_mod, ctx_node, expr, *tok_idx, LY_PATH_LREF_FALSE,
format, prefix_data, &mod, &name, &name_len));
- key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+ key = lys_find_child(ctx_node, mod, name, name_len, 0, 0);
if (!key) {
LOGVAL_P(ctx, cur_node, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
return LY_ENOTFOUND;
@@ -629,7 +629,7 @@
/* NameTest, find the key */
LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node, cur_node->module, ctx_node, expr, *tok_idx,
LY_PATH_LREF_TRUE, format, prefix_data, &mod, &name, &name_len));
- key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+ key = lys_find_child(ctx_node, mod, name, name_len, 0, 0);
if (!key) {
LOGVAL_P(cur_node->module->ctx, cur_node, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
return LY_EVALID;
@@ -688,7 +688,7 @@
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST);
LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node, cur_node->module, node, expr, *tok_idx,
LY_PATH_LREF_TRUE, format, prefix_data, &mod, &name, &name_len));
- node2 = lys_find_child(node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+ node2 = lys_find_child(node, mod, name, name_len, 0, 0);
if (!node2) {
LOGVAL_P(cur_node->module->ctx, cur_node, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
return LY_EVALID;
@@ -747,9 +747,9 @@
*path = NULL;
if (oper == LY_PATH_OPER_OUTPUT) {
- getnext_opts = LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_OUTPUT;
+ getnext_opts = LYS_GETNEXT_OUTPUT;
} else {
- getnext_opts = LYS_GETNEXT_NOSTATECHECK;
+ getnext_opts = 0;
}
if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
diff --git a/src/plugins_exts.c b/src/plugins_exts.c
index 9158e09..f5e3ce4 100644
--- a/src/plugins_exts.c
+++ b/src/plugins_exts.c
@@ -67,8 +67,6 @@
return "bit";
case LYEXT_PAR_TYPE_ENUM:
return "enum";
- case LYEXT_PAR_FEATURE:
- return "feature";
case LYEXT_PAR_MUST:
return "must";
case LYEXT_PAR_PATTERN:
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
index 0569468..a10a3dd 100644
--- a/src/plugins_exts.h
+++ b/src/plugins_exts.h
@@ -84,6 +84,7 @@
/**
* @brief Compile substatements of an extension instance.
* TODO
+ * @return LY_ENOT if the extension is disabled and should be ignored.
*/
LY_ERR lys_compile_extension_instance(struct lysc_ctx *ctx, const struct lysp_ext_instance *ext, struct lysc_ext_substmt *substmts);
@@ -125,6 +126,7 @@
* for later use (data validation or use of external tool).
* @return LY_SUCCESS in case of success.
* @return LY_EVALID in case of non-conforming parsed data.
+ * @return LY_ENOT in case the extension instance is not supported and should be removed.
*/
typedef LY_ERR (*lyext_clb_compile)(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext, struct lysc_ext_instance *c_ext);
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 9927a22..5049f98 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -1034,7 +1034,7 @@
size_t buf_size = 0;
char *buf = NULL;
size_t index;
- LY_ARRAY_COUNT_TYPE u, v;
+ LY_ARRAY_COUNT_TYPE u;
char *errmsg = NULL;
struct lysc_type_bits *type_bits = (struct lysc_type_bits *)type;
ly_bool iscanonical = 1;
@@ -1068,15 +1068,6 @@
/* we have the match */
uint32_t inserted;
- /* check that the bit is not disabled */
- LY_ARRAY_FOR(type_bits->bits[u].iffeatures, v) {
- if (lysc_iffeature_value(&type_bits->bits[u].iffeatures[v]) == LY_ENOT) {
- rc = asprintf(&errmsg, "Bit \"%s\" is disabled by its %" LY_PRI_ARRAY_COUNT_TYPE ". if-feature condition.",
- type_bits->bits[u].name, v + 1);
- goto cleanup;
- }
- }
-
if (iscanonical && items->count && (type_bits->bits[u].position < ((struct lysc_type_bitenum_item *)items->objs[items->count - 1])->position)) {
iscanonical = 0;
}
@@ -1215,7 +1206,7 @@
uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
{
- LY_ARRAY_COUNT_TYPE u, v;
+ LY_ARRAY_COUNT_TYPE u;
char *errmsg = NULL;
struct lysc_type_enum *type_enum = (struct lysc_type_enum *)type;
int rc = 0;
@@ -1226,16 +1217,7 @@
/* find the matching enumeration value item */
LY_ARRAY_FOR(type_enum->enums, u) {
if (!ly_strncmp(type_enum->enums[u].name, value, value_len)) {
- /* we have the match */
-
- /* check that the enumeration value is not disabled */
- LY_ARRAY_FOR(type_enum->enums[u].iffeatures, v) {
- if (lysc_iffeature_value(&type_enum->enums[u].iffeatures[v]) == LY_ENOT) {
- rc = asprintf(&errmsg, "Enumeration \"%s\" is disabled by its %" LY_PRI_ARRAY_COUNT_TYPE ". if-feature condition.",
- type_enum->enums[u].name, v + 1);
- goto error;
- }
- }
+ /* we have a match */
goto match;
}
}
@@ -1448,7 +1430,7 @@
} else if (!mod->implemented) {
/* non-implemented module */
if (options & LY_TYPE_STORE_IMPLEMENT) {
- LY_CHECK_RET(lys_set_implemented((struct lys_module *)mod));
+ LY_CHECK_RET(lys_set_implemented((struct lys_module *)mod, NULL));
} else {
rc = asprintf(&errmsg, "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
(int)value_len, value, mod->name);
diff --git a/src/printer_lyb.c b/src/printer_lyb.c
index a1188bd..860d5a0 100644
--- a/src/printer_lyb.c
+++ b/src/printer_lyb.c
@@ -130,8 +130,7 @@
mod = sibling->module;
sibling = NULL;
- /* ignore features so that their state does not affect hashes */
- while ((sibling = (struct lysc_node *)lys_getnext(sibling, parent, mod->compiled, LYS_GETNEXT_NOSTATECHECK))) {
+ while ((sibling = (struct lysc_node *)lys_getnext(sibling, parent, mod->compiled, 0))) {
/* find the first non-colliding hash (or specifically non-colliding hash sequence) */
for (i = 0; i < LYB_HASH_BITS; ++i) {
/* check that we are not colliding with nodes inserted with a lower collision ID than ours */
diff --git a/src/printer_yang.c b/src/printer_yang.c
index b60b0c4..147f860 100644
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -461,78 +461,6 @@
}
static void
-yprc_iffeature(struct ypr_ctx *ctx, struct lysc_iffeature *feat, size_t *index_e, size_t *index_f)
-{
- ly_bool brackets_flag = *index_e ? 1 : 0;
- uint8_t op;
-
- op = lysc_iff_getop(feat->expr, *index_e);
- (*index_e)++;
-
- switch (op) {
- case LYS_IFF_F:
- if (ctx->module == feat->features[*index_f]->module) {
- ly_print_(ctx->out, "%s", feat->features[*index_f]->name);
- } else {
- ly_print_(ctx->out, "%s:%s", feat->features[*index_f]->module->prefix, feat->features[*index_f]->name);
- }
- (*index_f)++;
- break;
- case LYS_IFF_NOT:
- ly_print_(ctx->out, "not ");
- yprc_iffeature(ctx, feat, index_e, index_f);
- break;
- case LYS_IFF_AND:
- if (brackets_flag) {
- /* AND need brackets only if previous op was not */
- if ((*index_e < 2) || (lysc_iff_getop(feat->expr, *index_e - 2) != LYS_IFF_NOT)) {
- brackets_flag = 0;
- }
- }
- /* falls through */
- case LYS_IFF_OR:
- if (brackets_flag) {
- ly_print_(ctx->out, "(");
- }
- yprc_iffeature(ctx, feat, index_e, index_f);
- ly_print_(ctx->out, " %s ", op == LYS_IFF_OR ? "or" : "and");
- yprc_iffeature(ctx, feat, index_e, index_f);
- if (brackets_flag) {
- ly_print_(ctx->out, ")");
- }
- }
-}
-
-static void
-yprc_iffeatures(struct ypr_ctx *ctx, struct lysc_iffeature *iff, struct lysc_ext_instance *exts, ly_bool *flag)
-{
- LY_ARRAY_COUNT_TYPE u, v;
- ly_bool extflag;
-
- LY_ARRAY_FOR(iff, u) {
- size_t index_e = 0, index_f = 0;
-
- ypr_open(ctx->out, flag);
- extflag = 0;
-
- ly_print_(ctx->out, "%*sif-feature \"", INDENT);
- yprc_iffeature(ctx, iff, &index_e, &index_f);
- ly_print_(ctx->out, "\"");
-
- /* extensions */
- LEVEL++;
- LY_ARRAY_FOR(exts, v) {
- if ((exts[v].insubstmt != LYEXT_SUBSTMT_IFFEATURE) || (exts[v].insubstmt_index != u)) {
- continue;
- }
- yprc_extension_instances(ctx, LYEXT_SUBSTMT_IFFEATURE, u, &exts[v], &extflag, 1);
- }
- LEVEL--;
- ypr_close(ctx, extflag);
- }
-}
-
-static void
yprp_extension(struct ypr_ctx *ctx, const struct lysp_ext *ext)
{
ly_bool flag = 0, flag2 = 0;
@@ -589,22 +517,6 @@
}
static void
-yprc_feature(struct ypr_ctx *ctx, const struct lysc_feature *feat)
-{
- ly_bool flag = 0;
-
- ly_print_(ctx->out, "\n%*sfeature %s", INDENT, feat->name);
- LEVEL++;
- yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, feat->exts, &flag, 0);
- yprc_iffeatures(ctx, feat->iffeatures, feat->exts, &flag);
- ypr_status(ctx, feat->flags, feat->exts, &flag);
- ypr_description(ctx, feat->dsc, feat->exts, &flag);
- ypr_reference(ctx, feat->ref, feat->exts, &flag);
- LEVEL--;
- ypr_close(ctx, flag);
-}
-
-static void
yprp_identity(struct ypr_ctx *ctx, const struct lysp_ident *ident)
{
ly_bool flag = 0;
@@ -639,7 +551,6 @@
LEVEL++;
yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, ident->exts, &flag, 0);
- yprc_iffeatures(ctx, ident->iffeatures, ident->exts, &flag);
LY_ARRAY_FOR(ident->derived, u) {
ypr_open(ctx->out, &flag);
@@ -987,7 +898,6 @@
ly_print_(ctx->out, "\"");
LEVEL++;
yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, item->exts, &inner_flag, 0);
- yprc_iffeatures(ctx, item->iffeatures, item->exts, &inner_flag);
if (type->basetype == LY_TYPE_BITS) {
ypr_unsigned(ctx, LYEXT_SUBSTMT_POSITION, 0, item->exts, item->position, &inner_flag);
} else { /* LY_TYPE_ENUM */
@@ -1231,7 +1141,6 @@
LEVEL++;
yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, notif->exts, &flag, 0);
- yprc_iffeatures(ctx, notif->iffeatures, notif->exts, &flag);
LY_ARRAY_FOR(notif->musts, u) {
yprc_must(ctx, ¬if->musts[u], &flag);
@@ -1292,7 +1201,6 @@
LEVEL++;
yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, action->exts, &flag, 0);
- yprc_iffeatures(ctx, action->iffeatures, action->exts, &flag);
ypr_status(ctx, action->flags, action->exts, &flag);
ypr_description(ctx, action->dsc, action->exts, &flag);
ypr_reference(ctx, action->ref, action->exts, &flag);
@@ -1327,7 +1235,6 @@
LY_ARRAY_FOR(node->when, u) {
yprc_when(ctx, node->when[u], flag);
}
- yprc_iffeatures(ctx, node->iffeatures, node->exts, flag);
}
/* macr oto unify the code */
@@ -2341,10 +2248,6 @@
yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, module->compiled->exts, NULL, 0);
}
- LY_ARRAY_FOR(module->features, u) {
- yprc_feature(ctx, &module->features[u]);
- }
-
LY_ARRAY_FOR(module->identities, u) {
yprc_identity(ctx, &module->identities[u]);
}
diff --git a/src/schema_compile.c b/src/schema_compile.c
index 24bc995..e32c84b 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -37,6 +37,7 @@
#include "plugins_types.h"
#include "schema_compile_amend.h"
#include "schema_compile_node.h"
+#include "schema_features.h"
#include "set.h"
#include "tree.h"
#include "tree_data.h"
@@ -45,16 +46,6 @@
#include "tree_schema_internal.h"
#include "xpath.h"
-#define COMPILE_CHECK_UNIQUENESS_ARRAY(CTX, ARRAY, MEMBER, EXCL, STMT, IDENT) \
- if (ARRAY) { \
- for (LY_ARRAY_COUNT_TYPE u__ = 0; u__ < LY_ARRAY_COUNT(ARRAY); ++u__) { \
- if (&(ARRAY)[u__] != EXCL && (void*)((ARRAY)[u__].MEMBER) == (void*)(IDENT)) { \
- LOGVAL((CTX)->ctx, LY_VLOG_STR, (CTX)->path, LY_VCODE_DUPIDENT, IDENT, STMT); \
- return LY_EVALID; \
- } \
- } \
- }
-
/**
* @brief Fill in the prepared compiled extensions definition structure according to the parsed extension definition.
*/
@@ -317,319 +308,6 @@
}
/**
- * @brief Stack for processing if-feature expressions.
- */
-struct iff_stack {
- size_t size; /**< number of items in the stack */
- size_t index; /**< first empty item */
- uint8_t *stack; /**< stack - array of @ref ifftokens to create the if-feature expression in prefix format */
-};
-
-/**
- * @brief Add @ref ifftokens into the stack.
- * @param[in] stack The if-feature stack to use.
- * @param[in] value One of the @ref ifftokens to store in the stack.
- * @return LY_EMEM in case of memory allocation error
- * @return LY_ESUCCESS if the value successfully stored.
- */
-static LY_ERR
-iff_stack_push(struct iff_stack *stack, uint8_t value)
-{
- if (stack->index == stack->size) {
- stack->size += 4;
- stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
- LY_CHECK_ERR_RET(!stack->stack, LOGMEM(NULL); stack->size = 0, LY_EMEM);
- }
- stack->stack[stack->index++] = value;
- return LY_SUCCESS;
-}
-
-/**
- * @brief Get (and remove) the last item form the stack.
- * @param[in] stack The if-feature stack to use.
- * @return The value from the top of the stack.
- */
-static uint8_t
-iff_stack_pop(struct iff_stack *stack)
-{
- assert(stack && stack->index);
-
- stack->index--;
- return stack->stack[stack->index];
-}
-
-/**
- * @brief Clean up the stack.
- * @param[in] stack The if-feature stack to use.
- */
-static void
-iff_stack_clean(struct iff_stack *stack)
-{
- stack->size = 0;
- free(stack->stack);
-}
-
-/**
- * @brief Store the @ref ifftokens (@p op) on the given position in the 2bits array
- * (libyang format of the if-feature expression).
- * @param[in,out] list The 2bits array to modify.
- * @param[in] op The operand (@ref ifftokens) to store.
- * @param[in] pos Position (0-based) where to store the given @p op.
- */
-static void
-iff_setop(uint8_t *list, uint8_t op, size_t pos)
-{
- uint8_t *item;
- uint8_t mask = 3;
-
- assert(op <= 3); /* max 2 bits */
-
- item = &list[pos / 4];
- mask = mask << 2 * (pos % 4);
- *item = (*item) & ~mask;
- *item = (*item) | (op << 2 * (pos % 4));
-}
-
-#define LYS_IFF_LP 0x04 /**< Additional, temporary, value of @ref ifftokens: ( */
-#define LYS_IFF_RP 0x08 /**< Additional, temporary, value of @ref ifftokens: ) */
-
-/**
- * @brief Find a feature of the given name and referenced in the given module.
- *
- * If the compiled schema is available (the schema is implemented), the feature from the compiled schema is
- * returned. Otherwise, the special array of pre-compiled features is used to search for the feature. Such
- * features are always disabled (feature from not implemented schema cannot be enabled), but in case the schema
- * will be made implemented in future (no matter if implicitly via augmenting/deviating it or explicitly via
- * ly_ctx_module_implement()), the compilation of these feature structure is finished, but the pointers
- * assigned till that time will be still valid.
- *
- * @param[in] pmod Module where the feature was referenced (used to resolve prefix of the feature).
- * @param[in] name Name of the feature including possible prefix.
- * @param[in] len Length of the string representing the feature identifier in the name variable (mandatory!).
- * @return Pointer to the feature structure if found, NULL otherwise.
- */
-static struct lysc_feature *
-lys_feature_find(const struct lysp_module *pmod, const char *name, size_t len)
-{
- LY_ARRAY_COUNT_TYPE u;
- struct lysc_feature *f;
- const struct lys_module *mod;
- const char *ptr;
-
- assert(pmod);
-
- if ((ptr = ly_strnchr(name, ':', len))) {
- /* we have a prefixed feature */
- mod = lysp_module_find_prefix(pmod, name, ptr - name);
- LY_CHECK_RET(!mod, NULL);
-
- len = len - (ptr - name) - 1;
- name = ptr + 1;
- } else {
- /* local feature */
- mod = pmod->mod;
- }
-
- /* we have the correct module, get the feature */
- LY_ARRAY_FOR(mod->features, u) {
- f = &mod->features[u];
- if (!ly_strncmp(f->name, name, len)) {
- return f;
- }
- }
-
- return NULL;
-}
-
-LY_ERR
-lys_compile_iffeature(struct lysc_ctx *ctx, struct lysp_qname *qname, struct lysc_iffeature *iff)
-{
- LY_ERR rc = LY_SUCCESS;
- const char *c = qname->str;
- int64_t i, j;
- int8_t op_len, last_not = 0, checkversion = 0;
- LY_ARRAY_COUNT_TYPE f_size = 0, expr_size = 0, f_exp = 1;
- uint8_t op;
- struct iff_stack stack = {0, 0, NULL};
- struct lysc_feature *f;
-
- assert(c);
-
- /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
- for (i = j = 0; c[i]; i++) {
- if (c[i] == '(') {
- j++;
- checkversion = 1;
- continue;
- } else if (c[i] == ')') {
- j--;
- continue;
- } else if (isspace(c[i])) {
- checkversion = 1;
- continue;
- }
-
- if (!strncmp(&c[i], "not", op_len = 3) || !strncmp(&c[i], "and", op_len = 3) || !strncmp(&c[i], "or", op_len = 2)) {
- uint64_t spaces;
- for (spaces = 0; c[i + op_len + spaces] && isspace(c[i + op_len + spaces]); spaces++) {}
- if (c[i + op_len + spaces] == '\0') {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - unexpected end of expression.", qname->str);
- return LY_EVALID;
- } else if (!isspace(c[i + op_len])) {
- /* feature name starting with the not/and/or */
- last_not = 0;
- f_size++;
- } else if (c[i] == 'n') { /* not operation */
- if (last_not) {
- /* double not */
- expr_size = expr_size - 2;
- last_not = 0;
- } else {
- last_not = 1;
- }
- } else { /* and, or */
- if (f_exp != f_size) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - missing feature/expression before \"%.*s\" operation.",
- qname->str, op_len, &c[i]);
- return LY_EVALID;
- }
- f_exp++;
-
- /* not a not operation */
- last_not = 0;
- }
- i += op_len;
- } else {
- f_size++;
- last_not = 0;
- }
- expr_size++;
-
- while (!isspace(c[i])) {
- if (!c[i] || (c[i] == ')') || (c[i] == '(')) {
- i--;
- break;
- }
- i++;
- }
- }
- if (j) {
- /* not matching count of ( and ) */
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - non-matching opening and closing parentheses.", qname->str);
- return LY_EVALID;
- }
- if (f_exp != f_size) {
- /* features do not match the needed arguments for the logical operations */
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - number of features in expression does not match "
- "the required number of operands for the operations.", qname->str);
- return LY_EVALID;
- }
-
- if (checkversion || (expr_size > 1)) {
- /* check that we have 1.1 module */
- if (qname->mod->version != LYS_VERSION_1_1) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", qname->str);
- return LY_EVALID;
- }
- }
-
- /* allocate the memory */
- LY_ARRAY_CREATE_RET(ctx->ctx, iff->features, f_size, LY_EMEM);
- iff->expr = calloc((j = (expr_size / 4) + ((expr_size % 4) ? 1 : 0)), sizeof *iff->expr);
- stack.stack = malloc(expr_size * sizeof *stack.stack);
- LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx->ctx); rc = LY_EMEM, error);
-
- stack.size = expr_size;
- f_size--; expr_size--; /* used as indexes from now */
-
- for (i--; i >= 0; i--) {
- if (c[i] == ')') {
- /* push it on stack */
- iff_stack_push(&stack, LYS_IFF_RP);
- continue;
- } else if (c[i] == '(') {
- /* pop from the stack into result all operators until ) */
- while ((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
- iff_setop(iff->expr, op, expr_size--);
- }
- continue;
- } else if (isspace(c[i])) {
- continue;
- }
-
- /* end of operator or operand -> find beginning and get what is it */
- j = i + 1;
- while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
- i--;
- }
- i++; /* go back by one step */
-
- if (!strncmp(&c[i], "not", 3) && isspace(c[i + 3])) {
- if (stack.index && (stack.stack[stack.index - 1] == LYS_IFF_NOT)) {
- /* double not */
- iff_stack_pop(&stack);
- } else {
- /* not has the highest priority, so do not pop from the stack
- * as in case of AND and OR */
- iff_stack_push(&stack, LYS_IFF_NOT);
- }
- } else if (!strncmp(&c[i], "and", 3) && isspace(c[i + 3])) {
- /* as for OR - pop from the stack all operators with the same or higher
- * priority and store them to the result, then push the AND to the stack */
- while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
- op = iff_stack_pop(&stack);
- iff_setop(iff->expr, op, expr_size--);
- }
- iff_stack_push(&stack, LYS_IFF_AND);
- } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
- while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
- op = iff_stack_pop(&stack);
- iff_setop(iff->expr, op, expr_size--);
- }
- iff_stack_push(&stack, LYS_IFF_OR);
- } else {
- /* feature name, length is j - i */
-
- /* add it to the expression */
- iff_setop(iff->expr, LYS_IFF_F, expr_size--);
-
- /* now get the link to the feature definition */
- f = lys_feature_find(qname->mod, &c[i], j - i);
- LY_CHECK_ERR_GOTO(!f, LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".", qname->str, j - i, &c[i]);
- rc = LY_EVALID, error)
- iff->features[f_size] = f;
- LY_ARRAY_INCREMENT(iff->features);
- f_size--;
- }
- }
- while (stack.index) {
- op = iff_stack_pop(&stack);
- iff_setop(iff->expr, op, expr_size--);
- }
-
- if (++expr_size || ++f_size) {
- /* not all expected operators and operands found */
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - processing error.", qname->str);
- rc = LY_EINT;
- } else {
- rc = LY_SUCCESS;
- }
-
-error:
- /* cleanup */
- iff_stack_clean(&stack);
-
- return rc;
-}
-
-/**
* @brief Compile information in the import statement - make sure there is the target module
* @param[in] ctx Compile context.
* @param[in] imp_p The parsed import statement structure to fill the module to.
@@ -652,7 +330,7 @@
LOGINT(ctx->ctx);
} else {
LY_CHECK_RET(lys_parse(ctx->ctx, in, !strcmp(&imp_p->module->filepath[strlen(imp_p->module->filepath - 4)],
- ".yin") ? LYS_IN_YIN : LYS_IN_YANG, &mod));
+ ".yin") ? LYS_IN_YIN : LYS_IN_YANG, NULL, &mod));
if (mod != imp_p->module) {
LOGERR(ctx->ctx, LY_EINT, "Filepath \"%s\" of the module \"%s\" does not match.",
imp_p->module->filepath, imp_p->module->name);
@@ -662,7 +340,7 @@
ly_in_free(in, 1);
}
if (!mod) {
- if (lysp_load_module(ctx->ctx, imp_p->module->name, imp_p->module->revision, 0, 1, (struct lys_module **)&mod)) {
+ if (lysp_load_module(ctx->ctx, imp_p->module->name, imp_p->module->revision, 0, 1, NULL, (struct lys_module **)&mod)) {
LOGERR(ctx->ctx, LY_ENOTFOUND, "Unable to reload \"%s\" module to import it into \"%s\", source data not found.",
imp_p->module->name, ctx->cur_mod->name);
return LY_ENOTFOUND;
@@ -677,9 +355,11 @@
lys_identity_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lysp_module *parsed_mod,
struct lysp_ident *identities_p, struct lysc_ident **identities)
{
- LY_ARRAY_COUNT_TYPE offset = 0, u, v;
+ LY_ARRAY_COUNT_TYPE u;
struct lysc_ctx context = {0};
+ struct lysc_ident *ident;
LY_ERR ret = LY_SUCCESS;
+ ly_bool enabled;
assert(ctx_sc || ctx);
@@ -695,27 +375,27 @@
if (!identities_p) {
return LY_SUCCESS;
}
- if (*identities) {
- offset = LY_ARRAY_COUNT(*identities);
- }
lysc_update_path(ctx_sc, NULL, "{identity}");
- LY_ARRAY_CREATE_RET(ctx_sc->ctx, *identities, LY_ARRAY_COUNT(identities_p), LY_EMEM);
LY_ARRAY_FOR(identities_p, u) {
+ /* evaluate if-features */
+ LY_CHECK_RET(lys_eval_iffeatures(ctx, identities_p[u].iffeatures, &enabled));
+ if (!enabled) {
+ continue;
+ }
+
lysc_update_path(ctx_sc, NULL, identities_p[u].name);
- LY_ARRAY_INCREMENT(*identities);
- COMPILE_CHECK_UNIQUENESS_ARRAY(ctx_sc, *identities, name, &(*identities)[offset + u], "identity", identities_p[u].name);
- DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].name, (*identities)[offset + u].name, ret, done);
- DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].dsc, (*identities)[offset + u].dsc, ret, done);
- DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].ref, (*identities)[offset + u].ref, ret, done);
- (*identities)[offset + u].module = ctx_sc->cur_mod;
- COMPILE_ARRAY_GOTO(ctx_sc, identities_p[u].iffeatures, (*identities)[offset + u].iffeatures, v,
- lys_compile_iffeature, ret, done);
+ /* add new compiled identity */
+ LY_ARRAY_NEW_RET(ctx_sc->ctx, *identities, ident, LY_EMEM);
+
+ DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].name, ident->name, ret, done);
+ DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].dsc, ident->dsc, ret, done);
+ DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].ref, ident->ref, ret, done);
+ ident->module = ctx_sc->cur_mod;
/* backlinks (derived) can be added no sooner than when all the identities in the current module are present */
- COMPILE_EXTS_GOTO(ctx_sc, identities_p[u].exts, (*identities)[offset + u].exts, &(*identities)[offset + u],
- LYEXT_PAR_IDENT, ret, done);
- (*identities)[offset + u].flags = identities_p[u].flags;
+ COMPILE_EXTS_GOTO(ctx_sc, identities_p[u].exts, ident->exts, ident, LYEXT_PAR_IDENT, ret, done);
+ ident->flags = identities_p[u].flags;
lysc_update_path(ctx_sc, NULL, NULL);
}
@@ -782,14 +462,14 @@
LY_ERR
lys_compile_identity_bases(struct lysc_ctx *ctx, const struct lysp_module *base_pmod, const char **bases_p,
- struct lysc_ident *ident, struct lysc_ident ***bases)
+ struct lysc_ident *ident, struct lysc_ident ***bases, ly_bool *enabled)
{
LY_ARRAY_COUNT_TYPE u, v;
const char *s, *name;
const struct lys_module *mod;
struct lysc_ident **idref;
- assert(ident || bases);
+ assert((ident && enabled) || bases);
if ((LY_ARRAY_COUNT(bases_p) > 1) && (ctx->pmod->version < LYS_VERSION_1_1)) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
@@ -841,6 +521,13 @@
}
if (!idref || !(*idref)) {
if (ident) {
+ /* look into the parsed module to check whether the identity is not merely disabled */
+ LY_ARRAY_FOR(mod->parsed->identities, v) {
+ if (!strcmp(mod->parsed->identities[v].name, name)) {
+ *enabled = 0;
+ return LY_SUCCESS;
+ }
+ }
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
"Unable to find base (%s) of identity \"%s\".", bases_p[u], ident->name);
} else {
@@ -850,6 +537,10 @@
return LY_EVALID;
}
}
+
+ if (ident) {
+ *enabled = 1;
+ }
return LY_SUCCESS;
}
@@ -857,263 +548,63 @@
* @brief For the given array of identities, set the backlinks from all their base identities.
* @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
* @param[in] idents_p Array of identities definitions from the parsed schema structure.
- * @param[in] idents Array of referencing identities to which the backlinks are supposed to be set.
+ * @param[in,out] idents Array of referencing identities to which the backlinks are supposed to be set. Any
+ * identities with disabled bases are removed.
* @return LY_ERR value - LY_SUCCESS or LY_EVALID.
*/
static LY_ERR
-lys_compile_identities_derived(struct lysc_ctx *ctx, struct lysp_ident *idents_p, struct lysc_ident *idents)
+lys_compile_identities_derived(struct lysc_ctx *ctx, struct lysp_ident *idents_p, struct lysc_ident **idents)
{
- LY_ARRAY_COUNT_TYPE u;
+ LY_ARRAY_COUNT_TYPE u, v;
+ ly_bool enabled;
lysc_update_path(ctx, NULL, "{identity}");
- for (u = 0; u < LY_ARRAY_COUNT(idents_p); ++u) {
- if (!idents_p[u].bases) {
+
+restart:
+ for (u = 0, v = 0; *idents && (u < LY_ARRAY_COUNT(*idents)); ++u) {
+ /* find matching parsed identity, the disabled ones are missing in the compiled array */
+ while (v < LY_ARRAY_COUNT(idents_p)) {
+ if (idents_p[v].name == (*idents)[u].name) {
+ break;
+ }
+ ++v;
+ }
+ assert(v < LY_ARRAY_COUNT(idents_p));
+
+ if (!idents_p[v].bases) {
continue;
}
- lysc_update_path(ctx, NULL, idents[u].name);
- LY_CHECK_RET(lys_compile_identity_bases(ctx, idents[u].module->parsed, idents_p[u].bases, &idents[u], NULL));
+ lysc_update_path(ctx, NULL, (*idents)[u].name);
+ LY_CHECK_RET(lys_compile_identity_bases(ctx, (*idents)[u].module->parsed, idents_p[v].bases, &(*idents)[u], NULL,
+ &enabled));
lysc_update_path(ctx, NULL, NULL);
+
+ if (!enabled) {
+ /* remove the identity */
+ lysc_ident_free(ctx->ctx, &(*idents)[u]);
+ LY_ARRAY_DECREMENT(*idents);
+ if (u < LY_ARRAY_COUNT(*idents)) {
+ memmove(&(*idents)[u], &(*idents)[u + 1], (LY_ARRAY_COUNT(*idents) - u) * sizeof **idents);
+ }
+ if (!LY_ARRAY_COUNT(*idents)) {
+ LY_ARRAY_FREE(*idents);
+ *idents = NULL;
+ }
+
+ /* revert compilation of all the previous identities */
+ for (v = 0; v < u; ++v) {
+ LY_ARRAY_FREE((*idents)[v].derived);
+ }
+
+ /* restart the whole process without this identity */
+ goto restart;
+ }
}
+
lysc_update_path(ctx, NULL, NULL);
return LY_SUCCESS;
}
-LY_ERR
-lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lysp_module *parsed_mod,
- struct lysp_feature *features_p, struct lysc_feature **features)
-{
- LY_ERR ret = LY_SUCCESS;
- LY_ARRAY_COUNT_TYPE offset = 0, u;
- struct lysc_ctx context = {0};
-
- assert(ctx_sc || ctx);
-
- if (!ctx_sc) {
- context.ctx = ctx;
- context.cur_mod = parsed_mod->mod;
- context.pmod = parsed_mod;
- context.path_len = 1;
- context.path[0] = '/';
- ctx_sc = &context;
- }
-
- if (!features_p) {
- return LY_SUCCESS;
- }
- if (*features) {
- offset = LY_ARRAY_COUNT(*features);
- }
-
- lysc_update_path(ctx_sc, NULL, "{feature}");
- LY_ARRAY_CREATE_RET(ctx_sc->ctx, *features, LY_ARRAY_COUNT(features_p), LY_EMEM);
- LY_ARRAY_FOR(features_p, u) {
- lysc_update_path(ctx_sc, NULL, features_p[u].name);
-
- LY_ARRAY_INCREMENT(*features);
- COMPILE_CHECK_UNIQUENESS_ARRAY(ctx_sc, *features, name, &(*features)[offset + u], "feature", features_p[u].name);
- DUP_STRING_GOTO(ctx_sc->ctx, features_p[u].name, (*features)[offset + u].name, ret, done);
- DUP_STRING_GOTO(ctx_sc->ctx, features_p[u].dsc, (*features)[offset + u].dsc, ret, done);
- DUP_STRING_GOTO(ctx_sc->ctx, features_p[u].ref, (*features)[offset + u].ref, ret, done);
- (*features)[offset + u].flags = features_p[u].flags;
- (*features)[offset + u].module = ctx_sc->cur_mod;
-
- lysc_update_path(ctx_sc, NULL, NULL);
- }
- lysc_update_path(ctx_sc, NULL, NULL);
-
-done:
- return ret;
-}
-
-/**
- * @brief Check circular dependency of features - feature MUST NOT reference itself (via their if-feature statement).
- *
- * The function works in the same way as lys_compile_identity_circular_check() with different structures and error messages.
- *
- * @param[in] ctx Compile context for logging.
- * @param[in] feature The feature referenced in if-feature statement (its depfeatures list is being extended by the feature
- * being currently processed).
- * @param[in] depfeatures The list of depending features of the feature being currently processed (not the one provided as @p feature)
- * @return LY_SUCCESS if everything is ok.
- * @return LY_EVALID if the feature references indirectly itself.
- */
-static LY_ERR
-lys_compile_feature_circular_check(struct lysc_ctx *ctx, struct lysc_feature *feature, struct lysc_feature **depfeatures)
-{
- LY_ERR ret = LY_SUCCESS;
- LY_ARRAY_COUNT_TYPE u, v;
- struct ly_set recursion = {0};
- struct lysc_feature *drv;
-
- if (!depfeatures) {
- return LY_SUCCESS;
- }
-
- for (u = 0; u < LY_ARRAY_COUNT(depfeatures); ++u) {
- if (feature == depfeatures[u]) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Feature \"%s\" is indirectly referenced from itself.", feature->name);
- ret = LY_EVALID;
- goto cleanup;
- }
- ret = ly_set_add(&recursion, depfeatures[u], 0, NULL);
- LY_CHECK_GOTO(ret, cleanup);
- }
-
- for (v = 0; v < recursion.count; ++v) {
- drv = recursion.objs[v];
- if (!drv->depfeatures) {
- continue;
- }
- for (u = 0; u < LY_ARRAY_COUNT(drv->depfeatures); ++u) {
- if (feature == drv->depfeatures[u]) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Feature \"%s\" is indirectly referenced from itself.", feature->name);
- ret = LY_EVALID;
- goto cleanup;
- }
- ly_set_add(&recursion, drv->depfeatures[u], 0, NULL);
- LY_CHECK_GOTO(ret, cleanup);
- }
- }
-
-cleanup:
- ly_set_erase(&recursion, NULL);
- return ret;
-}
-
-/**
- * @brief Create pre-compiled features array.
- *
- * See lys_feature_precompile() for more details.
- *
- * @param[in] ctx Compile context.
- * @param[in] feature_p Parsed feature definition to compile.
- * @param[in,out] features List of already (pre)compiled features to find the corresponding precompiled feature structure.
- * @return LY_ERR value.
- */
-static LY_ERR
-lys_feature_precompile_finish(struct lysc_ctx *ctx, struct lysp_feature *feature_p, struct lysc_feature *features)
-{
- LY_ARRAY_COUNT_TYPE u, v, x;
- struct lysc_feature *feature, **df;
- LY_ERR ret = LY_SUCCESS;
-
- /* find the preprecompiled feature */
- LY_ARRAY_FOR(features, x) {
- if (strcmp(features[x].name, feature_p->name)) {
- continue;
- }
- feature = &features[x];
- lysc_update_path(ctx, NULL, "{feature}");
- lysc_update_path(ctx, NULL, feature_p->name);
-
- /* finish compilation started in lys_feature_precompile() */
- COMPILE_EXTS_GOTO(ctx, feature_p->exts, feature->exts, feature, LYEXT_PAR_FEATURE, ret, done);
- COMPILE_ARRAY_GOTO(ctx, feature_p->iffeatures, feature->iffeatures, u, lys_compile_iffeature, ret, done);
- if (feature->iffeatures) {
- for (u = 0; u < LY_ARRAY_COUNT(feature->iffeatures); ++u) {
- if (feature->iffeatures[u].features) {
- for (v = 0; v < LY_ARRAY_COUNT(feature->iffeatures[u].features); ++v) {
- /* check for circular dependency - direct reference first,... */
- if (feature == feature->iffeatures[u].features[v]) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Feature \"%s\" is referenced from itself.", feature->name);
- return LY_EVALID;
- }
- /* ... and indirect circular reference */
- LY_CHECK_RET(lys_compile_feature_circular_check(ctx, feature->iffeatures[u].features[v], feature->depfeatures));
-
- /* add itself into the dependants list */
- LY_ARRAY_NEW_RET(ctx->ctx, feature->iffeatures[u].features[v]->depfeatures, df, LY_EMEM);
- *df = feature;
- }
- }
- }
- }
- lysc_update_path(ctx, NULL, NULL);
- lysc_update_path(ctx, NULL, NULL);
-done:
- return ret;
- }
-
- LOGINT(ctx->ctx);
- return LY_EINT;
-}
-
-void
-lys_feature_precompile_revert(struct lysc_ctx *ctx, struct lys_module *mod)
-{
- LY_ARRAY_COUNT_TYPE u, v;
-
- /* in the dis_features list, remove all the parts (from finished compiling process)
- * which may points into the data being freed here */
- LY_ARRAY_FOR(mod->features, u) {
- LY_ARRAY_FOR(mod->features[u].iffeatures, v) {
- lysc_iffeature_free(ctx->ctx, &mod->features[u].iffeatures[v]);
- }
- LY_ARRAY_FREE(mod->features[u].iffeatures);
- mod->features[u].iffeatures = NULL;
-
- LY_ARRAY_FOR(mod->features[u].exts, v) {
- lysc_ext_instance_free(ctx->ctx, &(mod->features[u].exts)[v]);
- }
- LY_ARRAY_FREE(mod->features[u].exts);
- mod->features[u].exts = NULL;
- }
-}
-
-/**
- * @brief Check the features used in if-feature statements applicable to the leafref and its target.
- *
- * The set of features used for target must be a subset of features used for the leafref.
- * This is not a perfect, we should compare the truth tables but it could require too much resources
- * and RFC 7950 does not require it explicitely, so we simplify that.
- *
- * @param[in] refnode The leafref node.
- * @param[in] target Tha target node of the leafref.
- * @return LY_SUCCESS or LY_EVALID;
- */
-static LY_ERR
-lys_compile_leafref_features_validate(const struct lysc_node *refnode, const struct lysc_node *target)
-{
- LY_ERR ret = LY_EVALID;
- const struct lysc_node *iter;
- LY_ARRAY_COUNT_TYPE u, v;
- struct ly_set features = {0};
-
- for (iter = refnode; iter; iter = iter->parent) {
- if (iter->iffeatures) {
- LY_ARRAY_FOR(iter->iffeatures, u) {
- LY_ARRAY_FOR(iter->iffeatures[u].features, v) {
- LY_CHECK_GOTO(ly_set_add(&features, iter->iffeatures[u].features[v], 0, NULL), cleanup);
- }
- }
- }
- }
-
- /* we should have, in features set, a superset of features applicable to the target node.
- * If the feature is not present, we don;t have a subset of features applicable
- * to the leafref itself. */
- for (iter = target; iter; iter = iter->parent) {
- if (iter->iffeatures) {
- LY_ARRAY_FOR(iter->iffeatures, u) {
- LY_ARRAY_FOR(iter->iffeatures[u].features, v) {
- if (!ly_set_contains(&features, iter->iffeatures[u].features[v], NULL)) {
- /* feature not present */
- goto cleanup;
- }
- }
- }
- }
- }
- ret = LY_SUCCESS;
-
-cleanup:
- ly_set_erase(&features, NULL);
- return ret;
-}
-
static void *
lys_compile_extension_instance_storage(enum ly_stmt stmt, struct lysc_ext_substmt *substmts)
{
@@ -1131,8 +622,9 @@
LY_ERR ret = LY_EVALID, r;
LY_ARRAY_COUNT_TYPE u;
struct lysp_stmt *stmt;
- struct lysp_qname qname;
+ struct lysp_qname *iffeatures, *iffeat;
void *parsed = NULL, **compiled = NULL;
+ ly_bool enabled;
/* check for invalid substatements */
for (stmt = ext->child; stmt; stmt = stmt->next) {
@@ -1216,26 +708,19 @@
LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
break;
}
- case LY_STMT_IF_FEATURE: {
- struct lysc_iffeature *iff = NULL;
-
- if (substmts[u].cardinality < LY_STMT_CARD_SOME) {
- /* single item */
- if (((struct lysc_iffeature *)substmts[u].storage)->features) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DUPSTMT, stmt->stmt);
- goto cleanup;
- }
- iff = (struct lysc_iffeature *)substmts[u].storage;
- } else {
- /* sized array */
- struct lysc_iffeature **iffs = (struct lysc_iffeature **)substmts[u].storage;
- LY_ARRAY_NEW_GOTO(ctx->ctx, *iffs, iff, ret, cleanup);
+ case LY_STMT_IF_FEATURE:
+ iffeatures = NULL;
+ LY_ARRAY_NEW_GOTO(ctx->ctx, iffeatures, iffeat, ret, cleanup);
+ iffeat->str = stmt->arg;
+ iffeat->mod = ctx->pmod;
+ r = lys_eval_iffeatures(ctx->ctx, iffeatures, &enabled);
+ LY_ARRAY_FREE(iffeatures);
+ LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+ if (!enabled) {
+ /* it is disabled, remove the whole extension instance */
+ return LY_ENOT;
}
- qname.str = stmt->arg;
- qname.mod = ctx->pmod;
- LY_CHECK_ERR_GOTO(r = lys_compile_iffeature(ctx, &qname, iff), ret = r, cleanup);
break;
- }
/* TODO support other substatements (parse stmt to lysp and then compile lysp to lysc),
* also note that in many statements their extensions are not taken into account */
default:
@@ -1401,7 +886,7 @@
if (!mod->implemented) {
/* unimplemented module found */
if (implement) {
- LY_CHECK_RET(lys_set_implemented((struct lys_module *)mod));
+ LY_CHECK_RET(lys_set_implemented((struct lys_module *)mod, NULL));
} else {
*mod_p = mod;
break;
@@ -1647,13 +1132,7 @@
}
}
- /* check if leafref and its target are under common if-features */
- if (lys_compile_leafref_features_validate(node, target)) {
- LOGVAL(ctx->ctx, LY_VLOG_LYSC, node, LYVE_REFERENCE,
- "Invalid leafref path \"%s\" - set of features applicable to the leafref target is not a subset of"
- " features applicable to the leafref itself.", lref->path->expr);
- return LY_EVALID;
- }
+ /* TODO check if leafref and its target are under common if-features */
return LY_SUCCESS;
}
@@ -1688,7 +1167,17 @@
ext_mod = ly_ctx_get_module_latest(ctx->ctx, "ietf-yang-metadata");
/* compile the extension instance */
- LY_CHECK_GOTO(ret = lys_compile_ext(ctx, ext_p, ext, mod->compiled, LYEXT_PAR_MODULE, ext_mod), cleanup);
+ ret = lys_compile_ext(ctx, ext_p, ext, mod->compiled, LYEXT_PAR_MODULE, ext_mod);
+ if (ret == LY_ENOT) {
+ /* free the extension */
+ lysc_ext_instance_free(ctx->ctx, ext);
+ LY_ARRAY_DECREMENT_FREE(mod->compiled->exts);
+
+ ret = LY_SUCCESS;
+ goto cleanup;
+ } else if (ret) {
+ goto cleanup;
+ }
cleanup:
lysp_ext_instance_free(ctx->ctx, ext_p);
@@ -1858,9 +1347,67 @@
struct lysc_type_leafref *lref;
struct lysc_augment *aug;
struct lysc_deviation *dev;
+ struct ly_set disabled_op = {0};
LY_ARRAY_COUNT_TYPE v;
uint32_t i;
+#define ARRAY_DEL_ITEM(array, item) \
+ { \
+ LY_ARRAY_COUNT_TYPE u__; \
+ LY_ARRAY_FOR(array, u__) { \
+ if ((array) + u__ == item) { \
+ LY_ARRAY_DECREMENT(array); \
+ if (u__ < LY_ARRAY_COUNT(array)) { \
+ memmove((array) + u__, (array) + u__ + 1, (LY_ARRAY_COUNT(array) - u__) * sizeof *(array)); \
+ } \
+ if (!LY_ARRAY_COUNT(array)) { \
+ LY_ARRAY_FREE(array); \
+ (array) = NULL; \
+ } \
+ break; \
+ } \
+ } \
+ }
+
+ /* remove all disabled nodes */
+ for (i = 0; i < ctx->disabled.count; ++i) {
+ node = ctx->disabled.snodes[i];
+ if (node->flags & LYS_KEY) {
+ LOGVAL(ctx->ctx, LY_VLOG_LYSC, node, LYVE_REFERENCE, "Key \"%s\" is disabled by its if-features.",
+ node->name);
+ return LY_EVALID;
+ }
+
+ if (node->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+ if (node->nodetype & (LYS_RPC | LYS_ACTION)) {
+ lysc_action_free(ctx->ctx, (struct lysc_action *)node);
+ } else {
+ lysc_notif_free(ctx->ctx, (struct lysc_notif *)node);
+ }
+ /* remember all freed RPCs/actions/notifs */
+ ly_set_add(&disabled_op, node, 1, NULL);
+ } else {
+ lysc_node_free(ctx->ctx, node, 1);
+ }
+ }
+
+ /* remove ops also from their arrays, from end so as not to move other items and change these pointer targets */
+ i = disabled_op.count;
+ while (i) {
+ --i;
+ node = disabled_op.snodes[i];
+ if (node->nodetype == LYS_RPC) {
+ ARRAY_DEL_ITEM(node->module->compiled->rpcs, (struct lysc_action *)node);
+ } else if (node->nodetype == LYS_ACTION) {
+ ARRAY_DEL_ITEM(*lysc_node_actions_p(node->parent), (struct lysc_action *)node);
+ } else if (node->parent) {
+ ARRAY_DEL_ITEM(*lysc_node_notifs_p(node->parent), (struct lysc_notif *)node);
+ } else {
+ ARRAY_DEL_ITEM(node->module->compiled->notifs, (struct lysc_notif *)node);
+ }
+ }
+ ly_set_erase(&disabled_op, NULL);
+
/* for leafref, we need 2 rounds - first detects circular chain by storing the first referred type (which
* can be also leafref, in case it is already resolved, go through the chain and check that it does not
* point to the starting leafref type). The second round stores the first non-leafref type for later data validation. */
@@ -1937,50 +1484,7 @@
}
return LY_SUCCESS;
-}
-
-/**
- * @brief Compile features in the current module and all its submodules.
- *
- * @param[in] ctx Compile context.
- * @return LY_ERR value.
- */
-static LY_ERR
-lys_compile_features(struct lysc_ctx *ctx)
-{
- struct lysp_submodule *submod;
- LY_ARRAY_COUNT_TYPE u, v;
-
- if (!ctx->cur_mod->features) {
- /* features are compiled directly into the module structure,
- * but it must be done in two steps to allow forward references (via if-feature) between the features themselves */
- LY_CHECK_RET(lys_feature_precompile(ctx, NULL, NULL, ctx->cur_mod->parsed->features, &ctx->cur_mod->features));
- LY_ARRAY_FOR(ctx->cur_mod->parsed->includes, v) {
- submod = ctx->cur_mod->parsed->includes[v].submodule;
- LY_CHECK_RET(lys_feature_precompile(ctx, NULL, NULL, submod->features, &ctx->cur_mod->features));
- }
- }
-
- /* finish feature compilation, not only for the main module, but also for the submodules.
- * Due to possible forward references, it must be done when all the features (including submodules)
- * are present. */
- LY_ARRAY_FOR(ctx->cur_mod->parsed->features, u) {
- LY_CHECK_RET(lys_feature_precompile_finish(ctx, &ctx->cur_mod->parsed->features[u], ctx->cur_mod->features));
- }
-
- lysc_update_path(ctx, NULL, "{submodule}");
- LY_ARRAY_FOR(ctx->cur_mod->parsed->includes, v) {
- submod = ctx->cur_mod->parsed->includes[v].submodule;
-
- lysc_update_path(ctx, NULL, submod->name);
- LY_ARRAY_FOR(submod->features, u) {
- LY_CHECK_RET(lys_feature_precompile_finish(ctx, &submod->features[u], ctx->cur_mod->features));
- }
- lysc_update_path(ctx, NULL, NULL);
- }
- lysc_update_path(ctx, NULL, NULL);
-
- return LY_SUCCESS;
+#undef ARRAY_DEL_ITEM
}
/**
@@ -2004,7 +1508,7 @@
}
if (ctx->cur_mod->parsed->identities) {
- LY_CHECK_RET(lys_compile_identities_derived(ctx, ctx->cur_mod->parsed->identities, ctx->cur_mod->identities));
+ LY_CHECK_RET(lys_compile_identities_derived(ctx, ctx->cur_mod->parsed->identities, &ctx->cur_mod->identities));
}
lysc_update_path(ctx, NULL, "{submodule}");
LY_ARRAY_FOR(ctx->cur_mod->parsed->includes, u) {
@@ -2012,7 +1516,7 @@
submod = ctx->cur_mod->parsed->includes[u].submodule;
if (submod->identities) {
lysc_update_path(ctx, NULL, submod->name);
- LY_CHECK_RET(lys_compile_identities_derived(ctx, submod->identities, ctx->cur_mod->identities));
+ LY_CHECK_RET(lys_compile_identities_derived(ctx, submod->identities, &ctx->cur_mod->identities));
lysc_update_path(ctx, NULL, NULL);
}
}
@@ -2021,6 +1525,86 @@
return LY_SUCCESS;
}
+static LY_ERR
+lys_recompile_mod(struct lys_module *mod, ly_bool log)
+{
+ LY_ERR ret;
+ uint32_t prev_lo;
+ struct lysp_import *imp;
+
+ if (!mod->implemented || mod->compiled) {
+ /* nothing to do */
+ return LY_SUCCESS;
+ }
+
+ /* we need all implemented imports to be recompiled */
+ LY_ARRAY_FOR(mod->parsed->imports, struct lysp_import, imp) {
+ LY_CHECK_RET(lys_recompile_mod(imp->module, log));
+ }
+
+ if (!log) {
+ /* recompile, must succeed because it was already compiled; hide messages because any
+ * warnings were already printed, are not really relevant, and would hide the real error */
+ prev_lo = ly_log_options(0);
+ }
+ ret = lys_compile(mod, 0);
+ if (!log) {
+ ly_log_options(prev_lo);
+ }
+
+ if (ret && !log) {
+ LOGERR(mod->ctx, ret, "Recompilation of module \"%s\" failed.", mod->name);
+ }
+ return ret;
+}
+
+LY_ERR
+lys_recompile(struct ly_ctx *ctx, const struct lys_module *skip)
+{
+ uint32_t idx;
+ struct lys_module *mod;
+ LY_ERR ret = LY_SUCCESS, r;
+
+ /* free all the modules */
+ for (idx = 0; idx < ctx->list.count; ++idx) {
+ mod = ctx->list.objs[idx];
+ /* skip this module */
+ if (skip && (mod == skip)) {
+ continue;
+ }
+
+ if (mod->compiled) {
+ /* free the module */
+ lysc_module_free(mod->compiled, NULL);
+ mod->compiled = NULL;
+ }
+
+ /* free precompiled iffeatures */
+ lys_free_feature_iffeatures(mod->parsed);
+ }
+
+ /* recompile all the modules */
+ for (idx = 0; idx < ctx->list.count; ++idx) {
+ mod = ctx->list.objs[idx];
+
+ /* skip this module */
+ if (skip && (mod == skip)) {
+ continue;
+ }
+
+ /* compile again */
+ r = lys_recompile_mod(mod, skip ? 1 : 0);
+ if (r) {
+ if (skip) {
+ return r;
+ }
+ ret = r;
+ }
+ }
+
+ return ret;
+}
+
LY_ERR
lys_compile(struct lys_module *mod, uint32_t options)
{
@@ -2062,10 +1646,7 @@
LY_CHECK_GOTO(ret = lys_compile_import(&ctx, &sp->imports[u]), error);
}
- /* features */
- LY_CHECK_GOTO(ret = lys_compile_features(&ctx), error);
-
- /* identities, work similarly to features with the precompilation */
+ /* identities */
LY_CHECK_GOTO(ret = lys_compile_identities(&ctx), error);
/* augments and deviations */
@@ -2161,7 +1742,8 @@
LY_CHECK_GOTO(ret = lys_compile_ietf_netconf_wd_annotation(&ctx, mod), error);
}
- /* there can be no leftover deviations */
+ /* there can be no leftover deviations or augments */
+ LY_CHECK_ERR_GOTO(ctx.augs.count, LOGINT(ctx.ctx); ret = LY_EINT, error);
LY_CHECK_ERR_GOTO(ctx.devs.count, LOGINT(ctx.ctx); ret = LY_EINT, error);
for (i = 0; i < ctx.dflts.count; ++i) {
@@ -2172,6 +1754,7 @@
ly_set_erase(&ctx.leafrefs, NULL);
ly_set_erase(&ctx.groupings, NULL);
ly_set_erase(&ctx.tpdf_chain, NULL);
+ ly_set_erase(&ctx.disabled, NULL);
ly_set_erase(&ctx.augs, NULL);
ly_set_erase(&ctx.devs, NULL);
ly_set_erase(&ctx.uses_augs, NULL);
@@ -2181,7 +1764,6 @@
error:
lys_precompile_augments_deviations_revert(ctx.ctx, mod);
- lys_feature_precompile_revert(&ctx, mod);
for (i = 0; i < ctx.dflts.count; ++i) {
lysc_unres_dflt_free(ctx.ctx, ctx.dflts.objs[i]);
}
@@ -2190,6 +1772,7 @@
ly_set_erase(&ctx.leafrefs, NULL);
ly_set_erase(&ctx.groupings, NULL);
ly_set_erase(&ctx.tpdf_chain, NULL);
+ ly_set_erase(&ctx.disabled, NULL);
for (i = 0; i < ctx.augs.count; ++i) {
lysc_augment_free(ctx.ctx, ctx.augs.objs[i]);
}
diff --git a/src/schema_compile.h b/src/schema_compile.h
index 5266ffb..0e2c800 100644
--- a/src/schema_compile.h
+++ b/src/schema_compile.h
@@ -29,14 +29,18 @@
*
* @{
*/
-#define LYS_COMPILE_RPC_INPUT LYS_CONFIG_W /**< Internal option when compiling schema tree of RPC/action input */
-#define LYS_COMPILE_RPC_OUTPUT LYS_CONFIG_R /**< Internal option when compiling schema tree of RPC/action output */
-#define LYS_COMPILE_RPC_MASK LYS_CONFIG_MASK /**< mask for the internal RPC options */
-#define LYS_COMPILE_NOTIFICATION 0x08 /**< Internal option when compiling schema tree of Notification */
+#define LYS_COMPILE_RPC_INPUT LYS_CONFIG_W /**< Internal option when compiling schema tree of RPC/action input */
+#define LYS_COMPILE_RPC_OUTPUT LYS_CONFIG_R /**< Internal option when compiling schema tree of RPC/action output */
+#define LYS_COMPILE_RPC_MASK LYS_CONFIG_MASK /**< mask for the internal RPC options */
+#define LYS_COMPILE_NOTIFICATION 0x04 /**< Internal option when compiling schema tree of Notification */
-#define LYS_COMPILE_GROUPING 0x10 /** Compiling (validation) of a non-instantiated grouping.
- In this case not all the restrictions are checked since they can be valid only
- in the real placement of the grouping. TODO - what specifically is not done */
+#define LYS_COMPILE_GROUPING 0x08 /**< Compiling (validation) of a non-instantiated grouping.
+ In this case not all the restrictions are checked since they can
+ be valid only in the real placement of the grouping.
+ TODO - what specifically is not done */
+#define LYS_COMPILE_DISABLED 0x10 /**< Compiling a disabled subtree (by its if-features). Meaning
+ it will be removed at the end of compilation and should not be
+ added to any unres sets. */
/** @} scflags */
/**
@@ -51,6 +55,7 @@
struct ly_set leafrefs; /**< to validate leafref's targets */
struct ly_set dflts; /**< set of incomplete default values */
struct ly_set tpdf_chain;
+ struct ly_set disabled; /**< set of compiled nodes whose if-feature(s) was not satisifed */
struct ly_set augs; /**< set of compiled non-applied top-level augments */
struct ly_set devs; /**< set of compiled non-applied deviations */
struct ly_set uses_augs; /**< set of compiled non-applied uses augments */
@@ -126,7 +131,12 @@
for (LY_ARRAY_COUNT_TYPE __exts_iter = 0, __array_offset = LY_ARRAY_COUNT(EXT_C); __exts_iter < LY_ARRAY_COUNT(EXTS_P); ++__exts_iter) { \
LY_ARRAY_INCREMENT(EXT_C); \
RET = lys_compile_ext(CTX, &(EXTS_P)[__exts_iter], &(EXT_C)[__exts_iter + __array_offset], PARENT, PARENT_TYPE, NULL); \
- LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
+ if (RET == LY_ENOT) { \
+ LY_ARRAY_DECREMENT_FREE(EXT_C); \
+ --__array_offset; \
+ RET = LY_SUCCESS; \
+ } \
+ LY_CHECK_GOTO(RET, GOTO); \
} \
}
@@ -139,21 +149,14 @@
* @param[in] parent Extension instance parent.
* @param[in] parent_type Extension instance parent type.
* @param[in] ext_mod Optional module with the extension instance extension definition, set only for internal annotations.
- * @return LY_ERR value.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the extension is disabled and should be ignored.
+ * @return LY_ERR on error.
*/
LY_ERR lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, struct lysc_ext_instance *ext, void *parent,
LYEXT_PARENT parent_type, const struct lys_module *ext_mod);
/**
- * @brief Compile information from the if-feature statement
- * @param[in] ctx Compile context.
- * @param[in] qname The if-feature argument to process. It is pointer-to-qname just to unify the compile functions.
- * @param[in,out] iff Prepared (empty) compiled if-feature structure to fill.
- * @return LY_ERR value.
- */
-LY_ERR lys_compile_iffeature(struct lysc_ctx *ctx, struct lysp_qname *qname, struct lysc_iffeature *iff);
-
-/**
* @brief Compile information from the identity statement
*
* The backlinks to the identities derived from this one are supposed to be filled later via ::lys_compile_identity_bases().
@@ -182,44 +185,11 @@
* @param[in] bases_p Array of names (including prefix if necessary) of base identities.
* @param[in] ident Referencing identity to work with, NULL for identityref.
* @param[in] bases Array of bases of identityref to fill in.
+ * @param[in] enabled Whether the base is disabled, must be set if @p ident is set.
* @return LY_ERR value.
*/
LY_ERR lys_compile_identity_bases(struct lysc_ctx *ctx, const struct lysp_module *base_pmod, const char **bases_p,
- struct lysc_ident *ident, struct lysc_ident ***bases);
-
-/**
- * @brief Create pre-compiled features array.
- *
- * Features are compiled in two steps to allow forward references between them via their if-feature statements.
- * In case of not implemented schemas, the precompiled list of features is stored in lys_module structure and
- * the compilation is not finished (if-feature and extensions are missing) and all the features are permanently
- * disabled without a chance to change it. The list is used as target for any if-feature statement in any
- * implemented module to get valid data to evaluate its result. The compilation is finished via
- * ::lys_feature_precompile_finish() in implemented modules. In case a not implemented module becomes implemented,
- * the precompiled list is reused to finish the compilation to preserve pointers already used in various compiled
- * if-feature structures.
- *
- * @param[in] ctx_sc Compile context - alternative to the combination of @p ctx and @p parsed_mod.
- * @param[in] ctx libyang context.
- * @param[in] parsed_mod Module with the features.
- * @param[in] features_p Array of the parsed features definitions to precompile.
- * @param[in,out] features Pointer to the storage of the (pre)compiled features array where the new features are
- * supposed to be added. The storage is supposed to be initiated to NULL when the first parsed features are going
- * to be processed.
- * @return LY_ERR value.
- */
-LY_ERR lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lysp_module *parsed_mod,
- struct lysp_feature *features_p, struct lysc_feature **features);
-
-/**
- * @brief Revert compiled list of features back to the precompiled state.
- *
- * Function is needed in case the compilation failed and the schema is expected to revert back to the non-compiled status.
- *
- * @param[in] ctx Compilation context.
- * @param[in] mod The module structure with the features to decompile.
- */
-void lys_feature_precompile_revert(struct lysc_ctx *ctx, struct lys_module *mod);
+ struct lysc_ident *ident, struct lysc_ident ***bases, ly_bool *enabled);
/**
* @brief Check statement's status for invalid combination.
diff --git a/src/schema_compile_amend.c b/src/schema_compile_amend.c
index 80f479c..74114da 100644
--- a/src/schema_compile_amend.c
+++ b/src/schema_compile_amend.c
@@ -110,7 +110,7 @@
/* all the modules must be implemented */
if (!mod->implemented) {
- ret = lys_set_implemented(mod);
+ ret = lys_set_implemented(mod, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
}
diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c
index 803fec2..a391fa1 100644
--- a/src/schema_compile_node.c
+++ b/src/schema_compile_node.c
@@ -37,6 +37,7 @@
#include "plugins_types.h"
#include "schema_compile.h"
#include "schema_compile_amend.h"
+#include "schema_features.h"
#include "set.h"
#include "tree.h"
#include "tree_data.h"
@@ -69,6 +70,10 @@
struct lysc_unres_dflt *r = NULL;
uint32_t i;
+ if (ctx->options & LYS_COMPILE_DISABLED) {
+ return LY_SUCCESS;
+ }
+
for (i = 0; i < ctx->dflts.count; ++i) {
if (((struct lysc_unres_dflt *)ctx->dflts.objs[i])->leaf == leaf) {
/* just replace the default */
@@ -113,6 +118,10 @@
struct lysc_unres_dflt *r = NULL;
uint32_t i;
+ if (ctx->options & LYS_COMPILE_DISABLED) {
+ return LY_SUCCESS;
+ }
+
for (i = 0; i < ctx->dflts.count; ++i) {
if (((struct lysc_unres_dflt *)ctx->dflts.objs[i])->llist == llist) {
/* just replace the defaults */
@@ -275,7 +284,7 @@
/* compile when */
LY_CHECK_RET(lys_compile_when_(ctx, when_p, flags, ctx_node, new_when));
- if (!(ctx->options & LYS_COMPILE_GROUPING)) {
+ if (!(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
/* do not check "when" semantics in a grouping */
LY_CHECK_RET(ly_set_add(&ctx->xpath, node, 0, NULL));
}
@@ -289,7 +298,7 @@
++(*when_c)->refcount;
*new_when = *when_c;
- if (!(ctx->options & LYS_COMPILE_GROUPING)) {
+ if (!(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
/* in this case check "when" again for all children because of dummy node check */
LY_CHECK_RET(ly_set_add(&ctx->xpath, node, 0, NULL));
}
@@ -1154,9 +1163,10 @@
{
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u, v, match = 0;
- int32_t value = 0;
- uint32_t position = 0;
+ int32_t value = 0, cur_val;
+ uint32_t position = 0, cur_pos;
struct lysc_type_bitenum_item *e, storage;
+ ly_bool enabled;
if (base_enums && (ctx->pmod->version < LYS_VERSION_1_1)) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "%s type can be subtyped only in YANG 1.1 modules.",
@@ -1165,48 +1175,43 @@
}
LY_ARRAY_FOR(enums_p, u) {
- LY_ARRAY_NEW_RET(ctx->ctx, *enums, e, LY_EMEM);
- DUP_STRING_GOTO(ctx->ctx, enums_p[u].name, e->name, ret, done);
- DUP_STRING_GOTO(ctx->ctx, enums_p[u].ref, e->dsc, ret, done);
- DUP_STRING_GOTO(ctx->ctx, enums_p[u].ref, e->ref, ret, done);
- e->flags = enums_p[u].flags & LYS_FLAGS_COMPILED_MASK;
+ /* perform all checks */
if (base_enums) {
/* check the enum/bit presence in the base type - the set of enums/bits in the derived type must be a subset */
LY_ARRAY_FOR(base_enums, v) {
- if (!strcmp(e->name, base_enums[v].name)) {
+ if (!strcmp(enums_p[u].name, base_enums[v].name)) {
break;
}
}
if (v == LY_ARRAY_COUNT(base_enums)) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
"Invalid %s - derived type adds new item \"%s\".",
- basetype == LY_TYPE_ENUM ? "enumeration" : "bits", e->name);
+ basetype == LY_TYPE_ENUM ? "enumeration" : "bits", enums_p[u].name);
return LY_EVALID;
}
match = v;
}
if (basetype == LY_TYPE_ENUM) {
- e->flags |= LYS_ISENUM;
if (enums_p[u].flags & LYS_SET_VALUE) {
- e->value = (int32_t)enums_p[u].value;
- if (!u || (e->value >= value)) {
- value = e->value + 1;
+ cur_val = (int32_t)enums_p[u].value;
+ if (!u || (cur_val >= value)) {
+ value = cur_val + 1;
}
/* check collision with other values */
- for (v = 0; v < LY_ARRAY_COUNT(*enums) - 1; ++v) {
- if (e->value == (*enums)[v].value) {
+ LY_ARRAY_FOR(*enums, v) {
+ if (cur_val == (*enums)[v].value) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
"Invalid enumeration - value %d collide in items \"%s\" and \"%s\".",
- e->value, e->name, (*enums)[v].name);
+ cur_val, enums_p[u].name, (*enums)[v].name);
return LY_EVALID;
}
}
} else if (base_enums) {
/* inherit the assigned value */
- e->value = base_enums[match].value;
- if (!u || (e->value >= value)) {
- value = e->value + 1;
+ cur_val = base_enums[match].value;
+ if (!u || (cur_val >= value)) {
+ value = cur_val + 1;
}
} else {
/* assign value automatically */
@@ -1214,31 +1219,31 @@
/* counter overflow */
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
"Invalid enumeration - it is not possible to auto-assign enum value for "
- "\"%s\" since the highest value is already 2147483647.", e->name);
+ "\"%s\" since the highest value is already 2147483647.", enums_p[u].name);
return LY_EVALID;
}
- e->value = value++;
+ cur_val = value++;
}
} else { /* LY_TYPE_BITS */
if (enums_p[u].flags & LYS_SET_VALUE) {
- e->value = (int32_t)enums_p[u].value;
- if (!u || ((uint32_t)e->value >= position)) {
- position = (uint32_t)e->value + 1;
+ cur_pos = (uint32_t)enums_p[u].value;
+ if (!u || (cur_pos >= position)) {
+ position = cur_pos + 1;
}
/* check collision with other values */
- for (v = 0; v < LY_ARRAY_COUNT(*enums) - 1; ++v) {
- if (e->value == (*enums)[v].value) {
+ LY_ARRAY_FOR(*enums, v) {
+ if (cur_pos == (*enums)[v].position) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
"Invalid bits - position %u collide in items \"%s\" and \"%s\".",
- (uint32_t)e->value, e->name, (*enums)[v].name);
+ cur_pos, enums_p[u].name, (*enums)[v].name);
return LY_EVALID;
}
}
} else if (base_enums) {
/* inherit the assigned value */
- e->value = base_enums[match].value;
- if (!u || ((uint32_t)e->value >= position)) {
- position = (uint32_t)e->value + 1;
+ cur_pos = base_enums[match].position;
+ if (!u || (cur_pos >= position)) {
+ position = cur_pos + 1;
}
} else {
/* assign value automatically */
@@ -1246,31 +1251,51 @@
/* counter overflow */
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
"Invalid bits - it is not possible to auto-assign bit position for "
- "\"%s\" since the highest value is already 4294967295.", e->name);
+ "\"%s\" since the highest value is already 4294967295.", enums_p[u].name);
return LY_EVALID;
}
- e->value = position++;
+ cur_pos = position++;
}
}
+ /* the assigned values must not change from the derived type */
if (base_enums) {
- /* the assigned values must not change from the derived type */
- if (e->value != base_enums[match].value) {
- if (basetype == LY_TYPE_ENUM) {
+ if (basetype == LY_TYPE_ENUM) {
+ if (cur_val != base_enums[match].value) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
"Invalid enumeration - value of the item \"%s\" has changed from %d to %d in the derived type.",
- e->name, base_enums[match].value, e->value);
- } else {
+ enums_p[u].name, base_enums[match].value, cur_val);
+ return LY_EVALID;
+ }
+ } else {
+ if (cur_pos != base_enums[match].position) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
"Invalid bits - position of the item \"%s\" has changed from %u to %u in the derived type.",
- e->name, (uint32_t)base_enums[match].value, (uint32_t)e->value);
+ enums_p[u].name, base_enums[match].position, cur_pos);
+ return LY_EVALID;
}
- return LY_EVALID;
}
}
- COMPILE_ARRAY_GOTO(ctx, enums_p[u].iffeatures, e->iffeatures, v, lys_compile_iffeature, ret, done);
- COMPILE_EXTS_GOTO(ctx, enums_p[u].exts, e->exts, e, basetype == LY_TYPE_ENUM ? LYEXT_PAR_TYPE_ENUM : LYEXT_PAR_TYPE_BIT, ret, done);
+ /* evaluate if-ffeatures */
+ LY_CHECK_RET(lys_eval_iffeatures(ctx->ctx, enums_p[u].iffeatures, &enabled));
+ if (!enabled) {
+ continue;
+ }
+
+ /* add new enum/bit */
+ LY_ARRAY_NEW_RET(ctx->ctx, *enums, e, LY_EMEM);
+ DUP_STRING_GOTO(ctx->ctx, enums_p[u].name, e->name, ret, done);
+ DUP_STRING_GOTO(ctx->ctx, enums_p[u].dsc, e->dsc, ret, done);
+ DUP_STRING_GOTO(ctx->ctx, enums_p[u].ref, e->ref, ret, done);
+ e->flags = (enums_p[u].flags & LYS_FLAGS_COMPILED_MASK) | (basetype == LY_TYPE_ENUM ? LYS_ISENUM : 0);
+ if (basetype == LY_TYPE_ENUM) {
+ e->value = cur_val;
+ } else {
+ e->position = cur_pos;
+ }
+ COMPILE_EXTS_GOTO(ctx, enums_p[u].exts, e->exts, e, basetype == LY_TYPE_ENUM ? LYEXT_PAR_TYPE_ENUM :
+ LYEXT_PAR_TYPE_BIT, ret, done);
if (basetype == LY_TYPE_BITS) {
/* keep bits ordered by position */
@@ -1465,7 +1490,7 @@
}
return LY_EVALID;
}
- LY_CHECK_RET(lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases));
+ LY_CHECK_RET(lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases, NULL));
}
if (!base && !type_p->flags) {
@@ -1985,7 +2010,7 @@
parent = lysc_data_parent(parent);
}
- getnext_flags = LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCHOICE;
+ getnext_flags = LYS_GETNEXT_WITHCHOICE;
if (parent && (parent->nodetype & (LYS_RPC | LYS_ACTION)) && (exclude->flags & LYS_CONFIG_R)) {
getnext_flags |= LYS_GETNEXT_OUTPUT;
}
@@ -1999,7 +2024,7 @@
/* we must compare with both the choice and all its nested data-definiition nodes (but not recursively) */
if (iter->nodetype == LYS_CHOICE) {
iter2 = NULL;
- while ((iter2 = lys_getnext(iter2, iter, NULL, LYS_GETNEXT_NOSTATECHECK))) {
+ while ((iter2 = lys_getnext(iter2, iter, NULL, 0))) {
if (CHECK_NODE(iter2, exclude, name)) {
goto error;
}
@@ -2037,7 +2062,7 @@
struct lysp_node *child_p, *dev_pnode = NULL, *dev_input_p = NULL, *dev_output_p = NULL;
struct lysp_action_inout *inout_p;
LY_ARRAY_COUNT_TYPE u;
- ly_bool not_supported;
+ ly_bool not_supported, enabled;
uint32_t opt_prev = ctx->options;
lysc_update_path(ctx, parent, action_p->name);
@@ -2046,7 +2071,8 @@
LY_CHECK_RET(lys_compile_node_deviations_refines(ctx, (struct lysp_node *)action_p, parent, &dev_pnode, ¬_supported));
if (not_supported) {
lysc_update_path(ctx, NULL, NULL);
- return LY_EDENIED;
+ ret = LY_EDENIED;
+ goto cleanup;
} else if (dev_pnode) {
action_p = (struct lysp_action *)dev_pnode;
}
@@ -2056,35 +2082,43 @@
action->module = ctx->cur_mod;
action->parent = parent;
- LY_CHECK_RET(lys_compile_node_uniqness(ctx, parent, action_p->name, (struct lysc_node *)action));
+ LY_CHECK_GOTO(ret = lys_compile_node_uniqness(ctx, parent, action_p->name, (struct lysc_node *)action), cleanup);
if (ctx->options & (LYS_COMPILE_RPC_MASK | LYS_COMPILE_NOTIFICATION)) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
"Action \"%s\" is placed inside %s.", action_p->name,
ctx->options & LYS_COMPILE_RPC_MASK ? "another RPC/action" : "notification");
- return LY_EVALID;
+ ret = LY_EVALID;
+ goto cleanup;
}
action->flags = action_p->flags & LYS_FLAGS_COMPILED_MASK;
+ /* if-features */
+ LY_CHECK_RET(lys_eval_iffeatures(ctx->ctx, action_p->iffeatures, &enabled));
+ if (!enabled && !(ctx->options & LYS_COMPILE_DISABLED)) {
+ ly_set_add(&ctx->disabled, action, 1, NULL);
+ ctx->options |= LYS_COMPILE_DISABLED;
+ }
+
/* status - it is not inherited by specification, but it does not make sense to have
* current in deprecated or deprecated in obsolete, so we do print warning and inherit status */
- LY_CHECK_RET(lys_compile_status(ctx, &action->flags, uses_status ? uses_status : (parent ? parent->flags : 0)));
+ ret = lys_compile_status(ctx, &action->flags, uses_status ? uses_status : (parent ? parent->flags : 0));
+ LY_CHECK_GOTO(ret, cleanup);
DUP_STRING_GOTO(ctx->ctx, action_p->name, action->name, ret, cleanup);
DUP_STRING_GOTO(ctx->ctx, action_p->dsc, action->dsc, ret, cleanup);
DUP_STRING_GOTO(ctx->ctx, action_p->ref, action->ref, ret, cleanup);
- COMPILE_ARRAY_GOTO(ctx, action_p->iffeatures, action->iffeatures, u, lys_compile_iffeature, ret, cleanup);
/* connect any action augments */
- LY_CHECK_RET(lys_compile_node_augments(ctx, (struct lysc_node *)action));
+ LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, (struct lysc_node *)action), cleanup);
/* input */
lysc_update_path(ctx, (struct lysc_node *)action, "input");
/* apply deviations on input */
- LY_CHECK_RET(lys_compile_node_deviations_refines(ctx, (struct lysp_node *)&action_p->input, (struct lysc_node *)action,
- &dev_input_p, ¬_supported));
+ LY_CHECK_GOTO(ret = lys_compile_node_deviations_refines(ctx, (struct lysp_node *)&action_p->input,
+ (struct lysc_node *)action, &dev_input_p, ¬_supported), cleanup);
if (not_supported) {
inout_p = NULL;
} else if (dev_input_p) {
@@ -2100,10 +2134,10 @@
ctx->options |= LYS_COMPILE_RPC_INPUT;
/* connect any input augments */
- LY_CHECK_RET(lys_compile_node_augments(ctx, (struct lysc_node *)&action->input));
+ LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, (struct lysc_node *)&action->input), cleanup);
LY_LIST_FOR(inout_p->data, child_p) {
- LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node *)action, uses_status, NULL));
+ LY_CHECK_GOTO(ret = lys_compile_node(ctx, child_p, (struct lysc_node *)action, uses_status, NULL), cleanup);
}
ctx->options = opt_prev;
}
@@ -2114,8 +2148,8 @@
lysc_update_path(ctx, (struct lysc_node *)action, "output");
/* apply deviations on output */
- LY_CHECK_RET(lys_compile_node_deviations_refines(ctx, (struct lysp_node *)&action_p->output, (struct lysc_node *)action,
- &dev_output_p, ¬_supported));
+ LY_CHECK_GOTO(ret = lys_compile_node_deviations_refines(ctx, (struct lysp_node *)&action_p->output,
+ (struct lysc_node *)action, &dev_output_p, ¬_supported), cleanup);
if (not_supported) {
inout_p = NULL;
} else if (dev_output_p) {
@@ -2131,10 +2165,10 @@
ctx->options |= LYS_COMPILE_RPC_OUTPUT;
/* connect any output augments */
- LY_CHECK_RET(lys_compile_node_augments(ctx, (struct lysc_node *)&action->output));
+ LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, (struct lysc_node *)&action->output), cleanup);
LY_LIST_FOR(inout_p->data, child_p) {
- LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node *)action, uses_status, NULL));
+ LY_CHECK_GOTO(ret = lys_compile_node(ctx, child_p, (struct lysc_node *)action, uses_status, NULL), cleanup);
}
ctx->options = opt_prev;
}
@@ -2144,7 +2178,7 @@
/* wait with extensions compilation until all the children are compiled */
COMPILE_EXTS_GOTO(ctx, action_p->exts, action->exts, action, LYEXT_PAR_NODE, ret, cleanup);
- if ((action->input.musts || action->output.musts) && !(ctx->options & LYS_COMPILE_GROUPING)) {
+ if ((action->input.musts || action->output.musts) && !(ctx->options & (LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
/* do not check "must" semantics in a grouping */
ret = ly_set_add(&ctx->xpath, action, 0, NULL);
LY_CHECK_GOTO(ret, cleanup);
@@ -2167,7 +2201,7 @@
LY_ERR ret = LY_SUCCESS;
struct lysp_node *child_p, *dev_pnode = NULL;
LY_ARRAY_COUNT_TYPE u;
- ly_bool not_supported;
+ ly_bool not_supported, enabled;
uint32_t opt_prev = ctx->options;
lysc_update_path(ctx, parent, notif_p->name);
@@ -2175,7 +2209,8 @@
LY_CHECK_RET(lys_compile_node_deviations_refines(ctx, (struct lysp_node *)notif_p, parent, &dev_pnode, ¬_supported));
if (not_supported) {
lysc_update_path(ctx, NULL, NULL);
- return LY_EDENIED;
+ ret = LY_EDENIED;
+ goto cleanup;
} else if (dev_pnode) {
notif_p = (struct lysp_notif *)dev_pnode;
}
@@ -2185,17 +2220,25 @@
notif->module = ctx->cur_mod;
notif->parent = parent;
- LY_CHECK_RET(lys_compile_node_uniqness(ctx, parent, notif_p->name, (struct lysc_node *)notif));
+ LY_CHECK_GOTO(ret = lys_compile_node_uniqness(ctx, parent, notif_p->name, (struct lysc_node *)notif), cleanup);
if (ctx->options & (LYS_COMPILE_RPC_MASK | LYS_COMPILE_NOTIFICATION)) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
"Notification \"%s\" is placed inside %s.", notif_p->name,
ctx->options & LYS_COMPILE_RPC_MASK ? "RPC/action" : "another notification");
- return LY_EVALID;
+ ret = LY_EVALID;
+ goto cleanup;
}
notif->flags = notif_p->flags & LYS_FLAGS_COMPILED_MASK;
+ /* if-features */
+ LY_CHECK_RET(lys_eval_iffeatures(ctx->ctx, notif_p->iffeatures, &enabled));
+ if (!enabled && !(ctx->options & LYS_COMPILE_DISABLED)) {
+ ly_set_add(&ctx->disabled, notif, 1, NULL);
+ ctx->options |= LYS_COMPILE_DISABLED;
+ }
+
/* status - it is not inherited by specification, but it does not make sense to have
* current in deprecated or deprecated in obsolete, so we do print warning and inherit status */
ret = lys_compile_status(ctx, ¬if->flags, uses_status ? uses_status : (parent ? parent->flags : 0));
@@ -2204,9 +2247,8 @@
DUP_STRING_GOTO(ctx->ctx, notif_p->name, notif->name, ret, cleanup);
DUP_STRING_GOTO(ctx->ctx, notif_p->dsc, notif->dsc, ret, cleanup);
DUP_STRING_GOTO(ctx->ctx, notif_p->ref, notif->ref, ret, cleanup);
- COMPILE_ARRAY_GOTO(ctx, notif_p->iffeatures, notif->iffeatures, u, lys_compile_iffeature, ret, cleanup);
COMPILE_ARRAY_GOTO(ctx, notif_p->musts, notif->musts, u, lys_compile_must, ret, cleanup);
- if (notif_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+ if (notif_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
/* do not check "must" semantics in a grouping */
ret = ly_set_add(&ctx->xpath, notif, 0, NULL);
LY_CHECK_GOTO(ret, cleanup);
@@ -2215,7 +2257,7 @@
ctx->options |= LYS_COMPILE_NOTIFICATION;
/* connect any notification augments */
- LY_CHECK_RET(lys_compile_node_augments(ctx, (struct lysc_node *)notif));
+ LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, (struct lysc_node *)notif), cleanup);
LY_LIST_FOR(notif_p->data, child_p) {
ret = lys_compile_node(ctx, child_p, (struct lysc_node *)notif, uses_status, NULL);
@@ -2287,7 +2329,7 @@
}
COMPILE_ARRAY_GOTO(ctx, cont_p->musts, cont->musts, u, lys_compile_must, ret, done);
- if (cont_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+ if (cont_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
/* do not check "must" semantics in a grouping */
ret = ly_set_add(&ctx->xpath, cont, 0, NULL);
LY_CHECK_GOTO(ret, done);
@@ -2321,10 +2363,10 @@
LY_CHECK_RET(lysc_unres_leaf_dflt_add(ctx, leaf, dflt));
}
- if (leaf->type->basetype == LY_TYPE_LEAFREF) {
+ if ((leaf->type->basetype == LY_TYPE_LEAFREF) && !(ctx->options & LYS_COMPILE_DISABLED)) {
/* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
LY_CHECK_RET(ly_set_add(&ctx->leafrefs, leaf, 0, NULL));
- } else if (leaf->type->basetype == LY_TYPE_UNION) {
+ } else if ((leaf->type->basetype == LY_TYPE_UNION) && !(ctx->options & LYS_COMPILE_DISABLED)) {
LY_ARRAY_COUNT_TYPE u;
LY_ARRAY_FOR(((struct lysc_type_union *)leaf->type)->types, u) {
if (((struct lysc_type_union *)leaf->type)->types[u]->basetype == LY_TYPE_LEAFREF) {
@@ -2360,7 +2402,7 @@
LY_ERR ret = LY_SUCCESS;
COMPILE_ARRAY_GOTO(ctx, leaf_p->musts, leaf->musts, u, lys_compile_must, ret, done);
- if (leaf_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+ if (leaf_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
/* do not check "must" semantics in a grouping */
ret = ly_set_add(&ctx->xpath, leaf, 0, NULL);
LY_CHECK_GOTO(ret, done);
@@ -2408,7 +2450,7 @@
LY_ERR ret = LY_SUCCESS;
COMPILE_ARRAY_GOTO(ctx, llist_p->musts, llist->musts, u, lys_compile_must, ret, done);
- if (llist_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+ if (llist_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
/* do not check "must" semantics in a grouping */
ret = ly_set_add(&ctx->xpath, llist, 0, NULL);
LY_CHECK_GOTO(ret, done);
@@ -2550,7 +2592,7 @@
}
} else {
ctx_node = lys_find_child(ctx_node, mod, name, name_len, 0,
- getnext_extra_flag | LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE);
+ getnext_extra_flag | LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE);
getnext_extra_flag = 0;
}
if (!ctx_node) {
@@ -2714,7 +2756,7 @@
}
COMPILE_ARRAY_GOTO(ctx, list_p->musts, list->musts, u, lys_compile_must, ret, done);
- if (list_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+ if (list_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
/* do not check "must" semantics in a grouping */
LY_CHECK_RET(ly_set_add(&ctx->xpath, list, 0, NULL));
}
@@ -2743,17 +2785,15 @@
}
/* key node must be present */
- key = (struct lysc_node_leaf *)lys_find_child(node, node->module, keystr, len, LYS_LEAF, LYS_GETNEXT_NOCHOICE | LYS_GETNEXT_NOSTATECHECK);
- if (!(key)) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "The list's key \"%.*s\" not found.", len, keystr);
+ key = (struct lysc_node_leaf *)lys_find_child(node, node->module, keystr, len, LYS_LEAF, LYS_GETNEXT_NOCHOICE);
+ if (!key) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, "The list's key \"%.*s\" not found.", len, keystr);
return LY_EVALID;
}
/* keys must be unique */
if (key->flags & LYS_KEY) {
/* the node was already marked as a key */
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "Duplicated key identifier \"%.*s\".", len, keystr);
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Duplicated key identifier \"%.*s\".", len, keystr);
return LY_EVALID;
}
@@ -2777,11 +2817,7 @@
"List's key must not have any \"when\" statement.");
return LY_EVALID;
}
- if (key->iffeatures) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "List's key must not have any \"if-feature\" statement.");
- return LY_EVALID;
- }
+ /* TODO check key, it cannot have any if-features */
}
/* check status */
@@ -2898,7 +2934,7 @@
mod = node->module;
}
- ch->dflt = (struct lysc_node_case *)lys_find_child(node, mod, name, 0, LYS_CASE, LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCASE);
+ ch->dflt = (struct lysc_node_case *)lys_find_child(node, mod, name, 0, LYS_CASE, LYS_GETNEXT_WITHCASE);
if (!ch->dflt) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
"Default case \"%s\" not found.", dflt->str);
@@ -3008,7 +3044,7 @@
LY_ERR ret = LY_SUCCESS;
COMPILE_ARRAY_GOTO(ctx, any_p->musts, any->musts, u, lys_compile_must, ret, done);
- if (any_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+ if (any_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
/* do not check "must" semantics in a grouping */
ret = ly_set_add(&ctx->xpath, any, 0, NULL);
LY_CHECK_GOTO(ret, done);
@@ -3511,7 +3547,7 @@
.parent = NULL, .next = NULL,
.prev = (struct lysc_node *)&fake_container,
.name = "fake",
- .dsc = NULL, .ref = NULL, .exts = NULL, .iffeatures = NULL, .when = NULL,
+ .dsc = NULL, .ref = NULL, .exts = NULL, .when = NULL,
.child = NULL, .musts = NULL, .actions = NULL, .notifs = NULL
};
@@ -3603,8 +3639,8 @@
LY_ERR ret = LY_SUCCESS;
struct lysc_node *node = NULL;
struct lysp_node *dev_pnode = NULL;
- LY_ARRAY_COUNT_TYPE u;
- ly_bool not_supported;
+ ly_bool not_supported, enabled;
+ uint32_t prev_opts = ctx->options;
LY_ERR (*node_compile_spec)(struct lysc_ctx *, struct lysp_node *, struct lysc_node *);
@@ -3657,12 +3693,11 @@
LY_CHECK_ERR_RET(!node, LOGMEM(ctx->ctx), LY_EMEM);
/* compile any deviations for this node */
- LY_CHECK_ERR_RET(ret = lys_compile_node_deviations_refines(ctx, pnode, parent, &dev_pnode, ¬_supported),
- free(node), ret);
+ LY_CHECK_ERR_GOTO(ret = lys_compile_node_deviations_refines(ctx, pnode, parent, &dev_pnode, ¬_supported),
+ free(node), cleanup);
if (not_supported) {
free(node);
- lysc_update_path(ctx, NULL, NULL);
- return LY_SUCCESS;
+ goto cleanup;
} else if (dev_pnode) {
pnode = dev_pnode;
}
@@ -3672,6 +3707,13 @@
node->prev = node;
node->flags = pnode->flags & LYS_FLAGS_COMPILED_MASK;
+ /* if-features */
+ LY_CHECK_ERR_RET(ret = lys_eval_iffeatures(ctx->ctx, pnode->iffeatures, &enabled), free(node), ret);
+ if (!enabled && !(ctx->options & LYS_COMPILE_DISABLED)) {
+ ly_set_add(&ctx->disabled, node, 1, NULL);
+ ctx->options |= LYS_COMPILE_DISABLED;
+ }
+
/* config */
ret = lys_compile_config(ctx, node, parent);
LY_CHECK_GOTO(ret, error);
@@ -3697,7 +3739,6 @@
DUP_STRING_GOTO(ctx->ctx, pnode->name, node->name, ret, error);
DUP_STRING_GOTO(ctx->ctx, pnode->dsc, node->dsc, ret, error);
DUP_STRING_GOTO(ctx->ctx, pnode->ref, node->ref, ret, error);
- COMPILE_ARRAY_GOTO(ctx, pnode->iffeatures, node->iffeatures, u, lys_compile_iffeature, ret, error);
/* insert into parent's children/compiled module (we can no longer free the node separately on error) */
LY_CHECK_GOTO(ret = lys_compile_node_connect(ctx, parent, node), cleanup);
@@ -3726,16 +3767,16 @@
LY_CHECK_GOTO(ret = ly_set_add(child_set, node, 1, NULL), cleanup);
}
- lysc_update_path(ctx, NULL, NULL);
- lysp_dev_node_free(ctx->ctx, dev_pnode);
- return LY_SUCCESS;
+ goto cleanup;
error:
- lysc_node_free(ctx->ctx, node);
+ lysc_node_free(ctx->ctx, node, 0);
cleanup:
- if (dev_pnode) {
+ ctx->options = prev_opts;
+ if (ret && dev_pnode) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_OTHER, "Compilation of a deviated and/or refined node failed.");
- lysp_dev_node_free(ctx->ctx, dev_pnode);
}
+ lysp_dev_node_free(ctx->ctx, dev_pnode);
+ lysc_update_path(ctx, NULL, NULL);
return ret;
}
diff --git a/src/schema_features.c b/src/schema_features.c
new file mode 100644
index 0000000..4e1b6aa
--- /dev/null
+++ b/src/schema_features.c
@@ -0,0 +1,657 @@
+/**
+ * @file schema_features.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema feature handling
+ *
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "schema_features.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 "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "log.h"
+#include "in_internal.h"
+#include "parser_internal.h"
+#include "parser_schema.h"
+#include "path.h"
+#include "schema_compile.h"
+#include "schema_compile_amend.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+uint8_t
+lysc_iff_getop(uint8_t *list, size_t pos)
+{
+ uint8_t *item;
+ uint8_t mask = 3, result;
+
+ item = &list[pos / 4];
+ result = (*item) & (mask << 2 * (pos % 4));
+ return result >> 2 * (pos % 4);
+}
+
+static LY_ERR
+lysc_iffeature_value_(const struct lysc_iffeature *iff, size_t *index_e, size_t *index_f)
+{
+ uint8_t op;
+ LY_ERR a, b;
+
+ op = lysc_iff_getop(iff->expr, *index_e);
+ (*index_e)++;
+
+ switch (op) {
+ case LYS_IFF_F:
+ /* resolve feature */
+ return (iff->features[(*index_f)++]->flags & LYS_FENABLED) ? LY_SUCCESS : LY_ENOT;
+ case LYS_IFF_NOT:
+ /* invert result */
+ return lysc_iffeature_value_(iff, index_e, index_f) == LY_SUCCESS ? LY_ENOT : LY_SUCCESS;
+ case LYS_IFF_AND:
+ case LYS_IFF_OR:
+ a = lysc_iffeature_value_(iff, index_e, index_f);
+ b = lysc_iffeature_value_(iff, index_e, index_f);
+ if (op == LYS_IFF_AND) {
+ if ((a == LY_SUCCESS) && (b == LY_SUCCESS)) {
+ return LY_SUCCESS;
+ } else {
+ return LY_ENOT;
+ }
+ } else { /* LYS_IFF_OR */
+ if ((a == LY_SUCCESS) || (b == LY_SUCCESS)) {
+ return LY_SUCCESS;
+ } else {
+ return LY_ENOT;
+ }
+ }
+ }
+
+ return LY_ENOT;
+}
+
+API LY_ERR
+lysc_iffeature_value(const struct lysc_iffeature *iff)
+{
+ size_t index_e = 0, index_f = 0;
+
+ LY_CHECK_ARG_RET(NULL, iff, LY_EINVAL);
+
+ if (iff->expr) {
+ return lysc_iffeature_value_(iff, &index_e, &index_f);
+ }
+ return LY_ENOT;
+}
+
+API struct lysp_feature *
+lysp_feature_next(const struct lysp_feature *last, const struct lysp_module *pmod, uint32_t *idx)
+{
+ struct lysp_feature *features;
+
+ if (!*idx) {
+ /* module features */
+ features = pmod->features;
+ } else if (pmod->includes && ((*idx - 1) < LY_ARRAY_COUNT(pmod->includes))) {
+ /* submodule features */
+ features = pmod->includes[*idx - 1].submodule->features;
+ } else {
+ /* no more features */
+ return NULL;
+ }
+
+ /* get the next feature */
+ if (features && (!last || (&features[LY_ARRAY_COUNT(features) - 1] != last))) {
+ return !last ? &features[0] : (struct lysp_feature *)last + 1;
+ }
+
+ /* no more features in current (sub)module */
+ ++(*idx);
+ return lysp_feature_next(NULL, pmod, idx);
+}
+
+/**
+ * @brief Find a feature of the given name and referenced in the given module.
+ *
+ * @param[in] pmod Module where the feature was referenced (used to resolve prefix of the feature).
+ * @param[in] name Name of the feature including possible prefix.
+ * @param[in] len Length of the string representing the feature identifier in the name variable (mandatory!).
+ * @param[in] prefixed Whether the feature name can be prefixed.
+ * @return Pointer to the feature structure if found, NULL otherwise.
+ */
+static struct lysp_feature *
+lysp_feature_find(const struct lysp_module *pmod, const char *name, size_t len, ly_bool prefixed)
+{
+ const struct lys_module *mod;
+ const char *ptr;
+ struct lysp_feature *f = NULL;
+ uint32_t idx = 0;
+
+ assert(pmod);
+
+ if (prefixed && (ptr = ly_strnchr(name, ':', len))) {
+ /* we have a prefixed feature */
+ mod = ly_resolve_prefix(pmod->mod->ctx, name, ptr - name, LY_PREF_SCHEMA, (void *)pmod);
+ LY_CHECK_RET(!mod, NULL);
+
+ pmod = mod->parsed;
+ len = len - (ptr - name) - 1;
+ name = ptr + 1;
+ }
+
+ /* we have the correct module, get the feature */
+ while ((f = lysp_feature_next(f, pmod, &idx))) {
+ if (!ly_strncmp(f->name, name, len)) {
+ return f;
+ }
+ }
+
+ return NULL;
+}
+
+API LY_ERR
+lys_feature_value(const struct lys_module *module, const char *feature)
+{
+ const struct lysp_feature *f;
+
+ LY_CHECK_ARG_RET(NULL, module, module->parsed, feature, LY_EINVAL);
+
+ /* search for the specified feature */
+ f = lysp_feature_find(module->parsed, feature, strlen(feature), 0);
+ LY_CHECK_RET(!f, LY_ENOTFOUND);
+
+ /* feature disabled */
+ if (!(f->flags & LYS_FENABLED)) {
+ return LY_ENOT;
+ }
+
+ /* feature enabled */
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Stack for processing if-feature expressions.
+ */
+struct iff_stack {
+ size_t size; /**< number of items in the stack */
+ size_t index; /**< first empty item */
+ uint8_t *stack; /**< stack - array of @ref ifftokens to create the if-feature expression in prefix format */
+};
+
+/**
+ * @brief Add @ref ifftokens into the stack.
+ * @param[in] stack The if-feature stack to use.
+ * @param[in] value One of the @ref ifftokens to store in the stack.
+ * @return LY_EMEM in case of memory allocation error
+ * @return LY_ESUCCESS if the value successfully stored.
+ */
+static LY_ERR
+iff_stack_push(struct iff_stack *stack, uint8_t value)
+{
+ if (stack->index == stack->size) {
+ stack->size += 4;
+ stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
+ LY_CHECK_ERR_RET(!stack->stack, LOGMEM(NULL); stack->size = 0, LY_EMEM);
+ }
+ stack->stack[stack->index++] = value;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Get (and remove) the last item form the stack.
+ * @param[in] stack The if-feature stack to use.
+ * @return The value from the top of the stack.
+ */
+static uint8_t
+iff_stack_pop(struct iff_stack *stack)
+{
+ assert(stack && stack->index);
+
+ stack->index--;
+ return stack->stack[stack->index];
+}
+
+/**
+ * @brief Clean up the stack.
+ * @param[in] stack The if-feature stack to use.
+ */
+static void
+iff_stack_clean(struct iff_stack *stack)
+{
+ stack->size = 0;
+ free(stack->stack);
+}
+
+/**
+ * @brief Store the @ref ifftokens (@p op) on the given position in the 2bits array
+ * (libyang format of the if-feature expression).
+ * @param[in,out] list The 2bits array to modify.
+ * @param[in] op The operand (@ref ifftokens) to store.
+ * @param[in] pos Position (0-based) where to store the given @p op.
+ */
+static void
+iff_setop(uint8_t *list, uint8_t op, size_t pos)
+{
+ uint8_t *item;
+ uint8_t mask = 3;
+
+ assert(op <= 3); /* max 2 bits */
+
+ item = &list[pos / 4];
+ mask = mask << 2 * (pos % 4);
+ *item = (*item) & ~mask;
+ *item = (*item) | (op << 2 * (pos % 4));
+}
+
+#define LYS_IFF_LP 0x04 /**< Additional, temporary, value of @ref ifftokens: ( */
+#define LYS_IFF_RP 0x08 /**< Additional, temporary, value of @ref ifftokens: ) */
+
+static LY_ERR
+lys_compile_iffeature(const struct ly_ctx *ctx, struct lysp_qname *qname, struct lysc_iffeature *iff)
+{
+ LY_ERR rc = LY_SUCCESS;
+ const char *c = qname->str;
+ int64_t i, j;
+ int8_t op_len, last_not = 0, checkversion = 0;
+ LY_ARRAY_COUNT_TYPE f_size = 0, expr_size = 0, f_exp = 1;
+ uint8_t op;
+ struct iff_stack stack = {0, 0, NULL};
+ struct lysp_feature *f;
+
+ assert(c);
+
+ /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
+ for (i = j = 0; c[i]; i++) {
+ if (c[i] == '(') {
+ j++;
+ checkversion = 1;
+ continue;
+ } else if (c[i] == ')') {
+ j--;
+ continue;
+ } else if (isspace(c[i])) {
+ checkversion = 1;
+ continue;
+ }
+
+ if (!strncmp(&c[i], "not", op_len = 3) || !strncmp(&c[i], "and", op_len = 3) || !strncmp(&c[i], "or", op_len = 2)) {
+ uint64_t spaces;
+ for (spaces = 0; c[i + op_len + spaces] && isspace(c[i + op_len + spaces]); spaces++) {}
+ if (c[i + op_len + spaces] == '\0') {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - unexpected end of expression.", qname->str);
+ return LY_EVALID;
+ } else if (!isspace(c[i + op_len])) {
+ /* feature name starting with the not/and/or */
+ last_not = 0;
+ f_size++;
+ } else if (c[i] == 'n') { /* not operation */
+ if (last_not) {
+ /* double not */
+ expr_size = expr_size - 2;
+ last_not = 0;
+ } else {
+ last_not = 1;
+ }
+ } else { /* and, or */
+ if (f_exp != f_size) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - missing feature/expression before \"%.*s\" operation.",
+ qname->str, op_len, &c[i]);
+ return LY_EVALID;
+ }
+ f_exp++;
+
+ /* not a not operation */
+ last_not = 0;
+ }
+ i += op_len;
+ } else {
+ f_size++;
+ last_not = 0;
+ }
+ expr_size++;
+
+ while (!isspace(c[i])) {
+ if (!c[i] || (c[i] == ')') || (c[i] == '(')) {
+ i--;
+ break;
+ }
+ i++;
+ }
+ }
+ if (j) {
+ /* not matching count of ( and ) */
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - non-matching opening and closing parentheses.", qname->str);
+ return LY_EVALID;
+ }
+ if (f_exp != f_size) {
+ /* features do not match the needed arguments for the logical operations */
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - number of features in expression does not match "
+ "the required number of operands for the operations.", qname->str);
+ return LY_EVALID;
+ }
+
+ if (checkversion || (expr_size > 1)) {
+ /* check that we have 1.1 module */
+ if (qname->mod->version != LYS_VERSION_1_1) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", qname->str);
+ return LY_EVALID;
+ }
+ }
+
+ /* allocate the memory */
+ LY_ARRAY_CREATE_RET(ctx, iff->features, f_size, LY_EMEM);
+ iff->expr = calloc((j = (expr_size / 4) + ((expr_size % 4) ? 1 : 0)), sizeof *iff->expr);
+ stack.stack = malloc(expr_size * sizeof *stack.stack);
+ LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx); rc = LY_EMEM, error);
+
+ stack.size = expr_size;
+ f_size--; expr_size--; /* used as indexes from now */
+
+ for (i--; i >= 0; i--) {
+ if (c[i] == ')') {
+ /* push it on stack */
+ iff_stack_push(&stack, LYS_IFF_RP);
+ continue;
+ } else if (c[i] == '(') {
+ /* pop from the stack into result all operators until ) */
+ while ((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
+ iff_setop(iff->expr, op, expr_size--);
+ }
+ continue;
+ } else if (isspace(c[i])) {
+ continue;
+ }
+
+ /* end of operator or operand -> find beginning and get what is it */
+ j = i + 1;
+ while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
+ i--;
+ }
+ i++; /* go back by one step */
+
+ if (!strncmp(&c[i], "not", 3) && isspace(c[i + 3])) {
+ if (stack.index && (stack.stack[stack.index - 1] == LYS_IFF_NOT)) {
+ /* double not */
+ iff_stack_pop(&stack);
+ } else {
+ /* not has the highest priority, so do not pop from the stack
+ * as in case of AND and OR */
+ iff_stack_push(&stack, LYS_IFF_NOT);
+ }
+ } else if (!strncmp(&c[i], "and", 3) && isspace(c[i + 3])) {
+ /* as for OR - pop from the stack all operators with the same or higher
+ * priority and store them to the result, then push the AND to the stack */
+ while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
+ op = iff_stack_pop(&stack);
+ iff_setop(iff->expr, op, expr_size--);
+ }
+ iff_stack_push(&stack, LYS_IFF_AND);
+ } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
+ while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
+ op = iff_stack_pop(&stack);
+ iff_setop(iff->expr, op, expr_size--);
+ }
+ iff_stack_push(&stack, LYS_IFF_OR);
+ } else {
+ /* feature name, length is j - i */
+
+ /* add it to the expression */
+ iff_setop(iff->expr, LYS_IFF_F, expr_size--);
+
+ /* now get the link to the feature definition */
+ f = lysp_feature_find(qname->mod, &c[i], j - i, 1);
+ if (!f) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".", qname->str, j - i, &c[i]);
+ rc = LY_EVALID;
+ goto error;
+ }
+ iff->features[f_size] = f;
+ LY_ARRAY_INCREMENT(iff->features);
+ f_size--;
+ }
+ }
+ while (stack.index) {
+ op = iff_stack_pop(&stack);
+ iff_setop(iff->expr, op, expr_size--);
+ }
+
+ if (++expr_size || ++f_size) {
+ /* not all expected operators and operands found */
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - processing error.", qname->str);
+ rc = LY_EINT;
+ } else {
+ rc = LY_SUCCESS;
+ }
+
+error:
+ /* cleanup */
+ iff_stack_clean(&stack);
+
+ return rc;
+}
+
+LY_ERR
+lys_eval_iffeatures(const struct ly_ctx *ctx, struct lysp_qname *iffeatures, ly_bool *enabled)
+{
+ LY_ERR ret;
+ struct lysc_iffeature iff = {0};
+
+ if (!iffeatures) {
+ *enabled = 1;
+ return LY_SUCCESS;
+ }
+
+ LY_CHECK_RET(lys_compile_iffeature(ctx, iffeatures, &iff));
+
+ ret = lysc_iffeature_value(&iff);
+ lysc_iffeature_free((struct ly_ctx *)ctx, &iff);
+ if (ret == LY_ENOT) {
+ *enabled = 0;
+ } else if (ret) {
+ return ret;
+ } else {
+ *enabled = 1;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_enable_features(struct lysp_module *pmod, const char **features)
+{
+ uint32_t i;
+ struct lysp_feature *f;
+ LY_ERR ret;
+
+ if (!features) {
+ /* keep all features disabled */
+ return LY_SUCCESS;
+ }
+
+ if (!strcmp(features[0], "*")) {
+ /* enable all features */
+ f = NULL;
+ i = 0;
+ while ((f = lysp_feature_next(f, pmod, &i))) {
+ f->flags |= LYS_FENABLED;
+ }
+ } else {
+ /* enable selected features */
+ for (i = 0; features[i]; ++i) {
+ /* find the feature */
+ f = lysp_feature_find(pmod, features[i], strlen(features[i]), 0);
+ if (!f) {
+ LOGERR(pmod->mod->ctx, LY_ENOTFOUND, "Feature \"%s\" not found in module \"%s\".", features[i],
+ pmod->mod->name);
+ return LY_ENOTFOUND;
+ }
+
+ /* enable feature */
+ f->flags |= LYS_FENABLED;
+ }
+ }
+
+check_iffeature:
+ /* check whether all enabled features have their if-features satisfied */
+ f = NULL;
+ i = 0;
+ while ((f = lysp_feature_next(f, pmod, &i))) {
+ if (!(f->flags & LYS_FENABLED) || !f->iffeatures) {
+ /* disabled feature or no if-features to check */
+ continue;
+ }
+
+ assert(f->iffeatures_c);
+ ret = lysc_iffeature_value(f->iffeatures_c);
+ if (ret == LY_ENOT) {
+ LOGWRN(pmod->mod->ctx, "Feature \"%s\" cannot be enabled because its \"if-feature\" is not satisfied.",
+ f->name);
+
+ /* disable feature and re-evaluate all the feature if-features again */
+ f->flags &= ~LYS_FENABLED;
+ goto check_iffeature;
+ } else if (ret) {
+ return ret;
+ } /* else if-feature satisfied */
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check circular dependency of features - feature MUST NOT reference itself (via their if-feature statement).
+ *
+ * The function works in the same way as lys_compile_identity_circular_check() with different structures and error messages.
+ *
+ * @param[in] ctx Compile context for logging.
+ * @param[in] feature The feature referenced in if-feature statement (its depfeatures list is being extended by the feature
+ * being currently processed).
+ * @param[in] depfeatures The list of depending features of the feature being currently processed (not the one provided as @p feature)
+ * @return LY_SUCCESS if everything is ok.
+ * @return LY_EVALID if the feature references indirectly itself.
+ */
+static LY_ERR
+lys_compile_feature_circular_check(const struct ly_ctx *ctx, struct lysp_feature *feature, struct lysp_feature **depfeatures)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct ly_set recursion = {0};
+ struct lysp_feature *drv;
+
+ if (!depfeatures) {
+ return LY_SUCCESS;
+ }
+
+ for (u = 0; u < LY_ARRAY_COUNT(depfeatures); ++u) {
+ if (feature == depfeatures[u]) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Feature \"%s\" is indirectly referenced from itself.",
+ feature->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ ret = ly_set_add(&recursion, depfeatures[u], 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ for (v = 0; v < recursion.count; ++v) {
+ drv = recursion.objs[v];
+ if (!drv->depfeatures) {
+ continue;
+ }
+ for (u = 0; u < LY_ARRAY_COUNT(drv->depfeatures); ++u) {
+ if (feature == drv->depfeatures[u]) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Feature \"%s\" is indirectly referenced from itself.",
+ feature->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ ly_set_add(&recursion, drv->depfeatures[u], 0, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ ly_set_erase(&recursion, NULL);
+ return ret;
+}
+
+LY_ERR
+lys_compile_feature_iffeatures(struct lysp_module *pmod)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lysp_feature *f = NULL, **df;
+ uint32_t idx = 0;
+
+ while ((f = lysp_feature_next(f, pmod, &idx))) {
+ if (!f->iffeatures) {
+ continue;
+ }
+
+ /* compile if-features */
+ LY_ARRAY_CREATE_RET(pmod->mod->ctx, f->iffeatures_c, LY_ARRAY_COUNT(f->iffeatures), LY_EMEM);
+ LY_ARRAY_FOR(f->iffeatures, u) {
+ LY_ARRAY_INCREMENT(f->iffeatures_c);
+ LY_CHECK_RET(lys_compile_iffeature(pmod->mod->ctx, &(f->iffeatures)[u], &(f->iffeatures_c)[u]));
+ }
+ LY_ARRAY_FOR(f->iffeatures_c, u) {
+ LY_ARRAY_FOR(f->iffeatures_c[u].features, v) {
+ /* check for circular dependency - direct reference first,... */
+ if (f == f->iffeatures_c[u].features[v]) {
+ LOGVAL(pmod->mod->ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Feature \"%s\" is referenced from itself.",
+ f->name);
+ return LY_EVALID;
+ }
+ /* ... and indirect circular reference */
+ LY_CHECK_RET(lys_compile_feature_circular_check(pmod->mod->ctx, f->iffeatures_c[u].features[v], f->depfeatures));
+
+ /* add itself into the dependants list */
+ LY_ARRAY_NEW_RET(pmod->mod->ctx, f->iffeatures_c[u].features[v]->depfeatures, df, LY_EMEM);
+ *df = f;
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+void
+lys_free_feature_iffeatures(struct lysp_module *pmod)
+{
+ struct lysp_feature *f = NULL;
+ uint32_t idx = 0;
+
+ while ((f = lysp_feature_next(f, pmod, &idx))) {
+ FREE_ARRAY(pmod->mod->ctx, f->iffeatures_c, lysc_iffeature_free);
+ f->iffeatures_c = NULL;
+ LY_ARRAY_FREE(f->depfeatures);
+ f->depfeatures = NULL;
+ }
+}
diff --git a/src/schema_features.h b/src/schema_features.h
new file mode 100644
index 0000000..e6da709
--- /dev/null
+++ b/src/schema_features.h
@@ -0,0 +1,61 @@
+/**
+ * @file schema_features.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Header for schema features.
+ *
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_SCHEMA_FEATURES_H_
+#define LY_SCHEMA_FEATURES_H_
+
+#include "log.h"
+
+struct lysp_module;
+struct lys_parser_ctx;
+struct lysp_qname;
+
+/**
+ * @brief Evaluate if-features array.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] iffeatures Sized array of if-features to evaluate.
+ * @param[out] enabled Whether if-features evaluated to true or false.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_eval_iffeatures(const struct ly_ctx *ctx, struct lysp_qname *iffeatures, ly_bool *enabled);
+
+/**
+ * @brief Enable features in a parsed module with their consolidation and checking that they really
+ * can be enabled and have all their if-features true.
+ *
+ * @param[in] pmod Parsed module to modify.
+ * @param[in] features Array of features ended with NULL to enable. NULL for all features disabled, '*' for all enabled
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_enable_features(struct lysp_module *pmod, const char **features);
+
+/**
+ * @brief Compile if-features of features in the current module and all its submodules.
+ *
+ * @param[in] ctx Compile context.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_feature_iffeatures(struct lysp_module *pmod);
+
+/**
+ * @brief Free all auxiliary if-feature structures in a parsed module used for compilation.
+ *
+ * @param[in] pmod Module to update.
+ */
+void lys_free_feature_iffeatures(struct lysp_module *pmod);
+
+#endif /* LY_SCHEMA_FEATURES_H_ */
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 45312ec..03ca6f8 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -17,6 +17,7 @@
#include "tree_schema.h"
#include <assert.h>
+#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdint.h>
@@ -37,6 +38,7 @@
#include "path.h"
#include "schema_compile.h"
#include "schema_compile_amend.h"
+#include "schema_features.h"
#include "set.h"
#include "tree.h"
#include "tree_schema_internal.h"
@@ -264,13 +266,6 @@
return NULL;
}
- if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
- /* check if the node is disabled by if-feature */
- if (lysc_node_is_disabled(next, 0)) {
- next = next->next;
- goto repeat;
- }
- }
return next;
}
@@ -553,338 +548,6 @@
}
API LY_ERR
-lysc_feature_value(const struct lysc_feature *feature)
-{
- LY_CHECK_ARG_RET(NULL, feature, LY_EINVAL);
- return feature->flags & LYS_FENABLED ? LY_SUCCESS : LY_ENOT;
-}
-
-uint8_t
-lysc_iff_getop(uint8_t *list, size_t pos)
-{
- uint8_t *item;
- uint8_t mask = 3, result;
-
- item = &list[pos / 4];
- result = (*item) & (mask << 2 * (pos % 4));
- return result >> 2 * (pos % 4);
-}
-
-static LY_ERR
-lysc_iffeature_value_(const struct lysc_iffeature *iff, size_t *index_e, size_t *index_f)
-{
- uint8_t op;
- LY_ERR a, b;
-
- op = lysc_iff_getop(iff->expr, *index_e);
- (*index_e)++;
-
- switch (op) {
- case LYS_IFF_F:
- /* resolve feature */
- return lysc_feature_value(iff->features[(*index_f)++]);
- case LYS_IFF_NOT:
- /* invert result */
- return lysc_iffeature_value_(iff, index_e, index_f) == LY_SUCCESS ? LY_ENOT : LY_SUCCESS;
- case LYS_IFF_AND:
- case LYS_IFF_OR:
- a = lysc_iffeature_value_(iff, index_e, index_f);
- b = lysc_iffeature_value_(iff, index_e, index_f);
- if (op == LYS_IFF_AND) {
- if ((a == LY_SUCCESS) && (b == LY_SUCCESS)) {
- return LY_SUCCESS;
- } else {
- return LY_ENOT;
- }
- } else { /* LYS_IFF_OR */
- if ((a == LY_SUCCESS) || (b == LY_SUCCESS)) {
- return LY_SUCCESS;
- } else {
- return LY_ENOT;
- }
- }
- }
-
- return 0;
-}
-
-API LY_ERR
-lysc_iffeature_value(const struct lysc_iffeature *iff)
-{
- size_t index_e = 0, index_f = 0;
-
- LY_CHECK_ARG_RET(NULL, iff, -1);
-
- if (iff->expr) {
- return lysc_iffeature_value_(iff, &index_e, &index_f);
- }
- return 0;
-}
-
-/**
- * @brief Enable/Disable the specified feature in the module.
- *
- * If the feature is already set to the desired value, LY_SUCCESS is returned.
- * By changing the feature, also all the feature which depends on it via their
- * if-feature statements are again evaluated (disabled if a if-feature statement
- * evaluates to false).
- *
- * @param[in] mod Module where to set (search for) the feature.
- * @param[in] name Name of the feature to set. Asterisk ('*') can be used to
- * set all the features in the module.
- * @param[in] value Desired value of the feature: 1 (enable) or 0 (disable).
- * @param[in] skip_checks Flag to skip checking of if-features and just set @p value of the feature.
- * @return LY_ERR value.
- */
-static LY_ERR
-lys_feature_change(const struct lys_module *mod, const char *name, ly_bool value, ly_bool skip_checks)
-{
- LY_ERR ret = LY_SUCCESS;
- ly_bool all = 0;
- LY_ARRAY_COUNT_TYPE u, disabled_count;
- uint32_t changed_count;
- struct lysc_feature *f, **df;
- struct lysc_iffeature *iff;
- struct ly_set *changed;
- struct ly_ctx *ctx = mod->ctx; /* shortcut */
-
- if (!strcmp(name, "*")) {
- /* enable all */
- all = 1;
- }
-
- if (!mod->implemented) {
- LOGERR(ctx, LY_EINVAL, "Module \"%s\" is not implemented so all its features are permanently disabled.", mod->name);
- return LY_EINVAL;
- }
- if (!mod->features) {
- if (all) {
- /* no feature to enable */
- return LY_SUCCESS;
- }
- LOGERR(ctx, LY_EINVAL, "Unable to switch feature since the module \"%s\" has no features.", mod->name);
- return LY_ENOTFOUND;
- }
-
- LY_CHECK_RET(ly_set_new(&changed));
- changed_count = 0;
-
-run:
- for (disabled_count = u = 0; u < LY_ARRAY_COUNT(mod->features); ++u) {
- f = &mod->features[u];
- if (all || !strcmp(f->name, name)) {
- if ((value && (f->flags & LYS_FENABLED)) || (!value && !(f->flags & LYS_FENABLED))) {
- if (all) {
- /* skip already set features */
- continue;
- } else {
- /* feature already set correctly */
- goto cleanup;
- }
- }
-
- if (value) { /* enable */
- if (!skip_checks) {
- /* check referenced features if they are enabled */
- LY_ARRAY_FOR(f->iffeatures, struct lysc_iffeature, iff) {
- if (lysc_iffeature_value(iff) == LY_ENOT) {
- if (all) {
- ++disabled_count;
- goto next;
- } else {
- LOGERR(ctx, LY_EDENIED, "Feature \"%s\" cannot be enabled since it is disabled by "
- "its if-feature condition(s).", f->name);
- ret = LY_EDENIED;
- goto cleanup;
- }
- }
- }
- }
- /* enable the feature */
- f->flags |= LYS_FENABLED;
- } else { /* disable */
- /* disable the feature */
- f->flags &= ~LYS_FENABLED;
- }
-
- /* remember the changed feature */
- ret = ly_set_add(changed, f, 1, NULL);
- LY_CHECK_GOTO(ret, cleanup);
-
- if (!all) {
- /* stop in case changing a single feature */
- break;
- }
- }
-next:
- ;
- }
-
- if (!all && !changed->count) {
- LOGERR(ctx, LY_EINVAL, "Feature \"%s\" not found in module \"%s\".", name, mod->name);
- ret = LY_ENOTFOUND;
- goto cleanup;
- }
-
- if (value && all && disabled_count) {
- if (changed_count == changed->count) {
- /* no change in last run -> not able to enable all ... */
- /* ... print errors */
- for (u = 0; disabled_count && u < LY_ARRAY_COUNT(mod->features); ++u) {
- if (!(mod->features[u].flags & LYS_FENABLED)) {
- LOGERR(ctx, LY_EDENIED, "Feature \"%s\" cannot be enabled since it is disabled by its if-feature "
- "condition(s).", mod->features[u].name);
- --disabled_count;
- }
- }
- /* ... restore the original state */
- for (u = 0; u < changed->count; ++u) {
- f = changed->objs[u];
- /* re-disable the feature */
- f->flags &= ~LYS_FENABLED;
- }
-
- ret = LY_EDENIED;
- goto cleanup;
- } else {
- /* we did some change in last run, try it again */
- changed_count = changed->count;
- goto run;
- }
- }
-
- /* reflect change(s) in the dependent features */
- for (u = 0; !skip_checks && (u < changed->count); ++u) {
- /* If a dependent feature is enabled, it can be now changed by the change (to false) of the value of
- * its if-feature statements. The reverse logic, automatically enable feature when its feature is enabled
- * is not done - by default, features are disabled and must be explicitely enabled. */
- f = changed->objs[u];
- LY_ARRAY_FOR(f->depfeatures, struct lysc_feature *, df) {
- if (!((*df)->flags & LYS_FENABLED)) {
- /* not enabled, nothing to do */
- continue;
- }
- /* check the feature's if-features which could change by the previous change of our feature */
- LY_ARRAY_FOR((*df)->iffeatures, struct lysc_iffeature, iff) {
- if (lysc_iffeature_value(iff) == LY_ENOT) {
- /* the feature must be disabled now */
- (*df)->flags &= ~LYS_FENABLED;
- /* add the feature into the list of changed features */
- ret = ly_set_add(changed, *df, 1, NULL);
- LY_CHECK_GOTO(ret, cleanup);
- break;
- }
- }
- }
- }
-
- /* success */
- ++mod->ctx->module_set_id;
-
-cleanup:
- ly_set_free(changed, NULL);
- return ret;
-}
-
-API LY_ERR
-lys_feature_enable(const struct lys_module *module, const char *feature)
-{
- LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
-
- return lys_feature_change((struct lys_module *)module, feature, 1, 0);
-}
-
-API LY_ERR
-lys_feature_disable(const struct lys_module *module, const char *feature)
-{
- LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
-
- return lys_feature_change((struct lys_module *)module, feature, 0, 0);
-}
-
-API LY_ERR
-lys_feature_enable_force(const struct lys_module *module, const char *feature)
-{
- LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
-
- return lys_feature_change((struct lys_module *)module, feature, 1, 1);
-}
-
-API LY_ERR
-lys_feature_disable_force(const struct lys_module *module, const char *feature)
-{
- LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
-
- return lys_feature_change((struct lys_module *)module, feature, 0, 1);
-}
-
-API LY_ERR
-lys_feature_value(const struct lys_module *module, const char *feature)
-{
- struct lysc_feature *f = NULL;
- LY_ARRAY_COUNT_TYPE u;
-
- LY_CHECK_ARG_RET(NULL, module, module->compiled, feature, -1);
-
- /* search for the specified feature */
- LY_ARRAY_FOR(module->features, u) {
- f = &module->features[u];
- if (!strcmp(f->name, feature)) {
- break;
- }
- }
-
- /* feature definition not found */
- if (!f) {
- return LY_ENOTFOUND;
- }
-
- /* feature disabled */
- if (!(f->flags & LYS_FENABLED)) {
- return LY_ENOT;
- }
-
- /* check referenced features if they are enabled */
- LY_ARRAY_FOR(f->iffeatures, u) {
- if (lysc_iffeature_value(&f->iffeatures[u]) == LY_ENOT) {
- /* if-feature disabled */
- return LY_ENOT;
- }
- }
-
- /* feature enabled */
- return LY_SUCCESS;
-}
-
-API const struct lysc_node *
-lysc_node_is_disabled(const struct lysc_node *node, ly_bool recursive)
-{
- LY_ARRAY_COUNT_TYPE u;
-
- LY_CHECK_ARG_RET(NULL, node, NULL);
-
- do {
- if (node->iffeatures) {
- /* check local if-features */
- LY_ARRAY_FOR(node->iffeatures, u) {
- if (lysc_iffeature_value(&node->iffeatures[u]) == LY_ENOT) {
- return node;
- }
- }
- }
-
- if (!recursive) {
- return NULL;
- }
-
- /* go through schema-only parents */
- node = node->parent;
- } while (node && (node->nodetype & (LYS_CASE | LYS_CHOICE)));
-
- return NULL;
-}
-
-API LY_ERR
lysc_set_private(const struct lysc_node *node, void *priv, void **prev_priv_p)
{
struct lysc_action *act;
@@ -929,7 +592,7 @@
}
API LY_ERR
-lys_set_implemented(struct lys_module *mod)
+lys_set_implemented(struct lys_module *mod, const char **features)
{
LY_ERR ret = LY_SUCCESS, r;
struct lys_module *m;
@@ -953,6 +616,9 @@
return LY_EDENIED;
}
+ /* enable features */
+ LY_CHECK_RET(lys_enable_features(mod->parsed, features));
+
/* add the module into newly implemented module set */
LY_CHECK_RET(ly_set_add(&mod->ctx->implementing, mod, 1, NULL));
@@ -1000,32 +666,32 @@
}
static LY_ERR
-lys_resolve_import_include(struct lys_parser_ctx *pctx, struct lysp_module *modp)
+lys_resolve_import_include(struct lys_parser_ctx *pctx, struct lysp_module *pmod)
{
struct lysp_import *imp;
struct lysp_include *inc;
LY_ARRAY_COUNT_TYPE u, v;
- modp->parsing = 1;
- LY_ARRAY_FOR(modp->imports, u) {
- imp = &modp->imports[u];
+ pmod->parsing = 1;
+ LY_ARRAY_FOR(pmod->imports, u) {
+ imp = &pmod->imports[u];
if (!imp->module) {
- LY_CHECK_RET(lysp_load_module(PARSER_CTX(pctx), imp->name, imp->rev[0] ? imp->rev : NULL, 0, 0, &imp->module));
+ LY_CHECK_RET(lysp_load_module(PARSER_CTX(pctx), imp->name, imp->rev[0] ? imp->rev : NULL, 0, 0, NULL, &imp->module));
}
/* check for importing the same module twice */
for (v = 0; v < u; ++v) {
- if (imp->module == modp->imports[v].module) {
+ if (imp->module == pmod->imports[v].module) {
LOGWRN(PARSER_CTX(pctx), "Single revision of the module \"%s\" imported twice.", imp->name);
}
}
}
- LY_ARRAY_FOR(modp->includes, u) {
- inc = &modp->includes[u];
+ LY_ARRAY_FOR(pmod->includes, u) {
+ inc = &pmod->includes[u];
if (!inc->submodule) {
LY_CHECK_RET(lysp_load_submodule(pctx, inc));
}
}
- modp->parsing = 0;
+ pmod->parsing = 0;
return LY_SUCCESS;
}
@@ -1122,7 +788,7 @@
LY_ERR
lys_create_module(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, ly_bool implement,
LY_ERR (*custom_check)(const struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *data),
- void *check_data, struct lys_module **module)
+ void *check_data, const char **features, struct lys_module **module)
{
struct lys_module *mod = NULL, *latest, *mod_dup;
struct lysp_submodule *submod;
@@ -1134,7 +800,7 @@
char *filename, *rev, *dot;
size_t len;
- LY_CHECK_ARG_RET(ctx, ctx, in, LY_EINVAL);
+ LY_CHECK_ARG_RET(ctx, ctx, in, !features || implement, LY_EINVAL);
if (module) {
*module = NULL;
}
@@ -1190,38 +856,23 @@
LY_CHECK_GOTO(ret = custom_check(ctx, mod->parsed, NULL, check_data), error);
}
- if (implement) {
- /* mark the loaded module implemented */
- if (ly_ctx_get_module_implemented(ctx, mod->name)) {
- LOGERR(ctx, LY_EDENIED, "Module \"%s\" is already implemented in the context.", mod->name);
- ret = LY_EDENIED;
- goto error;
- }
- }
-
/* check for duplicity in the context */
+ if (implement && ly_ctx_get_module_implemented(ctx, mod->name)) {
+ LOGERR(ctx, LY_EDENIED, "Module \"%s\" is already implemented in the context.", mod->name);
+ ret = LY_EDENIED;
+ goto error;
+ }
mod_dup = (struct lys_module *)ly_ctx_get_module(ctx, mod->name, mod->revision);
if (mod_dup) {
- if (mod_dup->parsed) {
- /* error */
- if (mod->parsed->revs) {
- LOGERR(ctx, LY_EEXIST, "Module \"%s\" of revision \"%s\" is already present in the context.",
- mod->name, mod->parsed->revs[0].date);
- } else {
- LOGERR(ctx, LY_EEXIST, "Module \"%s\" with no revision is already present in the context.",
- mod->name);
- }
- ret = LY_EEXIST;
- goto error;
+ if (mod->parsed->revs) {
+ LOGERR(ctx, LY_EEXIST, "Module \"%s\" of revision \"%s\" is already present in the context.",
+ mod->name, mod->parsed->revs[0].date);
} else {
- /* add the parsed data to the currently compiled-only module in the context */
- mod_dup->parsed = mod->parsed;
- mod_dup->parsed->mod = mod_dup;
- mod->parsed = NULL;
- lys_module_free(mod, NULL);
- mod = mod_dup;
- goto finish_parsing;
+ LOGERR(ctx, LY_EEXIST, "Module \"%s\" with no revision is already present in the context.",
+ mod->name);
}
+ ret = LY_EEXIST;
+ goto error;
}
switch (in->type) {
@@ -1261,15 +912,8 @@
ret = LY_EINT;
goto error;
}
-
lys_parser_fill_filepath(ctx, in, &mod->filepath);
- if (!implement) {
- /* pre-compile features and identities of the module */
- LY_CHECK_GOTO(ret = lys_feature_precompile(NULL, ctx, mod->parsed, mod->parsed->features, &mod->features), error);
- LY_CHECK_GOTO(ret = lys_identity_precompile(NULL, ctx, mod->parsed, mod->parsed->identities, &mod->identities), error);
- }
-
if (latest) {
latest->latest_revision = 0;
}
@@ -1279,27 +923,31 @@
LY_CHECK_GOTO(ret, error);
ctx->module_set_id++;
-finish_parsing:
- /* resolve imports and includes */
+ /* resolve includes and all imports */
LY_CHECK_GOTO(ret = lys_resolve_import_include(pctx, mod->parsed), error_ctx);
+ /* check name collisions */
+ LY_CHECK_GOTO(ret = lysp_check_dup_typedefs(pctx, mod->parsed), error_ctx);
+ /* TODO groupings */
+ LY_CHECK_GOTO(ret = lysp_check_dup_features(pctx, mod->parsed), error_ctx);
+ LY_CHECK_GOTO(ret = lysp_check_dup_identities(pctx, mod->parsed), error_ctx);
+
+ /* compile features */
+ LY_CHECK_GOTO(ret = lys_compile_feature_iffeatures(mod->parsed), error_ctx);
+
if (!implement) {
- /* pre-compile features and identities of any submodules */
+ /* pre-compile identities of the module */
+ LY_CHECK_GOTO(ret = lys_identity_precompile(NULL, ctx, mod->parsed, mod->parsed->identities, &mod->identities), error);
+
+ /* pre-compile identities of any submodules */
LY_ARRAY_FOR(mod->parsed->includes, u) {
submod = mod->parsed->includes[u].submodule;
- ret = lys_feature_precompile(NULL, ctx, (struct lysp_module *)submod, submod->features, &mod->features);
- LY_CHECK_GOTO(ret, error);
ret = lys_identity_precompile(NULL, ctx, (struct lysp_module *)submod, submod->identities, &mod->identities);
LY_CHECK_GOTO(ret, error);
}
- }
-
- /* check name collisions - typedefs and TODO groupings */
- LY_CHECK_GOTO(ret = lysp_check_typedefs(pctx, mod->parsed), error_ctx);
-
- if (implement) {
+ } else {
/* implement (compile) */
- LY_CHECK_GOTO(ret = lys_set_implemented(mod), error_ctx);
+ LY_CHECK_GOTO(ret = lys_set_implemented(mod, features), error_ctx);
}
if (format == LYS_IN_YANG) {
@@ -1329,7 +977,7 @@
}
API LY_ERR
-lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const struct lys_module **module)
+lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const char **features, const struct lys_module **module)
{
if (module) {
*module = NULL;
@@ -1339,7 +987,7 @@
/* remember input position */
in->func_start = in->current;
- return lys_create_module(ctx, in, format, 1, NULL, NULL, (struct lys_module **)module);
+ return lys_create_module(ctx, in, format, 1, NULL, NULL, features, (struct lys_module **)module);
}
API LY_ERR
@@ -1352,7 +1000,7 @@
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, module);
+ ret = lys_parse(ctx, in, format, NULL, module);
ly_in_free(in, 0);
return ret;
@@ -1368,7 +1016,7 @@
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, module);
+ ret = lys_parse(ctx, in, format, NULL, module);
ly_in_free(in, 0);
return ret;
@@ -1385,7 +1033,7 @@
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, module);
+ ret = lys_parse(ctx, in, format, NULL, module);
ly_in_free(in, 0);
return ret;
diff --git a/src/tree_schema.h b/src/tree_schema.h
index fc19147..35dade9 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -118,40 +118,25 @@
/**
* @page howtoSchemaFeatures YANG Features
*
- * YANG feature statement is an important part of the language which can significantly affect the meaning of the schemas. Despite
- * the features have similar effect as loading/removing schema from the context, manipulating with the feature value is not
- * limited to the context preparation period before working with data. YANG features, respectively their use in if-feature
- * statements, are evaluated as part of the [data validation process](@ref howtoDataValidation).
+ * YANG feature statement is an important part of the language which can significantly affect the meaning of the schemas.
+ * Modifying features may have similar effects as loading/removing schema from the context so it is limited to context
+ * preparation period before working with data. YANG features, respectively their use in if-feature
+ * statements, are evaluated as part of schema compilation so a feature-specific compiled schema tree is generated
+ * as a result.
*
- * The main functions with *lys_feature_* prefix are used to change the value (true/false) of the feature and to get its current
- * value. Enabling/disabling all the features in a particular module can be done using '`*`' value instead of the feature name.
+ * To enable any features, they must currently be specified when implementing a new schema with ::lys_parse() or
+ * ::ly_ctx_load_module(). To later examine what the status of a feature is, check its ::LYS_FENABLED flag or
+ * search for it first with ::lys_feature_value(). Lastly, to evaluate compiled if-features, use ::lysc_iffeature_value().
*
- * There are two options to reflect feature's if-feature statements when enabling/disabling the feature. The ::lys_feature_enable()
- * and ::lys_feature_disable() functions check their if-feature expressions (it is not possible to enable feature if it is not
- * allowed by its if-feature expressions) and also checks for and update other features those if-feature expressions use the
- * changed feature. On the contrary, ::lys_feature_enable_force() and ::lys_feature_disable_force() ignore all the if-feature
- * limitations.
- *
- * The ::lysc_feature_value() and ::lys_feature_value() functions differ only by their parameters. The ::lysc_iffeature_value()
- * is used to evaluate (possibly complex in YANG 1.1) logical expression from if-feature statement.
- *
- * The list of features of a particular YANG module is available in ::lys_module.features.
- *
- * To get know, if a specific schema node is currently disabled or enable, the ::lysc_node_is_disabled() function can be used.
+ * To iterate over all features of a particular YANG module, use ::lysp_feature_next().
*
* Note, that the feature's state can affect some of the output formats (e.g. Tree format).
*
* Functions List
* --------------
- * - ::lys_feature_enable()
- * - ::lys_feature_enable_force()
- * - ::lys_feature_disable()
- * - ::lys_feature_disable_force()
* - ::lys_feature_value()
- * - ::lysc_feature_value()
* - ::lysc_iffeature_value()
- *
- * - ::lysc_node_is_disabled()
+ * - ::lysp_feature_next()
*/
/**
@@ -368,7 +353,6 @@
LYEXT_PAR_TYPE, /**< ::lysc_type */
LYEXT_PAR_TYPE_BIT, /**< ::lysc_type_bitenum_item */
LYEXT_PAR_TYPE_ENUM, /**< ::lysc_type_bitenum_item */
- LYEXT_PAR_FEATURE, /**< ::lysc_feature */
LYEXT_PAR_MUST, /**< ::lysc_must */
LYEXT_PAR_PATTERN, /**< ::lysc_pattern */
LYEXT_PAR_LENGTH, /**< ::lysc_range */
@@ -521,11 +505,23 @@
*/
struct lysp_feature {
const char *name; /**< feature name (mandatory) */
- struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysp_qname *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysc_iffeature *iffeatures_c; /**< compiled if-features */
+ struct lysp_feature **depfeatures; /**< list of pointers to other features depending on this one
+ ([sized array](@ref sizedarrays)) */
const char *dsc; /**< description statement */
const char *ref; /**< reference statement */
struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values allowed */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values and
+ LYS_FENABLED are allowed */
+};
+
+/**
+ * @brief Compiled YANG if-feature-stmt
+ */
+struct lysc_iffeature {
+ uint8_t *expr; /**< 2bits array describing the if-feature expression in prefix format, see @ref ifftokens */
+ struct lysp_feature **features; /**< array of pointers to the features used in expression ([sized array](@ref sizedarrays)) */
};
/**
@@ -788,6 +784,7 @@
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 6 LYS_MAND_TRUE | |x|x| | |x| | | | | | | | | | | |x| |x| | | |
* LYS_SET_PATH | | | | | | | | | | | | | | | | | | | | | |x| |
+ * LYS_FENABLED | | | | | | | | | | | |x| | | | | | | | | | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 7 LYS_MAND_FALSE | |x|x| | |x| | | | | | | | | | | |x| |x| | | |
* LYS_ORDBY_USER | | | |x|x| | | | | | | | | | | | | | | | | | |
@@ -850,7 +847,6 @@
* LYS_UNIQUE | | |x| | | | | | | | | | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 9 LYS_KEY | | |x| | | | | | | | | | | |
- * LYS_FENABLED | | | | | | | | | |x| | | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 10 LYS_SET_DFLT | | |x|x| | |x| | | | | | | |
* LYS_ISENUM | | | | | | | | | | | | |x| |
@@ -892,10 +888,7 @@
#define LYS_UNIQUE 0x80 /**< flag for leafs being part of a unique set, applicable only to ::lysc_node_leaf */
#define LYS_KEY 0x100 /**< flag for leafs being a key of a list, applicable only to ::lysc_node_leaf */
#define LYS_KEYLESS 0x200 /**< flag for list without any key, applicable only to ::lysc_node_list */
-#define LYS_FENABLED 0x100 /**< feature enabled flag, applicable only to ::lysc_feature.
- Do not interpret presence of this flag as enabled feature! Also if-feature statements
- are supposed to be taken into account, so use ::lys_feature_value() or
- ::lysc_feature_value() to get know if a specific feature is really enabled. */
+#define LYS_FENABLED 0x20 /**< feature enabled flag, applicable only to ::lysp_feature. */
#define LYS_ORDBY_SYSTEM 0x80 /**< ordered-by user lists, applicable only to ::lysc_node_leaflist/::lysp_node_leaflist and
::lysc_node_list/::lysp_node_list */
#define LYS_ORDBY_USER 0x40 /**< ordered-by user lists, applicable only to ::lysc_node_leaflist/::lysp_node_leaflist and
@@ -1295,14 +1288,6 @@
};
/**
- * @brief Compiled YANG if-feature-stmt
- */
-struct lysc_iffeature {
- uint8_t *expr; /**< 2bits array describing the if-feature expression in prefix format, see @ref ifftokens */
- struct lysc_feature **features; /**< array of pointers to the features used in expression ([sized array](@ref sizedarrays)) */
-};
-
-/**
* @brief YANG when-stmt
*/
struct lysc_when {
@@ -1326,26 +1311,10 @@
struct lys_module *module; /**< module structure */
struct lysc_ident **derived; /**< list of (pointers to the) derived identities ([sized array](@ref sizedarrays)) */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ values are allowed */
};
/**
- * @brief YANG feature-stmt
- */
-struct lysc_feature {
- const char *name; /**< feature name (mandatory) */
- const char *dsc; /**< description */
- const char *ref; /**< reference */
- struct lys_module *module; /**< module structure */
- struct lysc_feature **depfeatures;/**< list of pointers to other features depending on this one ([sized array](@ref sizedarrays)) */
- struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
- uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* and
- #LYS_FENABLED values allowed */
-};
-
-/**
* @defgroup ifftokens if-feature expression tokens
* Tokens of if-feature expression used in ::lysc_iffeature.expr.
*
@@ -1443,7 +1412,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
union {
int32_t value; /**< integer value associated with the enumeration */
uint32_t position; /**< non-negative integer value associated with the bit */
@@ -1538,7 +1506,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_action_inout input; /**< RPC's/action's input */
struct lysc_action_inout output; /**< RPC's/action's output */
@@ -1561,7 +1528,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
void *priv; /** private arbitrary user data, not used by libyang */
};
@@ -1584,7 +1550,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
void *priv; /**< private arbitrary user data, not used by libyang */
};
@@ -1604,7 +1569,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
void *priv; /**< private arbitrary user data, not used by libyang */
@@ -1629,7 +1593,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
void *priv; /**< private arbitrary user data, not used by libyang */
@@ -1652,7 +1615,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
void *priv; /**< private arbitrary user data, not used by libyang */
@@ -1677,7 +1639,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
void *priv; /**< private arbitrary user data, not used by libyang */
@@ -1703,7 +1664,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
void *priv; /**< private arbitrary user data, not used by libyang */
@@ -1733,7 +1693,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
void *priv; /**< private arbitrary user data, not used by libyang */
@@ -1762,7 +1721,6 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
void *priv; /**< private arbitrary user data, not used by libyang */
@@ -1945,14 +1903,14 @@
LY_ERR lysc_iffeature_value(const struct lysc_iffeature *iff);
/**
- * @brief Get the current status of the provided feature.
+ * @brief Get the next feature in the module or submodules.
*
- * @param[in] feature Compiled feature statement to examine.
- * @return LY_SUCCESS if feature is enabled,
- * @return LY_ENOT if feature is disabled,
- * @return LY_ERR in case of error (invalid argument)
+ * @param[in] last Last returned feature.
+ * @param[in] pmod Parsed module and submodoules whose features to iterate over.
+ * @param[in,out] idx Submodule index, set to 0 on first call.
+ * @return Next found feature, NULL if the last has already been returned.
*/
-LY_ERR lysc_feature_value(const struct lysc_feature *feature);
+struct lysp_feature *lysp_feature_next(const struct lysp_feature *last, const struct lysp_module *pmod, uint32_t *idx);
/**
* @defgroup findxpathoptions Atomize XPath options
@@ -2054,13 +2012,6 @@
struct lysc_module *compiled; /**< Compiled and fully validated YANG schema tree for data parsing.
Available only for implemented modules. */
- struct lysc_feature *features; /**< List of compiled features of the module ([sized array](@ref sizedarrays)).
- Features are outside the compiled tree since they are needed even the module is not
- compiled. In such a case, the features are always disabled and cannot be enabled until
- the module is implemented. The features are present in this form to allow their linkage
- from if-feature statements of the compiled schemas and their proper use in case
- the module became implemented in future (no matter if implicitly via augment/deviate
- or explicitly via ::lys_set_implemented()). */
struct lysc_ident *identities; /**< List of compiled identities of the module ([sized array](@ref sizedarrays))
Identities are outside the compiled tree to allow their linkage to the identities from
the implemented modules. This avoids problems when the module became implemented in
@@ -2079,67 +2030,6 @@
};
/**
- * @brief Enable specified feature in the module
- *
- * By default, when the module is loaded by libyang parser, all features are disabled.
- *
- * If all features are being enabled, it must be possible respecting their if-feature conditions. For example,
- * enabling all features on the following feature set will fail since it is not possible to enable both features
- * (and it is unclear which of them should be enabled then). In this case the LY_EDENIED is returned and the feature
- * is untouched.
- *
- * feature f1;
- * feature f2 { if-feature 'not f1';}
- *
- * @param[in] module Module where the feature will be enabled.
- * @param[in] feature Name of the feature to enable. To enable all features at once, use asterisk (`*`) character.
- * @return LY_SUCCESS on success,
- * @return LY_EINVAL if @p module is not implemented,
- * @return LY_ENOTFOUND if @p feature was not found,
- * @return LY_EDENIED if @p feature could not be enabled because it has some false if-feature statements.
- */
-LY_ERR lys_feature_enable(const struct lys_module *module, const char *feature);
-
-/**
- * @brief Disable specified feature in the module
- *
- * By default, when the module is loaded by libyang parser, all features are disabled.
- *
- * If disabling a feature causes some other features that depend on this feature to become disabled.
- *
- * @param[in] module Module where the feature will be disabled.
- * @param[in] feature Name of the feature to disable. To disable all features at once, use asterisk (`*`) character.
- * @return LY_SUCCESS on success,
- * @return LY_EINVAL if @p module is not implemented,
- * @return LY_ENOTFOUND if @p feature was not found.
- */
-LY_ERR lys_feature_disable(const struct lys_module *module, const char *feature);
-
-/**
- * @brief Enable specified feature in the module disregarding its if-features.
- *
- * @param[in] module Module where the feature will be enabled.
- * @param[in] feature Name of the feature to enable. To enable all features at once, use asterisk character.
- * @return LY_SUCCESS on success,
- * @return LY_EINVAL if @p module is not implemented,
- * @return LY_ENOTFOUND if @p feature was not found.
- */
-LY_ERR lys_feature_enable_force(const struct lys_module *module, const char *feature);
-
-/**
- * @brief Disable specified feature in the module disregarding dependant features.
- *
- * By default, when the module is loaded by libyang parser, all features are disabled.
- *
- * @param[in] module Module where the feature will be disabled.
- * @param[in] feature Name of the feature to disable. To disable all features at once, use asterisk character.
- * @return LY_SUCCESS on success,
- * @return LY_EINVAL if @p module is not implemented,
- * @return LY_ENOTFOUND if @p feature was not found.
- */
-LY_ERR lys_feature_disable_force(const struct lys_module *module, const char *feature);
-
-/**
* @brief Get the current real status of the specified feature in the module.
*
* If the feature is enabled, but some of its if-features are false, the feature is considered
@@ -2186,10 +2076,8 @@
#define LYS_GETNEXT_WITHCHOICE 0x01 /**< ::lys_getnext() option to allow returning #LYS_CHOICE nodes instead of looking into them */
#define LYS_GETNEXT_NOCHOICE 0x02 /**< ::lys_getnext() option to ignore (kind of conditional) nodes within choice node */
#define LYS_GETNEXT_WITHCASE 0x04 /**< ::lys_getnext() option to allow returning #LYS_CASE nodes instead of looking into them */
-#define LYS_GETNEXT_INTONPCONT 0x40 /**< ::lys_getnext() option to look into non-presence container, instead of returning container itself */
-#define LYS_GETNEXT_NOSTATECHECK 0x100 /**< ::lys_getnext() option to skip checking module validity (import-only, disabled) and
- relevant if-feature conditions state */
-#define LYS_GETNEXT_OUTPUT 0x200 /**< ::lys_getnext() option to provide RPC's/action's output schema nodes instead of input schema nodes
+#define LYS_GETNEXT_INTONPCONT 0x08 /**< ::lys_getnext() option to look into non-presence container, instead of returning container itself */
+#define LYS_GETNEXT_OUTPUT 0x10 /**< ::lys_getnext() option to provide RPC's/action's output schema nodes instead of input schema nodes
provided by default */
/** @} sgetnextflags */
@@ -2213,22 +2101,13 @@
*
* @param[in] mod Module to make implemented. It is not an error
* to provide already implemented module, it just does nothing.
+ * @param[in] features Optional array of features ended with NULL to be enabled if the module is being implemented.
+ * NULL for all features disabled and '*' for all enabled.
* @return LY_SUCCESS on success.
* @return LY_EDENIED in case the context contains some other revision of the same module which is already implemented.
+ * @return LY_ERR on other errors during module compilation.
*/
-LY_ERR lys_set_implemented(struct lys_module *mod);
-
-/**
- * @brief Check if the schema node is disabled in the schema tree, i.e. there is any disabled if-feature statement
- * affecting the node.
- *
- * @param[in] node Schema node to check.
- * @param[in] recursive - 0 to check if-feature only in the \p node schema node,
- * - 1 to check if-feature in all ascendant schema nodes until there is a node possibly having an instance in a data tree
- * @return NULL if enabled,
- * @return pointer to the node with the unsatisfied (disabled) if-feature expression.
- */
-const struct lysc_node *lysc_node_is_disabled(const struct lysc_node *node, ly_bool recursive);
+LY_ERR lys_set_implemented(struct lys_module *mod, const char **features);
/**
* @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index e969a37..7597445 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -27,6 +27,7 @@
void lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp);
void lysc_extension_free(struct ly_ctx *ctx, struct lysc_ext **ext);
+static void lysc_node_free_(struct ly_ctx *ctx, struct lysc_node *node);
static void
lysp_stmt_free(struct ly_ctx *ctx, struct lysp_stmt *stmt)
@@ -105,6 +106,8 @@
{
FREE_STRING(ctx, feat->name);
FREE_ARRAY(ctx, feat->iffeatures, lysp_qname_free);
+ FREE_ARRAY(ctx, feat->iffeatures_c, lysc_iffeature_free);
+ LY_ARRAY_FREE(feat->depfeatures);
FREE_STRING(ctx, feat->dsc);
FREE_STRING(ctx, feat->ref);
FREE_ARRAY(ctx, feat->exts, lysp_ext_instance_free);
@@ -509,28 +512,16 @@
FREE_ARRAY(ctx, must->exts, lysc_ext_instance_free);
}
-static void
+void
lysc_ident_free(struct ly_ctx *ctx, struct lysc_ident *ident)
{
FREE_STRING(ctx, ident->name);
FREE_STRING(ctx, ident->dsc);
FREE_STRING(ctx, ident->ref);
- FREE_ARRAY(ctx, ident->iffeatures, lysc_iffeature_free);
LY_ARRAY_FREE(ident->derived);
FREE_ARRAY(ctx, ident->exts, lysc_ext_instance_free);
}
-void
-lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat)
-{
- FREE_STRING(ctx, feat->name);
- FREE_STRING(ctx, feat->dsc);
- FREE_STRING(ctx, feat->ref);
- FREE_ARRAY(ctx, feat->iffeatures, lysc_iffeature_free);
- LY_ARRAY_FREE(feat->depfeatures);
- FREE_ARRAY(ctx, feat->exts, lysc_ext_instance_free);
-}
-
static void
lysc_range_free(struct ly_ctx *ctx, struct lysc_range *range)
{
@@ -564,7 +555,6 @@
FREE_STRING(ctx, item->name);
FREE_STRING(ctx, item->dsc);
FREE_STRING(ctx, item->ref);
- FREE_ARRAY(ctx, item->iffeatures, lysc_iffeature_free);
FREE_ARRAY(ctx, item->exts, lysc_ext_instance_free);
}
@@ -637,7 +627,7 @@
FREE_ARRAY(ctx, inout->musts, lysc_must_free);
LY_LIST_FOR_SAFE(inout->data, child_next, child) {
- lysc_node_free(ctx, child);
+ lysc_node_free_(ctx, child);
}
}
@@ -647,7 +637,6 @@
FREE_STRING(ctx, action->name);
FREE_STRING(ctx, action->dsc);
FREE_STRING(ctx, action->ref);
- FREE_ARRAY(ctx, action->iffeatures, lysc_iffeature_free);
FREE_ARRAY(ctx, action->exts, lysc_ext_instance_free);
FREE_ARRAY(ctx, action->when, lysc_when_free);
FREE_ARRAY(ctx, action->input_exts, lysc_ext_instance_free);
@@ -664,12 +653,11 @@
FREE_STRING(ctx, notif->name);
FREE_STRING(ctx, notif->dsc);
FREE_STRING(ctx, notif->ref);
- FREE_ARRAY(ctx, notif->iffeatures, lysc_iffeature_free);
FREE_ARRAY(ctx, notif->exts, lysc_ext_instance_free);
FREE_ARRAY(ctx, notif->when, lysc_when_free);
FREE_ARRAY(ctx, notif->musts, lysc_must_free);
LY_LIST_FOR_SAFE(notif->data, child_next, child) {
- lysc_node_free(ctx, child);
+ lysc_node_free_(ctx, child);
}
}
@@ -679,7 +667,7 @@
struct lysc_node *child, *child_next;
LY_LIST_FOR_SAFE(node->child, child_next, child) {
- lysc_node_free(ctx, child);
+ lysc_node_free_(ctx, child);
}
FREE_ARRAY(ctx, node->musts, lysc_must_free);
FREE_ARRAY(ctx, node->actions, lysc_action_free);
@@ -726,7 +714,7 @@
struct lysc_node *child, *child_next;
LY_LIST_FOR_SAFE(node->child, child_next, child) {
- lysc_node_free(ctx, child);
+ lysc_node_free_(ctx, child);
}
FREE_ARRAY(ctx, node->musts, lysc_must_free);
@@ -745,7 +733,7 @@
struct lysc_node *child, *child_next;
LY_LIST_FOR_SAFE((struct lysc_node *)node->cases, child_next, child) {
- lysc_node_free(ctx, child);
+ lysc_node_free_(ctx, child);
}
}
@@ -755,7 +743,7 @@
struct lysc_node *child, *child_next;
LY_LIST_FOR_SAFE(node->child, child_next, child) {
- lysc_node_free(ctx, child);
+ lysc_node_free_(ctx, child);
}
}
@@ -765,8 +753,8 @@
FREE_ARRAY(ctx, node->musts, lysc_must_free);
}
-void
-lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node)
+static void
+lysc_node_free_(struct ly_ctx *ctx, struct lysc_node *node)
{
/* common part */
FREE_STRING(ctx, node->name);
@@ -802,11 +790,48 @@
}
FREE_ARRAY(ctx, node->when, lysc_when_free);
- FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
FREE_ARRAY(ctx, node->exts, lysc_ext_instance_free);
free(node);
}
+void
+lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node, ly_bool unlink)
+{
+ struct lysc_node *iter, **child_p;
+
+ if (unlink) {
+ /* unlink from siblings */
+ if (node->prev->next) {
+ node->prev->next = node->next;
+ }
+ if (node->next) {
+ node->next->prev = node->prev;
+ } else {
+ /* unlinking the last node */
+ if (node->parent) {
+ iter = (struct lysc_node *)lysc_node_children(node->parent, node->flags & LYS_CONFIG_MASK);
+ } else {
+ iter = node->module->compiled->data;
+ }
+ /* update the "last" pointer from the first node */
+ iter->prev = node->prev;
+ }
+
+ /* unlink from parent */
+ if (node->parent) {
+ child_p = lysc_node_children_p(node->parent, node->flags & LYS_CONFIG_MASK);
+ } else {
+ child_p = &node->module->compiled->data;
+ }
+ if (child_p && (*child_p == node)) {
+ /* the node is the first child */
+ *child_p = node->next;
+ }
+ }
+
+ lysc_node_free_(ctx, node);
+}
+
static void
lysc_module_free_(struct lysc_module *module)
{
@@ -817,7 +842,7 @@
ctx = module->mod->ctx;
LY_LIST_FOR_SAFE(module->data, node_next, node) {
- lysc_node_free(ctx, node);
+ lysc_node_free_(ctx, node);
}
FREE_ARRAY(ctx, module->rpcs, lysc_action_free);
FREE_ARRAY(ctx, module->notifs, lysc_notif_free);
@@ -845,7 +870,6 @@
}
lysc_module_free(module->compiled, private_destructor);
- FREE_ARRAY(module->ctx, module->features, lysc_feature_free);
FREE_ARRAY(module->ctx, module->identities, lysc_ident_free);
lysp_module_free(module->parsed);
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index a5f6ef5..3ce3f3b 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -320,7 +320,7 @@
* @return LY_EEXIST in case of collision, LY_SUCCESS otherwise.
*/
static LY_ERR
-lysp_check_typedef(struct lys_parser_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
+lysp_check_dup_typedef(struct lys_parser_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
struct hash_table *tpdfs_global, struct hash_table *tpdfs_scoped)
{
struct lysp_node *parent;
@@ -485,7 +485,7 @@
}
LY_ERR
-lysp_check_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod)
+lysp_check_dup_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod)
{
struct hash_table *ids_global;
struct hash_table *ids_scoped;
@@ -498,13 +498,13 @@
ids_global = lyht_new(8, sizeof(char *), lysp_id_cmp, NULL, 1);
ids_scoped = lyht_new(8, sizeof(char *), lysp_id_cmp, NULL, 1);
LY_ARRAY_FOR(mod->typedefs, v) {
- if (lysp_check_typedef(ctx, NULL, &mod->typedefs[v], ids_global, ids_scoped)) {
+ if (lysp_check_dup_typedef(ctx, NULL, &mod->typedefs[v], ids_global, ids_scoped)) {
goto cleanup;
}
}
LY_ARRAY_FOR(mod->includes, v) {
LY_ARRAY_FOR(mod->includes[v].submodule->typedefs, u) {
- if (lysp_check_typedef(ctx, NULL, &mod->includes[v].submodule->typedefs[u], ids_global, ids_scoped)) {
+ if (lysp_check_dup_typedef(ctx, NULL, &mod->includes[v].submodule->typedefs[u], ids_global, ids_scoped)) {
goto cleanup;
}
}
@@ -512,7 +512,7 @@
for (i = 0; i < ctx->tpdfs_nodes.count; ++i) {
typedefs = lysp_node_typedefs((struct lysp_node *)ctx->tpdfs_nodes.objs[i]);
LY_ARRAY_FOR(typedefs, u) {
- if (lysp_check_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[i], &typedefs[u], ids_global, ids_scoped)) {
+ if (lysp_check_dup_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[i], &typedefs[u], ids_global, ids_scoped)) {
goto cleanup;
}
}
@@ -526,6 +526,108 @@
return ret;
}
+static ly_bool
+ly_ptrequal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ void *ptr1 = *((void **)val1_p), *ptr2 = *((void **)val2_p);
+
+ return ptr1 == ptr2 ? 1 : 0;
+}
+
+LY_ERR
+lysp_check_dup_features(struct lys_parser_ctx *ctx, struct lysp_module *mod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct hash_table *ht;
+ struct lysp_feature *f;
+ uint32_t hash;
+ LY_ERR ret = LY_SUCCESS, r;
+
+ ht = lyht_new(1, sizeof(void *), ly_ptrequal_cb, NULL, 1);
+ LY_CHECK_RET(!ht, LY_EMEM);
+
+ /* add all module features into a hash table */
+ LY_ARRAY_FOR(mod->features, struct lysp_feature, f) {
+ hash = dict_hash(f->name, strlen(f->name));
+ r = lyht_insert(ht, &f->name, hash, NULL);
+ if (r == LY_EEXIST) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPIDENT, f->name, "feature");
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (r) {
+ ret = r;
+ goto cleanup;
+ }
+ }
+
+ /* add all submodule features into a hash table */
+ LY_ARRAY_FOR(mod->includes, u) {
+ LY_ARRAY_FOR(mod->includes[u].submodule->features, struct lysp_feature, f) {
+ hash = dict_hash(f->name, strlen(f->name));
+ r = lyht_insert(ht, &f->name, hash, NULL);
+ if (r == LY_EEXIST) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPIDENT, f->name, "feature");
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (r) {
+ ret = r;
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ lyht_free(ht);
+ return ret;
+}
+
+LY_ERR
+lysp_check_dup_identities(struct lys_parser_ctx *ctx, struct lysp_module *mod)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ struct hash_table *ht;
+ struct lysp_ident *i;
+ uint32_t hash;
+ LY_ERR ret = LY_SUCCESS, r;
+
+ ht = lyht_new(1, sizeof(void *), ly_ptrequal_cb, NULL, 1);
+ LY_CHECK_RET(!ht, LY_EMEM);
+
+ /* add all module identities into a hash table */
+ LY_ARRAY_FOR(mod->identities, struct lysp_ident, i) {
+ hash = dict_hash(i->name, strlen(i->name));
+ r = lyht_insert(ht, &i->name, hash, NULL);
+ if (r == LY_EEXIST) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPIDENT, i->name, "identity");
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (r) {
+ ret = r;
+ goto cleanup;
+ }
+ }
+
+ /* add all submodule identities into a hash table */
+ LY_ARRAY_FOR(mod->includes, u) {
+ LY_ARRAY_FOR(mod->includes[u].submodule->identities, struct lysp_ident, i) {
+ hash = dict_hash(i->name, strlen(i->name));
+ r = lyht_insert(ht, &i->name, hash, NULL);
+ if (r == LY_EEXIST) {
+ LOGVAL_PARSER(ctx, LY_VCODE_DUPIDENT, i->name, "identity");
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (r) {
+ ret = r;
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ lyht_free(ht);
+ return ret;
+}
+
struct lysp_load_module_check_data {
const char *name;
const char *revision;
@@ -608,7 +710,7 @@
}
LY_ERR
-lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, ly_bool implement,
+lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, const char **features, ly_bool implement,
struct lys_parser_ctx *main_ctx, const char *main_name, ly_bool required, void **result)
{
struct ly_in *in;
@@ -641,7 +743,7 @@
ret = lys_parse_submodule(ctx, in, format, main_ctx, lysp_load_module_check, &check_data,
(struct lysp_submodule **)&mod);
} else {
- ret = lys_create_module(ctx, in, format, implement, lysp_load_module_check, &check_data,
+ ret = lys_create_module(ctx, in, format, implement, lysp_load_module_check, &check_data, features,
(struct lys_module **)&mod);
}
@@ -659,7 +761,7 @@
LY_ERR
lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, ly_bool implement, ly_bool require_parsed,
- struct lys_module **mod)
+ const char **features, struct lys_module **mod)
{
const char *module_data = NULL;
LYS_INFORMAT format = LYS_IN_UNKNOWN;
@@ -718,7 +820,7 @@
LY_CHECK_RET(ly_in_new_memory(module_data, &in));
check_data.name = name;
check_data.revision = revision;
- lys_create_module(ctx, in, format, implement, lysp_load_module_check, &check_data, mod);
+ lys_create_module(ctx, in, format, implement, lysp_load_module_check, &check_data, features, mod);
ly_in_free(in, 0);
if (module_data_free) {
module_data_free((void *)module_data, ctx->imp_clb_data);
@@ -732,7 +834,7 @@
search_file:
if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
/* module was not received from the callback or there is no callback set */
- lys_module_localfile(ctx, name, revision, implement, NULL, NULL, m ? 0 : 1, (void **)mod);
+ lys_module_localfile(ctx, name, revision, features, implement, NULL, NULL, m ? 0 : 1, (void **)mod);
}
if (!(*mod) && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
goto search_clb;
@@ -852,8 +954,8 @@
search_file:
if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
/* submodule was not received from the callback or there is no callback set */
- lys_module_localfile(ctx, inc->name, inc->rev[0] ? inc->rev : NULL, 0, pctx, pctx->parsed_mod->mod->name, 1,
- (void **)&submod);
+ lys_module_localfile(ctx, inc->name, inc->rev[0] ? inc->rev : NULL, NULL, 0, pctx,
+ pctx->parsed_mod->mod->name, 1, (void **)&submod);
}
if (!submod && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
goto search_clb;
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 4d45f7e..fbab73c 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -211,7 +211,25 @@
* @param[in] mod Module where the type is being defined.
* @return LY_ERR value.
*/
-LY_ERR lysp_check_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod);
+LY_ERR lysp_check_dup_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod);
+
+/**
+ * @brief Check names of features in the parsed module and submodules to detect collisions.
+ *
+ * @param[in] ctx Parser context.
+ * @param[in] mod Module where the type is being defined.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_dup_features(struct lys_parser_ctx *ctx, struct lysp_module *mod);
+
+/**
+ * @brief Check names of identities in the parsed module and submodules to detect collisions.
+ *
+ * @param[in] ctx Parser context.
+ * @param[in] mod Module where the type is being defined.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_dup_identities(struct lys_parser_ctx *ctx, struct lysp_module *mod);
/**
* @brief Finalize some of the structures in case they are stored in sized array,
@@ -267,10 +285,12 @@
* of the latest revision can not be made implemented.
* @param[in] require_parsed Flag to require parsed module structure in case the module is already in the context,
* but only the compiled structure is available.
+ * @param[in] features All the features to enable if implementing the module.
* @param[out] mod Parsed module structure.
* @return LY_ERR value.
*/
-LY_ERR lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, ly_bool implement, ly_bool require_parsed, struct lys_module **mod);
+LY_ERR lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, ly_bool implement,
+ ly_bool require_parsed, const char **features, struct lys_module **mod);
/**
* @brief Parse included submodule into the simply parsed YANG module.
@@ -436,11 +456,12 @@
* @param[in] implement Flag if the schema is supposed to be marked as implemented and compiled.
* @param[in] custom_check Callback to check the parsed schema before it is accepted.
* @param[in] check_data Caller's data to pass to the custom_check callback.
+ * @param[in] features Array of features to enable ended with NULL. NULL for all features disabled and '*' for all enabled.
* @param[out] module Created module.
* @return LY_ERR value.
*/
LY_ERR lys_create_module(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, ly_bool implement,
- lys_custom_check custom_check, void *check_data, struct lys_module **module);
+ lys_custom_check custom_check, void *check_data, const char **features, struct lys_module **module);
/**
* @brief Parse submodule.
@@ -479,6 +500,7 @@
* @param[in] ctx libyang context where to work.
* @param[in] name Name of the (sub)module to load.
* @param[in] revision Optional revision of the (sub)module to load, if NULL the newest revision is being loaded.
+ * @param[in] features Array of enabled features ended with NULL.
* @param[in] implement Flag if the (sub)module is supposed to be marked as implemented.
* @param[in] main_ctx Parser context of the main module in case of loading submodule.
* @param[in] main_name Main module name in case of loading submodule.
@@ -488,8 +510,8 @@
* If it is a module, it is already in the context!
* @return LY_ERR value, in case of LY_SUCCESS, the \arg result is always provided.
*/
-LY_ERR lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, ly_bool implement,
- struct lys_parser_ctx *main_ctx, const char *main_name, ly_bool required, void **result);
+LY_ERR lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, const char **features,
+ ly_bool implement, struct lys_parser_ctx *main_ctx, const char *main_name, ly_bool required, void **result);
/**
* @brief Get the @ref ifftokens from the given position in the 2bits array
@@ -559,6 +581,14 @@
void lysc_iffeature_free(struct ly_ctx *ctx, struct lysc_iffeature *iff);
/**
+ * @brief Free the compiled identity structure.
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] ident Compiled identity structure to be cleaned.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+void lysc_ident_free(struct ly_ctx *ctx, struct lysc_ident *ident);
+
+/**
* @brief Free the compiled must structure.
* @param[in] ctx libyang context where the string data resides in a dictionary.
* @param[in,out] must Compiled must structure to be cleaned.
@@ -601,9 +631,10 @@
/**
* @brief Free the compiled node structure.
* @param[in] ctx libyang context where the string data resides in a dictionary.
- * @param[in,out] node Compiled node structure to be freed.
+ * @param[in] node Compiled node structure to be freed.
+ * @param[in] unlink Whether to first unlink the node before freeing.
*/
-void lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node);
+void lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node, ly_bool unlink);
/**
* @brief Free the compiled container node structure.
diff --git a/src/validation.c b/src/validation.c
index 36e8de6..6b495e2 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -952,7 +952,6 @@
LYD_VALIDATE_OP op)
{
struct lyd_node *next = NULL, *node;
- const struct lysc_node *snode;
/* validate all restrictions of nodes themselves */
LY_LIST_FOR_SAFE(first, next, node) {
@@ -983,12 +982,6 @@
/* obsolete data */
lyd_validate_obsolete(node);
- /* node's schema if-features */
- if ((snode = lysc_node_is_disabled(node->schema, 1))) {
- LOGVAL(LYD_CTX(node), LY_VLOG_LYD, node, LY_VCODE_NOIFF, snode->name);
- return LY_EVALID;
- }
-
/* node's musts */
LY_CHECK_RET(lyd_validate_must(node, op));
@@ -1265,11 +1258,6 @@
/* perform final validation of the operation/notification */
lyd_validate_obsolete(op_node);
- if (lysc_node_is_disabled(op_node->schema, 1)) {
- LOGVAL(LYD_CTX(op_tree), LY_VLOG_LYD, op_node, LY_VCODE_NOIFF, op_node->schema->name);
- ret = LY_EVALID;
- goto cleanup;
- }
LY_CHECK_GOTO(ret = lyd_validate_must(op_node, op), cleanup);
/* final validation of all the descendants */
diff --git a/src/xpath.c b/src/xpath.c
index 4d3c5fc..acfeaf4 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -5663,7 +5663,7 @@
}
/* getnext opts */
- getnext_opts = LYS_GETNEXT_NOSTATECHECK;
+ getnext_opts = 0;
if (options & LYXP_SCNODE_OUTPUT) {
getnext_opts |= LYS_GETNEXT_OUTPUT;
}
@@ -6250,7 +6250,7 @@
}
/* getnext opts */
- getnext_opts = LYS_GETNEXT_NOSTATECHECK;
+ getnext_opts = 0;
if (options & LYXP_SCNODE_OUTPUT) {
getnext_opts |= LYS_GETNEXT_OUTPUT;
}