context FEATURE flag for implementing referenced modules
diff --git a/src/context.c b/src/context.c
index 5f91f38..9457436 100644
--- a/src/context.c
+++ b/src/context.c
@@ -250,7 +250,7 @@
LY_CHECK_GOTO(rc, error);
/* load internal modules */
- for (i = 0; i < ((options & LY_CTX_NOYANGLIBRARY) ? (LY_INTERNAL_MODS_COUNT - 2) : LY_INTERNAL_MODS_COUNT); i++) {
+ 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);
@@ -277,7 +277,7 @@
ly_ctx_set_options(struct ly_ctx *ctx, uint16_t option)
{
LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
- LY_CHECK_ERR_RET(option & LY_CTX_NOYANGLIBRARY, LOGARG(ctx, option), LY_EINVAL);
+ LY_CHECK_ERR_RET(option & LY_CTX_NO_YANGLIBRARY, LOGARG(ctx, option), LY_EINVAL);
/* set the option(s) */
ctx->flags |= option;
@@ -289,7 +289,7 @@
ly_ctx_unset_options(struct ly_ctx *ctx, uint16_t option)
{
LY_CHECK_ARG_RET(ctx, ctx, LY_EINVAL);
- LY_CHECK_ERR_RET(option & LY_CTX_NOYANGLIBRARY, LOGARG(ctx, option), LY_EINVAL);
+ LY_CHECK_ERR_RET(option & LY_CTX_NO_YANGLIBRARY, LOGARG(ctx, option), LY_EINVAL);
/* unset the option(s) */
ctx->flags &= ~option;
@@ -604,7 +604,7 @@
return 0;
}
- if (ctx->flags & LY_CTX_NOYANGLIBRARY) {
+ if (ctx->flags & LY_CTX_NO_YANGLIBRARY) {
return LY_INTERNAL_MODS_COUNT - 2;
} else {
return LY_INTERNAL_MODS_COUNT;
diff --git a/src/context.h b/src/context.h
index 6249f05..758b419 100644
--- a/src/context.h
+++ b/src/context.h
@@ -3,7 +3,7 @@
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief internal context structures and functions
*
- * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
+ * 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.
@@ -61,9 +61,10 @@
* handled as implemented - libyang is able to instantiate data representing such a module. The modules loaded implicitly, are
* not implemented and serve only as a source of grouping or typedef definitions. Context can hold multiple revisions of the same
* YANG module, but only one of them can be implemented. Details about the difference between implemented and imported modules
- * can be found on @ref howtoSchema page. This behavior can be changed with the context's ::LY_CTX_ALLIMPLEMENTED option, which
- * causes that all the parsed modules, despite they were loaded explicitly or implicitly, are set to be implemented. Note, that as
- * a consequence of this option, only a single revision of any module can be present in the context in this case.
+ * can be found on @ref howtoSchema page. This behavior can be changed with the context's ::LY_CTX_ALL_IMPLEMENTED option, which
+ * causes that all the parsed modules, whether loaded explicitly or implicitly, are set to be implemented. Note, that as
+ * a consequence of this option, only a single revision of any module can be present in the context in this case. Also, a less
+ * crude option ::LY_CTX_REF_IMPLEMENTED can be used to implement only referenced modules that should also be implemented.
*
* When loading/importing a module without revision, the latest revision of the required module is supposed to load.
* For a context, the first time the latest revision of a module is requested, it is properly searched for and loaded.
@@ -162,11 +163,14 @@
* @{
*/
-#define LY_CTX_ALLIMPLEMENTED 0x01 /**< All the imports of the schema being parsed are treated implemented. */
-#define LY_CTX_TRUSTED 0x02 /**< Handle the schema being parsed as trusted and skip its validation
- tests. Note that while this option improves performance, it can
- lead to an undefined behavior if the schema is not correct. */
-#define LY_CTX_NOYANGLIBRARY 0x04 /**< Do not internally implement ietf-yang-library module. The option
+#define LY_CTX_ALL_IMPLEMENTED 0x01 /**< All the imported modules of the schema being parsed are implemented. */
+#define LY_CTX_REF_IMPLEMENTED 0x02 /**< Implement all imported modules "referenced" from an implemented module.
+ Normally, leafrefs, augment and deviation targets are implemented as
+ specified by YANG 1.1. In addition to this, implement any modules of
+ nodes referenced by when and must conditions and by any default values.
+ Generally, only if all these modules are implemented, the explicitly
+ implemented modules can be properly used and instantiated in data. */
+#define LY_CTX_NO_YANGLIBRARY 0x04 /**< Do not internally implement ietf-yang-library module. The option
causes that function ::ly_ctx_get_yanglib_data() does not work (returns ::LY_EINVAL) until
the ietf-yang-library module is loaded manually. While any revision
of this schema can be loaded with this option, note that the only
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 166f736..9927a22 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -27,6 +27,7 @@
#include "compat.h"
#include "dict.h"
#include "path.h"
+#include "schema_compile.h"
#include "set.h"
#include "tree.h"
#include "tree_schema.h"
@@ -750,7 +751,7 @@
storage->int64 = num;
storage->realtype = type;
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
free((char *)value);
}
return LY_SUCCESS;
@@ -804,7 +805,7 @@
storage->int64 = num;
storage->realtype = type;
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
free((char *)value);
}
return LY_SUCCESS;
@@ -869,7 +870,7 @@
storage->dec64 = d;
storage->realtype = type;
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
free((char *)value);
}
return LY_SUCCESS;
@@ -961,7 +962,7 @@
storage->ptr = NULL;
storage->realtype = type;
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
free((char *)value);
}
return LY_SUCCESS;
@@ -1005,7 +1006,7 @@
/* pattern restrictions */
LY_CHECK_RET(ly_type_validate_patterns(type_str->patterns, value, value_len, err));
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
LY_CHECK_RET(lydict_insert_zc(ctx, (char *)value, &storage->canonical));
} else {
LY_CHECK_RET(lydict_insert(ctx, value_len ? value : "", value_len, &storage->canonical));
@@ -1103,11 +1104,11 @@
items_ordered = items;
items = NULL;
- if (!ws_count && !lws_count && (options & LY_TYPE_OPTS_DYNAMIC)) {
+ if (!ws_count && !lws_count && (options & LY_TYPE_STORE_DYNAMIC)) {
ret = lydict_insert_zc(ctx, (char *)value, &can);
LY_CHECK_GOTO(ret, cleanup);
value = NULL;
- options &= ~LY_TYPE_OPTS_DYNAMIC;
+ options &= ~LY_TYPE_STORE_DYNAMIC;
} else {
ret = lydict_insert(ctx, value_len ? &value[lws_count] : "", value_len - ws_count - lws_count, &can);
LY_CHECK_GOTO(ret, cleanup);
@@ -1150,7 +1151,7 @@
}
storage->realtype = type;
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
free((char *)value);
}
@@ -1245,7 +1246,7 @@
match:
/* validation done */
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
LY_CHECK_RET(lydict_insert_zc(ctx, (char *)value, &storage->canonical));
} else {
LY_CHECK_RET(lydict_insert(ctx, value_len ? value : "", value_len, &storage->canonical));
@@ -1295,7 +1296,7 @@
}
}
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
LY_CHECK_RET(lydict_insert_zc(ctx, (char *)value, &storage->canonical));
} else {
LY_CHECK_RET(lydict_insert(ctx, value, value_len, &storage->canonical));
@@ -1446,9 +1447,13 @@
goto error;
} else if (!mod->implemented) {
/* non-implemented module */
- rc = asprintf(&errmsg, "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
- (int)value_len, value, mod->name);
- goto error;
+ if (options & LY_TYPE_STORE_IMPLEMENT) {
+ LY_CHECK_RET(lys_set_implemented((struct lys_module *)mod));
+ } else {
+ rc = asprintf(&errmsg, "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
+ (int)value_len, value, mod->name);
+ goto error;
+ }
}
/* check that the identity matches some of the type's base identities */
@@ -1473,7 +1478,7 @@
LY_CHECK_RET(lydict_insert_zc(ctx, str, &storage->canonical));
storage->realtype = type;
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
free((char *)value);
}
return LY_SUCCESS;
@@ -1567,6 +1572,11 @@
goto error;
}
+ if (options & LY_TYPE_STORE_IMPLEMENT) {
+ /* implement all prefixes */
+ LY_CHECK_GOTO(ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, NULL), error);
+ }
+
/* resolve it on schema tree */
ret = ly_path_compile(ctx, NULL, ctx_node, exp, LY_PATH_LREF_FALSE, lysc_is_output(ctx_node) ?
LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, format, prefix_data, &path);
@@ -1590,7 +1600,7 @@
lyxp_expr_free(ctx, exp);
ly_path_free(ctx, path);
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
free((char *)value);
}
@@ -2319,11 +2329,11 @@
}
/* remember the original value */
- if (options & LY_TYPE_OPTS_DYNAMIC) {
+ if (options & LY_TYPE_STORE_DYNAMIC) {
ret = lydict_insert_zc(ctx, (char *)value, &subvalue->original);
LY_CHECK_GOTO(ret, cleanup);
- options &= ~LY_TYPE_OPTS_DYNAMIC;
+ options &= ~LY_TYPE_STORE_DYNAMIC;
} else {
ret = lydict_insert(ctx, value_len ? value : "", value_len, &subvalue->original);
LY_CHECK_GOTO(ret, cleanup);
diff --git a/src/plugins_types.h b/src/plugins_types.h
index ffde8dc..1900d05 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -156,18 +156,19 @@
void lysc_prefixes_free(struct lysc_prefix *prefixes);
/**
- * @defgroup plugintypeopts Type store callback options.
+ * @defgroup plugintypestoreopts Type store callback options.
*
* Options applicable to ::ly_type_store_clb().
*
* @{
*/
-#define LY_TYPE_OPTS_DYNAMIC 0x01 /**< Flag for the dynamically allocated string value, in this case the value
- is supposed to be freed or directly inserted into the context's dictionary
- (e.g. in case of canonization).
- In any case, the caller of the callback does not free the provided string value after calling
- the type's callbacks with this option */
-/** @} plugintypeopts */
+#define LY_TYPE_STORE_DYNAMIC 0x01 /**< String value was dynamically allocated and is supposed to be freed or
+ directly inserted into the context's dictionary (e.g. in case of canonization).
+ In any case, the caller of the callback does not free the provided string
+ value after calling the type's store callback with this option */
+#define LY_TYPE_STORE_IMPLEMENT 0x02 /**< If a foreign module is needed to be implemented to successfully instantiate
+ the value, make the module implemented. */
+/** @} plugintypestoreopts */
/**
* @brief Callback to store and canonize the given @p value according to the given @p type.
@@ -182,7 +183,7 @@
* @param[in] value Lexical representation of the value to be stored.
* It is never NULL, empty string is represented as "" with zero @p value_len.
* @param[in] value_len Length (number of bytes) of the given \p value.
- * @param[in] options [Type plugin options](@ref plugintypeopts).
+ * @param[in] options [Type plugin store options](@ref plugintypestoreopts).
* @param[in] format Input format of the value.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_type_store_resolve_prefix).
* @param[in] hints Bitmap of [value hints](@ref lydvalhints) of all the allowed value types.
diff --git a/src/schema_compile.c b/src/schema_compile.c
index f6c2552..6156baa 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -1372,24 +1372,16 @@
return LY_SUCCESS;
}
-/**
- * @brief Check parsed expression for any prefixes of unimplemented modules.
- *
- * @param[in] ctx libyang context.
- * @param[in] expr Parsed expression.
- * @param[in] format Prefix format.
- * @param[in] prefix_data Format-specific data (see ::ly_resolve_prefix()).
- * @param[out] mod_p Optional module that is not implemented.
- * @return Whether all the found modules are implemented or at least one is not.
- */
-static ly_bool
-lys_compile_expr_target_is_implemented(const struct ly_ctx *ctx, const struct lyxp_expr *expr, LY_PREFIX_FORMAT format,
- void *prefix_data, const struct lys_module **mod_p)
+LY_ERR
+lys_compile_expr_implement(const struct ly_ctx *ctx, const struct lyxp_expr *expr, LY_PREFIX_FORMAT format,
+ void *prefix_data, ly_bool implement, const struct lys_module **mod_p)
{
uint32_t i;
const char *ptr, *start;
const struct lys_module *mod;
+ assert(implement || mod_p);
+
for (i = 0; i < expr->used; ++i) {
if ((expr->tokens[i] != LYXP_TOKEN_NAMETEST) && (expr->tokens[i] != LYXP_TOKEN_LITERAL)) {
/* token cannot have a prefix */
@@ -1409,14 +1401,16 @@
if (!mod->implemented) {
/* unimplemented module found */
- if (mod_p) {
+ if (implement) {
+ LY_CHECK_RET(lys_set_implemented((struct lys_module *)mod));
+ } else {
*mod_p = mod;
+ break;
}
- return 0;
}
}
- return 1;
+ return LY_SUCCESS;
}
/**
@@ -1494,8 +1488,12 @@
LY_ARRAY_FOR(when, u) {
/* first check whether all the referenced modules are implemented */
- if (!lys_compile_expr_target_is_implemented(ctx->ctx, when[u]->cond, LY_PREF_SCHEMA_RESOLVED,
- when[u]->prefixes, &mod)) {
+ mod = NULL;
+ ret = lys_compile_expr_implement(ctx->ctx, when[u]->cond, LY_PREF_SCHEMA_RESOLVED, when[u]->prefixes,
+ ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED, &mod);
+ if (ret) {
+ goto cleanup;
+ } else if (mod) {
LOGWRN(ctx->ctx, "When condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
when[u]->cond->expr, mod->name);
continue;
@@ -1504,7 +1502,7 @@
/* check "when" */
ret = lyxp_atomize(when[u]->cond, node->module, LY_PREF_SCHEMA_RESOLVED, when[u]->prefixes, when[u]->context,
&tmp_set, opts);
- if (ret != LY_SUCCESS) {
+ if (ret) {
LOGVAL(ctx->ctx, LY_VLOG_LYSC, node, LYVE_SEMANTICS, "Invalid when condition \"%s\".", when[u]->cond->expr);
goto cleanup;
}
@@ -1540,8 +1538,12 @@
check_musts:
LY_ARRAY_FOR(musts, u) {
/* first check whether all the referenced modules are implemented */
- if (!lys_compile_expr_target_is_implemented(ctx->ctx, musts[u].cond, LY_PREF_SCHEMA_RESOLVED,
- musts[u].prefixes, &mod)) {
+ mod = NULL;
+ ret = lys_compile_expr_implement(ctx->ctx, musts[u].cond, LY_PREF_SCHEMA_RESOLVED, musts[u].prefixes,
+ ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED, &mod);
+ if (ret) {
+ goto cleanup;
+ } else if (mod) {
LOGWRN(ctx->ctx, "Must condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
musts[u].cond->expr, mod->name);
continue;
@@ -1549,7 +1551,7 @@
/* check "must" */
ret = lyxp_atomize(musts[u].cond, node->module, LY_PREF_SCHEMA_RESOLVED, musts[u].prefixes, node, &tmp_set, opts);
- if (ret != LY_SUCCESS) {
+ if (ret) {
LOGVAL(ctx->ctx, LY_VLOG_LYSC, node, LYVE_SEMANTICS, "Invalid must restriction \"%s\".", musts[u].cond->expr);
goto cleanup;
}
@@ -1711,9 +1713,11 @@
const struct lysp_module *dflt_pmod, struct lyd_value *storage)
{
LY_ERR ret;
+ uint32_t options;
struct ly_err_item *err = NULL;
- ret = type->plugin->store(ctx->ctx, type, dflt, strlen(dflt), 0, LY_PREF_SCHEMA, (void *)dflt_pmod,
+ options = (ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED) ? LY_TYPE_STORE_IMPLEMENT : 0;
+ ret = type->plugin->store(ctx->ctx, type, dflt, strlen(dflt), options, LY_PREF_SCHEMA, (void *)dflt_pmod,
LYD_HINT_SCHEMA, node, storage, &err);
if (ret == LY_EINCOMPLETE) {
/* we have no data so we will not be resolving it */
@@ -1728,8 +1732,7 @@
"Invalid default - value does not fit the type (%s).", err->msg);
ly_err_free(err);
} else {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "Invalid default - value does not fit the type.");
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Invalid default - value does not fit the type.");
}
return ret;
}
diff --git a/src/schema_compile.h b/src/schema_compile.h
index db4fa09..5266ffb 100644
--- a/src/schema_compile.h
+++ b/src/schema_compile.h
@@ -241,6 +241,22 @@
void *mod2, const char *name2);
/**
+ * @brief Check parsed expression for any prefixes of unimplemented modules.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] expr Parsed expression.
+ * @param[in] format Prefix format.
+ * @param[in] prefix_data Format-specific data (see ::ly_resolve_prefix()).
+ * @param[in] implement Whether all the non-implemented modules should are implemented or the first
+ * non-implemented module, if any, returned in @p mod_p.
+ * @param[out] mod_p Module that is not implemented.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_compile_expr_implement(const struct ly_ctx *ctx, const struct lyxp_expr *expr, LY_PREFIX_FORMAT format,
+ void *prefix_data, ly_bool implement, const struct lys_module **mod_p);
+
+/**
* @brief Compile printable schema into a validated schema linking all the references.
*
* @param[in] mod Pointer to the schema structure holding pointers to both schema structure types. The ::lys_module#parsed
diff --git a/src/tree_data.c b/src/tree_data.c
index 9a40133..8e5fed4 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -61,7 +61,7 @@
{
LY_ERR ret;
struct ly_err_item *err = NULL;
- uint32_t options = (dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0);
+ uint32_t options = (dynamic && *dynamic ? LY_TYPE_STORE_DYNAMIC : 0);
if (incomplete) {
*incomplete = 0;
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index ea8643d..a5f6ef5 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -671,7 +671,7 @@
assert(mod);
- if (ctx->flags & LY_CTX_ALLIMPLEMENTED) {
+ if (ctx->flags & LY_CTX_ALL_IMPLEMENTED) {
implement = 1;
}
diff --git a/src/xpath.c b/src/xpath.c
index c1dd71f..4d3c5fc 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -1550,7 +1550,7 @@
}
/* ignore errors, the value may not satisfy schema constraints */
- rc = type->plugin->store(src->ctx, type, str, strlen(str), LY_TYPE_OPTS_DYNAMIC, LY_PREF_JSON, NULL, LYD_HINT_DATA,
+ rc = type->plugin->store(src->ctx, type, str, strlen(str), LY_TYPE_STORE_DYNAMIC, LY_PREF_JSON, NULL, LYD_HINT_DATA,
xp_node->node->schema, &val, &err);
ly_err_free(err);
if (rc) {