schema compilation CHANGE basic support for global augments
Including the process of making dependent module implemented (compiling
it) and reverting the process in case of failure.
diff --git a/src/context.c b/src/context.c
index ad3abb2..10542c0 100644
--- a/src/context.c
+++ b/src/context.c
@@ -484,8 +484,8 @@
}
}
-API LY_ERR
-ly_ctx_module_implement(struct ly_ctx *ctx, struct lys_module *mod)
+LY_ERR
+ly_ctx_module_implement_internal(struct ly_ctx *ctx, struct lys_module *mod, uint8_t value)
{
struct lys_module *m;
@@ -510,14 +510,20 @@
}
/* mark the module implemented, check for collision was already done */
- mod->implemented = 1;
+ mod->implemented = value;
/* compile the schema */
- LY_CHECK_RET(lys_compile(mod, 0));
+ LY_CHECK_RET(lys_compile(mod, LYSC_OPT_INTERNAL));
return LY_SUCCESS;
}
+API LY_ERR
+ly_ctx_module_implement(struct ly_ctx *ctx, struct lys_module *mod)
+{
+ return ly_ctx_module_implement_internal(ctx, mod, 1);
+}
+
API void
ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lysc_node *node, void *priv))
{
diff --git a/src/tree_schema.h b/src/tree_schema.h
index bcf92d4..40ad340 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1178,6 +1178,7 @@
struct lysc_action {
uint16_t nodetype; /**< LYS_ACTION */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lys_module *module; /**< module structure */
const char *name; /**< action/RPC name (mandatory) */
/* TODO */
};
@@ -1185,6 +1186,7 @@
struct lysc_notif {
uint16_t nodetype; /**< LYS_NOTIF */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lys_module *module; /**< module structure */
const char *name; /**< Notification name (mandatory) */
/* TODO */
};
@@ -1512,8 +1514,11 @@
the module became implemented in future (no matter if implicitly via augment/deviate
or explicitly via ly_ctx_module_implement()). */
- uint8_t implemented:1; /**< flag if the module is implemented, not just imported */
- uint8_t latest_revision:2; /**< flag to mark the latest available revision:
+ uint8_t implemented; /**< flag if the module is implemented, not just imported. The module is implemented if
+ the flag has non-zero value. Specific values are used internally:
+ 1 - implemented module
+ 2 - recently implemented module by dependency, it can be reverted in rollback procedure */
+ uint8_t latest_revision; /**< flag to mark the latest available revision:
1 - the latest revision in searchdirs was not searched yet and this is the
latest revision in the current context
2 - searchdirs were searched and this is the latest available revision */
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index f94cfed..b623af7 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -703,6 +703,32 @@
return LY_EINT;
}
+static void
+lys_feature_precompile_revert(struct lysc_ctx *ctx, struct lys_module *mod)
+{
+ unsigned int u, v;
+
+ /* keep the off_features list until the complete lys_module is freed */
+ mod->off_features = mod->compiled->features;
+ mod->compiled->features = NULL;
+
+ /* in the off_features list, remove all the parts (from finished compiling process)
+ * which may points into the data being freed here */
+ LY_ARRAY_FOR(mod->off_features, u) {
+ LY_ARRAY_FOR(mod->off_features[u].iffeatures, v) {
+ lysc_iffeature_free(ctx->ctx, &mod->off_features[u].iffeatures[v]);
+ }
+ LY_ARRAY_FREE(mod->off_features[u].iffeatures);
+ mod->off_features[u].iffeatures = NULL;
+
+ LY_ARRAY_FOR(mod->off_features[u].exts, v) {
+ lysc_ext_instance_free(ctx->ctx, &(mod->off_features[u].exts)[v]);
+ }
+ LY_ARRAY_FREE(mod->off_features[u].exts);
+ mod->off_features[u].exts = NULL;
+ }
+}
+
/**
* @brief Validate and normalize numeric value from a range definition.
* @param[in] ctx Compile context.
@@ -3025,7 +3051,7 @@
/* unique node must be present */
LY_ARRAY_NEW_RET(ctx->ctx, *unique, key, LY_EMEM);
- ret = lys_resolve_descendant_schema_nodeid(ctx, keystr, len, node, LYS_LEAF, (const struct lysc_node**)key);
+ ret = lys_resolve_schema_nodeid(ctx, keystr, len, node, LYS_LEAF, 0, (const struct lysc_node**)key);
if (ret != LY_SUCCESS) {
if (ret == LY_EDENIED) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
@@ -3221,17 +3247,17 @@
unsigned int u;
LY_LIST_FOR(children, iter) {
- if (iter != exclude && !strcmp(name, iter->name)) {
+ if (iter != exclude && iter->module == ctx->mod && !strcmp(name, iter->name)) {
goto error;
}
}
LY_ARRAY_FOR(actions, u) {
- if (&actions[u] != exclude && !strcmp(name, actions[u].name)) {
+ if (&actions[u] != exclude && actions[u].module == ctx->mod && !strcmp(name, actions[u].name)) {
goto error;
}
}
LY_ARRAY_FOR(notifs, u) {
- if (¬ifs[u] != exclude && !strcmp(name, notifs[u].name)) {
+ if (¬ifs[u] != exclude && notifs[u].module == ctx->mod && !strcmp(name, notifs[u].name)) {
goto error;
}
}
@@ -3285,6 +3311,10 @@
return LY_SUCCESS;
}
+/**
+ * @param[in] node_p Node image from the parsed tree. If the case is explicit, it is the LYS_CASE node, but in case of implicit case,
+ * it is the LYS_CHOICE node or LYS_AUGMENT node.
+ */
static struct lysc_node_case*
lys_compile_node_case(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node_choice *ch, struct lysc_node *child)
{
@@ -3293,27 +3323,27 @@
unsigned int u;
LY_ERR ret;
-#define UNIQUE_CHECK(NAME) \
+#define UNIQUE_CHECK(NAME, MOD) \
LY_LIST_FOR((struct lysc_node*)ch->cases, iter) { \
- if (!strcmp(iter->name, NAME)) { \
+ if (iter->module == MOD && !strcmp(iter->name, NAME)) { \
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DUPIDENT, NAME, "case"); \
return NULL; \
} \
}
- if (node_p->nodetype == LYS_CHOICE) {
- UNIQUE_CHECK(child->name);
+ if (node_p->nodetype == LYS_CHOICE || node_p->nodetype == LYS_AUGMENT) {
+ UNIQUE_CHECK(child->name, ctx->mod);
/* we have to add an implicit case node into the parent choice */
cs = calloc(1, sizeof(struct lysc_node_case));
DUP_STRING(ctx->ctx, child->name, cs->name);
cs->flags = ch->flags & LYS_STATUS_MASK;
- } else { /* node_p->nodetype == LYS_CASE */
+ } else if (node_p->nodetype == LYS_CASE) {
if (ch->cases && (node_p == ch->cases->prev->sp)) {
/* the case is already present since the child is not its first children */
return (struct lysc_node_case*)ch->cases->prev;
}
- UNIQUE_CHECK(node_p->name);
+ UNIQUE_CHECK(node_p->name, ctx->mod);
/* explicit parent case is not present (this is its first child) */
cs = calloc(1, sizeof(struct lysc_node_case));
@@ -3325,6 +3355,9 @@
LY_CHECK_RET(lys_compile_status(ctx, (struct lysc_node*)cs, ch->flags), NULL);
COMPILE_MEMBER_GOTO(ctx, node_p->when, cs->when, options, lys_compile_when, ret, error);
COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, cs->iffeatures, options, u, lys_compile_iffeature, ret, error);
+ } else {
+ LOGINT(ctx->ctx);
+ goto error;
}
cs->module = ctx->mod;
cs->prev = (struct lysc_node*)cs;
@@ -3510,7 +3543,7 @@
/* apply refine */
LY_ARRAY_FOR(uses_p->refines, struct lysp_refine, rfn) {
- LY_CHECK_GOTO(lys_resolve_descendant_schema_nodeid(ctx, rfn->nodeid, 0, (struct lysc_node*)&context_node_fake, 0, (const struct lysc_node**)&node),
+ LY_CHECK_GOTO(lys_resolve_schema_nodeid(ctx, rfn->nodeid, 0, (struct lysc_node*)&context_node_fake, 0, 0, (const struct lysc_node**)&node),
error);
ly_set_add(&refined, node, LY_SET_OPT_USEASLIST);
@@ -3867,7 +3900,7 @@
} else { /* other than choice */
node->parent = parent;
}
- LY_CHECK_RET(lys_compile_node_connect(ctx, parent, node), LY_EVALID);
+ LY_CHECK_RET(lys_compile_node_connect(ctx, parent->nodetype == LYS_CASE ? parent->parent : parent, node), LY_EVALID);
} else {
/* top-level element */
if (!ctx->mod->compiled->data) {
@@ -3891,6 +3924,102 @@
return ret;
}
+LY_ERR
+lys_compile_augment_sort(struct lysc_ctx *ctx, struct lysp_augment *aug_p, struct lysp_augment ***augments)
+{
+ struct lysp_augment **result = NULL;
+ unsigned int u, v;
+ size_t len;
+
+ assert(augments);
+
+ if (!aug_p || !LY_ARRAY_SIZE(aug_p)) {
+ *augments = NULL;
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_CREATE_RET(ctx->ctx, result, LY_ARRAY_SIZE(aug_p), LY_EMEM);
+
+ /* sort by the length of schema-nodeid - we need to solve /x before /x/xy. It is not necessary to group them
+ * together, so there can be even /z/y betwwen them. */
+ LY_ARRAY_FOR(aug_p, u) {
+ len = strlen(aug_p[u].nodeid);
+ LY_ARRAY_FOR(result, v) {
+ if (strlen(result[v]->nodeid) <= len) {
+ continue;
+ }
+ if (v < LY_ARRAY_SIZE(result)) {
+ /* move the rest of array */
+ memmove(&result[v + 1], &result[v], (LY_ARRAY_SIZE(result) - v) * sizeof *result);
+ break;
+ }
+ }
+ result[v] = &aug_p[u];
+ LY_ARRAY_INCREMENT(result);
+ }
+
+ *augments = result;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile the parsed augment connecting it into its target.
+ *
+ * It is expected that all the data referenced in path are present - augments are ordered so that augment B
+ * targeting data from augment A is being compiled after augment A. Also the modules referenced in the path
+ * are already implemented and compiled.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] aug_p Parsed augment to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] parent Parent node to provide the augment's context. It is NULL for the top level augments and a node holding uses's
+ * children in case of the augmenting uses data.
+ * @return LY_SUCCESS on success.
+ * @return LY_EVALID on failure.
+ */
+LY_ERR
+lys_compile_augment(struct lysc_ctx *ctx, struct lysp_augment *aug_p, int options, const struct lysc_node *parent)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_node *node_p, *case_node_p;
+ struct lysc_node *target; /* target target of the augment */
+
+ ret = lys_resolve_schema_nodeid(ctx, aug_p->nodeid, 0, parent,
+ LYS_CONTAINER | LYS_LIST | LYS_CHOICE | LYS_CASE | LYS_INOUT | LYS_NOTIF,
+ 1, (const struct lysc_node**)&target);
+ if (ret != LY_SUCCESS) {
+ if (ret == LY_EDENIED) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Augment's %s-schema-nodeid \"%s\" refers to a %s node which is not an allowed augment's target.",
+ parent ? "descendant" : "absolute", aug_p->nodeid, lys_nodetype2str(target->nodetype));
+ }
+ return LY_EVALID;
+ }
+
+ LY_LIST_FOR(aug_p->child, node_p) {
+ /* check if the subnode can be connected to the found target (e.g. case cannot be inserted into container) */
+ if (!(target->nodetype == LYS_CHOICE && node_p->nodetype == LYS_CASE)
+ && !((target->nodetype & (LYS_CONTAINER | LYS_LIST)) && (node_p->nodetype & (LYS_ACTION | LYS_NOTIF)))
+ && !(node_p->nodetype & (LYS_ANYDATA | LYS_CONTAINER | LYS_CHOICE | LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_USES))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid augment (%s) of %s node which is not allowed to contain %s node \"%s\".",
+ aug_p->nodeid, lys_nodetype2str(target->nodetype), lys_nodetype2str(node_p->nodetype), node_p->name);
+ return LY_EVALID;
+ }
+ /* compile the children */
+ if (node_p->nodetype != LYS_CASE) {
+ LY_CHECK_RET(lys_compile_node(ctx, node_p, options, target, 0));
+ } else {
+ LY_LIST_FOR(((struct lysp_node_case *)node_p)->child, case_node_p) {
+ LY_CHECK_RET(lys_compile_node(ctx, case_node_p, options, target, 0));
+ }
+ }
+ }
+ /* TODO actions, notifications */
+
+ return ret;
+}
+
/**
* @brief Compile the given YANG submodule into the main module.
* @param[in] ctx Compile context
@@ -3922,6 +4051,8 @@
return ret;
}
+
+
LY_ERR
lys_compile(struct lys_module *mod, int options)
{
@@ -3930,8 +4061,9 @@
struct lysc_type *type, *typeiter;
struct lysp_module *sp;
struct lysp_node *node_p;
+ struct lysp_augment **augments = NULL;
+ struct lys_module *m;
unsigned int u, v;
- int using_precompiled_features = 0;
LY_ERR ret = LY_SUCCESS;
LY_CHECK_ARG_RET(NULL, mod, mod->parsed, mod->ctx, LY_EINVAL);
@@ -3960,7 +4092,6 @@
/* there is already precompiled array of features */
mod_c->features = mod->off_features;
mod->off_features = NULL;
- using_precompiled_features = 1;
} else {
/* features are compiled directly into the compiled module structure,
* but it must be done in two steps to allow forward references (via if-feature) between the features themselves */
@@ -3986,10 +4117,19 @@
LY_CHECK_RET(lys_compile_identities_derived(&ctx, sp->identities, mod_c->identities));
}
+ /* data nodes */
LY_LIST_FOR(sp->data, node_p) {
ret = lys_compile_node(&ctx, node_p, options, NULL, 0);
LY_CHECK_GOTO(ret, error);
}
+
+ /* augments - sort first to cover augments augmenting other augments */
+ ret = lys_compile_augment_sort(&ctx, sp->augments, &augments);
+ LY_CHECK_GOTO(ret, error);
+ LY_ARRAY_FOR(augments, u) {
+ ret = lys_compile_augment(&ctx, augments[u], options, NULL);
+ LY_CHECK_GOTO(ret, error);
+ }
//COMPILE_ARRAY_GOTO(ctx, sp->rpcs, mod_c->rpcs, options, u, lys_compile_action, ret, error);
//COMPILE_ARRAY_GOTO(ctx, sp->notifs, mod_c->notifs, options, u, lys_compile_notif, ret, error);
@@ -4042,36 +4182,47 @@
}
ly_set_erase(&ctx.unres, NULL);
ly_set_erase(&ctx.groupings, NULL);
+ LY_ARRAY_FREE(augments);
if (options & LYSC_OPT_FREE_SP) {
lysp_module_free(mod->parsed);
((struct lys_module*)mod)->parsed = NULL;
}
+ if (!(options & LYSC_OPT_INTERNAL)) {
+ /* remove flag of the modules implemented by dependency */
+ for (u = 0; u < ctx.ctx->list.count; ++u) {
+ m = ctx.ctx->list.objs[u];
+ if (m->implemented == 2) {
+ m->implemented = 1;
+ }
+ }
+ }
+
((struct lys_module*)mod)->compiled = mod_c;
return LY_SUCCESS;
error:
- if (using_precompiled_features) {
- /* keep the off_features list until the complete lys_module is freed */
- mod->off_features = mod->compiled->features;
- mod->compiled->features = NULL;
- }
- /* in the off_features list, remove all the parts (from finished compiling process)
- * which may points into the data being freed here */
- LY_ARRAY_FOR(mod->off_features, u) {
- LY_ARRAY_FOR(mod->off_features[u].iffeatures, v) {
- lysc_iffeature_free(ctx.ctx, &mod->off_features[u].iffeatures[v]);
- }
- LY_ARRAY_FREE(mod->off_features[u].iffeatures);
- LY_ARRAY_FOR(mod->off_features[u].exts, v) {
- lysc_ext_instance_free(ctx.ctx, &(mod->off_features[u].exts)[v]);
- }
- LY_ARRAY_FREE(mod->off_features[u].exts);
- }
+ lys_feature_precompile_revert(&ctx, mod);
ly_set_erase(&ctx.unres, NULL);
ly_set_erase(&ctx.groupings, NULL);
+ LY_ARRAY_FREE(augments);
lysc_module_free(mod_c, NULL);
- ((struct lys_module*)mod)->compiled = NULL;
+ mod->compiled = NULL;
+
+ /* revert compilation of modules implemented by dependency */
+ for (u = 0; u < ctx.ctx->list.count; ++u) {
+ m = ctx.ctx->list.objs[u];
+ if (m->implemented == 2) {
+ /* revert features list to the precompiled state */
+ lys_feature_precompile_revert(&ctx, m);
+ /* mark module as imported-only / not-implemented */
+ m->implemented = 0;
+ /* free the compiled version of the module */
+ lysc_module_free(m->compiled, NULL);
+ m->compiled = NULL;
+ }
+ }
+
return ret;
}
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 20a06dc..6edb645 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -85,38 +85,61 @@
}
LY_ERR
-lys_resolve_descendant_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t nodeid_len, const struct lysc_node *context_node,
- int nodetype, const struct lysc_node **target)
+lys_resolve_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t nodeid_len, const struct lysc_node *context_node,
+ int nodetype, int implement, const struct lysc_node **target)
{
LY_ERR ret = LY_EVALID;
const char *name, *prefix, *id;
const struct lysc_node *context;
size_t name_len, prefix_len;
- const struct lys_module *mod;
+ const struct lys_module *mod, *context_module;
+ const char *nodeid_type;
assert(nodeid);
- assert(context_node);
assert(target);
*target = NULL;
id = nodeid;
context = context_node;
+
+ if (context_node) {
+ /* descendant-schema-nodeid */
+ nodeid_type = "descendant";
+ context_module = context_node->module;
+ } else {
+ /* absolute-schema-nodeid */
+ nodeid_type = "absolute";
+ context_module = ctx->mod_def;
+
+ if (*id != '/') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid absolute-schema-nodeid value \"%.*s\" - missing starting \"/\".",
+ nodeid_len, nodeid);
+ return LY_EVALID;
+ }
+ ++id;
+ }
+
while (*id && (ret = lys_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len)) == LY_SUCCESS) {
if (prefix) {
- mod = lys_module_find_prefix(context_node->module, prefix, prefix_len);
+ mod = lys_module_find_prefix(context_module, prefix, prefix_len);
if (!mod) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Invalid descendant-schema-nodeid value \"%.*s\" - prefix \"%.*s\" not defined in module \"%s\".",
- id - nodeid, nodeid, prefix_len, prefix, context_node->module->name);
+ "Invalid %s-schema-nodeid value \"%.*s\" - prefix \"%.*s\" not defined in module \"%s\".",
+ nodeid_type, id - nodeid, nodeid, prefix_len, prefix, context_module->name);
return LY_ENOTFOUND;
}
} else {
- mod = context_node->module;
+ mod = context_module;
+ }
+ if (implement && !mod->implemented) {
+ /* make the module implemented */
+ ly_ctx_module_implement_internal(ctx->ctx, (struct lys_module*)mod, 2);
}
context = lys_child(context, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE);
if (!context) {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Invalid descendant-schema-nodeid value \"%.*s\" - target node not found.", id - nodeid, nodeid);
+ "Invalid %s-schema-nodeid value \"%.*s\" - target node not found.", nodeid_type, id - nodeid, nodeid);
return LY_ENOTFOUND;
}
if (!*id || (nodeid_len && ((size_t)(id - nodeid) >= nodeid_len))) {
@@ -124,8 +147,8 @@
}
if (*id != '/') {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Invalid descendant-schema-nodeid value \"%.*s\" - missing \"/\" as node-identifier separator.",
- id - nodeid + 1, nodeid);
+ "Invalid %s-schema-nodeid value \"%.*s\" - missing \"/\" as node-identifier separator.",
+ nodeid_type, id - nodeid + 1, nodeid);
return LY_EVALID;
}
++id;
@@ -136,6 +159,10 @@
if (nodetype && !(context->nodetype & nodetype)) {
return LY_EDENIED;
}
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid %s-schema-nodeid value \"%.*s\" - unexpected end of expression.",
+ nodeid_type, nodeid_len, nodeid);
}
return ret;
@@ -1103,7 +1130,7 @@
return &((struct lysc_node_container*)node)->child;
case LYS_CHOICE:
if (((struct lysc_node_choice*)node)->cases) {
- return &((struct lysc_node_choice*)node)->cases[0].child;
+ return &((struct lysc_node_choice*)node)->cases->child;
} else {
return NULL;
}
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index b30c123..6bb131e 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -155,6 +155,7 @@
* @{
*/
#define LYSC_OPT_FREE_SP 1 /**< Free the input printable schema */
+#define LYSC_OPT_INTERNAL 2 /**< Internal compilation caused by dependency */
/** @} scflags */
/**
@@ -266,21 +267,24 @@
LY_ERR lys_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len);
/**
- * @brief Find the node according to the given descendant schema node id.
- * Used in unique, refine and uses's augment statements
+ * @brief Find the node according to the given descendant/absolute schema nodeid.
+ * Used in unique, refine and augment statements.
*
* @param[in] ctx Compile context
* @param[in] nodeid Descendant-schema-nodeid (according to the YANG grammar)
* @param[in] nodeid_len Length of the given nodeid, if it is not NULL-terminated string.
* @param[in] context_node Node where the nodeid is specified to correctly resolve prefixes and to start searching.
+ * If no context node is provided, the nodeid is actually expected to be the absolute schema node id and the module
+ * to resolve prefixes and to start searching is taken from ctx's mod_def.
* @param[in] nodetype Optional (can be 0) restriction for target's nodetype. If target exists, but does not match
- * the given nodetype, LY_EDENIED is returned, but no error message is printed. The value can be even an ORed value to allow
- * multiple nodetypes.
+ * the given nodetype, LY_EDENIED is returned (and target is provided), but no error message is printed.
+ * The value can be even an ORed value to allow multiple nodetypes.
+ * @param[in] implement Flag if the modules mentioned in the nodeid are supposed to be made implemented.
* @param[out] target Found target node if any.
* @return LY_ERR values - LY_ENOTFOUND, LY_EVALID, LY_EDENIED or LY_SUCCESS.
*/
-LY_ERR lys_resolve_descendant_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t nodeid_len, const struct lysc_node *context_node,
- int nodetype, const struct lysc_node **target);
+LY_ERR lys_resolve_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t nodeid_len, const struct lysc_node *context_node,
+ int nodetype, int implement, const struct lysc_node **target);
/**
* @brief Find the module referenced by prefix in the provided mod.
@@ -526,4 +530,16 @@
*/
LY_ERR yang_parse_module(struct ly_parser_ctx *ctx, const char *data, struct lys_module *mod);
+/**
+ * @brief Make the specific module implemented, use the provided value as flag.
+ *
+ * @param[in] ctx libyang context to change.
+ * @param[in] mod Module from the given context to make implemented. It is not an error
+ * to provide already implemented module, it just does nothing.
+ * @param[in] implemented Flag value for the ::lys_module#implemented item.
+ * @return LY_SUCCESS or LY_EDENIED in case the context contains some other revision of the
+ * same module which is already implemented.
+ */
+LY_ERR ly_ctx_module_implement_internal(struct ly_ctx *ctx, struct lys_module *mod, uint8_t implemented);
+
#endif /* LY_TREE_SCHEMA_INTERNAL_H_ */
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index 076afcc..24c6eba 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -112,6 +112,7 @@
FREE_STRING(module->ctx, module->contact);
FREE_STRING(module->ctx, module->dsc);
FREE_STRING(module->ctx, module->ref);
+ FREE_ARRAY(module->ctx, module->off_features, lysc_feature_free);
memset(module, 0, sizeof *module);
module->ctx = ctx;
@@ -2183,6 +2184,13 @@
"uses grp {status obsolete;}}", LYS_IN_YANG));
logbuf_assert("A \"deprecated\" status is in conflict with the parent's \"obsolete\" status.");
+ assert_null(lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;grouping grp {leaf l {type string;}}"
+ "leaf l {type int8;}uses grp;}", LYS_IN_YANG));
+ logbuf_assert("Duplicate identifier \"l\" of data definition statement.");
+ assert_null(lys_parse_mem(ctx, "module fg {namespace urn:fg;prefix fg;grouping grp {leaf m {type string;}}"
+ "uses grp;leaf m {type int8;}}", LYS_IN_YANG));
+ logbuf_assert("Duplicate identifier \"m\" of data definition statement.");
+
*state = NULL;
ly_ctx_destroy(ctx, NULL);
}
@@ -2331,6 +2339,83 @@
ly_ctx_destroy(ctx, NULL);
}
+static void
+test_augment(void **state)
+{
+ *state = test_augment;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ const struct lysc_node *node;
+ const struct lysc_node_choice *ch;
+ const struct lysc_node_case *c;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "module a {namespace urn:a;prefix a; typedef atype {type string;}"
+ "container top {leaf a {type string;}}}");
+ assert_non_null(lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;import a {prefix a;}"
+ "leaf b {type a:atype;}}", LYS_IN_YANG));
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "module c {namespace urn:c;prefix c; import a {prefix a;}"
+ "augment /a:top/ { container c {leaf c {type a:atype;}}}}");
+ assert_non_null(lys_parse_mem(ctx, "module d {namespace urn:d;prefix d;import a {prefix a;} import c {prefix c;}"
+ "augment /a:top/c:c/ { leaf d {type a:atype;} leaf c {type string;}}}", LYS_IN_YANG));
+ assert_non_null((mod = ly_ctx_get_module_implemented(ctx, "a")));
+ assert_non_null(ly_ctx_get_module_implemented(ctx, "b"));
+ assert_non_null(ly_ctx_get_module_implemented(ctx, "c"));
+ assert_non_null(ly_ctx_get_module_implemented(ctx, "d"));
+ assert_non_null(node = mod->compiled->data);
+ assert_string_equal(node->name, "top");
+ assert_non_null(node = lysc_node_children(node));
+ assert_string_equal(node->name, "a");
+ assert_non_null(node = node->next);
+ assert_string_equal(node->name, "c");
+ assert_non_null(node = lysc_node_children(node));
+ assert_string_equal(node->name, "c");
+ assert_non_null(node = node->next);
+ assert_string_equal(node->name, "d");
+ assert_non_null(node = node->next);
+ assert_string_equal(node->name, "c");
+
+ assert_non_null((mod = lys_parse_mem(ctx, "module e {namespace urn:e;prefix e;choice ch {leaf a {type string;}}"
+ "augment /ch/c { leaf lc2 {type uint16;}}"
+ "augment /ch { leaf b {type int8;} case c {leaf lc1 {type uint8;}}}}", LYS_IN_YANG)));
+ assert_non_null((ch = (const struct lysc_node_choice*)mod->compiled->data));
+ assert_null(mod->compiled->data->next);
+ assert_string_equal("ch", ch->name);
+ assert_non_null(c = ch->cases);
+ assert_string_equal("a", c->name);
+ assert_string_equal("a", c->child->name);
+ assert_non_null(c = (const struct lysc_node_case*)c->next);
+ assert_string_equal("b", c->name);
+ assert_string_equal("b", c->child->name);
+ assert_non_null(c = (const struct lysc_node_case*)c->next);
+ assert_string_equal("c", c->name);
+ assert_string_equal("lc1", ((const struct lysc_node_case*)c)->child->name);
+ assert_string_equal("lc2", ((const struct lysc_node_case*)c)->child->next->name);
+ assert_ptr_equal(ch->cases->child->prev, ((const struct lysc_node_case*)c)->child->next);
+ assert_null(c->next);
+
+ assert_non_null((mod = lys_parse_mem(ctx, "module f {namespace urn:f;prefix f;grouping g {leaf a {type string;}}"
+ "container c;"
+ "augment /c {uses g;}}", LYS_IN_YANG)));
+ assert_non_null(node = lysc_node_children(mod->compiled->data));
+ assert_string_equal(node->name, "a");
+
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; container c {leaf a {type string;}}"
+ "augment /x {leaf a {type int8;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid absolute-schema-nodeid value \"/x\" - target node not found.");
+
+ assert_null(lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb; container c {leaf a {type string;}}"
+ "augment /c {leaf a {type int8;}}}", LYS_IN_YANG));
+ logbuf_assert("Duplicate identifier \"a\" of data definition statement.");
+
+
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
@@ -2357,6 +2442,7 @@
cmocka_unit_test_setup_teardown(test_node_anydata, logger_setup, logger_teardown),
cmocka_unit_test_setup_teardown(test_uses, logger_setup, logger_teardown),
cmocka_unit_test_setup_teardown(test_refine, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_augment, logger_setup, logger_teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);