Merge branch 'libyang2' of https://github.com/Dajvid/libyang into libyang2
diff --git a/src/common.h b/src/common.h
index 6fc0ae7..2be4a20 100644
--- a/src/common.h
+++ b/src/common.h
@@ -444,7 +444,7 @@
*
* @param[in] CTX libyang context for logging.
* @param[in,out] ARRAY Pointer to the array to create.
- * @param[in] SIZE Number of items the array is supposed to hold. The size of the allocated
+ * @param[in] SIZE Number of the new items the array is supposed to hold. The size of the allocated
* space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
* @param[out] RET Variable to store error code.
* @param[in] GOTO Label to go in case of error (memory allocation 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/parser_yin.c b/src/parser_yin.c
index e8b0333..aa67056 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -35,55 +35,87 @@
YIN_ARG_TAG,
};
+LY_ERR
+parse_text_element(struct lyxml_context *xml_ctx, const char **data, const char **value)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *buf = NULL, *out = NULL;
+ size_t buf_len = 0, out_len = 0;
+ int dynamic;
+
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
+
+
+ if (xml_ctx->status == LYXML_ELEM_CONTENT) {
+ ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
+ LY_CHECK_RET(ret);
+ *value = lydict_insert(xml_ctx->ctx, out, out_len);
+ LY_CHECK_ERR_RET(!(*value), LOGMEM(xml_ctx->ctx), LY_EMEM);
+ }
+
+ LY_CHECK_ERR_RET(xml_ctx->status != LYXML_ELEMENT, "erere", LY_EINT);
+ lyxml_get_element(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
+
+ return 0;
+}
+
+/**
+ * @brief Match argument name.
+ *
+ * @param[in] name String representing name.
+ * @param[in] len Lenght of the name.
+ *
+ * @reurn YIN_ARGUMENT value.
+ */
enum YIN_ARGUMENT
match_argument_name(const char *name, size_t len)
{
enum YIN_ARGUMENT arg = YIN_ARG_NONE;
size_t already_read = 0;
-#define MOVE_DATA(DATA, COUNT) already_read+=COUNT;
-#define IF_ARG(STR, LEN, STMT) if (!strncmp((name) + already_read, STR, LEN)) {MOVE_DATA(name, LEN);arg=STMT;}
-#define IF_ARG_PREFIX(STR, LEN) if (!strncmp((name) + already_read, STR, LEN)) {MOVE_DATA(name, LEN);
+#define IF_ARG(STR, LEN, STMT) if (!strncmp((name) + already_read, STR, LEN)) {already_read+=LEN;arg=STMT;}
+#define IF_ARG_PREFIX(STR, LEN) if (!strncmp((name) + already_read, STR, LEN)) {already_read+=LEN;
#define IF_ARG_PREFIX_END }
- switch(*name) {
- case 'c':
- MOVE_DATA(name, 1);
- IF_ARG("ondition", 8, YIN_ARG_CONDITION);
+ switch (*name) {
+ case 'c':
+ already_read += 1;
+ IF_ARG("ondition", 8, YIN_ARG_CONDITION);
break;
- case 'd':
- MOVE_DATA(name, 1);
- IF_ARG("ate", 3, YIN_ARG_DATE);
+ case 'd':
+ already_read += 1;
+ IF_ARG("ate", 3, YIN_ARG_DATE);
break;
- case 'm':
- MOVE_DATA(name, 1);
- IF_ARG("odule", 5, YIN_ARG_MODULE);
+ case 'm':
+ already_read += 1;
+ IF_ARG("odule", 5, YIN_ARG_MODULE);
break;
- case 'n':
- MOVE_DATA(name, 1);
- IF_ARG("ame", 3, YIN_ARG_NAME);
+ case 'n':
+ already_read += 1;
+ IF_ARG("ame", 3, YIN_ARG_NAME);
break;
- case 't':
- MOVE_DATA(name, 1);
- IF_ARG_PREFIX("a", 1)
- IF_ARG("g", 1, YIN_ARG_TAG)
- else IF_ARG("rget-node", 9, YIN_ARG_TARGET_NODE)
- IF_ARG_PREFIX_END
- else IF_ARG("ext", 3, YIN_ARG_TEXT)
+ case 't':
+ already_read += 1;
+ IF_ARG_PREFIX("a", 1)
+ IF_ARG("g", 1, YIN_ARG_TAG)
+ else IF_ARG("rget-node", 9, YIN_ARG_TARGET_NODE)
+ IF_ARG_PREFIX_END
+ else IF_ARG("ext", 3, YIN_ARG_TEXT)
break;
- case 'u':
- MOVE_DATA(name, 1);
- IF_ARG("ri", 2, YIN_ARG_URI)
+ case 'u':
+ already_read += 1;
+ IF_ARG("ri", 2, YIN_ARG_URI)
break;
- case 'v':
- MOVE_DATA(name, 1);
- IF_ARG("alue", 4, YIN_ARG_VALUE);
+ case 'v':
+ already_read += 1;
+ IF_ARG("alue", 4, YIN_ARG_VALUE);
break;
}
@@ -95,60 +127,69 @@
return arg;
}
-LY_ERR
-parser_belongs_to(struct lyxml_context *xml_ctx, const char **data, const char **belongsto, const char **prefix, struct lysp_ext **extensions)
-{
- enum yang_keyword kw = YANG_NONE;
- LY_ERR ret = LY_SUCCESS;
- const char *prefix_out, *name;
- size_t prefix_len, name_len;
+// LY_ERR
+// parser_belongs_to(struct lyxml_context *xml_ctx, const char **data, const char **belongsto, const char **prefix, struct lysp_ext **extensions)
+// {
+// enum yang_keyword kw = YANG_NONE;
+// LY_ERR ret = LY_SUCCESS;
+// const char *prefix_out, *name;
+// size_t prefix_len, name_len;
- char *buf = NULL, *out = NULL;
- size_t buf_len = 0, out_len = 0;
- int dynamic;
+// char *buf = NULL, *out = NULL;
+// size_t buf_len = 0, out_len = 0;
+// int dynamic;
- /* check if belongs-to has argument module */
- ret = lyxml_get_attribute(xml_ctx, data, &prefix_out, &prefix_len, &name, &name_len);
- LY_CHECK_RET1(ret);
- if (match_argument_name(name, name_len) != YIN_ARG_MODULE) {
- LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Invalid argument name \"%s\", expected \"module\".", name);
- return LY_EINVAL;
- }
+// /* check if belongs-to has argument module */
+// ret = lyxml_get_attribute(xml_ctx, data, &prefix_out, &prefix_len, &name, &name_len);
+// LY_CHECK_RET1(ret);
+// if (match_argument_name(name, name_len) != YIN_ARG_MODULE) {
+// LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Invalid argument name \"%s\", expected \"module\".", name);
+// return LY_EINVAL;
+// }
- /* read content of argument */
- ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
- LY_CHECK_RET1(ret);
- *belongsto = lydict_insert(xml_ctx->ctx, out, out_len);
- LY_CHECK_ERR_RET(!belongsto, LOGMEM(xml_ctx->ctx), LY_EMEM);
+// /* read content of argument */
+// ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
+// LY_CHECK_RET1(ret);
+// *belongsto = lydict_insert(xml_ctx->ctx, out, out_len);
+// LY_CHECK_ERR_RET(!belongsto, LOGMEM(xml_ctx->ctx), LY_EMEM);
- /* read substatements */
- while (xml_ctx->status == LYXML_ATTRIBUTE) {
- ret = lyxml_get_attribute(xml_ctx, data, &prefix_out, &prefix_len, &name, &name_len);
- LY_CHECK_ERR_RET(ret != LY_SUCCESS, LOGMEM(xml_ctx->ctx), ret);
- kw = match_keyword(name);
+// /* read substatements */
+// while (xml_ctx->status == LYXML_ATTRIBUTE) {
+// ret = lyxml_get_attribute(xml_ctx, data, &prefix_out, &prefix_len, &name, &name_len);
+// LY_CHECK_ERR_RET(ret != LY_SUCCESS, LOGMEM(xml_ctx->ctx), ret);
+// kw = match_keyword(name);
- switch (kw) {
- case YANG_PREFIX:
- ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
- *prefix = lydict_insert(xml_ctx->ctx, out, out_len);
- break;
- case YANG_CUSTOM:
- /* TODO parse extension */
- break;
- default:
- LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Unexpected attribute");
- return LY_EVALID;
- }
- }
+// switch (kw) {
+// case YANG_PREFIX:
+// ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
+// *prefix = lydict_insert(xml_ctx->ctx, out, out_len);
+// break;
+// case YANG_CUSTOM:
+// /* TODO parse extension */
+// break;
+// default:
+// LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Unexpected attribute");
+// return LY_EVALID;
+// }
+// }
- if (!prefix) {
- LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Missing prefix");
- return LY_EVALID;
- }
+// if (!prefix) {
+// LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Missing prefix");
+// return LY_EVALID;
+// }
- return LY_SUCCESS;
-}
+// return LY_SUCCESS;
+// }
+/**
+ * @brief Parse namespace statement.
+ *
+ * @param[in] xml_ctx xml context.
+ * @param[in, out] data Data to read from.
+ * @param[in, out] mod_p Module to write to.
+ *
+ * @return LY_ERR values.
+ */
LY_ERR
parse_namespace(struct lyxml_context *xml_ctx, const char **data, struct lysp_module **mod_p)
{
@@ -162,20 +203,20 @@
/* check if namespace has argument uri */
ret = lyxml_get_attribute(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
- LY_CHECK_RET1(ret);
+ LY_CHECK_RET(ret);
if (match_argument_name(name, name_len) != YIN_ARG_URI) {
LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Invalid argument name \"%s\", expected \"uri\".", name);
return LY_EVALID;
}
ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
- LY_CHECK_RET1(ret);
- (*mod_p)->ns = lydict_insert(xml_ctx->ctx, out, out_len);
- LY_CHECK_ERR_RET(!(*mod_p)->ns, LOGMEM(xml_ctx->ctx), LY_EMEM);
+ LY_CHECK_RET(ret);
+ (*mod_p)->mod->ns = lydict_insert(xml_ctx->ctx, out, out_len);
+ LY_CHECK_ERR_RET(!(*mod_p)->mod->ns, LOGMEM(xml_ctx->ctx), LY_EMEM);
- /* namespace can only have one argument */
+ /* namespace can have only one argument */
ret = lyxml_get_attribute(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
- LY_CHECK_RET1(ret);
+ LY_CHECK_RET(ret);
if (name) {
LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Unexpected argument \"%s\".", name);
return LY_EVALID;
@@ -184,6 +225,15 @@
return LY_SUCCESS;
}
+/**
+ * @brief Parse prefix statement.
+ *
+ * @param[in] xml_ctx Xml context.
+ * @param[in, out] data Data to reda from.
+ * @param[out] mod_p Module to write to.
+ *
+ * @return LY_ERR values.
+ */
LY_ERR
parse_prefix(struct lyxml_context *xml_ctx, const char **data, struct lysp_module **mod_p)
{
@@ -197,20 +247,20 @@
/* check if prefix has argument value */
ret = lyxml_get_attribute(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
- LY_CHECK_RET1(ret);
+ LY_CHECK_RET(ret);
if (match_argument_name(name, name_len) != YIN_ARG_VALUE) {
LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Invalid argument name \"%s\", expected \"value\".", name);
return LY_EVALID;
}
ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
- LY_CHECK_RET1(ret);
- (*mod_p)->prefix = lydict_insert(xml_ctx->ctx, out, out_len);
- LY_CHECK_ERR_RET(!(*mod_p)->prefix, LOGMEM(xml_ctx->ctx), LY_EMEM);
+ LY_CHECK_RET(ret);
+ (*mod_p)->mod->prefix = lydict_insert(xml_ctx->ctx, out, out_len);
+ LY_CHECK_ERR_RET(!(*mod_p)->mod->prefix, LOGMEM(xml_ctx->ctx), LY_EMEM);
- /* prefix element can only have one argument */
+ /* prefix element can have only one argument */
ret = lyxml_get_attribute(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
- LY_CHECK_RET1(ret);
+ LY_CHECK_RET(ret);
if (name) {
LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Unexpected argument \"%s\".", name);
return LY_EVALID;
@@ -218,11 +268,12 @@
return LY_SUCCESS;
}
-LY_ERR
-parse_submodule(struct lyxml_context *xml_ctx, const char **data, struct lysp_module **mod_p)
+static LY_ERR
+yin_parse_import(struct lyxml_context *xml_ctx, const char *module_prefix, const char **data, struct lysp_import **imports)
{
LY_ERR ret = LY_SUCCESS;
- enum yang_keyword kw = YANG_NONE;
+ enum yang_keyword kw;
+ struct lysp_import *imp;
const char *prefix, *name;
size_t prefix_len, name_len;
@@ -230,7 +281,69 @@
size_t buf_len = 0, out_len = 0;
int dynamic;
- /* check if module/submodule has argument "name" */
+ /* allocate sized array for imports */
+ LY_ARRAY_NEW_RET(xml_ctx->ctx, *imports, imp, LY_EVALID);
+
+ /* get value */
+ ret = lyxml_get_attribute(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
+ LY_CHECK_RET(ret);
+ if (match_argument_name(name, name_len) != YIN_ARG_MODULE) {
+ LOGVAL(xml_ctx->ctx, LY_VLOG_LINE, &xml_ctx->line, LYVE_SYNTAX, "Invalid argument name \"%s\", expected \"module\".", name);
+ return LY_EVALID;
+ }
+ ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
+ LY_CHECK_RET(ret);
+ imp->name = lydict_insert(xml_ctx->ctx, out, out_len);
+ LY_CHECK_ERR_RET(!imp->name, LOGMEM(xml_ctx->ctx), LY_EMEM);
+
+
+ while ((ret = lyxml_get_element(xml_ctx, data, &prefix, &prefix_len, &name, &name_len) == LY_SUCCESS && name != NULL)) {
+ kw = match_keyword(name, name_len);
+ switch (kw) {
+ case YANG_PREFIX:
+ /* TODO parse prefix */
+ case YANG_DESCRIPTION:
+ /* TODO parse description */
+ case YANG_REFERENCE:
+ /* TODO parse reference */
+ case YANG_REVISION_DATE:
+ /* TODO parse revision date */
+ case YANG_CUSTOM:
+ /* TODO parse extension */
+ default:
+ /* TODO log error */
+ return LY_EVALID;
+ }
+ }
+
+ /* TODO add log macro and log error */
+ LY_CHECK_RET(!imp->prefix);
+ return ret;
+}
+
+/**
+ * @brief Parse module substatements.
+ *
+ * @param[in] xml_ctx xml context.
+ * @param[in, out] data Data to read from.
+ * @param[out] mod Parsed module structure
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR
+parse_mod(struct lyxml_context *xml_ctx, const char **data, struct lysp_module **mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ enum yang_keyword kw = YANG_NONE;
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
+ enum yang_module_stmt mod_stmt = Y_MOD_MODULE_HEADER;
+
+ char *buf = NULL, *out = NULL;
+ size_t buf_len = 0, out_len = 0;
+ int dynamic;
+
+ /* check if module has argument "name" */
ret = lyxml_get_attribute(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
LY_CHECK_ERR_RET(ret != LY_SUCCESS, LOGMEM(xml_ctx->ctx), LY_EMEM);
if (match_argument_name(name, name_len) != YIN_ARG_NAME) {
@@ -243,8 +356,8 @@
}
ret = lyxml_get_string(xml_ctx, data, &buf, &buf_len, &out, &out_len, &dynamic);
LY_CHECK_ERR_RET(ret != LY_SUCCESS, LOGMEM(xml_ctx->ctx), LY_EMEM);
- (*mod_p)->name = lydict_insert(xml_ctx->ctx, out, out_len);
- LY_CHECK_ERR_RET(!(*mod_p)->name, LOGMEM(xml_ctx->ctx), LY_EMEM);
+ (*mod)->mod->name = lydict_insert(xml_ctx->ctx, out, out_len);
+ LY_CHECK_ERR_RET(!(*mod)->mod->name, LOGMEM(xml_ctx->ctx), LY_EMEM);
/* read all attributes and their content only for testing */
while (xml_ctx->status == LYXML_ATTRIBUTE) {
@@ -254,27 +367,103 @@
}
}
-
+ /* loop over all elements and parse them */
while (xml_ctx->status == LYXML_ELEMENT || xml_ctx->status == LYXML_ELEM_CONTENT) {
- ret = lyxml_get_element(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
- LY_CHECK_ERR_RET(ret != LY_SUCCESS, LOGMEM(xml_ctx->ctx), LY_EMEM);
- kw = match_keyword(name);
+
+/* TODO ADD error log to macro */
+#define CHECK_ORDER(SECTION) \
+ if (mod_stmt > SECTION) {return LY_EVALID;}mod_stmt = SECTION
switch (kw) {
- case YANG_NAMESPACE:
- ret = parse_namespace(xml_ctx, data, mod_p);
+ /* module header */
+ case YANG_NAMESPACE:
+ case YANG_PREFIX:
+ CHECK_ORDER(Y_MOD_MODULE_HEADER);
break;
- case YANG_PREFIX:
- ret = parse_prefix(xml_ctx, data, mod_p);
- /* TODO change lysp_check_prefix function to work with ctx and not parser_ctx */
- //LY_CHECK_RET(lysp_check_prefix(&xml_ctx->ctx, *mod_p, &((*mod_p)->prefix)), LY_EVALID);
+ case YANG_YANG_VERSION:
+ CHECK_ORDER(Y_MOD_MODULE_HEADER);
break;
- case YANG_BELONGS_TO:
- ret = parser_belongs_to(xml_ctx, data, &(*mod_p)->belongsto, &(*mod_p)->prefix, &(*mod_p)->extensions);
+ /* linkage */
+ case YANG_INCLUDE:
+ case YANG_IMPORT:
+ CHECK_ORDER(Y_MOD_LINKAGE);
+ break;
+ /* meta */
+ case YANG_ORGANIZATION:
+ case YANG_CONTACT:
+ case YANG_DESCRIPTION:
+ case YANG_REFERENCE:
+ CHECK_ORDER(Y_MOD_META);
break;
- default:
- /* error */
+ /* revision */
+ case YANG_REVISION:
+ CHECK_ORDER(Y_MOD_REVISION);
+ break;
+ /* body */
+ case YANG_ANYDATA:
+ case YANG_ANYXML:
+ case YANG_AUGMENT:
+ case YANG_CHOICE:
+ case YANG_CONTAINER:
+ case YANG_DEVIATION:
+ case YANG_EXTENSION:
+ case YANG_FEATURE:
+ case YANG_GROUPING:
+ case YANG_IDENTITY:
+ case YANG_LEAF:
+ case YANG_LEAF_LIST:
+ case YANG_LIST:
+ case YANG_NOTIFICATION:
+ case YANG_RPC:
+ case YANG_TYPEDEF:
+ case YANG_USES:
+ case YANG_CUSTOM:
+ mod_stmt = Y_MOD_BODY;
+ break;
+ default:
+ /* error will be handled in the next switch */
+ break;
+ }
+#undef CHECK_ORDER
+
+ ret = lyxml_get_element(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
+ LY_CHECK_ERR_RET(ret != LY_SUCCESS, LOGMEM(xml_ctx->ctx), LY_EMEM);
+ kw = match_keyword(name, name_len);
+
+ switch (kw) {
+
+ /* module header */
+ case YANG_NAMESPACE:
+ LY_CHECK_RET(parse_namespace(xml_ctx, data, mod));
+ break;
+ case YANG_PREFIX:
+ LY_CHECK_RET(parse_prefix(xml_ctx, data, mod));
+ /* TODO change lysp_check_prefix function to work with ctx and not parser_ctx */
+ //LY_CHECK_RET(lysp_check_prefix(&xml_ctx->ctx, *mod_p, &((*mod_p)->prefix)), LY_EVALID);
+ break;
+
+ /* linkage */
+ case YANG_IMPORT:
+ yin_parse_import(xml_ctx, (*mod)->mod->prefix, data, &(*mod)->imports);
+ break;
+
+ /* meta */
+ case YANG_ORGANIZATION:
+ LY_CHECK_RET(parse_text_element(xml_ctx, data, &(*mod)->mod->org));
+ break;
+ case YANG_CONTACT:
+ LY_CHECK_RET(parse_text_element(xml_ctx, data, &(*mod)->mod->contact));
+ break;
+ case YANG_DESCRIPTION:
+ LY_CHECK_RET(parse_text_element(xml_ctx, data, &(*mod)->mod->dsc));
+ break;
+ case YANG_REFERENCE:
+ LY_CHECK_RET(parse_text_element(xml_ctx, data, &(*mod)->mod->ref));
+ break;
+
+ default:
+ /* error */
break;
}
}
@@ -282,38 +471,121 @@
return ret;
}
+/**
+ * @brief Parse yin submodule.
+ *
+ * @param[in] ctx Context of YANG schemas.
+ * @param[in] data Data to read from.
+ * @param[out] submod Module to write to.
+ *
+ * @return LY_ERR values.
+ */
LY_ERR
-yin_parse(struct ly_ctx *ctx, const char *data, struct lysp_module **mod_p)
+yin_parse_submodule(struct ly_ctx *ctx, const char *data, struct lysp_submodule **submod)
{
LY_ERR ret = LY_SUCCESS;
enum yang_keyword kw = YANG_NONE;
struct lyxml_context xml_ctx;
+ struct lysp_submodule *mod_p = NULL;
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
+ /* initialize xml context */
memset(&xml_ctx, 0, sizeof xml_ctx);
xml_ctx.ctx = ctx;
xml_ctx.line = 1;
- const char *prefix, *name;
- size_t prefix_len, name_len;
-
- /* check if root element is module or submodule */
+ /* check submodule */
ret = lyxml_get_element(&xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
- LY_CHECK_GOTO(ret != LY_SUCCESS, error);
- kw = match_keyword(name);
- if (kw != YANG_MODULE && kw != YANG_SUBMODULE) {
- LOGVAL(xml_ctx.ctx, LY_VLOG_LINE, &xml_ctx.line, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".", name);
+ LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ kw = match_keyword(name, name_len);
+ if (kw == YANG_MODULE) {
+ LOGERR(ctx, LY_EDENIED, "Input data contains module in situation when a submodule is expected.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ } else if (kw != YANG_SUBMODULE) {
+ /* TODO log error using LOGVAL_YIN macro */
+ ret = LY_EVALID;
+ goto cleanup;
}
- if (kw == YANG_SUBMODULE) {
- (*mod_p)->submodule = 1;
- }
+ /* allocate module */
+ mod_p = calloc(1, sizeof *mod_p);
+ LY_CHECK_ERR_GOTO(!mod_p, LOGMEM(ctx), cleanup);
+ mod_p->parsing = 1;
- ret = parse_submodule(&xml_ctx, &data, mod_p);
+ /* parser submodule substatements */
+ //ret = parse_submod(&xml_ctx, &data, mod_p);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ mod_p->parsing = 0;
+ *submod = mod_p;
+
+cleanup:
+ if (ret) {
+ lysp_submodule_free(ctx, mod_p);
+ }
lyxml_context_clear(&xml_ctx);
return ret;
+}
-error:
+/**
+ * @brief Parse yin module.
+ *
+ * @param[in] ctx Context of YANG schemas.
+ * @param[in] data Data to read from.
+ * @param[out] mod Module to write to.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR
+yin_parse_module(struct ly_ctx *ctx, const char *data, struct lys_module *mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ enum yang_keyword kw = YANG_NONE;
+ struct lyxml_context xml_ctx;
+ struct lysp_module *mod_p = NULL;
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
+
+ /* initialize xml context */
+ memset(&xml_ctx, 0, sizeof xml_ctx);
+ xml_ctx.ctx = ctx;
+ xml_ctx.line = 1;
+
+ /* check submodule */
+ ret = lyxml_get_element(&xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+ LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ kw = match_keyword(name, name_len);
+ if (kw == YANG_SUBMODULE) {
+ LOGERR(ctx, LY_EDENIED, "Input data contains submodule which cannot be parsed directly without its main module.");
+ ret = LY_EINVAL;
+ goto cleanup;
+ } else if (kw != YANG_MODULE) {
+ /* TODO log error using LOGVAL_YIN macro */
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* allocate module */
+ mod_p = calloc(1, sizeof *mod_p);
+ LY_CHECK_ERR_GOTO(!mod_p, LOGMEM(ctx), cleanup);
+ mod_p->mod = mod;
+ mod_p->parsing = 1;
+
+ /* parser module substatements */
+ ret = parse_mod(&xml_ctx, &data, &mod_p);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ mod_p->parsing = 0;
+ mod->parsed = mod_p;
+
+cleanup:
+ if (ret) {
+ lysp_module_free(mod_p);
+ }
+
lyxml_context_clear(&xml_ctx);
return ret;
}
diff --git a/src/tree_schema.h b/src/tree_schema.h
index bcf92d4..cb78110 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -517,14 +517,14 @@
* LYS_SET_LENGTH | | | | | | | | | | | | | | | | | | | | | |x|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 6 LYS_MAND_TRUE | |x|x| | |x| | | | | | | | | | | |x| |x| | |
- * LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | | | | | | | | | |
* LYS_SET_PATH | | | | | | | | | | | | | | | | | | | | | |x|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 7 LYS_MAND_FALSE | |x|x| | |x| | | | | | | | | | | |x| |x| | |
* LYS_ORDBY_USER | | | |x|x| | | | | | | | | | | | | | | | | |
* LYS_SET_PATTERN | | | | | | | | | | | | | | | | | | | | | |x|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * 8 LYS_YINELEM_TRUE | | | | | | | | | | | | | |x| | | | | | | | |
+ * 8 LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | | | | | | | | | |
+ * LYS_YINELEM_TRUE | | | | | | | | | | | | | |x| | | | | | | | |
* LYS_SET_RANGE | | | | | | | | | | | | | | | | | | | | | |x|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 9 LYS_YINELEM_FALSE| | | | | | | | | | | | | |x| | | | | | | | |
@@ -564,12 +564,12 @@
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x| | |x|x|x|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * 6 LYS_MAND_TRUE | |x|x| | |x| | | | | | | | |
- * LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | |
+ * 6 LYS_MAND_TRUE |x|x|x|x|x|x| | | | | | | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 7 LYS_ORDBY_USER | | | |x|x| | | | | | | | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * 8 LYS_PRESENCE |x| | | | | | | | | | | | | |
+ * 8 LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | |
+ * LYS_PRESENCE |x| | | | | | | | | | | | | |
* LYS_UNIQUE | | |x| | | | | | | | | | | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 9 LYS_KEY | | |x| | | | | | | | | | | |
@@ -593,14 +593,17 @@
#define LYS_STATUS_OBSLT 0x10 /**< status obsolete; */
#define LYS_STATUS_MASK 0x1C /**< mask for status value */
#define LYS_MAND_TRUE 0x20 /**< mandatory true; applicable only to ::lysp_node_choice/::lysc_node_choice,
- ::lysp_node_leaf/::lysc_node_leaf and ::lysp_node_anydata/::lysc_node_anydata */
+ ::lysp_node_leaf/::lysc_node_leaf and ::lysp_node_anydata/::lysc_node_anydata.
+ The ::lysc_node_leaflist and ::lysc_node_leaflist have this flag in case that min-elements > 0.
+ The ::lysc_node_container has this flag if it is not a presence container and it has at least one
+ child with LYS_MAND_TRUE. */
#define LYS_MAND_FALSE 0x40 /**< mandatory false; applicable only to ::lysp_node_choice, ::lysp_node_leaf and ::lysp_node_anydata */
#define LYS_MAND_MASK 0x60 /**< mask for mandatory values */
#define LYS_PRESENCE 0x80 /**< flag for presence property of a container, applicable only to ::lysc_node_container */
#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_FENABLED 0x100 /**< feature enabled flag, applicable only to ::lysc_feature */
-#define LYS_ORDBY_SYSTEM 0x20 /**< ordered-by user lists, applicable only to ::lysc_node_leaflist/::lysp_node_leaflist and
+#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
::lysc_node_list/::lysp_node_list */
@@ -627,7 +630,7 @@
with default statement mandatory. In case the default leaf value is taken from type, it is thrown
away when it is refined to be mandatory node. */
-#define LYS_FLAGS_COMPILED_MASK 0x7f /**< mask for flags that maps to the compiled structures */
+#define LYS_FLAGS_COMPILED_MASK 0xff /**< mask for flags that maps to the compiled structures */
/** @} */
/**
@@ -975,8 +978,9 @@
struct lyxp_expr *cond; /**< XPath when condition */
const char *dsc; /**< description */
const char *ref; /**< reference */
- struct lysc_node *context; /**< context node of the expression */
+ struct lysc_node *context; /**< context node for evaluating the expression */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint32_t refcount; /**< reference counter since some of the when statements are shared among several nodes */
};
/**
@@ -1178,6 +1182,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 +1190,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 */
};
@@ -1207,7 +1213,7 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_when *when; /**< when statement */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
};
@@ -1226,7 +1232,7 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_when *when; /**< when statement */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_node *child; /**< first child node (linked list) */
@@ -1250,7 +1256,7 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_when *when; /**< when statement */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_node *child; /**< first child node of the case (linked list). Note that all the children of all the sibling cases are linked
@@ -1274,7 +1280,7 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_when *when; /**< when statement */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_node_case *cases; /**< list of the cases (linked list). Note that all the children of all the cases are linked each other
@@ -1298,7 +1304,7 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_when *when; /**< when statement */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
@@ -1323,7 +1329,7 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_when *when; /**< when statement */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
@@ -1351,7 +1357,7 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_when *when; /**< when statement */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_node *child; /**< first child node (linked list) */
@@ -1380,7 +1386,7 @@
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_when *when; /**< when statement */
+ struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
@@ -1512,8 +1518,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..9f8d3e7 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -60,6 +60,15 @@
LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
}
+#define COMPILE_MEMBER_ARRAY_GOTO(CTX, MEMBER_P, ARRAY_C, OPTIONS, FUNC, RET, GOTO) \
+ if (MEMBER_P) { \
+ LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, 1, RET, GOTO); \
+ size_t __array_offset = LY_ARRAY_SIZE(ARRAY_C); \
+ LY_ARRAY_INCREMENT(ARRAY_C); \
+ RET = FUNC(CTX, MEMBER_P, OPTIONS, &(ARRAY_C)[__array_offset]); \
+ LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
+ }
+
#define COMPILE_CHECK_UNIQUENESS(CTX, ARRAY, MEMBER, EXCL, STMT, IDENT) \
if (ARRAY) { \
for (unsigned int u__ = 0; u__ < LY_ARRAY_SIZE(ARRAY); ++u__) { \
@@ -79,6 +88,14 @@
return NULL;
}
+/**
+ * @brief Duplicate the compiled pattern structure.
+ *
+ * Instead of duplicating memory, the reference counter in the @p orig is increased.
+ *
+ * @param[in] orig The pattern structure to duplicate.
+ * @return The duplicated structure to use.
+ */
static struct lysc_pattern*
lysc_pattern_dup(struct lysc_pattern *orig)
{
@@ -86,12 +103,24 @@
return orig;
}
+/**
+ * @brief Duplicate the array of compiled patterns.
+ *
+ * The sized array itself is duplicated, but the pattern structures are just shadowed by increasing their reference counter.
+ *
+ * @param[in] ctx Libyang context for logging.
+ * @param[in] orig The patterns sized array to duplicate.
+ * @return New sized array as a copy of @p orig.
+ * @return NULL in case of memory allocation error.
+ */
static struct lysc_pattern**
lysc_patterns_dup(struct ly_ctx *ctx, struct lysc_pattern **orig)
{
struct lysc_pattern **dup = NULL;
unsigned int u;
+ assert(orig);
+
LY_ARRAY_CREATE_RET(ctx, dup, LY_ARRAY_SIZE(orig), NULL);
LY_ARRAY_FOR(orig, u) {
dup[u] = lysc_pattern_dup(orig[u]);
@@ -100,12 +129,22 @@
return dup;
}
+/**
+ * @brief Duplicate compiled range structure.
+ *
+ * @param[in] ctx Libyang context for logging.
+ * @param[in] orig The range structure to be duplicated.
+ * @return New compiled range structure as a copy of @p orig.
+ * @return NULL in case of memory allocation error.
+ */
struct lysc_range*
lysc_range_dup(struct ly_ctx *ctx, const struct lysc_range *orig)
{
struct lysc_range *dup;
LY_ERR ret;
+ assert(orig);
+
dup = calloc(1, sizeof *dup);
LY_CHECK_ERR_RET(!dup, LOGMEM(ctx), NULL);
if (orig->parts) {
@@ -124,12 +163,22 @@
return NULL;
}
+/**
+ * @brief Stack for processing if-feature expressions.
+ */
struct iff_stack {
- int size;
- int index; /* first empty item */
- uint8_t *stack;
+ int size; /**< number of items in the stack */
+ int 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)
{
@@ -142,13 +191,24 @@
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)
{
@@ -156,6 +216,13 @@
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, int pos)
{
@@ -171,8 +238,8 @@
*item = (*item) | (op << 2 * (pos % 4));
}
-#define LYS_IFF_LP 0x04 /* ( */
-#define LYS_IFF_RP 0x08 /* ) */
+#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.
@@ -262,6 +329,14 @@
return LY_SUCCESS;
}
+/**
+ * @brief Compile information from the if-feature statement
+ * @param[in] ctx Compile context.
+ * @param[in] value The if-feature argument to process. It is pointer-to-pointer-to-char just to unify the compile functions.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] iff Prepared (empty) compiled if-feature structure to fill.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_iffeature(struct lysc_ctx *ctx, const char **value, int UNUSED(options), struct lysc_iffeature *iff)
{
@@ -433,22 +508,40 @@
return rc;
}
+/**
+ * @brief Compile information from the when statement
+ * @param[in] ctx Compile context.
+ * @param[in] when_p The parsed when statement structure.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[out] when Pointer where to store pointer to the created compiled when structure.
+ * @return LY_ERR value.
+ */
static LY_ERR
-lys_compile_when(struct lysc_ctx *ctx, struct lysp_when *when_p, int options, struct lysc_when *when)
+lys_compile_when(struct lysc_ctx *ctx, struct lysp_when *when_p, int options, struct lysc_when **when)
{
unsigned int u;
LY_ERR ret = LY_SUCCESS;
- when->cond = lyxp_expr_parse(ctx->ctx, when_p->cond);
- DUP_STRING(ctx->ctx, when_p->dsc, when->dsc);
- DUP_STRING(ctx->ctx, when_p->ref, when->ref);
- LY_CHECK_ERR_GOTO(!when->cond, ret = ly_errcode(ctx->ctx), done);
- COMPILE_ARRAY_GOTO(ctx, when_p->exts, when->exts, options, u, lys_compile_ext, ret, done);
+ *when = calloc(1, sizeof **when);
+ (*when)->refcount = 1;
+ (*when)->cond = lyxp_expr_parse(ctx->ctx, when_p->cond);
+ DUP_STRING(ctx->ctx, when_p->dsc, (*when)->dsc);
+ DUP_STRING(ctx->ctx, when_p->ref, (*when)->ref);
+ LY_CHECK_ERR_GOTO(!(*when)->cond, ret = ly_errcode(ctx->ctx), done);
+ COMPILE_ARRAY_GOTO(ctx, when_p->exts, (*when)->exts, options, u, lys_compile_ext, ret, done);
done:
return ret;
}
+/**
+ * @brief Compile information from the must statement
+ * @param[in] ctx Compile context.
+ * @param[in] must_p The parsed must statement structure.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] must Prepared (empty) compiled must structure to fill.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_must(struct lysc_ctx *ctx, struct lysp_restr *must_p, int options, struct lysc_must *must)
{
@@ -468,6 +561,14 @@
return ret;
}
+/**
+ * @brief Compile information from the import statement
+ * @param[in] ctx Compile context.
+ * @param[in] imp_p The parsed import statement structure.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] imp Prepared (empty) compiled import structure to fill.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_import(struct lysc_ctx *ctx, struct lysp_import *imp_p, int options, struct lysc_import *imp)
{
@@ -506,6 +607,18 @@
return ret;
}
+/**
+ * @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().
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] ident_p The parsed identity statement structure.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] idents List of so far compiled identities to check the name uniqueness.
+ * @param[in,out] ident Prepared (empty) compiled identity structure to fill.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_identity(struct lysc_ctx *ctx, struct lysp_ident *ident_p, int options, struct lysc_ident *idents, struct lysc_ident *ident)
{
@@ -526,6 +639,59 @@
}
/**
+ * @brief Check circular dependency of identities - identity MUST NOT reference itself (via their base statement).
+ *
+ * The function works in the same way as lys_compile_feature_circular_check() with different structures and error messages.
+ *
+ * @param[in] ctx Compile context for logging.
+ * @param[in] ident The base identity (its derived list is being extended by the identity being currently processed).
+ * @param[in] derived The list of derived identities of the identity being currently processed (not the one provided as @p ident)
+ * @return LY_SUCCESS if everything is ok.
+ * @return LY_EVALID if the identity is derived from itself.
+ */
+static LY_ERR
+lys_compile_identity_circular_check(struct lysc_ctx *ctx, struct lysc_ident *ident, struct lysc_ident **derived)
+{
+ LY_ERR ret = LY_EVALID;
+ unsigned int u, v;
+ struct ly_set recursion = {0};
+ struct lysc_ident *drv;
+
+ if (!derived) {
+ return LY_SUCCESS;
+ }
+
+ for (u = 0; u < LY_ARRAY_SIZE(derived); ++u) {
+ if (ident == derived[u]) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Identity \"%s\" is indirectly derived from itself.", ident->name);
+ goto cleanup;
+ }
+ ly_set_add(&recursion, derived[u], 0);
+ }
+
+ for (v = 0; v < recursion.count; ++v) {
+ drv = recursion.objs[v];
+ if (!drv->derived) {
+ continue;
+ }
+ for (u = 0; u < LY_ARRAY_SIZE(drv->derived); ++u) {
+ if (ident == drv->derived[u]) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Identity \"%s\" is indirectly derived from itself.", ident->name);
+ goto cleanup;
+ }
+ ly_set_add(&recursion, drv->derived[u], 0);
+ }
+ }
+ ret = LY_SUCCESS;
+
+cleanup:
+ ly_set_erase(&recursion, NULL);
+ return ret;
+}
+
+/**
* @brief Find and process the referenced base identities from another identity or identityref
*
* For bases in identity se backlinks to them from the base identities. For identityref, store
@@ -579,6 +745,12 @@
for (v = 0; v < LY_ARRAY_SIZE(mod->compiled->identities); ++v) {
if (!strcmp(name, mod->compiled->identities[v].name)) {
if (ident) {
+ if (ident == &mod->compiled->identities[v]) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Identity \"%s\" is derived from itself.", ident->name);
+ return LY_EVALID;
+ }
+ LY_CHECK_RET(lys_compile_identity_circular_check(ctx, &mod->compiled->identities[v], ident->derived));
/* we have match! store the backlink */
LY_ARRAY_NEW_RET(ctx->ctx, mod->compiled->identities[v].derived, idref, LY_EMEM);
*idref = ident;
@@ -656,6 +828,60 @@
}
/**
+ * @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_EVALID;
+ unsigned int u, v;
+ struct ly_set recursion = {0};
+ struct lysc_feature *drv;
+
+ if (!depfeatures) {
+ return LY_SUCCESS;
+ }
+
+ for (u = 0; u < LY_ARRAY_SIZE(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);
+ goto cleanup;
+ }
+ ly_set_add(&recursion, depfeatures[u], 0);
+ }
+
+ for (v = 0; v < recursion.count; ++v) {
+ drv = recursion.objs[v];
+ if (!drv->depfeatures) {
+ continue;
+ }
+ for (u = 0; u < LY_ARRAY_SIZE(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);
+ goto cleanup;
+ }
+ ly_set_add(&recursion, drv->depfeatures[u], 0);
+ }
+ }
+ ret = LY_SUCCESS;
+
+cleanup:
+ ly_set_erase(&recursion, NULL);
+ return ret;
+}
+
+/**
* @brief Create pre-compiled features array.
*
* See lys_feature_precompile() for more details.
@@ -687,11 +913,19 @@
for (u = 0; u < LY_ARRAY_SIZE(feature->iffeatures); ++u) {
if (feature->iffeatures[u].features) {
for (v = 0; v < LY_ARRAY_SIZE(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;
}
- /* TODO check for circular dependency */
}
}
}
@@ -704,6 +938,42 @@
}
/**
+ * @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.
+ * The features are supposed to be stored again as off_features in ::lys_module structure.
+ *
+ * @param[in] ctx Compilation context.
+ * @param[in] mod The module structure still holding the compiled (but possibly not finished, only the list of compiled features is taken) schema
+ * and supposed to hold the off_features list.
+ */
+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.
* @param[in] basetype Base YANG built-in type of the node connected with the range restriction. Actually only LY_TYPE_DEC64 is important to
@@ -1208,7 +1478,6 @@
parts = NULL;
ret = LY_SUCCESS;
cleanup:
- /* TODO clean up */
LY_ARRAY_FREE(parts);
return ret;
@@ -2753,6 +3022,10 @@
unsigned int u;
LY_ERR ret = LY_SUCCESS;
+ if (cont_p->presence) {
+ cont->flags |= LYS_PRESENCE;
+ }
+
LY_LIST_FOR(cont_p->child, child_p) {
LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node, 0));
}
@@ -2846,6 +3119,9 @@
}
llist->min = llist_p->min;
+ if (llist->min) {
+ llist->flags |= LYS_MAND_TRUE;
+ }
llist->max = llist_p->max ? llist_p->max : (uint32_t)-1;
ret = lys_compile_type(ctx, node_p, node_p->flags, ctx->mod_def->parsed, node_p->name, &llist_p->type, options, &llist->type,
@@ -2921,6 +3197,9 @@
LY_ERR ret = LY_SUCCESS;
list->min = list_p->min;
+ if (list->min) {
+ list->flags |= LYS_MAND_TRUE;
+ }
list->max = list_p->max ? list_p->max : (uint32_t)-1;
LY_LIST_FOR(list_p->child, child_p) {
@@ -3025,7 +3304,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,
@@ -3067,6 +3346,17 @@
return ret;
}
+/**
+ * @brief Do some checks and set the default choice's case.
+ *
+ * Selects (and stores into ::lysc_node_choice#dflt) the default case and set LYS_SET_DFLT flag on it.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] dflt Name of the default branch. Can contain even the prefix, but it make sense only in case it is the prefix of the module itself,
+ * not the reference to the imported module.
+ * @param[in,out] ch The compiled choice node, its dflt member is filled to point to the default case node of the choice.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_node_choice_dflt(struct lysc_ctx *ctx, const char *dflt, struct lysc_node_choice *ch)
{
@@ -3171,24 +3461,18 @@
return ret;
}
-static LY_ERR
-lys_compile_status_check(struct lysc_ctx *ctx, uint16_t node_flags, uint16_t parent_flags)
-{
- /* check status compatibility with the parent */
- if ((parent_flags & LYS_STATUS_MASK) > (node_flags & LYS_STATUS_MASK)) {
- if (node_flags & LYS_STATUS_CURR) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "A \"current\" status is in conflict with the parent's \"%s\" status.",
- (parent_flags & LYS_STATUS_DEPRC) ? "deprecated" : "obsolete");
- } else { /* LYS_STATUS_DEPRC */
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "A \"deprecated\" status is in conflict with the parent's \"obsolete\" status.");
- }
- return LY_EVALID;
- }
- return LY_SUCCESS;
-}
-
+/**
+ * @brief Compile status information of the given node.
+ *
+ * To simplify getting status of the node, the flags are set following inheritance rules, so all the nodes
+ * has the status correctly set during the compilation.
+ *
+ * @param[in] ctx Compile context
+ * @param[in,out] node Compiled node which status is supposed to be resolved. If the status was set explicitely on the node, it is already set in the
+ * flags value and we just check the compatibility with the parent's status value.
+ * @param[in] parent_flags Flags of the parent node to check/inherit the status value.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_status(struct lysc_ctx *ctx, struct lysc_node *node, uint16_t parent_flags)
{
@@ -3207,11 +3491,37 @@
node->flags |= LYS_STATUS_CURR;
}
} else if (parent_flags & LYS_STATUS_MASK) {
- return lys_compile_status_check(ctx, node->flags, parent_flags);
+ /* check status compatibility with the parent */
+ if ((parent_flags & LYS_STATUS_MASK) > (node->flags & LYS_STATUS_MASK)) {
+ if (node->flags & LYS_STATUS_CURR) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "A \"current\" status is in conflict with the parent's \"%s\" status.",
+ (parent_flags & LYS_STATUS_DEPRC) ? "deprecated" : "obsolete");
+ } else { /* LYS_STATUS_DEPRC */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "A \"deprecated\" status is in conflict with the parent's \"obsolete\" status.");
+ }
+ return LY_EVALID;
+ }
}
return LY_SUCCESS;
}
+/**
+ * @brief Check uniqness of the node/action/notification name.
+ *
+ * Data nodes, actions/RPCs and Notifications are stored separately (in distinguish lists) in the schema
+ * structures, but they share the namespace so we need to check their name collisions.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] children List (linked list) of data nodes to go through.
+ * @param[in] actions List (sized array) of actions or RPCs to go through.
+ * @param[in] notifs List (sized array) of Notifications to go through.
+ * @param[in] name Name of the item to find in the given lists.
+ * @param[in] exclude Pointer to an object to exclude from the name checking - for the case that the object
+ * with the @p name being checked is already inserted into one of the list so we need to skip it when searching for duplicity.
+ * @return LY_SUCCESS in case of unique name, LY_EEXIST otherwise.
+ */
static LY_ERR
lys_compile_node_uniqness(struct lysc_ctx *ctx, const struct lysc_node *children,
const struct lysc_action *actions, const struct lysc_notif *notifs,
@@ -3221,17 +3531,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,35 +3595,65 @@
return LY_SUCCESS;
}
+/**
+ * @brief Get the XPath context node for the given schema node.
+ * @param[in] start The schema node where the XPath expression appears.
+ * @return The context node to evaluate XPath expression in given schema node.
+ * @return NULL in case the context node is the root node.
+ */
+static struct lysc_node *
+lysc_xpath_context(struct lysc_node *start)
+{
+ for (; start && !(start->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_ACTION | LYS_NOTIF));
+ start = start->parent);
+ return start;
+}
+
+/**
+ * @brief Prepare the case structure in choice node for the new data node.
+ *
+ * It is able to handle implicit as well as explicit cases and the situation when the case has multiple data nodes and the case was already
+ * created in the choice when the first child was processed.
+ *
+ * @param[in] ctx Compile context.
+ * @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.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] ch The compiled choice structure where the new case structures are created (if needed).
+ * @param[in] child The new data node being part of a case (no matter if explicit or implicit).
+ * @return The case structure where the child node belongs to, NULL in case of error. Note that the child is not connected into the siblings list,
+ * it is linked from the case structure only in case it is its first child.
+ */
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)
{
struct lysc_node *iter;
struct lysc_node_case *cs;
+ struct lysc_when **when;
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));
@@ -3323,8 +3663,17 @@
/* check the case's status (don't need to solve uses_status since case statement cannot be directly in grouping statement */
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);
+
+ if (node_p->when) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, cs->when, when, ret, error);
+ ret = lys_compile_when(ctx, node_p->when, options, when);
+ LY_CHECK_GOTO(ret, error);
+ (*when)->context = lysc_xpath_context(ch->parent);
+ }
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;
@@ -3340,6 +3689,17 @@
#undef UNIQUE_CHECK
}
+/**
+ * @brief Apply refined config to the refine's target node.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] node Refine's target node.
+ * @param[in] rfn Parsed refine information.
+ * @param[in] inheriting Flag (inverted) to check the refined config compatibility with the node's parent. This is
+ * done only on the node for which the refine was created. The function applies also recursively to apply the config change
+ * to the complete subtree and the test is not needed for the subnodes.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_refine_config(struct lysc_ctx *ctx, struct lysc_node *node, struct lysp_refine *rfn, int inheriting)
{
@@ -3374,6 +3734,229 @@
}
/**
+ * @brief Set LYS_MAND_TRUE flag for the non-presence container parents.
+ *
+ * A non-presence container is mandatory in case it has at least one mandatory children. This function propagate
+ * the flag to such parents from a mandatory children.
+ *
+ * @param[in] parent A schema node to be examined if the mandatory child make it also mandatory.
+ * @param[in] add Flag to distinguish adding the mandatory flag (new mandatory children appeared) or removing the flag
+ * (mandatory children was removed).
+ */
+void
+lys_compile_mandatory_parents(struct lysc_node *parent, int add)
+{
+ struct lysc_node *iter;
+
+ if (add) { /* set flag */
+ for (; parent && parent->nodetype == LYS_CONTAINER && !(parent->flags & LYS_MAND_TRUE) && !(parent->flags & LYS_PRESENCE);
+ parent = parent->parent) {
+ parent->flags |= LYS_MAND_TRUE;
+ }
+ } else { /* unset flag */
+ for (; parent && parent->nodetype == LYS_CONTAINER && (parent->flags & LYS_MAND_TRUE); parent = parent->parent) {
+ for (iter = (struct lysc_node*)lysc_node_children(parent); iter; iter = iter->next) {
+ if (iter->flags && LYS_MAND_TRUE) {
+ /* there is another mandatory node */
+ return;
+ }
+ }
+ /* unset mandatory flag - there is no mandatory children in the non-presence container */
+ parent->flags &= ~LYS_MAND_TRUE;
+ }
+ }
+}
+
+/**
+ * @brief Internal sorting process for the lys_compile_augment_sort().
+ * @param[in] aug_p The parsed augment structure to insert into the sorter sized array @p result.
+ * @param[in,out] result Sized array to store the sorted list of augments. The array is expected
+ * to be allocated to hold the complete list, its size is just incremented by adding another item.
+ */
+static void
+lys_compile_augment_sort_(struct lysp_augment *aug_p, struct lysp_augment **result)
+{
+ unsigned int v;
+ size_t len;
+
+ len = strlen(aug_p->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;
+ LY_ARRAY_INCREMENT(result);
+}
+
+/**
+ * @brief Sort augments to apply /a/b before /a/b/c (where the /a/b/c was added by the first augment).
+ *
+ * The sorting is based only on the length of the augment's path since it guarantee the correct order
+ * (it doesn't matter the /a/x is done before /a/b/c from the example above).
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] mod_p Parsed module with the global augments (also augments from the submodules are taken).
+ * @param[in] aug_p Parsed sized array of augments to sort (no matter if global or uses's)
+ * @param[in] inc_p In case of global augments, sized array of module includes (submodules) to get global augments from submodules.
+ * @param[out] augments Resulting sorted sized array of pointers to the augments.
+ * @return LY_ERR value.
+ */
+LY_ERR
+lys_compile_augment_sort(struct lysc_ctx *ctx, struct lysp_augment *aug_p, struct lysp_include *inc_p, struct lysp_augment ***augments)
+{
+ struct lysp_augment **result = NULL;
+ unsigned int u, v;
+ size_t count = 0;
+
+ assert(augments);
+
+ /* get count of the augments in module and all its submodules */
+ if (aug_p) {
+ count += LY_ARRAY_SIZE(aug_p);
+ }
+ LY_ARRAY_FOR(inc_p, u) {
+ if (inc_p[u].submodule->augments) {
+ count += LY_ARRAY_SIZE(inc_p[u].submodule->augments);
+ }
+ }
+
+ if (!count) {
+ *augments = NULL;
+ return LY_SUCCESS;
+ }
+ LY_ARRAY_CREATE_RET(ctx->ctx, result, count, 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) {
+ lys_compile_augment_sort_(&aug_p[u], result);
+ }
+ LY_ARRAY_FOR(inc_p, u) {
+ LY_ARRAY_FOR(inc_p[u].submodule->augments, v) {
+ lys_compile_augment_sort_(&inc_p[u].submodule->augments[v], 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 */
+ struct lysc_node *node;
+ struct lysc_node_case *next_case;
+ struct lysc_when **when, *when_shared;
+ int allow_mandatory = 0;
+
+ 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;
+ }
+
+ /* check for mandatory nodes
+ * - new cases augmenting some choice can have mandatory nodes
+ * - mandatory nodes are allowed only in case the augmentation is made conditional with a when statement
+ */
+ if (aug_p->when || target->nodetype == LYS_CHOICE) {
+ allow_mandatory = 1;
+ }
+
+ when_shared = NULL;
+ 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));
+ }
+ }
+
+ /* since the augment node is not present in the compiled tree, we need to pass some of its statements to all its children */
+ if (target->nodetype == LYS_CASE) {
+ /* the compiled node is the last child of the target (but it is a case, so we have to be careful) */
+ next_case = target->next ? (struct lysc_node_case*)target->next : ((struct lysc_node_choice*)target->parent)->cases;
+ for (node = (struct lysc_node*)lysc_node_children(target); node->next && node->next != next_case->child; node = node->next);
+ } else if (target->nodetype == LYS_CHOICE) {
+ /* to pass when statement, we need the last case no matter if it is explicit or implicit case */
+ node = ((struct lysc_node_choice*)target)->cases->prev;
+ } else {
+ /* the compiled node is the last child of the target */
+ node = lysc_node_children(target)->prev;
+ }
+
+ if (!allow_mandatory && (node->flags & LYS_MAND_TRUE)) {
+ node->flags &= ~LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(target, 0);
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid augment (%s) adding mandatory node \"%s\" without making it conditional via when statement.",
+ aug_p->nodeid, node->name);
+ return LY_EVALID;
+ }
+
+ /* pass augment's when to all the children */
+ if (aug_p->when) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, node->when, when, ret, error);
+ if (!when_shared) {
+ ret = lys_compile_when(ctx, aug_p->when, options, when);
+ LY_CHECK_GOTO(ret, error);
+ (*when)->context = lysc_xpath_context(target);
+ when_shared = *when;
+ } else {
+ ++when_shared->refcount;
+ (*when) = when_shared;
+ }
+ }
+ }
+ /* TODO actions, notifications */
+
+error:
+ return ret;
+}
+
+/**
* @brief Compile parsed uses statement - resolve target grouping and connect its content into parent.
* If present, also apply uses's modificators.
*
@@ -3407,6 +3990,8 @@
LY_ERR ret = LY_EVALID;
uint32_t min, max;
struct ly_set refined = {0};
+ struct lysc_when **when, *when_shared;
+ struct lysp_augment **augments = NULL;
/* search for the grouping definition */
found = 0;
@@ -3487,30 +4072,47 @@
/* 0x3 in uses_status is a special bits combination to be able to detect status flags from uses */
LY_CHECK_GOTO(lys_compile_node(ctx, node_p, options, parent, (uses_p->flags & LYS_STATUS_MASK) | 0x3), error);
child = parent ? lysc_node_children(parent)->prev : ctx->mod->compiled->data->prev;
- if (uses_p->refines) {
- /* some preparation for applying refines */
- if (grp->data == node_p) {
- /* remember the first child */
- context_node_fake.child = child;
- }
+
+ /* some preparation for applying refines */
+ if (grp->data == node_p) {
+ /* remember the first child */
+ context_node_fake.child = child;
}
}
+ when_shared = NULL;
LY_LIST_FOR(context_node_fake.child, child) {
child->parent = (struct lysc_node*)&context_node_fake;
+
+ /* pass uses's when to all the children */
+ if (uses_p->when) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, child->when, when, ret, error);
+ if (!when_shared) {
+ LY_CHECK_GOTO(lys_compile_when(ctx, uses_p->when, options, when), error);
+ (*when)->context = lysc_xpath_context(parent);
+ when_shared = *when;
+ } else {
+ ++when_shared->refcount;
+ (*when) = when_shared;
+ }
+ }
}
if (context_node_fake.child) {
child = context_node_fake.child->prev;
context_node_fake.child->prev = parent ? lysc_node_children(parent)->prev : ctx->mod->compiled->data->prev;
}
- /* TODO: apply augment */
+ /* sort and apply augments */
+ LY_CHECK_GOTO(lys_compile_augment_sort(ctx, uses_p->augments, NULL, &augments), error);
+ LY_ARRAY_FOR(augments, u) {
+ LY_CHECK_GOTO(lys_compile_augment(ctx, augments[u], options, (struct lysc_node*)&context_node_fake), error);
+ }
/* reload previous context's mod_def */
ctx->mod_def = mod_old;
/* 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);
@@ -3610,9 +4212,11 @@
}
node->flags |= LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(node->parent, 1);
} else {
/* make mandatory false */
node->flags &= ~LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(node->parent, 0);
if ((node->nodetype & LYS_LEAF) && !((struct lysc_node_leaf*)node)->dflt) {
/* get the type's default value if any */
DUP_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->type->dflt, ((struct lysc_node_leaf*)node)->dflt);
@@ -3667,6 +4271,13 @@
}
if (rfn->flags & LYS_SET_MIN) {
((struct lysc_node_leaflist*)node)->min = rfn->min;
+ if (rfn->min) {
+ node->flags |= LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(node->parent, 1);
+ } else {
+ node->flags &= ~LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(node->parent, 0);
+ }
}
break;
case LYS_LIST:
@@ -3675,6 +4286,13 @@
}
if (rfn->flags & LYS_SET_MIN) {
((struct lysc_node_list*)node)->min = rfn->min;
+ if (rfn->min) {
+ node->flags |= LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(node->parent, 1);
+ } else {
+ node->flags &= ~LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(node->parent, 0);
+ }
}
break;
default:
@@ -3738,10 +4356,12 @@
ly_set_rm_index(&ctx->groupings, ctx->groupings.count - 1, NULL);
assert(ctx->groupings.count == grp_stack_count);
ly_set_erase(&refined, NULL);
+ LY_ARRAY_FREE(augments);
return ret;
}
+
/**
* @brief Compile parsed schema node information.
* @param[in] ctx Compile context
@@ -3760,6 +4380,7 @@
LY_ERR ret = LY_EVALID;
struct lysc_node *node;
struct lysc_node_case *cs;
+ struct lysc_when **when;
unsigned int u;
LY_ERR (*node_compile_spec)(struct lysc_ctx*, struct lysp_node*, int, struct lysc_node*);
@@ -3844,13 +4465,23 @@
DUP_STRING(ctx->ctx, node_p->name, node->name);
DUP_STRING(ctx->ctx, node_p->dsc, node->dsc);
DUP_STRING(ctx->ctx, node_p->ref, node->ref);
- COMPILE_MEMBER_GOTO(ctx, node_p->when, node->when, options, lys_compile_when, ret, error);
+ if (node_p->when) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, node->when, when, ret, error);
+ ret = lys_compile_when(ctx, node_p->when, options, when);
+ LY_CHECK_GOTO(ret, error);
+ (*when)->context = lysc_xpath_context(node);
+ }
COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, node->iffeatures, options, u, lys_compile_iffeature, ret, error);
COMPILE_ARRAY_GOTO(ctx, node_p->exts, node->exts, options, u, lys_compile_ext, ret, error);
/* nodetype-specific part */
LY_CHECK_GOTO(node_compile_spec(ctx, node_p, options, node), error);
+ /* inherit LYS_MAND_TRUE in parent containers */
+ if (node->flags & LYS_MAND_TRUE) {
+ lys_compile_mandatory_parents(parent, 1);
+ }
+
/* insert into parent's children */
if (parent) {
if (parent->nodetype == LYS_CHOICE) {
@@ -3867,7 +4498,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) {
@@ -3930,8 +4561,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 +4592,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 +4617,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, sp->includes, &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 +4682,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_free.c b/src/tree_schema_free.c
index ce0dfb8..aa026f7 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -479,12 +479,16 @@
}
static void
-lysc_when_free(struct ly_ctx *ctx, struct lysc_when *w)
+lysc_when_free(struct ly_ctx *ctx, struct lysc_when **w)
{
- lyxp_expr_free(ctx, w->cond);
- FREE_STRING(ctx, w->dsc);
- FREE_STRING(ctx, w->ref);
- FREE_ARRAY(ctx, w->exts, lysc_ext_instance_free);
+ if (--(*w)->refcount) {
+ return;
+ }
+ lyxp_expr_free(ctx, (*w)->cond);
+ FREE_STRING(ctx, (*w)->dsc);
+ FREE_STRING(ctx, (*w)->ref);
+ FREE_ARRAY(ctx, (*w)->exts, lysc_ext_instance_free);
+ free(*w);
}
static void
@@ -742,7 +746,7 @@
LOGINT(ctx);
}
- FREE_MEMBER(ctx, node->when, lysc_when_free);
+ 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);
@@ -773,6 +777,9 @@
void
lysc_module_free(struct lysc_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv))
{
+ /* TODO use the destructor, this just suppress warning about unused parameter */
+ (void) private_destructor;
+
if (module) {
lysc_module_free_(module);
}
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 74e950d..cd056d9 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -85,38 +85,68 @@
}
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;
+
+ if (*id == '/') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid descendant-schema-nodeid value \"%.*s\" - absolute-schema-nodeid used.",
+ nodeid_len ? nodeid_len : strlen(nodeid), nodeid);
+ return LY_EVALID;
+ }
+ } 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_len : strlen(nodeid), 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 +154,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 +166,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_len : strlen(nodeid), nodeid);
}
return ret;
@@ -897,6 +931,8 @@
return "anyxml";
case LYS_ANYDATA:
return "anydata";
+ case LYS_CASE:
+ return "case";
default:
return "unknown";
}
@@ -1103,7 +1139,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;
}
@@ -1148,7 +1184,7 @@
}
enum yang_keyword
-match_keyword(const char *data)
+match_keyword(const char *data, size_t len)
{
/* TODO make this function usable in get_keyword function */
#define MOVE_IN(DATA, COUNT) (data)+=COUNT;
@@ -1156,6 +1192,7 @@
#define IF_KEYWORD_PREFIX(STR, LEN) if (!strncmp((data), STR, LEN)) {MOVE_IN(data, LEN);
#define IF_KEYWORD_PREFIX_END }
+ const char *start = data;
enum yang_keyword kw = YANG_NONE;
/* read the keyword itself */
switch (*data) {
@@ -1339,6 +1376,10 @@
break;
}
- /* TODO important fix whole keyword must be matched */
- return kw;
+ if (data - start == (long int)len) {
+ return kw;
+ } else {
+ return YANG_NONE;
+ }
+
}
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 1a3e078..563ec0f 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.
@@ -527,7 +531,19 @@
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);
+
+/**
* @brief match yang keyword
*/
-enum yang_keyword match_keyword(const char *data);
+enum yang_keyword match_keyword(const char *data, size_t len);
#endif /* LY_TREE_SCHEMA_INTERNAL_H_ */
diff --git a/tests/src/test_parser_yang.c b/tests/src/test_parser_yang.c
index 7a034ee..d0935ad 100644
--- a/tests/src/test_parser_yang.c
+++ b/tests/src/test_parser_yang.c
@@ -1101,12 +1101,7 @@
TEST_DUP_GENERIC(" test {", MEMBER, VALUE1, VALUE2, parse_identity, \
&ident, "1", FREE_ARRAY(ctx.ctx, ident, lysp_ident_free); ident = NULL)
- //TEST_DUP("description", "a", "b");
- str = " test {description a;description b;} ...";
- assert_int_equal(LY_EVALID, parse_identity(&ctx, &str, &ident));
- logbuf_assert("Duplicate keyword \"description\". Line number 1.");
- FREE_ARRAY(ctx.ctx, ident, lysp_ident_free); ident = NULL;
-
+ TEST_DUP("description", "a", "b");
TEST_DUP("reference", "a", "b");
TEST_DUP("status", "current", "obsolete");
@@ -1905,6 +1900,55 @@
*state = NULL;
ly_ctx_destroy(ctx.ctx, NULL);
}
+
+
+static void
+test_augment(void **state)
+{
+ *state = test_augment;
+
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_augment *a = NULL;
+ const char *str;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx.ctx));
+ assert_non_null(ctx.ctx);
+ ctx.line = 1;
+ //ctx.mod_version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ str = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_augment(&ctx, &str, NULL, &a)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ lysp_augment_free(ctx.ctx, a); a = NULL;
+
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ str = "/target/nodeid {action x; anydata any;anyxml anyxml; case cs; choice ch;container c;description test;if-feature f;leaf l {type string;}"
+ "leaf-list ll {type string;} list li;notification not;reference test;status current;uses g;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_augment(&ctx, &str, NULL, &a));
+ assert_non_null(a);
+ assert_int_equal(LYS_AUGMENT, a->nodetype);
+ assert_string_equal("/target/nodeid", a->nodeid);
+ assert_string_equal("test", a->dsc);
+ assert_non_null(a->exts);
+ assert_non_null(a->iffeatures);
+ assert_string_equal("test", a->ref);
+ assert_non_null(a->when);
+ assert_null(a->parent);
+ assert_int_equal(LYS_STATUS_CURR, a->flags);
+ lysp_augment_free(ctx.ctx, a); a = NULL;
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
diff --git a/tests/src/test_parser_yin.c b/tests/src/test_parser_yin.c
index 870e5b4..f1dc88c 100644
--- a/tests/src/test_parser_yin.c
+++ b/tests/src/test_parser_yin.c
@@ -28,13 +28,14 @@
(void)state; /* unused */
struct ly_ctx *ctx;
- struct lysp_module *mod;
+ struct lys_module *mod;
+ LY_ERR ret = LY_SUCCESS;
ly_ctx_new(NULL, 0, &ctx);
mod = calloc(1, sizeof(*mod));
mod->ctx = ctx;
- yin_parse(ctx, "<module name=\"example-foo\"\
+ ret = yin_parse_module(ctx, "<module name=\"example-foo\"\
xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\
xmlns:foo=\"urn:example:foo\"\
xmlns:myext=\"urn:example:extensions\">\
@@ -59,12 +60,14 @@
</leaf>\
</list>\
</module>",
- &mod);
+ mod);
- assert_string_equal(mod->name, "example-foo");
- assert_string_equal(mod->prefix, "foo");
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_string_equal(mod->parsed->mod->name, "example-foo");
+ assert_string_equal(mod->parsed->mod->prefix, "foo");
+ assert_string_equal(mod->parsed->imports->name, "example-extensions");
- lysp_module_free(mod);
+ lys_module_free(mod, NULL);
ly_ctx_destroy(ctx, NULL);
}
@@ -79,76 +82,76 @@
{
(void)state; /* unused */
- assert_int_equal(match_keyword("anydata"), YANG_ANYDATA);
- assert_int_equal(match_keyword("asdasd"), YANG_NONE);
- assert_int_equal(match_keyword(""), YANG_NONE);
- assert_int_equal(match_keyword("anydata"), YANG_ANYDATA);
- assert_int_equal(match_keyword("anyxml"), YANG_ANYXML);
- assert_int_equal(match_keyword("argument"), YANG_ARGUMENT);
- assert_int_equal(match_keyword("augment"), YANG_AUGMENT);
- assert_int_equal(match_keyword("base"), YANG_BASE);
- assert_int_equal(match_keyword("belongs-to"), YANG_BELONGS_TO);
- assert_int_equal(match_keyword("bit"), YANG_BIT);
- assert_int_equal(match_keyword("case"), YANG_CASE);
- assert_int_equal(match_keyword("choice"), YANG_CHOICE);
- assert_int_equal(match_keyword("config"), YANG_CONFIG);
- assert_int_equal(match_keyword("contact"), YANG_CONTACT);
- assert_int_equal(match_keyword("container"), YANG_CONTAINER);
- assert_int_equal(match_keyword("default"), YANG_DEFAULT);
- assert_int_equal(match_keyword("description"), YANG_DESCRIPTION);
- assert_int_equal(match_keyword("deviate"), YANG_DEVIATE);
- assert_int_equal(match_keyword("deviation"), YANG_DEVIATION);
- assert_int_equal(match_keyword("enum"), YANG_ENUM);
- assert_int_equal(match_keyword("error-app-tag"), YANG_ERROR_APP_TAG);
- assert_int_equal(match_keyword("error-message"), YANG_ERROR_MESSAGE);
- assert_int_equal(match_keyword("extension"), YANG_EXTENSION);
- assert_int_equal(match_keyword("feature"), YANG_FEATURE);
- assert_int_equal(match_keyword("fraction-digits"), YANG_FRACTION_DIGITS);
- assert_int_equal(match_keyword("grouping"), YANG_GROUPING);
- assert_int_equal(match_keyword("identity"), YANG_IDENTITY);
- assert_int_equal(match_keyword("if-feature"), YANG_IF_FEATURE);
- assert_int_equal(match_keyword("import"), YANG_IMPORT);
- assert_int_equal(match_keyword("include"), YANG_INCLUDE);
- assert_int_equal(match_keyword("input"), YANG_INPUT);
- assert_int_equal(match_keyword("key"), YANG_KEY);
- assert_int_equal(match_keyword("leaf"), YANG_LEAF);
- assert_int_equal(match_keyword("leaf-list"), YANG_LEAF_LIST);
- assert_int_equal(match_keyword("length"), YANG_LENGTH);
- assert_int_equal(match_keyword("list"), YANG_LIST);
- assert_int_equal(match_keyword("mandatory"), YANG_MANDATORY);
- assert_int_equal(match_keyword("max-elements"), YANG_MAX_ELEMENTS);
- assert_int_equal(match_keyword("min-elements"), YANG_MIN_ELEMENTS);
- assert_int_equal(match_keyword("modifier"), YANG_MODIFIER);
- assert_int_equal(match_keyword("module"), YANG_MODULE);
- assert_int_equal(match_keyword("must"), YANG_MUST);
- assert_int_equal(match_keyword("namespace"), YANG_NAMESPACE);
- assert_int_equal(match_keyword("notification"), YANG_NOTIFICATION);
- assert_int_equal(match_keyword("ordered-by"), YANG_ORDERED_BY);
- assert_int_equal(match_keyword("organization"), YANG_ORGANIZATION);
- assert_int_equal(match_keyword("output"), YANG_OUTPUT);
- assert_int_equal(match_keyword("path"), YANG_PATH);
- assert_int_equal(match_keyword("pattern"), YANG_PATTERN);
- assert_int_equal(match_keyword("position"), YANG_POSITION);
- assert_int_equal(match_keyword("prefix"), YANG_PREFIX);
- assert_int_equal(match_keyword("presence"), YANG_PRESENCE);
- assert_int_equal(match_keyword("range"), YANG_RANGE);
- assert_int_equal(match_keyword("reference"), YANG_REFERENCE);
- assert_int_equal(match_keyword("refine"), YANG_REFINE);
- assert_int_equal(match_keyword("require-instance"), YANG_REQUIRE_INSTANCE);
- assert_int_equal(match_keyword("revision"), YANG_REVISION);
- assert_int_equal(match_keyword("revision-date"), YANG_REVISION_DATE);
- assert_int_equal(match_keyword("rpc"), YANG_RPC);
- assert_int_equal(match_keyword("status"), YANG_STATUS);
- assert_int_equal(match_keyword("submodule"), YANG_SUBMODULE);
- assert_int_equal(match_keyword("type"), YANG_TYPE);
- assert_int_equal(match_keyword("typedef"), YANG_TYPEDEF);
- assert_int_equal(match_keyword("unique"), YANG_UNIQUE);
- assert_int_equal(match_keyword("units"), YANG_UNITS);
- assert_int_equal(match_keyword("uses"), YANG_USES);
- assert_int_equal(match_keyword("value"), YANG_VALUE);
- assert_int_equal(match_keyword("when"), YANG_WHEN);
- assert_int_equal(match_keyword("yang-version"), YANG_YANG_VERSION);
- assert_int_equal(match_keyword("yin-element"), YANG_YIN_ELEMENT);
+ assert_int_equal(match_keyword("anydatax", strlen("anydatax")), YANG_NONE);
+ assert_int_equal(match_keyword("asdasd", strlen("asdasd")), YANG_NONE);
+ assert_int_equal(match_keyword("", 0), YANG_NONE);
+ assert_int_equal(match_keyword("anydata", strlen("anydata")), YANG_ANYDATA);
+ assert_int_equal(match_keyword("anyxml", strlen("anyxml")), YANG_ANYXML);
+ assert_int_equal(match_keyword("argument", strlen("argument")), YANG_ARGUMENT);
+ assert_int_equal(match_keyword("augment", strlen("augment")), YANG_AUGMENT);
+ assert_int_equal(match_keyword("base", strlen("base")), YANG_BASE);
+ assert_int_equal(match_keyword("belongs-to", strlen("belongs-to")), YANG_BELONGS_TO);
+ assert_int_equal(match_keyword("bit", strlen("bit")), YANG_BIT);
+ assert_int_equal(match_keyword("case", strlen("case")), YANG_CASE);
+ assert_int_equal(match_keyword("choice", strlen("choice")), YANG_CHOICE);
+ assert_int_equal(match_keyword("config", strlen("config")), YANG_CONFIG);
+ assert_int_equal(match_keyword("contact", strlen("contact")), YANG_CONTACT);
+ assert_int_equal(match_keyword("container", strlen("container")), YANG_CONTAINER);
+ assert_int_equal(match_keyword("default", strlen("default")), YANG_DEFAULT);
+ assert_int_equal(match_keyword("description", strlen("description")), YANG_DESCRIPTION);
+ assert_int_equal(match_keyword("deviate", strlen("deviate")), YANG_DEVIATE);
+ assert_int_equal(match_keyword("deviation", strlen("deviation")), YANG_DEVIATION);
+ assert_int_equal(match_keyword("enum", strlen("enum")), YANG_ENUM);
+ assert_int_equal(match_keyword("error-app-tag", strlen("error-app-tag")), YANG_ERROR_APP_TAG);
+ assert_int_equal(match_keyword("error-message", strlen("error-message")), YANG_ERROR_MESSAGE);
+ assert_int_equal(match_keyword("extension", strlen("extension")), YANG_EXTENSION);
+ assert_int_equal(match_keyword("feature", strlen("feature")), YANG_FEATURE);
+ assert_int_equal(match_keyword("fraction-digits", strlen("fraction-digits")), YANG_FRACTION_DIGITS);
+ assert_int_equal(match_keyword("grouping", strlen("grouping")), YANG_GROUPING);
+ assert_int_equal(match_keyword("identity", strlen("identity")), YANG_IDENTITY);
+ assert_int_equal(match_keyword("if-feature", strlen("if-feature")), YANG_IF_FEATURE);
+ assert_int_equal(match_keyword("import", strlen("import")), YANG_IMPORT);
+ assert_int_equal(match_keyword("include", strlen("include")), YANG_INCLUDE);
+ assert_int_equal(match_keyword("input", strlen("input")), YANG_INPUT);
+ assert_int_equal(match_keyword("key", strlen("key")), YANG_KEY);
+ assert_int_equal(match_keyword("leaf", strlen("leaf")), YANG_LEAF);
+ assert_int_equal(match_keyword("leaf-list", strlen("leaf-list")), YANG_LEAF_LIST);
+ assert_int_equal(match_keyword("length", strlen("length")), YANG_LENGTH);
+ assert_int_equal(match_keyword("list", strlen("list")), YANG_LIST);
+ assert_int_equal(match_keyword("mandatory", strlen("mandatory")), YANG_MANDATORY);
+ assert_int_equal(match_keyword("max-elements", strlen("max-elements")), YANG_MAX_ELEMENTS);
+ assert_int_equal(match_keyword("min-elements", strlen("min-elements")), YANG_MIN_ELEMENTS);
+ assert_int_equal(match_keyword("modifier", strlen("modifier")), YANG_MODIFIER);
+ assert_int_equal(match_keyword("module", strlen("module")), YANG_MODULE);
+ assert_int_equal(match_keyword("must", strlen("must")), YANG_MUST);
+ assert_int_equal(match_keyword("namespace", strlen("namespace")), YANG_NAMESPACE);
+ assert_int_equal(match_keyword("notification", strlen("notification")), YANG_NOTIFICATION);
+ assert_int_equal(match_keyword("ordered-by", strlen("ordered-by")), YANG_ORDERED_BY);
+ assert_int_equal(match_keyword("organization", strlen("organization")), YANG_ORGANIZATION);
+ assert_int_equal(match_keyword("output", strlen("output")), YANG_OUTPUT);
+ assert_int_equal(match_keyword("path", strlen("path")), YANG_PATH);
+ assert_int_equal(match_keyword("pattern", strlen("pattern")), YANG_PATTERN);
+ assert_int_equal(match_keyword("position", strlen("position")), YANG_POSITION);
+ assert_int_equal(match_keyword("prefix", strlen("prefix")), YANG_PREFIX);
+ assert_int_equal(match_keyword("presence", strlen("presence")), YANG_PRESENCE);
+ assert_int_equal(match_keyword("range", strlen("range")), YANG_RANGE);
+ assert_int_equal(match_keyword("reference", strlen("reference")), YANG_REFERENCE);
+ assert_int_equal(match_keyword("refine", strlen("refine")), YANG_REFINE);
+ assert_int_equal(match_keyword("require-instance", strlen("require-instance")), YANG_REQUIRE_INSTANCE);
+ assert_int_equal(match_keyword("revision", strlen("revision")), YANG_REVISION);
+ assert_int_equal(match_keyword("revision-date", strlen("revision-date")), YANG_REVISION_DATE);
+ assert_int_equal(match_keyword("rpc", strlen("rpc")), YANG_RPC);
+ assert_int_equal(match_keyword("status", strlen("status")), YANG_STATUS);
+ assert_int_equal(match_keyword("submodule", strlen("submodule")), YANG_SUBMODULE);
+ assert_int_equal(match_keyword("type", strlen("type")), YANG_TYPE);
+ assert_int_equal(match_keyword("typedef", strlen("typedef")), YANG_TYPEDEF);
+ assert_int_equal(match_keyword("unique", strlen("unique")), YANG_UNIQUE);
+ assert_int_equal(match_keyword("units", strlen("units")), YANG_UNITS);
+ assert_int_equal(match_keyword("uses", strlen("uses")), YANG_USES);
+ assert_int_equal(match_keyword("value", strlen("value")), YANG_VALUE);
+ assert_int_equal(match_keyword("when", strlen("when")), YANG_WHEN);
+ assert_int_equal(match_keyword("yang-version", strlen("yang-version")), YANG_YANG_VERSION);
+ assert_int_equal(match_keyword("yin-element", strlen("yin-element")), YANG_YIN_ELEMENT);
}
static void
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index 1288ee3..cfdc33a 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;
@@ -353,6 +354,11 @@
assert_null(lys_parse_mem(ctx.ctx, "module z{namespace urn:z; prefix z; include sz;feature f1;}", LYS_IN_YANG));
logbuf_assert("Duplicate identifier \"f1\" of feature statement.");
+ assert_null(lys_parse_mem(ctx.ctx, "module aa{namespace urn:aa; prefix aa; feature f1 {if-feature f2;} feature f2 {if-feature f1;}}", LYS_IN_YANG));
+ logbuf_assert("Feature \"f1\" is indirectly referenced from itself.");
+ assert_null(lys_parse_mem(ctx.ctx, "module ab{namespace urn:ab; prefix ab; feature f1 {if-feature f1;}}", LYS_IN_YANG));
+ logbuf_assert("Feature \"f1\" is referenced from itself.");
+
/* import reference */
assert_non_null(modp = lys_parse_mem(ctx.ctx, str, LYS_IN_YANG));
assert_int_equal(LY_SUCCESS, lys_feature_enable(modp, "f1"));
@@ -372,12 +378,12 @@
struct ly_ctx *ctx;
struct lys_module *mod1, *mod2;
- const char *mod1_str = "module a {namespace urn:a;prefix a; identity a1;}";
- const char *mod2_str = "module b {yang-version 1.1;namespace urn:b;prefix b; import a {prefix a;}identity b1; identity b2; identity b3 {base b1; base b:b2; base a:a1;} identity b4 {base b:b1; base b3;}}";
assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
- assert_non_null(mod1 = lys_parse_mem(ctx, mod1_str, LYS_IN_YANG));
- assert_non_null(mod2 = lys_parse_mem(ctx, mod2_str, LYS_IN_YANG));
+ assert_non_null(mod1 = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a; identity a1;}", LYS_IN_YANG));
+ assert_non_null(mod2 = lys_parse_mem(ctx, "module b {yang-version 1.1;namespace urn:b;prefix b; import a {prefix a;}"
+ "identity b1; identity b2; identity b3 {base b1; base b:b2; base a:a1;}"
+ "identity b4 {base b:b1; base b3;}}", LYS_IN_YANG));
assert_non_null(mod1->compiled);
assert_non_null(mod1->compiled->identities);
@@ -398,13 +404,26 @@
assert_int_equal(1, LY_ARRAY_SIZE(mod2->compiled->identities[2].derived));
assert_ptr_equal(mod2->compiled->identities[2].derived[0], &mod2->compiled->identities[3]);
- assert_null(lys_parse_mem(ctx, "module c{namespace urn:c; prefix c; identity i1;identity i1;}", LYS_IN_YANG));
+ assert_non_null(mod2 = lys_parse_mem(ctx, "module c {yang-version 1.1;namespace urn:c;prefix c;"
+ "identity c2 {base c1;} identity c1;}", LYS_IN_YANG));
+ assert_int_equal(1, LY_ARRAY_SIZE(mod2->compiled->identities[1].derived));
+ assert_ptr_equal(mod2->compiled->identities[1].derived[0], &mod2->compiled->identities[0]);
+
+ assert_null(lys_parse_mem(ctx, "module aa{namespace urn:aa; prefix aa; identity i1;identity i1;}", LYS_IN_YANG));
logbuf_assert("Duplicate identifier \"i1\" of identity statement.");
- ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule sd {belongs-to d {prefix d;} identity i1;}");
- assert_null(lys_parse_mem(ctx, "module d{namespace urn:d; prefix d; include sd;identity i1;}", LYS_IN_YANG));
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule sbb {belongs-to bb {prefix bb;} identity i1;}");
+ assert_null(lys_parse_mem(ctx, "module bb{namespace urn:bb; prefix bb; include sbb;identity i1;}", LYS_IN_YANG));
logbuf_assert("Duplicate identifier \"i1\" of identity statement.");
+ assert_null(lys_parse_mem(ctx, "module cc{namespace urn:cc; prefix cc; identity i1 {base i2;}}", LYS_IN_YANG));
+ logbuf_assert("Unable to find base (i2) of identity \"i1\".");
+
+ assert_null(lys_parse_mem(ctx, "module dd{namespace urn:dd; prefix dd; identity i1 {base i1;}}", LYS_IN_YANG));
+ logbuf_assert("Identity \"i1\" is derived from itself.");
+ assert_null(lys_parse_mem(ctx, "module de{namespace urn:de; prefix de; identity i1 {base i2;}identity i2 {base i3;}identity i3 {base i1;}}", LYS_IN_YANG));
+ logbuf_assert("Identity \"i1\" is indirectly derived from itself.");
+
*state = NULL;
ly_ctx_destroy(ctx, NULL);
}
@@ -2102,7 +2121,7 @@
struct ly_ctx *ctx;
struct lys_module *mod;
- struct lysc_node *parent, *child;
+ const struct lysc_node *parent, *child;
assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
@@ -2139,11 +2158,12 @@
assert_string_equal("f", child->iffeatures[0].features[0]->name);
assert_int_equal(1, lysc_iffeature_value(&child->iffeatures[0]));
- ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule bsub {belongs-to b {prefix b;} grouping grp {leaf b {type string;}}}");
- assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;include bsub;uses grp;}", LYS_IN_YANG));
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule bsub {belongs-to b {prefix b;} grouping grp {leaf b {when 1; type string;}}}");
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;include bsub;uses grp {when 2;}}", LYS_IN_YANG));
assert_non_null(mod->compiled->data);
assert_int_equal(LYS_LEAF, mod->compiled->data->nodetype);
assert_string_equal("b", mod->compiled->data->name);
+ assert_int_equal(2, LY_ARRAY_SIZE(mod->compiled->data->when));
logbuf_clean();
assert_non_null(mod = lys_parse_mem(ctx, "module c {namespace urn:ii;prefix ii;"
@@ -2157,6 +2177,14 @@
assert_true(LYS_STATUS_OBSLT & mod->compiled->data->next->flags);
logbuf_assert(""); /* no warning about inheriting deprecated flag from uses */
+ assert_non_null(mod = lys_parse_mem(ctx, "module d {namespace urn:d;prefix d; grouping grp {container g;}"
+ "container top {uses grp {augment g {leaf x {type int8;}}}}}", LYS_IN_YANG));
+ assert_non_null(mod->compiled->data);
+ assert_non_null(child = lysc_node_children(mod->compiled->data));
+ assert_string_equal("g", child->name);
+ assert_non_null(child = lysc_node_children(child));
+ assert_string_equal("x", child->name);
+
/* invalid */
assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;uses missinggrp;}", LYS_IN_YANG));
logbuf_assert("Grouping \"missinggrp\" referenced by a uses statement not found.");
@@ -2178,6 +2206,20 @@
"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.");
+
+
+ assert_null(lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg; grouping grp {container g;}"
+ "leaf g {type string;}"
+ "container top {uses grp {augment /g {leaf x {type int8;}}}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid descendant-schema-nodeid value \"/g\" - absolute-schema-nodeid used.");
+
+
*state = NULL;
ly_ctx_destroy(ctx, NULL);
}
@@ -2326,6 +2368,123 @@
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 {when 1; leaf lc2 {type uint16;}}"
+ "augment /ch { when 1; 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_null(c->when);
+ 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_non_null(c->when);
+ 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_non_null(c->when);
+ assert_string_equal("lc1", ((const struct lysc_node_case*)c)->child->name);
+ assert_null(((const struct lysc_node_case*)c)->child->when);
+ assert_string_equal("lc2", ((const struct lysc_node_case*)c)->child->next->name);
+ assert_non_null(((const struct lysc_node_case*)c)->child->next->when);
+ 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");
+
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule gsub {belongs-to g {prefix g;}"
+ "augment /c {container sub;}}");
+ assert_non_null(mod = lys_parse_mem(ctx, "module g {namespace urn:g;prefix g;include gsub; container c;"
+ "augment /c/sub {leaf main {type string;}}}", LYS_IN_YANG));
+ assert_non_null(mod->compiled->data);
+ assert_string_equal("c", mod->compiled->data->name);
+ assert_non_null(node = ((struct lysc_node_container*)mod->compiled->data)->child);
+ assert_string_equal("sub", node->name);
+ assert_non_null(node = ((struct lysc_node_container*)node)->child);
+ assert_string_equal("main", node->name);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module h {namespace urn:h;prefix h;container top;"
+ "augment /top {container p {presence XXX; leaf x {mandatory true;type string;}}}"
+ "augment /top {list l {key x;leaf x {type string;}leaf y {mandatory true; type string;}}}}", LYS_IN_YANG));
+ assert_non_null(node = mod->compiled->data);
+ assert_non_null(node = ((struct lysc_node_container*)node)->child);
+ assert_string_equal("p", node->name);
+ assert_non_null(node->next);
+ assert_string_equal("l", node->next->name);
+
+ 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.");
+
+
+ assert_null(lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc; container c {leaf a {type string;}}"
+ "augment /c/a {leaf a {type int8;}}}", LYS_IN_YANG));
+ logbuf_assert("Augment's absolute-schema-nodeid \"/c/a\" refers to a leaf node which is not an allowed augment's target.");
+
+ assert_null(lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd; container c {leaf a {type string;}}"
+ "augment /c {case b {leaf d {type int8;}}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid augment (/c) of container node which is not allowed to contain case node \"b\".");
+
+ assert_null(lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee; container top;"
+ "augment /top {container c {leaf d {mandatory true; type int8;}}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid augment (/top) adding mandatory node \"c\" without making it conditional via when statement.");
+
+ assert_null(lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff; container top;"
+ "augment ../top {leaf x {type int8;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid absolute-schema-nodeid value \"../top\" - missing starting \"/\".");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
@@ -2352,6 +2511,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);