Merge remote-tracking branch 'upstream/libyang2' into libyang2
diff --git a/.travis.yml b/.travis.yml
index 29c8b72..45b2768 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -32,7 +32,7 @@
script:
- mkdir build && cd build
- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then cmake ..; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then cmake -DENABLE_VALGRIND_TESTS=OFF ..; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cmake ..; fi
- make -j2 && ctest --output-on-failure
- cd -
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3d5e883..b45c692 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,7 +21,7 @@
set(CMAKE_BUILD_TYPE debug)
endif()
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=c99")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers -std=c99")
set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG")
set(CMAKE_C_FLAGS_PACKAGE "-g -O2 -DNDEBUG")
set(CMAKE_C_FLAGS_DEBUG "-g -O0")
@@ -158,10 +158,13 @@
src/set.c
src/context.c
src/tree_schema.c
+ src/tree_schema_free.c
+ src/tree_schema_compile.c
src/tree_schema_helpers.c
src/parser_yang.c
src/parser_yin.c
- src/xml.c)
+ src/xml.c
+ src/xpath.c)
#set(lintsrc
# tools/lint/main.c
@@ -179,7 +182,9 @@
set(headers
src/libyang.h
+ src/context.h
src/tree_schema.h
+ src/extensions.h
src/dict.h
src/log.h
src/set.h)
@@ -207,7 +212,7 @@
endif(ENABLE_STATIC)
set_target_properties(yang PROPERTIES VERSION ${LIBYANG_VERSION} SOVERSION ${LIBYANG_SOVERSION})
-set_target_properties(yang PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
+set_target_properties(yangobj PROPERTIES COMPILE_FLAGS "-fvisibility=hidden")
# link math
target_link_libraries(yang m)
diff --git a/Doxyfile.in b/Doxyfile.in
index 6d7170a..dbc9674 100644
--- a/Doxyfile.in
+++ b/Doxyfile.in
@@ -782,6 +782,7 @@
# Note: If this tag is empty the current directory is searched.
INPUT = ./src/libyang.h \
+ ./src/context.h \
./src/tree_schema.h \
./src/log.h \
./src/set.h \
diff --git a/LICENSE b/LICENSE
index f7cafac..5b323cd 100644
--- a/LICENSE
+++ b/LICENSE
@@ -11,7 +11,7 @@
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
-* Neither the name of libyang nor the names of its
+* Neither the name of CESNET nor the names of
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
diff --git a/src/common.c b/src/common.c
index ab3b71f..dc96310 100644
--- a/src/common.c
+++ b/src/common.c
@@ -11,11 +11,12 @@
*
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _XOPEN_SOURCE
-#define _DEFAULT_SOURCE
+
+#include "common.h"
#include <assert.h>
#include <errno.h>
+#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
@@ -25,7 +26,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include "common.h"
#include "tree_schema.h"
const char *const ly_stmt_list[] = {
@@ -145,6 +145,22 @@
[LYS_DEV_REPLACE] = "replace",
};
+#ifndef __USE_GNU
+char *
+get_current_dir_name(void)
+{
+ char tmp[PATH_MAX];
+ char *retval;
+
+ if (getcwd(tmp, sizeof(tmp))) {
+ retval = strdup(tmp);
+ LY_CHECK_ERR_RET(!retval, LOGMEM(NULL), NULL);
+ return retval;
+ }
+ return NULL;
+}
+#endif
+
void *
ly_realloc(void *ptr, size_t size)
{
@@ -304,3 +320,59 @@
}
return LY_SUCCESS;
}
+
+LY_ERR
+ly_parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret)
+{
+ char *strptr;
+
+ LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
+
+ /* convert to 64-bit integer, all the redundant characters are handled */
+ errno = 0;
+ strptr = NULL;
+
+ /* parse the value */
+ *ret = strtoll(val_str, &strptr, base);
+ if (errno) {
+ return LY_EVALID;
+ } else if ((*ret < min) || (*ret > max)) {
+ return LY_EDENIED;
+ } else if (strptr && *strptr) {
+ while (isspace(*strptr)) {
+ ++strptr;
+ }
+ if (*strptr) {
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+ly_parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret)
+{
+ char *strptr;
+ uint64_t u;
+
+ LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
+
+ errno = 0;
+ strptr = NULL;
+ u = strtoull(val_str, &strptr, base);
+ if (errno) {
+ return LY_EVALID;
+ } else if ((u > max) || (u && val_str[0] == '-')) {
+ return LY_EDENIED;
+ } else if (strptr && *strptr) {
+ while (isspace(*strptr)) {
+ ++strptr;
+ }
+ if (*strptr) {
+ return LY_EVALID;
+ }
+ }
+
+ *ret = u;
+ return LY_SUCCESS;
+}
diff --git a/src/common.h b/src/common.h
index c704ca8..3a550b8 100644
--- a/src/common.h
+++ b/src/common.h
@@ -15,13 +15,21 @@
#ifndef LY_COMMON_H_
#define LY_COMMON_H_
+#define _DEFAULT_SOURCE
+#define _GNU_SOURCE
+
#include <assert.h>
+#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
+#include <unistd.h>
#include "config.h"
#include "log.h"
+#include "context.h"
#include "tree_schema.h"
+#include "set.h"
+#include "hash_table.h"
#if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
# define THREAD_LOCAL _Thread_local
@@ -45,6 +53,13 @@
*/
#define API __attribute__((visibility("default")))
+#ifndef __USE_GNU
+/*
+ * If we don't have GNU extension, implement these function on your own
+ */
+char *get_current_dir_name(void);
+#endif
+
/******************************************************************************
* Logger
*****************************************************************************/
@@ -75,7 +90,7 @@
void ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...);
#define LOGERR(ctx, errno, str, args...) ly_log(ctx, LY_LLERR, errno, str, ##args)
-#define LOGWRN(ctx, str, args...) ly_log(ctx, LY_LLWRN, 0, str, ##args)
+#define LOGWRN(ctx, str, ...) ly_log(ctx, LY_LLWRN, 0, str, ##__VA_ARGS__)
#define LOGVRB(str, args...) ly_log(NULL, LY_LLVRB, 0, str, ##args)
#ifdef NDEBUG
@@ -99,17 +114,17 @@
*/
#define LY_CHECK_GOTO(COND, GOTO) if (COND) {goto GOTO;}
#define LY_CHECK_ERR_GOTO(COND, ERR, GOTO) if (COND) {ERR; goto GOTO;}
-#define LY_CHECK_RET1(RETVAL) if (RETVAL != LY_SUCCESS) {return RETVAL;}
+#define LY_CHECK_RET1(RETVAL) {LY_ERR ret__ = RETVAL;if (ret__ != LY_SUCCESS) {return ret__;}}
#define LY_CHECK_RET2(COND, RETVAL) if (COND) {return RETVAL;}
#define LY_CHECK_RET(...) GETMACRO2(__VA_ARGS__, LY_CHECK_RET2, LY_CHECK_RET1)(__VA_ARGS__)
#define LY_CHECK_ERR_RET(COND, ERR, RETVAL) if (COND) {ERR; return RETVAL;}
-#define LY_CHECK_ARG_GOTO1(CTX, ARG, GOTO) if (!ARG) {LOGARG(CTX, ARG);goto GOTO;}
+#define LY_CHECK_ARG_GOTO1(CTX, ARG, GOTO) if (!(ARG)) {LOGARG(CTX, ARG);goto GOTO;}
#define LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO) LY_CHECK_ARG_GOTO1(CTX, ARG1, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG2, GOTO)
#define LY_CHECK_ARG_GOTO3(CTX, ARG1, ARG2, ARG3, GOTO) LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG3, GOTO)
#define LY_CHECK_ARG_GOTO(CTX, ...) GETMACRO4(__VA_ARGS__, LY_CHECK_ARG_GOTO3, LY_CHECK_ARG_GOTO2, LY_CHECK_ARG_GOTO1)(CTX, __VA_ARGS__)
-#define LY_CHECK_ARG_RET1(CTX, ARG, RETVAL) if (!ARG) {LOGARG(CTX, ARG);return RETVAL;}
+#define LY_CHECK_ARG_RET1(CTX, ARG, RETVAL) if (!(ARG)) {LOGARG(CTX, ARG);return RETVAL;}
#define LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL) LY_CHECK_ARG_RET1(CTX, ARG1, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG2, RETVAL)
#define LY_CHECK_ARG_RET3(CTX, ARG1, ARG2, ARG3, RETVAL) LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG3, RETVAL)
#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO4(__VA_ARGS__, LY_CHECK_ARG_RET3, LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1)(CTX, __VA_ARGS__)
@@ -126,12 +141,50 @@
#define LY_VCODE_NSUPP LYVE_SYNTAX, "%s not supported."
#define LY_VCODE_INSTMT LYVE_SYNTAX_YANG, "Invalid keyword \"%s\"."
#define LY_VCODE_INCHILDSTMT LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s\"."
+#define LY_VCODE_INCHILDSTMT2 LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s\" - the statement is allowed only in YANG 1.1 modules."
+#define LY_VCODE_INCHILDSTMSCOMB LYVE_SYNTAX_YANG, "Invalid combination of keywords \"%s\" and \"%s\" as substatements of \"%s\"."
#define LY_VCODE_DUPSTMT LYVE_SYNTAX_YANG, "Duplicate keyword \"%s\"."
+#define LY_VCODE_DUPIDENT LYVE_SYNTAX_YANG, "Duplicate identifier \"%s\" of %s statement."
#define LY_VCODE_INVAL LYVE_SYNTAX_YANG, "Invalid value \"%.*s\" of \"%s\"."
#define LY_VCODE_MISSTMT LYVE_SYNTAX_YANG, "Missing mandatory keyword \"%s\" as a child of \"%s\"."
+#define LY_VCODE_MISSCHILDSTMT LYVE_SYNTAX_YANG, "Missing %s substatement for %s%s."
#define LY_VCODE_INORD LYVE_SYNTAX_YANG, "Invalid keyword \"%s\", it cannot appear after \"%s\"."
#define LY_VCODE_OOB LYVE_SYNTAX_YANG, "Value \"%.*s\" is out of \"%s\" bounds."
#define LY_VCODE_INDEV LYVE_SYNTAX_YANG, "Deviate \"%s\" does not support keyword \"%s\"."
+#define LY_VCODE_INREGEXP LYVE_SYNTAX_YANG, "Regular expression \"%s\" is not valid (\"%s\": %s)."
+#define LY_VCODE_XP_EOE LYVE_XPATH, "Unterminated string delimited with %c (%.15s)."
+#define LY_VCODE_XP_INEXPR LYVE_XPATH, "Invalid expression 0x%x."
+
+/******************************************************************************
+ * Context
+ *****************************************************************************/
+
+/**
+ * @brief Context of the YANG schemas
+ */
+struct ly_ctx {
+ struct dict_table dict; /**< dictionary to effectively store strings used in the context related structures */
+ struct ly_set search_paths; /**< set of directories where to search for schema's imports/includes */
+ struct ly_set list; /**< set of YANG schemas */
+ ly_module_imp_clb imp_clb; /**< Optional callback for retrieving missing included or imported models in a custom way. */
+ void *imp_clb_data; /**< Optional private data for imp_clb() */
+ uint16_t module_set_id; /**< ID of the current set of schemas */
+ uint16_t flags; /**< context settings, see @ref contextoptions. */
+ pthread_key_t errlist_key; /**< key for the thread-specific list of errors related to the context */
+};
+
+/**
+ * @brief Try to find submodule in the context. Submodules are present only in the parsed (lysp_) schema trees, if only
+ * the compiled versions of the schemas are present, the submodule cannot be returned even if it was used to compile
+ * some of the currently present schemas.
+ *
+ * @param[in] ctx Context where to search
+ * @param[in] module Name of the module where the submodule is supposed to belongs-to. If NULL, the module name is not checked.
+ * @param[in] submodule Name of the submodule to find.
+ * @param[in] revision Optional revision of the submodule to find. If not specified, the latest revision is returned.
+ * @return Pointer to the specified submodule if it is present in the context.
+ */
+struct lysp_module *ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *module, const char *submodule, const char *revision);
/******************************************************************************
* Parsers
@@ -230,6 +283,8 @@
* Generic useful functions.
*****************************************************************************/
+#define FREE_STRING(CTX, STRING) if (STRING) {lydict_remove(CTX, STRING);}
+
/**
* @brief Wrapper for realloc() call. The only difference is that if it fails to
* allocate the requested memory, the original memory is freed as well.
@@ -252,6 +307,39 @@
LY_ERR ly_getutf8(const char **input, unsigned int *utf8_char, size_t *bytes_read);
/**
+ * @brief Parse signed integer with possible limitation.
+ * @param[in] val_str String value containing signed integer, note that
+ * nothing else than whitespaces are expected after the value itself.
+ * @param[in] min Limitation for the value which must not be lower than min.
+ * @param[in] max Limitation for the value which must not be higher than max.
+ * @param[in] base Numeric base for parsing:
+ * 0 - to accept decimal, octal, hexadecimal (e.g. in default value)
+ * 10 - to accept only decimal (e.g. data instance value)
+ * @param[out] ret Resulting value.
+ * @return LY_ERR value:
+ * LY_EDENIED - the value breaks the limits,
+ * LY_EVALID - string contains invalid value,
+ * LY_SUCCESS - successful parsing.
+ */
+LY_ERR ly_parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret);
+
+/**
+ * @brief Parse unsigned integer with possible limitation.
+ * @param[in] val_str String value containing unsigned integer, note that
+ * nothing else than whitespaces are expected after the value itself.
+ * @param[in] max Limitation for the value which must not be higher than max.
+ * @param[in] base Numeric base for parsing:
+ * 0 - to accept decimal, octal, hexadecimal (e.g. in default value)
+ * 10 - to accept only decimal (e.g. data instance value)
+ * @param[out] ret Resulting value.
+ * @return LY_ERR value:
+ * LY_EDENIED - the value breaks the limits,
+ * LY_EVALID - string contains invalid value,
+ * LY_SUCCESS - successful parsing.
+ */
+LY_ERR ly_parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret);
+
+/**
* @brief mmap(2) wrapper to map input files into memory to unify parsing.
*
* The address space is allocate only for reading.
@@ -298,7 +386,33 @@
memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
/**
+ * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
+ *
+ * Increases the size information.
+ *
+ * @param[in] CTX libyang context for logging.
+ * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
+ * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
+ * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
+ * @param[out] RET Variable to store error code.
+ * @param[in] GOTO Label to go in case of error (memory allocation failure).
+ */
+#define LY_ARRAY_NEW_GOTO(CTX, ARRAY, NEW_ITEM, RET, GOTO) \
+ if (!(ARRAY)) { \
+ ARRAY = malloc(sizeof(uint32_t) + sizeof *(ARRAY)); \
+ *((uint32_t*)(ARRAY)) = 1; \
+ } else { \
+ ++(*((uint32_t*)(ARRAY) - 1)); \
+ ARRAY = ly_realloc(((uint32_t*)(ARRAY) - 1), sizeof(uint32_t) + (*((uint32_t*)(ARRAY) - 1) * sizeof *(ARRAY))); \
+ LY_CHECK_ERR_GOTO(!(ARRAY), LOGMEM(CTX); RET = LY_EMEM, GOTO); \
+ } \
+ ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
+ (NEW_ITEM) = &(ARRAY)[*((uint32_t*)(ARRAY) - 1) - 1]; \
+ memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
+
+/**
* @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
+ * If the ARRAY already exists, it is resized (space for SIZE items is added).
*
* Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
* when the items are filled.
@@ -310,9 +424,42 @@
* @param[in] RETVAL Return value for the case of error (memory allocation failure).
*/
#define LY_ARRAY_CREATE_RET(CTX, ARRAY, SIZE, RETVAL) \
- ARRAY = calloc(1, sizeof(uint32_t) + SIZE * sizeof *(ARRAY)); \
- LY_CHECK_ERR_RET(!(ARRAY), LOGMEM(CTX), RETVAL); \
- ARRAY = (void*)((uint32_t*)(ARRAY) + 1)
+ if (ARRAY) { \
+ ARRAY = ly_realloc(((uint32_t*)(ARRAY) - 1), sizeof(uint32_t) + ((*((uint32_t*)(ARRAY) - 1) + SIZE) * sizeof *(ARRAY))); \
+ LY_CHECK_ERR_RET(!(ARRAY), LOGMEM(CTX), RETVAL); \
+ ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
+ memset(&(ARRAY)[*((uint32_t*)(ARRAY) - 1)], 0, SIZE * sizeof *(ARRAY)); \
+ } else { \
+ ARRAY = calloc(1, sizeof(uint32_t) + SIZE * sizeof *(ARRAY)); \
+ LY_CHECK_ERR_RET(!(ARRAY), LOGMEM(CTX), RETVAL); \
+ ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
+ }
+
+/**
+ * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
+ * If the ARRAY already exists, it is resized (space for SIZE items is added).
+ *
+ * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
+ * when the items are filled.
+ *
+ * @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
+ * 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).
+ */
+#define LY_ARRAY_CREATE_GOTO(CTX, ARRAY, SIZE, RET, GOTO) \
+ if (ARRAY) { \
+ ARRAY = ly_realloc(((uint32_t*)(ARRAY) - 1), sizeof(uint32_t) + ((*((uint32_t*)(ARRAY) - 1) + SIZE) * sizeof *(ARRAY))); \
+ LY_CHECK_ERR_GOTO(!(ARRAY), LOGMEM(CTX); RET = LY_EMEM, GOTO); \
+ ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
+ memset(&(ARRAY)[*((uint32_t*)(ARRAY) - 1)], 0, SIZE * sizeof *(ARRAY)); \
+ } else { \
+ ARRAY = calloc(1, sizeof(uint32_t) + SIZE * sizeof *(ARRAY)); \
+ LY_CHECK_ERR_GOTO(!(ARRAY), LOGMEM(CTX); RET = LY_EMEM, GOTO); \
+ ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
+ }
#define LY_ARRAY_INCREMENT(ARRAY) \
++(*((uint32_t*)(ARRAY) - 1))
diff --git a/src/config.h.in b/src/config.h.in
index 2a2af23..0d9284b 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -15,6 +15,8 @@
#ifndef LY_CONFIG_H_
#define LY_CONFIG_H_
+#cmakedefine APPLE
+
/**
* @brief Compiler flag for unused function attributes
*/
diff --git a/src/context.c b/src/context.c
index 1216704..66b62e5 100644
--- a/src/context.c
+++ b/src/context.c
@@ -12,8 +12,8 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _DEFAULT_SOURCE
-#define _BSD_SOURCE
+#include "common.h"
+
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
@@ -23,27 +23,19 @@
#include <unistd.h>
#include "context.h"
-#include "common.h"
#include "tree_schema_internal.h"
#include "libyang.h"
#define LY_INTERNAL_MODS_COUNT 6
-#define IETF_YANG_METADATA_PATH "../models/ietf-yang-metadata@2016-08-05.h"
-#define YANG_PATH "../models/yang@2017-02-20.h"
-#define IETF_INET_TYPES_PATH "../models/ietf-inet-types@2013-07-15.h"
-#define IETF_YANG_TYPES_PATH "../models/ietf-yang-types@2013-07-15.h"
-#define IETF_DATASTORES "../models/ietf-datastores@2017-08-17.h"
-#define IETF_YANG_LIB_PATH "../models/ietf-yang-library@2018-01-17.h"
+#include "../models/ietf-yang-metadata@2016-08-05.h"
+#include "../models/yang@2017-02-20.h"
+#include "../models/ietf-inet-types@2013-07-15.h"
+#include "../models/ietf-yang-types@2013-07-15.h"
+#include "../models/ietf-datastores@2017-08-17.h"
+#include "../models/ietf-yang-library@2018-01-17.h"
#define IETF_YANG_LIB_REV "2018-01-17"
-#include IETF_YANG_METADATA_PATH
-#include YANG_PATH
-#include IETF_INET_TYPES_PATH
-#include IETF_YANG_TYPES_PATH
-#include IETF_DATASTORES
-#include IETF_YANG_LIB_PATH
-
static struct internal_modules_s {
const char *name;
const char *revision;
@@ -95,6 +87,9 @@
return LY_EMEM;
}
+ /* new searchdir - possibly more latest revision available */
+ ly_ctx_reset_latests(ctx);
+
return LY_SUCCESS;
} else {
/* consider that no change is not actually an error */
@@ -212,7 +207,7 @@
/* load internal modules */
for (i = 0; i < ((options & LY_CTX_NOYANGLIBRARY) ? (LY_INTERNAL_MODS_COUNT - 2) : LY_INTERNAL_MODS_COUNT); i++) {
module = (struct lys_module *)lys_parse_mem(ctx, internal_modules[i].data, internal_modules[i].format);
- LY_CHECK_GOTO(!module, error);
+ LY_CHECK_ERR_GOTO(!module, rc = ly_errcode(ctx), error);
module->parsed->implemented = internal_modules[i].implemented;
}
@@ -262,6 +257,242 @@
}
API void
+ly_ctx_set_module_imp_clb(struct ly_ctx *ctx, ly_module_imp_clb clb, void *user_data)
+{
+ LY_CHECK_ARG_RET(ctx, ctx,);
+
+ ctx->imp_clb = clb;
+ ctx->imp_clb_data = user_data;
+}
+
+API ly_module_imp_clb
+ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, NULL);
+
+ if (user_data) {
+ *user_data = ctx->imp_clb_data;
+ }
+ return ctx->imp_clb;
+}
+
+/**
+ * @brief Iterate over the modules in the given context. Returned modules must match the given key at the offset of
+ * lysp_module and lysc_module structures (they are supposed to be placed at the same offset in both structures).
+ *
+ * @param[in] ctx Context where to iterate.
+ * @param[in] key Key value to search for.
+ * @param[in] key_offset Key's offset in struct lysp_module and struct lysc_module to get value from the context's
+ * modules to match with the key.
+ * @param[in,out] Iterator to pass between the function calls. On the first call, the variable is supposed to be
+ * initiated to 0. After each call returning a module, the value is greater by 1 than the index of the returned
+ * module in the context.
+ * @return Module matching the given key, NULL if no such module found.
+ */
+static const struct lys_module *
+ly_ctx_get_module_by_iter(const struct ly_ctx *ctx, const char *key, size_t key_offset, unsigned int *index)
+{
+ const struct lys_module *mod;
+ const char *value;
+
+ for (; *index < ctx->list.count; ++(*index)) {
+ mod = ctx->list.objs[*index];
+ if (mod->compiled) {
+ value = *(const char**)(((int8_t*)(mod->compiled)) + key_offset);
+ } else {
+ value = *(const char**)(((int8_t*)(mod->parsed)) + key_offset);
+ }
+ if (!strcmp(key, value)) {
+ /* increment index for the next run */
+ ++(*index);
+ return mod;
+ }
+ }
+ /* done */
+ return NULL;
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module() and ly_ctx_get_module_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lysp_module to get value from the context's modules to match with the key.
+ * @param[in] revision Revision date to match. If NULL, the matching module must have no revision. To search for the latest
+ * revision module, use ly_ctx_get_module_latest_by().
+ * @return Matching module if any.
+ */
+static const struct lys_module *
+ly_ctx_get_module_by(const struct ly_ctx *ctx, const char *key, size_t key_offset, const char *revision)
+{
+ const struct lys_module *mod;
+ unsigned int index = 0;
+
+ while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
+ if (!revision) {
+ if ((mod->compiled && !mod->compiled->revision) || (!mod->compiled && !mod->parsed->revs)) {
+ /* found requested module without revision */
+ return mod;
+ }
+ } else {
+ if ((mod->compiled && mod->compiled->revision && !strcmp(mod->compiled->revision, revision)) ||
+ (!mod->compiled && mod->parsed->revs && !strcmp(mod->parsed->revs[0].date, revision))) {
+ /* found requested module of the specific revision */
+ return mod;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+API const struct lys_module *
+ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+ return ly_ctx_get_module_by(ctx, ns, offsetof(struct lysp_module, ns), revision);
+}
+
+API const struct lys_module *
+ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+ return ly_ctx_get_module_by(ctx, name, offsetof(struct lysp_module, name), revision);
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module_latest() and ly_ctx_get_module_latest_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lysp_module to get value from the context's modules to match with the key.
+ * @return Matching module if any.
+ */
+static const struct lys_module *
+ly_ctx_get_module_latest_by(const struct ly_ctx *ctx, const char *key, size_t key_offset)
+{
+ const struct lys_module *mod;
+ unsigned int index = 0;
+
+ while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
+ if ((mod->compiled && mod->compiled->latest_revision) || (!mod->compiled && mod->parsed->latest_revision)) {
+ return mod;
+ }
+ }
+
+ return NULL;
+}
+
+API const struct lys_module *
+ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+ return ly_ctx_get_module_latest_by(ctx, name, offsetof(struct lysp_module, name));
+}
+
+const struct lys_module *
+ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+ return ly_ctx_get_module_latest_by(ctx, ns, offsetof(struct lysp_module, ns));
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module_implemented() and ly_ctx_get_module_implemented_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lysp_module to get value from the context's modules to match with the key.
+ * @return Matching module if any.
+ */
+static const struct lys_module *
+ly_ctx_get_module_implemented_by(const struct ly_ctx *ctx, const char *key, size_t key_offset)
+{
+ const struct lys_module *mod;
+ unsigned int index = 0;
+
+ while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
+ if ((mod->compiled && mod->compiled->implemented) || (!mod->compiled && mod->parsed->implemented)) {
+ return mod;
+ }
+ }
+
+ return NULL;
+}
+
+API const struct lys_module *
+ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+ return ly_ctx_get_module_implemented_by(ctx, name, offsetof(struct lysp_module, name));
+}
+
+API const struct lys_module *
+ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+ return ly_ctx_get_module_implemented_by(ctx, ns, offsetof(struct lysp_module, ns));
+}
+
+struct lysp_module *
+ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *module, const char *submodule, const char *revision)
+{
+ const struct lys_module *mod;
+ struct lysp_include *inc;
+ unsigned int v, u;
+
+ assert(submodule);
+
+ for (v = 0; v < ctx->list.count; ++v) {
+ mod = ctx->list.objs[v];
+ if (!mod->parsed) {
+ continue;
+ }
+ if (module && strcmp(module, mod->parsed->name)) {
+ continue;
+ }
+
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ if (mod->parsed->includes[u].submodule && !strcmp(submodule, mod->parsed->includes[u].submodule->name)) {
+ inc = &mod->parsed->includes[u];
+ if (!revision) {
+ if (inc->submodule->latest_revision) {
+ return inc->submodule;
+ }
+ } else if (inc->submodule->revs && !strcmp(revision, inc->submodule->revs[0].date)) {
+ return inc->submodule;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+API void
+ly_ctx_reset_latests(struct ly_ctx *ctx)
+{
+ unsigned int u,v ;
+ struct lys_module *mod;
+
+ for (u = 0; u < ctx->list.count; ++u) {
+ mod = ctx->list.objs[u];
+ if (mod->compiled && mod->compiled->latest_revision == 2) {
+ mod->compiled->latest_revision = 1;
+ }
+ if (mod->parsed) {
+ if (mod->parsed->latest_revision == 2) {
+ mod->parsed->latest_revision = 1;
+ }
+ if (mod->parsed->includes) {
+ for (v = 0; v < LY_ARRAY_SIZE(mod->parsed->includes); ++v) {
+ if (mod->parsed->includes[v].submodule->latest_revision == 2) {
+ mod->parsed->includes[v].submodule->latest_revision = 1;
+ }
+ }
+ }
+ }
+ }
+}
+
+API void
ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lysc_node *node, void *priv))
{
if (!ctx) {
diff --git a/src/context.h b/src/context.h
index e907042..8bed61b 100644
--- a/src/context.h
+++ b/src/context.h
@@ -15,23 +15,293 @@
#ifndef LY_CONTEXT_H_
#define LY_CONTEXT_H_
-#include <pthread.h>
-
-#include "common.h"
-#include "set.h"
-#include "hash_table.h"
+#include "log.h"
#include "tree_schema.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
- * @brief Context of the YANG schemas
+ * @defgroup context Context
+ * @{
+ *
+ * Structures and functions to manipulate with the libyang "containers". The \em context concept allows callers
+ * to work in environments with different sets of YANG schemas. More detailed information can be found at
+ * @ref howtocontext page.
*/
-struct ly_ctx {
- struct dict_table dict; /**< dictionary to effectively store strings used in the context related structures */
- struct ly_set search_paths; /**< set of directories where to search for schema's imports/includes */
- struct ly_set list; /**< set of YANG schemas */
- uint16_t module_set_id; /**< ID of the current set of schemas */
- uint16_t flags; /**< context settings, see @ref contextoptions. */
- pthread_key_t errlist_key; /**< key for the thread-specific list of errors related to the context */
-};
+
+/**
+ * @struct ly_ctx
+ * @brief libyang context handler.
+ */
+struct ly_ctx;
+
+/**
+ * @defgroup contextoptions Context options
+ * @ingroup context
+ *
+ * Options to change context behavior.
+ * @{
+ */
+
+#define LY_CTX_ALLIMPLEMENTED 0x01 /**< All the imports of the schema being parsed are treated implemented. */
+#define LY_CTX_TRUSTED 0x02 /**< Handle the schema being parsed as trusted and skip its validation
+ tests. Note that while this option improves performance, it can
+ lead to an undefined behavior if the schema is not correct. */
+#define LY_CTX_NOYANGLIBRARY 0x04 /**< Do not internally implement ietf-yang-library module. The option
+ causes that function ly_ctx_info() does not work (returns NULL) until
+ the ietf-yang-library module is loaded manually. While any revision
+ of this schema can be loaded with this option, note that the only
+ revisions implemented by ly_ctx_info() are 2016-04-09 and 2018-01-17.
+ This option cannot be changed on existing context. */
+#define LY_CTX_DISABLE_SEARCHDIRS 0x08 /**< Do not search for schemas in context's searchdirs neither in current
+ working directory. It is entirely skipped and the only way to get
+ schema data for imports or for ly_ctx_load_module() is to use the
+ callbacks provided by caller via ly_ctx_set_module_imp_clb() */
+#define LY_CTX_DISABLE_SEARCHDIR_CWD 0x10 /**< Do not automatically search for schemas in current working
+ directory, which is by default searched automatically (despite not
+ recursively). */
+#define LY_CTX_PREFER_SEARCHDIRS 0x20 /**< When searching for schema, prefer searchdirs instead of user callback. */
+/**@} contextoptions */
+
+/**
+ * @brief Create libyang context.
+ *
+ * Context is used to hold all information about schemas. Usually, the application is supposed
+ * to work with a single context in which libyang is holding all schemas (and other internal
+ * information) according to which the data trees will be processed and validated. So, the schema
+ * trees are tightly connected with the specific context and they are held by the context internally
+ * - caller does not need to keep pointers to the schemas returned by lys_parse(), context knows
+ * about them. The data trees created with lyd_parse() are still connected with the specific context,
+ * but they are not internally held by the context. The data tree just points and lean on some data
+ * held by the context (schema tree, string dictionary, etc.). Therefore, in case of data trees, caller
+ * is supposed to keep pointers returned by the lyd_parse() and manage the data tree on its own. This
+ * also affects the number of instances of both tree types. While you can have only one instance of
+ * specific schema connected with a single context, number of data tree instances is not connected.
+ *
+ * @param[in] search_dir Directory where libyang will search for the imported or included modules
+ * and submodules. If no such directory is available, NULL is accepted.
+ * @param[in] options Context options, see @ref contextoptions.
+ * @param[out] new_ctx Pointer to the created libyang context if LY_SUCCESS returned.
+ * @return LY_ERR return value.
+ */
+LY_ERR ly_ctx_new(const char *search_dir, int options, struct ly_ctx **new_ctx);
+
+/**
+ * @brief Add the search path into libyang context
+ *
+ * To reset search paths set in the context, use ly_ctx_unset_searchdirs() and then
+ * set search paths again.
+ *
+ * @param[in] ctx Context to be modified.
+ * @param[in] search_dir New search path to add to the current paths previously set in ctx.
+ * @return LY_ERR return value.
+ */
+LY_ERR ly_ctx_set_searchdir(struct ly_ctx *ctx, const char *search_dir);
+
+/**
+ * @brief Clean the search path(s) from the libyang context
+ *
+ * @param[in] ctx Context to be modified.
+ * @param[in] value Searchdir to be removed, use NULL to remove them all.
+ * @return LY_ERR return value
+ */
+LY_ERR ly_ctx_unset_searchdirs(struct ly_ctx *ctx, const char *value);
+
+/**
+ * @brief Get the NULL-terminated list of the search paths in libyang context. Do not modify the result!
+ *
+ * @param[in] ctx Context to query.
+ * @return NULL-terminated list (array) of the search paths, NULL if no searchpath was set.
+ * Do not modify the provided data in any way!
+ */
+const char * const *ly_ctx_get_searchdirs(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get the currently set context's options.
+ *
+ * @param[in] ctx Context to query.
+ * @return Combination of all the currently set context's options, see @ref contextoptions.
+ */
+int ly_ctx_get_options(const struct ly_ctx *ctx);
+
+/**
+ * @brief Set some of the context's options, see @ref contextoptions.
+ * @param[in] ctx Context to be modified.
+ * @param[in] option Combination of the context's options to be set, see @ref contextoptions.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_ctx_set_option(struct ly_ctx *ctx, int option);
+
+/**
+ * @brief Unset some of the context's options, see @ref contextoptions.
+ * @param[in] ctx Context to be modified.
+ * @param[in] option Combination of the context's options to be unset, see @ref contextoptions.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_ctx_unset_option(struct ly_ctx *ctx, int option);
+
+/**
+ * @brief Get current ID of the modules set. The value is available also
+ * as module-set-id in ly_ctx_info() result.
+ *
+ * @param[in] ctx Context to be examined.
+ * @return Numeric identifier of the current context's modules set.
+ */
+uint16_t ly_ctx_get_module_set_id(const struct ly_ctx *ctx);
+
+/**
+ * @brief Callback for retrieving missing included or imported models in a custom way.
+ *
+ * When submod_name is provided, the submodule is requested instead of the module (in this case only
+ * the module name without its revision is provided).
+ *
+ * If an @arg free_module_data callback is provided, it will be used later to free the allegedly const data
+ * which were returned by this callback.
+ *
+ * @param[in] mod_name Missing module name.
+ * @param[in] mod_rev Optional missing module revision. If NULL and submod_name is not provided, the latest revision is
+ * requested, the parsed module is then marked by the latest_revision flag.
+ * @param[in] submod_name Optional missing submodule name.
+ * @param[in] submod_rev Optional missing submodule revision. If NULL and submod_name is provided, the latest revision is
+ * requested, the parsed submodule is then marked by the latest_revision flag.
+ * @param[in] user_data User-supplied callback data.
+ * @param[out] format Format of the returned module data.
+ * @param[out] module_data Requested module data.
+ * @param[out] free_module_data Callback for freeing the returned module data. If not set, the data will be left untouched.
+ * @return LY_ERR value. If the returned value differs from LY_SUCCESS, libyang continue in trying to get the module data
+ * according to the settings of its mechanism to search for the imported/included schemas.
+ */
+typedef LY_ERR (*ly_module_imp_clb)(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
+ void *user_data, LYS_INFORMAT *format, const char **module_data,
+ void (**free_module_data)(void *model_data, void *user_data));
+
+/**
+ * @brief Get the custom callback for missing import/include module retrieval.
+ *
+ * @param[in] ctx Context to read from.
+ * @param[in] user_data Optional pointer for getting the user-supplied callback data.
+ * @return Callback or NULL if not set.
+ */
+ly_module_imp_clb ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data);
+
+/**
+ * @brief Set missing include or import module callback. It is meant to be used when the models
+ * are not locally available (such as when downloading modules from a NETCONF server), it should
+ * not be required in other cases.
+ *
+ * @param[in] ctx Context that will use this callback.
+ * @param[in] clb Callback responsible for returning the missing model.
+ * @param[in] user_data Arbitrary data that will always be passed to the callback \p clb.
+ */
+void ly_ctx_set_module_imp_clb(struct ly_ctx *ctx, ly_module_imp_clb clb, void *user_data);
+
+/**
+ * @brief Get YANG module of the given name and revision.
+ *
+ * @param[in] ctx Context to work in.
+ * @param[in] name Name of the YANG module to get.
+ * @param[in] revision Requested revision date of the YANG module to get. If not specified,
+ * the schema with no revision is returned, if it is present in the context.
+ * @return Pointer to the YANG module, NULL if no schema in the context follows the name and revision requirements.
+ */
+const struct lys_module *ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision);
+
+/**
+ * @brief Get the latest revision of the YANG module specified by its name.
+ *
+ * YANG modules with no revision are supposed to be the oldest one.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] name Name of the YANG module to get.
+ * @return The latest revision of the specified YANG module in the given context, NULL if no YANG module of the
+ * given name is present in the context.
+ */
+const struct lys_module *ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name);
+
+/**
+ * @brief Get the (only) implemented YANG module specified by its name.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] name Name of the YANG module to get.
+ * @return The only implemented YANG module revision of the given name in the given context. NULL if there is no
+ * implemented module of the given name.
+ */
+const struct lys_module *ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name);
+
+/**
+ * @brief Get YANG module of the given namespace and revision.
+ *
+ * @param[in] ctx Context to work in.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @param[in] revision Requested revision date of the YANG module to get. If not specified,
+ * the schema with no revision is returned, if it is present in the context.
+ * @return Pointer to the YANG module, NULL if no schema in the context follows the namespace and revision requirements.
+ */
+const struct lys_module *ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision);
+
+/**
+ * @brief Get the latest revision of the YANG module specified by its namespace.
+ *
+ * YANG modules with no revision are supposed to be the oldest one.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @return The latest revision of the specified YANG module in the given context, NULL if no YANG module of the
+ * given namespace is present in the context.
+ */
+const struct lys_module *ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns);
+
+/**
+ * @brief Get the (only) implemented YANG module specified by its namespace.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @return The only implemented YANG module revision of the given namespace in the given context. NULL if there is no
+ * implemented module of the given namespace.
+ */
+const struct lys_module *ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns);
+
+/**
+ * @brief Reset cached latest revision information of the schemas in the context.
+ *
+ * When a (sub)module is imported/included without revision, the latest revision is
+ * searched. libyang searches for the latest revision in searchdirs and/or via provided
+ * import callback ly_module_imp_clb() just once. Then it is expected that the content
+ * of searchdirs or data returned by the callback does not change. So when it changes,
+ * it is necessary to force searching for the latest revision in case of loading another
+ * module, which what this function does.
+ *
+ * The latest revision information is also reset when the searchdirs set changes via
+ * ly_ctx_set_searchdir().
+ *
+ * @param[in] ctx libyang context where the latest revision information is going to be reset.
+ */
+void ly_ctx_reset_latests(struct ly_ctx *ctx);
+
+/**
+ * @brief Free all internal structures of the specified context.
+ *
+ * The function should be used before terminating the application to destroy
+ * and free all structures internally used by libyang. If the caller uses
+ * multiple contexts, the function should be called for each used context.
+ *
+ * All instance data are supposed to be freed before destroying the context.
+ * Data models are destroyed automatically as part of ly_ctx_destroy() call.
+ *
+ * @param[in] ctx libyang context to destroy
+ * @param[in] private_destructor Optional destructor function for private objects assigned
+ * to the nodes via lys_set_private(). If NULL, the private objects are not freed by libyang.
+ * Remember the differences between the structures derived from ::lysc_node and always check
+ * ::lysc_node#nodetype.
+ */
+void ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lysc_node *node, void *priv));
+
+/** @} context */
+
+#ifdef __cplusplus
+}
+#endif
#endif /* LY_CONTEXT_H_ */
diff --git a/src/extensions.h b/src/extensions.h
new file mode 100644
index 0000000..588f63a
--- /dev/null
+++ b/src/extensions.h
@@ -0,0 +1,108 @@
+/**
+ * @file extesnions.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang support for YANG extensions implementation.
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_EXTENSIONS_H_
+#define LY_EXTENSIONS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup extensions YANG Extensions
+ *
+ * @{
+ */
+
+/**
+ * @brief Extension instance structure parent enumeration
+ */
+typedef enum {
+ LYEXT_PAR_MODULE, /**< ::lys_module or ::lys_submodule */
+ LYEXT_PAR_NODE, /**< ::lys_node (and the derived structures) */
+ LYEXT_PAR_TPDF, /**< ::lys_tpdf */
+ LYEXT_PAR_TYPE, /**< ::lys_type */
+ LYEXT_PAR_TYPE_BIT, /**< ::lys_type_bit */
+ LYEXT_PAR_TYPE_ENUM, /**< ::lys_type_enum */
+ LYEXT_PAR_FEATURE, /**< ::lys_feature */
+ LYEXT_PAR_RESTR, /**< ::lys_restr - YANG's must, range, length and pattern statements */
+ LYEXT_PAR_WHEN, /**< ::lys_when */
+ LYEXT_PAR_IDENT, /**< ::lys_ident */
+ LYEXT_PAR_EXT, /**< ::lys_ext */
+ LYEXT_PAR_EXTINST, /**< ::lys_ext_instance */
+ LYEXT_PAR_REFINE, /**< ::lys_refine */
+ LYEXT_PAR_DEVIATION, /**< ::lys_deviation */
+ LYEXT_PAR_DEVIATE, /**< ::lys_deviate */
+ LYEXT_PAR_IMPORT, /**< ::lys_import */
+ LYEXT_PAR_INCLUDE, /**< ::lysp_include */
+ LYEXT_PAR_REVISION, /**< ::lysc_revision */
+} LYEXT_PARENT;
+
+/**
+ * @brief Enum of substatements in which extension instances can appear.
+ */
+typedef enum {
+ LYEXT_SUBSTMT_SELF = 0, /**< extension of the structure itself, not substatement's */
+ LYEXT_SUBSTMT_ARGUMENT, /**< extension of the argument statement, can appear in lys_ext */
+ LYEXT_SUBSTMT_BASE, /**< extension of the base statement, can appear (repeatedly) in lys_type and lys_ident */
+ LYEXT_SUBSTMT_BELONGSTO, /**< extension of the belongs-to statement, can appear in lys_submodule */
+ LYEXT_SUBSTMT_CONTACT, /**< extension of the contact statement, can appear in lys_module */
+ LYEXT_SUBSTMT_DEFAULT, /**< extension of the default statement, can appear in lys_node_leaf, lys_node_leaflist,
+ lys_node_choice and lys_deviate */
+ LYEXT_SUBSTMT_DESCRIPTION, /**< extension of the description statement, can appear in lys_module, lys_submodule,
+ lys_node, lys_import, lys_include, lys_ext, lys_feature, lys_tpdf, lys_restr,
+ lys_ident, lys_deviation, lys_type_enum, lys_type_bit, lys_when and lys_revision */
+ LYEXT_SUBSTMT_ERRTAG, /**< extension of the error-app-tag statement, can appear in lys_restr */
+ LYEXT_SUBSTMT_ERRMSG, /**< extension of the error-message statement, can appear in lys_restr */
+ LYEXT_SUBSTMT_KEY, /**< extension of the key statement, can appear in lys_node_list */
+ LYEXT_SUBSTMT_NAMESPACE, /**< extension of the namespace statement, can appear in lys_module */
+ LYEXT_SUBSTMT_ORGANIZATION, /**< extension of the organization statement, can appear in lys_module and lys_submodule */
+ LYEXT_SUBSTMT_PATH, /**< extension of the path statement, can appear in lys_type */
+ LYEXT_SUBSTMT_PREFIX, /**< extension of the prefix statement, can appear in lys_module, lys_submodule (for
+ belongs-to's prefix) and lys_import */
+ LYEXT_SUBSTMT_PRESENCE, /**< extension of the presence statement, can appear in lys_node_container */
+ LYEXT_SUBSTMT_REFERENCE, /**< extension of the reference statement, can appear in lys_module, lys_submodule,
+ lys_node, lys_import, lys_include, lys_revision, lys_tpdf, lys_restr, lys_ident,
+ lys_ext, lys_feature, lys_deviation, lys_type_enum, lys_type_bit and lys_when */
+ LYEXT_SUBSTMT_REVISIONDATE, /**< extension of the revision-date statement, can appear in lys_import and lys_include */
+ LYEXT_SUBSTMT_UNITS, /**< extension of the units statement, can appear in lys_tpdf, lys_node_leaf,
+ lys_node_leaflist and lys_deviate */
+ LYEXT_SUBSTMT_VALUE, /**< extension of the value statement, can appear in lys_type_enum */
+ LYEXT_SUBSTMT_VERSION, /**< extension of the yang-version statement, can appear in lys_module and lys_submodule */
+ LYEXT_SUBSTMT_MODIFIER, /**< extension of the modifier statement, can appear in lys_restr */
+ LYEXT_SUBSTMT_REQINSTANCE, /**< extension of the require-instance statement, can appear in lys_type */
+ LYEXT_SUBSTMT_YINELEM, /**< extension of the yin-element statement, can appear in lys_ext */
+ LYEXT_SUBSTMT_CONFIG, /**< extension of the config statement, can appear in lys_node and lys_deviate */
+ LYEXT_SUBSTMT_MANDATORY, /**< extension of the mandatory statement, can appear in lys_node_leaf, lys_node_choice,
+ lys_node_anydata and lys_deviate */
+ LYEXT_SUBSTMT_ORDEREDBY, /**< extension of the ordered-by statement, can appear in lys_node_list and lys_node_leaflist */
+ LYEXT_SUBSTMT_STATUS, /**< extension of the status statement, can appear in lys_tpdf, lys_node, lys_ident,
+ lys_ext, lys_feature, lys_type_enum and lys_type_bit */
+ LYEXT_SUBSTMT_FRACDIGITS, /**< extension of the fraction-digits statement, can appear in lys_type */
+ LYEXT_SUBSTMT_MAX, /**< extension of the max-elements statement, can appear in lys_node_list,
+ lys_node_leaflist and lys_deviate */
+ LYEXT_SUBSTMT_MIN, /**< extension of the min-elements statement, can appear in lys_node_list,
+ lys_node_leaflist and lys_deviate */
+ LYEXT_SUBSTMT_POSITION, /**< extension of the position statement, can appear in lys_type_bit */
+ LYEXT_SUBSTMT_UNIQUE, /**< extension of the unique statement, can appear in lys_node_list and lys_deviate */
+ LYEXT_SUBSTMT_IFFEATURE, /**< extension of the if-feature statement */
+} LYEXT_SUBSTMT;
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_TREE_SCHEMA_H_ */
diff --git a/src/hash_table.c b/src/hash_table.c
index a60c3da..11ca8f3 100644
--- a/src/hash_table.c
+++ b/src/hash_table.c
@@ -83,28 +83,6 @@
}
/*
- * Bob Jenkin's one-at-a-time hash
- * http://www.burtleburtle.net/bob/hash/doobs.html
- *
- * Spooky hash is faster, but it works only for little endian architectures.
- */
-static uint32_t
-dict_hash(const char *key, size_t len)
-{
- uint32_t hash, i;
-
- for (hash = i = 0; i < len; ++i) {
- hash += key[i];
- hash += (hash << 10);
- hash ^= (hash >> 6);
- }
- hash += (hash << 3);
- hash ^= (hash >> 11);
- hash += (hash << 15);
- return hash;
-}
-
-/*
* Usage:
* - init hash to 0
* - repeatedly call dict_hash_multi(), provide hash from the last call
@@ -130,6 +108,21 @@
return hash;
}
+/*
+ * Bob Jenkin's one-at-a-time hash
+ * http://www.burtleburtle.net/bob/hash/doobs.html
+ *
+ * Spooky hash is faster, but it works only for little endian architectures.
+ */
+uint32_t
+dict_hash(const char *key, size_t len)
+{
+ uint32_t hash;
+
+ hash = dict_hash_multi(0, key, len);
+ return dict_hash_multi(hash, NULL, len);
+}
+
API void
lydict_remove(struct ly_ctx *ctx, const char *value)
{
@@ -139,8 +132,6 @@
struct dict_rec rec, *match = NULL;
char *val_p;
- LY_CHECK_ARG_RET(ctx, ctx, value,);
-
if (!value) {
return;
}
diff --git a/src/hash_table.h b/src/hash_table.h
index 55a0158..70ec695 100644
--- a/src/hash_table.h
+++ b/src/hash_table.h
@@ -32,6 +32,11 @@
*/
uint32_t dict_hash_multi(uint32_t hash, const char *key_part, size_t len);
+/*
+ * @brief Compute hash from a string.
+ */
+uint32_t dict_hash(const char *key, size_t len);
+
/**
* @brief Callback for checking hash table values equivalence.
*
diff --git a/src/libyang.h b/src/libyang.h
index 060d49a..3c001fc 100644
--- a/src/libyang.h
+++ b/src/libyang.h
@@ -21,6 +21,12 @@
extern "C" {
#endif
+#include "log.h"
+#include "set.h"
+#include "dict.h"
+#include "context.h"
+#include "tree_schema.h"
+
/**
* @mainpage About
*
@@ -97,167 +103,6 @@
* The lists are structures connected via a `next` pointer. Iterating over the siblings can be simply done by ::LY_LIST_FOR macro.
*/
-/**
- * @defgroup context Context
- * @{
- *
- * Structures and functions to manipulate with the libyang "containers". The \em context concept allows callers
- * to work in environments with different sets of YANG schemas. More detailed information can be found at
- * @ref howtocontext page.
- */
-
-/**
- * @struct ly_ctx
- * @brief libyang context handler.
- */
-struct ly_ctx;
-
-/**@} context */
-
-#include "log.h"
-#include "set.h"
-#include "dict.h"
-#include "tree_schema.h"
-
-/**
- * @ingroup context
- * @{
- */
-
-/**
- * @defgroup contextoptions Context options
- * @ingroup context
- *
- * Options to change context behavior.
- * @{
- */
-
-#define LY_CTX_ALLIMPLEMENTED 0x01 /**< All the imports of the schema being parsed are treated implemented. */
-#define LY_CTX_TRUSTED 0x02 /**< Handle the schema being parsed as trusted and skip its validation
- tests. Note that while this option improves performance, it can
- lead to an undefined behavior if the schema is not correct. */
-#define LY_CTX_NOYANGLIBRARY 0x04 /**< Do not internally implement ietf-yang-library module. The option
- causes that function ly_ctx_info() does not work (returns NULL) until
- the ietf-yang-library module is loaded manually. While any revision
- of this schema can be loaded with this option, note that the only
- revisions implemented by ly_ctx_info() are 2016-04-09 and 2018-01-17.
- This option cannot be changed on existing context. */
-#define LY_CTX_DISABLE_SEARCHDIRS 0x08 /**< Do not search for schemas in context's searchdirs neither in current
- working directory. It is entirely skipped and the only way to get
- schema data for imports or for ly_ctx_load_module() is to use the
- callbacks provided by caller via ly_ctx_set_module_imp_clb() */
-#define LY_CTX_DISABLE_SEARCHDIR_CWD 0x10 /**< Do not automatically search for schemas in current working
- directory, which is by default searched automatically (despite not
- recursively). */
-#define LY_CTX_PREFER_SEARCHDIRS 0x20 /**< When searching for schema, prefer searchdirs instead of user callback. */
-/**@} contextoptions */
-
-/**
- * @brief Create libyang context.
- *
- * Context is used to hold all information about schemas. Usually, the application is supposed
- * to work with a single context in which libyang is holding all schemas (and other internal
- * information) according to which the data trees will be processed and validated. So, the schema
- * trees are tightly connected with the specific context and they are held by the context internally
- * - caller does not need to keep pointers to the schemas returned by lys_parse(), context knows
- * about them. The data trees created with lyd_parse() are still connected with the specific context,
- * but they are not internally held by the context. The data tree just points and lean on some data
- * held by the context (schema tree, string dictionary, etc.). Therefore, in case of data trees, caller
- * is supposed to keep pointers returned by the lyd_parse() and manage the data tree on its own. This
- * also affects the number of instances of both tree types. While you can have only one instance of
- * specific schema connected with a single context, number of data tree instances is not connected.
- *
- * @param[in] search_dir Directory where libyang will search for the imported or included modules
- * and submodules. If no such directory is available, NULL is accepted.
- * @param[in] options Context options, see @ref contextoptions.
- * @param[out] new_ctx Pointer to the created libyang context if LY_SUCCESS returned.
- * @return LY_ERR return value.
- */
-LY_ERR ly_ctx_new(const char *search_dir, int options, struct ly_ctx **new_ctx);
-
-/**
- * @brief Add the search path into libyang context
- *
- * To reset search paths set in the context, use ly_ctx_unset_searchdirs() and then
- * set search paths again.
- *
- * @param[in] ctx Context to be modified.
- * @param[in] search_dir New search path to add to the current paths previously set in ctx.
- * @return LY_ERR return value.
- */
-LY_ERR ly_ctx_set_searchdir(struct ly_ctx *ctx, const char *search_dir);
-
-/**
- * @brief Clean the search path(s) from the libyang context
- *
- * @param[in] ctx Context to be modified.
- * @param[in] value Searchdir to be removed, use NULL to remove them all.
- * @return LY_ERR return value
- */
-LY_ERR ly_ctx_unset_searchdirs(struct ly_ctx *ctx, const char *value);
-
-/**
- * @brief Get the NULL-terminated list of the search paths in libyang context. Do not modify the result!
- *
- * @param[in] ctx Context to query.
- * @return NULL-terminated list (array) of the search paths, NULL if no searchpath was set.
- * Do not modify the provided data in any way!
- */
-const char * const *ly_ctx_get_searchdirs(const struct ly_ctx *ctx);
-
-/**
- * @brief Get the currently set context's options.
- *
- * @param[in] ctx Context to query.
- * @return Combination of all the currently set context's options, see @ref contextoptions.
- */
-int ly_ctx_get_options(const struct ly_ctx *ctx);
-
-/**
- * @brief Set some of the context's options, see @ref contextoptions.
- * @param[in] ctx Context to be modified.
- * @param[in] option Combination of the context's options to be set, see @ref contextoptions.
- * @return LY_ERR value.
- */
-LY_ERR ly_ctx_set_option(struct ly_ctx *ctx, int option);
-
-/**
- * @brief Unset some of the context's options, see @ref contextoptions.
- * @param[in] ctx Context to be modified.
- * @param[in] option Combination of the context's options to be unset, see @ref contextoptions.
- * @return LY_ERR value.
- */
-LY_ERR ly_ctx_unset_option(struct ly_ctx *ctx, int option);
-
-/**
- * @brief Get current ID of the modules set. The value is available also
- * as module-set-id in ly_ctx_info() result.
- *
- * @param[in] ctx Context to be examined.
- * @return Numeric identifier of the current context's modules set.
- */
-uint16_t ly_ctx_get_module_set_id(const struct ly_ctx *ctx);
-
-/**
- * @brief Free all internal structures of the specified context.
- *
- * The function should be used before terminating the application to destroy
- * and free all structures internally used by libyang. If the caller uses
- * multiple contexts, the function should be called for each used context.
- *
- * All instance data are supposed to be freed before destroying the context.
- * Data models are destroyed automatically as part of ly_ctx_destroy() call.
- *
- * @param[in] ctx libyang context to destroy
- * @param[in] private_destructor Optional destructor function for private objects assigned
- * to the nodes via lys_set_private(). If NULL, the private objects are not freed by libyang.
- * Remember the differences between the structures derived from ::lysc_node and always check
- * ::lysc_node#nodetype.
- */
-void ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lysc_node *node, void *priv));
-
-/** @} context */
-
#ifdef __cplusplus
}
#endif
diff --git a/src/log.c b/src/log.c
index 18d0b38..b576437 100644
--- a/src/log.c
+++ b/src/log.c
@@ -12,14 +12,14 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
+#include "common.h"
+
#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include "libyang.h"
-#include "common.h"
#include "context.h"
THREAD_LOCAL enum int_log_opts log_opt;
@@ -34,6 +34,19 @@
/* how many bytes add when enlarging buffers */
#define LY_BUF_STEP 128
+API LY_ERR
+ly_errcode(const struct ly_ctx *ctx)
+{
+ struct ly_err_item *i;
+
+ i = ly_err_first(ctx);
+ if (i) {
+ return i->prev->no;
+ }
+
+ return LY_SUCCESS;
+}
+
API LY_VECODE
ly_vecode(const struct ly_ctx *ctx)
{
diff --git a/src/log.h b/src/log.h
index 95232bc..d8b0a04 100644
--- a/src/log.h
+++ b/src/log.h
@@ -146,6 +146,7 @@
LY_ESYS, /**< System call failure */
LY_EINVAL, /**< Invalid value */
LY_EEXIST, /**< Item already exists */
+ LY_ENOTFOUND, /**< Item does not exists */
LY_EINT, /**< Internal error */
LY_EVALID, /**< Validation failure */
LY_EPLUGIN, /**< Error reported by a plugin */
@@ -163,6 +164,7 @@
LYVE_SYNTAX, /**< generic syntax error */
LYVE_SYNTAX_YANG, /**< YANG-related syntax error */
LYVE_REFERENCE, /**< invalid referencing or using an item */
+ LYVE_XPATH, /**< invalid XPath expression */
LYVE_SEMANTICS /**< generic semantic error */
} LY_VECODE;
@@ -191,6 +193,14 @@
LY_VECODE ly_vecode(const struct ly_ctx *ctx);
/**
+ * @brief Get the last (thread, context-specific) error code.
+ *
+ * @param[in] ctx Relative context.
+ * @return LY_ERR value of the last error code.
+ */
+LY_ERR ly_errcode(const struct ly_ctx *ctx);
+
+/**
* @brief Get the last (thread, context-specific) error message. If the coresponding module defined
* a specific error message, it will be used instead the default one.
*
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 434073d..9eadc37 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -41,10 +41,15 @@
(c >= 0xd0000 && c <= 0xdfffd) || (c >= 0xe0000 && c <= 0xefffd) || \
(c >= 0xf0000 && c <= 0xffffd) || (c >= 0x100000 && c <= 0x10fffd))
-/* These 2 macros checks YANG's identifier grammar rule */
-#define is_yangidentstartchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
-#define is_yangidentchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || \
- c == '_' || c == '-' || c == '.')
+#define CHECK_UNIQUENESS(CTX, ARRAY, MEMBER, STMT, IDENT) \
+ if (ARRAY) { \
+ for (unsigned int u = 0; u < LY_ARRAY_SIZE(ARRAY) - 1; ++u) { \
+ if (!strcmp((ARRAY)[u].MEMBER, IDENT)) { \
+ LOGVAL_YANG(CTX, LY_VCODE_DUPIDENT, IDENT, STMT); \
+ return LY_EVALID; \
+ } \
+ } \
+ }
#define INSERT_WORD(CTX, BUF, TARGET, WORD, LEN) \
if (BUF) {(TARGET) = lydict_insert_zc((CTX)->ctx, WORD);}\
@@ -52,8 +57,6 @@
#define MOVE_INPUT(CTX, DATA, COUNT) (*(data))+=COUNT;(CTX)->indent+=COUNT
-#define LOGVAL_YANG(CTX, ...) LOGVAL((CTX)->ctx, LY_VLOG_LINE, &(CTX)->line, __VA_ARGS__)
-
/**
* @brief Loop through all substatements providing, return if there are none.
*
@@ -66,12 +69,11 @@
*
* @return In case there are no substatements or a fatal error encountered.
*/
-#define YANG_READ_SUBSTMT_FOR(CTX, DATA, KW, WORD, WORD_LEN, ERR) \
- ERR = get_keyword(CTX, DATA, &KW, &WORD, &WORD_LEN); \
- LY_CHECK_RET(ERR); \
- \
+#define YANG_READ_SUBSTMT_FOR(CTX, DATA, KW, WORD, WORD_LEN, ERR, CHECKGOTO) \
+ LY_CHECK_RET(get_keyword(CTX, DATA, &KW, &WORD, &WORD_LEN)); \
if (KW == YANG_SEMICOLON) { \
- return ERR; \
+ CHECKGOTO; \
+ return LY_SUCCESS; \
} \
if (KW != YANG_LEFT_BRACE) { \
LOGVAL_YANG(CTX, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\", expected \";\" or \"{\".", ly_stmt2str(KW)); \
@@ -81,12 +83,15 @@
!ERR && (KW != YANG_RIGHT_BRACE); \
ERR = get_keyword(CTX, DATA, &KW, &WORD, &WORD_LEN))
-static LY_ERR parse_container(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings);
-static LY_ERR parse_uses(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings);
-static LY_ERR parse_choice(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings);
-static LY_ERR parse_case(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings);
-static LY_ERR parse_list(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings);
-static LY_ERR parse_grouping(struct ly_parser_ctx *ctx, const char **data, struct lysp_grp **groupings);
+#define YANG_CHECK_STMTVER_RET(CTX, KW, PARENT) \
+ if ((CTX)->mod->version < 2) {LOGVAL_YANG((CTX), LY_VCODE_INCHILDSTMT2, KW, PARENT); return LY_EVALID;}
+
+static LY_ERR parse_container(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+static LY_ERR parse_uses(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+static LY_ERR parse_choice(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+static LY_ERR parse_case(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+static LY_ERR parse_list(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+static LY_ERR parse_grouping(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_grp **groupings);
/**
* @brief Add another character to dynamic buffer, a low-level function.
@@ -539,7 +544,6 @@
get_argument(struct ly_parser_ctx *ctx, const char **data, enum yang_arg arg, char **word_p, char **word_b, size_t *word_len)
{
size_t buf_len = 0;
- LY_ERR ret;
/* word buffer - dynamically allocated */
*word_b = NULL;
@@ -558,23 +562,21 @@
"unquoted string character, optsep, semicolon or opening brace");
return LY_EVALID;
}
- ret = read_qstring(ctx, data, arg, word_p, word_b, word_len, &buf_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(read_qstring(ctx, data, arg, word_p, word_b, word_len, &buf_len));
goto str_end;
case '/':
if ((*data)[1] == '/') {
/* one-line comment */
MOVE_INPUT(ctx, data, 2);
- ret = skip_comment(ctx, data, 1);
+ LY_CHECK_RET(skip_comment(ctx, data, 1));
} else if ((*data)[1] == '*') {
/* block comment */
MOVE_INPUT(ctx, data, 2);
- ret = skip_comment(ctx, data, 2);
+ LY_CHECK_RET(skip_comment(ctx, data, 2));
} else {
/* not a comment after all */
- ret = buf_store_char(ctx, data, arg, word_p, word_len, word_b, &buf_len, 0);
+ LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, &buf_len, 0));
}
- LY_CHECK_RET(ret);
break;
case ' ':
if (*word_len) {
@@ -652,7 +654,6 @@
static LY_ERR
get_keyword(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword *kw, char **word_p, size_t *word_len)
{
- LY_ERR ret;
int prefix;
const char *word_start;
unsigned int c;
@@ -670,17 +671,11 @@
if ((*data)[1] == '/') {
/* one-line comment */
MOVE_INPUT(ctx, data, 2);
- ret = skip_comment(ctx, data, 1);
- if (ret) {
- return ret;
- }
+ LY_CHECK_RET(skip_comment(ctx, data, 1));
} else if ((*data)[1] == '*') {
/* block comment */
MOVE_INPUT(ctx, data, 2);
- ret = skip_comment(ctx, data, 2);
- if (ret) {
- return ret;
- }
+ LY_CHECK_RET(skip_comment(ctx, data, 2));
} else {
/* error - not a comment after all, keyword cannot start with slash */
LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Invalid identifier first character '/'.");
@@ -973,7 +968,7 @@
struct lysp_stmt **child)
{
char *buf;
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
enum yang_keyword kw;
struct lysp_stmt *stmt, *par_child;
@@ -983,8 +978,7 @@
stmt->stmt = lydict_insert(ctx->ctx, word, word_len);
/* get optional argument */
- ret = get_argument(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len));
if (word) {
if (buf) {
@@ -1002,14 +996,9 @@
par_child->next = stmt;
}
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
- ret = parse_ext_substmt(ctx, data, word, word_len, &stmt->child);
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, ) {
+ LY_CHECK_RET(parse_ext_substmt(ctx, data, word, word_len, &stmt->child));
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1030,7 +1019,7 @@
parse_ext(struct ly_parser_ctx *ctx, const char **data, const char *ext_name, int ext_name_len, LYEXT_SUBSTMT insubstmt,
uint32_t insubstmt_index, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
struct lysp_ext_instance *e;
@@ -1044,21 +1033,15 @@
e->insubstmt_index = insubstmt_index;
/* get optional argument */
- ret = get_argument(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len));
if (word) {
INSERT_WORD(ctx, buf, e->argument, word, word_len);
}
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
- ret = parse_ext_substmt(ctx, data, word, word_len, &e->child);
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
+ LY_CHECK_RET(parse_ext_substmt(ctx, data, word, word_len, &e->child));
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1080,7 +1063,7 @@
parse_text_field(struct ly_parser_ctx *ctx, const char **data, LYEXT_SUBSTMT substmt, uint32_t substmt_index,
const char **value, enum yang_arg arg, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -1091,27 +1074,21 @@
}
/* get value */
- ret = get_argument(ctx, data, arg, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, arg, &word, &buf, &word_len));
/* store value and spend buf if allocated */
INSERT_WORD(ctx, buf, *value, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, substmt, substmt_index, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, substmt, substmt_index, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), lyext_substmt2str(substmt));
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1127,7 +1104,7 @@
static LY_ERR
parse_yangversion(struct ly_parser_ctx *ctx, const char **data, struct lysp_module *mod)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -1138,8 +1115,7 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if ((word_len == 3) && !strncmp(word, "1.0", word_len)) {
mod->version = LYS_VERSION_1_0;
@@ -1152,21 +1128,16 @@
}
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_VERSION, 0, &mod->exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_VERSION, 0, &mod->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "yang-version");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1184,7 +1155,7 @@
static LY_ERR
parse_belongsto(struct ly_parser_ctx *ctx, const char **data, const char **belongsto, const char **prefix, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -1195,34 +1166,29 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, *belongsto, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
switch (kw) {
case YANG_PREFIX:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, prefix, Y_IDENTIF_ARG, exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, prefix, Y_IDENTIF_ARG, exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_BELONGSTO, 0, exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_BELONGSTO, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "belongs-to");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
LY_CHECK_RET(ret);
-
+checks:
/* mandatory substatements */
if (!*prefix) {
LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "prefix", "belongs-to");
return LY_EVALID;
}
-
return ret;
}
@@ -1239,7 +1205,7 @@
static LY_ERR
parse_revisiondate(struct ly_parser_ctx *ctx, const char **data, char *rev, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -1250,11 +1216,10 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
/* check value */
- if (lysp_check_date(ctx->ctx, word, word_len, "revision-date")) {
+ if (lysp_check_date(ctx, word, word_len, "revision-date")) {
free(buf);
return LY_EVALID;
}
@@ -1263,21 +1228,16 @@
strncpy(rev, word, word_len);
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_REVISIONDATE, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_REVISIONDATE, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "revision-date");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1291,45 +1251,49 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_include(struct ly_parser_ctx *ctx, const char **data, struct lysp_include **includes)
+parse_include(struct ly_parser_ctx *ctx, const char **data, struct lysp_module *mod)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
struct lysp_include *inc;
- LY_ARRAY_NEW_RET(ctx->ctx, *includes, inc, LY_EMEM);
+ LY_ARRAY_NEW_RET(ctx->ctx, mod->includes, inc, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, inc->name, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ /* submodules share the namespace with the module names, so there must not be
+ * a module of the same name in the context, no need for revision matching */
+ if (!strcmp(ctx->mod->name, inc->name) || ly_ctx_get_module_latest(ctx->ctx, inc->name)) {
+ LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Name collision between module and submodule of name \"%s\".", inc->name);
+ return LY_EVALID;
+ }
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &inc->dsc, Y_STR_ARG, &inc->exts);
+ YANG_CHECK_STMTVER_RET(ctx, "description", "include");
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &inc->dsc, Y_STR_ARG, &inc->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &inc->ref, Y_STR_ARG, &inc->exts);
+ YANG_CHECK_STMTVER_RET(ctx, "reference", "include");
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &inc->ref, Y_STR_ARG, &inc->exts));
break;
case YANG_REVISION_DATE:
- ret = parse_revisiondate(ctx, data, inc->rev, &inc->exts);
+ LY_CHECK_RET(parse_revisiondate(ctx, data, inc->rev, &inc->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inc->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inc->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "include");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1345,7 +1309,7 @@
static LY_ERR
parse_import(struct ly_parser_ctx *ctx, const char **data, struct lysp_module *module)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -1354,43 +1318,38 @@
LY_ARRAY_NEW_RET(ctx->ctx, module->imports, imp, LY_EVALID);
/* get value */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, imp->name, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
switch (kw) {
case YANG_PREFIX:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &imp->prefix, Y_IDENTIF_ARG, &imp->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &imp->prefix, Y_IDENTIF_ARG, &imp->exts));
LY_CHECK_RET(lysp_check_prefix(ctx, module, &imp->prefix), LY_EVALID);
break;
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &imp->dsc, Y_STR_ARG, &imp->exts);
+ YANG_CHECK_STMTVER_RET(ctx, "description", "import");
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &imp->dsc, Y_STR_ARG, &imp->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &imp->ref, Y_STR_ARG, &imp->exts);
+ YANG_CHECK_STMTVER_RET(ctx, "reference", "import");
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &imp->ref, Y_STR_ARG, &imp->exts));
break;
case YANG_REVISION_DATE:
- ret = parse_revisiondate(ctx, data, imp->rev, &imp->exts);
+ LY_CHECK_RET(parse_revisiondate(ctx, data, imp->rev, &imp->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &imp->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &imp->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "import");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
LY_CHECK_RET(ret);
-
+checks:
/* mandatory substatements */
- if (!imp->prefix) {
- LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "prefix", "import");
- return LY_EVALID;
- }
+ LY_CHECK_ERR_RET(!imp->prefix, LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "prefix", "import"), LY_EVALID);
return ret;
}
@@ -1407,7 +1366,7 @@
static LY_ERR
parse_revision(struct ly_parser_ctx *ctx, const char **data, struct lysp_revision **revs)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -1416,38 +1375,32 @@
LY_ARRAY_NEW_RET(ctx->ctx, *revs, rev, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
/* check value */
- if (lysp_check_date(ctx->ctx, word, word_len, "revision")) {
+ if (lysp_check_date(ctx, word, word_len, "revision")) {
return LY_EVALID;
}
- strncpy(rev->rev, word, word_len);
+ strncpy(rev->date, word, word_len);
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &rev->dsc, Y_STR_ARG, &rev->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &rev->dsc, Y_STR_ARG, &rev->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &rev->ref, Y_STR_ARG, &rev->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &rev->ref, Y_STR_ARG, &rev->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &rev->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &rev->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "revision");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1467,7 +1420,7 @@
parse_text_fields(struct ly_parser_ctx *ctx, const char **data, LYEXT_SUBSTMT substmt, const char ***texts, enum yang_arg arg,
struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
const char **item;
size_t word_len;
@@ -1477,25 +1430,19 @@
LY_ARRAY_NEW_RET(ctx->ctx, *texts, item, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, arg, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, arg, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, *item, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, substmt, LY_ARRAY_SIZE(*texts) - 1, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, substmt, LY_ARRAY_SIZE(*texts) - 1, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), lyext_substmt2str(substmt));
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1512,7 +1459,7 @@
static LY_ERR
parse_config(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -1523,8 +1470,7 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if ((word_len == 4) && !strncmp(word, "true", word_len)) {
*flags |= LYS_CONFIG_W;
@@ -1537,21 +1483,16 @@
}
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_CONFIG, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_CONFIG, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "config");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1568,7 +1509,7 @@
static LY_ERR
parse_mandatory(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -1579,8 +1520,7 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if ((word_len == 4) && !strncmp(word, "true", word_len)) {
*flags |= LYS_MAND_TRUE;
@@ -1593,21 +1533,16 @@
}
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MANDATORY, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MANDATORY, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "mandatory");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1624,43 +1559,37 @@
static LY_ERR
parse_restr(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword restr_kw, struct lysp_restr *restr)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, restr->arg, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts));
break;
case YANG_ERROR_APP_TAG:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts));
break;
case YANG_ERROR_MESSAGE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &restr->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &restr->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(restr_kw));
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1680,7 +1609,6 @@
struct lysp_restr *restr;
LY_ARRAY_NEW_RET(ctx->ctx, *restrs, restr, LY_EMEM);
-
return parse_restr(ctx, data, restr_kw, restr);
}
@@ -1697,7 +1625,7 @@
static LY_ERR
parse_status(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -1708,8 +1636,7 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if ((word_len == 7) && !strncmp(word, "current", word_len)) {
*flags |= LYS_STATUS_CURR;
@@ -1724,21 +1651,16 @@
}
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_STATUS, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_STATUS, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "status");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1754,7 +1676,7 @@
static LY_ERR
parse_when(struct ly_parser_ctx *ctx, const char **data, struct lysp_when **when_p)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -1770,31 +1692,25 @@
*when_p = when;
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, when->cond, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &when->dsc, Y_STR_ARG, &when->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &when->dsc, Y_STR_ARG, &when->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &when->ref, Y_STR_ARG, &when->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &when->ref, Y_STR_ARG, &when->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &when->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &when->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "when");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1809,9 +1725,9 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_any(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_node **siblings)
+parse_any(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_node *parent, struct lysp_node **siblings)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
struct lysp_node *iter;
@@ -1821,6 +1737,7 @@
any = calloc(1, sizeof *any);
LY_CHECK_ERR_RET(!any, LOGMEM(ctx->ctx), LY_EMEM);
any->nodetype = kw == YANG_ANYDATA ? LYS_ANYDATA : LYS_ANYXML;
+ any->parent = parent;
/* insert into siblings */
if (!*siblings) {
@@ -1831,52 +1748,45 @@
}
/* get name */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, any->name, word, word_len);
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CONFIG:
- ret = parse_config(ctx, data, &any->flags, &any->exts);
+ LY_CHECK_RET(parse_config(ctx, data, &any->flags, &any->exts));
break;
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &any->dsc, Y_STR_ARG, &any->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &any->dsc, Y_STR_ARG, &any->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &any->iffeatures, Y_STR_ARG, &any->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &any->iffeatures, Y_STR_ARG, &any->exts));
break;
case YANG_MANDATORY:
- ret = parse_mandatory(ctx, data, &any->flags, &any->exts);
+ LY_CHECK_RET(parse_mandatory(ctx, data, &any->flags, &any->exts));
break;
case YANG_MUST:
- ret = parse_restrs(ctx, data, kw, &any->musts);
+ LY_CHECK_RET(parse_restrs(ctx, data, kw, &any->musts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &any->ref, Y_STR_ARG, &any->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &any->ref, Y_STR_ARG, &any->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &any->flags, &any->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &any->flags, &any->exts));
break;
case YANG_WHEN:
- ret = parse_when(ctx, data, &any->when);
+ LY_CHECK_RET(parse_when(ctx, data, &any->when));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &any->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &any->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw),
(any->nodetype & LYS_ANYDATA) == LYS_ANYDATA ? ly_stmt2str(YANG_ANYDATA) : ly_stmt2str(YANG_ANYXML));
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -1896,7 +1806,7 @@
parse_type_enum_value_pos(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword val_kw, int64_t *value, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word, *ptr;
size_t word_len;
long int num;
@@ -1910,31 +1820,35 @@
*flags |= LYS_SET_VALUE;
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if (!word_len || (word[0] == '+') || ((word[0] == '0') && (word_len > 1)) || ((val_kw == YANG_VALUE) && !strncmp(word, "-0", 2))) {
LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
- free(buf);
- return LY_EVALID;
+ goto error;
}
errno = 0;
if (val_kw == YANG_VALUE) {
num = strtol(word, &ptr, 10);
+ if (num < INT64_C(-2147483648) || num > INT64_C(2147483647)) {
+ LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
+ goto error;
+ }
} else {
unum = strtoul(word, &ptr, 10);
+ if (unum > UINT64_C(4294967295)) {
+ LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
+ goto error;
+ }
}
/* we have not parsed the whole argument */
if ((size_t)(ptr - word) != word_len) {
LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
- free(buf);
- return LY_EVALID;
+ goto error;
}
if (errno == ERANGE) {
LOGVAL_YANG(ctx, LY_VCODE_OOB, word_len, word, ly_stmt2str(val_kw));
- free(buf);
- return LY_EVALID;
+ goto error;
}
if (val_kw == YANG_VALUE) {
*value = num;
@@ -1943,22 +1857,21 @@
}
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, val_kw == YANG_VALUE ? LYEXT_SUBSTMT_VALUE : LYEXT_SUBSTMT_POSITION, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, val_kw == YANG_VALUE ? LYEXT_SUBSTMT_VALUE : LYEXT_SUBSTMT_POSITION, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(val_kw));
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
+
+error:
+ free(buf);
+ return LY_EVALID;
}
/**
@@ -1974,50 +1887,68 @@
static LY_ERR
parse_type_enum(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword enum_kw, struct lysp_type_enum **enums)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
- size_t word_len;
+ size_t word_len, u;
enum yang_keyword kw;
struct lysp_type_enum *enm;
LY_ARRAY_NEW_RET(ctx->ctx, *enums, enm, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, enum_kw == YANG_ENUM ? Y_STR_ARG : Y_IDENTIF_ARG, &word, &buf, &word_len));
+ if (enum_kw == YANG_ENUM) {
+ if (!word_len) {
+ LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Enum name must not be zero-length.");
+ free(buf);
+ return LY_EVALID;
+ } else if (isspace(word[0]) || isspace(word[word_len - 1])) {
+ LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Enum name must not have any leading or trailing whitespaces (\"%.*s\").",
+ word_len, word);
+ free(buf);
+ return LY_EVALID;
+ } else {
+ for (u = 0; u < word_len; ++u) {
+ if (iscntrl(word[u])) {
+ LOGWRN(ctx->ctx, "Control characters in enum name should be avoided (\"%.*s\", character number %d).",
+ word_len, word, u + 1);
+ break;
+ }
+ }
+ }
+ } else { /* YANG_BIT */
+ }
INSERT_WORD(ctx, buf, enm->name, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ CHECK_UNIQUENESS(ctx, *enums, name, ly_stmt2str(enum_kw), enm->name);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &enm->dsc, Y_STR_ARG, &enm->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &enm->dsc, Y_STR_ARG, &enm->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &enm->iffeatures, Y_STR_ARG, &enm->exts);
+ YANG_CHECK_STMTVER_RET(ctx, "if-feature", ly_stmt2str(enum_kw));
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &enm->iffeatures, Y_STR_ARG, &enm->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &enm->ref, Y_STR_ARG, &enm->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &enm->ref, Y_STR_ARG, &enm->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &enm->flags, &enm->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &enm->flags, &enm->exts));
break;
case YANG_VALUE:
case YANG_POSITION:
- ret = parse_type_enum_value_pos(ctx, data, kw, &enm->value, &enm->flags, &enm->exts);
+ LY_CHECK_RET(parse_type_enum_value_pos(ctx, data, kw, &enm->value, &enm->flags, &enm->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &enm->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &enm->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(enum_kw));
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -2034,7 +1965,7 @@
static LY_ERR
parse_type_fracdigits(struct ly_parser_ctx *ctx, const char **data, uint8_t *fracdig, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word, *ptr;
size_t word_len;
unsigned long int num;
@@ -2046,8 +1977,7 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if (!word_len || (word[0] == '0') || !isdigit(word[0])) {
LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
@@ -2071,21 +2001,16 @@
*fracdig = num;
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_FRACDIGITS, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_FRACDIGITS, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "fraction-digits");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -2104,7 +2029,7 @@
parse_type_reqinstance(struct ly_parser_ctx *ctx, const char **data, uint8_t *reqinst, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -2116,8 +2041,7 @@
*flags |= LYS_SET_REQINST;
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if ((word_len == 4) && !strncmp(word, "true", word_len)) {
*reqinst = 1;
@@ -2128,21 +2052,16 @@
}
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_REQINSTANCE, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_REQINSTANCE, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "require-instance");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -2159,7 +2078,7 @@
static LY_ERR
parse_type_pattern_modifier(struct ly_parser_ctx *ctx, const char **data, const char **pat, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -2170,8 +2089,7 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if ((word_len != 12) || strncmp(word, "invert-match", word_len)) {
LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "modifier");
@@ -2190,21 +2108,16 @@
buf[0] = 0x15;
*pat = lydict_insert_zc(ctx->ctx, buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MODIFIER, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MODIFIER, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "modifier");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -2220,7 +2133,7 @@
static LY_ERR
parse_type_pattern(struct ly_parser_ctx *ctx, const char **data, struct lysp_restr **patterns)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -2229,8 +2142,7 @@
LY_ARRAY_NEW_RET(ctx->ctx, *patterns, restr, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
/* add special meaning first byte */
if (buf) {
@@ -2245,36 +2157,32 @@
buf[word_len + 1] = '\0'; /* terminating NULL byte */
restr->arg = lydict_insert_zc(ctx->ctx, buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts));
break;
case YANG_ERROR_APP_TAG:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts));
break;
case YANG_ERROR_MESSAGE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts));
break;
case YANG_MODIFIER:
- ret = parse_type_pattern_modifier(ctx, data, &restr->arg, &restr->exts);
+ YANG_CHECK_STMTVER_RET(ctx, "modifier", "pattern");
+ LY_CHECK_RET(parse_type_pattern_modifier(ctx, data, &restr->arg, &restr->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &restr->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &restr->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "pattern");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -2290,7 +2198,7 @@
static LY_ERR
parse_type(struct ly_parser_ctx *ctx, const char **data, struct lysp_type *type)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -2302,25 +2210,26 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, type->name, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_BASE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_BASE, &type->bases, Y_PREF_IDENTIF_ARG, &type->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_BASE, &type->bases, Y_PREF_IDENTIF_ARG, &type->exts));
+ type->flags |= LYS_SET_BASE;
break;
case YANG_BIT:
- ret = parse_type_enum(ctx, data, kw, &type->bits);
+ LY_CHECK_RET(parse_type_enum(ctx, data, kw, &type->bits));
+ type->flags |= LYS_SET_BIT;
break;
case YANG_ENUM:
- ret = parse_type_enum(ctx, data, kw, &type->enums);
+ LY_CHECK_RET(parse_type_enum(ctx, data, kw, &type->enums));
+ type->flags |= LYS_SET_ENUM;
break;
case YANG_FRACTION_DIGITS:
- ret = parse_type_fracdigits(ctx, data, &type->fraction_digits, &type->exts);
+ LY_CHECK_RET(parse_type_fracdigits(ctx, data, &type->fraction_digits, &type->exts));
+ type->flags |= LYS_SET_FRDIGITS;
break;
case YANG_LENGTH:
if (type->length) {
@@ -2330,13 +2239,16 @@
type->length = calloc(1, sizeof *type->length);
LY_CHECK_ERR_RET(!type->length, LOGMEM(ctx->ctx), LY_EMEM);
- ret = parse_restr(ctx, data, kw, type->length);
+ LY_CHECK_RET(parse_restr(ctx, data, kw, type->length));
+ type->flags |= LYS_SET_LENGTH;
break;
case YANG_PATH:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PATH, 0, &type->path, Y_STR_ARG, &type->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PATH, 0, &type->path, Y_STR_ARG, &type->exts));
+ type->flags |= LYS_SET_PATH;
break;
case YANG_PATTERN:
- ret = parse_type_pattern(ctx, data, &type->patterns);
+ LY_CHECK_RET(parse_type_pattern(ctx, data, &type->patterns));
+ type->flags |= LYS_SET_PATTERN;
break;
case YANG_RANGE:
if (type->range) {
@@ -2346,28 +2258,26 @@
type->range = calloc(1, sizeof *type->range);
LY_CHECK_ERR_RET(!type->range, LOGMEM(ctx->ctx), LY_EVALID);
- ret = parse_restr(ctx, data, kw, type->range);
+ LY_CHECK_RET(parse_restr(ctx, data, kw, type->range));
+ type->flags |= LYS_SET_RANGE;
break;
case YANG_REQUIRE_INSTANCE:
- ret = parse_type_reqinstance(ctx, data, &type->require_instance, &type->flags, &type->exts);
+ LY_CHECK_RET(parse_type_reqinstance(ctx, data, &type->require_instance, &type->flags, &type->exts));
+ /* LYS_SET_REQINST checked and set inside parse_type_reqinstance() */
break;
case YANG_TYPE:
- {
- LY_ARRAY_NEW_RET(ctx->ctx, type->types, nest_type, LY_EMEM);
- }
- ret = parse_type(ctx, data, nest_type);
+ LY_ARRAY_NEW_RET(ctx->ctx, type->types, nest_type, LY_EMEM);
+ LY_CHECK_RET(parse_type(ctx, data, nest_type));
+ type->flags |= LYS_SET_TYPE;
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &type->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &type->exts));
break;
default:
- LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "when");
+ LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "type");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -2381,9 +2291,9 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_leaf(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_leaf(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -2394,6 +2304,7 @@
leaf = calloc(1, sizeof *leaf);
LY_CHECK_ERR_RET(!leaf, LOGMEM(ctx->ctx), LY_EMEM);
leaf->nodetype = LYS_LEAF;
+ leaf->parent = parent;
/* insert into siblings */
if (!*siblings) {
@@ -2404,65 +2315,64 @@
}
/* get name */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, leaf->name, word, word_len);
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
switch (kw) {
case YANG_CONFIG:
- ret = parse_config(ctx, data, &leaf->flags, &leaf->exts);
+ LY_CHECK_RET(parse_config(ctx, data, &leaf->flags, &leaf->exts));
break;
case YANG_DEFAULT:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &leaf->dflt, Y_STR_ARG, &leaf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &leaf->dflt, Y_STR_ARG, &leaf->exts));
break;
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &leaf->dsc, Y_STR_ARG, &leaf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &leaf->dsc, Y_STR_ARG, &leaf->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &leaf->iffeatures, Y_STR_ARG, &leaf->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &leaf->iffeatures, Y_STR_ARG, &leaf->exts));
break;
case YANG_MANDATORY:
- ret = parse_mandatory(ctx, data, &leaf->flags, &leaf->exts);
+ LY_CHECK_RET(parse_mandatory(ctx, data, &leaf->flags, &leaf->exts));
break;
case YANG_MUST:
- ret = parse_restrs(ctx, data, kw, &leaf->musts);
+ LY_CHECK_RET(parse_restrs(ctx, data, kw, &leaf->musts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &leaf->ref, Y_STR_ARG, &leaf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &leaf->ref, Y_STR_ARG, &leaf->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &leaf->flags, &leaf->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &leaf->flags, &leaf->exts));
break;
case YANG_TYPE:
- ret = parse_type(ctx, data, &leaf->type);
+ LY_CHECK_RET(parse_type(ctx, data, &leaf->type));
break;
case YANG_UNITS:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &leaf->units, Y_STR_ARG, &leaf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &leaf->units, Y_STR_ARG, &leaf->exts));
break;
case YANG_WHEN:
- ret = parse_when(ctx, data, &leaf->when);
+ LY_CHECK_RET(parse_when(ctx, data, &leaf->when));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &leaf->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &leaf->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "leaf");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
LY_CHECK_RET(ret);
-
+checks:
/* mandatory substatements */
if (!leaf->type.name) {
LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "type", "leaf");
return LY_EVALID;
}
+ if ((leaf->flags & LYS_MAND_TRUE) && (leaf->dflt)) {
+ LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMSCOMB, "mandatory", "default", "leaf");
+ return LY_EVALID;
+ }
return ret;
}
@@ -2481,7 +2391,7 @@
static LY_ERR
parse_maxelements(struct ly_parser_ctx *ctx, const char **data, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word, *ptr;
size_t word_len;
unsigned long int num;
@@ -2494,8 +2404,7 @@
*flags |= LYS_SET_MAX;
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if (!word_len || (word[0] == '0') || ((word[0] != 'u') && !isdigit(word[0]))) {
LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
@@ -2522,21 +2431,16 @@
}
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MAX, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MAX, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "max-elements");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -2554,7 +2458,7 @@
static LY_ERR
parse_minelements(struct ly_parser_ctx *ctx, const char **data, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word, *ptr;
size_t word_len;
unsigned long int num;
@@ -2567,8 +2471,7 @@
*flags |= LYS_SET_MIN;
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if (!word_len || !isdigit(word[0]) || ((word[0] == '0') && (word_len > 1))) {
LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
@@ -2592,21 +2495,16 @@
*min = num;
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MIN, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MIN, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "min-elements");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -2623,7 +2521,7 @@
static LY_ERR
parse_orderedby(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -2634,8 +2532,7 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if ((word_len == 6) && !strncmp(word, "system", word_len)) {
*flags |= LYS_ORDBY_SYSTEM;
@@ -2648,21 +2545,16 @@
}
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_ORDEREDBY, 0, exts);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_ORDEREDBY, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "ordered-by");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -2676,9 +2568,9 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_leaflist(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_leaflist(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -2689,6 +2581,7 @@
llist = calloc(1, sizeof *llist);
LY_CHECK_ERR_RET(!llist, LOGMEM(ctx->ctx), LY_EMEM);
llist->nodetype = LYS_LEAFLIST;
+ llist->parent = parent;
/* insert into siblings */
if (!*siblings) {
@@ -2699,71 +2592,77 @@
}
/* get name */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, llist->name, word, word_len);
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
switch (kw) {
case YANG_CONFIG:
- ret = parse_config(ctx, data, &llist->flags, &llist->exts);
+ LY_CHECK_RET(parse_config(ctx, data, &llist->flags, &llist->exts));
break;
case YANG_DEFAULT:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, &llist->dflts, Y_STR_ARG, &llist->exts);
+ YANG_CHECK_STMTVER_RET(ctx, "default", "leaf-list");
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, &llist->dflts, Y_STR_ARG, &llist->exts));
break;
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &llist->dsc, Y_STR_ARG, &llist->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &llist->dsc, Y_STR_ARG, &llist->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &llist->iffeatures, Y_STR_ARG, &llist->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &llist->iffeatures, Y_STR_ARG, &llist->exts));
break;
case YANG_MAX_ELEMENTS:
- ret = parse_maxelements(ctx, data, &llist->max, &llist->flags, &llist->exts);
+ LY_CHECK_RET(parse_maxelements(ctx, data, &llist->max, &llist->flags, &llist->exts));
break;
case YANG_MIN_ELEMENTS:
- ret = parse_minelements(ctx, data, &llist->min, &llist->flags, &llist->exts);
+ LY_CHECK_RET(parse_minelements(ctx, data, &llist->min, &llist->flags, &llist->exts));
break;
case YANG_MUST:
- ret = parse_restrs(ctx, data, kw, &llist->musts);
+ LY_CHECK_RET(parse_restrs(ctx, data, kw, &llist->musts));
break;
case YANG_ORDERED_BY:
- ret = parse_orderedby(ctx, data, &llist->flags, &llist->exts);
+ LY_CHECK_RET(parse_orderedby(ctx, data, &llist->flags, &llist->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &llist->ref, Y_STR_ARG, &llist->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &llist->ref, Y_STR_ARG, &llist->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &llist->flags, &llist->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &llist->flags, &llist->exts));
break;
case YANG_TYPE:
- ret = parse_type(ctx, data, &llist->type);
+ LY_CHECK_RET(parse_type(ctx, data, &llist->type));
break;
case YANG_UNITS:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &llist->units, Y_STR_ARG, &llist->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &llist->units, Y_STR_ARG, &llist->exts));
break;
case YANG_WHEN:
- ret = parse_when(ctx, data, &llist->when);
+ LY_CHECK_RET(parse_when(ctx, data, &llist->when));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &llist->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &llist->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "llist");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
LY_CHECK_RET(ret);
-
+checks:
/* mandatory substatements */
if (!llist->type.name) {
LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "type", "leaf-list");
return LY_EVALID;
}
+ if ((llist->min) && (llist->dflts)) {
+ LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMSCOMB, "min-elements", "default", "leaf-list");
+ return LY_EVALID;
+ }
+ if (llist->max && llist->min > llist->max) {
+ LOGVAL_YANG(ctx, LYVE_SEMANTICS,
+ "Invalid combination of min-elements and max-elements: min value %u is bigger than the max value %u.",
+ llist->min, llist->max);
+ return LY_EVALID;
+ }
return ret;
}
@@ -2780,7 +2679,7 @@
static LY_ERR
parse_refine(struct ly_parser_ctx *ctx, const char **data, struct lysp_refine **refines)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -2789,55 +2688,50 @@
LY_ARRAY_NEW_RET(ctx->ctx, *refines, rf, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, rf->nodeid, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CONFIG:
- ret = parse_config(ctx, data, &rf->flags, &rf->exts);
+ LY_CHECK_RET(parse_config(ctx, data, &rf->flags, &rf->exts));
break;
case YANG_DEFAULT:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, &rf->dflts, Y_STR_ARG, &rf->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, &rf->dflts, Y_STR_ARG, &rf->exts));
break;
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &rf->dsc, Y_STR_ARG, &rf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &rf->dsc, Y_STR_ARG, &rf->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &rf->iffeatures, Y_STR_ARG, &rf->exts);
+ YANG_CHECK_STMTVER_RET(ctx, "if-feature", "refine");
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &rf->iffeatures, Y_STR_ARG, &rf->exts));
break;
case YANG_MAX_ELEMENTS:
- ret = parse_maxelements(ctx, data, &rf->max, &rf->flags, &rf->exts);
+ LY_CHECK_RET(parse_maxelements(ctx, data, &rf->max, &rf->flags, &rf->exts));
break;
case YANG_MIN_ELEMENTS:
- ret = parse_minelements(ctx, data, &rf->min, &rf->flags, &rf->exts);
+ LY_CHECK_RET(parse_minelements(ctx, data, &rf->min, &rf->flags, &rf->exts));
break;
case YANG_MUST:
- ret = parse_restrs(ctx, data, kw, &rf->musts);
+ LY_CHECK_RET(parse_restrs(ctx, data, kw, &rf->musts));
break;
case YANG_MANDATORY:
- ret = parse_mandatory(ctx, data, &rf->flags, &rf->exts);
+ LY_CHECK_RET(parse_mandatory(ctx, data, &rf->flags, &rf->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &rf->ref, Y_STR_ARG, &rf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &rf->ref, Y_STR_ARG, &rf->exts));
break;
case YANG_PRESENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PRESENCE, 0, &rf->presence, Y_STR_ARG, &rf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PRESENCE, 0, &rf->presence, Y_STR_ARG, &rf->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &rf->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &rf->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "refine");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -2851,9 +2745,9 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_typedef(struct ly_parser_ctx *ctx, const char **data, struct lysp_tpdf **typedefs)
+parse_typedef(struct ly_parser_ctx *ctx, struct lysp_node *parent, const char **data, struct lysp_tpdf **typedefs)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -2862,51 +2756,51 @@
LY_ARRAY_NEW_RET(ctx->ctx, *typedefs, tpdf, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, tpdf->name, word, word_len);
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
switch (kw) {
case YANG_DEFAULT:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &tpdf->dflt, Y_STR_ARG, &tpdf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &tpdf->dflt, Y_STR_ARG, &tpdf->exts));
break;
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &tpdf->dsc, Y_STR_ARG, &tpdf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &tpdf->dsc, Y_STR_ARG, &tpdf->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &tpdf->ref, Y_STR_ARG, &tpdf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &tpdf->ref, Y_STR_ARG, &tpdf->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &tpdf->flags, &tpdf->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &tpdf->flags, &tpdf->exts));
break;
case YANG_TYPE:
- ret = parse_type(ctx, data, &tpdf->type);
+ LY_CHECK_RET(parse_type(ctx, data, &tpdf->type));
break;
case YANG_UNITS:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &tpdf->units, Y_STR_ARG, &tpdf->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &tpdf->units, Y_STR_ARG, &tpdf->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &tpdf->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &tpdf->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "typedef");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
LY_CHECK_RET(ret);
-
+checks:
/* mandatory substatements */
if (!tpdf->type.name) {
LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "type", "typedef");
return LY_EVALID;
}
+ /* store data for collision check */
+ if (parent) {
+ ly_set_add(&ctx->tpdfs_nodes, parent, 0);
+ }
+
return ret;
}
@@ -2921,15 +2815,18 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_inout(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_action_inout **inout_p)
+parse_inout(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword inout_kw, struct lysp_node *parent, struct lysp_action_inout **inout_p)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *word;
size_t word_len;
struct lysp_action_inout *inout;
+ enum yang_keyword kw;
+ unsigned int u;
+ struct lysp_node *child;
if (*inout_p) {
- LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(kw));
+ LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(inout_kw));
return LY_EVALID;
}
@@ -2937,54 +2834,60 @@
inout = calloc(1, sizeof *inout);
LY_CHECK_ERR_RET(!inout, LOGMEM(ctx->ctx), LY_EMEM);
*inout_p = inout;
+ inout->nodetype = LYS_INOUT;
+ inout->parent = parent;
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_ANYDATA:
+ YANG_CHECK_STMTVER_RET(ctx, "anydata", ly_stmt2str(inout_kw));
+ /* fall through */
case YANG_ANYXML:
- ret = parse_any(ctx, data, kw, &inout->data);
+ LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)inout, &inout->data));
break;
case YANG_CHOICE:
- ret = parse_choice(ctx, data, &inout->data);
+ LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)inout, &inout->data));
break;
case YANG_CONTAINER:
- ret = parse_container(ctx, data, &inout->data);
+ LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)inout, &inout->data));
break;
case YANG_LEAF:
- ret = parse_leaf(ctx, data, &inout->data);
+ LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)inout, &inout->data));
break;
case YANG_LEAF_LIST:
- ret = parse_leaflist(ctx, data, &inout->data);
+ LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)inout, &inout->data));
break;
case YANG_LIST:
- ret = parse_list(ctx, data, &inout->data);
+ LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)inout, &inout->data));
break;
case YANG_USES:
- ret = parse_uses(ctx, data, &inout->data);
+ LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)inout, &inout->data));
break;
case YANG_TYPEDEF:
- ret = parse_typedef(ctx, data, &inout->typedefs);
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)inout, data, &inout->typedefs));
break;
case YANG_MUST:
- ret = parse_restrs(ctx, data, kw, &inout->musts);
+ YANG_CHECK_STMTVER_RET(ctx, "must", ly_stmt2str(inout_kw));
+ LY_CHECK_RET(parse_restrs(ctx, data, kw, &inout->musts));
break;
case YANG_GROUPING:
- ret = parse_grouping(ctx, data, &inout->groupings);
+ LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)inout, &inout->groupings));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inout->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inout->exts));
break;
default:
- LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "input/output");
+ LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(inout_kw));
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
+ /* finalize parent pointers to the reallocated items */
+ LY_ARRAY_FOR(inout->groupings, u) {
+ LY_LIST_FOR(inout->groupings[u].data, child) {
+ child->parent = (struct lysp_node*)&inout->groupings[u];
+ }
+ }
return ret;
}
@@ -2998,62 +2901,66 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_action(struct ly_parser_ctx *ctx, const char **data, struct lysp_action **actions)
+parse_action(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_action **actions)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
struct lysp_action *act;
+ struct lysp_node *child;
+ unsigned int u;
LY_ARRAY_NEW_RET(ctx->ctx, *actions, act, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, act->name, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ act->nodetype = LYS_ACTION;
+ act->parent = parent;
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &act->dsc, Y_STR_ARG, &act->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &act->dsc, Y_STR_ARG, &act->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &act->iffeatures, Y_STR_ARG, &act->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &act->iffeatures, Y_STR_ARG, &act->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &act->ref, Y_STR_ARG, &act->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &act->ref, Y_STR_ARG, &act->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &act->flags, &act->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &act->flags, &act->exts));
break;
case YANG_INPUT:
- ret = parse_inout(ctx, data, kw, &act->input);
+ LY_CHECK_RET(parse_inout(ctx, data, kw, (struct lysp_node*)act, &act->input));
break;
case YANG_OUTPUT:
- ret = parse_inout(ctx, data, kw, &act->output);
+ LY_CHECK_RET(parse_inout(ctx, data, kw, (struct lysp_node*)act, &act->output));
break;
case YANG_TYPEDEF:
- ret = parse_typedef(ctx, data, &act->typedefs);
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)act, data, &act->typedefs));
break;
case YANG_GROUPING:
- ret = parse_grouping(ctx, data, &act->groupings);
+ LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)act, &act->groupings));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &act->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &act->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "action");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
+ /* finalize parent pointers to the reallocated items */
+ LY_ARRAY_FOR(act->groupings, u) {
+ LY_LIST_FOR(act->groupings[u].data, child) {
+ child->parent = (struct lysp_node*)&act->groupings[u];
+ }
+ }
return ret;
}
@@ -3067,81 +2974,88 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_notif(struct ly_parser_ctx *ctx, const char **data, struct lysp_notif **notifs)
+parse_notif(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_notif **notifs)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
struct lysp_notif *notif;
+ struct lysp_node *child;
+ unsigned int u;
LY_ARRAY_NEW_RET(ctx->ctx, *notifs, notif, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, notif->name, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ notif->nodetype = LYS_NOTIF;
+ notif->parent = parent;
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, ¬if->dsc, Y_STR_ARG, ¬if->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, ¬if->dsc, Y_STR_ARG, ¬if->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, ¬if->iffeatures, Y_STR_ARG, ¬if->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, ¬if->iffeatures, Y_STR_ARG, ¬if->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, ¬if->ref, Y_STR_ARG, ¬if->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, ¬if->ref, Y_STR_ARG, ¬if->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, ¬if->flags, ¬if->exts);
+ LY_CHECK_RET(parse_status(ctx, data, ¬if->flags, ¬if->exts));
break;
case YANG_ANYDATA:
+ YANG_CHECK_STMTVER_RET(ctx, "anydata", "notification");
+ /* fall through */
case YANG_ANYXML:
- ret = parse_any(ctx, data, kw, ¬if->data);
+ LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)notif, ¬if->data));
break;
case YANG_CHOICE:
- ret = parse_case(ctx, data, ¬if->data);
+ LY_CHECK_RET(parse_case(ctx, data, (struct lysp_node*)notif, ¬if->data));
break;
case YANG_CONTAINER:
- ret = parse_container(ctx, data, ¬if->data);
+ LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)notif, ¬if->data));
break;
case YANG_LEAF:
- ret = parse_leaf(ctx, data, ¬if->data);
+ LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)notif, ¬if->data));
break;
case YANG_LEAF_LIST:
- ret = parse_leaflist(ctx, data, ¬if->data);
+ LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)notif, ¬if->data));
break;
case YANG_LIST:
- ret = parse_list(ctx, data, ¬if->data);
+ LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)notif, ¬if->data));
break;
case YANG_USES:
- ret = parse_uses(ctx, data, ¬if->data);
+ LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)notif, ¬if->data));
break;
case YANG_MUST:
- ret = parse_restrs(ctx, data, kw, ¬if->musts);
+ YANG_CHECK_STMTVER_RET(ctx, "must", "notification");
+ LY_CHECK_RET(parse_restrs(ctx, data, kw, ¬if->musts));
break;
case YANG_TYPEDEF:
- ret = parse_typedef(ctx, data, ¬if->typedefs);
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)notif, data, ¬if->typedefs));
break;
case YANG_GROUPING:
- ret = parse_grouping(ctx, data, ¬if->groupings);
+ LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)notif, ¬if->groupings));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, ¬if->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, ¬if->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "notification");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
+ /* finalize parent pointers to the reallocated items */
+ LY_ARRAY_FOR(notif->groupings, u) {
+ LY_LIST_FOR(notif->groupings[u].data, child) {
+ child->parent = (struct lysp_node*)¬if->groupings[u];
+ }
+ }
return ret;
}
@@ -3155,82 +3069,89 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_grouping(struct ly_parser_ctx *ctx, const char **data, struct lysp_grp **groupings)
+parse_grouping(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_grp **groupings)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
struct lysp_grp *grp;
+ struct lysp_node *child;
+ unsigned int u;
LY_ARRAY_NEW_RET(ctx->ctx, *groupings, grp, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, grp->name, word, word_len);
+ grp->nodetype = LYS_GROUPING;
+ grp->parent = parent;
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &grp->dsc, Y_STR_ARG, &grp->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &grp->dsc, Y_STR_ARG, &grp->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &grp->ref, Y_STR_ARG, &grp->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &grp->ref, Y_STR_ARG, &grp->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &grp->flags, &grp->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &grp->flags, &grp->exts));
break;
case YANG_ANYDATA:
+ YANG_CHECK_STMTVER_RET(ctx, "anydata", "grouping");
+ /* fall through */
case YANG_ANYXML:
- ret = parse_any(ctx, data, kw, &grp->data);
+ LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)grp, &grp->data));
break;
case YANG_CHOICE:
- ret = parse_choice(ctx, data, &grp->data);
+ LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)grp, &grp->data));
break;
case YANG_CONTAINER:
- ret = parse_container(ctx, data, &grp->data);
+ LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)grp, &grp->data));
break;
case YANG_LEAF:
- ret = parse_leaf(ctx, data, &grp->data);
+ LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)grp, &grp->data));
break;
case YANG_LEAF_LIST:
- ret = parse_leaflist(ctx, data, &grp->data);
+ LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)grp, &grp->data));
break;
case YANG_LIST:
- ret = parse_list(ctx, data, &grp->data);
+ LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)grp, &grp->data));
break;
case YANG_USES:
- ret = parse_uses(ctx, data, &grp->data);
+ LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)grp, &grp->data));
break;
case YANG_TYPEDEF:
- ret = parse_typedef(ctx, data, &grp->typedefs);
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)grp, data, &grp->typedefs));
break;
case YANG_ACTION:
- ret = parse_action(ctx, data, &grp->actions);
+ YANG_CHECK_STMTVER_RET(ctx, "action", "grouping");
+ LY_CHECK_RET(parse_action(ctx, data, (struct lysp_node*)grp, &grp->actions));
break;
case YANG_GROUPING:
- ret = parse_grouping(ctx, data, &grp->groupings);
+ LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)grp, &grp->groupings));
break;
case YANG_NOTIFICATION:
- ret = parse_notif(ctx, data, &grp->notifs);
+ YANG_CHECK_STMTVER_RET(ctx, "notification", "grouping");
+ LY_CHECK_RET(parse_notif(ctx, data, (struct lysp_node*)grp, &grp->notifs));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &grp->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &grp->exts));
break;
default:
- LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "augment");
+ LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "grouping");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
+ /* finalize parent pointers to the reallocated items */
+ LY_ARRAY_FOR(grp->groupings, u) {
+ LY_LIST_FOR(grp->groupings[u].data, child) {
+ child->parent = (struct lysp_node*)&grp->groupings[u];
+ }
+ }
return ret;
}
@@ -3244,9 +3165,9 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_augment(struct ly_parser_ctx *ctx, const char **data, struct lysp_augment **augments)
+parse_augment(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_augment **augments)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -3255,73 +3176,73 @@
LY_ARRAY_NEW_RET(ctx->ctx, *augments, aug, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, aug->nodeid, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ aug->nodetype = LYS_AUGMENT;
+ aug->parent = parent;
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &aug->dsc, Y_STR_ARG, &aug->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &aug->dsc, Y_STR_ARG, &aug->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &aug->iffeatures, Y_STR_ARG, &aug->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &aug->iffeatures, Y_STR_ARG, &aug->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &aug->ref, Y_STR_ARG, &aug->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &aug->ref, Y_STR_ARG, &aug->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &aug->flags, &aug->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &aug->flags, &aug->exts));
break;
case YANG_WHEN:
- ret = parse_when(ctx, data, &aug->when);
+ LY_CHECK_RET(parse_when(ctx, data, &aug->when));
break;
case YANG_ANYDATA:
+ YANG_CHECK_STMTVER_RET(ctx, "anydata", "augment");
+ /* fall through */
case YANG_ANYXML:
- ret = parse_any(ctx, data, kw, &aug->child);
+ LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)aug, &aug->child));
break;
case YANG_CASE:
- ret = parse_case(ctx, data, &aug->child);
+ LY_CHECK_RET(parse_case(ctx, data, (struct lysp_node*)aug, &aug->child));
break;
case YANG_CHOICE:
- ret = parse_choice(ctx, data, &aug->child);
+ LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)aug, &aug->child));
break;
case YANG_CONTAINER:
- ret = parse_container(ctx, data, &aug->child);
+ LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)aug, &aug->child));
break;
case YANG_LEAF:
- ret = parse_leaf(ctx, data, &aug->child);
+ LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)aug, &aug->child));
break;
case YANG_LEAF_LIST:
- ret = parse_leaflist(ctx, data, &aug->child);
+ LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)aug, &aug->child));
break;
case YANG_LIST:
- ret = parse_list(ctx, data, &aug->child);
+ LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)aug, &aug->child));
break;
case YANG_USES:
- ret = parse_uses(ctx, data, &aug->child);
+ LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)aug, &aug->child));
break;
case YANG_ACTION:
- ret = parse_action(ctx, data, &aug->actions);
+ YANG_CHECK_STMTVER_RET(ctx, "action", "augment");
+ LY_CHECK_RET(parse_action(ctx, data, (struct lysp_node*)aug, &aug->actions));
break;
case YANG_NOTIFICATION:
- ret = parse_notif(ctx, data, &aug->notifs);
+ YANG_CHECK_STMTVER_RET(ctx, "notification", "augment");
+ LY_CHECK_RET(parse_notif(ctx, data, (struct lysp_node*)aug, &aug->notifs));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &aug->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &aug->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "augment");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -3335,9 +3256,9 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_uses(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_uses(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -3348,6 +3269,7 @@
uses = calloc(1, sizeof *uses);
LY_CHECK_ERR_RET(!uses, LOGMEM(ctx->ctx), LY_EMEM);
uses->nodetype = LYS_USES;
+ uses->parent = parent;
/* insert into siblings */
if (!*siblings) {
@@ -3358,49 +3280,42 @@
}
/* get name */
- ret = get_argument(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, uses->name, word, word_len);
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &uses->dsc, Y_STR_ARG, &uses->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &uses->dsc, Y_STR_ARG, &uses->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &uses->iffeatures, Y_STR_ARG, &uses->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &uses->iffeatures, Y_STR_ARG, &uses->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &uses->ref, Y_STR_ARG, &uses->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &uses->ref, Y_STR_ARG, &uses->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &uses->flags, &uses->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &uses->flags, &uses->exts));
break;
case YANG_WHEN:
- ret = parse_when(ctx, data, &uses->when);
+ LY_CHECK_RET(parse_when(ctx, data, &uses->when));
break;
case YANG_REFINE:
- ret = parse_refine(ctx, data, &uses->refines);
+ LY_CHECK_RET(parse_refine(ctx, data, &uses->refines));
break;
case YANG_AUGMENT:
- ret = parse_augment(ctx, data, &uses->augments);
+ LY_CHECK_RET(parse_augment(ctx, data, (struct lysp_node*)uses, &uses->augments));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &uses->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &uses->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "uses");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -3414,9 +3329,9 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_case(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_case(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -3427,6 +3342,7 @@
cas = calloc(1, sizeof *cas);
LY_CHECK_ERR_RET(!cas, LOGMEM(ctx->ctx), LY_EMEM);
cas->nodetype = LYS_CASE;
+ cas->parent = parent;
/* insert into siblings */
if (!*siblings) {
@@ -3437,65 +3353,60 @@
}
/* get name */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, cas->name, word, word_len);
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &cas->dsc, Y_STR_ARG, &cas->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &cas->dsc, Y_STR_ARG, &cas->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &cas->iffeatures, Y_STR_ARG, &cas->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &cas->iffeatures, Y_STR_ARG, &cas->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &cas->ref, Y_STR_ARG, &cas->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &cas->ref, Y_STR_ARG, &cas->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &cas->flags, &cas->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &cas->flags, &cas->exts));
break;
case YANG_WHEN:
- ret = parse_when(ctx, data, &cas->when);
+ LY_CHECK_RET(parse_when(ctx, data, &cas->when));
break;
case YANG_ANYDATA:
+ YANG_CHECK_STMTVER_RET(ctx, "anydata", "case");
+ /* fall through */
case YANG_ANYXML:
- ret = parse_any(ctx, data, kw, &cas->child);
+ LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)cas, &cas->child));
break;
case YANG_CHOICE:
- ret = parse_case(ctx, data, &cas->child);
+ LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)cas, &cas->child));
break;
case YANG_CONTAINER:
- ret = parse_container(ctx, data, &cas->child);
+ LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)cas, &cas->child));
break;
case YANG_LEAF:
- ret = parse_leaf(ctx, data, &cas->child);
+ LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)cas, &cas->child));
break;
case YANG_LEAF_LIST:
- ret = parse_leaflist(ctx, data, &cas->child);
+ LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)cas, &cas->child));
break;
case YANG_LIST:
- ret = parse_list(ctx, data, &cas->child);
+ LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)cas, &cas->child));
break;
case YANG_USES:
- ret = parse_uses(ctx, data, &cas->child);
+ LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)cas, &cas->child));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &cas->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &cas->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "case");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -3509,9 +3420,9 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_choice(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_choice(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -3522,6 +3433,7 @@
choice = calloc(1, sizeof *choice);
LY_CHECK_ERR_RET(!choice, LOGMEM(ctx->ctx), LY_EMEM);
choice->nodetype = LYS_CHOICE;
+ choice->parent = parent;
/* insert into siblings */
if (!*siblings) {
@@ -3532,74 +3444,76 @@
}
/* get name */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, choice->name, word, word_len);
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
switch (kw) {
case YANG_CONFIG:
- ret = parse_config(ctx, data, &choice->flags, &choice->exts);
+ LY_CHECK_RET(parse_config(ctx, data, &choice->flags, &choice->exts));
break;
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &choice->dsc, Y_STR_ARG, &choice->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &choice->dsc, Y_STR_ARG, &choice->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &choice->iffeatures, Y_STR_ARG, &choice->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &choice->iffeatures, Y_STR_ARG, &choice->exts));
break;
case YANG_MANDATORY:
- ret = parse_mandatory(ctx, data, &choice->flags, &choice->exts);
+ LY_CHECK_RET(parse_mandatory(ctx, data, &choice->flags, &choice->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &choice->ref, Y_STR_ARG, &choice->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &choice->ref, Y_STR_ARG, &choice->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &choice->flags, &choice->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &choice->flags, &choice->exts));
break;
case YANG_WHEN:
- ret = parse_when(ctx, data, &choice->when);
+ LY_CHECK_RET(parse_when(ctx, data, &choice->when));
break;
case YANG_DEFAULT:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &choice->dflt, Y_IDENTIF_ARG, &choice->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &choice->dflt, Y_PREF_IDENTIF_ARG, &choice->exts));
break;
case YANG_ANYDATA:
+ YANG_CHECK_STMTVER_RET(ctx, "anydata", "choice");
+ /* fall through */
case YANG_ANYXML:
- ret = parse_any(ctx, data, kw, &choice->child);
+ LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)choice, &choice->child));
break;
case YANG_CASE:
- ret = parse_case(ctx, data, &choice->child);
+ LY_CHECK_RET(parse_case(ctx, data, (struct lysp_node*)choice, &choice->child));
break;
case YANG_CHOICE:
- ret = parse_choice(ctx, data, &choice->child);
+ YANG_CHECK_STMTVER_RET(ctx, "choice", "choice");
+ LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)choice, &choice->child));
break;
case YANG_CONTAINER:
- ret = parse_container(ctx, data, &choice->child);
+ LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)choice, &choice->child));
break;
case YANG_LEAF:
- ret = parse_leaf(ctx, data, &choice->child);
+ LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)choice, &choice->child));
break;
case YANG_LEAF_LIST:
- ret = parse_leaflist(ctx, data, &choice->child);
+ LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)choice, &choice->child));
break;
case YANG_LIST:
- ret = parse_list(ctx, data, &choice->child);
+ LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)choice, &choice->child));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &choice->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &choice->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "choice");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
LY_CHECK_RET(ret);
-
+checks:
+ if ((choice->flags & LYS_MAND_TRUE) && choice->dflt) {
+ LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMSCOMB, "mandatory", "default", "choice");
+ return LY_EVALID;
+ }
return ret;
}
@@ -3613,7 +3527,7 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_container(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_container(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
{
LY_ERR ret = 0;
char *buf, *word;
@@ -3621,11 +3535,13 @@
enum yang_keyword kw;
struct lysp_node *iter;
struct lysp_node_container *cont;
+ unsigned int u;
/* create structure */
cont = calloc(1, sizeof *cont);
LY_CHECK_ERR_RET(!cont, LOGMEM(ctx->ctx), LY_EMEM);
cont->nodetype = LYS_CONTAINER;
+ cont->parent = parent;
/* insert into siblings */
if (!*siblings) {
@@ -3636,87 +3552,90 @@
}
/* get name */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, cont->name, word, word_len);
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CONFIG:
- ret = parse_config(ctx, data, &cont->flags, &cont->exts);
+ LY_CHECK_RET(parse_config(ctx, data, &cont->flags, &cont->exts));
break;
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &cont->dsc, Y_STR_ARG, &cont->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &cont->dsc, Y_STR_ARG, &cont->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &cont->iffeatures, Y_STR_ARG, &cont->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &cont->iffeatures, Y_STR_ARG, &cont->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &cont->ref, Y_STR_ARG, &cont->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &cont->ref, Y_STR_ARG, &cont->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &cont->flags, &cont->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &cont->flags, &cont->exts));
break;
case YANG_WHEN:
- ret = parse_when(ctx, data, &cont->when);
+ LY_CHECK_RET(parse_when(ctx, data, &cont->when));
break;
case YANG_PRESENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PRESENCE, 0, &cont->presence, Y_STR_ARG, &cont->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PRESENCE, 0, &cont->presence, Y_STR_ARG, &cont->exts));
break;
case YANG_ANYDATA:
+ YANG_CHECK_STMTVER_RET(ctx, "anydata", "container");
+ /* fall through */
case YANG_ANYXML:
- ret = parse_any(ctx, data, kw, &cont->child);
+ LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)cont, &cont->child));
break;
case YANG_CHOICE:
- ret = parse_choice(ctx, data, &cont->child);
+ LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)cont, &cont->child));
break;
case YANG_CONTAINER:
- ret = parse_container(ctx, data, &cont->child);
+ LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)cont, &cont->child));
break;
case YANG_LEAF:
- ret = parse_leaf(ctx, data, &cont->child);
+ LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)cont, &cont->child));
break;
case YANG_LEAF_LIST:
- ret = parse_leaflist(ctx, data, &cont->child);
+ LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)cont, &cont->child));
break;
case YANG_LIST:
- ret = parse_list(ctx, data, &cont->child);
+ LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)cont, &cont->child));
break;
case YANG_USES:
- ret = parse_uses(ctx, data, &cont->child);
+ LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)cont, &cont->child));
break;
case YANG_TYPEDEF:
- ret = parse_typedef(ctx, data, &cont->typedefs);
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)cont, data, &cont->typedefs));
break;
case YANG_MUST:
- ret = parse_restrs(ctx, data, kw, &cont->musts);
+ LY_CHECK_RET(parse_restrs(ctx, data, kw, &cont->musts));
break;
case YANG_ACTION:
- ret = parse_action(ctx, data, &cont->actions);
+ YANG_CHECK_STMTVER_RET(ctx, "action", "container");
+ LY_CHECK_RET(parse_action(ctx, data, (struct lysp_node*)cont, &cont->actions));
break;
case YANG_GROUPING:
- ret = parse_grouping(ctx, data, &cont->groupings);
+ LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)cont, &cont->groupings));
break;
case YANG_NOTIFICATION:
- ret = parse_notif(ctx, data, &cont->notifs);
+ YANG_CHECK_STMTVER_RET(ctx, "notification", "container");
+ LY_CHECK_RET(parse_notif(ctx, data, (struct lysp_node*)cont, &cont->notifs));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &cont->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &cont->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "container");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
+ /* finalize parent pointers to the reallocated items */
+ LY_ARRAY_FOR(cont->groupings, u) {
+ LY_LIST_FOR(cont->groupings[u].data, iter) {
+ iter->parent = (struct lysp_node*)&cont->groupings[u];
+ }
+ }
return ret;
}
@@ -3730,19 +3649,21 @@
* @return LY_ERR values.
*/
static LY_ERR
-parse_list(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_list(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
struct lysp_node *iter;
struct lysp_node_list *list;
+ unsigned int u;
/* create structure */
list = calloc(1, sizeof *list);
LY_CHECK_ERR_RET(!list, LOGMEM(ctx->ctx), LY_EMEM);
list->nodetype = LYS_LIST;
+ list->parent = parent;
/* insert into siblings */
if (!*siblings) {
@@ -3753,98 +3674,110 @@
}
/* get name */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, list->name, word, word_len);
/* parse substatements */
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
switch (kw) {
case YANG_CONFIG:
- ret = parse_config(ctx, data, &list->flags, &list->exts);
+ LY_CHECK_RET(parse_config(ctx, data, &list->flags, &list->exts));
break;
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &list->dsc, Y_STR_ARG, &list->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &list->dsc, Y_STR_ARG, &list->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &list->iffeatures, Y_STR_ARG, &list->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &list->iffeatures, Y_STR_ARG, &list->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &list->ref, Y_STR_ARG, &list->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &list->ref, Y_STR_ARG, &list->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &list->flags, &list->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &list->flags, &list->exts));
break;
case YANG_WHEN:
- ret = parse_when(ctx, data, &list->when);
+ LY_CHECK_RET(parse_when(ctx, data, &list->when));
break;
case YANG_KEY:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_KEY, 0, &list->key, Y_STR_ARG, &list->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_KEY, 0, &list->key, Y_STR_ARG, &list->exts));
break;
case YANG_MAX_ELEMENTS:
- ret = parse_maxelements(ctx, data, &list->max, &list->flags, &list->exts);
+ LY_CHECK_RET(parse_maxelements(ctx, data, &list->max, &list->flags, &list->exts));
break;
case YANG_MIN_ELEMENTS:
- ret = parse_minelements(ctx, data, &list->min, &list->flags, &list->exts);
+ LY_CHECK_RET(parse_minelements(ctx, data, &list->min, &list->flags, &list->exts));
break;
case YANG_ORDERED_BY:
- ret = parse_orderedby(ctx, data, &list->flags, &list->exts);
+ LY_CHECK_RET(parse_orderedby(ctx, data, &list->flags, &list->exts));
break;
case YANG_UNIQUE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, &list->uniques, Y_STR_ARG, &list->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, &list->uniques, Y_STR_ARG, &list->exts));
break;
case YANG_ANYDATA:
+ YANG_CHECK_STMTVER_RET(ctx, "anydata", "list");
+ /* fall through */
case YANG_ANYXML:
- ret = parse_any(ctx, data, kw, &list->child);
+ LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)list, &list->child));
break;
case YANG_CHOICE:
- ret = parse_choice(ctx, data, &list->child);
+ LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)list, &list->child));
break;
case YANG_CONTAINER:
- ret = parse_container(ctx, data, &list->child);
+ LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)list, &list->child));
break;
case YANG_LEAF:
- ret = parse_leaf(ctx, data, &list->child);
+ LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)list, &list->child));
break;
case YANG_LEAF_LIST:
- ret = parse_leaflist(ctx, data, &list->child);
+ LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)list, &list->child));
break;
case YANG_LIST:
- ret = parse_list(ctx, data, &list->child);
+ LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)list, &list->child));
break;
case YANG_USES:
- ret = parse_uses(ctx, data, &list->child);
+ LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)list, &list->child));
break;
case YANG_TYPEDEF:
- ret = parse_typedef(ctx, data, &list->typedefs);
+ LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)list, data, &list->typedefs));
break;
case YANG_MUST:
- ret = parse_restrs(ctx, data, kw, &list->musts);
+ LY_CHECK_RET(parse_restrs(ctx, data, kw, &list->musts));
break;
case YANG_ACTION:
- ret = parse_action(ctx, data, &list->actions);
+ YANG_CHECK_STMTVER_RET(ctx, "action", "list");
+ LY_CHECK_RET(parse_action(ctx, data, (struct lysp_node*)list, &list->actions));
break;
case YANG_GROUPING:
- ret = parse_grouping(ctx, data, &list->groupings);
+ LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)list, &list->groupings));
break;
case YANG_NOTIFICATION:
- ret = parse_notif(ctx, data, &list->notifs);
+ YANG_CHECK_STMTVER_RET(ctx, "notification", "list");
+ LY_CHECK_RET(parse_notif(ctx, data, (struct lysp_node*)list, &list->notifs));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &list->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &list->exts));
break;
default:
- LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "container");
+ LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "list");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
LY_CHECK_RET(ret);
+ /* finalize parent pointers to the reallocated items */
+ LY_ARRAY_FOR(list->groupings, u) {
+ LY_LIST_FOR(list->groupings[u].data, iter) {
+ iter->parent = (struct lysp_node*)&list->groupings[u];
+ }
+ }
+checks:
+ if (list->max && list->min > list->max) {
+ LOGVAL_YANG(ctx, LYVE_SEMANTICS,
+ "Invalid combination of min-elements and max-elements: min value %u is bigger than the max value %u.",
+ list->min, list->max);
+ return LY_EVALID;
+ }
return ret;
}
@@ -3862,7 +3795,7 @@
static LY_ERR
parse_yinelement(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -3873,8 +3806,7 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if ((word_len == 4) && !strncmp(word, "true", word_len)) {
*flags |= LYS_YINELEM_TRUE;
@@ -3887,21 +3819,16 @@
}
free(buf);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_YINELEM, 0, exts);
- LY_CHECK_RET(ret);
- break;
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_YINELEM, 0, exts));
+ LY_CHECK_RET(ret); break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "yin-element");
return LY_EVALID;
}
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -3919,7 +3846,7 @@
static LY_ERR
parse_argument(struct ly_parser_ctx *ctx, const char **data, const char **argument, uint16_t *flags, struct lysp_ext_instance **exts)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -3930,28 +3857,22 @@
}
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, *argument, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_YIN_ELEMENT:
- ret = parse_yinelement(ctx, data, flags, exts);
+ LY_CHECK_RET(parse_yinelement(ctx, data, flags, exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_ARGUMENT, 0, exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_ARGUMENT, 0, exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "argument");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -3967,7 +3888,7 @@
static LY_ERR
parse_extension(struct ly_parser_ctx *ctx, const char **data, struct lysp_ext **extensions)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -3976,37 +3897,31 @@
LY_ARRAY_NEW_RET(ctx->ctx, *extensions, ex, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, ex->name, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &ex->dsc, Y_STR_ARG, &ex->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &ex->dsc, Y_STR_ARG, &ex->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &ex->ref, Y_STR_ARG, &ex->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &ex->ref, Y_STR_ARG, &ex->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &ex->flags, &ex->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &ex->flags, &ex->exts));
break;
case YANG_ARGUMENT:
- ret = parse_argument(ctx, data, &ex->argument, &ex->flags, &ex->exts);
+ LY_CHECK_RET(parse_argument(ctx, data, &ex->argument, &ex->flags, &ex->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &ex->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &ex->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "extension");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -4022,7 +3937,7 @@
static LY_ERR
parse_deviate(struct ly_parser_ctx *ctx, const char **data, struct lysp_deviate **deviates)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len, dev_mod;
enum yang_keyword kw;
@@ -4036,8 +3951,7 @@
uint32_t *d_min, *d_max;
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
if ((word_len == 13) && !strncmp(word, "not-supported", word_len)) {
dev_mod = LYS_DEV_NOT_SUPPORTED;
@@ -4105,9 +4019,7 @@
iter->next = d;
}
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
-
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_CONFIG:
switch (dev_mod) {
@@ -4116,7 +4028,7 @@
LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
return LY_EVALID;
default:
- ret = parse_config(ctx, data, d_flags, &d->exts);
+ LY_CHECK_RET(parse_config(ctx, data, d_flags, &d->exts));
break;
}
break;
@@ -4126,10 +4038,10 @@
LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
return LY_EVALID;
case LYS_DEV_REPLACE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &d_rpl->dflt, Y_STR_ARG, &d->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &d_rpl->dflt, Y_STR_ARG, &d->exts));
break;
default:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, d_dflts, Y_STR_ARG, &d->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, d_dflts, Y_STR_ARG, &d->exts));
break;
}
break;
@@ -4140,7 +4052,7 @@
LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
return LY_EVALID;
default:
- ret = parse_mandatory(ctx, data, d_flags, &d->exts);
+ LY_CHECK_RET(parse_mandatory(ctx, data, d_flags, &d->exts));
break;
}
break;
@@ -4151,7 +4063,7 @@
LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
return LY_EVALID;
default:
- ret = parse_maxelements(ctx, data, d_max, d_flags, &d->exts);
+ LY_CHECK_RET(parse_maxelements(ctx, data, d_max, d_flags, &d->exts));
break;
}
break;
@@ -4162,7 +4074,7 @@
LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
return LY_EVALID;
default:
- ret = parse_minelements(ctx, data, d_min, d_flags, &d->exts);
+ LY_CHECK_RET(parse_minelements(ctx, data, d_min, d_flags, &d->exts));
break;
}
break;
@@ -4173,7 +4085,7 @@
LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
return LY_EVALID;
default:
- ret = parse_restrs(ctx, data, kw, d_musts);
+ LY_CHECK_RET(parse_restrs(ctx, data, kw, d_musts));
break;
}
break;
@@ -4191,7 +4103,7 @@
}
d_rpl->type = calloc(1, sizeof *d_rpl->type);
LY_CHECK_ERR_RET(!d_rpl->type, LOGMEM(ctx->ctx), LY_EMEM);
- ret = parse_type(ctx, data, d_rpl->type);
+ LY_CHECK_RET(parse_type(ctx, data, d_rpl->type));
break;
}
break;
@@ -4202,7 +4114,7 @@
LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
return LY_EVALID;
default:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, d_uniques, Y_STR_ARG, &d->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, d_uniques, Y_STR_ARG, &d->exts));
break;
}
break;
@@ -4212,21 +4124,18 @@
LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
return LY_EVALID;
default:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, d_units, Y_STR_ARG, &d->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, d_units, Y_STR_ARG, &d->exts));
break;
}
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &d->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &d->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "deviate");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -4242,7 +4151,7 @@
static LY_ERR
parse_deviation(struct ly_parser_ctx *ctx, const char **data, struct lysp_deviation **deviations)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -4251,34 +4160,30 @@
LY_ARRAY_NEW_RET(ctx->ctx, *deviations, dev, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, dev->nodeid, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &dev->dsc, Y_STR_ARG, &dev->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &dev->dsc, Y_STR_ARG, &dev->exts));
break;
case YANG_DEVIATE:
- ret = parse_deviate(ctx, data, &dev->deviates);
+ LY_CHECK_RET(parse_deviate(ctx, data, &dev->deviates));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &dev->ref, Y_STR_ARG, &dev->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &dev->ref, Y_STR_ARG, &dev->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &dev->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &dev->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "deviation");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
LY_CHECK_RET(ret);
-
+checks:
/* mandatory substatements */
if (!dev->deviates) {
LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "deviate", "deviation");
@@ -4300,7 +4205,7 @@
static LY_ERR
parse_feature(struct ly_parser_ctx *ctx, const char **data, struct lysp_feature **features)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -4309,37 +4214,31 @@
LY_ARRAY_NEW_RET(ctx->ctx, *features, feat, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, feat->name, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &feat->dsc, Y_STR_ARG, &feat->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &feat->dsc, Y_STR_ARG, &feat->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &feat->iffeatures, Y_STR_ARG, &feat->exts);
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &feat->iffeatures, Y_STR_ARG, &feat->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &feat->ref, Y_STR_ARG, &feat->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &feat->ref, Y_STR_ARG, &feat->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &feat->flags, &feat->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &feat->flags, &feat->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &feat->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &feat->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "feature");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
- LY_CHECK_RET(ret);
-
return ret;
}
@@ -4355,7 +4254,7 @@
static LY_ERR
parse_identity(struct ly_parser_ctx *ctx, const char **data, struct lysp_ident **identities)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *buf, *word;
size_t word_len;
enum yang_keyword kw;
@@ -4364,39 +4263,39 @@
LY_ARRAY_NEW_RET(ctx->ctx, *identities, ident, LY_EMEM);
/* get value */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, ident->name, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
switch (kw) {
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &ident->dsc, Y_STR_ARG, &ident->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &ident->dsc, Y_STR_ARG, &ident->exts));
break;
case YANG_IF_FEATURE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &ident->iffeatures, Y_STR_ARG, &ident->exts);
+ YANG_CHECK_STMTVER_RET(ctx, "if-feature", "identity");
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &ident->iffeatures, Y_STR_ARG, &ident->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &ident->ref, Y_STR_ARG, &ident->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &ident->ref, Y_STR_ARG, &ident->exts));
break;
case YANG_STATUS:
- ret = parse_status(ctx, data, &ident->flags, &ident->exts);
+ LY_CHECK_RET(parse_status(ctx, data, &ident->flags, &ident->exts));
break;
case YANG_BASE:
- ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_BASE, &ident->bases, Y_PREF_IDENTIF_ARG, &ident->exts);
+ if (ident->bases && ctx->mod->version < 2) {
+ LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Identity can be derived from multiple base identities only in YANG 1.1 modules");
+ return LY_EVALID;
+ }
+ LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_BASE, &ident->bases, Y_PREF_IDENTIF_ARG, &ident->exts));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &ident->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &ident->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "identity");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
-
return ret;
}
@@ -4417,14 +4316,15 @@
size_t word_len;
enum yang_keyword kw, prev_kw = 0;
enum yang_module_stmt mod_stmt = Y_MOD_MODULE_HEADER;
+ struct lysp_module *dup;
+ struct lysp_node *child;
+ unsigned int u;
/* (sub)module name */
- ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
- LY_CHECK_RET(ret);
-
+ LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
INSERT_WORD(ctx, buf, mod->name, word, word_len);
- YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
- LY_CHECK_RET(ret);
+
+ YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
#define CHECK_ORDER(SECTION) \
if (mod_stmt > SECTION) {LOGVAL_YANG(ctx, LY_VCODE_INORD, ly_stmt2str(kw), ly_stmt2str(prev_kw)); return LY_EVALID;}mod_stmt = SECTION
@@ -4497,109 +4397,119 @@
switch (kw) {
/* module header */
case YANG_YANG_VERSION:
- ret = parse_yangversion(ctx, data, mod);
+ LY_CHECK_RET(parse_yangversion(ctx, data, mod));
break;
case YANG_NAMESPACE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_NAMESPACE, 0, &mod->ns, Y_STR_ARG, &mod->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_NAMESPACE, 0, &mod->ns, Y_STR_ARG, &mod->exts));
break;
case YANG_PREFIX:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &mod->prefix, Y_IDENTIF_ARG, &mod->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &mod->prefix, Y_IDENTIF_ARG, &mod->exts));
LY_CHECK_RET(lysp_check_prefix(ctx, mod, &mod->prefix), LY_EVALID);
break;
case YANG_BELONGS_TO:
- ret = parse_belongsto(ctx, data, &mod->belongsto, &mod->prefix, &mod->exts);
+ LY_CHECK_RET(parse_belongsto(ctx, data, &mod->belongsto, &mod->prefix, &mod->exts));
break;
/* linkage */
case YANG_INCLUDE:
- ret = parse_include(ctx, data, &mod->includes);
+ LY_CHECK_RET(parse_include(ctx, data, mod));
break;
case YANG_IMPORT:
- ret = parse_import(ctx, data, mod);
+ LY_CHECK_RET(parse_import(ctx, data, mod));
break;
/* meta */
case YANG_ORGANIZATION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ORGANIZATION, 0, &mod->org, Y_STR_ARG, &mod->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_ORGANIZATION, 0, &mod->org, Y_STR_ARG, &mod->exts));
break;
case YANG_CONTACT:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_CONTACT, 0, &mod->contact, Y_STR_ARG, &mod->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_CONTACT, 0, &mod->contact, Y_STR_ARG, &mod->exts));
break;
case YANG_DESCRIPTION:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &mod->dsc, Y_STR_ARG, &mod->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &mod->dsc, Y_STR_ARG, &mod->exts));
break;
case YANG_REFERENCE:
- ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &mod->ref, Y_STR_ARG, &mod->exts);
+ LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &mod->ref, Y_STR_ARG, &mod->exts));
break;
/* revision */
case YANG_REVISION:
- ret = parse_revision(ctx, data, &mod->revs);
+ LY_CHECK_RET(parse_revision(ctx, data, &mod->revs));
break;
/* body */
case YANG_ANYDATA:
+ YANG_CHECK_STMTVER_RET(ctx, "anydata", mod->submodule ? "submodule" : "module");
+ /* fall through */
case YANG_ANYXML:
- ret = parse_any(ctx, data, kw, &mod->data);
+ LY_CHECK_RET(parse_any(ctx, data, kw, NULL, &mod->data));
break;
case YANG_CHOICE:
- ret = parse_choice(ctx, data, &mod->data);
+ LY_CHECK_RET(parse_choice(ctx, data, NULL, &mod->data));
break;
case YANG_CONTAINER:
- ret = parse_container(ctx, data, &mod->data);
+ LY_CHECK_RET(parse_container(ctx, data, NULL, &mod->data));
break;
case YANG_LEAF:
- ret = parse_leaf(ctx, data, &mod->data);
+ LY_CHECK_RET(parse_leaf(ctx, data, NULL, &mod->data));
break;
case YANG_LEAF_LIST:
- ret = parse_leaflist(ctx, data, &mod->data);
+ LY_CHECK_RET(parse_leaflist(ctx, data, NULL, &mod->data));
break;
case YANG_LIST:
- ret = parse_list(ctx, data, &mod->data);
+ LY_CHECK_RET(parse_list(ctx, data, NULL, &mod->data));
break;
case YANG_USES:
- ret = parse_uses(ctx, data, &mod->data);
+ LY_CHECK_RET(parse_uses(ctx, data, NULL, &mod->data));
break;
case YANG_AUGMENT:
- ret = parse_augment(ctx, data, &mod->augments);
+ LY_CHECK_RET(parse_augment(ctx, data, NULL, &mod->augments));
break;
case YANG_DEVIATION:
- ret = parse_deviation(ctx, data, &mod->deviations);
+ LY_CHECK_RET(parse_deviation(ctx, data, &mod->deviations));
break;
case YANG_EXTENSION:
- ret = parse_extension(ctx, data, &mod->extensions);
+ LY_CHECK_RET(parse_extension(ctx, data, &mod->extensions));
break;
case YANG_FEATURE:
- ret = parse_feature(ctx, data, &mod->features);
+ LY_CHECK_RET(parse_feature(ctx, data, &mod->features));
break;
case YANG_GROUPING:
- ret = parse_grouping(ctx, data, &mod->groupings);
+ LY_CHECK_RET(parse_grouping(ctx, data, NULL, &mod->groupings));
break;
case YANG_IDENTITY:
- ret = parse_identity(ctx, data, &mod->identities);
+ LY_CHECK_RET(parse_identity(ctx, data, &mod->identities));
break;
case YANG_NOTIFICATION:
- ret = parse_notif(ctx, data, &mod->notifs);
+ LY_CHECK_RET(parse_notif(ctx, data, NULL, &mod->notifs));
break;
case YANG_RPC:
- ret = parse_action(ctx, data, &mod->rpcs);
+ LY_CHECK_RET(parse_action(ctx, data, NULL, &mod->rpcs));
break;
case YANG_TYPEDEF:
- ret = parse_typedef(ctx, data, &mod->typedefs);
+ LY_CHECK_RET(parse_typedef(ctx, NULL, data, &mod->typedefs));
break;
case YANG_CUSTOM:
- ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &mod->exts);
+ LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &mod->exts));
break;
default:
LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), mod->submodule ? "submodule" : "module");
return LY_EVALID;
}
- LY_CHECK_RET(ret);
}
LY_CHECK_RET(ret);
+ /* finalize parent pointers to the reallocated items */
+ LY_ARRAY_FOR(mod->groupings, u) {
+ LY_LIST_FOR(mod->groupings[u].data, child) {
+ child->parent = (struct lysp_node*)&mod->groupings[u];
+ }
+ }
+ /* TODO the same finalization for rpcs and notifications, do also in the relevant nodes */
+
+checks:
/* mandatory substatements */
if (mod->submodule) {
if (!mod->belongsto) {
@@ -4616,62 +4526,69 @@
}
}
+ /* submodules share the namespace with the module names, so there must not be
+ * a submodule of the same name in the context, no need for revision matching */
+ dup = ly_ctx_get_submodule(ctx->ctx, NULL, mod->name, NULL);
+ if (dup && (!mod->submodule || strcmp(dup->belongsto, mod->belongsto))) {
+ LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Name collision between %s of name \"%s\".",
+ mod->submodule ? "submodules" : "module and submodule", mod->name);
+ return LY_EVALID;
+ }
+
return ret;
}
LY_ERR
-yang_parse(struct ly_ctx *ctx, const char *data, struct lysp_module **mod_p)
+yang_parse(struct ly_parser_ctx *context, const char *data, struct lysp_module **mod_p)
{
- LY_ERR ret = 0;
+ LY_ERR ret = LY_SUCCESS;
char *word, *buf;
size_t word_len;
enum yang_keyword kw;
struct lysp_module *mod = NULL;
- struct ly_parser_ctx context = {0};
-
- context.ctx = ctx;
- context.line = 1;
/* "module"/"submodule" */
- ret = get_keyword(&context, &data, &kw, &word, &word_len);
- LY_CHECK_GOTO(ret, error);
+ ret = get_keyword(context, &data, &kw, &word, &word_len);
+ LY_CHECK_GOTO(ret, cleanup);
if ((kw != YANG_MODULE) && (kw != YANG_SUBMODULE)) {
- LOGVAL_YANG(&context, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
+ LOGVAL_YANG(context, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
ly_stmt2str(kw));
- goto error;
+ goto cleanup;
}
mod = calloc(1, sizeof *mod);
- LY_CHECK_ERR_GOTO(!mod, LOGMEM(ctx), error);
+ LY_CHECK_ERR_GOTO(!mod, LOGMEM(context->ctx), cleanup);
if (kw == YANG_SUBMODULE) {
mod->submodule = 1;
}
- mod->ctx = ctx;
+ mod->parsing = 1;
+ mod->ctx = context->ctx;
+ context->mod = mod;
/* substatements */
- ret = parse_sub_module(&context, &data, mod);
- LY_CHECK_GOTO(ret, error);
+ ret = parse_sub_module(context, &data, mod);
+ LY_CHECK_GOTO(ret, cleanup);
/* read some trailing spaces or new lines */
- ret = get_argument(&context, &data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
- LY_CHECK_GOTO(ret, error);
+ ret = get_argument(context, &data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
+ LY_CHECK_GOTO(ret, cleanup);
if (word) {
- LOGVAL_YANG(&context, LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected end-of-file.",
+ LOGVAL_YANG(context, LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected end-of-file.",
word_len, word);
free(buf);
- goto error;
+ goto cleanup;
}
assert(!buf);
- /* make sure that the newest revision is at position 0 */
- lysp_sort_revisions(mod->revs);
-
+ mod->parsing = 0;
*mod_p = mod;
- return ret;
-error:
- lysp_module_free(mod);
+cleanup:
+ if (ret) {
+ lysp_module_free(mod);
+ }
+
return ret;
}
diff --git a/src/parser_yin.c b/src/parser_yin.c
index 3501585..e8b0333 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -257,7 +257,6 @@
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);
- printf("%s\n\n\n", name);
LY_CHECK_ERR_RET(ret != LY_SUCCESS, LOGMEM(xml_ctx->ctx), LY_EMEM);
kw = match_keyword(name);
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 3960bb3..dd085de 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -11,542 +11,165 @@
*
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _DEFAULT_SOURCE
-#include <ctype.h>
+#include "common.h"
+
+#include <dirent.h>
#include <errno.h>
+#include <limits.h>
#include <fcntl.h>
-#include <linux/limits.h>
#include <stdio.h>
-#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "libyang.h"
-#include "common.h"
#include "context.h"
#include "tree_schema_internal.h"
+#include "xpath.h"
-#define FREE_ARRAY(CTX, ARRAY, FUNC) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FUNC(CTX, &ARRAY[c__], dict);}LY_ARRAY_FREE(ARRAY);}
-#define FREE_MEMBER(CTX, MEMBER, FUNC) if (MEMBER) {FUNC(CTX, MEMBER, dict);free(MEMBER);}
-#define FREE_STRING(CTX, STRING, DICT) if (DICT && STRING) {lydict_remove(CTX, STRING);}
-#define FREE_STRINGS(CTX, ARRAY, DICT) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FREE_STRING(CTX, ARRAY[c__], DICT);}LY_ARRAY_FREE(ARRAY);}
-
-static void lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp, int dict);
-static void lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node, int dict);
-
-#define LYSC_CTX_BUFSIZE 4086
-struct lysc_ctx {
- struct lysc_module *mod;
- uint16_t path_len;
- char path[LYSC_CTX_BUFSIZE];
-};
-
-static void
-lysp_stmt_free(struct ly_ctx *ctx, struct lysp_stmt *stmt, int dict)
+API const struct lysc_node *
+lys_getnext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module, int options)
{
- struct lysp_stmt *child, *next;
+ const struct lysc_node *next;
+ struct lysc_node **snode;
- FREE_STRING(ctx, stmt->stmt, dict);
- FREE_STRING(ctx, stmt->arg, dict);
+ LY_CHECK_ARG_RET(NULL, parent || module, NULL);
- LY_LIST_FOR_SAFE(stmt->child, next, child) {
- lysp_stmt_free(ctx, child, dict);
- }
+ if (!last) {
+ /* first call */
- free(stmt);
-}
-
-static void
-lysp_ext_instance_free(struct ly_ctx *ctx, struct lysp_ext_instance *ext, int dict)
-{
- struct lysp_stmt *stmt, *next;
-
- FREE_STRING(ctx, ext->name, dict);
- FREE_STRING(ctx, ext->argument, dict);
-
- LY_LIST_FOR_SAFE(ext->child, next, stmt) {
- lysp_stmt_free(ctx, stmt, dict);
- }
-}
-
-static void
-lysp_import_free(struct ly_ctx *ctx, struct lysp_import *import, int dict)
-{
- FREE_STRING(ctx, import->name, dict);
- FREE_STRING(ctx, import->prefix, dict);
- FREE_STRING(ctx, import->dsc, dict);
- FREE_STRING(ctx, import->ref, dict);
- FREE_ARRAY(ctx, import->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_include_free(struct ly_ctx *ctx, struct lysp_include *include, int dict)
-{
- FREE_STRING(ctx, include->name, dict);
- FREE_STRING(ctx, include->dsc, dict);
- FREE_STRING(ctx, include->ref, dict);
- FREE_ARRAY(ctx, include->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_revision_free(struct ly_ctx *ctx, struct lysp_revision *rev, int dict)
-{
- FREE_STRING(ctx, rev->dsc, dict);
- FREE_STRING(ctx, rev->ref, dict);
- FREE_ARRAY(ctx, rev->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_ext_free(struct ly_ctx *ctx, struct lysp_ext *ext, int dict)
-{
- FREE_STRING(ctx, ext->name, dict);
- FREE_STRING(ctx, ext->argument, dict);
- FREE_STRING(ctx, ext->dsc, dict);
- FREE_STRING(ctx, ext->ref, dict);
- FREE_ARRAY(ctx, ext->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_feature_free(struct ly_ctx *ctx, struct lysp_feature *feat, int dict)
-{
- FREE_STRING(ctx, feat->name, dict);
- FREE_STRINGS(ctx, feat->iffeatures, 1);
- FREE_STRING(ctx, feat->dsc, dict);
- FREE_STRING(ctx, feat->ref, dict);
- FREE_ARRAY(ctx, feat->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_ident_free(struct ly_ctx *ctx, struct lysp_ident *ident, int dict)
-{
- FREE_STRING(ctx, ident->name, dict);
- FREE_STRINGS(ctx, ident->iffeatures, 1);
- FREE_STRINGS(ctx, ident->bases, dict);
- FREE_STRING(ctx, ident->dsc, dict);
- FREE_STRING(ctx, ident->ref, dict);
- FREE_ARRAY(ctx, ident->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_restr_free(struct ly_ctx *ctx, struct lysp_restr *restr, int dict)
-{
- FREE_STRING(ctx, restr->arg, dict);
- FREE_STRING(ctx, restr->emsg, dict);
- FREE_STRING(ctx, restr->eapptag, dict);
- FREE_STRING(ctx, restr->dsc, dict);
- FREE_STRING(ctx, restr->ref, dict);
- FREE_ARRAY(ctx, restr->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_type_enum_free(struct ly_ctx *ctx, struct lysp_type_enum *item, int dict)
-{
- FREE_STRING(ctx, item->name, dict);
- FREE_STRING(ctx, item->dsc, dict);
- FREE_STRING(ctx, item->ref, dict);
- FREE_STRINGS(ctx, item->iffeatures, 1);
- FREE_ARRAY(ctx, item->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_type_free(struct ly_ctx *ctx, struct lysp_type *type, int dict)
-{
- FREE_STRING(ctx, type->name, dict);
- FREE_MEMBER(ctx, type->range, lysp_restr_free);
- FREE_MEMBER(ctx, type->length, lysp_restr_free);
- FREE_ARRAY(ctx, type->patterns, lysp_restr_free);
- FREE_ARRAY(ctx, type->enums, lysp_type_enum_free);
- FREE_ARRAY(ctx, type->bits, lysp_type_enum_free);
- FREE_STRING(ctx, type->path, dict);
- FREE_STRINGS(ctx, type->bases, dict);
- FREE_ARRAY(ctx, type->types, lysp_type_free);
- FREE_ARRAY(ctx, type->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_tpdf_free(struct ly_ctx *ctx, struct lysp_tpdf *tpdf, int dict)
-{
- FREE_STRING(ctx, tpdf->name, dict);
- FREE_STRING(ctx, tpdf->units, dict);
- FREE_STRING(ctx, tpdf->dflt, dict);
- FREE_STRING(ctx, tpdf->dsc, dict);
- FREE_STRING(ctx, tpdf->ref, dict);
- FREE_ARRAY(ctx, tpdf->exts, lysp_ext_instance_free);
- lysp_type_free(ctx, &tpdf->type, dict);
-}
-
-static void
-lysp_action_inout_free(struct ly_ctx *ctx, struct lysp_action_inout *inout, int dict)
-{
- struct lysp_node *node, *next;
-
- FREE_ARRAY(ctx, inout->musts, lysp_restr_free);
- FREE_ARRAY(ctx, inout->typedefs, lysp_tpdf_free);
- FREE_ARRAY(ctx, inout->groupings, lysp_grp_free);
- LY_LIST_FOR_SAFE(inout->data, next, node) {
- lysp_node_free(ctx, node, dict);
- }
- FREE_ARRAY(ctx, inout->exts, lysp_ext_instance_free);
-
-}
-
-static void
-lysp_action_free(struct ly_ctx *ctx, struct lysp_action *action, int dict)
-{
- FREE_STRING(ctx, action->name, dict);
- FREE_STRING(ctx, action->dsc, dict);
- FREE_STRING(ctx, action->ref, dict);
- FREE_STRINGS(ctx, action->iffeatures, 1);
- FREE_ARRAY(ctx, action->typedefs, lysp_tpdf_free);
- FREE_ARRAY(ctx, action->groupings, lysp_grp_free);
- FREE_MEMBER(ctx, action->input, lysp_action_inout_free);
- FREE_MEMBER(ctx, action->output, lysp_action_inout_free);
- FREE_ARRAY(ctx, action->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_notif_free(struct ly_ctx *ctx, struct lysp_notif *notif, int dict)
-{
- struct lysp_node *node, *next;
-
- FREE_STRING(ctx, notif->name, dict);
- FREE_STRING(ctx, notif->dsc, dict);
- FREE_STRING(ctx, notif->ref, dict);
- FREE_STRINGS(ctx, notif->iffeatures, 1);
- FREE_ARRAY(ctx, notif->musts, lysp_restr_free);
- FREE_ARRAY(ctx, notif->typedefs, lysp_tpdf_free);
- FREE_ARRAY(ctx, notif->groupings, lysp_grp_free);
- LY_LIST_FOR_SAFE(notif->data, next, node) {
- lysp_node_free(ctx, node, dict);
- }
- FREE_ARRAY(ctx, notif->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp, int dict)
-{
- struct lysp_node *node, *next;
-
- FREE_STRING(ctx, grp->name, dict);
- FREE_STRING(ctx, grp->dsc, dict);
- FREE_STRING(ctx, grp->ref, dict);
- FREE_ARRAY(ctx, grp->typedefs, lysp_tpdf_free);
- FREE_ARRAY(ctx, grp->groupings, lysp_grp_free);
- LY_LIST_FOR_SAFE(grp->data, next, node) {
- lysp_node_free(ctx, node, dict);
- }
- FREE_ARRAY(ctx, grp->actions, lysp_action_free);
- FREE_ARRAY(ctx, grp->notifs, lysp_notif_free);
- FREE_ARRAY(ctx, grp->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_when_free(struct ly_ctx *ctx, struct lysp_when *when, int dict)
-{
- FREE_STRING(ctx, when->cond, dict);
- FREE_STRING(ctx, when->dsc, dict);
- FREE_STRING(ctx, when->ref, dict);
- FREE_ARRAY(ctx, when->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_augment_free(struct ly_ctx *ctx, struct lysp_augment *augment, int dict)
-{
- struct lysp_node *node, *next;
-
- FREE_STRING(ctx, augment->nodeid, dict);
- FREE_STRING(ctx, augment->dsc, dict);
- FREE_STRING(ctx, augment->ref, dict);
- FREE_MEMBER(ctx, augment->when, lysp_when_free);
- FREE_STRINGS(ctx, augment->iffeatures, 1);
- LY_LIST_FOR_SAFE(augment->child, next, node) {
- lysp_node_free(ctx, node, dict);
- }
- FREE_ARRAY(ctx, augment->actions, lysp_action_free);
- FREE_ARRAY(ctx, augment->notifs, lysp_notif_free);
- FREE_ARRAY(ctx, augment->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_deviate_free(struct ly_ctx *ctx, struct lysp_deviate *d, int dict)
-{
- struct lysp_deviate_add *add = (struct lysp_deviate_add*)d;
- struct lysp_deviate_rpl *rpl = (struct lysp_deviate_rpl*)d;
-
- FREE_ARRAY(ctx, d->exts, lysp_ext_instance_free);
- switch(d->mod) {
- case LYS_DEV_NOT_SUPPORTED:
- /* nothing to do */
- break;
- case LYS_DEV_ADD:
- case LYS_DEV_DELETE: /* compatible for dynamically allocated data */
- FREE_STRING(ctx, add->units, dict);
- FREE_ARRAY(ctx, add->musts, lysp_restr_free);
- FREE_STRINGS(ctx, add->uniques, dict);
- FREE_STRINGS(ctx, add->dflts, dict);
- break;
- case LYS_DEV_REPLACE:
- FREE_MEMBER(ctx, rpl->type, lysp_type_free);
- FREE_STRING(ctx, rpl->units, dict);
- FREE_STRING(ctx, rpl->dflt, dict);
- break;
- default:
- LOGINT(ctx);
- break;
- }
-}
-
-static void
-lysp_deviation_free(struct ly_ctx *ctx, struct lysp_deviation *dev, int dict)
-{
- struct lysp_deviate *next, *iter;
-
- FREE_STRING(ctx, dev->nodeid, dict);
- FREE_STRING(ctx, dev->dsc, dict);
- FREE_STRING(ctx, dev->ref, dict);
- LY_LIST_FOR_SAFE(dev->deviates, next, iter) {
- lysp_deviate_free(ctx, iter, dict);
- free(iter);
- }
- FREE_ARRAY(ctx, dev->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_refine_free(struct ly_ctx *ctx, struct lysp_refine *ref, int dict)
-{
- FREE_STRING(ctx, ref->nodeid, dict);
- FREE_STRING(ctx, ref->dsc, dict);
- FREE_STRING(ctx, ref->ref, dict);
- FREE_STRINGS(ctx, ref->iffeatures, 1);
- FREE_ARRAY(ctx, ref->musts, lysp_restr_free);
- FREE_STRING(ctx, ref->presence, dict);
- FREE_STRINGS(ctx, ref->dflts, dict);
- FREE_ARRAY(ctx, ref->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node, int dict)
-{
- struct lysp_node *child, *next;
-
- FREE_STRING(ctx, node->name, dict);
- FREE_STRING(ctx, node->dsc, dict);
- FREE_STRING(ctx, node->ref, dict);
- FREE_MEMBER(ctx, node->when, lysp_when_free);
- FREE_STRINGS(ctx, node->iffeatures, dict);
- FREE_ARRAY(ctx, node->exts, lysp_ext_instance_free);
-
- switch(node->nodetype) {
- case LYS_CONTAINER:
- FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->musts, lysp_restr_free);
- FREE_STRING(ctx, ((struct lysp_node_container*)node)->presence, dict);
- FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->typedefs, lysp_tpdf_free);
- FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->groupings, lysp_grp_free);
- LY_LIST_FOR_SAFE(((struct lysp_node_container*)node)->child, next, child) {
- lysp_node_free(ctx, child, dict);
+ /* get know where to start */
+ if (parent) {
+ /* schema subtree */
+ if (parent->nodetype == LYS_CHOICE && (options & LYS_GETNEXT_WITHCASE)) {
+ if (!((struct lysc_node_choice*)parent)->cases) {
+ return NULL;
+ }
+ next = last = (const struct lysc_node*)&((struct lysc_node_choice*)parent)->cases[0];
+ } else {
+ snode = lysc_node_children_p(parent);
+ /* do not return anything if the augment does not have any children */
+ if (!snode || !(*snode)) {
+ return NULL;
+ }
+ next = last = *snode;
+ }
+ } else {
+ /* top level data */
+ next = last = module->data;
}
- FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->actions, lysp_action_free);
- FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->notifs, lysp_notif_free);
- break;
+ if (!next) {
+ return next;
+ } else if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
+ if (!lys_is_disabled(next, 0)) {
+ return next;
+ }
+ /* continue to find next available node */
+ } else {
+ return next;
+ }
+ }
+
+ next = last->next;
+repeat:
+ if (!next) {
+ /* possibly go back to parent */
+ if (last->parent != parent) {
+ last = last->parent;
+ next = last->next;
+ goto repeat;
+ }
+ return next;
+ }
+ switch (next->nodetype) {
+ case LYS_ACTION:
+ case LYS_NOTIF:
case LYS_LEAF:
- FREE_ARRAY(ctx, ((struct lysp_node_leaf*)node)->musts, lysp_restr_free);
- lysp_type_free(ctx, &((struct lysp_node_leaf*)node)->type, dict);
- FREE_STRING(ctx, ((struct lysp_node_leaf*)node)->units, dict);
- FREE_STRING(ctx, ((struct lysp_node_leaf*)node)->dflt, dict);
- break;
- case LYS_LEAFLIST:
- FREE_ARRAY(ctx, ((struct lysp_node_leaflist*)node)->musts, lysp_restr_free);
- lysp_type_free(ctx, &((struct lysp_node_leaflist*)node)->type, dict);
- FREE_STRING(ctx, ((struct lysp_node_leaflist*)node)->units, dict);
- FREE_STRINGS(ctx, ((struct lysp_node_leaflist*)node)->dflts, dict);
- break;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
case LYS_LIST:
- FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->musts, lysp_restr_free);
- FREE_STRING(ctx, ((struct lysp_node_list*)node)->key, dict);
- FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->typedefs, lysp_tpdf_free);
- FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->groupings, lysp_grp_free);
- LY_LIST_FOR_SAFE(((struct lysp_node_list*)node)->child, next, child) {
- lysp_node_free(ctx, child, dict);
+ case LYS_LEAFLIST:
+ case LYS_CASE:
+ break;
+ case LYS_CONTAINER:
+ if (!(((struct lysc_node_container *)next)->flags & LYS_PRESENCE) && (options & LYS_GETNEXT_INTONPCONT)) {
+ if (((struct lysc_node_container *)next)->child) {
+ /* go into */
+ next = ((struct lysc_node_container *)next)->child;
+ } else {
+ next = next->next;
+ }
+ goto repeat;
}
- FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->actions, lysp_action_free);
- FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->notifs, lysp_notif_free);
- FREE_STRINGS(ctx, ((struct lysp_node_list*)node)->uniques, dict);
break;
case LYS_CHOICE:
- LY_LIST_FOR_SAFE(((struct lysp_node_choice*)node)->child, next, child) {
- lysp_node_free(ctx, child, dict);
+ if (options & LYS_GETNEXT_WITHCHOICE) {
+ return next;
+ } else if ((options & LYS_GETNEXT_NOCHOICE) || !((struct lysc_node_choice *)next)->cases) {
+ next = next->next;
+ } else {
+ /* go into */
+ if (options & LYS_GETNEXT_WITHCASE) {
+ next = (struct lysc_node*)&((struct lysc_node_choice *)next)->cases;
+ } else {
+ next = ((struct lysc_node_choice *)next)->cases->child;
+ }
}
- FREE_STRING(ctx, ((struct lysp_node_choice*)node)->dflt, dict);
- break;
- case LYS_CASE:
- LY_LIST_FOR_SAFE(((struct lysp_node_case*)node)->child, next, child) {
- lysp_node_free(ctx, child, dict);
- }
- break;
- case LYS_ANYDATA:
- case LYS_ANYXML:
- FREE_ARRAY(ctx, ((struct lysp_node_anydata*)node)->musts, lysp_restr_free);
- break;
- case LYS_USES:
- FREE_ARRAY(ctx, ((struct lysp_node_uses*)node)->refines, lysp_refine_free);
- FREE_ARRAY(ctx, ((struct lysp_node_uses*)node)->augments, lysp_augment_free);
- break;
+ goto repeat;
default:
- LOGINT(ctx);
+ /* we should not be here */
+ LOGINT(last->module->compiled->ctx);
+ return NULL;
}
- free(node);
-}
-
-static void
-lysp_module_free_(struct lysp_module *module, int dict)
-{
- struct ly_ctx *ctx;
- struct lysp_node *node, *next;
-
- LY_CHECK_ARG_RET(NULL, module,);
- ctx = module->ctx;
-
- FREE_STRING(ctx, module->name, dict);
- FREE_STRING(ctx, module->filepath, dict);
- FREE_STRING(ctx, module->ns, dict); /* or belongs-to */
- FREE_STRING(ctx, module->prefix, dict);
-
- FREE_ARRAY(ctx, module->imports, lysp_import_free);
- FREE_ARRAY(ctx, module->includes, lysp_include_free);
-
- FREE_STRING(ctx, module->org, dict);
- FREE_STRING(ctx, module->contact, dict);
- FREE_STRING(ctx, module->dsc, dict);
- FREE_STRING(ctx, module->ref, dict);
-
- FREE_ARRAY(ctx, module->revs, lysp_revision_free);
- FREE_ARRAY(ctx, module->extensions, lysp_ext_free);
- FREE_ARRAY(ctx, module->features, lysp_feature_free);
- FREE_ARRAY(ctx, module->identities, lysp_ident_free);
- FREE_ARRAY(ctx, module->typedefs, lysp_tpdf_free);
- FREE_ARRAY(ctx, module->groupings, lysp_grp_free);
- LY_LIST_FOR_SAFE(module->data, next, node) {
- lysp_node_free(ctx, node, dict);
+ if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
+ /* check if the node is disabled by if-feature */
+ if (lys_is_disabled(next, 0)) {
+ next = next->next;
+ goto repeat;
+ }
}
- FREE_ARRAY(ctx, module->augments, lysp_augment_free);
- FREE_ARRAY(ctx, module->rpcs, lysp_action_free);
- FREE_ARRAY(ctx, module->notifs, lysp_notif_free);
- FREE_ARRAY(ctx, module->deviations, lysp_deviation_free);
- FREE_ARRAY(ctx, module->exts, lysp_ext_instance_free);
-
- free(module);
+ return next;
}
-API void
-lysp_module_free(struct lysp_module *module)
+API const struct lysc_node *
+lys_child(const struct lysc_node *parent, const struct lys_module *module,
+ const char *name, size_t name_len, uint16_t nodetype, int options)
{
- if (module) {
- lysp_module_free_(module, 1);
- }
-}
+ const struct lysc_node *node = NULL;
-static void
-lysc_iffeature_free(struct ly_ctx *UNUSED(ctx), struct lysc_iffeature *iff, int UNUSED(dict))
-{
- LY_ARRAY_FREE(iff->features);
- free(iff->expr);
-}
-
-static void
-lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat, int dict)
-{
- FREE_STRING(ctx, feat->name, dict);
- FREE_ARRAY(ctx, feat->iffeatures, lysc_iffeature_free);
- LY_ARRAY_FREE(feat->depfeatures);
-}
-
-static void
-lysc_module_free_(struct lysc_module *module, int dict)
-{
- struct ly_ctx *ctx;
-
- LY_CHECK_ARG_RET(NULL, module,);
- ctx = module->ctx;
-
- FREE_STRING(ctx, module->name, dict);
- FREE_STRING(ctx, module->ns, dict);
- FREE_STRING(ctx, module->prefix, dict);
-
-
- FREE_ARRAY(ctx, module->features, lysc_feature_free);
-
-
- free(module);
-}
-
-API void
-lysc_module_free(struct lysc_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv))
-{
- if (module) {
- lysc_module_free_(module, 1);
- }
-}
-
-void
-lys_module_free(struct lys_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv))
-{
- if (!module) {
- return;
+ LY_CHECK_ARG_RET(NULL, module, name, NULL);
+ if (!nodetype) {
+ nodetype = 0xffff;
}
- lysc_module_free(module->compiled, private_destructor);
- lysp_module_free(module->parsed);
- free(module);
-}
+ while ((node = lys_getnext(node, parent, module->compiled, options))) {
+ if (!(node->nodetype & nodetype)) {
+ continue;
+ }
+ if (node->module != module) {
+ continue;
+ }
-struct iff_stack {
- int size;
- int index; /* first empty item */
- uint8_t *stack;
-};
-
-static LY_ERR
-iff_stack_push(struct iff_stack *stack, uint8_t value)
-{
- if (stack->index == stack->size) {
- stack->size += 4;
- stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
- LY_CHECK_ERR_RET(!stack->stack, LOGMEM(NULL); stack->size = 0, LY_EMEM);
+ if (name_len) {
+ if (!strncmp(node->name, name, name_len) && !node->name[name_len]) {
+ return node;
+ }
+ } else {
+ if (!strcmp(node->name, name)) {
+ return node;
+ }
+ }
}
- stack->stack[stack->index++] = value;
- return LY_SUCCESS;
+ return NULL;
}
-static uint8_t
-iff_stack_pop(struct iff_stack *stack)
+API int
+lysc_feature_value(const struct lysc_feature *feature)
{
- stack->index--;
- return stack->stack[stack->index];
-}
-
-static void
-iff_stack_clean(struct iff_stack *stack)
-{
- stack->size = 0;
- free(stack->stack);
-}
-
-static void
-iff_setop(uint8_t *list, uint8_t op, int pos)
-{
- uint8_t *item;
- uint8_t mask = 3;
-
- assert(pos >= 0);
- assert(op <= 3); /* max 2 bits */
-
- item = &list[pos / 4];
- mask = mask << 2 * (pos % 4);
- *item = (*item) & ~mask;
- *item = (*item) | (op << 2 * (pos % 4));
+ LY_CHECK_ARG_RET(NULL, feature, -1);
+ return feature->flags & LYS_FENABLED ? 1 : 0;
}
static uint8_t
@@ -562,44 +185,6 @@
return result >> 2 * (pos % 4);
}
-#define LYS_IFF_LP 0x04 /* ( */
-#define LYS_IFF_RP 0x08 /* ) */
-
-API int
-lysc_feature_value(const struct lysc_feature *feature)
-{
- LY_CHECK_ARG_RET(NULL, feature, -1);
- return feature->flags & LYS_FENABLED ? 1 : 0;
-}
-
-static struct lysc_feature *
-lysc_feature_find(struct lysc_module *mod, const char *name, size_t len)
-{
- size_t i;
- struct lysc_feature *f;
-
- for (i = 0; i < len; ++i) {
- if (name[i] == ':') {
- /* we have a prefixed feature */
- mod = lysc_module_find_prefix(mod, name, i);
- LY_CHECK_RET(!mod, NULL);
-
- name = &name[i + 1];
- len = len - i - 1;
- }
- }
-
- /* we have the correct module, get the feature */
- LY_ARRAY_FOR(mod->features, i) {
- f = &mod->features[i];
- if (!strncmp(f->name, name, len) && f->name[len] == '\0') {
- return f;
- }
- }
-
- return NULL;
-}
-
static int
lysc_iffeature_value_(const struct lysc_iffeature *iff, int *index_e, int *index_f)
{
@@ -643,9 +228,6 @@
return 0;
}
-/*
- * op: 1 - enable, 0 - disable
- */
/**
* @brief Enable/Disable the specified feature in the module.
*
@@ -664,7 +246,7 @@
lys_feature_change(const struct lysc_module *mod, const char *name, int value)
{
int all = 0;
- unsigned int u;
+ unsigned int u, changed_count, disabled_count;
struct lysc_feature *f, **df;
struct lysc_iffeature *iff;
struct ly_set *changed;
@@ -679,8 +261,10 @@
all = 1;
}
changed = ly_set_new();
+ changed_count = 0;
- for (u = 0; u < LY_ARRAY_SIZE(mod->features); ++u) {
+run:
+ for (disabled_count = u = 0; u < LY_ARRAY_SIZE(mod->features); ++u) {
f = &mod->features[u];
if (all || !strcmp(f->name, name)) {
if ((value && (f->flags & LYS_FENABLED)) || (!value && !(f->flags & LYS_FENABLED))) {
@@ -699,9 +283,7 @@
LY_ARRAY_FOR(f->iffeatures, struct lysc_iffeature, iff) {
if (!lysc_iffeature_value(iff)) {
if (all) {
- LOGWRN(mod->ctx,
- "Feature \"%s\" cannot be enabled since it is disabled by its if-feature condition(s).",
- f->name);
+ ++disabled_count;
goto next;
} else {
LOGERR(mod->ctx, LY_EDENIED,
@@ -737,6 +319,34 @@
return LY_EINVAL;
}
+ if (value && all && disabled_count) {
+ if (changed_count == changed->count) {
+ /* no change in last run -> not able to enable all ... */
+ /* ... print errors */
+ for (u = 0; disabled_count && u < LY_ARRAY_SIZE(mod->features); ++u) {
+ if (!(mod->features[u].flags & LYS_FENABLED)) {
+ LOGERR(mod->ctx, LY_EDENIED,
+ "Feature \"%s\" cannot be enabled since it is disabled by its if-feature condition(s).",
+ mod->features[u].name);
+ --disabled_count;
+ }
+ }
+ /* ... restore the original state */
+ for (u = 0; u < changed->count; ++u) {
+ f = changed->objs[u];
+ /* re-disable the feature */
+ f->flags &= ~LYS_FENABLED;
+ }
+
+ ly_set_free(changed, NULL);
+ return LY_EDENIED;
+ } else {
+ /* we did some change in last run, try it again */
+ changed_count = changed->count;
+ goto run;
+ }
+ }
+
/* reflect change(s) in the dependent features */
for (u = 0; u < changed->count; ++u) {
/* If a dependent feature is enabled, it can be now changed by the change (to false) of the value of
@@ -807,283 +417,73 @@
return -1;
}
-static LY_ERR
-lys_compile_iffeature(struct lysc_ctx *ctx, const char *value, int UNUSED(options), struct lysc_iffeature *iff, struct lysc_feature *parent)
+API const struct lysc_iffeature *
+lys_is_disabled(const struct lysc_node *node, int recursive)
{
- const char *c = value;
- int r, rc = EXIT_FAILURE;
- int i, j, last_not, checkversion = 0;
- unsigned int f_size = 0, expr_size = 0, f_exp = 1;
- uint8_t op;
- struct iff_stack stack = {0, 0, NULL};
- struct lysc_feature *f, **df;
+ unsigned int u;
- assert(c);
+ LY_CHECK_ARG_RET(NULL, node, NULL);
- /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
- for (i = j = last_not = 0; c[i]; i++) {
- if (c[i] == '(') {
- j++;
- checkversion = 1;
- continue;
- } else if (c[i] == ')') {
- j--;
- continue;
- } else if (isspace(c[i])) {
- checkversion = 1;
- continue;
+ while(node) {
+ if (node->nodetype & LYS_CHOICE) {
+ return NULL;
}
- if (!strncmp(&c[i], "not", r = 3) || !strncmp(&c[i], "and", r = 3) || !strncmp(&c[i], "or", r = 2)) {
- if (c[i + r] == '\0') {
- LOGVAL(ctx->mod->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - unexpected end of expression.", value);
- return LY_EVALID;
- } else if (!isspace(c[i + r])) {
- /* feature name starting with the not/and/or */
- last_not = 0;
- f_size++;
- } else if (c[i] == 'n') { /* not operation */
- if (last_not) {
- /* double not */
- expr_size = expr_size - 2;
- last_not = 0;
- } else {
- last_not = 1;
+ if (node->iffeatures) {
+ /* check local if-features */
+ LY_ARRAY_FOR(node->iffeatures, u) {
+ if (!lysc_iffeature_value(&node->iffeatures[u])) {
+ return &node->iffeatures[u];
}
- } else { /* and, or */
- f_exp++;
- /* not a not operation */
- last_not = 0;
}
- i += r;
- } else {
- f_size++;
- last_not = 0;
- }
- expr_size++;
-
- while (!isspace(c[i])) {
- if (!c[i] || c[i] == ')') {
- i--;
- break;
- }
- i++;
- }
- }
- if (j || f_exp != f_size) {
- /* not matching count of ( and ) */
- LOGVAL(ctx->mod->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - non-matching opening and closing parentheses.", value);
- return LY_EVALID;
- }
-
- if (checkversion || expr_size > 1) {
- /* check that we have 1.1 module */
- if (ctx->mod->version != LYS_VERSION_1_1) {
- LOGVAL(ctx->mod->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", value);
- return LY_EVALID;
- }
- }
-
- /* allocate the memory */
- LY_ARRAY_CREATE_RET(ctx->mod->ctx, iff->features, f_size, LY_EMEM);
- iff->expr = calloc((j = (expr_size / 4) + ((expr_size % 4) ? 1 : 0)), sizeof *iff->expr);
- stack.stack = malloc(expr_size * sizeof *stack.stack);
- LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx->mod->ctx), error);
-
- stack.size = expr_size;
- f_size--; expr_size--; /* used as indexes from now */
-
- for (i--; i >= 0; i--) {
- if (c[i] == ')') {
- /* push it on stack */
- iff_stack_push(&stack, LYS_IFF_RP);
- continue;
- } else if (c[i] == '(') {
- /* pop from the stack into result all operators until ) */
- while((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
- iff_setop(iff->expr, op, expr_size--);
- }
- continue;
- } else if (isspace(c[i])) {
- continue;
}
- /* end of operator or operand -> find beginning and get what is it */
- j = i + 1;
- while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
- i--;
+ if (!recursive) {
+ return NULL;
}
- i++; /* go back by one step */
- if (!strncmp(&c[i], "not", 3) && isspace(c[i + 3])) {
- if (stack.index && stack.stack[stack.index - 1] == LYS_IFF_NOT) {
- /* double not */
- iff_stack_pop(&stack);
- } else {
- /* not has the highest priority, so do not pop from the stack
- * as in case of AND and OR */
- iff_stack_push(&stack, LYS_IFF_NOT);
- }
- } else if (!strncmp(&c[i], "and", 3) && isspace(c[i + 3])) {
- /* as for OR - pop from the stack all operators with the same or higher
- * priority and store them to the result, then push the AND to the stack */
- while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
- op = iff_stack_pop(&stack);
- iff_setop(iff->expr, op, expr_size--);
- }
- iff_stack_push(&stack, LYS_IFF_AND);
- } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
- while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
- op = iff_stack_pop(&stack);
- iff_setop(iff->expr, op, expr_size--);
- }
- iff_stack_push(&stack, LYS_IFF_OR);
- } else {
- /* feature name, length is j - i */
-
- /* add it to the expression */
- iff_setop(iff->expr, LYS_IFF_F, expr_size--);
-
- /* now get the link to the feature definition */
- f = lysc_feature_find(ctx->mod, &c[i], j - i);
- LY_CHECK_ERR_GOTO(!f,
- LOGVAL(ctx->mod->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".", value, j - i, &c[i]);
- rc = LY_EINVAL,
- error)
- iff->features[f_size] = f;
- LY_ARRAY_INCREMENT(iff->features);
- if (parent) {
- /* and add itself into the dependants list */
- LY_ARRAY_NEW_RET(ctx->mod->ctx, f->depfeatures, df, LY_EMEM);
- *df = parent;
-
- /* TODO check for circular dependency */
- }
- f_size--;
- }
+ /* go through parents */
+ node = node->parent;
}
- while (stack.index) {
- op = iff_stack_pop(&stack);
- iff_setop(iff->expr, op, expr_size--);
- }
-
- if (++expr_size || ++f_size) {
- /* not all expected operators and operands found */
- LOGVAL(ctx->mod->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
- "Invalid value \"%s\" of if-feature - processing error.", value);
- rc = LY_EINT;
- } else {
- rc = LY_SUCCESS;
- }
-
-error:
- /* cleanup */
- iff_stack_clean(&stack);
-
- return rc;
+ return NULL;
}
-static LY_ERR
-lys_compile_feature(struct lysc_ctx *ctx, struct lysp_feature *feature_p, int options, struct lysc_feature *feature)
+static void
+lys_latest_switch(struct lys_module *old, struct lysp_module *new)
{
- unsigned int u;
- LY_ERR ret;
-
- if (options & LYSC_OPT_FREE_SP) {
- /* just switch the pointers */
- feature->name = feature_p->name;
- } else {
- /* keep refcounts correct for lysp_module_free() */
- feature->name = lydict_insert(ctx->mod->ctx, feature_p->name, 0);
+ if (old->parsed) {
+ new->latest_revision = old->parsed->latest_revision;
+ old->parsed->latest_revision = 0;
}
- feature->flags = feature_p->flags;
-
- if (feature_p->iffeatures) {
- /* allocate everything now */
- LY_ARRAY_CREATE_RET(ctx->mod->ctx, feature->iffeatures, LY_ARRAY_SIZE(feature_p->iffeatures), LY_EMEM);
-
- for (u = 0; u < LY_ARRAY_SIZE(feature_p->iffeatures); ++u) {
- ret = lys_compile_iffeature(ctx, feature_p->iffeatures[u], options, &feature->iffeatures[u], feature);
- LY_CHECK_RET(ret);
- LY_ARRAY_INCREMENT(feature->iffeatures);
- }
+ if (old->compiled) {
+ new->latest_revision = old->parsed->latest_revision;
+ old->compiled->latest_revision = 0;
}
-
- return LY_SUCCESS;
}
-LY_ERR
-lys_compile(struct lysp_module *sp, int options, struct lysc_module **sc)
+struct lys_module *
+lys_parse_mem_(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+ LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data)
{
- struct lysc_ctx ctx = {0};
- struct lysc_module *mod_c;
- unsigned int u;
- LY_ERR ret;
-
- LY_CHECK_ARG_RET(NULL, sc, sp, sp->ctx, LY_EINVAL);
-
- if (sp->submodule) {
- LOGERR(sp->ctx, LY_EINVAL, "Submodules (%s) are not supposed to be compiled, compile only the main modules.", sp->name);
- return LY_EINVAL;
- }
-
- ctx.mod = mod_c = calloc(1, sizeof *mod_c);
- LY_CHECK_ERR_RET(!mod_c, LOGMEM(sp->ctx), LY_EMEM);
- mod_c->ctx = sp->ctx;
- mod_c->version = sp->version;
-
- if (options & LYSC_OPT_FREE_SP) {
- /* just switch the pointers */
- mod_c->name = sp->name;
- mod_c->ns = sp->ns;
- mod_c->prefix = sp->prefix;
- } else {
- /* keep refcounts correct for lysp_module_free() */
- mod_c->name = lydict_insert(sp->ctx, sp->name, 0);
- mod_c->ns = lydict_insert(sp->ctx, sp->ns, 0);
- mod_c->prefix = lydict_insert(sp->ctx, sp->prefix, 0);
- }
-
- if (sp->features) {
- /* allocate everything now */
- LY_ARRAY_CREATE_RET(ctx.mod->ctx, mod_c->features, LY_ARRAY_SIZE(sp->features), LY_EMEM);
-
- for (u = 0; u < LY_ARRAY_SIZE(sp->features); ++u) {
- ret = lys_compile_feature(&ctx, &sp->features[u], options, &mod_c->features[u]);
- LY_CHECK_GOTO(ret != LY_SUCCESS, error);
- LY_ARRAY_INCREMENT(mod_c->features);
- }
- }
-
- if (options & LYSC_OPT_FREE_SP) {
- lysp_module_free_(sp, 0);
- }
-
- (*sc) = mod_c;
- return LY_SUCCESS;
-
-error:
-
- if (options & LYSC_OPT_FREE_SP) {
- lysc_module_free_(mod_c, 0);
- } else {
- lysc_module_free_(mod_c, 1);
- }
- return ret;
-}
-
-const struct lys_module *
-lys_parse_mem_(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, const char *revision, int implement)
-{
- struct lys_module *mod = NULL;
- LY_ERR ret;
+ struct lys_module *mod = NULL, *latest, *mod_dup;
+ struct lysp_module *latest_p;
+ struct lysp_import *imp;
+ struct lysp_include *inc;
+ LY_ERR ret = LY_EINVAL;
+ unsigned int u, i;
+ struct ly_parser_ctx context = {0};
LY_CHECK_ARG_RET(ctx, ctx, data, NULL);
+ context.ctx = ctx;
+ context.line = 1;
+
+ if (main_ctx) {
+ /* map the typedefs and groupings list from main context to the submodule's context */
+ memcpy(&context.tpdfs_nodes, &main_ctx->tpdfs_nodes, sizeof main_ctx->tpdfs_nodes);
+ memcpy(&context.grps_nodes, &main_ctx->grps_nodes, sizeof main_ctx->grps_nodes);
+ }
+
mod = calloc(1, sizeof *mod);
LY_CHECK_ERR_RET(!mod, LOGMEM(ctx), NULL);
@@ -1094,33 +494,79 @@
*/
break;
case LYS_IN_YANG:
- ret = yang_parse(ctx, data, &mod->parsed);
- LY_CHECK_RET(ret, NULL);
+ ret = yang_parse(&context, data, &mod->parsed);
break;
default:
LOGERR(ctx, LY_EINVAL, "Invalid schema input format.");
break;
}
+ LY_CHECK_ERR_RET(ret, free(mod), NULL);
+
+ /* make sure that the newest revision is at position 0 */
+ lysp_sort_revisions(mod->parsed->revs);
if (implement) {
+ /* mark the loaded module implemented */
+ if (ly_ctx_get_module_implemented(ctx, mod->parsed->name)) {
+ LOGERR(ctx, LY_EDENIED, "Module \"%s\" is already implemented in the context.", mod->parsed->name);
+ goto error;
+ }
mod->parsed->implemented = 1;
}
- if (revision) {
- /* check revision of the parsed model */
- if (!mod->parsed->revs || strcmp(revision, mod->parsed->revs[0].rev)) {
- LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").",
- mod->parsed->name, mod->parsed->revs[0].rev, revision);
- lysp_module_free(mod->parsed);
- free(mod);
- return NULL;
- }
+ if (custom_check) {
+ LY_CHECK_GOTO(custom_check(ctx, mod->parsed, check_data), error);
}
- /* check for duplicity in the context */
-
- /* add into context */
- ly_set_add(&ctx->list, mod, LY_SET_OPT_USEASLIST);
+ if (mod->parsed->submodule) { /* submodule */
+ if (!main_ctx) {
+ LOGERR(ctx, LY_EDENIED, "Input data contains submodule \"%s\" which cannot be parsed directly without its main module.",
+ mod->parsed->name);
+ goto error;
+ }
+ /* decide the latest revision */
+ latest_p = ly_ctx_get_submodule(ctx, mod->parsed->belongsto, mod->parsed->name, NULL);
+ if (latest_p) {
+ if (mod->parsed->revs) {
+ if (!latest_p->revs) {
+ /* latest has no revision, so mod is anyway newer */
+ mod->parsed->latest_revision = latest_p->latest_revision;
+ latest_p->latest_revision = 0;
+ } else {
+ if (strcmp(mod->parsed->revs[0].date, latest_p->revs[0].date) > 0) {
+ mod->parsed->latest_revision = latest_p->latest_revision;
+ latest_p->latest_revision = 0;
+ }
+ }
+ }
+ } else {
+ mod->parsed->latest_revision = 1;
+ }
+ /* remap possibly changed and reallocated typedefs and groupings list back to the main context */
+ memcpy(&main_ctx->tpdfs_nodes, &context.tpdfs_nodes, sizeof main_ctx->tpdfs_nodes);
+ memcpy(&main_ctx->grps_nodes, &context.grps_nodes, sizeof main_ctx->grps_nodes);
+ } else { /* module */
+ /* check for duplicity in the context */
+ mod_dup = (struct lys_module*)ly_ctx_get_module(ctx, mod->parsed->name, mod->parsed->revs ? mod->parsed->revs[0].date : NULL);
+ if (mod_dup) {
+ if (mod_dup->parsed) {
+ /* error */
+ if (mod->parsed->revs) {
+ LOGERR(ctx, LY_EEXIST, "Module \"%s\" of revision \"%s\" is already present in the context.",
+ mod->parsed->name, mod->parsed->revs[0].date);
+ } else {
+ LOGERR(ctx, LY_EEXIST, "Module \"%s\" with no revision is already present in the context.",
+ mod->parsed->name);
+ }
+ goto error;
+ } else {
+ /* add the parsed data to the currently compiled-only module in the context */
+ mod_dup->parsed = mod->parsed;
+ free(mod);
+ mod = mod_dup;
+ goto finish_parsing;
+ }
+ }
#if 0
/* hack for NETCONF's edit-config's operation attribute. It is not defined in the schema, but since libyang
@@ -1136,30 +582,83 @@
}
#endif
+ /* decide the latest revision */
+ latest = (struct lys_module*)ly_ctx_get_module_latest(ctx, mod->parsed->name);
+ if (latest) {
+ if (mod->parsed->revs) {
+ if ((latest->parsed && !latest->parsed->revs) || (!latest->parsed && !latest->compiled->revision)) {
+ /* latest has no revision, so mod is anyway newer */
+ lys_latest_switch(latest, mod->parsed);
+ } else {
+ if (strcmp(mod->parsed->revs[0].date, latest->parsed ? latest->parsed->revs[0].date : latest->compiled->revision) > 0) {
+ lys_latest_switch(latest, mod->parsed);
+ }
+ }
+ }
+ } else {
+ mod->parsed->latest_revision = 1;
+ }
+
+ /* add into context */
+ ly_set_add(&ctx->list, mod, LY_SET_OPT_USEASLIST);
+
+finish_parsing:
+ /* resolve imports */
+ mod->parsed->parsing = 1;
+ LY_ARRAY_FOR(mod->parsed->imports, u) {
+ imp = &mod->parsed->imports[u];
+ if (!imp->module && lysp_load_module(ctx, imp->name, imp->rev[0] ? imp->rev : NULL, 0, 0, &imp->module)) {
+ goto error_ctx;
+ }
+ /* check for importing the same module twice */
+ for (i = 0; i < u; ++i) {
+ if (imp->module == mod->parsed->imports[i].module) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Single revision of the module \"%s\" referred twice.", imp->name);
+ goto error_ctx;
+ }
+ }
+ }
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ inc = &mod->parsed->includes[u];
+ if (!inc->submodule && lysp_load_submodule(&context, mod->parsed, inc)) {
+ goto error_ctx;
+ }
+ }
+ mod->parsed->parsing = 0;
+
+ /* check name collisions - typedefs and groupings */
+ LY_CHECK_GOTO(lysp_check_typedefs(&context), error_ctx);
+ }
+
return mod;
+
+error_ctx:
+ ly_set_rm(&ctx->list, mod, NULL);
+error:
+ lys_module_free(mod, NULL);
+ ly_set_erase(&context.tpdfs_nodes, NULL);
+ return NULL;
}
-API const struct lys_module *
+API struct lys_module *
lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format)
{
- return lys_parse_mem_(ctx, data, format, NULL, 1);
+ return lys_parse_mem_(ctx, data, format, 1, NULL, NULL, NULL);
}
static void
lys_parse_set_filename(struct ly_ctx *ctx, const char **filename, int fd)
{
-#ifdef __APPLE__
- char path[MAXPATHLEN];
-#else
- int len;
- char path[PATH_MAX], proc_path[32];
-#endif
+ char path[PATH_MAX];
#ifdef __APPLE__
if (fcntl(fd, F_GETPATH, path) != -1) {
*filename = lydict_insert(ctx, path, 0);
}
#else
+ int len;
+ char proc_path[32];
+
/* get URI if there is /proc */
sprintf(proc_path, "/proc/self/fd/%d", fd);
if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
@@ -1168,10 +667,11 @@
#endif
}
-const struct lys_module *
-lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, const char *revision, int implement)
+struct lys_module *
+lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+ LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data)
{
- const struct lys_module *mod;
+ struct lys_module *mod;
size_t length;
char *addr;
@@ -1187,7 +687,7 @@
return NULL;
}
- mod = lys_parse_mem_(ctx, addr, format, revision, implement);
+ mod = lys_parse_mem_(ctx, addr, format, implement, main_ctx, custom_check, check_data);
ly_munmap(addr, length);
if (mod && !mod->parsed->filepath) {
@@ -1197,17 +697,18 @@
return mod;
}
-API const struct lys_module *
+API struct lys_module *
lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format)
{
- return lys_parse_fd_(ctx, fd, format, NULL, 1);
+ return lys_parse_fd_(ctx, fd, format, 1, NULL, NULL, NULL);
}
-API const struct lys_module *
-lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format)
+struct lys_module *
+lys_parse_path_(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+ LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data)
{
int fd;
- const struct lys_module *mod;
+ struct lys_module *mod;
const char *rev, *dot, *filename;
size_t len;
@@ -1216,7 +717,7 @@
fd = open(path, O_RDONLY);
LY_CHECK_ERR_RET(fd == -1, LOGERR(ctx, LY_ESYS, "Opening file \"%s\" failed (%s).", path, strerror(errno)), NULL);
- mod = lys_parse_fd(ctx, fd, format);
+ mod = lys_parse_fd_(ctx, fd, format, implement, main_ctx, custom_check, check_data);
close(fd);
LY_CHECK_RET(!mod, NULL);
@@ -1238,9 +739,9 @@
}
if (rev) {
len = dot - ++rev;
- if (!mod->parsed->revs || len != 10 || strncmp(mod->parsed->revs[0].rev, rev, len)) {
+ if (!mod->parsed->revs || len != 10 || strncmp(mod->parsed->revs[0].date, rev, len)) {
LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
- mod->parsed->revs ? mod->parsed->revs[0].rev : "none");
+ mod->parsed->revs ? mod->parsed->revs[0].date : "none");
}
}
@@ -1257,3 +758,199 @@
return mod;
}
+API struct lys_module *
+lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format)
+{
+ return lys_parse_path_(ctx, path, format, 1, NULL, NULL, NULL);
+}
+
+API LY_ERR
+lys_search_localfile(const char * const *searchpaths, int cwd, const char *name, const char *revision,
+ char **localfile, LYS_INFORMAT *format)
+{
+ size_t len, flen, match_len = 0, dir_len;
+ int i, implicit_cwd = 0, ret = EXIT_FAILURE;
+ char *wd, *wn = NULL;
+ DIR *dir = NULL;
+ struct dirent *file;
+ char *match_name = NULL;
+ LYS_INFORMAT format_aux, match_format = 0;
+ struct ly_set *dirs;
+ struct stat st;
+
+ LY_CHECK_ARG_RET(NULL, localfile, LY_EINVAL);
+
+ /* start to fill the dir fifo with the context's search path (if set)
+ * and the current working directory */
+ dirs = ly_set_new();
+ if (!dirs) {
+ LOGMEM(NULL);
+ return EXIT_FAILURE;
+ }
+
+ len = strlen(name);
+ if (cwd) {
+ wd = get_current_dir_name();
+ if (!wd) {
+ LOGMEM(NULL);
+ goto cleanup;
+ } else {
+ /* add implicit current working directory (./) to be searched,
+ * this directory is not searched recursively */
+ if (ly_set_add(dirs, wd, 0) == -1) {
+ goto cleanup;
+ }
+ implicit_cwd = 1;
+ }
+ }
+ if (searchpaths) {
+ for (i = 0; searchpaths[i]; i++) {
+ /* check for duplicities with the implicit current working directory */
+ if (implicit_cwd && !strcmp(dirs->objs[0], searchpaths[i])) {
+ implicit_cwd = 0;
+ continue;
+ }
+ wd = strdup(searchpaths[i]);
+ if (!wd) {
+ LOGMEM(NULL);
+ goto cleanup;
+ } else if (ly_set_add(dirs, wd, 0) == -1) {
+ goto cleanup;
+ }
+ }
+ }
+ wd = NULL;
+
+ /* start searching */
+ while (dirs->count) {
+ free(wd);
+ free(wn); wn = NULL;
+
+ dirs->count--;
+ wd = (char *)dirs->objs[dirs->count];
+ dirs->objs[dirs->count] = NULL;
+ LOGVRB("Searching for \"%s\" in %s.", name, wd);
+
+ if (dir) {
+ closedir(dir);
+ }
+ dir = opendir(wd);
+ dir_len = strlen(wd);
+ if (!dir) {
+ LOGWRN(NULL, "Unable to open directory \"%s\" for searching (sub)modules (%s).", wd, strerror(errno));
+ } else {
+ while ((file = readdir(dir))) {
+ if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
+ /* skip . and .. */
+ continue;
+ }
+ free(wn);
+ if (asprintf(&wn, "%s/%s", wd, file->d_name) == -1) {
+ LOGMEM(NULL);
+ goto cleanup;
+ }
+ if (stat(wn, &st) == -1) {
+ LOGWRN(NULL, "Unable to get information about \"%s\" file in \"%s\" when searching for (sub)modules (%s)",
+ file->d_name, wd, strerror(errno));
+ continue;
+ }
+ if (S_ISDIR(st.st_mode) && (dirs->count || !implicit_cwd)) {
+ /* we have another subdirectory in searchpath to explore,
+ * subdirectories are not taken into account in current working dir (dirs->set.g[0]) */
+ if (ly_set_add(dirs, wn, 0) == -1) {
+ goto cleanup;
+ }
+ /* continue with the next item in current directory */
+ wn = NULL;
+ continue;
+ } else if (!S_ISREG(st.st_mode)) {
+ /* not a regular file (note that we see the target of symlinks instead of symlinks */
+ continue;
+ }
+
+ /* here we know that the item is a file which can contain a module */
+ if (strncmp(name, file->d_name, len) ||
+ (file->d_name[len] != '.' && file->d_name[len] != '@')) {
+ /* different filename than the module we search for */
+ continue;
+ }
+
+ /* get type according to filename suffix */
+ flen = strlen(file->d_name);
+ if (!strcmp(&file->d_name[flen - 4], ".yin")) {
+ format_aux = LYS_IN_YIN;
+ } else if (!strcmp(&file->d_name[flen - 5], ".yang")) {
+ format_aux = LYS_IN_YANG;
+ } else {
+ /* not supportde suffix/file format */
+ continue;
+ }
+
+ if (revision) {
+ /* we look for the specific revision, try to get it from the filename */
+ if (file->d_name[len] == '@') {
+ /* check revision from the filename */
+ if (strncmp(revision, &file->d_name[len + 1], strlen(revision))) {
+ /* another revision */
+ continue;
+ } else {
+ /* exact revision */
+ free(match_name);
+ match_name = wn;
+ wn = NULL;
+ match_len = dir_len + 1 + len;
+ match_format = format_aux;
+ goto success;
+ }
+ } else {
+ /* continue trying to find exact revision match, use this only if not found */
+ free(match_name);
+ match_name = wn;
+ wn = NULL;
+ match_len = dir_len + 1 +len;
+ match_format = format_aux;
+ continue;
+ }
+ } else {
+ /* remember the revision and try to find the newest one */
+ if (match_name) {
+ if (file->d_name[len] != '@' ||
+ lysp_check_date(NULL, &file->d_name[len + 1], flen - (format_aux == LYS_IN_YANG ? 5 : 4) - len - 1, NULL)) {
+ continue;
+ } else if (match_name[match_len] == '@' &&
+ (strncmp(&match_name[match_len + 1], &file->d_name[len + 1], LY_REV_SIZE - 1) >= 0)) {
+ continue;
+ }
+ free(match_name);
+ }
+
+ match_name = wn;
+ wn = NULL;
+ match_len = dir_len + 1 + len;
+ match_format = format_aux;
+ continue;
+ }
+ }
+ }
+ }
+
+success:
+ (*localfile) = match_name;
+ match_name = NULL;
+ if (format) {
+ (*format) = match_format;
+ }
+ ret = EXIT_SUCCESS;
+
+cleanup:
+ free(wn);
+ free(wd);
+ if (dir) {
+ closedir(dir);
+ }
+ free(match_name);
+ ly_set_free(dirs, free);
+
+ return ret;
+}
+
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 12394eb..a6ed85e 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -15,8 +15,11 @@
#ifndef LY_TREE_SCHEMA_H_
#define LY_TREE_SCHEMA_H_
+#include <pcre.h>
#include <stdint.h>
+#include "extensions.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -110,7 +113,6 @@
(ELEM) = (ELEM)->next)
/**
- * @ingroup datatree
* @brief Macro to iterate via all sibling elements allowing to modify the list itself (e.g. removing elements)
*
* Use with opening curly bracket (`{`). All parameters must be of the same type.
@@ -154,15 +156,55 @@
#define LYS_LEAFLIST 0x0008 /**< leaf-list statement node */
#define LYS_LIST 0x0010 /**< list statement node */
#define LYS_ANYXML 0x0020 /**< anyxml statement node */
+#define LYS_ANYDATA 0x0120 /**< anydata statement node, in tests it can be used for both #LYS_ANYXML and #LYS_ANYDATA */
+
#define LYS_CASE 0x0040 /**< case statement node */
#define LYS_USES 0x0080 /**< uses statement node */
-#define LYS_ANYDATA 0x0120 /**< anydata statement node, in tests it can be used for both #LYS_ANYXML and #LYS_ANYDATA */
+#define LYS_INOUT 0x200
+#define LYS_ACTION 0x400
+#define LYS_NOTIF 0x800
+#define LYS_GROUPING 0x1000
+#define LYS_AUGMENT 0x2000
+
+/**
+ * @brief YANG built-in types
+ */
+typedef enum {
+ LY_TYPE_UNKNOWN = 0, /**< Unknown type */
+ LY_TYPE_BINARY, /**< Any binary data ([RFC 6020 sec 9.8](http://tools.ietf.org/html/rfc6020#section-9.8)) */
+ LY_TYPE_UINT8, /**< 8-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_UINT16, /**< 16-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_UINT32, /**< 32-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_UINT64, /**< 64-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_STRING, /**< Human-readable string ([RFC 6020 sec 9.4](http://tools.ietf.org/html/rfc6020#section-9.4)) */
+ LY_TYPE_BITS, /**< A set of bits or flags ([RFC 6020 sec 9.7](http://tools.ietf.org/html/rfc6020#section-9.7)) */
+ LY_TYPE_BOOL, /**< "true" or "false" ([RFC 6020 sec 9.5](http://tools.ietf.org/html/rfc6020#section-9.5)) */
+ LY_TYPE_DEC64, /**< 64-bit signed decimal number ([RFC 6020 sec 9.3](http://tools.ietf.org/html/rfc6020#section-9.3))*/
+ LY_TYPE_EMPTY, /**< A leaf that does not have any value ([RFC 6020 sec 9.11](http://tools.ietf.org/html/rfc6020#section-9.11)) */
+ LY_TYPE_ENUM, /**< Enumerated strings ([RFC 6020 sec 9.6](http://tools.ietf.org/html/rfc6020#section-9.6)) */
+ LY_TYPE_IDENT, /**< A reference to an abstract identity ([RFC 6020 sec 9.10](http://tools.ietf.org/html/rfc6020#section-9.10)) */
+ LY_TYPE_INST, /**< References a data tree node ([RFC 6020 sec 9.13](http://tools.ietf.org/html/rfc6020#section-9.13)) */
+ LY_TYPE_LEAFREF, /**< A reference to a leaf instance ([RFC 6020 sec 9.9](http://tools.ietf.org/html/rfc6020#section-9.9))*/
+ LY_TYPE_UNION, /**< Choice of member types ([RFC 6020 sec 9.12](http://tools.ietf.org/html/rfc6020#section-9.12)) */
+ LY_TYPE_INT8, /**< 8-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_INT16, /**< 16-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_INT32, /**< 32-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+ LY_TYPE_INT64, /**< 64-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+} LY_DATA_TYPE;
+#define LY_DATA_TYPE_COUNT 20 /**< Number of different types */
+
+/**
+ * @brief Stringified YANG built-in data types
+ */
+extern const char* ly_data_type2str[LY_DATA_TYPE_COUNT];
/**
* @brief YANG import-stmt
*/
struct lysp_import {
- const char *name; /**< name of the module to import (mandatory) */
+ struct lys_module *module; /**< pointer to the imported module
+ (mandatory, but resolved when the referring module is completely parsed) */
+ const char *name; /**< name of the imported module (mandatory) */
const char *prefix; /**< prefix for the data from the imported schema (mandatory) */
const char *dsc; /**< description */
const char *ref; /**< reference */
@@ -174,7 +216,9 @@
* @brief YANG include-stmt
*/
struct lysp_include {
- const char *name; /**< name of the submodule to include (mandatory) */
+ struct lysp_module *submodule; /**< pointer to the parsed submodule structure
+ (mandatory, but resolved when the referring module is completely parsed) */
+ const char *name; /**< name of the included submodule (mandatory) */
const char *dsc; /**< description */
const char *ref; /**< reference */
struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
@@ -204,56 +248,6 @@
};
/**
- * @brief Enum of substatements in which extension instances can appear.
- */
-typedef enum {
- LYEXT_SUBSTMT_SELF = 0, /**< extension of the structure itself, not substatement's */
- LYEXT_SUBSTMT_ARGUMENT, /**< extension of the argument statement, can appear in lys_ext */
- LYEXT_SUBSTMT_BASE, /**< extension of the base statement, can appear (repeatedly) in lys_type and lys_ident */
- LYEXT_SUBSTMT_BELONGSTO, /**< extension of the belongs-to statement, can appear in lys_submodule */
- LYEXT_SUBSTMT_CONTACT, /**< extension of the contact statement, can appear in lys_module */
- LYEXT_SUBSTMT_DEFAULT, /**< extension of the default statement, can appear in lys_node_leaf, lys_node_leaflist,
- lys_node_choice and lys_deviate */
- LYEXT_SUBSTMT_DESCRIPTION, /**< extension of the description statement, can appear in lys_module, lys_submodule,
- lys_node, lys_import, lys_include, lys_ext, lys_feature, lys_tpdf, lys_restr,
- lys_ident, lys_deviation, lys_type_enum, lys_type_bit, lys_when and lys_revision */
- LYEXT_SUBSTMT_ERRTAG, /**< extension of the error-app-tag statement, can appear in lys_restr */
- LYEXT_SUBSTMT_ERRMSG, /**< extension of the error-message statement, can appear in lys_restr */
- LYEXT_SUBSTMT_KEY, /**< extension of the key statement, can appear in lys_node_list */
- LYEXT_SUBSTMT_NAMESPACE, /**< extension of the namespace statement, can appear in lys_module */
- LYEXT_SUBSTMT_ORGANIZATION, /**< extension of the organization statement, can appear in lys_module and lys_submodule */
- LYEXT_SUBSTMT_PATH, /**< extension of the path statement, can appear in lys_type */
- LYEXT_SUBSTMT_PREFIX, /**< extension of the prefix statement, can appear in lys_module, lys_submodule (for
- belongs-to's prefix) and lys_import */
- LYEXT_SUBSTMT_PRESENCE, /**< extension of the presence statement, can appear in lys_node_container */
- LYEXT_SUBSTMT_REFERENCE, /**< extension of the reference statement, can appear in lys_module, lys_submodule,
- lys_node, lys_import, lys_include, lys_revision, lys_tpdf, lys_restr, lys_ident,
- lys_ext, lys_feature, lys_deviation, lys_type_enum, lys_type_bit and lys_when */
- LYEXT_SUBSTMT_REVISIONDATE, /**< extension of the revision-date statement, can appear in lys_import and lys_include */
- LYEXT_SUBSTMT_UNITS, /**< extension of the units statement, can appear in lys_tpdf, lys_node_leaf,
- lys_node_leaflist and lys_deviate */
- LYEXT_SUBSTMT_VALUE, /**< extension of the value statement, can appear in lys_type_enum */
- LYEXT_SUBSTMT_VERSION, /**< extension of the yang-version statement, can appear in lys_module and lys_submodule */
- LYEXT_SUBSTMT_MODIFIER, /**< extension of the modifier statement, can appear in lys_restr */
- LYEXT_SUBSTMT_REQINSTANCE, /**< extension of the require-instance statement, can appear in lys_type */
- LYEXT_SUBSTMT_YINELEM, /**< extension of the yin-element statement, can appear in lys_ext */
- LYEXT_SUBSTMT_CONFIG, /**< extension of the config statement, can appear in lys_node and lys_deviate */
- LYEXT_SUBSTMT_MANDATORY, /**< extension of the mandatory statement, can appear in lys_node_leaf, lys_node_choice,
- lys_node_anydata and lys_deviate */
- LYEXT_SUBSTMT_ORDEREDBY, /**< extension of the ordered-by statement, can appear in lys_node_list and lys_node_leaflist */
- LYEXT_SUBSTMT_STATUS, /**< extension of the status statement, can appear in lys_tpdf, lys_node, lys_ident,
- lys_ext, lys_feature, lys_type_enum and lys_type_bit */
- LYEXT_SUBSTMT_FRACDIGITS, /**< extension of the fraction-digits statement, can appear in lys_type */
- LYEXT_SUBSTMT_MAX, /**< extension of the max-elements statement, can appear in lys_node_list,
- lys_node_leaflist and lys_deviate */
- LYEXT_SUBSTMT_MIN, /**< extension of the min-elements statement, can appear in lys_node_list,
- lys_node_leaflist and lys_deviate */
- LYEXT_SUBSTMT_POSITION, /**< extension of the position statement, can appear in lys_type_bit */
- LYEXT_SUBSTMT_UNIQUE, /**< extension of the unique statement, can appear in lys_node_list and lys_deviate */
- LYEXT_SUBSTMT_IFFEATURE, /**< extension of the if-feature statement */
-} LYEXT_SUBSTMT;
-
-/**
* @brief YANG extension instance
*/
struct lysp_ext_instance {
@@ -308,7 +302,7 @@
* @brief YANG revision-stmt
*/
struct lysp_revision {
- char rev[LY_REV_SIZE]; /**< revision date (madatory) */
+ char date[LY_REV_SIZE]; /**< revision date (madatory) */
const char *dsc; /**< description statement */
const char *ref; /**< reference statement */
struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
@@ -345,9 +339,11 @@
struct lysp_type *types; /**< list of sub-types ([sized array](@ref sizedarrays)) - union */
struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysc_type *compiled; /**< pointer to the compiled type */
+
uint8_t fraction_digits; /**< number of fraction digits - decimal64 */
uint8_t require_instance; /**< require-instance flag - leafref, instance */
- uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_SET_REQINST allowed */
+ uint16_t flags; /**< [schema node flags](@ref spnodeflags) */
};
/**
@@ -361,13 +357,16 @@
const char *ref; /**< reference statement */
struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
struct lysp_type type; /**< base type from which the typedef is derived (mandatory) */
- uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values allowed */
+ uint16_t flags; /**< [schema node flags](@ref spnodeflags) */
};
/**
* @brief YANG grouping-stmt
*/
struct lysp_grp {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level grouping) */
+ uint16_t nodetype; /**< LYS_GROUPING */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
const char *name; /**< grouping name (mandatory) */
const char *dsc; /**< description statement */
const char *ref; /**< reference statement */
@@ -377,7 +376,6 @@
struct lysp_action *actions; /**< list of actions ([sized array](@ref sizedarrays)) */
struct lysp_notif *notifs; /**< list of notifications ([sized array](@ref sizedarrays)) */
struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
};
/**
@@ -411,6 +409,9 @@
* @brief YANG uses-augment-stmt and augment-stmt
*/
struct lysp_augment {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level augment) */
+ uint16_t nodetype; /**< LYS_AUGMENT */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
const char *nodeid; /**< target schema nodeid (mandatory) - absolute for global augments, descendant for uses's augments */
const char *dsc; /**< description statement */
const char *ref; /**< reference statement */
@@ -420,7 +421,6 @@
struct lysp_action *actions; /**< list of actions ([sized array](@ref sizedarrays)) */
struct lysp_notif *notifs; /**< list of notifications ([sized array](@ref sizedarrays)) */
struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
};
/**
@@ -486,43 +486,160 @@
struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
};
+/**
+ * @defgroup spnodeflags Parsed schema nodes flags
+ * @ingroup snodeflags
+ *
+ * Various flags for parsed schema nodes.
+ *
+ * 1 - container 6 - anydata/anyxml 11 - output 16 - grouping 21 - enum
+ * 2 - choice 7 - case 12 - feature 17 - uses 22 - type
+ * 3 - leaf 8 - notification 13 - identity 18 - refine
+ * 4 - leaflist 9 - rpc 14 - extension 19 - augment
+ * 5 - list 10 - input 15 - typedef 20 - deviate
+ *
+ * 1 1 1 1 1 1 1 1 1 1 2 2 2
+ * bit name 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
+ * ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 1 LYS_CONFIG_W |x|x|x|x|x|x|x| | | | | | | | | | |x| |x| | |
+ * LYS_SET_BASE | | | | | | | | | | | | | | | | | | | | | |x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 2 LYS_CONFIG_R |x|x|x|x|x|x|x| | | | | | | | | | |x| |x| | |
+ * LYS_SET_BIT | | | | | | | | | | | | | | | | | | | | | |x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 3 LYS_STATUS_CURR |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| |
+ * LYS_SET_ENUM | | | | | | | | | | | | | | | | | | | | | |x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| |
+ * LYS_SET_FRDIGITS | | | | | | | | | | | | | | | | | | | | | |x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| |
+ * 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| | | | | | | | |
+ * LYS_SET_RANGE | | | | | | | | | | | | | | | | | | | | | |x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 9 LYS_YINELEM_FALSE| | | | | | | | | | | | | |x| | | | | | | | |
+ * LYS_SET_TYPE | | | | | | | | | | | | | | | | | | | | | |x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 10 LYS_SET_VALUE | | | | | | | | | | | | | | | | | | | | |x| |
+ * LYS_SET_REQINST | | | | | | | | | | | | | | | | | | | | | |x|
+ * LYS_SET_MIN | | | |x|x| | | | | | | | | | | | |x| |x| | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 11 LYS_SET_MAX | | | |x|x| | | | | | | | | | | | |x| |x| | |
+ * ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+/**
+ * @defgroup scnodeflags Compiled schema nodes flags
+ * @ingroup snodeflags
+ *
+ * Various flags for compiled schema nodes.
+ *
+ * 1 - container 6 - anydata/anyxml 11 - output
+ * 2 - choice 7 - case 12 - feature
+ * 3 - leaf 8 - notification 13 - identity
+ * 4 - leaflist 9 - rpc 14 - extension
+ * 5 - list 10 - input
+ *
+ * 1 1 1 1 1
+ * bit name 1 2 3 4 5 6 7 8 9 0 1 2 3 4
+ * ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 1 LYS_CONFIG_W |x|x|x|x|x|x|x| | |x| | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 2 LYS_CONFIG_R |x|x|x|x|x|x|x| | | |x| | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 3 LYS_STATUS_CURR |x|x|x|x|x|x|x|x|x| | |x|x|x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x| | |x|x|x|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 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| | | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 7 LYS_ORDBY_USER | | | |x|x| | | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 8 LYS_PRESENCE |x| | | | | | | | | | | | | |
+ * LYS_UNIQUE | | |x| | | | | | | | | | | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 9 LYS_KEY | | |x| | | | | | | | | | | |
+ * LYS_FENABLED | | | | | | | | | | | |x| | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 10 LYS_SET_DFLT | | |x| | | |x| | | | | | | |
+ * ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+/**
+ * @defgroup snodeflags Schema nodes flags
+ * @ingroup schematree
+ * @{
+ */
#define LYS_CONFIG_W 0x01 /**< config true; */
#define LYS_CONFIG_R 0x02 /**< config false; */
#define LYS_CONFIG_MASK 0x03 /**< mask for config value */
-#define LYS_STATUS_CURR 0x08 /**< status current; */
-#define LYS_STATUS_DEPRC 0x10 /**< status deprecated; */
-#define LYS_STATUS_OBSLT 0x20 /**< status obsolete; */
-#define LYS_STATUS_MASK 0x38 /**< mask for status value */
-#define LYS_MAND_TRUE 0x40 /**< mandatory true; applicable only to
+#define LYS_STATUS_CURR 0x04 /**< status current; */
+#define LYS_STATUS_DEPRC 0x08 /**< status deprecated; */
+#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 */
+#define LYS_MAND_FALSE 0x40 /**< mandatory false; applicable only to
::lysp_node_choice/::lysc_node_choice, ::lysp_node_leaf/::lysc_node_leaf
and ::lysp_node_anydata/::lysc_node_anydata */
-#define LYS_MAND_FALSE 0x80 /**< mandatory false; applicable only to
- ::lysp_node_choice/::lysc_node_choice, ::lysp_node_leaf/::lysc_node_leaf
- and ::lysp_node_anydata/::lysc_node_anydata */
-#define LYS_MAND_MASK 0xc0 /**< mask for mandatory values */
-#define LYS_ORDBY_SYSTEM 0x100 /**< ordered-by system lists, applicable only to
- ::lysp_node_list/lysc_node_list and ::lysp_node_leaflist/::lysc_node_list */
-#define LYS_ORDBY_USER 0x200 /**< ordered-by user lists, applicable only to
- ::lysp_node_list/lysc_node_list and ::lysp_node_leaflist/::lysc_node_list */
-#define LYS_ORDBY_MASK 0x300 /**< mask for ordered-by flags */
-#define LYS_FENABLED 0x100 /**< feature enabled flag, applicable only to ::lysp_feature/::lysc_feature */
-#define LYS_AUTOASSIGNED 0x01 /**< value was auto-assigned, applicable only to
- ::lysp_type/::lysc_type enum and bits flags */
-#define LYS_YINELEM_TRUE 0x01 /**< yin-element true for extension's argument */
-#define LYS_YINELEM_FALSE 0x02 /**< yin-element false for extension's argument */
-#define LYS_YINELEM_MASK 0x03 /**< mask for yin-element value */
-#define LYS_SET_VALUE 0x01 /**< value attribute is set */
+#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
+ ::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 */
+#define LYS_ORDBY_MASK 0x60 /**< mask for ordered-by values */
+#define LYS_YINELEM_TRUE 0x80 /**< yin-element true for extension's argument */
+#define LYS_YINELEM_FALSE 0x100 /**< yin-element false for extension's argument */
+#define LYS_YINELEM_MASK 0x180 /**< mask for yin-element value */
+#define LYS_SET_VALUE 0x200 /**< value attribute is set */
+#define LYS_SET_MIN 0x200 /**< min attribute is set */
#define LYS_SET_MAX 0x400 /**< max attribute is set */
-#define LYS_SET_MIN 0x800 /**< min attribute is set */
-#define LYS_SET_REQINST 0x01 /**< require_instance attribute is set */
+
+#define LYS_SET_BASE 0x0001 /**< type's flag for present base substatement */
+#define LYS_SET_BIT 0x0002 /**< type's flag for present bit substatement */
+#define LYS_SET_ENUM 0x0004 /**< type's flag for present enum substatement */
+#define LYS_SET_FRDIGITS 0x0008 /**< type's flag for present fraction-digits substatement */
+#define LYS_SET_LENGTH 0x0010 /**< type's flag for present length substatement */
+#define LYS_SET_PATH 0x0020 /**< type's flag for present path substatement */
+#define LYS_SET_PATTERN 0x0040 /**< type's flag for present pattern substatement */
+#define LYS_SET_RANGE 0x0080 /**< type's flag for present range substatement */
+#define LYS_SET_TYPE 0x0100 /**< type's flag for present type substatement */
+#define LYS_SET_REQINST 0x0200 /**< type's flag for present require-instance substatement */
+#define LYS_SET_DFLT 0x0200 /**< flag to mark leaf with own default value, not a default value taken from its type, and default
+ cases of choice. This information is important for refines, since it is prohibited to make leafs
+ 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 */
+/** @} */
/**
* @brief Generic YANG data node
*/
struct lysp_node {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
uint16_t nodetype; /**< type of the node (mandatory) */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
- struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ struct lysp_node *next; /**< next sibling node (NULL if there is no one) */
const char *name; /**< node name (mandatory) */
const char *dsc; /**< description statement */
const char *ref; /**< reference statement */
@@ -535,6 +652,7 @@
* @brief Extension structure of the lysp_node for YANG container
*/
struct lysp_node_container {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
uint16_t nodetype; /**< LYS_CONTAINER */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
@@ -556,6 +674,7 @@
};
struct lysp_node_leaf {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
uint16_t nodetype; /**< LYS_LEAF */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
@@ -574,6 +693,7 @@
};
struct lysp_node_leaflist {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
uint16_t nodetype; /**< LYS_LEAFLIST */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
@@ -594,6 +714,7 @@
};
struct lysp_node_list {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
uint16_t nodetype; /**< LYS_LIST */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
@@ -618,6 +739,7 @@
};
struct lysp_node_choice {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
uint16_t nodetype; /**< LYS_CHOICE */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
@@ -634,6 +756,7 @@
};
struct lysp_node_case {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
uint16_t nodetype; /**< LYS_CASE */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
@@ -649,6 +772,7 @@
};
struct lysp_node_anydata {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
uint16_t nodetype; /**< LYS_ANYXML || LYS_ANYDATA */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
@@ -664,6 +788,7 @@
};
struct lysp_node_uses {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
uint16_t nodetype; /**< LYS_USES */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
struct lysp_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
@@ -683,6 +808,8 @@
* @brief YANG input-stmt and output-stmt
*/
struct lysp_action_inout {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_INOUT */
struct lysp_restr *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
struct lysp_tpdf *typedefs; /**< list of typedefs ([sized array](@ref sizedarrays)) */
struct lysp_grp *groupings; /**< list of groupings ([sized array](@ref sizedarrays)) */
@@ -694,6 +821,9 @@
* @brief YANG rpc-stmt and action-stmt
*/
struct lysp_action {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_ACTION */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
const char *name; /**< grouping name reference (mandatory) */
const char *dsc; /**< description statement */
const char *ref; /**< reference statement */
@@ -703,13 +833,15 @@
struct lysp_action_inout *input; /**< RPC's/Action's input */
struct lysp_action_inout *output;/**< RPC's/Action's output */
struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- uint16_t flags; /**< [schema node flags](@ref snodeflags) */
};
/**
* @brief YANG notification-stmt
*/
struct lysp_notif {
+ struct lysp_node *parent; /**< parent node (NULL if this is a top-level node) */
+ uint16_t nodetype; /**< LYS_NOTIF */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
const char *name; /**< grouping name reference (mandatory) */
const char *dsc; /**< description statement */
const char *ref; /**< reference statement */
@@ -719,7 +851,6 @@
struct lysp_grp *groupings; /**< list of groupings ([sized array](@ref sizedarrays)) */
struct lysp_node *data; /**< list of data nodes (linked list) */
struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
};
/**
@@ -747,14 +878,14 @@
const char *belongsto; /**< belongs to parent module (submodule - mandatory) */
};
const char *prefix; /**< module prefix or submodule belongsto prefix of main module (mandatory) */
+ struct lysp_revision *revs; /**< list of the module revisions ([sized array](@ref sizedarrays)), the first revision
+ in the list is always the last (newest) revision of the module */
struct lysp_import *imports; /**< list of imported modules ([sized array](@ref sizedarrays)) */
struct lysp_include *includes; /**< list of included submodules ([sized array](@ref sizedarrays)) */
const char *org; /**< party/company responsible for the module */
const char *contact; /**< contact information for the module */
const char *dsc; /**< description of the module */
const char *ref; /**< cross-reference for the module */
- struct lysp_revision *revs; /**< list of the module revisions ([sized array](@ref sizedarrays)), the first revision
- in the list is always the last (newest) revision of the module */
struct lysp_ext *extensions; /**< list of extension statements ([sized array](@ref sizedarrays)) */
struct lysp_feature *features; /**< list of feature definitions ([sized array](@ref sizedarrays)) */
struct lysp_ident *identities; /**< list of identities ([sized array](@ref sizedarrays)) */
@@ -769,9 +900,12 @@
uint8_t submodule:1; /**< flag to distinguish main modules and submodules */
uint8_t implemented:1; /**< flag if the module is implemented, not just imported */
- uint8_t latest_revision:1; /**< flag if the module was loaded without specific revision and is
- the latest revision found */
- uint8_t version:4; /**< yang-version (LYS_VERSION values) */
+ uint8_t latest_revision:2; /**< 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 */
+ uint8_t parsing:1; /**< flag for circular check */
+ uint8_t version; /**< yang-version (LYS_VERSION values) */
};
/**
@@ -782,12 +916,42 @@
void lysp_module_free(struct lysp_module *module);
/**
+ * @brief YANG extension instance
+ */
+struct lysc_ext_instance {
+ struct lyext_plugin *plugin; /**< pointer to the plugin implementing the extension (if present) */
+ void *parent; /**< pointer to the parent element holding the extension instance(s), use
+ ::lysc_ext_instance#parent_type to access the schema element */
+ const char *argument; /**< optional value of the extension's argument */
+ LYEXT_SUBSTMT insubstmt; /**< value identifying placement of the extension instance */
+ uint32_t insubstmt_index; /**< in case the instance is in a substatement that can appear multiple times,
+ this identifies the index of the substatement for this extension instance */
+ LYEXT_PARENT parent_type; /**< type of the parent structure */
+#if 0
+ uint8_t ext_type; /**< extension type (#LYEXT_TYPE) */
+ uint8_t padding; /**< 32b padding */
+ struct lys_module *module; /**< pointer to the extension instance's module (mandatory) */
+ LYS_NODE nodetype; /**< LYS_EXT */
+#endif
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ void *priv; /**< private caller's data, not used by libyang */
+};
+
+/**
+ * @brief Compiled YANG if-feature-stmt
+ */
+struct lysc_iffeature {
+ uint8_t *expr; /**< 2bits array describing the if-feature expression in prefix format, see @ref ifftokens */
+ struct lysc_feature **features; /**< array of pointers to the features used in expression ([sized array](@ref sizedarrays)) */
+};
+
+/**
* @brief YANG import-stmt
*/
struct lysc_import {
- struct lysc_module *module; /**< link to the imported module */
+ struct lys_module *module; /**< link to the imported module */
const char *prefix; /**< prefix for the data from the imported schema (mandatory) */
- struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
};
/**
@@ -795,17 +959,29 @@
*/
struct lysc_when {
struct lyxp_expr *cond; /**< XPath when condition */
+ struct lysc_node *context; /**< context node of the expression */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
};
/**
+ * @brief YANG identity-stmt
+ */
+struct lysc_ident {
+ const char *name; /**< identity name (mandatory), including possible prefix */
+ struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysc_ident **derived; /**< list of (pointers to the) derived identities ([sized array](@ref sizedarrays)) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ values are allowed */
+};
+
+/**
* @brief YANG feature-stmt
*/
struct lysc_feature {
const char *name; /**< feature name (mandatory) */
struct lysc_feature **depfeatures;/**< list of pointers to other features depending on this one ([sized array](@ref sizedarrays)) */
struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
- struct lysp_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
uint16_t flags; /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* and
#LYS_FENABLED values allowed */
};
@@ -825,11 +1001,156 @@
*/
/**
- * @brief Compiled YANG if-feature-stmt
+ * @brief Compiled YANG revision statement
*/
-struct lysc_iffeature {
- uint8_t *expr; /**< 2bits array describing the if-feature expression in prefix format, see @ref ifftokens */
- struct lysc_feature **features; /**< array of pointers to the features used in expression ([sized array](@ref sizedarrays)) */
+struct lysc_revision {
+ char date[LY_REV_SIZE]; /**< revision-date (mandatory) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_default {
+ struct lys_module *module; /**< module where the default was defined */
+ const char *value; /**< default value (with possible prefixes from the module where defined) */
+};
+
+struct lysc_range {
+ struct lysc_range_part {
+ union { /**< min boundary TODO decimal */
+ int64_t min_64; /**< for int8, int16, int32 and int64 */
+ uint64_t min_u64; /**< for uint8, uint16, uint32 and uint64 */
+ };
+ union { /**< max boundary TODO decimal */
+ int64_t max_64; /**< for int8, int16, int32 and int64 */
+ uint64_t max_u64; /**< for uint8, uint16, uint32 and uint64 */
+ };
+ } *parts; /**< compiled range expression ([sized array](@ref sizedarrays)) */
+ const char *emsg; /**< error-message */
+ const char *eapptag; /**< error-app-tag value */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_pattern {
+ pcre *expr; /**< compiled regular expression */
+ pcre_extra *expr_extra; /**< additional information to speed up matching */
+ const char *emsg; /**< error-message */
+ const char *eapptag; /**< error-app-tag value */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint32_t inverted:1; /**< invert-match flag */
+ uint32_t refcount:31; /**< reference counter */
+};
+
+struct lysc_must {
+ struct lys_module *module; /**< module where the must was defined */
+ struct lyxp_expr *cond; /**< XPath when condition */
+ const char *emsg; /**< error-message */
+ const char *eapptag; /**< error-app-tag value */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_type {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+};
+
+struct lysc_type_num {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_range *range; /**< Optional range limitation */
+};
+
+struct lysc_type_dec {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ uint8_t fraction_digits; /**< fraction digits specification */
+ struct lysc_range *range; /**< Optional range limitation */
+};
+
+struct lysc_type_str {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_range *length; /**< Optional length limitation */
+ struct lysc_pattern **patterns; /**< Optional list of pointers to pattern limitations ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_type_enum {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_type_enum_item {
+ const char *name; /**< enumeration identifier */
+ struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ int32_t value; /**< integer value associated with the enumeration */
+ } *enums; /**< enumerations list ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
+};
+
+struct lysc_type_leafref {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ const char* path; /**< target path */
+ struct lys_module *path_context; /**< module where the path is defined, so it provides context to resolve prefixes */
+ struct lysc_type *realtype; /**< pointer to the real (first non-leafref in possible leafrefs chain) type. */
+ uint8_t require_instance; /**< require-instance flag */
+};
+
+struct lysc_type_identityref {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_ident **bases; /**< list of pointers to the base identities ([sized array](@ref sizedarrays)),
+ mandatory (at least 1 item) */
+};
+
+struct lysc_type_instanceid {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ uint8_t require_instance; /**< require-instance flag */
+};
+
+struct lysc_type_bits {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_type_bits_item {
+ const char *name; /**< bit identifier */
+ struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ uint32_t position; /**< non-negative integer value associated with the bit */
+ } *bits; /**< bits list ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
+};
+
+struct lysc_type_union {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_type **types; /**< list of types in the union ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
+};
+
+struct lysc_type_bin {
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ LY_DATA_TYPE basetype; /**< Base type of the type */
+ uint32_t refcount; /**< reference counter for type sharing */
+ struct lysc_range *length; /**< Optional length limitation */
+};
+
+struct lysc_action {
+ uint16_t nodetype; /**< LYS_ACTION */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ const char *name; /**< action/RPC name (mandatory) */
+ /* TODO */
+};
+
+struct lysc_notif {
+ uint16_t nodetype; /**< LYS_NOTIF */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ const char *name; /**< Notification name (mandatory) */
+ /* TODO */
};
/**
@@ -838,12 +1159,179 @@
struct lysc_node {
uint16_t nodetype; /**< type of the node (mandatory) */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
- struct lysp_node *sp; /**< link to the simply parsed (SP) original of the node, NULL if the SP schema was removed. */
- struct lysc_node *next; /**< pointer to the next sibling node (NULL if there is no one) */
+ struct lys_module *module; /**< module structure */
+ struct lysp_node *sp; /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
const char *name; /**< node name (mandatory) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
struct lysc_when *when; /**< when statement */
struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_node_container {
+ uint16_t nodetype; /**< LYS_CONTAINER */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lys_module *module; /**< module structure */
+ struct lysp_node *sp; /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysc_when *when; /**< when statement */
+ struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+
+ struct lysc_node *child; /**< first child node (linked list) */
+ struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysc_action *actions; /**< list of actions ([sized array](@ref sizedarrays)) */
+ struct lysc_notif *notifs; /**< list of notifications ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_node_case {
+ uint16_t nodetype; /**< LYS_CASE */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lys_module *module; /**< module structure */
+ struct lysp_node *sp; /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< name of the case, including the implicit case */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysc_when *when; /**< when statement */
+ 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
+ each other as siblings with the parent pointer pointing to the choice node holding the case. To distinguish
+ which children node belongs to which case, it is needed to match the first children of the cases while going
+ through the children linked list. */
+};
+
+struct lysc_node_choice {
+ uint16_t nodetype; /**< LYS_CHOICE */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lys_module *module; /**< module structure */
+ struct lysp_node *sp; /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysc_when *when; /**< when statement */
+ 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
+ as siblings. Their parent pointers points to the specific case they belongs to, so distinguish the
+ case is simple. */
+ struct lysc_node_case *dflt; /**< default case of the choice, only a pointer into the cases array. */
+};
+
+struct lysc_node_leaf {
+ uint16_t nodetype; /**< LYS_LEAF */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lys_module *module; /**< module structure */
+ struct lysp_node *sp; /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysc_when *when; /**< when statement */
+ 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)) */
+ struct lysc_type *type; /**< type of the leaf node (mandatory) */
+
+ const char *units; /**< units of the leaf's type */
+ const char *dflt; /**< default value */
+};
+
+struct lysc_node_leaflist {
+ uint16_t nodetype; /**< LYS_LEAFLIST */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lys_module *module; /**< module structure */
+ struct lysp_node *sp; /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysc_when *when; /**< when statement */
+ 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)) */
+ struct lysc_type *type; /**< type of the leaf node (mandatory) */
+
+ const char *units; /**< units of the leaf's type */
+ const char **dflts; /**< list of default values ([sized array](@ref sizedarrays)) */
+ uint32_t min; /**< min-elements constraint */
+ uint32_t max; /**< max-elements constraint */
+
+};
+
+struct lysc_node_list {
+ uint16_t nodetype; /**< LYS_LIST */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lys_module *module; /**< module structure */
+ struct lysp_node *sp; /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysc_when *when; /**< when statement */
+ struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+
+ struct lysc_node *child; /**< first child node (linked list) */
+ struct lysc_must *musts; /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+ struct lysc_action *actions; /**< list of actions ([sized array](@ref sizedarrays)) */
+ struct lysc_notif *notifs; /**< list of notifications ([sized array](@ref sizedarrays)) */
+
+ struct lysc_node_leaf **keys; /**< list of pointers to the keys ([sized array](@ref sizedarrays)) */
+ struct lysc_node_leaf ***uniques; /**< list of sized arrays of pointers to the unique nodes ([sized array](@ref sizedarrays)) */
+ uint32_t min; /**< min-elements constraint */
+ uint32_t max; /**< max-elements constraint */
+};
+
+struct lysc_node_anydata {
+ uint16_t nodetype; /**< LYS_ANYXML or LYS_ANYDATA */
+ uint16_t flags; /**< [schema node flags](@ref snodeflags) */
+ struct lys_module *module; /**< module structure */
+ struct lysp_node *sp; /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
+ struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
+ struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
+ struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
+ never NULL. If there is no sibling node, pointer points to the node
+ itself. In case of the first node, this pointer points to the last
+ node in the list. */
+ const char *name; /**< node name (mandatory) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lysc_when *when; /**< when statement */
+ 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)) */
};
/**
@@ -855,21 +1343,92 @@
struct lysc_module {
struct ly_ctx *ctx; /**< libyang context of the module (mandatory) */
const char *name; /**< name of the module (mandatory) */
+ const char *filepath; /**< path, if the schema was read from a file, NULL in case of reading from memory */
const char *ns; /**< namespace of the module (mandatory) */
const char *prefix; /**< module prefix (mandatory) */
+ const char *revision; /**< the revision of the module */
struct lysc_import *imports; /**< list of imported modules ([sized array](@ref sizedarrays)) */
-
struct lysc_feature *features; /**< list of feature definitions ([sized array](@ref sizedarrays)) */
-
+ struct lysc_ident *identities; /**< list of identities ([sized array](@ref sizedarrays)) */
+ struct lysc_node *data; /**< list of module's top-level data nodes (linked list) */
+ struct lysc_action *rpcs; /**< list of RPCs ([sized array](@ref sizedarrays)) */
+ struct lysc_notif *notifs; /**< list of notifications ([sized array](@ref sizedarrays)) */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
uint8_t implemented:1; /**< flag if the module is implemented, not just imported */
- uint8_t latest_revision:1; /**< flag if the module was loaded without specific revision and is
- the latest revision found */
- uint8_t version:4; /**< yang-version (LYS_VERSION values) */
+ uint8_t latest_revision:2; /**< 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 */
+ uint8_t version; /**< yang-version (LYS_VERSION values) */
};
/**
+ * @brief Get the groupings sized array of the given (parsed) schema node.
+ * Decides the node's type and in case it has a groupings array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's groupings sized array if any, NULL otherwise.
+ */
+const struct lysp_grp *lysp_node_groupings(const struct lysp_node *node);
+
+/**
+ * @brief Get the typedefs sized array of the given (parsed) schema node.
+ * Decides the node's type and in case it has a typedefs array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's typedefs sized array if any, NULL otherwise.
+ */
+const struct lysp_tpdf *lysp_node_typedefs(const struct lysp_node *node);
+
+/**
+ * @brief Get the actions/RPCs sized array of the given (parsed) schema node.
+ * Decides the node's type and in case it has a actions/RPCs array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's actions/RPCs sized array if any, NULL otherwise.
+ */
+const struct lysp_action *lysp_node_actions(const struct lysp_node *node);
+
+/**
+ * @brief Get the Notifications sized array of the given (parsed) schema node.
+ * Decides the node's type and in case it has a Notifications array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's Notifications sized array if any, NULL otherwise.
+ */
+const struct lysp_notif *lysp_node_notifs(const struct lysp_node *node);
+
+/**
+ * @brief Get the children linked list of the given (parsed) schema node.
+ * Decides the node's type and in case it has a children list, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's children linked list if any, NULL otherwise.
+ */
+const struct lysp_node *lysp_node_children(const struct lysp_node *node);
+
+/**
+ * @brief Get the actions/RPCs sized array of the given (compiled) schema node.
+ * Decides the node's type and in case it has a actions/RPCs array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's actions/RPCs sized array if any, NULL otherwise.
+ */
+const struct lysc_action *lysc_node_actions(const struct lysc_node *node);
+
+/**
+ * @brief Get the Notifications sized array of the given (compiled) schema node.
+ * Decides the node's type and in case it has a Notifications array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's Notifications sized array if any, NULL otherwise.
+ */
+const struct lysc_notif *lysc_node_notifs(const struct lysc_node *node);
+
+/**
+ * @brief Get the children linked list of the given (compiled) schema node.
+ * Decides the node's type and in case it has a children list, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's children linked list if any, NULL otherwise.
+ */
+const struct lysc_node *lysc_node_children(const struct lysc_node *node);
+
+/**
* @brief Get how the if-feature statement currently evaluates.
*
* @param[in] iff Compiled if-feature statement to evaluate.
@@ -901,6 +1460,14 @@
*
* By default, when the module is loaded by libyang parser, all features are disabled.
*
+ * If all features are being enabled, it must be possible respecting their if-feature conditions. For example,
+ * enabling all features on the following feature set will fail since it is not possible to enable both features
+ * (and it is unclear which of them should be enabled then). In this case the LY_EDENIED is returned and the feature
+ * is untouched.
+ *
+ * feature f1;
+ * feature f2 { if-feature 'not f1';}
+ *
* @param[in] module Module where the feature will be enabled.
* @param[in] feature Name of the feature to enable. To enable all features at once, use asterisk (`*`) character.
* @return LY_ERR value.
@@ -939,7 +1506,7 @@
* @param[in] format Format of the input data (YANG or YIN).
* @return Pointer to the data model structure or NULL on error.
*/
-const struct lys_module *lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format);
+struct lys_module *lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format);
/**
* @brief Read a schema from file descriptor into the specified context.
@@ -952,17 +1519,34 @@
* @param[in] format Format of the input data (YANG or YIN).
* @return Pointer to the data model structure or NULL on error.
*/
-const struct lys_module *lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format);
+struct lys_module *lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format);
/**
- * @brief Load a schema into the specified context from a file.
+ * @brief Read a schema into the specified context from a file.
*
* @param[in] ctx libyang context where to process the data model.
* @param[in] path Path to the file with the model in the specified format.
* @param[in] format Format of the input data (YANG or YIN).
* @return Pointer to the data model structure or NULL on error.
*/
-const struct lys_module *lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format);
+struct lys_module *lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format);
+
+/**
+ * @brief Search for the schema file in the specified searchpaths.
+ *
+ * @param[in] searchpaths NULL-terminated array of paths to be searched (recursively). Current working
+ * directory is searched automatically (but non-recursively if not in the provided list). Caller can use
+ * result of the ly_ctx_get_searchdirs().
+ * @param[in] cwd Flag to implicitly search also in the current working directory (non-recursively).
+ * @param[in] name Name of the schema to find.
+ * @param[in] revision Revision of the schema to find. If NULL, the newest found schema filepath is returned.
+ * @param[out] localfile Mandatory output variable containing absolute path of the found schema. If no schema
+ * complying the provided restriction is found, NULL is set.
+ * @param[out] format Optional output variable containing expected format of the schema document according to the
+ * file suffix.
+ * @return LY_ERR value (LY_SUCCESS is returned even if the file is not found, then the *localfile is NULL).
+ */
+LY_ERR lys_search_localfile(const char * const *searchpaths, int cwd, const char *name, const char *revision, char **localfile, LYS_INFORMAT *format);
/**
* @defgroup scflags Schema compile flags
@@ -971,20 +1555,81 @@
* @{
*/
#define LYSC_OPT_FREE_SP 1 /**< Free the input printable schema */
-
-/**
- * @}
- */
+/** @} scflags */
/**
* @brief Compile printable schema into a validated schema linking all the references.
*
- * @param[in] sp Simple parsed printable schema to compile. Can be changed according to the provided options.
+ * @param[in, out] mod Schema structure holding pointers to both schema structure types. The ::lys_module#parsed
+ * member is used as input and ::lys_module#compiled is used to hold the result of the compilation.
* @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
- * @param[out] sc Resulting compiled schema structure.
* @return LY_ERR value.
*/
-LY_ERR lys_compile(struct lysp_module *sp, int options, struct lysc_module **sc);
+LY_ERR lys_compile(struct lys_module *mod, int options);
+
+/**
+ * @brief Get next schema tree (sibling) node element that can be instantiated in a data tree. Returned node can
+ * be from an augment.
+ *
+ * lys_getnext() is supposed to be called sequentially. In the first call, the \p last parameter is usually NULL
+ * and function starts returning i) the first \p parent's child or ii) the first top level element of the \p module.
+ * Consequent calls suppose to provide the previously returned node as the \p last parameter and still the same
+ * \p parent and \p module parameters.
+ *
+ * Without options, the function is used to traverse only the schema nodes that can be paired with corresponding
+ * data nodes in a data tree. By setting some \p options the behavior can be modified to the extent that
+ * all the schema nodes are iteratively returned.
+ *
+ * @param[in] last Previously returned schema tree node, or NULL in case of the first call.
+ * @param[in] parent Parent of the subtree where the function starts processing.
+ * @param[in] module In case of iterating on top level elements, the \p parent is NULL and
+ * module must be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Next schema tree node that can be instantiated in a data tree, NULL in case there is no such element.
+ */
+const struct lysc_node *lys_getnext(const struct lysc_node *last, const struct lysc_node *parent,
+ const struct lysc_module *module, int options);
+
+/**
+ * @defgroup sgetnextflags lys_getnext() flags
+ * @ingroup schematree
+ *
+ * @{
+ */
+#define LYS_GETNEXT_WITHCHOICE 0x01 /**< lys_getnext() option to allow returning #LYS_CHOICE nodes instead of looking into them */
+#define LYS_GETNEXT_NOCHOICE 0x02 /**< lys_getnext() option to ignore (kind of conditional) nodes within choice node */
+#define LYS_GETNEXT_WITHCASE 0x04 /**< lys_getnext() option to allow returning #LYS_CASE nodes instead of looking into them */
+#define LYS_GETNEXT_INTONPCONT 0x40 /**< lys_getnext() option to look into non-presence container, instead of returning container itself */
+#define LYS_GETNEXT_NOSTATECHECK 0x100 /**< lys_getnext() option to skip checking module validity (import-only, disabled) and
+ relevant if-feature conditions state */
+/** @} sgetnextflags */
+
+/**
+ * @brief Get child node according to the specified criteria.
+ *
+ * @param[in] parent Optional parent of the node to find. If not specified, the module's top-level nodes are searched.
+ * @param[in] module module of the node to find. It is also limitation for the children node of the given parent.
+ * @param[in] name Name of the node to find.
+ * @param[in] name_len Optional length of the name in case it is not NULL-terminated string.
+ * @param[in] nodetype Optional criteria (to speedup) specifying nodetype(s) of the node to find.
+ * Used as a bitmask, so multiple nodetypes can be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Found node if any.
+ */
+const struct lysc_node *lys_child(const struct lysc_node *parent, const struct lys_module *module,
+ const char *name, size_t name_len, uint16_t nodetype, int options);
+
+/**
+ * @brief Check if the schema node is disabled in the schema tree, i.e. there is any disabled if-feature statement
+ * affecting the node.
+ *
+ * @param[in] node Schema node to check.
+ * @param[in] recursive - 0 to check if-feature only in the \p node schema node,
+ * - 1 to check if-feature in all ascendant schema nodes until there is a node possibly having an instance in a data tree
+ * @return - NULL if enabled,
+ * - pointer to the node with the unsatisfied (disabling) if-feature expression.
+ */
+const struct lysc_iffeature *lys_is_disabled(const struct lysc_node *node, int recursive);
/** @} */
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
new file mode 100644
index 0000000..4761520
--- /dev/null
+++ b/src/tree_schema_compile.c
@@ -0,0 +1,3787 @@
+/**
+ * @file tree_schema.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema tree implementation
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "common.h"
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include "libyang.h"
+#include "context.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+/**
+ * @brief Duplicate string into dictionary
+ * @param[in] CTX libyang context of the dictionary.
+ * @param[in] ORIG String to duplicate.
+ * @param[out] DUP Where to store the result.
+ */
+#define DUP_STRING(CTX, ORIG, DUP) if (ORIG) {DUP = lydict_insert(CTX, ORIG, 0);}
+
+#define COMPILE_ARRAY_GOTO(CTX, ARRAY_P, ARRAY_C, OPTIONS, ITER, FUNC, RET, GOTO) \
+ if (ARRAY_P) { \
+ LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, LY_ARRAY_SIZE(ARRAY_P), RET, GOTO); \
+ size_t __array_offset = LY_ARRAY_SIZE(ARRAY_C); \
+ for (ITER = 0; ITER < LY_ARRAY_SIZE(ARRAY_P); ++ITER) { \
+ LY_ARRAY_INCREMENT(ARRAY_C); \
+ RET = FUNC(CTX, &(ARRAY_P)[ITER], OPTIONS, &(ARRAY_C)[ITER + __array_offset]); \
+ LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
+ } \
+ }
+
+#define COMPILE_ARRAY_UNIQUE_GOTO(CTX, ARRAY_P, ARRAY_C, OPTIONS, ITER, FUNC, RET, GOTO) \
+ if (ARRAY_P) { \
+ LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, LY_ARRAY_SIZE(ARRAY_P), RET, GOTO); \
+ size_t __array_offset = LY_ARRAY_SIZE(ARRAY_C); \
+ for (ITER = 0; ITER < LY_ARRAY_SIZE(ARRAY_P); ++ITER) { \
+ LY_ARRAY_INCREMENT(ARRAY_C); \
+ RET = FUNC(CTX, &(ARRAY_P)[ITER], OPTIONS, ARRAY_C, &(ARRAY_C)[ITER + __array_offset]); \
+ LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
+ } \
+ }
+
+#define COMPILE_MEMBER_GOTO(CTX, MEMBER_P, MEMBER_C, OPTIONS, FUNC, RET, GOTO) \
+ if (MEMBER_P) { \
+ MEMBER_C = calloc(1, sizeof *(MEMBER_C)); \
+ LY_CHECK_ERR_GOTO(!(MEMBER_C), LOGMEM((CTX)->ctx); RET = LY_EMEM, GOTO); \
+ RET = FUNC(CTX, MEMBER_P, OPTIONS, MEMBER_C); \
+ 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) { \
+ if (&(ARRAY)[u] != EXCL && (void*)((ARRAY)[u].MEMBER) == (void*)(IDENT)) { \
+ LOGVAL((CTX)->ctx, LY_VLOG_STR, (CTX)->path, LY_VCODE_DUPIDENT, IDENT, STMT); \
+ return LY_EVALID; \
+ } \
+ } \
+ }
+
+static struct lysc_ext_instance *
+lysc_ext_instance_dup(struct ly_ctx *ctx, struct lysc_ext_instance *orig)
+{
+ /* TODO */
+ (void) ctx;
+ (void) orig;
+ return NULL;
+}
+
+static struct lysc_pattern*
+lysc_pattern_dup(struct lysc_pattern *orig)
+{
+ ++orig->refcount;
+ return orig;
+}
+
+static struct lysc_pattern**
+lysc_patterns_dup(struct ly_ctx *ctx, struct lysc_pattern **orig)
+{
+ struct lysc_pattern **dup = NULL;
+ unsigned int u;
+
+ LY_ARRAY_CREATE_RET(ctx, dup, LY_ARRAY_SIZE(orig), NULL);
+ LY_ARRAY_FOR(orig, u) {
+ dup[u] = lysc_pattern_dup(orig[u]);
+ LY_ARRAY_INCREMENT(dup);
+ }
+ return dup;
+}
+
+struct lysc_range*
+lysc_range_dup(struct ly_ctx *ctx, const struct lysc_range *orig)
+{
+ struct lysc_range *dup;
+ LY_ERR ret;
+
+ dup = calloc(1, sizeof *dup);
+ LY_CHECK_ERR_RET(!dup, LOGMEM(ctx), NULL);
+ if (orig->parts) {
+ LY_ARRAY_CREATE_GOTO(ctx, dup->parts, LY_ARRAY_SIZE(orig->parts), ret, cleanup);
+ LY_ARRAY_SIZE(dup->parts) = LY_ARRAY_SIZE(orig->parts);
+ memcpy(dup->parts, orig->parts, LY_ARRAY_SIZE(dup->parts) * sizeof *dup->parts);
+ }
+ DUP_STRING(ctx, orig->eapptag, dup->eapptag);
+ DUP_STRING(ctx, orig->emsg, dup->emsg);
+ dup->exts = lysc_ext_instance_dup(ctx, orig->exts);
+
+ return dup;
+cleanup:
+ free(dup);
+ (void) ret; /* set but not used due to the return type */
+ return NULL;
+}
+
+struct iff_stack {
+ int size;
+ int index; /* first empty item */
+ uint8_t *stack;
+};
+
+static LY_ERR
+iff_stack_push(struct iff_stack *stack, uint8_t value)
+{
+ if (stack->index == stack->size) {
+ stack->size += 4;
+ stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
+ LY_CHECK_ERR_RET(!stack->stack, LOGMEM(NULL); stack->size = 0, LY_EMEM);
+ }
+ stack->stack[stack->index++] = value;
+ return LY_SUCCESS;
+}
+
+static uint8_t
+iff_stack_pop(struct iff_stack *stack)
+{
+ stack->index--;
+ return stack->stack[stack->index];
+}
+
+static void
+iff_stack_clean(struct iff_stack *stack)
+{
+ stack->size = 0;
+ free(stack->stack);
+}
+
+static void
+iff_setop(uint8_t *list, uint8_t op, int pos)
+{
+ uint8_t *item;
+ uint8_t mask = 3;
+
+ assert(pos >= 0);
+ assert(op <= 3); /* max 2 bits */
+
+ item = &list[pos / 4];
+ mask = mask << 2 * (pos % 4);
+ *item = (*item) & ~mask;
+ *item = (*item) | (op << 2 * (pos % 4));
+}
+
+#define LYS_IFF_LP 0x04 /* ( */
+#define LYS_IFF_RP 0x08 /* ) */
+
+static struct lysc_feature *
+lysc_feature_find(struct lysc_module *mod, const char *name, size_t len)
+{
+ size_t i;
+ struct lysc_feature *f;
+
+ for (i = 0; i < len; ++i) {
+ if (name[i] == ':') {
+ /* we have a prefixed feature */
+ mod = lysc_module_find_prefix(mod, name, i);
+ LY_CHECK_RET(!mod, NULL);
+
+ name = &name[i + 1];
+ len = len - i - 1;
+ }
+ }
+
+ /* we have the correct module, get the feature */
+ LY_ARRAY_FOR(mod->features, i) {
+ f = &mod->features[i];
+ if (!strncmp(f->name, name, len) && f->name[len] == '\0') {
+ return f;
+ }
+ }
+
+ return NULL;
+}
+
+static LY_ERR
+lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, int UNUSED(options), struct lysc_ext_instance *ext)
+{
+ const char *name;
+ unsigned int u;
+ const struct lys_module *mod;
+ struct lysp_ext *edef = NULL;
+
+ DUP_STRING(ctx->ctx, ext_p->argument, ext->argument);
+ ext->insubstmt = ext_p->insubstmt;
+ ext->insubstmt_index = ext_p->insubstmt_index;
+
+ /* get module where the extension definition should be placed */
+ for (u = 0; ext_p->name[u] != ':'; ++u);
+ mod = lys_module_find_prefix(ctx->mod_def, ext_p->name, u);
+ LY_CHECK_ERR_RET(!mod, LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid prefix \"%.*s\" used for extension instance identifier.", u, ext_p->name),
+ LY_EVALID);
+ LY_CHECK_ERR_RET(!mod->parsed->extensions,
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Extension instance \"%s\" refers \"%s\" module that does not contain extension definitions.",
+ ext_p->name, mod->parsed->name),
+ LY_EVALID);
+ name = &ext_p->name[u + 1];
+ /* find the extension definition there */
+ for (ext = NULL, u = 0; u < LY_ARRAY_SIZE(mod->parsed->extensions); ++u) {
+ if (!strcmp(name, mod->parsed->extensions[u].name)) {
+ edef = &mod->parsed->extensions[u];
+ break;
+ }
+ }
+ LY_CHECK_ERR_RET(!edef, LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Extension definition of extension instance \"%s\" not found.", ext_p->name),
+ LY_EVALID);
+ /* TODO plugins */
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+lys_compile_iffeature(struct lysc_ctx *ctx, const char **value, int UNUSED(options), struct lysc_iffeature *iff)
+{
+ const char *c = *value;
+ int r, rc = EXIT_FAILURE;
+ int i, j, last_not, checkversion = 0;
+ unsigned int f_size = 0, expr_size = 0, f_exp = 1;
+ uint8_t op;
+ struct iff_stack stack = {0, 0, NULL};
+ struct lysc_feature *f;
+
+ assert(c);
+
+ /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
+ for (i = j = last_not = 0; c[i]; i++) {
+ if (c[i] == '(') {
+ j++;
+ checkversion = 1;
+ continue;
+ } else if (c[i] == ')') {
+ j--;
+ continue;
+ } else if (isspace(c[i])) {
+ checkversion = 1;
+ continue;
+ }
+
+ if (!strncmp(&c[i], "not", r = 3) || !strncmp(&c[i], "and", r = 3) || !strncmp(&c[i], "or", r = 2)) {
+ if (c[i + r] == '\0') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - unexpected end of expression.", *value);
+ return LY_EVALID;
+ } else if (!isspace(c[i + r])) {
+ /* feature name starting with the not/and/or */
+ last_not = 0;
+ f_size++;
+ } else if (c[i] == 'n') { /* not operation */
+ if (last_not) {
+ /* double not */
+ expr_size = expr_size - 2;
+ last_not = 0;
+ } else {
+ last_not = 1;
+ }
+ } else { /* and, or */
+ f_exp++;
+ /* not a not operation */
+ last_not = 0;
+ }
+ i += r;
+ } else {
+ f_size++;
+ last_not = 0;
+ }
+ expr_size++;
+
+ while (!isspace(c[i])) {
+ if (!c[i] || c[i] == ')') {
+ i--;
+ break;
+ }
+ i++;
+ }
+ }
+ if (j || f_exp != f_size) {
+ /* not matching count of ( and ) */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - non-matching opening and closing parentheses.", *value);
+ return LY_EVALID;
+ }
+
+ if (checkversion || expr_size > 1) {
+ /* check that we have 1.1 module */
+ if (ctx->mod_def->parsed->version != LYS_VERSION_1_1) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", *value);
+ return LY_EVALID;
+ }
+ }
+
+ /* allocate the memory */
+ LY_ARRAY_CREATE_RET(ctx->ctx, iff->features, f_size, LY_EMEM);
+ iff->expr = calloc((j = (expr_size / 4) + ((expr_size % 4) ? 1 : 0)), sizeof *iff->expr);
+ stack.stack = malloc(expr_size * sizeof *stack.stack);
+ LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx->ctx), error);
+
+ stack.size = expr_size;
+ f_size--; expr_size--; /* used as indexes from now */
+
+ for (i--; i >= 0; i--) {
+ if (c[i] == ')') {
+ /* push it on stack */
+ iff_stack_push(&stack, LYS_IFF_RP);
+ continue;
+ } else if (c[i] == '(') {
+ /* pop from the stack into result all operators until ) */
+ while((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
+ iff_setop(iff->expr, op, expr_size--);
+ }
+ continue;
+ } else if (isspace(c[i])) {
+ continue;
+ }
+
+ /* end of operator or operand -> find beginning and get what is it */
+ j = i + 1;
+ while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
+ i--;
+ }
+ i++; /* go back by one step */
+
+ if (!strncmp(&c[i], "not", 3) && isspace(c[i + 3])) {
+ if (stack.index && stack.stack[stack.index - 1] == LYS_IFF_NOT) {
+ /* double not */
+ iff_stack_pop(&stack);
+ } else {
+ /* not has the highest priority, so do not pop from the stack
+ * as in case of AND and OR */
+ iff_stack_push(&stack, LYS_IFF_NOT);
+ }
+ } else if (!strncmp(&c[i], "and", 3) && isspace(c[i + 3])) {
+ /* as for OR - pop from the stack all operators with the same or higher
+ * priority and store them to the result, then push the AND to the stack */
+ while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
+ op = iff_stack_pop(&stack);
+ iff_setop(iff->expr, op, expr_size--);
+ }
+ iff_stack_push(&stack, LYS_IFF_AND);
+ } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
+ while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
+ op = iff_stack_pop(&stack);
+ iff_setop(iff->expr, op, expr_size--);
+ }
+ iff_stack_push(&stack, LYS_IFF_OR);
+ } else {
+ /* feature name, length is j - i */
+
+ /* add it to the expression */
+ iff_setop(iff->expr, LYS_IFF_F, expr_size--);
+
+ /* now get the link to the feature definition */
+ f = lysc_feature_find(ctx->mod->compiled, &c[i], j - i);
+ LY_CHECK_ERR_GOTO(!f,
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".", *value, j - i, &c[i]);
+ rc = LY_EVALID,
+ error)
+ iff->features[f_size] = f;
+ LY_ARRAY_INCREMENT(iff->features);
+ f_size--;
+ }
+ }
+ while (stack.index) {
+ op = iff_stack_pop(&stack);
+ iff_setop(iff->expr, op, expr_size--);
+ }
+
+ if (++expr_size || ++f_size) {
+ /* not all expected operators and operands found */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid value \"%s\" of if-feature - processing error.", *value);
+ rc = LY_EINT;
+ } else {
+ rc = LY_SUCCESS;
+ }
+
+error:
+ /* cleanup */
+ iff_stack_clean(&stack);
+
+ return rc;
+}
+
+static LY_ERR
+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);
+ 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;
+}
+
+static LY_ERR
+lys_compile_must(struct lysc_ctx *ctx, struct lysp_restr *must_p, int options, struct lysc_must *must)
+{
+ unsigned int u;
+ LY_ERR ret = LY_SUCCESS;
+
+ must->cond = lyxp_expr_parse(ctx->ctx, must_p->arg);
+ LY_CHECK_ERR_GOTO(!must->cond, ret = ly_errcode(ctx->ctx), done);
+
+ DUP_STRING(ctx->ctx, must_p->eapptag, must->eapptag);
+ DUP_STRING(ctx->ctx, must_p->emsg, must->emsg);
+ COMPILE_ARRAY_GOTO(ctx, must_p->exts, must->exts, options, u, lys_compile_ext, ret, done);
+
+done:
+ return ret;
+}
+
+static LY_ERR
+lys_compile_import(struct lysc_ctx *ctx, struct lysp_import *imp_p, int options, struct lysc_import *imp)
+{
+ unsigned int u;
+ struct lys_module *mod = NULL;
+ struct lysc_module *comp;
+ LY_ERR ret = LY_SUCCESS;
+
+ DUP_STRING(ctx->ctx, imp_p->prefix, imp->prefix);
+ COMPILE_ARRAY_GOTO(ctx, imp_p->exts, imp->exts, options, u, lys_compile_ext, ret, done);
+ imp->module = imp_p->module;
+
+ /* make sure that we have the parsed version (lysp_) of the imported module to import groupings or typedefs.
+ * The compiled version is needed only for augments, deviates and leafrefs, so they are checked (and added,
+ * if needed) when these nodes are finally being instantiated and validated at the end of schema compilation. */
+ if (!imp->module->parsed) {
+ comp = imp->module->compiled;
+ /* try to get filepath from the compiled version */
+ if (comp->filepath) {
+ mod = (struct lys_module*)lys_parse_path(ctx->ctx, comp->filepath,
+ !strcmp(&comp->filepath[strlen(comp->filepath - 4)], ".yin") ? LYS_IN_YIN : LYS_IN_YANG);
+ if (mod != imp->module) {
+ LOGERR(ctx->ctx, LY_EINT, "Filepath \"%s\" of the module \"%s\" does not match.",
+ comp->filepath, comp->name);
+ mod = NULL;
+ }
+ }
+ if (!mod) {
+ if (lysp_load_module(ctx->ctx, comp->name, comp->revision, 0, 1, &mod)) {
+ LOGERR(ctx->ctx, LY_ENOTFOUND, "Unable to reload \"%s\" module to import it into \"%s\", source data not found.",
+ comp->name, ctx->mod->compiled->name);
+ return LY_ENOTFOUND;
+ }
+ }
+ }
+
+done:
+ return ret;
+}
+
+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)
+{
+ unsigned int u;
+ LY_ERR ret = LY_SUCCESS;
+
+ COMPILE_CHECK_UNIQUENESS(ctx, idents, name, ident, "identity", ident_p->name);
+ DUP_STRING(ctx->ctx, ident_p->name, ident->name);
+ COMPILE_ARRAY_GOTO(ctx, ident_p->iffeatures, ident->iffeatures, options, u, lys_compile_iffeature, ret, done);
+ /* backlings (derived) can be added no sooner than when all the identities in the current module are present */
+ COMPILE_ARRAY_GOTO(ctx, ident_p->exts, ident->exts, options, u, lys_compile_ext, ret, done);
+ ident->flags = ident_p->flags;
+
+done:
+ 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
+ * the array of pointers to the base identities. So one of the ident or bases parameter must be set
+ * to distinguish these two use cases.
+ *
+ * @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
+ * @param[in] bases_p Array of names (including prefix if necessary) of base identities.
+ * @param[in] ident Referencing identity to work with.
+ * @param[in] bases Array of bases of identityref to fill in.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_identity_bases(struct lysc_ctx *ctx, const char **bases_p, struct lysc_ident *ident, struct lysc_ident ***bases)
+{
+ unsigned int u, v;
+ const char *s, *name;
+ struct lys_module *mod;
+ struct lysc_ident **idref;
+
+ assert(ident || bases);
+
+ if (LY_ARRAY_SIZE(bases_p) > 1 && ctx->mod_def->parsed->version < 2) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Multiple bases in %s are allowed only in YANG 1.1 modules.", ident ? "identity" : "identityref type");
+ return LY_EVALID;
+ }
+
+ for (u = 0; u < LY_ARRAY_SIZE(bases_p); ++u) {
+ s = strchr(bases_p[u], ':');
+ if (s) {
+ /* prefixed identity */
+ name = &s[1];
+ mod = lys_module_find_prefix(ctx->mod_def, bases_p[u], s - bases_p[u]);
+ } else {
+ name = bases_p[u];
+ mod = ctx->mod_def;
+ }
+ if (!mod) {
+ if (ident) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid prefix used for base (%s) of identity \"%s\".", bases_p[u], ident->name);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid prefix used for base (%s) of identityref.", bases_p[u]);
+ }
+ return LY_EVALID;
+ }
+ idref = NULL;
+ if (mod->compiled && mod->compiled->identities) {
+ for (v = 0; v < LY_ARRAY_SIZE(mod->compiled->identities); ++v) {
+ if (!strcmp(name, mod->compiled->identities[v].name)) {
+ if (ident) {
+ /* we have match! store the backlink */
+ LY_ARRAY_NEW_RET(ctx->ctx, mod->compiled->identities[v].derived, idref, LY_EMEM);
+ *idref = ident;
+ } else {
+ /* we have match! store the found identity */
+ LY_ARRAY_NEW_RET(ctx->ctx, *bases, idref, LY_EMEM);
+ *idref = &mod->compiled->identities[v];
+ }
+ break;
+ }
+ }
+ }
+ if (!idref || !(*idref)) {
+ if (ident) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Unable to find base (%s) of identity \"%s\".", bases_p[u], ident->name);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Unable to find base (%s) of identityref.", bases_p[u]);
+ }
+ return LY_EVALID;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief For the given array of identities, set the backlinks from all their base identities.
+ * @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
+ * @param[in] idents_p Array of identities definitions from the parsed schema structure.
+ * @param[in] idents Array of referencing identities to which the backlinks are supposed to be set.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_identities_derived(struct lysc_ctx *ctx, struct lysp_ident *idents_p, struct lysc_ident *idents)
+{
+ unsigned int i;
+
+ for (i = 0; i < LY_ARRAY_SIZE(idents_p); ++i) {
+ if (!idents_p[i].bases) {
+ continue;
+ }
+ LY_CHECK_RET(lys_compile_identity_bases(ctx, idents_p[i].bases, &idents[i], NULL));
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Create compiled feature structure.
+ * @param[in] ctx Compile context.
+ * @param[in] feature_p Parsed feature definition to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] features List of already compiled features to check name duplicity.
+ * @param[in,out] feature Compiled feature structure to fill.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_feature(struct lysc_ctx *ctx, struct lysp_feature *feature_p, int options, struct lysc_feature *features, struct lysc_feature *feature)
+{
+ unsigned int u, v;
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_feature **df;
+
+ COMPILE_CHECK_UNIQUENESS(ctx, features, name, feature, "feature", feature_p->name);
+ DUP_STRING(ctx->ctx, feature_p->name, feature->name);
+ feature->flags = feature_p->flags;
+
+ COMPILE_ARRAY_GOTO(ctx, feature_p->exts, feature->exts, options, u, lys_compile_ext, ret, done);
+ COMPILE_ARRAY_GOTO(ctx, feature_p->iffeatures, feature->iffeatures, options, u, lys_compile_iffeature, ret, done);
+ if (feature->iffeatures) {
+ 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) {
+ /* 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 */
+ }
+ }
+ }
+done:
+ return ret;
+}
+
+/**
+ * @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
+ * allow processing of the fractions. The fraction point is extracted from the value which is then normalize according to given frdigits into
+ * valcopy to allow easy parsing and storing of the value. libyang stores decimal number without the decimal point which is always recovered from
+ * the known fraction-digits value. So, with fraction-digits 2, number 3.14 is stored as 314 and number 1 is stored as 100.
+ * @param[in] frdigits The fraction-digits of the type in case of LY_TYPE_DEC64.
+ * @param[in] value String value of the range boundary.
+ * @param[out] len Number of the processed bytes from the value. Processing stops on the first character which is not part of the number boundary.
+ * @param[out] valcopy NULL-terminated string with the numeric value to parse and store.
+ * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID (no number) or LY_EINVAL (decimal64 not matching fraction-digits value).
+ */
+static LY_ERR
+range_part_check_value_syntax(struct lysc_ctx *ctx, LY_DATA_TYPE basetype, uint8_t frdigits, const char *value, size_t *len, char **valcopy)
+{
+ size_t fraction = 0, size;
+
+ *len = 0;
+
+ assert(value);
+ /* parse value */
+ if (!isdigit(value[*len]) && (value[*len] != '-') && (value[*len] != '+')) {
+ return LY_EVALID;
+ }
+
+ if ((value[*len] == '-') || (value[*len] == '+')) {
+ ++(*len);
+ }
+
+ while (isdigit(value[*len])) {
+ ++(*len);
+ }
+
+ if ((basetype != LY_TYPE_DEC64) || (value[*len] != '.') || !isdigit(value[*len + 1])) {
+ if (basetype == LY_TYPE_DEC64) {
+ goto decimal;
+ } else {
+ *valcopy = strndup(value, *len);
+ return LY_SUCCESS;
+ }
+ }
+ fraction = *len;
+
+ ++(*len);
+ while (isdigit(value[*len])) {
+ ++(*len);
+ }
+
+ if (basetype == LY_TYPE_DEC64) {
+decimal:
+ assert(frdigits);
+ if (*len - 1 - fraction > frdigits) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Range boundary \"%.*s\" of decimal64 type exceeds defined number (%u) of fraction digits.",
+ *len, value, frdigits);
+ return LY_EINVAL;
+ }
+ if (fraction) {
+ size = (*len) + (frdigits - ((*len) - 1 - fraction));
+ } else {
+ size = (*len) + frdigits + 1;
+ }
+ *valcopy = malloc(size * sizeof **valcopy);
+ LY_CHECK_ERR_RET(!(*valcopy), LOGMEM(ctx->ctx), LY_EMEM);
+
+ (*valcopy)[size - 1] = '\0';
+ if (fraction) {
+ memcpy(&(*valcopy)[0], &value[0], fraction);
+ memcpy(&(*valcopy)[fraction], &value[fraction + 1], (*len) - 1 - (fraction));
+ memset(&(*valcopy)[(*len) - 1], '0', frdigits - ((*len) - 1 - fraction));
+ } else {
+ memcpy(&(*valcopy)[0], &value[0], *len);
+ memset(&(*valcopy)[*len], '0', frdigits);
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check that values in range are in ascendant order.
+ * @param[in] unsigned_value Flag to note that we are working with unsigned values.
+ * @param[in] max Flag to distinguish if checking min or max value. min value must be strictly higher than previous,
+ * max can be also equal.
+ * @param[in] value Current value to check.
+ * @param[in] prev_value The last seen value.
+ * @return LY_SUCCESS or LY_EEXIST for invalid order.
+ */
+static LY_ERR
+range_part_check_ascendancy(int unsigned_value, int max, int64_t value, int64_t prev_value)
+{
+ if (unsigned_value) {
+ if ((max && (uint64_t)prev_value > (uint64_t)value) || (!max && (uint64_t)prev_value >= (uint64_t)value)) {
+ return LY_EEXIST;
+ }
+ } else {
+ if ((max && prev_value > value) || (!max && prev_value >= value)) {
+ return LY_EEXIST;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Set min/max value of the range part.
+ * @param[in] ctx Compile context.
+ * @param[in] part Range part structure to fill.
+ * @param[in] max Flag to distinguish if storing min or max value.
+ * @param[in] prev The last seen value to check that all values in range are specified in ascendant order.
+ * @param[in] basetype Type of the value to get know implicit min/max values and other checking rules.
+ * @param[in] first Flag for the first value of the range to avoid ascendancy order.
+ * @param[in] length_restr Flag to distinguish between range and length restrictions. Only for logging.
+ * @param[in] frdigits The fraction-digits value in case of LY_TYPE_DEC64 basetype.
+ * @param[in] base_range Range from the type from which the current type is derived (if not built-in) to get type's min and max values.
+ * @param[in,out] value Numeric range value to be stored, if not provided the type's min/max value is set.
+ * @return LY_ERR value - LY_SUCCESS, LY_EDENIED (value brokes type's boundaries), LY_EVALID (not a number),
+ * LY_EEXIST (value is smaller than the previous one), LY_EINVAL (decimal64 value does not corresponds with the
+ * frdigits value), LY_EMEM.
+ */
+static LY_ERR
+range_part_minmax(struct lysc_ctx *ctx, struct lysc_range_part *part, int max, int64_t prev, LY_DATA_TYPE basetype, int first, int length_restr,
+ uint8_t frdigits, struct lysc_range *base_range, const char **value)
+{
+ LY_ERR ret = LY_SUCCESS;
+ char *valcopy = NULL;
+ size_t len;
+
+ if (value) {
+ ret = range_part_check_value_syntax(ctx, basetype, frdigits, *value, &len, &valcopy);
+ LY_CHECK_GOTO(ret, finalize);
+ }
+ if (!valcopy && base_range) {
+ if (max) {
+ part->max_64 = base_range->parts[LY_ARRAY_SIZE(base_range->parts) - 1].max_64;
+ } else {
+ part->min_64 = base_range->parts[0].min_64;
+ }
+ if (!first) {
+ ret = range_part_check_ascendancy(basetype <= LY_TYPE_STRING ? 1 : 0, max, max ? part->max_64 : part->min_64, prev);
+ }
+ goto finalize;
+ }
+
+ switch (basetype) {
+ case LY_TYPE_INT8: /* range */
+ if (valcopy) {
+ ret = ly_parse_int(valcopy, INT64_C(-128), INT64_C(127), 10, max ? &part->max_64 : &part->min_64);
+ } else if (max) {
+ part->max_64 = INT64_C(127);
+ } else {
+ part->min_64 = INT64_C(-128);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_INT16: /* range */
+ if (valcopy) {
+ ret = ly_parse_int(valcopy, INT64_C(-32768), INT64_C(32767), 10, max ? &part->max_64 : &part->min_64);
+ } else if (max) {
+ part->max_64 = INT64_C(32767);
+ } else {
+ part->min_64 = INT64_C(-32768);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_INT32: /* range */
+ if (valcopy) {
+ ret = ly_parse_int(valcopy, INT64_C(-2147483648), INT64_C(2147483647), 10, max ? &part->max_64 : &part->min_64);
+ } else if (max) {
+ part->max_64 = INT64_C(2147483647);
+ } else {
+ part->min_64 = INT64_C(-2147483648);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_INT64: /* range */
+ case LY_TYPE_DEC64: /* range */
+ if (valcopy) {
+ ret = ly_parse_int(valcopy, INT64_C(-9223372036854775807) - INT64_C(1), INT64_C(9223372036854775807), 10,
+ max ? &part->max_64 : &part->min_64);
+ } else if (max) {
+ part->max_64 = INT64_C(9223372036854775807);
+ } else {
+ part->min_64 = INT64_C(-9223372036854775807) - INT64_C(1);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_UINT8: /* range */
+ if (valcopy) {
+ ret = ly_parse_uint(valcopy, UINT64_C(255), 10, max ? &part->max_u64 : &part->min_u64);
+ } else if (max) {
+ part->max_u64 = UINT64_C(255);
+ } else {
+ part->min_u64 = UINT64_C(0);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_UINT16: /* range */
+ if (valcopy) {
+ ret = ly_parse_uint(valcopy, UINT64_C(65535), 10, max ? &part->max_u64 : &part->min_u64);
+ } else if (max) {
+ part->max_u64 = UINT64_C(65535);
+ } else {
+ part->min_u64 = UINT64_C(0);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_UINT32: /* range */
+ if (valcopy) {
+ ret = ly_parse_uint(valcopy, UINT64_C(4294967295), 10, max ? &part->max_u64 : &part->min_u64);
+ } else if (max) {
+ part->max_u64 = UINT64_C(4294967295);
+ } else {
+ part->min_u64 = UINT64_C(0);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ case LY_TYPE_UINT64: /* range */
+ case LY_TYPE_STRING: /* length */
+ case LY_TYPE_BINARY: /* length */
+ if (valcopy) {
+ ret = ly_parse_uint(valcopy, UINT64_C(18446744073709551615), 10, max ? &part->max_u64 : &part->min_u64);
+ } else if (max) {
+ part->max_u64 = UINT64_C(18446744073709551615);
+ } else {
+ part->min_u64 = UINT64_C(0);
+ }
+ if (!ret && !first) {
+ ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+ }
+ break;
+ default:
+ LOGINT(ctx->ctx);
+ ret = LY_EINT;
+ }
+
+finalize:
+ if (ret == LY_EDENIED) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - value \"%s\" does not fit the type limitations.",
+ length_restr ? "length" : "range", valcopy ? valcopy : *value);
+ } else if (ret == LY_EVALID) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - invalid value \"%s\".",
+ length_restr ? "length" : "range", valcopy ? valcopy : *value);
+ } else if (ret == LY_EEXIST) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - values are not in ascending order (%s).",
+ length_restr ? "length" : "range",
+ (valcopy && basetype != LY_TYPE_DEC64) ? valcopy : value ? *value : max ? "max" : "min");
+ } else if (!ret && value) {
+ *value = *value + len;
+ }
+ free(valcopy);
+ return ret;
+}
+
+/**
+ * @brief Compile the parsed range restriction.
+ * @param[in] ctx Compile context.
+ * @param[in] range_p Parsed range structure to compile.
+ * @param[in] basetype Base YANG built-in type of the node with the range restriction.
+ * @param[in] length_restr Flag to distinguish between range and length restrictions. Only for logging.
+ * @param[in] frdigits The fraction-digits value in case of LY_TYPE_DEC64 basetype.
+ * @param[in] base_range Range restriction of the type from which the current type is derived. The current
+ * range restriction must be more restrictive than the base_range.
+ * @param[in,out] range Pointer to the created current range structure.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_type_range(struct lysc_ctx *ctx, struct lysp_restr *range_p, LY_DATA_TYPE basetype, int length_restr, uint8_t frdigits,
+ struct lysc_range *base_range, struct lysc_range **range)
+{
+ LY_ERR ret = LY_EVALID;
+ const char *expr;
+ struct lysc_range_part *parts = NULL, *part;
+ int range_expected = 0, uns;
+ unsigned int parts_done = 0, u, v;
+
+ assert(range);
+ assert(range_p);
+
+ expr = range_p->arg;
+ while(1) {
+ if (isspace(*expr)) {
+ ++expr;
+ } else if (*expr == '\0') {
+ if (range_expected) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - unexpected end of the expression after \"..\" (%s).",
+ length_restr ? "length" : "range", range_p->arg);
+ goto cleanup;
+ } else if (!parts || parts_done == LY_ARRAY_SIZE(parts)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - unexpected end of the expression (%s).",
+ length_restr ? "length" : "range", range_p->arg);
+ goto cleanup;
+ }
+ parts_done++;
+ break;
+ } else if (!strncmp(expr, "min", 3)) {
+ if (parts) {
+ /* min cannot be used elsewhere than in the first part */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - unexpected data before min keyword (%.*s).", length_restr ? "length" : "range",
+ expr - range_p->arg, range_p->arg);
+ goto cleanup;
+ }
+ expr += 3;
+
+ LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
+ LY_CHECK_GOTO(range_part_minmax(ctx, part, 0, 0, basetype, 1, length_restr, frdigits, base_range, NULL), cleanup);
+ part->max_64 = part->min_64;
+ } else if (*expr == '|') {
+ if (!parts || range_expected) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - unexpected beginning of the expression (%s).", length_restr ? "length" : "range", expr);
+ goto cleanup;
+ }
+ expr++;
+ parts_done++;
+ /* process next part of the expression */
+ } else if (!strncmp(expr, "..", 2)) {
+ expr += 2;
+ while (isspace(*expr)) {
+ expr++;
+ }
+ if (!parts || LY_ARRAY_SIZE(parts) == parts_done) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - unexpected \"..\" without a lower bound.", length_restr ? "length" : "range");
+ goto cleanup;
+ }
+ /* continue expecting the upper boundary */
+ range_expected = 1;
+ } else if (isdigit(*expr) || (*expr == '-') || (*expr == '+')) {
+ /* number */
+ if (range_expected) {
+ part = &parts[LY_ARRAY_SIZE(parts) - 1];
+ LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, frdigits, NULL, &expr), cleanup);
+ range_expected = 0;
+ } else {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
+ LY_CHECK_GOTO(range_part_minmax(ctx, part, 0, parts_done ? parts[LY_ARRAY_SIZE(parts) - 2].max_64 : 0,
+ basetype, parts_done ? 0 : 1, length_restr, frdigits, NULL, &expr), cleanup);
+ part->max_64 = part->min_64;
+ }
+
+ /* continue with possible another expression part */
+ } else if (!strncmp(expr, "max", 3)) {
+ expr += 3;
+ while (isspace(*expr)) {
+ expr++;
+ }
+ if (*expr != '\0') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid %s restriction - unexpected data after max keyword (%s).",
+ length_restr ? "length" : "range", expr);
+ goto cleanup;
+ }
+ if (range_expected) {
+ part = &parts[LY_ARRAY_SIZE(parts) - 1];
+ LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, frdigits, base_range, NULL), cleanup);
+ range_expected = 0;
+ } else {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
+ LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, parts_done ? parts[LY_ARRAY_SIZE(parts) - 2].max_64 : 0,
+ basetype, parts_done ? 0 : 1, length_restr, frdigits, base_range, NULL), cleanup);
+ part->min_64 = part->max_64;
+ }
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid %s restriction - unexpected data (%s).",
+ length_restr ? "length" : "range", expr);
+ goto cleanup;
+ }
+ }
+
+ /* check with the previous range/length restriction */
+ if (base_range) {
+ switch (basetype) {
+ case LY_TYPE_BINARY:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_UINT64:
+ case LY_TYPE_STRING:
+ uns = 1;
+ break;
+ case LY_TYPE_DEC64:
+ case LY_TYPE_INT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_INT64:
+ uns = 0;
+ break;
+ default:
+ LOGINT(ctx->ctx);
+ ret = LY_EINT;
+ goto cleanup;
+ }
+ for (u = v = 0; u < parts_done && v < LY_ARRAY_SIZE(base_range->parts); ++u) {
+ if ((uns && parts[u].min_u64 < base_range->parts[v].min_u64) || (!uns && parts[u].min_64 < base_range->parts[v].min_64)) {
+ goto baseerror;
+ }
+ /* current lower bound is not lower than the base */
+ if (base_range->parts[v].min_64 == base_range->parts[v].max_64) {
+ /* base has single value */
+ if (base_range->parts[v].min_64 == parts[u].min_64) {
+ /* both lower bounds are the same */
+ if (parts[u].min_64 != parts[u].max_64) {
+ /* current continues with a range */
+ goto baseerror;
+ } else {
+ /* equal single values, move both forward */
+ ++v;
+ continue;
+ }
+ } else {
+ /* base is single value lower than current range, so the
+ * value from base range is removed in the current,
+ * move only base and repeat checking */
+ ++v;
+ --u;
+ continue;
+ }
+ } else {
+ /* base is the range */
+ if (parts[u].min_64 == parts[u].max_64) {
+ /* current is a single value */
+ if ((uns && parts[u].max_u64 > base_range->parts[v].max_u64) || (!uns && parts[u].max_64 > base_range->parts[v].max_64)) {
+ /* current is behind the base range, so base range is omitted,
+ * move the base and keep the current for further check */
+ ++v;
+ --u;
+ } /* else it is within the base range, so move the current, but keep the base */
+ continue;
+ } else {
+ /* both are ranges - check the higher bound, the lower was already checked */
+ if ((uns && parts[u].max_u64 > base_range->parts[v].max_u64) || (!uns && parts[u].max_64 > base_range->parts[v].max_64)) {
+ /* higher bound is higher than the current higher bound */
+ if ((uns && parts[u].min_u64 > base_range->parts[v].max_u64) || (!uns && parts[u].min_64 > base_range->parts[v].max_64)) {
+ /* but the current lower bound is also higher, so the base range is omitted,
+ * continue with the same current, but move the base */
+ --u;
+ ++v;
+ continue;
+ }
+ /* current range starts within the base range but end behind it */
+ goto baseerror;
+ } else {
+ /* current range is smaller than the base,
+ * move current, but stay with the base */
+ continue;
+ }
+ }
+ }
+ }
+ if (u != parts_done) {
+baseerror:
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid %s restriction - the derived restriction (%s) is not equally or more limiting.",
+ length_restr ? "length" : "range", range_p->arg);
+ goto cleanup;
+ }
+ }
+
+ if (!(*range)) {
+ *range = calloc(1, sizeof **range);
+ LY_CHECK_ERR_RET(!(*range), LOGMEM(ctx->ctx), LY_EMEM);
+ }
+
+ if (range_p->eapptag) {
+ lydict_remove(ctx->ctx, (*range)->eapptag);
+ (*range)->eapptag = lydict_insert(ctx->ctx, range_p->eapptag, 0);
+ }
+ if (range_p->emsg) {
+ lydict_remove(ctx->ctx, (*range)->emsg);
+ (*range)->emsg = lydict_insert(ctx->ctx, range_p->emsg, 0);
+ }
+ /* extensions are taken only from the last range by the caller */
+
+ (*range)->parts = parts;
+ parts = NULL;
+ ret = LY_SUCCESS;
+cleanup:
+ /* TODO clean up */
+ LY_ARRAY_FREE(parts);
+
+ return ret;
+}
+
+/**
+ * @brief Checks pattern syntax.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] pattern Pattern to check.
+ * @param[in,out] pcre_precomp Precompiled PCRE pattern. If NULL, the compiled information used to validate pattern are freed.
+ * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID.
+ */
+static LY_ERR
+lys_compile_type_pattern_check(struct lysc_ctx *ctx, const char *pattern, pcre **pcre_precomp)
+{
+ int idx, idx2, start, end, err_offset, count;
+ char *perl_regex, *ptr;
+ const char *err_msg, *orig_ptr;
+ pcre *precomp;
+#define URANGE_LEN 19
+ char *ublock2urange[][2] = {
+ {"BasicLatin", "[\\x{0000}-\\x{007F}]"},
+ {"Latin-1Supplement", "[\\x{0080}-\\x{00FF}]"},
+ {"LatinExtended-A", "[\\x{0100}-\\x{017F}]"},
+ {"LatinExtended-B", "[\\x{0180}-\\x{024F}]"},
+ {"IPAExtensions", "[\\x{0250}-\\x{02AF}]"},
+ {"SpacingModifierLetters", "[\\x{02B0}-\\x{02FF}]"},
+ {"CombiningDiacriticalMarks", "[\\x{0300}-\\x{036F}]"},
+ {"Greek", "[\\x{0370}-\\x{03FF}]"},
+ {"Cyrillic", "[\\x{0400}-\\x{04FF}]"},
+ {"Armenian", "[\\x{0530}-\\x{058F}]"},
+ {"Hebrew", "[\\x{0590}-\\x{05FF}]"},
+ {"Arabic", "[\\x{0600}-\\x{06FF}]"},
+ {"Syriac", "[\\x{0700}-\\x{074F}]"},
+ {"Thaana", "[\\x{0780}-\\x{07BF}]"},
+ {"Devanagari", "[\\x{0900}-\\x{097F}]"},
+ {"Bengali", "[\\x{0980}-\\x{09FF}]"},
+ {"Gurmukhi", "[\\x{0A00}-\\x{0A7F}]"},
+ {"Gujarati", "[\\x{0A80}-\\x{0AFF}]"},
+ {"Oriya", "[\\x{0B00}-\\x{0B7F}]"},
+ {"Tamil", "[\\x{0B80}-\\x{0BFF}]"},
+ {"Telugu", "[\\x{0C00}-\\x{0C7F}]"},
+ {"Kannada", "[\\x{0C80}-\\x{0CFF}]"},
+ {"Malayalam", "[\\x{0D00}-\\x{0D7F}]"},
+ {"Sinhala", "[\\x{0D80}-\\x{0DFF}]"},
+ {"Thai", "[\\x{0E00}-\\x{0E7F}]"},
+ {"Lao", "[\\x{0E80}-\\x{0EFF}]"},
+ {"Tibetan", "[\\x{0F00}-\\x{0FFF}]"},
+ {"Myanmar", "[\\x{1000}-\\x{109F}]"},
+ {"Georgian", "[\\x{10A0}-\\x{10FF}]"},
+ {"HangulJamo", "[\\x{1100}-\\x{11FF}]"},
+ {"Ethiopic", "[\\x{1200}-\\x{137F}]"},
+ {"Cherokee", "[\\x{13A0}-\\x{13FF}]"},
+ {"UnifiedCanadianAboriginalSyllabics", "[\\x{1400}-\\x{167F}]"},
+ {"Ogham", "[\\x{1680}-\\x{169F}]"},
+ {"Runic", "[\\x{16A0}-\\x{16FF}]"},
+ {"Khmer", "[\\x{1780}-\\x{17FF}]"},
+ {"Mongolian", "[\\x{1800}-\\x{18AF}]"},
+ {"LatinExtendedAdditional", "[\\x{1E00}-\\x{1EFF}]"},
+ {"GreekExtended", "[\\x{1F00}-\\x{1FFF}]"},
+ {"GeneralPunctuation", "[\\x{2000}-\\x{206F}]"},
+ {"SuperscriptsandSubscripts", "[\\x{2070}-\\x{209F}]"},
+ {"CurrencySymbols", "[\\x{20A0}-\\x{20CF}]"},
+ {"CombiningMarksforSymbols", "[\\x{20D0}-\\x{20FF}]"},
+ {"LetterlikeSymbols", "[\\x{2100}-\\x{214F}]"},
+ {"NumberForms", "[\\x{2150}-\\x{218F}]"},
+ {"Arrows", "[\\x{2190}-\\x{21FF}]"},
+ {"MathematicalOperators", "[\\x{2200}-\\x{22FF}]"},
+ {"MiscellaneousTechnical", "[\\x{2300}-\\x{23FF}]"},
+ {"ControlPictures", "[\\x{2400}-\\x{243F}]"},
+ {"OpticalCharacterRecognition", "[\\x{2440}-\\x{245F}]"},
+ {"EnclosedAlphanumerics", "[\\x{2460}-\\x{24FF}]"},
+ {"BoxDrawing", "[\\x{2500}-\\x{257F}]"},
+ {"BlockElements", "[\\x{2580}-\\x{259F}]"},
+ {"GeometricShapes", "[\\x{25A0}-\\x{25FF}]"},
+ {"MiscellaneousSymbols", "[\\x{2600}-\\x{26FF}]"},
+ {"Dingbats", "[\\x{2700}-\\x{27BF}]"},
+ {"BraillePatterns", "[\\x{2800}-\\x{28FF}]"},
+ {"CJKRadicalsSupplement", "[\\x{2E80}-\\x{2EFF}]"},
+ {"KangxiRadicals", "[\\x{2F00}-\\x{2FDF}]"},
+ {"IdeographicDescriptionCharacters", "[\\x{2FF0}-\\x{2FFF}]"},
+ {"CJKSymbolsandPunctuation", "[\\x{3000}-\\x{303F}]"},
+ {"Hiragana", "[\\x{3040}-\\x{309F}]"},
+ {"Katakana", "[\\x{30A0}-\\x{30FF}]"},
+ {"Bopomofo", "[\\x{3100}-\\x{312F}]"},
+ {"HangulCompatibilityJamo", "[\\x{3130}-\\x{318F}]"},
+ {"Kanbun", "[\\x{3190}-\\x{319F}]"},
+ {"BopomofoExtended", "[\\x{31A0}-\\x{31BF}]"},
+ {"EnclosedCJKLettersandMonths", "[\\x{3200}-\\x{32FF}]"},
+ {"CJKCompatibility", "[\\x{3300}-\\x{33FF}]"},
+ {"CJKUnifiedIdeographsExtensionA", "[\\x{3400}-\\x{4DB5}]"},
+ {"CJKUnifiedIdeographs", "[\\x{4E00}-\\x{9FFF}]"},
+ {"YiSyllables", "[\\x{A000}-\\x{A48F}]"},
+ {"YiRadicals", "[\\x{A490}-\\x{A4CF}]"},
+ {"HangulSyllables", "[\\x{AC00}-\\x{D7A3}]"},
+ {"PrivateUse", "[\\x{E000}-\\x{F8FF}]"},
+ {"CJKCompatibilityIdeographs", "[\\x{F900}-\\x{FAFF}]"},
+ {"AlphabeticPresentationForms", "[\\x{FB00}-\\x{FB4F}]"},
+ {"ArabicPresentationForms-A", "[\\x{FB50}-\\x{FDFF}]"},
+ {"CombiningHalfMarks", "[\\x{FE20}-\\x{FE2F}]"},
+ {"CJKCompatibilityForms", "[\\x{FE30}-\\x{FE4F}]"},
+ {"SmallFormVariants", "[\\x{FE50}-\\x{FE6F}]"},
+ {"ArabicPresentationForms-B", "[\\x{FE70}-\\x{FEFE}]"},
+ {"HalfwidthandFullwidthForms", "[\\x{FF00}-\\x{FFEF}]"},
+ {NULL, NULL}
+ };
+
+ /* adjust the expression to a Perl equivalent
+ * http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs */
+
+ /* we need to replace all "$" with "\$", count them now */
+ for (count = 0, ptr = strpbrk(pattern, "^$"); ptr; ++count, ptr = strpbrk(ptr + 1, "^$"));
+
+ perl_regex = malloc((strlen(pattern) + 4 + count) * sizeof(char));
+ LY_CHECK_ERR_RET(!perl_regex, LOGMEM(ctx->ctx), LY_EMEM);
+ perl_regex[0] = '\0';
+
+ ptr = perl_regex;
+
+ if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) {
+ /* we will add line-end anchoring */
+ ptr[0] = '(';
+ ++ptr;
+ }
+
+ for (orig_ptr = pattern; orig_ptr[0]; ++orig_ptr) {
+ if (orig_ptr[0] == '$') {
+ ptr += sprintf(ptr, "\\$");
+ } else if (orig_ptr[0] == '^') {
+ ptr += sprintf(ptr, "\\^");
+ } else {
+ ptr[0] = orig_ptr[0];
+ ++ptr;
+ }
+ }
+
+ if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) {
+ ptr += sprintf(ptr, ")$");
+ } else {
+ ptr[0] = '\0';
+ ++ptr;
+ }
+
+ /* substitute Unicode Character Blocks with exact Character Ranges */
+ while ((ptr = strstr(perl_regex, "\\p{Is"))) {
+ start = ptr - perl_regex;
+
+ ptr = strchr(ptr, '}');
+ if (!ptr) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_INREGEXP,
+ pattern, perl_regex + start + 2, "unterminated character property");
+ free(perl_regex);
+ return LY_EVALID;
+ }
+ end = (ptr - perl_regex) + 1;
+
+ /* need more space */
+ if (end - start < URANGE_LEN) {
+ perl_regex = ly_realloc(perl_regex, strlen(perl_regex) + (URANGE_LEN - (end - start)) + 1);
+ LY_CHECK_ERR_RET(!perl_regex, LOGMEM(ctx->ctx); free(perl_regex), LY_EMEM);
+ }
+
+ /* find our range */
+ for (idx = 0; ublock2urange[idx][0]; ++idx) {
+ if (!strncmp(perl_regex + start + 5, ublock2urange[idx][0], strlen(ublock2urange[idx][0]))) {
+ break;
+ }
+ }
+ if (!ublock2urange[idx][0]) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_INREGEXP,
+ pattern, perl_regex + start + 5, "unknown block name");
+ free(perl_regex);
+ return LY_EVALID;
+ }
+
+ /* make the space in the string and replace the block (but we cannot include brackets if it was already enclosed in them) */
+ for (idx2 = 0, count = 0; idx2 < start; ++idx2) {
+ if ((perl_regex[idx2] == '[') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) {
+ ++count;
+ }
+ if ((perl_regex[idx2] == ']') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) {
+ --count;
+ }
+ }
+ if (count) {
+ /* skip brackets */
+ memmove(perl_regex + start + (URANGE_LEN - 2), perl_regex + end, strlen(perl_regex + end) + 1);
+ memcpy(perl_regex + start, ublock2urange[idx][1] + 1, URANGE_LEN - 2);
+ } else {
+ memmove(perl_regex + start + URANGE_LEN, perl_regex + end, strlen(perl_regex + end) + 1);
+ memcpy(perl_regex + start, ublock2urange[idx][1], URANGE_LEN);
+ }
+ }
+
+ /* must return 0, already checked during parsing */
+ precomp = pcre_compile(perl_regex, PCRE_UTF8 | PCRE_ANCHORED | PCRE_DOLLAR_ENDONLY | PCRE_NO_AUTO_CAPTURE,
+ &err_msg, &err_offset, NULL);
+ if (!precomp) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_INREGEXP, pattern, perl_regex + err_offset, err_msg);
+ free(perl_regex);
+ return LY_EVALID;
+ }
+ free(perl_regex);
+
+ if (pcre_precomp) {
+ *pcre_precomp = precomp;
+ } else {
+ free(precomp);
+ }
+
+ return LY_SUCCESS;
+
+#undef URANGE_LEN
+}
+
+/**
+ * @brief Compile parsed pattern restriction in conjunction with the patterns from base type.
+ * @param[in] ctx Compile context.
+ * @param[in] patterns_p Array of parsed patterns from the current type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] base_patterns Compiled patterns from the type from which the current type is derived.
+ * Patterns from the base type are inherited to have all the patterns that have to match at one place.
+ * @param[out] patterns Pointer to the storage for the patterns of the current type.
+ * @return LY_ERR LY_SUCCESS, LY_EMEM, LY_EVALID.
+ */
+static LY_ERR
+lys_compile_type_patterns(struct lysc_ctx *ctx, struct lysp_restr *patterns_p, int options,
+ struct lysc_pattern **base_patterns, struct lysc_pattern ***patterns)
+{
+ struct lysc_pattern **pattern;
+ unsigned int u, v;
+ const char *err_msg;
+ LY_ERR ret = LY_SUCCESS;
+
+ /* first, copy the patterns from the base type */
+ if (base_patterns) {
+ *patterns = lysc_patterns_dup(ctx->ctx, base_patterns);
+ LY_CHECK_ERR_RET(!(*patterns), LOGMEM(ctx->ctx), LY_EMEM);
+ }
+
+ LY_ARRAY_FOR(patterns_p, u) {
+ LY_ARRAY_NEW_RET(ctx->ctx, (*patterns), pattern, LY_EMEM);
+ *pattern = calloc(1, sizeof **pattern);
+ ++(*pattern)->refcount;
+
+ ret = lys_compile_type_pattern_check(ctx, &patterns_p[u].arg[1], &(*pattern)->expr);
+ LY_CHECK_RET(ret);
+ (*pattern)->expr_extra = pcre_study((*pattern)->expr, 0, &err_msg);
+ if (err_msg) {
+ LOGWRN(ctx->ctx, "Studying pattern \"%s\" failed (%s).", pattern, err_msg);
+ }
+
+ if (patterns_p[u].arg[0] == 0x15) {
+ (*pattern)->inverted = 1;
+ }
+ DUP_STRING(ctx->ctx, patterns_p[u].eapptag, (*pattern)->eapptag);
+ DUP_STRING(ctx->ctx, patterns_p[u].emsg, (*pattern)->emsg);
+ COMPILE_ARRAY_GOTO(ctx, patterns_p[u].exts, (*pattern)->exts,
+ options, v, lys_compile_ext, ret, done);
+ }
+done:
+ return ret;
+}
+
+/**
+ * @brief map of the possible restrictions combination for the specific built-in type.
+ */
+static uint16_t type_substmt_map[LY_DATA_TYPE_COUNT] = {
+ 0 /* LY_TYPE_UNKNOWN */,
+ LYS_SET_LENGTH /* LY_TYPE_BINARY */,
+ LYS_SET_RANGE /* LY_TYPE_UINT8 */,
+ LYS_SET_RANGE /* LY_TYPE_UINT16 */,
+ LYS_SET_RANGE /* LY_TYPE_UINT32 */,
+ LYS_SET_RANGE /* LY_TYPE_UINT64 */,
+ LYS_SET_LENGTH | LYS_SET_PATTERN /* LY_TYPE_STRING */,
+ LYS_SET_BIT /* LY_TYPE_BITS */,
+ 0 /* LY_TYPE_BOOL */,
+ LYS_SET_FRDIGITS | LYS_SET_RANGE /* LY_TYPE_DEC64 */,
+ 0 /* LY_TYPE_EMPTY */,
+ LYS_SET_ENUM /* LY_TYPE_ENUM */,
+ LYS_SET_BASE /* LY_TYPE_IDENT */,
+ LYS_SET_REQINST /* LY_TYPE_INST */,
+ LYS_SET_REQINST | LYS_SET_PATH /* LY_TYPE_LEAFREF */,
+ LYS_SET_TYPE /* LY_TYPE_UNION */,
+ LYS_SET_RANGE /* LY_TYPE_INT8 */,
+ LYS_SET_RANGE /* LY_TYPE_INT16 */,
+ LYS_SET_RANGE /* LY_TYPE_INT32 */,
+ LYS_SET_RANGE /* LY_TYPE_INT64 */
+};
+
+/**
+ * @brief stringification of the YANG built-in data types
+ */
+const char* ly_data_type2str[LY_DATA_TYPE_COUNT] = {"unknown", "binary", "8bit unsigned integer", "16bit unsigned integer",
+ "32bit unsigned integer", "64bit unsigned integer", "string", "bits", "boolean", "decimal64", "empty", "enumeration",
+ "identityref", "instance-identifier", "leafref", "union", "8bit integer", "16bit integer", "32bit integer", "64bit integer"
+};
+
+/**
+ * @brief Compile parsed type's enum structures (for enumeration and bits types).
+ * @param[in] ctx Compile context.
+ * @param[in] enums_p Array of the parsed enum structures to compile.
+ * @param[in] basetype Base YANG built-in type from which the current type is derived. Only LY_TYPE_ENUM and LY_TYPE_BITS are expected.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] base_enums Array of the compiled enums information from the (latest) base type to check if the current enums are compatible.
+ * @param[out] enums Newly created array of the compiled enums information for the current type.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_type_enums(struct lysc_ctx *ctx, struct lysp_type_enum *enums_p, LY_DATA_TYPE basetype, int options,
+ struct lysc_type_enum_item *base_enums, struct lysc_type_enum_item **enums)
+{
+ LY_ERR ret = LY_SUCCESS;
+ unsigned int u, v, match;
+ int32_t value = 0;
+ uint32_t position = 0;
+ struct lysc_type_enum_item *e, storage;
+
+ if (base_enums && ctx->mod_def->parsed->version < 2) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "%s type can be subtyped only in YANG 1.1 modules.",
+ basetype == LY_TYPE_ENUM ? "Enumeration" : "Bits");
+ return LY_EVALID;
+ }
+
+ LY_ARRAY_FOR(enums_p, u) {
+ LY_ARRAY_NEW_RET(ctx->ctx, *enums, e, LY_EMEM);
+ DUP_STRING(ctx->ctx, enums_p[u].name, e->name);
+ if (base_enums) {
+ /* check the enum/bit presence in the base type - the set of enums/bits in the derived type must be a subset */
+ LY_ARRAY_FOR(base_enums, v) {
+ if (!strcmp(e->name, base_enums[v].name)) {
+ break;
+ }
+ }
+ if (v == LY_ARRAY_SIZE(base_enums)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid %s - derived type adds new item \"%s\".",
+ basetype == LY_TYPE_ENUM ? "enumeration" : "bits", e->name);
+ return LY_EVALID;
+ }
+ match = v;
+ }
+
+ if (basetype == LY_TYPE_ENUM) {
+ if (enums_p[u].flags & LYS_SET_VALUE) {
+ e->value = (int32_t)enums_p[u].value;
+ if (!u || e->value >= value) {
+ value = e->value + 1;
+ }
+ /* check collision with other values */
+ for (v = 0; v < LY_ARRAY_SIZE(*enums) - 1; ++v) {
+ if (e->value == (*enums)[v].value) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid enumeration - value %d collide in items \"%s\" and \"%s\".",
+ e->value, e->name, (*enums)[v].name);
+ return LY_EVALID;
+ }
+ }
+ } else if (base_enums) {
+ /* inherit the assigned value */
+ e->value = base_enums[match].value;
+ if (!u || e->value >= value) {
+ value = e->value + 1;
+ }
+ } else {
+ /* assign value automatically */
+ if (u && value == INT32_MIN) {
+ /* counter overflow */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid enumeration - it is not possible to auto-assign enum value for "
+ "\"%s\" since the highest value is already 2147483647.", e->name);
+ return LY_EVALID;
+ }
+ e->value = value++;
+ }
+ } else { /* LY_TYPE_BITS */
+ if (enums_p[u].flags & LYS_SET_VALUE) {
+ e->value = (int32_t)enums_p[u].value;
+ if (!u || (uint32_t)e->value >= position) {
+ position = (uint32_t)e->value + 1;
+ }
+ /* check collision with other values */
+ for (v = 0; v < LY_ARRAY_SIZE(*enums) - 1; ++v) {
+ if (e->value == (*enums)[v].value) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid bits - position %u collide in items \"%s\" and \"%s\".",
+ (uint32_t)e->value, e->name, (*enums)[v].name);
+ return LY_EVALID;
+ }
+ }
+ } else if (base_enums) {
+ /* inherit the assigned value */
+ e->value = base_enums[match].value;
+ if (!u || (uint32_t)e->value >= position) {
+ position = (uint32_t)e->value + 1;
+ }
+ } else {
+ /* assign value automatically */
+ if (u && position == 0) {
+ /* counter overflow */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid bits - it is not possible to auto-assign bit position for "
+ "\"%s\" since the highest value is already 4294967295.", e->name);
+ return LY_EVALID;
+ }
+ e->value = position++;
+ }
+ }
+
+ if (base_enums) {
+ /* the assigned values must not change from the derived type */
+ if (e->value != base_enums[match].value) {
+ if (basetype == LY_TYPE_ENUM) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid enumeration - value of the item \"%s\" has changed from %d to %d in the derived type.",
+ e->name, base_enums[match].value, e->value);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid bits - position of the item \"%s\" has changed from %u to %u in the derived type.",
+ e->name, (uint32_t)base_enums[match].value, (uint32_t)e->value);
+ }
+ return LY_EVALID;
+ }
+ }
+
+ COMPILE_ARRAY_GOTO(ctx, enums_p[u].iffeatures, e->iffeatures, options, v, lys_compile_iffeature, ret, done);
+ COMPILE_ARRAY_GOTO(ctx, enums_p[u].exts, e->exts, options, v, lys_compile_ext, ret, done);
+
+ if (basetype == LY_TYPE_BITS) {
+ /* keep bits ordered by position */
+ for (v = u; v && (*enums)[v - 1].value > e->value; --v);
+ if (v != u) {
+ memcpy(&storage, e, sizeof *e);
+ memmove(&(*enums)[v + 1], &(*enums)[v], (u - v) * sizeof **enums);
+ memcpy(&(*enums)[v], &storage, sizeof storage);
+ }
+ }
+ }
+
+done:
+ return ret;
+}
+
+#define MOVE_PATH_PARENT(NODE, LIMIT_COND, TERM, ERR_MSG, ...) \
+ for ((NODE) = (NODE)->parent; \
+ (NODE) && !((NODE)->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_ACTION | LYS_NOTIF | LYS_ACTION)); \
+ (NODE) = (NODE)->parent); \
+ if (!(NODE) && (LIMIT_COND)) { /* we are going higher than top-level */ \
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, ERR_MSG, ##__VA_ARGS__); \
+ TERM; \
+ }
+
+/**
+ * @brief Validate the predicate(s) from the leafref path.
+ * @param[in] ctx Compile context
+ * @param[in, out] predicate Pointer to the predicate in the leafref path. The pointer is moved after the validated predicate(s).
+ * Since there can be multiple adjacent predicates for lists with multiple keys, all such predicates are validated.
+ * @param[in] start_node Path context node (where the path is instantiated).
+ * @param[in] context_node Predicate context node (where the predicate is placed).
+ * @param[in] path_context Schema where the path was defined to correct resolve of the prefixes.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_leafref_predicate_validate(struct lysc_ctx *ctx, const char **predicate, const struct lysc_node *start_node,
+ const struct lysc_node_list *context_node, const struct lys_module *path_context)
+{
+ LY_ERR ret = LY_EVALID;
+ const struct lys_module *mod;
+ const struct lysc_node *src_node, *dst_node;
+ const char *path_key_expr, *pke_start, *src, *src_prefix, *dst, *dst_prefix;
+ size_t src_len, src_prefix_len, dst_len, dst_prefix_len;
+ unsigned int dest_parent_times, c, u;
+ const char *start, *end, *pke_end;
+ struct ly_set keys = {0};
+ int i;
+
+ assert(path_context);
+
+ while (**predicate == '[') {
+ start = (*predicate)++;
+
+ while (isspace(**predicate)) {
+ ++(*predicate);
+ }
+ LY_CHECK_GOTO(lys_parse_nodeid(predicate, &src_prefix, &src_prefix_len, &src, &src_len), cleanup);
+ while (isspace(**predicate)) {
+ ++(*predicate);
+ }
+ if (**predicate != '=') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - missing \"=\" after node-identifier.",
+ *predicate - start + 1, start);
+ goto cleanup;
+ }
+ ++(*predicate);
+ while (isspace(**predicate)) {
+ ++(*predicate);
+ }
+
+ if ((end = pke_end = strchr(*predicate, ']')) == NULL) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%s\" - missing predicate termination.", start);
+ goto cleanup;
+ }
+ --pke_end;
+ while (isspace(*pke_end)) {
+ --pke_end;
+ }
+ ++pke_end;
+ /* localize path-key-expr */
+ pke_start = path_key_expr = *predicate;
+ /* move after the current predicate */
+ *predicate = end + 1;
+
+ /* source (must be leaf or leaf-list) */
+ if (src_prefix) {
+ mod = lys_module_find_prefix(path_context, src_prefix, src_prefix_len);
+ if (!mod) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - prefix \"%.*s\" not defined in module \"%s\".",
+ *predicate - start, start, src_prefix_len, src_prefix, path_context->compiled->name);
+ goto cleanup;
+ }
+ } else {
+ mod = start_node->module;
+ }
+ src_node = NULL;
+ if (context_node->keys) {
+ for (u = 0; u < LY_ARRAY_SIZE(context_node->keys); ++u) {
+ if (!strncmp(src, context_node->keys[u]->name, src_len) && context_node->keys[u]->name[src_len] == '\0') {
+ src_node = (const struct lysc_node*)context_node->keys[u];
+ break;
+ }
+ }
+ }
+ if (!src_node) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - predicate's key node \"%.*s\" not found.",
+ *predicate - start, start, src_len, src, mod->compiled->name);
+ goto cleanup;
+ }
+
+ /* check that there is only one predicate for the */
+ c = keys.count;
+ i = ly_set_add(&keys, (void*)src_node, 0);
+ LY_CHECK_GOTO(i == -1, cleanup);
+ if (keys.count == c) { /* node was already present in the set */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - multiple equality tests for the key \"%s\".",
+ *predicate - start, start, src_node->name);
+ goto cleanup;
+ }
+
+ /* destination */
+ dest_parent_times = 0;
+ dst_node = start_node;
+
+ /* current-function-invocation *WSP "/" *WSP rel-path-keyexpr */
+ if (strncmp(path_key_expr, "current()", 9)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - missing current-function-invocation.",
+ *predicate - start, start);
+ goto cleanup;
+ }
+ path_key_expr += 9;
+ while (isspace(*path_key_expr)) {
+ ++path_key_expr;
+ }
+
+ if (*path_key_expr != '/') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - missing \"/\" after current-function-invocation.",
+ *predicate - start, start);
+ goto cleanup;
+ }
+ ++path_key_expr;
+ while (isspace(*path_key_expr)) {
+ ++path_key_expr;
+ }
+
+ /* rel-path-keyexpr:
+ * 1*(".." *WSP "/" *WSP) *(node-identifier *WSP "/" *WSP) node-identifier */
+ while (!strncmp(path_key_expr, "..", 2)) {
+ ++dest_parent_times;
+ path_key_expr += 2;
+ while (isspace(*path_key_expr)) {
+ ++path_key_expr;
+ }
+ if (*path_key_expr != '/') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - missing \"/\" in \"../\" rel-path-keyexpr pattern.",
+ *predicate - start, start);
+ goto cleanup;
+ }
+ ++path_key_expr;
+ while (isspace(*path_key_expr)) {
+ ++path_key_expr;
+ }
+
+ /* path is supposed to be evaluated in data tree, so we have to skip
+ * all schema nodes that cannot be instantiated in data tree */
+ MOVE_PATH_PARENT(dst_node, !strncmp(path_key_expr, "..", 2), goto cleanup,
+ "Invalid leafref path predicate \"%.*s\" - too many \"..\" in rel-path-keyexpr.",
+ *predicate - start, start);
+ }
+ if (!dest_parent_times) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - at least one \"..\" is expected in rel-path-keyexpr.",
+ *predicate - start, start);
+ goto cleanup;
+ }
+ if (path_key_expr == pke_end) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - at least one node-identifier is expected in rel-path-keyexpr.",
+ *predicate - start, start);
+ goto cleanup;
+ }
+
+ while(path_key_expr != pke_end) {
+ if (lys_parse_nodeid(&path_key_expr, &dst_prefix, &dst_prefix_len, &dst, &dst_len)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid node identifier in leafref path predicate - character %d (of %.*s).",
+ path_key_expr - start + 1, *predicate - start, start);
+ goto cleanup;
+ }
+
+ if (dst_prefix) {
+ mod = lys_module_find_prefix(path_context, dst_prefix, dst_prefix_len);
+ } else {
+ mod = start_node->module;
+ }
+ if (!mod) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - unable to find module of the node \"%.*s\" in rel-path_keyexpr.",
+ *predicate - start, start, dst_len, dst);
+ goto cleanup;
+ }
+
+ dst_node = lys_child(dst_node, mod, dst, dst_len, 0, LYS_GETNEXT_NOSTATECHECK);
+ if (!dst_node) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - unable to find node \"%.*s\" in the rel-path_keyexpr.",
+ *predicate - start, start, path_key_expr - pke_start, pke_start);
+ goto cleanup;
+ }
+ }
+ if (!(dst_node->nodetype & (dst_node->module->compiled->version < LYS_VERSION_1_1 ? LYS_LEAF : LYS_LEAF | LYS_LEAFLIST))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - rel-path_keyexpr \"%.*s\" refers %s instead of leaf.",
+ *predicate - start, start, path_key_expr - pke_start, pke_start, lys_nodetype2str(dst_node->nodetype));
+ goto cleanup;
+ }
+ }
+
+ ret = LY_SUCCESS;
+cleanup:
+ ly_set_erase(&keys, NULL);
+ return ret;
+}
+
+/**
+ * @brief Parse path-arg (leafref). Get tokens of the path by repetitive calls of the function.
+ *
+ * path-arg = absolute-path / relative-path
+ * absolute-path = 1*("/" (node-identifier *path-predicate))
+ * relative-path = 1*(".." "/") descendant-path
+ *
+ * @param[in,out] path Path to parse.
+ * @param[out] prefix Prefix of the token, NULL if there is not any.
+ * @param[out] pref_len Length of the prefix, 0 if there is not any.
+ * @param[out] name Name of the token.
+ * @param[out] nam_len Length of the name.
+ * @param[out] parent_times Number of leading ".." in the path. Must be 0 on the first call,
+ * must not be changed between consecutive calls. -1 if the
+ * path is absolute.
+ * @param[out] has_predicate Flag to mark whether there is a predicate specified.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid character in the path.
+ */
+static LY_ERR
+lys_path_token(const char **path, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len,
+ int *parent_times, int *has_predicate)
+{
+ int par_times = 0;
+
+ assert(path && *path);
+ assert(parent_times);
+ assert(prefix);
+ assert(prefix_len);
+ assert(name);
+ assert(name_len);
+ assert(has_predicate);
+
+ *prefix = NULL;
+ *prefix_len = 0;
+ *name = NULL;
+ *name_len = 0;
+ *has_predicate = 0;
+
+ if (!*parent_times) {
+ if (!strncmp(*path, "..", 2)) {
+ *path += 2;
+ ++par_times;
+ while (!strncmp(*path, "/..", 3)) {
+ *path += 3;
+ ++par_times;
+ }
+ }
+ if (par_times) {
+ *parent_times = par_times;
+ } else {
+ *parent_times = -1;
+ }
+ }
+
+ if (**path != '/') {
+ return LY_EINVAL;
+ }
+ /* skip '/' */
+ ++(*path);
+
+ /* node-identifier ([prefix:]name) */
+ LY_CHECK_RET(lys_parse_nodeid(path, prefix, prefix_len, name, name_len));
+
+ if ((**path == '/' && (*path)[1]) || !**path) {
+ /* path continues by another token or this is the last token */
+ return LY_SUCCESS;
+ } else if ((*path)[0] != '[') {
+ /* unexpected character */
+ return LY_EINVAL;
+ } else {
+ /* predicate starting with [ */
+ *has_predicate = 1;
+ return LY_SUCCESS;
+ }
+}
+
+/**
+ * @brief Check the features used in if-feature statements applicable to the leafref and its target.
+ *
+ * The set of features used for target must be a subset of features used for the leafref.
+ * This is not a perfect, we should compare the truth tables but it could require too much resources
+ * and RFC 7950 does not require it explicitely, so we simplify that.
+ *
+ * @param[in] refnode The leafref node.
+ * @param[in] target Tha target node of the leafref.
+ * @return LY_SUCCESS or LY_EVALID;
+ */
+static LY_ERR
+lys_compile_leafref_features_validate(const struct lysc_node *refnode, const struct lysc_node *target)
+{
+ LY_ERR ret = LY_EVALID;
+ const struct lysc_node *iter;
+ unsigned int u, v, count;
+ struct ly_set features = {0};
+
+ for (iter = refnode; iter; iter = iter->parent) {
+ if (iter->iffeatures) {
+ LY_ARRAY_FOR(iter->iffeatures, u) {
+ LY_ARRAY_FOR(iter->iffeatures[u].features, v) {
+ LY_CHECK_GOTO(ly_set_add(&features, iter->iffeatures[u].features[v], 0) == -1, cleanup);
+ }
+ }
+ }
+ }
+
+ /* we should have, in features set, a superset of features applicable to the target node.
+ * So when adding features applicable to the target into the features set, we should not be
+ * able to actually add any new feature, otherwise it is not a subset of features applicable
+ * to the leafref itself. */
+ count = features.count;
+ for (iter = target; iter; iter = iter->parent) {
+ if (iter->iffeatures) {
+ LY_ARRAY_FOR(iter->iffeatures, u) {
+ LY_ARRAY_FOR(iter->iffeatures[u].features, v) {
+ if ((unsigned int)ly_set_add(&features, iter->iffeatures[u].features[v], 0) >= count) {
+ /* new feature was added (or LY_EMEM) */
+ goto cleanup;
+ }
+ }
+ }
+ }
+ }
+ ret = LY_SUCCESS;
+
+cleanup:
+ ly_set_erase(&features, NULL);
+ return ret;
+}
+
+/**
+ * @brief Validate the leafref path.
+ * @param[in] ctx Compile context
+ * @param[in] startnode Path context node (where the leafref path begins/is placed).
+ * @param[in] leafref Leafref to validate.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_leafref_validate(struct lysc_ctx *ctx, struct lysc_node *startnode, struct lysc_type_leafref *leafref)
+{
+ const struct lysc_node *node = NULL, *parent = NULL;
+ const struct lys_module *mod;
+ struct lysc_type *type;
+ const char *id, *prefix, *name;
+ size_t prefix_len, name_len;
+ int parent_times = 0, has_predicate;
+ unsigned int iter, u;
+ LY_ERR ret = LY_SUCCESS;
+
+ assert(ctx);
+ assert(startnode);
+ assert(leafref);
+
+ /* TODO leafref targets may be not implemented, in such a case we actually could make (we did it in libyang1) such a models implemented */
+
+ iter = 0;
+ id = leafref->path;
+ while(*id && (ret = lys_path_token(&id, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate)) == LY_SUCCESS) {
+ if (!iter) { /* first iteration */
+ /* precess ".." in relative paths */
+ if (parent_times > 0) {
+ /* move from the context node */
+ for (u = 0, parent = startnode; u < (unsigned int)parent_times; u++) {
+ /* path is supposed to be evaluated in data tree, so we have to skip
+ * all schema nodes that cannot be instantiated in data tree */
+ MOVE_PATH_PARENT(parent, u < (unsigned int)parent_times - 1, return LY_EVALID,
+ "Invalid leafref path \"%s\" - too many \"..\" in the path.", leafref->path);
+ }
+ }
+ }
+
+ if (prefix) {
+ mod = lys_module_find_prefix(leafref->path_context, prefix, prefix_len);
+ } else {
+ mod = startnode->module;
+ }
+ if (!mod) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path - unable to find module connected with the prefix of the node \"%.*s\".",
+ id - leafref->path, leafref->path);
+ return LY_EVALID;
+ }
+
+ node = lys_child(parent, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+ if (!node) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path - unable to find \"%.*s\".", id - leafref->path, leafref->path);
+ return LY_EVALID;
+ }
+ parent = node;
+
+ if (has_predicate) {
+ /* we have predicate, so the current result must be list */
+ if (node->nodetype != LYS_LIST) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path - node \"%.*s\" is expected to be a list, but it is %s.",
+ id - leafref->path, leafref->path, lys_nodetype2str(node->nodetype));
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lys_compile_leafref_predicate_validate(ctx, &id, startnode, (struct lysc_node_list*)node, leafref->path_context),
+ LY_EVALID);
+ }
+
+ ++iter;
+ }
+ if (ret) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid leafref path at character %d (%s).", id - leafref->path + 1, leafref->path);
+ return LY_EVALID;
+ }
+
+ if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path \"%s\" - target node is %s instead of leaf or leaf-list.",
+ leafref->path, lys_nodetype2str(node->nodetype));
+ return LY_EVALID;
+ }
+
+ /* check status */
+ if (lysc_check_status(ctx, startnode->flags, startnode->module, startnode->name, node->flags, node->module, node->name)) {
+ return LY_EVALID;
+ }
+
+ /* check config */
+ if (leafref->require_instance && (startnode->flags & LYS_CONFIG_W)) {
+ if (node->flags & LYS_CONFIG_R) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path \"%s\" - target is supposed to represent configuration data (as the leafref does), but it does not.",
+ leafref->path);
+ return LY_EVALID;
+ }
+ }
+
+ /* store the target's type and check for circular chain of leafrefs */
+ leafref->realtype = ((struct lysc_node_leaf*)node)->type;
+ for (type = leafref->realtype; type && type->basetype == LY_TYPE_LEAFREF; type = ((struct lysc_type_leafref*)type)->realtype) {
+ if (type == (struct lysc_type*)leafref) {
+ /* circular chain detected */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path \"%s\" - circular chain of leafrefs detected.", leafref->path);
+ return LY_EVALID;
+ }
+ }
+
+ /* check if leafref and its target are under common if-features */
+ if (lys_compile_leafref_features_validate(startnode, node)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path \"%s\" - set of features applicable to the leafref target is not a subset of features applicable to the leafref itself.",
+ leafref->path);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_node_p, uint16_t context_flags, struct lysp_module *context_mod, const char *context_name,
+ struct lysp_type *type_p, int options, struct lysc_type **type, const char **units, const char **dflt);
+/**
+ * @brief The core of the lys_compile_type() - compile information about the given type (from typedef or leaf/leaf-list).
+ * @param[in] ctx Compile context.
+ * @param[in] context_node_p Schema node where the type/typedef is placed to correctly find the base types.
+ * @param[in] context_flags Flags of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_mod Module of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_name Name of the context node or referencing typedef for logging.
+ * @param[in] type_p Parsed type to compile.
+ * @param[in] module Context module for the leafref path (to correctly resolve prefixes in path)
+ * @param[in] basetype Base YANG built-in type of the type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] tpdfname Name of the type's typedef, serves as a flag - if it is leaf/leaf-list's type, it is NULL.
+ * @param[in] base The latest base (compiled) type from which the current type is being derived.
+ * @param[out] type Newly created type structure with the filled information about the type.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_node_p, uint16_t context_flags, struct lysp_module *context_mod, const char *context_name,
+ struct lysp_type *type_p, struct lys_module *module, LY_DATA_TYPE basetype, int options, const char *tpdfname,
+ struct lysc_type *base, struct lysc_type **type)
+{
+ LY_ERR ret = LY_SUCCESS;
+ unsigned int u, v, additional;
+ struct lysc_type_bin *bin;
+ struct lysc_type_num *num;
+ struct lysc_type_str *str;
+ struct lysc_type_bits *bits;
+ struct lysc_type_enum *enumeration;
+ struct lysc_type_dec *dec;
+ struct lysc_type_identityref *idref;
+ struct lysc_type_union *un, *un_aux;
+ void *p;
+
+ switch (basetype) {
+ case LY_TYPE_BINARY:
+ bin = (struct lysc_type_bin*)(*type);
+
+ /* RFC 7950 9.8.1, 9.4.4 - length, number of octets it contains */
+ if (type_p->length) {
+ ret = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+ base ? ((struct lysc_type_bin*)base)->length : NULL, &bin->length);
+ LY_CHECK_RET(ret);
+ if (!tpdfname) {
+ COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, bin->length->exts,
+ options, u, lys_compile_ext, ret, done);
+ }
+ }
+
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_bin));
+ }
+ break;
+ case LY_TYPE_BITS:
+ /* RFC 7950 9.7 - bits */
+ bits = (struct lysc_type_bits*)(*type);
+ if (type_p->bits) {
+ ret = lys_compile_type_enums(ctx, type_p->bits, basetype, options,
+ base ? (struct lysc_type_enum_item*)((struct lysc_type_bits*)base)->bits : NULL,
+ (struct lysc_type_enum_item**)&bits->bits);
+ LY_CHECK_RET(ret);
+ }
+
+ if (!base && !type_p->flags) {
+ /* type derived from bits built-in type must contain at least one bit */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "bit", "bits type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "bit", "bits type", "");
+ free(*type);
+ *type = NULL;
+ }
+ return LY_EVALID;
+ }
+
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_bits));
+ }
+ break;
+ case LY_TYPE_DEC64:
+ dec = (struct lysc_type_dec*)(*type);
+
+ /* RFC 7950 9.3.4 - fraction-digits */
+ if (!base) {
+ if (!type_p->fraction_digits) {
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "fraction-digits", "decimal64 type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "fraction-digits", "decimal64 type", "");
+ free(*type);
+ *type = NULL;
+ }
+ return LY_EVALID;
+ }
+ } else if (type_p->fraction_digits) {
+ /* fraction digits is prohibited in types not directly derived from built-in decimal64 */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid fraction-digits substatement for type \"%s\" not directly derived from decimal64 built-in type.",
+ tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.");
+ free(*type);
+ *type = NULL;
+ }
+ return LY_EVALID;
+ }
+ dec->fraction_digits = type_p->fraction_digits;
+
+ /* RFC 7950 9.2.4 - range */
+ if (type_p->range) {
+ ret = lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
+ base ? ((struct lysc_type_dec*)base)->range : NULL, &dec->range);
+ LY_CHECK_RET(ret);
+ if (!tpdfname) {
+ COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, dec->range->exts,
+ options, u, lys_compile_ext, ret, done);
+ }
+ }
+
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_dec));
+ }
+ break;
+ case LY_TYPE_STRING:
+ str = (struct lysc_type_str*)(*type);
+
+ /* RFC 7950 9.4.4 - length */
+ if (type_p->length) {
+ ret = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+ base ? ((struct lysc_type_str*)base)->length : NULL, &str->length);
+ LY_CHECK_RET(ret);
+ if (!tpdfname) {
+ COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, str->length->exts,
+ options, u, lys_compile_ext, ret, done);
+ }
+ } else if (base && ((struct lysc_type_str*)base)->length) {
+ str->length = lysc_range_dup(ctx->ctx, ((struct lysc_type_str*)base)->length);
+ }
+
+ /* RFC 7950 9.4.5 - pattern */
+ if (type_p->patterns) {
+ ret = lys_compile_type_patterns(ctx, type_p->patterns, options,
+ base ? ((struct lysc_type_str*)base)->patterns : NULL, &str->patterns);
+ LY_CHECK_RET(ret);
+ } else if (base && ((struct lysc_type_str*)base)->patterns) {
+ str->patterns = lysc_patterns_dup(ctx->ctx, ((struct lysc_type_str*)base)->patterns);
+ }
+
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_str));
+ }
+ break;
+ case LY_TYPE_ENUM:
+ enumeration = (struct lysc_type_enum*)(*type);
+
+ /* RFC 7950 9.6 - enum */
+ if (type_p->enums) {
+ ret = lys_compile_type_enums(ctx, type_p->enums, basetype, options,
+ base ? ((struct lysc_type_enum*)base)->enums : NULL, &enumeration->enums);
+ LY_CHECK_RET(ret);
+ }
+
+ if (!base && !type_p->flags) {
+ /* type derived from enumerations built-in type must contain at least one enum */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "enum", "enumeration type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "enum", "enumeration type", "");
+ free(*type);
+ *type = NULL;
+ }
+ return LY_EVALID;
+ }
+
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_enum));
+ }
+ break;
+ case LY_TYPE_INT8:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ num = (struct lysc_type_num*)(*type);
+
+ /* RFC 6020 9.2.4 - range */
+ if (type_p->range) {
+ ret = lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
+ base ? ((struct lysc_type_num*)base)->range : NULL, &num->range);
+ LY_CHECK_RET(ret);
+ if (!tpdfname) {
+ COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, num->range->exts,
+ options, u, lys_compile_ext, ret, done);
+ }
+ }
+
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_num));
+ }
+ break;
+ case LY_TYPE_IDENT:
+ idref = (struct lysc_type_identityref*)(*type);
+
+ /* RFC 7950 9.10.2 - base */
+ if (type_p->bases) {
+ if (base) {
+ /* only the directly derived identityrefs can contain base specification */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid base substatement for the type \"%s\" not directly derived from identityref built-in type.",
+ tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid base substatement for the type not directly derived from identityref built-in type.");
+ free(*type);
+ *type = NULL;
+ }
+ return LY_EVALID;
+ }
+ ret = lys_compile_identity_bases(ctx, type_p->bases, NULL, &idref->bases);
+ LY_CHECK_RET(ret);
+ }
+
+ if (!base && !type_p->flags) {
+ /* type derived from identityref built-in type must contain at least one base */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "base", "identityref type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "base", "identityref type", "");
+ free(*type);
+ *type = NULL;
+ }
+ return LY_EVALID;
+ }
+
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_identityref));
+ }
+ break;
+ case LY_TYPE_LEAFREF:
+ /* RFC 7950 9.9.3 - require-instance */
+ if (type_p->flags & LYS_SET_REQINST) {
+ if (context_mod->version < LYS_VERSION_1_1) {
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Leafref type \"%s\" can be restricted by require-instance statement only in YANG 1.1 modules.", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.");
+ free(*type);
+ *type = NULL;
+ }
+ return LY_EVALID;
+ }
+ ((struct lysc_type_leafref*)(*type))->require_instance = type_p->require_instance;
+ } else if (base) {
+ /* inherit */
+ ((struct lysc_type_leafref*)(*type))->require_instance = ((struct lysc_type_leafref*)base)->require_instance;
+ } else {
+ /* default is true */
+ ((struct lysc_type_leafref*)(*type))->require_instance = 1;
+ }
+ if (type_p->path) {
+ DUP_STRING(ctx->ctx, (void*)type_p->path, ((struct lysc_type_leafref*)(*type))->path);
+ ((struct lysc_type_leafref*)(*type))->path_context = module;
+ } else if (base) {
+ DUP_STRING(ctx->ctx, ((struct lysc_type_leafref*)base)->path, ((struct lysc_type_leafref*)(*type))->path);
+ ((struct lysc_type_leafref*)(*type))->path_context = ((struct lysc_type_leafref*)base)->path_context;
+ } else if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "path", "leafref type ", tpdfname);
+ return LY_EVALID;
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "path", "leafref type", "");
+ free(*type);
+ *type = NULL;
+ return LY_EVALID;
+ }
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_leafref));
+ }
+ break;
+ case LY_TYPE_INST:
+ /* RFC 7950 9.9.3 - require-instance */
+ if (type_p->flags & LYS_SET_REQINST) {
+ ((struct lysc_type_instanceid*)(*type))->require_instance = type_p->require_instance;
+ } else {
+ /* default is true */
+ ((struct lysc_type_instanceid*)(*type))->require_instance = 1;
+ }
+
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_instanceid));
+ }
+ break;
+ case LY_TYPE_UNION:
+ un = (struct lysc_type_union*)(*type);
+
+ /* RFC 7950 7.4 - type */
+ if (type_p->types) {
+ if (base) {
+ /* only the directly derived union can contain types specification */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid type substatement for the type \"%s\" not directly derived from union built-in type.",
+ tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid type substatement for the type not directly derived from union built-in type.");
+ free(*type);
+ *type = NULL;
+ }
+ return LY_EVALID;
+ }
+ /* compile the type */
+ additional = 0;
+ LY_ARRAY_CREATE_RET(ctx->ctx, un->types, LY_ARRAY_SIZE(type_p->types), LY_EVALID);
+ for (u = 0; u < LY_ARRAY_SIZE(type_p->types); ++u) {
+ ret = lys_compile_type(ctx, context_node_p, context_flags, context_mod, context_name, &type_p->types[u], options, &un->types[u + additional], NULL, NULL);
+ if (un->types[u + additional]->basetype == LY_TYPE_UNION) {
+ /* add space for additional types from the union subtype */
+ un_aux = (struct lysc_type_union *)un->types[u + additional];
+ p = ly_realloc(((uint32_t*)(un->types) - 1), sizeof(uint32_t) + ((LY_ARRAY_SIZE(type_p->types) + additional + LY_ARRAY_SIZE(un_aux->types) - 1) * sizeof *(un->types)));
+ LY_CHECK_ERR_RET(!p, LOGMEM(ctx->ctx);lysc_type_free(ctx->ctx, (struct lysc_type*)un_aux), LY_EMEM);
+ un->types = (void*)((uint32_t*)(p) + 1);
+
+ /* copy subtypes of the subtype union */
+ for (v = 0; v < LY_ARRAY_SIZE(un_aux->types); ++v) {
+ if (un_aux->types[v]->basetype == LY_TYPE_LEAFREF) {
+ /* duplicate the whole structure because of the instance-specific path resolving for realtype */
+ un->types[u + additional] = calloc(1, sizeof(struct lysc_type_leafref));
+ LY_CHECK_ERR_RET(!un->types[u + additional], LOGMEM(ctx->ctx);lysc_type_free(ctx->ctx, (struct lysc_type*)un_aux), LY_EMEM);
+ ((struct lysc_type_leafref*)un->types[u + additional])->basetype = LY_TYPE_LEAFREF;
+ DUP_STRING(ctx->ctx, ((struct lysc_type_leafref*)un_aux->types[v])->path, ((struct lysc_type_leafref*)un->types[u + additional])->path);
+ ((struct lysc_type_leafref*)un->types[u + additional])->refcount = 1;
+ ((struct lysc_type_leafref*)un->types[u + additional])->require_instance = ((struct lysc_type_leafref*)un_aux->types[v])->require_instance;
+ ((struct lysc_type_leafref*)un->types[u + additional])->path_context = ((struct lysc_type_leafref*)un_aux->types[v])->path_context;
+ /* TODO extensions */
+
+ } else {
+ un->types[u + additional] = un_aux->types[v];
+ ++un_aux->types[v]->refcount;
+ }
+ ++additional;
+ LY_ARRAY_INCREMENT(un->types);
+ }
+ /* compensate u increment in main loop */
+ --additional;
+
+ /* free the replaced union subtype */
+ lysc_type_free(ctx->ctx, (struct lysc_type*)un_aux);
+ } else {
+ LY_ARRAY_INCREMENT(un->types);
+ }
+ LY_CHECK_RET(ret);
+ }
+ }
+
+ if (!base && !type_p->flags) {
+ /* type derived from union built-in type must contain at least one type */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "type", "union type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "type", "union type", "");
+ free(*type);
+ *type = NULL;
+ }
+ return LY_EVALID;
+ }
+
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_union));
+ }
+ break;
+ case LY_TYPE_BOOL:
+ case LY_TYPE_EMPTY:
+ case LY_TYPE_UNKNOWN: /* just to complete switch */
+ break;
+ }
+ LY_CHECK_ERR_RET(!(*type), LOGMEM(ctx->ctx), LY_EMEM);
+done:
+ return ret;
+}
+
+/**
+ * @brief Compile information about the leaf/leaf-list's type.
+ * @param[in] ctx Compile context.
+ * @param[in] context_node_p Schema node where the type/typedef is placed to correctly find the base types.
+ * @param[in] context_flags Flags of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_mod Module of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_name Name of the context node or referencing typedef for logging.
+ * @param[in] type_p Parsed type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[out] type Newly created (or reused with increased refcount) type structure with the filled information about the type.
+ * @param[out] units Storage for inheriting units value from the typedefs the current type derives from.
+ * @param[out] dflt Storage for inheriting default value from the typedefs the current type derives from.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_node_p, uint16_t context_flags, struct lysp_module *context_mod, const char *context_name,
+ struct lysp_type *type_p, int options, struct lysc_type **type, const char **units, const char **dflt)
+{
+ LY_ERR ret = LY_SUCCESS;
+ unsigned int u;
+ int dummyloops = 0;
+ struct type_context {
+ const struct lysp_tpdf *tpdf;
+ struct lysp_node *node;
+ struct lysp_module *mod;
+ } *tctx, *tctx_prev = NULL;
+ LY_DATA_TYPE basetype = LY_TYPE_UNKNOWN;
+ struct lysc_type *base = NULL, *prev_type;
+ struct ly_set tpdf_chain = {0};
+
+ (*type) = NULL;
+
+ tctx = calloc(1, sizeof *tctx);
+ LY_CHECK_ERR_RET(!tctx, LOGMEM(ctx->ctx), LY_EMEM);
+ for (ret = lysp_type_find(type_p->name, context_node_p, ctx->mod_def->parsed,
+ &basetype, &tctx->tpdf, &tctx->node, &tctx->mod);
+ ret == LY_SUCCESS;
+ ret = lysp_type_find(tctx_prev->tpdf->type.name, tctx_prev->node, tctx_prev->mod,
+ &basetype, &tctx->tpdf, &tctx->node, &tctx->mod)) {
+ if (basetype) {
+ break;
+ }
+
+ /* check status */
+ ret = lysc_check_status(ctx, context_flags, context_mod, context_name,
+ tctx->tpdf->flags, tctx->mod, tctx->node ? tctx->node->name : tctx->tpdf->name);
+ LY_CHECK_ERR_GOTO(ret, free(tctx), cleanup);
+
+ if (units && !*units) {
+ /* inherit units */
+ DUP_STRING(ctx->ctx, tctx->tpdf->units, *units);
+ }
+ if (dflt && !*dflt) {
+ /* inherit default */
+ DUP_STRING(ctx->ctx, tctx->tpdf->dflt, *dflt);
+ }
+ if (dummyloops && (!units || *units) && (!dflt || *dflt)) {
+ basetype = ((struct type_context*)tpdf_chain.objs[tpdf_chain.count - 1])->tpdf->type.compiled->basetype;
+ break;
+ }
+
+ if (tctx->tpdf->type.compiled) {
+ /* it is not necessary to continue, the rest of the chain was already compiled,
+ * but we still may need to inherit default and units values, so start dummy loops */
+ basetype = tctx->tpdf->type.compiled->basetype;
+ ly_set_add(&tpdf_chain, tctx, LY_SET_OPT_USEASLIST);
+ if ((units && !*units) || (dflt && !*dflt)) {
+ dummyloops = 1;
+ goto preparenext;
+ } else {
+ tctx = NULL;
+ break;
+ }
+ }
+
+ /* store information for the following processing */
+ ly_set_add(&tpdf_chain, tctx, LY_SET_OPT_USEASLIST);
+
+preparenext:
+ /* prepare next loop */
+ tctx_prev = tctx;
+ tctx = calloc(1, sizeof *tctx);
+ LY_CHECK_ERR_RET(!tctx, LOGMEM(ctx->ctx), LY_EMEM);
+ }
+ free(tctx);
+
+ /* allocate type according to the basetype */
+ switch (basetype) {
+ case LY_TYPE_BINARY:
+ *type = calloc(1, sizeof(struct lysc_type_bin));
+ break;
+ case LY_TYPE_BITS:
+ *type = calloc(1, sizeof(struct lysc_type_bits));
+ break;
+ case LY_TYPE_BOOL:
+ case LY_TYPE_EMPTY:
+ *type = calloc(1, sizeof(struct lysc_type));
+ break;
+ case LY_TYPE_DEC64:
+ *type = calloc(1, sizeof(struct lysc_type_dec));
+ break;
+ case LY_TYPE_ENUM:
+ *type = calloc(1, sizeof(struct lysc_type_enum));
+ break;
+ case LY_TYPE_IDENT:
+ *type = calloc(1, sizeof(struct lysc_type_identityref));
+ break;
+ case LY_TYPE_INST:
+ *type = calloc(1, sizeof(struct lysc_type_instanceid));
+ break;
+ case LY_TYPE_LEAFREF:
+ *type = calloc(1, sizeof(struct lysc_type_leafref));
+ break;
+ case LY_TYPE_STRING:
+ *type = calloc(1, sizeof(struct lysc_type_str));
+ break;
+ case LY_TYPE_UNION:
+ *type = calloc(1, sizeof(struct lysc_type_union));
+ break;
+ case LY_TYPE_INT8:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ *type = calloc(1, sizeof(struct lysc_type_num));
+ break;
+ case LY_TYPE_UNKNOWN:
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Referenced type \"%s\" not found.", tctx_prev ? tctx_prev->tpdf->type.name : type_p->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ LY_CHECK_ERR_GOTO(!(*type), LOGMEM(ctx->ctx), cleanup);
+ if (~type_substmt_map[basetype] & type_p->flags) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid type restrictions for %s type.",
+ ly_data_type2str[basetype]);
+ free(*type);
+ (*type) = NULL;
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* get restrictions from the referred typedefs */
+ for (u = tpdf_chain.count - 1; u + 1 > 0; --u) {
+ tctx = (struct type_context*)tpdf_chain.objs[u];
+ if (tctx->tpdf->type.compiled) {
+ base = tctx->tpdf->type.compiled;
+ continue;
+ } else if (basetype != LY_TYPE_LEAFREF && (u != tpdf_chain.count - 1) && !(tctx->tpdf->type.flags)) {
+ /* no change, just use the type information from the base */
+ base = ((struct lysp_tpdf*)tctx->tpdf)->type.compiled = ((struct type_context*)tpdf_chain.objs[u + 1])->tpdf->type.compiled;
+ ++base->refcount;
+ continue;
+ }
+
+ ++(*type)->refcount;
+ if (~type_substmt_map[basetype] & tctx->tpdf->type.flags) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid type \"%s\" restriction(s) for %s type.",
+ tctx->tpdf->name, ly_data_type2str[basetype]);
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if (basetype == LY_TYPE_EMPTY && tctx->tpdf->dflt) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid type \"%s\" - \"empty\" type must not have a default value (%s).",
+ tctx->tpdf->name, tctx->tpdf->dflt);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ (*type)->basetype = basetype;
+ prev_type = *type;
+ ret = lys_compile_type_(ctx, tctx->node, tctx->tpdf->flags, tctx->mod, tctx->tpdf->name, &((struct lysp_tpdf*)tctx->tpdf)->type,
+ basetype & (LY_TYPE_LEAFREF | LY_TYPE_UNION) ? lysp_find_module(ctx->ctx, tctx->mod) : NULL,
+ basetype, options, tctx->tpdf->name, base, type);
+ LY_CHECK_GOTO(ret, cleanup);
+ base = prev_type;
+ }
+
+ /* process the type definition in leaf */
+ if (type_p->flags || !base || basetype == LY_TYPE_LEAFREF) {
+ /* get restrictions from the node itself */
+ (*type)->basetype = basetype;
+ ++(*type)->refcount;
+ ret = lys_compile_type_(ctx, context_node_p, context_flags, context_mod, context_name, type_p, ctx->mod_def, basetype, options, NULL, base, type);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ /* no specific restriction in leaf's type definition, copy from the base */
+ free(*type);
+ (*type) = base;
+ ++(*type)->refcount;
+ }
+
+ COMPILE_ARRAY_GOTO(ctx, type_p->exts, (*type)->exts, options, u, lys_compile_ext, ret, cleanup);
+
+cleanup:
+ ly_set_erase(&tpdf_chain, free);
+ return ret;
+}
+
+static LY_ERR lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *parent);
+
+/**
+ * @brief Compile parsed container node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed container node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the container-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_container(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+ struct lysp_node_container *cont_p = (struct lysp_node_container*)node_p;
+ struct lysc_node_container *cont = (struct lysc_node_container*)node;
+ struct lysp_node *child_p;
+ unsigned int u;
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_LIST_FOR(cont_p->child, child_p) {
+ LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node));
+ }
+
+ COMPILE_ARRAY_GOTO(ctx, cont_p->musts, cont->musts, options, u, lys_compile_must, ret, done);
+ //COMPILE_ARRAY_GOTO(ctx, cont_p->actions, cont->actions, options, u, lys_compile_action, ret, done);
+ //COMPILE_ARRAY_GOTO(ctx, cont_p->notifs, cont->notifs, options, u, lys_compile_notif, ret, done);
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Compile parsed leaf node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed leaf node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the leaf-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_leaf(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+ struct lysp_node_leaf *leaf_p = (struct lysp_node_leaf*)node_p;
+ struct lysc_node_leaf *leaf = (struct lysc_node_leaf*)node;
+ unsigned int u;
+ LY_ERR ret = LY_SUCCESS;
+
+ COMPILE_ARRAY_GOTO(ctx, leaf_p->musts, leaf->musts, options, u, lys_compile_must, ret, done);
+ DUP_STRING(ctx->ctx, leaf_p->units, leaf->units);
+ DUP_STRING(ctx->ctx, leaf_p->dflt, leaf->dflt);
+ if (leaf->dflt) {
+ leaf->flags |= LYS_SET_DFLT;
+ }
+
+ ret = lys_compile_type(ctx, node_p, node_p->flags, ctx->mod_def->parsed, node_p->name, &leaf_p->type, options, &leaf->type,
+ leaf->units ? NULL : &leaf->units, leaf->dflt || (leaf->flags & LYS_MAND_TRUE) ? NULL : &leaf->dflt);
+ LY_CHECK_GOTO(ret, done);
+ if (leaf->type->basetype == LY_TYPE_LEAFREF) {
+ /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
+ ly_set_add(&ctx->unres, leaf, 0);
+ } else if (leaf->type->basetype == LY_TYPE_UNION) {
+ LY_ARRAY_FOR(((struct lysc_type_union*)leaf->type)->types, u) {
+ if (((struct lysc_type_union*)leaf->type)->types[u]->basetype == LY_TYPE_LEAFREF) {
+ /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
+ ly_set_add(&ctx->unres, leaf, 0);
+ }
+ }
+ } else if (leaf->type->basetype == LY_TYPE_EMPTY && leaf_p->dflt) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Leaf of type \"empty\" must not have a default value (%s).",leaf_p->dflt);
+ return LY_EVALID;
+ }
+
+ /* TODO validate default value according to the type, possibly postpone the check when the leafref target is known */
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Compile parsed leaf-list node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed leaf-list node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the leaf-list-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_leaflist(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+ struct lysp_node_leaflist *llist_p = (struct lysp_node_leaflist*)node_p;
+ struct lysc_node_leaflist *llist = (struct lysc_node_leaflist*)node;
+ unsigned int u, v;
+ const char *dflt = NULL;
+ LY_ERR ret = LY_SUCCESS;
+
+ COMPILE_ARRAY_GOTO(ctx, llist_p->musts, llist->musts, options, u, lys_compile_must, ret, done);
+ DUP_STRING(ctx->ctx, llist_p->units, llist->units);
+
+ if (llist_p->dflts) {
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, llist->dflts, LY_ARRAY_SIZE(llist_p->dflts), ret, done);
+ LY_ARRAY_FOR(llist_p->dflts, u) {
+ DUP_STRING(ctx->ctx, llist_p->dflts[u], llist->dflts[u]);
+ LY_ARRAY_INCREMENT(llist->dflts);
+ }
+ }
+
+ llist->min = llist_p->min;
+ 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,
+ llist->units ? NULL : &llist->units, (llist->dflts || llist->min) ? NULL : &dflt);
+ LY_CHECK_GOTO(ret, done);
+ if (dflt) {
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, llist->dflts, 1, ret, done);
+ llist->dflts[0] = dflt;
+ LY_ARRAY_INCREMENT(llist->dflts);
+ }
+
+ if (llist->type->basetype == LY_TYPE_LEAFREF) {
+ /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
+ ly_set_add(&ctx->unres, llist, 0);
+ } else if (llist->type->basetype == LY_TYPE_UNION) {
+ LY_ARRAY_FOR(((struct lysc_type_union*)llist->type)->types, u) {
+ if (((struct lysc_type_union*)llist->type)->types[u]->basetype == LY_TYPE_LEAFREF) {
+ /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
+ ly_set_add(&ctx->unres, llist, 0);
+ }
+ }
+ } else if (llist->type->basetype == LY_TYPE_EMPTY) {
+ if (ctx->mod_def->parsed->version < LYS_VERSION_1_1) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.");
+ return LY_EVALID;
+ } else if (llist_p->dflts) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Leaf-list of type \"empty\" must not have a default value (%s).", llist_p->dflts[0]);
+ return LY_EVALID;
+ }
+ }
+
+ if ((llist->flags & LYS_CONFIG_W) && llist->dflts && LY_ARRAY_SIZE(llist->dflts)) {
+ /* configuration data values must be unique - so check the default values */
+ LY_ARRAY_FOR(llist->dflts, u) {
+ for (v = u + 1; v < LY_ARRAY_SIZE(llist->dflts); ++v) {
+ if (!strcmp(llist->dflts[u], llist->dflts[v])) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Configuration leaf-list has multiple defaults of the same value \"%s\".", llist->dflts[v]);
+ return LY_EVALID;
+ }
+ }
+ }
+ }
+
+ /* TODO validate default value according to the type, possibly postpone the check when the leafref target is known */
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Compile parsed list node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed list node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the list-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_list(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+ struct lysp_node_list *list_p = (struct lysp_node_list*)node_p;
+ struct lysc_node_list *list = (struct lysc_node_list*)node;
+ struct lysp_node *child_p;
+ struct lysc_node_leaf **key, ***unique;
+ size_t len;
+ unsigned int u, v;
+ const char *keystr, *delim;
+ int config;
+ LY_ERR ret = LY_SUCCESS;
+
+ list->min = list_p->min;
+ list->max = list_p->max ? list_p->max : (uint32_t)-1;
+
+ LY_LIST_FOR(list_p->child, child_p) {
+ LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node));
+ }
+
+ COMPILE_ARRAY_GOTO(ctx, list_p->musts, list->musts, options, u, lys_compile_must, ret, done);
+
+ /* keys */
+ if ((list->flags & LYS_CONFIG_W) && (!list_p->key || !list_p->key[0])) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Missing key in list representing configuration data.");
+ return LY_EVALID;
+ }
+
+ /* find all the keys (must be direct children) */
+ keystr = list_p->key;
+ while (keystr) {
+ delim = strpbrk(keystr, " \t\n");
+ if (delim) {
+ len = delim - keystr;
+ while (isspace(*delim)) {
+ ++delim;
+ }
+ } else {
+ len = strlen(keystr);
+ }
+
+ /* key node must be present */
+ LY_ARRAY_NEW_RET(ctx->ctx, list->keys, key, LY_EMEM);
+ *key = (struct lysc_node_leaf*)lys_child(node, node->module, keystr, len, LYS_LEAF, LYS_GETNEXT_NOCHOICE | LYS_GETNEXT_NOSTATECHECK);
+ if (!(*key)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "The list's key \"%.*s\" not found.", len, keystr);
+ return LY_EVALID;
+ }
+ /* keys must be unique */
+ for(u = 0; u < LY_ARRAY_SIZE(list->keys) - 1; ++u) {
+ if (*key == list->keys[u]) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Duplicated key identifier \"%.*s\".", len, keystr);
+ return LY_EVALID;
+ }
+ }
+ /* key must have the same config flag as the list itself */
+ if ((list->flags & LYS_CONFIG_MASK) != ((*key)->flags & LYS_CONFIG_MASK)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Key of the configuration list must not be status leaf.");
+ return LY_EVALID;
+ }
+ if (ctx->mod_def->parsed->version < LYS_VERSION_1_1) {
+ /* YANG 1.0 denies key to be of empty type */
+ if ((*key)->type->basetype == LY_TYPE_EMPTY) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Key of a list can be of type \"empty\" only in YANG 1.1 modules.");
+ return LY_EVALID;
+ }
+ } else {
+ /* when and if-feature are illegal on list keys */
+ if ((*key)->when) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "List's key \"%s\" must not have any \"when\" statement.", (*key)->name);
+ return LY_EVALID;
+ }
+ if ((*key)->iffeatures) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "List's key \"%s\" must not have any \"if-feature\" statement.", (*key)->name);
+ return LY_EVALID;
+ }
+ }
+
+ /* check status */
+ LY_CHECK_RET(lysc_check_status(ctx, list->flags, list->module, list->name,
+ (*key)->flags, (*key)->module, (*key)->name));
+
+ /* ignore default values of the key */
+ if ((*key)->dflt) {
+ lydict_remove(ctx->ctx, (*key)->dflt);
+ (*key)->dflt = NULL;
+ }
+ /* mark leaf as key */
+ (*key)->flags |= LYS_KEY;
+
+ /* next key value */
+ keystr = delim;
+ }
+
+ /* uniques */
+ if (list_p->uniques) {
+ for (v = 0; v < LY_ARRAY_SIZE(list_p->uniques); ++v) {
+ config = -1;
+ LY_ARRAY_NEW_RET(ctx->ctx, list->uniques, unique, LY_EMEM);
+ keystr = list_p->uniques[v];
+ while (keystr) {
+ delim = strpbrk(keystr, " \t\n");
+ if (delim) {
+ len = delim - keystr;
+ while (isspace(*delim)) {
+ ++delim;
+ }
+ } else {
+ len = strlen(keystr);
+ }
+
+ /* 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);
+ if (ret != LY_SUCCESS) {
+ if (ret == LY_EDENIED) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Unique's descendant-schema-nodeid \"%.*s\" refers to a %s node instead of a leaf.",
+ len, keystr, lys_nodetype2str((*key)->nodetype));
+ }
+ return LY_EVALID;
+ }
+
+ /* all referenced leafs must be of the same config type */
+ if (config != -1 && ((((*key)->flags & LYS_CONFIG_W) && config == 0) || (((*key)->flags & LYS_CONFIG_R) && config == 1))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Unique statement \"%s\" refers to leafs with different config type.", list_p->uniques[v]);
+ return LY_EVALID;
+ } else if ((*key)->flags & LYS_CONFIG_W) {
+ config = 1;
+ } else { /* LYS_CONFIG_R */
+ config = 0;
+ }
+
+ /* check status */
+ LY_CHECK_RET(lysc_check_status(ctx, list->flags, list->module, list->name,
+ (*key)->flags, (*key)->module, (*key)->name));
+
+ /* mark leaf as unique */
+ (*key)->flags |= LYS_UNIQUE;
+
+ /* next unique value in line */
+ keystr = delim;
+ }
+ /* next unique definition */
+ }
+ }
+
+ //COMPILE_ARRAY_GOTO(ctx, list_p->actions, list->actions, options, u, lys_compile_action, ret, done);
+ //COMPILE_ARRAY_GOTO(ctx, list_p->notifs, list->notifs, options, u, lys_compile_notif, ret, done);
+
+done:
+ return ret;
+}
+
+static LY_ERR
+lys_compile_node_choice_dflt(struct lysc_ctx *ctx, const char *dflt, struct lysc_node_choice *ch)
+{
+ struct lysc_node *iter, *node = (struct lysc_node*)ch;
+ const char *prefix = NULL, *name;
+ size_t prefix_len = 0;
+
+ /* could use lys_parse_nodeid(), but it checks syntax which is already done in this case by the parsers */
+ name = strchr(dflt, ':');
+ if (name) {
+ prefix = dflt;
+ prefix_len = name - prefix;
+ ++name;
+ } else {
+ name = dflt;
+ }
+ if (prefix && (strncmp(prefix, node->module->compiled->prefix, prefix_len) || node->module->compiled->prefix[prefix_len] != '\0')) {
+ /* prefixed default case make sense only for the prefix of the schema itself */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid default case referencing a case from different YANG module (by prefix \"%.*s\").",
+ prefix_len, prefix);
+ return LY_EVALID;
+ }
+ ch->dflt = (struct lysc_node_case*)lys_child(node, node->module, name, 0, LYS_CASE, LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCASE);
+ if (!ch->dflt) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Default case \"%s\" not found.", dflt);
+ return LY_EVALID;
+ }
+ /* no mandatory nodes directly under the default case */
+ LY_LIST_FOR(ch->dflt->child, iter) {
+ if (iter->flags & LYS_MAND_TRUE) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Mandatory node \"%s\" under the default case \"%s\".", iter->name, dflt);
+ return LY_EVALID;
+ }
+ }
+ ch->flags |= LYS_SET_DFLT;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile parsed choice node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed choice node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the choice-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_choice(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+ struct lysp_node_choice *ch_p = (struct lysp_node_choice*)node_p;
+ struct lysc_node_choice *ch = (struct lysc_node_choice*)node;
+ struct lysp_node *child_p, *case_child_p;
+ struct lys_module;
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_LIST_FOR(ch_p->child, child_p) {
+ if (child_p->nodetype == LYS_CASE) {
+ LY_LIST_FOR(((struct lysp_node_case*)child_p)->child, case_child_p) {
+ LY_CHECK_RET(lys_compile_node(ctx, case_child_p, options, node));
+ }
+ } else {
+ LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node));
+ }
+ }
+
+ /* default branch */
+ if (ch_p->dflt) {
+ LY_CHECK_RET(lys_compile_node_choice_dflt(ctx, ch_p->dflt, ch));
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Compile parsed anydata or anyxml node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed anydata or anyxml node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the any-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_any(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+ struct lysp_node_anydata *any_p = (struct lysp_node_anydata*)node_p;
+ struct lysc_node_anydata *any = (struct lysc_node_anydata*)node;
+ unsigned int u;
+ LY_ERR ret = LY_SUCCESS;
+
+ COMPILE_ARRAY_GOTO(ctx, any_p->musts, any->musts, options, u, lys_compile_must, ret, done);
+
+ if (any->flags & LYS_CONFIG_W) {
+ LOGWRN(ctx->ctx, "Use of %s to define configuration data is not recommended.",
+ ly_stmt2str(any->nodetype == LYS_ANYDATA ? YANG_ANYDATA : YANG_ANYXML));
+ }
+done:
+ 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;
+}
+
+static LY_ERR
+lys_compile_status(struct lysc_ctx *ctx, struct lysc_node *node, struct lysc_node *parent)
+{
+
+ /* status - it is not inherited by specification, but it does not make sense to have
+ * current in deprecated or deprecated in obsolete, so we do print warning and inherit status */
+ if (!(node->flags & LYS_STATUS_MASK)) {
+ if (parent && (parent->flags & (LYS_STATUS_DEPRC | LYS_STATUS_OBSLT))) {
+ LOGWRN(ctx->ctx, "Missing explicit \"%s\" status that was already specified in parent, inheriting.",
+ (parent->flags & LYS_STATUS_DEPRC) ? "deprecated" : "obsolete");
+ node->flags |= parent->flags & LYS_STATUS_MASK;
+ } else {
+ node->flags |= LYS_STATUS_CURR;
+ }
+ } else if (parent) {
+ return lys_compile_status_check(ctx, node->flags, parent->flags);
+ }
+ return LY_SUCCESS;
+}
+
+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,
+ const char *name, void *exclude)
+{
+ const struct lysc_node *iter;
+ unsigned int u;
+
+ LY_LIST_FOR(children, iter) {
+ if (iter != exclude && !strcmp(name, iter->name)) {
+ goto error;
+ }
+ }
+ LY_ARRAY_FOR(actions, u) {
+ if (&actions[u] != exclude && !strcmp(name, actions[u].name)) {
+ goto error;
+ }
+ }
+ LY_ARRAY_FOR(notifs, u) {
+ if (¬ifs[u] != exclude && !strcmp(name, notifs[u].name)) {
+ goto error;
+ }
+ }
+ return LY_SUCCESS;
+error:
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DUPIDENT, name, "data definition");
+ return LY_EEXIST;
+}
+
+/**
+ * @brief Connect the node into the siblings list and check its name uniqueness.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] parent Parent node holding the children list, in case of node from a choice's case,
+ * the choice itself is expected instead of a specific case node.
+ * @param[in] node Schema node to connect into the list.
+ * @return LY_ERR value - LY_SUCCESS or LY_EEXIST.
+ */
+static LY_ERR
+lys_compile_node_connect(struct lysc_ctx *ctx, struct lysc_node *parent, struct lysc_node *node)
+{
+ struct lysc_node **children;
+
+ if (node->nodetype == LYS_CASE) {
+ children = (struct lysc_node**)&((struct lysc_node_choice*)parent)->cases;
+ } else {
+ children = lysc_node_children_p(parent);
+ }
+ if (children) {
+ if (!(*children)) {
+ /* first child */
+ *children = node;
+ } else if (*children != node) {
+ /* by the condition in previous branch we cover the choice/case children
+ * - the children list is shared by the choice and the the first case, in addition
+ * the first child of each case must be referenced from the case node. So the node is
+ * actually always already inserted in case it is the first children - so here such
+ * a situation actually corresponds to the first branch */
+ /* insert at the end of the parent's children list */
+ (*children)->prev->next = node;
+ node->prev = (*children)->prev;
+ (*children)->prev = node;
+
+ /* check the name uniqueness */
+ if (lys_compile_node_uniqness(ctx, *children, lysc_node_actions(parent),
+ lysc_node_notifs(parent), node->name, node)) {
+ return LY_EEXIST;
+ }
+ }
+ }
+ return LY_SUCCESS;
+}
+
+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;
+ unsigned int u;
+ LY_ERR ret;
+
+#define UNIQUE_CHECK(NAME) \
+ LY_LIST_FOR((struct lysc_node*)ch->cases, iter) { \
+ if (!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);
+
+ /* 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 */
+ 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);
+
+ /* explicit parent case is not present (this is its first child) */
+ cs = calloc(1, sizeof(struct lysc_node_case));
+ DUP_STRING(ctx->ctx, node_p->name, cs->name);
+ cs->flags = LYS_STATUS_MASK & node_p->flags;
+ cs->sp = node_p;
+
+ /* check the case's status */
+ LY_CHECK_RET(lys_compile_status(ctx, (struct lysc_node*)cs, (struct lysc_node* )ch), NULL);
+ COMPILE_MEMBER_GOTO(ctx, node_p->when, cs->when, options, lys_compile_when, ret, error);
+ COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, cs->iffeatures, options, u, lys_compile_iffeature, ret, error);
+ }
+ cs->module = ctx->mod;
+ cs->prev = (struct lysc_node*)cs;
+ cs->nodetype = LYS_CASE;
+ lys_compile_node_connect(ctx, (struct lysc_node*)ch, (struct lysc_node*)cs);
+ cs->parent = (struct lysc_node*)ch;
+ cs->child = child;
+
+ return cs;
+error:
+ return NULL;
+
+#undef UNIQUE_CHECK
+}
+
+static LY_ERR
+lys_compile_refine_config(struct lysc_ctx *ctx, struct lysc_node *node, struct lysp_refine *rfn, int inheriting)
+{
+ struct lysc_node *child;
+ uint16_t config = rfn->flags & LYS_CONFIG_MASK;
+
+ if (config == (node->flags & LYS_CONFIG_MASK)) {
+ /* nothing to do */
+ return LY_SUCCESS;
+ }
+
+ if (!inheriting) {
+ /* explicit refine */
+ if (config == LYS_CONFIG_W && node->parent && (node->parent->flags & LYS_CONFIG_R)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid refine of config in \"%s\" - configuration node cannot be child of any state data node.");
+ return LY_EVALID;
+ }
+ }
+ node->flags &= ~LYS_CONFIG_MASK;
+ node->flags |= config;
+
+ /* inherit the change into the children */
+ LY_LIST_FOR((struct lysc_node*)lysc_node_children(node), child) {
+ LY_CHECK_RET(lys_compile_refine_config(ctx, child, rfn, 1));
+ }
+
+ /* TODO actions and notifications */
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile parsed uses statement - resolve target grouping and connect its content into parent.
+ * If present, also apply uses's modificators.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] uses_p Parsed uses schema node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] parent Compiled parent node where the content of the referenced grouping is supposed to be connected. It is
+ * NULL for top-level nodes, in such a case the module where the node will be connected is taken from
+ * the compile context.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_uses(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p, int options, struct lysc_node *parent)
+{
+ struct lysp_node *node_p;
+ struct lysc_node *node;
+ const struct lysp_grp *grp = NULL;
+ unsigned int u, v, grp_stack_count;
+ int found;
+ const char *id, *name, *prefix;
+ size_t prefix_len, name_len;
+ struct lys_module *mod, *mod_old;
+ struct lysp_refine *rfn;
+ LY_ERR ret = LY_EVALID;
+
+ /* search for the grouping definition */
+ found = 0;
+ id = uses_p->name;
+ lys_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len);
+ if (prefix) {
+ mod = lys_module_find_prefix(ctx->mod_def, prefix, prefix_len);
+ if (!mod) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid prefix used for grouping reference (%s).", uses_p->name);
+ return LY_EVALID;
+ }
+ } else {
+ mod = ctx->mod_def;
+ }
+ if (mod == ctx->mod_def) {
+ for (node_p = uses_p->parent; !found && node_p; node_p = node_p->parent) {
+ grp = lysp_node_groupings(node_p);
+ LY_ARRAY_FOR(grp, u) {
+ if (!strcmp(grp[u].name, name)) {
+ grp = &grp[u];
+ found = 1;
+ break;
+ }
+ }
+ }
+ }
+ if (!found) {
+ /* search in top-level groupings of the main module ... */
+ grp = mod->parsed->groupings;
+ if (grp) {
+ for (u = 0; !found && u < LY_ARRAY_SIZE(grp); ++u) {
+ if (!strcmp(grp[u].name, name)) {
+ grp = &grp[u];
+ found = 1;
+ }
+ }
+ }
+ if (!found && mod->parsed->includes) {
+ /* ... and all the submodules */
+ for (u = 0; !found && u < LY_ARRAY_SIZE(mod->parsed->includes); ++u) {
+ grp = mod->parsed->includes[u].submodule->groupings;
+ if (grp) {
+ for (v = 0; !found && v < LY_ARRAY_SIZE(grp); ++v) {
+ if (!strcmp(grp[v].name, name)) {
+ grp = &grp[v];
+ found = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!found) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Grouping \"%s\" referenced by a uses statement not found.", uses_p->name);
+ return LY_EVALID;
+ }
+
+ /* grouping must not reference themselves - stack in ctx maintains list of groupings currently being applied */
+ grp_stack_count = ctx->groupings.count;
+ ly_set_add(&ctx->groupings, (void*)grp, 0);
+ if (grp_stack_count == ctx->groupings.count) {
+ /* the target grouping is already in the stack, so we are already inside it -> circular dependency */
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Grouping \"%s\" references itself through a uses statement.", grp->name);
+ return LY_EVALID;
+ }
+
+ /* switch context's mod_def */
+ mod_old = ctx->mod_def;
+ ctx->mod_def = mod;
+
+ /* check status */
+ LY_CHECK_GOTO(lysc_check_status(ctx, uses_p->flags, mod_old, uses_p->name, grp->flags, mod, grp->name), error);
+
+ /* connect the grouping's content */
+ LY_LIST_FOR(grp->data, node_p) {
+ LY_CHECK_GOTO(lys_compile_node(ctx, node_p, options, parent), error);
+ /* check status between parent (uses in this case) and child - lys_compile_node() compares parent and the new node */
+ if (lys_compile_status_check(ctx, parent ? lysc_node_children(parent)->prev->flags : ctx->mod->compiled->data->prev->flags, uses_p->flags)) {
+ goto error;
+ }
+ }
+
+ /* apply refine */
+ LY_ARRAY_FOR(uses_p->refines, struct lysp_refine, rfn) {
+ LY_CHECK_GOTO(lys_resolve_descendant_schema_nodeid(ctx, rfn->nodeid, 0, parent, 0, (const struct lysc_node**)&node), error);
+
+ /* default value */
+ if (rfn->dflts) {
+ if ((node->nodetype & (LYS_LEAF | LYS_CHOICE)) && LY_ARRAY_SIZE(rfn->dflts) > 1) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid refine of default in \"%s\" - %s cannot hold %d default values.",
+ rfn->nodeid, lys_nodetype2str(node->nodetype), LY_ARRAY_SIZE(rfn->dflts));
+ goto error;
+ }
+ if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_CHOICE))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid refine of default in \"%s\" - %s cannot hold default value(s).",
+ rfn->nodeid, lys_nodetype2str(node->nodetype));
+ goto error;
+ }
+ if (node->nodetype == LYS_LEAF) {
+ FREE_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->dflt);
+ DUP_STRING(ctx->ctx, rfn->dflts[0], ((struct lysc_node_leaf*)node)->dflt);
+ /* TODO check the default value according to type */
+ } else if (node->nodetype == LYS_LEAFLIST) {
+ LY_ARRAY_FOR(((struct lysc_node_leaflist*)node)->dflts, u) {
+ lydict_remove(ctx->ctx, ((struct lysc_node_leaflist*)node)->dflts[u]);
+ }
+ LY_ARRAY_FREE(((struct lysc_node_leaflist*)node)->dflts);
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, ((struct lysc_node_leaflist*)node)->dflts, LY_ARRAY_SIZE(rfn->dflts), ret, error);
+ LY_ARRAY_FOR(rfn->dflts, u) {
+ LY_ARRAY_INCREMENT(((struct lysc_node_leaflist*)node)->dflts);
+ DUP_STRING(ctx->ctx, rfn->dflts[u], ((struct lysc_node_leaflist*)node)->dflts[u]);
+ }
+ /* TODO check the default values according to type */
+ } else if (node->nodetype == LYS_CHOICE) {
+ LY_CHECK_GOTO(lys_compile_node_choice_dflt(ctx, rfn->dflts[0], (struct lysc_node_choice*)node), error);
+ }
+ }
+
+ /* description refine not applicable */
+ /* reference refine not applicable */
+
+ /* config */
+ if (rfn->flags & LYS_CONFIG_MASK) {
+ LY_CHECK_GOTO(lys_compile_refine_config(ctx, node, rfn, 0), error);
+ }
+
+ /* mandatory */
+ if (rfn->flags & LYS_MAND_MASK) {
+ if (!(node->nodetype & (LYS_LEAF | LYS_ANYDATA | LYS_ANYXML | LYS_CHOICE))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid refine of mandatory in \"%s\" - %s cannot hold mandatory statement.",
+ rfn->nodeid, lys_nodetype2str(node->nodetype));
+ goto error;
+ }
+ /* in compiled flags, only the LYS_MAND_TRUE is present */
+ if (rfn->flags & LYS_MAND_TRUE) {
+ /* check if node has default value */
+ if (node->nodetype & LYS_LEAF) {
+ if (node->flags & LYS_SET_DFLT) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid refine of mandatory in \"%s\" - leafswith \"default\" statement.", rfn->nodeid);
+ goto error;
+ } else {
+ /* remove the default value taken from the leaf's type */
+ FREE_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->dflt);
+ ((struct lysc_node_leaf*)node)->dflt = NULL;
+ }
+ } else if ((node->nodetype & LYS_CHOICE) && ((struct lysc_node_choice*)node)->dflt) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid refine of mandatory in \"%s\" - choice with \"default\" statement.", rfn->nodeid);
+ goto error;
+ }
+ if (node->parent && (node->parent->flags & LYS_SET_DFLT)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid refine of mandatory in \"%s\" - %s under the default case.",
+ rfn->nodeid, lys_nodetype2str(node->nodetype));
+ goto error;
+ }
+
+ node->flags |= LYS_MAND_TRUE;
+ } else {
+ node->flags &= ~LYS_MAND_TRUE;
+ }
+ }
+ }
+
+ ret = LY_SUCCESS;
+error:
+ /* reload previous context's mod_def */
+ ctx->mod_def = mod_old;
+ /* remove the grouping from the stack for circular groupings dependency check */
+ ly_set_rm_index(&ctx->groupings, ctx->groupings.count - 1, NULL);
+ assert(ctx->groupings.count == grp_stack_count);
+
+ return ret;
+}
+
+/**
+ * @brief Compile parsed schema node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed schema node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] parent Compiled parent node where the current node is supposed to be connected. It is
+ * NULL for top-level nodes, in such a case the module where the node will be connected is taken from
+ * the compile context.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *parent)
+{
+ LY_ERR ret = LY_EVALID;
+ struct lysc_node *node;
+ struct lysc_node_case *cs;
+ unsigned int u;
+ LY_ERR (*node_compile_spec)(struct lysc_ctx*, struct lysp_node*, int, struct lysc_node*);
+
+ switch (node_p->nodetype) {
+ case LYS_CONTAINER:
+ node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_container));
+ node_compile_spec = lys_compile_node_container;
+ break;
+ case LYS_LEAF:
+ node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_leaf));
+ node_compile_spec = lys_compile_node_leaf;
+ break;
+ case LYS_LIST:
+ node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_list));
+ node_compile_spec = lys_compile_node_list;
+ break;
+ case LYS_LEAFLIST:
+ node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_leaflist));
+ node_compile_spec = lys_compile_node_leaflist;
+ break;
+ case LYS_CHOICE:
+ node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_choice));
+ node_compile_spec = lys_compile_node_choice;
+ break;
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_anydata));
+ node_compile_spec = lys_compile_node_any;
+ break;
+ case LYS_USES:
+ return lys_compile_uses(ctx, (struct lysp_node_uses*)node_p, options, parent);
+ default:
+ LOGINT(ctx->ctx);
+ return LY_EINT;
+ }
+ LY_CHECK_ERR_RET(!node, LOGMEM(ctx->ctx), LY_EMEM);
+ node->nodetype = node_p->nodetype;
+ node->module = ctx->mod;
+ node->prev = node;
+ node->flags = node_p->flags & LYS_FLAGS_COMPILED_MASK;
+
+ /* config */
+ if (!(node->flags & LYS_CONFIG_MASK)) {
+ /* config not explicitely set, inherit it from parent */
+ if (parent) {
+ node->flags |= parent->flags & LYS_CONFIG_MASK;
+ } else {
+ /* default is config true */
+ node->flags |= LYS_CONFIG_W;
+ }
+ }
+ if (parent && (parent->flags & LYS_CONFIG_R) && (node->flags & LYS_CONFIG_W)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Configuration node cannot be child of any state data node.");
+ goto error;
+ }
+
+ /* *list ordering */
+ if (node->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+ if ((node->flags & LYS_CONFIG_R) && (node->flags & LYS_ORDBY_MASK)) {
+ LOGWRN(ctx->ctx, "The ordered-by statement is ignored in lists representing state data, "
+ "RPC/action output parameters or notification content (%s).", ctx->path);
+ node->flags &= ~LYS_ORDBY_MASK;
+ node->flags |= LYS_ORDBY_SYSTEM;
+ } else if (!(node->flags & LYS_ORDBY_MASK)) {
+ /* default ordering is system */
+ node->flags |= LYS_ORDBY_SYSTEM;
+ }
+ }
+
+ /* status - it is not inherited by specification, but it does not make sense to have
+ * current in deprecated or deprecated in obsolete, so we do print warning and inherit status */
+ if (!parent || parent->nodetype != LYS_CHOICE) {
+ /* in case of choice/case's children, postpone the check to the moment we know if
+ * the parent is choice (parent here) or some case (so we have to get its flags to check) */
+ LY_CHECK_GOTO(lys_compile_status(ctx, node, parent), error);
+ }
+
+ if (!(options & LYSC_OPT_FREE_SP)) {
+ node->sp = node_p;
+ }
+ DUP_STRING(ctx->ctx, node_p->name, node->name);
+ COMPILE_MEMBER_GOTO(ctx, node_p->when, node->when, options, lys_compile_when, ret, error);
+ 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);
+
+ /* insert into parent's children */
+ if (parent) {
+ if (parent->nodetype == LYS_CHOICE) {
+ cs = lys_compile_node_case(ctx, node_p->parent, options, (struct lysc_node_choice*)parent, node);
+ LY_CHECK_ERR_GOTO(!cs, ret = LY_EVALID, error);
+ /* the postponed status check of the node and its real parent - in case of implicit case,
+ * it directly gets the same status flags as the choice */
+ LY_CHECK_GOTO(lys_compile_status(ctx, node, (struct lysc_node*)cs), error);
+ node->parent = (struct lysc_node*)cs;
+ } else { /* other than choice */
+ node->parent = parent;
+ }
+ LY_CHECK_RET(lys_compile_node_connect(ctx, parent, node), LY_EVALID);
+ } else {
+ /* top-level element */
+ if (!ctx->mod->compiled->data) {
+ ctx->mod->compiled->data = node;
+ } else {
+ /* insert at the end of the module's top-level nodes list */
+ ctx->mod->compiled->data->prev->next = node;
+ node->prev = ctx->mod->compiled->data->prev;
+ ctx->mod->compiled->data->prev = node;
+ }
+ if (lys_compile_node_uniqness(ctx, ctx->mod->compiled->data, ctx->mod->compiled->rpcs,
+ ctx->mod->compiled->notifs, node->name, node)) {
+ return LY_EVALID;
+ }
+ }
+
+ return LY_SUCCESS;
+
+error:
+ lysc_node_free(ctx->ctx, node);
+ return ret;
+}
+
+/**
+ * @brief Compile the given YANG submodule into the main module.
+ * @param[in] ctx Compile context
+ * @param[in] inc Include structure from the main module defining the submodule.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+LY_ERR
+lys_compile_submodule(struct lysc_ctx *ctx, struct lysp_include *inc, int options)
+{
+ unsigned int u;
+ LY_ERR ret = LY_SUCCESS;
+ /* shortcuts */
+ struct lysp_module *submod = inc->submodule;
+ struct lysc_module *mainmod = ctx->mod->compiled;
+
+ COMPILE_ARRAY_UNIQUE_GOTO(ctx, submod->features, mainmod->features, options, u, lys_compile_feature, ret, error);
+ COMPILE_ARRAY_UNIQUE_GOTO(ctx, submod->identities, mainmod->identities, options, u, lys_compile_identity, ret, error);
+
+error:
+ return ret;
+}
+
+/**
+ * @brief Compile the given YANG module.
+ * @param[in] mod Module structure where the parsed schema is expected and the compiled schema will be placed.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+LY_ERR
+lys_compile(struct lys_module *mod, int options)
+{
+ struct lysc_ctx ctx = {0};
+ struct lysc_module *mod_c;
+ struct lysc_type *type, *typeiter;
+ struct lysp_module *sp;
+ struct lysp_node *node_p;
+ unsigned int u, v;
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(NULL, mod, mod->parsed, mod->parsed->ctx, LY_EINVAL);
+ sp = mod->parsed;
+
+ if (sp->submodule) {
+ LOGERR(sp->ctx, LY_EINVAL, "Submodules (%s) are not supposed to be compiled, compile only the main modules.", sp->name);
+ return LY_EINVAL;
+ }
+
+ ctx.ctx = sp->ctx;
+ ctx.mod = mod;
+ ctx.mod_def = mod;
+
+ mod->compiled = mod_c = calloc(1, sizeof *mod_c);
+ LY_CHECK_ERR_RET(!mod_c, LOGMEM(sp->ctx), LY_EMEM);
+ mod_c->ctx = sp->ctx;
+ mod_c->implemented = sp->implemented;
+ mod_c->latest_revision = sp->latest_revision;
+ mod_c->version = sp->version;
+
+ DUP_STRING(sp->ctx, sp->name, mod_c->name);
+ DUP_STRING(sp->ctx, sp->ns, mod_c->ns);
+ DUP_STRING(sp->ctx, sp->prefix, mod_c->prefix);
+ if (sp->revs) {
+ DUP_STRING(sp->ctx, sp->revs[0].date, mod_c->revision);
+ }
+ COMPILE_ARRAY_GOTO(&ctx, sp->imports, mod_c->imports, options, u, lys_compile_import, ret, error);
+ LY_ARRAY_FOR(sp->includes, u) {
+ ret = lys_compile_submodule(&ctx, &sp->includes[u], options);
+ LY_CHECK_GOTO(ret != LY_SUCCESS, error);
+ }
+ COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->features, mod_c->features, options, u, lys_compile_feature, ret, error);
+ COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->identities, mod_c->identities, options, u, lys_compile_identity, ret, error);
+ if (sp->identities) {
+ LY_CHECK_RET(lys_compile_identities_derived(&ctx, sp->identities, mod_c->identities));
+ }
+
+ LY_LIST_FOR(sp->data, node_p) {
+ ret = lys_compile_node(&ctx, node_p, 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);
+
+ COMPILE_ARRAY_GOTO(&ctx, sp->exts, mod_c->exts, options, u, lys_compile_ext, ret, error);
+
+ /* validate leafref's paths and when/must xpaths */
+ /* for leafref, we need 2 rounds - first detects circular chain by storing the first referred type (which
+ * can be also leafref, in case it is already resolved, go through the chain and check that it does not
+ * point to the starting leafref type). The second round stores the first non-leafref type for later data validation. */
+ for (u = 0; u < ctx.unres.count; ++u) {
+ if (((struct lysc_node*)ctx.unres.objs[u])->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+ type = ((struct lysc_node_leaf*)ctx.unres.objs[u])->type;
+ if (type->basetype == LY_TYPE_LEAFREF) {
+ /* validate the path */
+ ret = lys_compile_leafref_validate(&ctx, ((struct lysc_node*)ctx.unres.objs[u]), (struct lysc_type_leafref*)type);
+ LY_CHECK_GOTO(ret, error);
+ } else if (type->basetype == LY_TYPE_UNION) {
+ LY_ARRAY_FOR(((struct lysc_type_union*)type)->types, v) {
+ if (((struct lysc_type_union*)type)->types[v]->basetype == LY_TYPE_LEAFREF) {
+ /* validate the path */
+ ret = lys_compile_leafref_validate(&ctx, ((struct lysc_node*)ctx.unres.objs[u]),
+ (struct lysc_type_leafref*)((struct lysc_type_union*)type)->types[v]);
+ LY_CHECK_GOTO(ret, error);
+ }
+ }
+ }
+ }
+ }
+ for (u = 0; u < ctx.unres.count; ++u) {
+ if (((struct lysc_node*)ctx.unres.objs[u])->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+ type = ((struct lysc_node_leaf*)ctx.unres.objs[u])->type;
+ if (type->basetype == LY_TYPE_LEAFREF) {
+ /* store pointer to the real type */
+ for (typeiter = ((struct lysc_type_leafref*)type)->realtype;
+ typeiter->basetype == LY_TYPE_LEAFREF;
+ typeiter = ((struct lysc_type_leafref*)typeiter)->realtype);
+ ((struct lysc_type_leafref*)type)->realtype = typeiter;
+ } else if (type->basetype == LY_TYPE_UNION) {
+ LY_ARRAY_FOR(((struct lysc_type_union*)type)->types, v) {
+ if (((struct lysc_type_union*)type)->types[v]->basetype == LY_TYPE_LEAFREF) {
+ /* store pointer to the real type */
+ for (typeiter = ((struct lysc_type_leafref*)((struct lysc_type_union*)type)->types[v])->realtype;
+ typeiter->basetype == LY_TYPE_LEAFREF;
+ typeiter = ((struct lysc_type_leafref*)typeiter)->realtype);
+ ((struct lysc_type_leafref*)((struct lysc_type_union*)type)->types[v])->realtype = typeiter;
+ }
+ }
+ }
+ }
+ }
+ ly_set_erase(&ctx.unres, NULL);
+ ly_set_erase(&ctx.groupings, NULL);
+
+ if (options & LYSC_OPT_FREE_SP) {
+ lysp_module_free(mod->parsed);
+ ((struct lys_module*)mod)->parsed = NULL;
+ }
+
+ ((struct lys_module*)mod)->compiled = mod_c;
+ return LY_SUCCESS;
+
+error:
+ ly_set_erase(&ctx.unres, NULL);
+ ly_set_erase(&ctx.groupings, NULL);
+ lysc_module_free(mod_c, NULL);
+ ((struct lys_module*)mod)->compiled = NULL;
+ return ret;
+}
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
new file mode 100644
index 0000000..7dbe83c
--- /dev/null
+++ b/src/tree_schema_free.c
@@ -0,0 +1,750 @@
+/**
+ * @file tree_schema.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema tree implementation
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "common.h"
+
+#include "libyang.h"
+#include "context.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+#define FREE_ARRAY(CTX, ARRAY, FUNC) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FUNC(CTX, &(ARRAY)[c__]);}LY_ARRAY_FREE(ARRAY);}
+#define FREE_MEMBER(CTX, MEMBER, FUNC) if (MEMBER) {FUNC(CTX, MEMBER);free(MEMBER);}
+#define FREE_STRINGS(CTX, ARRAY) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FREE_STRING(CTX, ARRAY[c__]);}LY_ARRAY_FREE(ARRAY);}
+
+static void lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp);
+static void lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node);
+
+static void
+lysp_stmt_free(struct ly_ctx *ctx, struct lysp_stmt *stmt)
+{
+ struct lysp_stmt *child, *next;
+
+ FREE_STRING(ctx, stmt->stmt);
+ FREE_STRING(ctx, stmt->arg);
+
+ LY_LIST_FOR_SAFE(stmt->child, next, child) {
+ lysp_stmt_free(ctx, child);
+ }
+
+ free(stmt);
+}
+
+static void
+lysp_ext_instance_free(struct ly_ctx *ctx, struct lysp_ext_instance *ext)
+{
+ struct lysp_stmt *stmt, *next;
+
+ FREE_STRING(ctx, ext->name);
+ FREE_STRING(ctx, ext->argument);
+
+ LY_LIST_FOR_SAFE(ext->child, next, stmt) {
+ lysp_stmt_free(ctx, stmt);
+ }
+}
+
+static void
+lysp_import_free(struct ly_ctx *ctx, struct lysp_import *import)
+{
+ /* imported module is freed directly from the context's list */
+ FREE_STRING(ctx, import->name);
+ FREE_STRING(ctx, import->prefix);
+ FREE_STRING(ctx, import->dsc);
+ FREE_STRING(ctx, import->ref);
+ FREE_ARRAY(ctx, import->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_include_free(struct ly_ctx *ctx, struct lysp_include *include)
+{
+ if (include->submodule) {
+ lysp_module_free(include->submodule);
+ }
+ FREE_STRING(ctx, include->name);
+ FREE_STRING(ctx, include->dsc);
+ FREE_STRING(ctx, include->ref);
+ FREE_ARRAY(ctx, include->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_revision_free(struct ly_ctx *ctx, struct lysp_revision *rev)
+{
+ FREE_STRING(ctx, rev->dsc);
+ FREE_STRING(ctx, rev->ref);
+ FREE_ARRAY(ctx, rev->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_ext_free(struct ly_ctx *ctx, struct lysp_ext *ext)
+{
+ FREE_STRING(ctx, ext->name);
+ FREE_STRING(ctx, ext->argument);
+ FREE_STRING(ctx, ext->dsc);
+ FREE_STRING(ctx, ext->ref);
+ FREE_ARRAY(ctx, ext->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_feature_free(struct ly_ctx *ctx, struct lysp_feature *feat)
+{
+ FREE_STRING(ctx, feat->name);
+ FREE_STRINGS(ctx, feat->iffeatures);
+ FREE_STRING(ctx, feat->dsc);
+ FREE_STRING(ctx, feat->ref);
+ FREE_ARRAY(ctx, feat->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_ident_free(struct ly_ctx *ctx, struct lysp_ident *ident)
+{
+ FREE_STRING(ctx, ident->name);
+ FREE_STRINGS(ctx, ident->iffeatures);
+ FREE_STRINGS(ctx, ident->bases);
+ FREE_STRING(ctx, ident->dsc);
+ FREE_STRING(ctx, ident->ref);
+ FREE_ARRAY(ctx, ident->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_restr_free(struct ly_ctx *ctx, struct lysp_restr *restr)
+{
+ FREE_STRING(ctx, restr->arg);
+ FREE_STRING(ctx, restr->emsg);
+ FREE_STRING(ctx, restr->eapptag);
+ FREE_STRING(ctx, restr->dsc);
+ FREE_STRING(ctx, restr->ref);
+ FREE_ARRAY(ctx, restr->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_type_enum_free(struct ly_ctx *ctx, struct lysp_type_enum *item)
+{
+ FREE_STRING(ctx, item->name);
+ FREE_STRING(ctx, item->dsc);
+ FREE_STRING(ctx, item->ref);
+ FREE_STRINGS(ctx, item->iffeatures);
+ FREE_ARRAY(ctx, item->exts, lysp_ext_instance_free);
+}
+
+void lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type);
+static void
+lysp_type_free(struct ly_ctx *ctx, struct lysp_type *type)
+{
+ FREE_STRING(ctx, type->name);
+ FREE_MEMBER(ctx, type->range, lysp_restr_free);
+ FREE_MEMBER(ctx, type->length, lysp_restr_free);
+ FREE_ARRAY(ctx, type->patterns, lysp_restr_free);
+ FREE_ARRAY(ctx, type->enums, lysp_type_enum_free);
+ FREE_ARRAY(ctx, type->bits, lysp_type_enum_free);
+ FREE_STRING(ctx, type->path);
+ FREE_STRINGS(ctx, type->bases);
+ FREE_ARRAY(ctx, type->types, lysp_type_free);
+ FREE_ARRAY(ctx, type->exts, lysp_ext_instance_free);
+ if (type->compiled) {
+ lysc_type_free(ctx, type->compiled);
+ }
+}
+
+static void
+lysp_tpdf_free(struct ly_ctx *ctx, struct lysp_tpdf *tpdf)
+{
+ FREE_STRING(ctx, tpdf->name);
+ FREE_STRING(ctx, tpdf->units);
+ FREE_STRING(ctx, tpdf->dflt);
+ FREE_STRING(ctx, tpdf->dsc);
+ FREE_STRING(ctx, tpdf->ref);
+ FREE_ARRAY(ctx, tpdf->exts, lysp_ext_instance_free);
+
+ lysp_type_free(ctx, &tpdf->type);
+
+}
+
+static void
+lysp_action_inout_free(struct ly_ctx *ctx, struct lysp_action_inout *inout)
+{
+ struct lysp_node *node, *next;
+
+ FREE_ARRAY(ctx, inout->musts, lysp_restr_free);
+ FREE_ARRAY(ctx, inout->typedefs, lysp_tpdf_free);
+ FREE_ARRAY(ctx, inout->groupings, lysp_grp_free);
+ LY_LIST_FOR_SAFE(inout->data, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ FREE_ARRAY(ctx, inout->exts, lysp_ext_instance_free);
+
+}
+
+static void
+lysp_action_free(struct ly_ctx *ctx, struct lysp_action *action)
+{
+ FREE_STRING(ctx, action->name);
+ FREE_STRING(ctx, action->dsc);
+ FREE_STRING(ctx, action->ref);
+ FREE_STRINGS(ctx, action->iffeatures);
+ FREE_ARRAY(ctx, action->typedefs, lysp_tpdf_free);
+ FREE_ARRAY(ctx, action->groupings, lysp_grp_free);
+ FREE_MEMBER(ctx, action->input, lysp_action_inout_free);
+ FREE_MEMBER(ctx, action->output, lysp_action_inout_free);
+ FREE_ARRAY(ctx, action->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_notif_free(struct ly_ctx *ctx, struct lysp_notif *notif)
+{
+ struct lysp_node *node, *next;
+
+ FREE_STRING(ctx, notif->name);
+ FREE_STRING(ctx, notif->dsc);
+ FREE_STRING(ctx, notif->ref);
+ FREE_STRINGS(ctx, notif->iffeatures);
+ FREE_ARRAY(ctx, notif->musts, lysp_restr_free);
+ FREE_ARRAY(ctx, notif->typedefs, lysp_tpdf_free);
+ FREE_ARRAY(ctx, notif->groupings, lysp_grp_free);
+ LY_LIST_FOR_SAFE(notif->data, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ FREE_ARRAY(ctx, notif->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp)
+{
+ struct lysp_node *node, *next;
+
+ FREE_STRING(ctx, grp->name);
+ FREE_STRING(ctx, grp->dsc);
+ FREE_STRING(ctx, grp->ref);
+ FREE_ARRAY(ctx, grp->typedefs, lysp_tpdf_free);
+ FREE_ARRAY(ctx, grp->groupings, lysp_grp_free);
+ LY_LIST_FOR_SAFE(grp->data, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ FREE_ARRAY(ctx, grp->actions, lysp_action_free);
+ FREE_ARRAY(ctx, grp->notifs, lysp_notif_free);
+ FREE_ARRAY(ctx, grp->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_when_free(struct ly_ctx *ctx, struct lysp_when *when)
+{
+ FREE_STRING(ctx, when->cond);
+ FREE_STRING(ctx, when->dsc);
+ FREE_STRING(ctx, when->ref);
+ FREE_ARRAY(ctx, when->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_augment_free(struct ly_ctx *ctx, struct lysp_augment *augment)
+{
+ struct lysp_node *node, *next;
+
+ FREE_STRING(ctx, augment->nodeid);
+ FREE_STRING(ctx, augment->dsc);
+ FREE_STRING(ctx, augment->ref);
+ FREE_MEMBER(ctx, augment->when, lysp_when_free);
+ FREE_STRINGS(ctx, augment->iffeatures);
+ LY_LIST_FOR_SAFE(augment->child, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ FREE_ARRAY(ctx, augment->actions, lysp_action_free);
+ FREE_ARRAY(ctx, augment->notifs, lysp_notif_free);
+ FREE_ARRAY(ctx, augment->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_deviate_free(struct ly_ctx *ctx, struct lysp_deviate *d)
+{
+ struct lysp_deviate_add *add = (struct lysp_deviate_add*)d;
+ struct lysp_deviate_rpl *rpl = (struct lysp_deviate_rpl*)d;
+
+ FREE_ARRAY(ctx, d->exts, lysp_ext_instance_free);
+ switch(d->mod) {
+ case LYS_DEV_NOT_SUPPORTED:
+ /* nothing to do */
+ break;
+ case LYS_DEV_ADD:
+ case LYS_DEV_DELETE: /* compatible for dynamically allocated data */
+ FREE_STRING(ctx, add->units);
+ FREE_ARRAY(ctx, add->musts, lysp_restr_free);
+ FREE_STRINGS(ctx, add->uniques);
+ FREE_STRINGS(ctx, add->dflts);
+ break;
+ case LYS_DEV_REPLACE:
+ FREE_MEMBER(ctx, rpl->type, lysp_type_free);
+ FREE_STRING(ctx, rpl->units);
+ FREE_STRING(ctx, rpl->dflt);
+ break;
+ default:
+ LOGINT(ctx);
+ break;
+ }
+}
+
+static void
+lysp_deviation_free(struct ly_ctx *ctx, struct lysp_deviation *dev)
+{
+ struct lysp_deviate *next, *iter;
+
+ FREE_STRING(ctx, dev->nodeid);
+ FREE_STRING(ctx, dev->dsc);
+ FREE_STRING(ctx, dev->ref);
+ LY_LIST_FOR_SAFE(dev->deviates, next, iter) {
+ lysp_deviate_free(ctx, iter);
+ free(iter);
+ }
+ FREE_ARRAY(ctx, dev->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_refine_free(struct ly_ctx *ctx, struct lysp_refine *ref)
+{
+ FREE_STRING(ctx, ref->nodeid);
+ FREE_STRING(ctx, ref->dsc);
+ FREE_STRING(ctx, ref->ref);
+ FREE_STRINGS(ctx, ref->iffeatures);
+ FREE_ARRAY(ctx, ref->musts, lysp_restr_free);
+ FREE_STRING(ctx, ref->presence);
+ FREE_STRINGS(ctx, ref->dflts);
+ FREE_ARRAY(ctx, ref->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node)
+{
+ struct lysp_node *child, *next;
+
+ FREE_STRING(ctx, node->name);
+ FREE_STRING(ctx, node->dsc);
+ FREE_STRING(ctx, node->ref);
+ FREE_MEMBER(ctx, node->when, lysp_when_free);
+ FREE_STRINGS(ctx, node->iffeatures);
+ FREE_ARRAY(ctx, node->exts, lysp_ext_instance_free);
+
+ switch(node->nodetype) {
+ case LYS_CONTAINER:
+ FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->musts, lysp_restr_free);
+ FREE_STRING(ctx, ((struct lysp_node_container*)node)->presence);
+ FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->typedefs, lysp_tpdf_free);
+ FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->groupings, lysp_grp_free);
+ LY_LIST_FOR_SAFE(((struct lysp_node_container*)node)->child, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->actions, lysp_action_free);
+ FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->notifs, lysp_notif_free);
+ break;
+ case LYS_LEAF:
+ FREE_ARRAY(ctx, ((struct lysp_node_leaf*)node)->musts, lysp_restr_free);
+ lysp_type_free(ctx, &((struct lysp_node_leaf*)node)->type);
+ FREE_STRING(ctx, ((struct lysp_node_leaf*)node)->units);
+ FREE_STRING(ctx, ((struct lysp_node_leaf*)node)->dflt);
+ break;
+ case LYS_LEAFLIST:
+ FREE_ARRAY(ctx, ((struct lysp_node_leaflist*)node)->musts, lysp_restr_free);
+ lysp_type_free(ctx, &((struct lysp_node_leaflist*)node)->type);
+ FREE_STRING(ctx, ((struct lysp_node_leaflist*)node)->units);
+ FREE_STRINGS(ctx, ((struct lysp_node_leaflist*)node)->dflts);
+ break;
+ case LYS_LIST:
+ FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->musts, lysp_restr_free);
+ FREE_STRING(ctx, ((struct lysp_node_list*)node)->key);
+ FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->typedefs, lysp_tpdf_free);
+ FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->groupings, lysp_grp_free);
+ LY_LIST_FOR_SAFE(((struct lysp_node_list*)node)->child, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->actions, lysp_action_free);
+ FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->notifs, lysp_notif_free);
+ FREE_STRINGS(ctx, ((struct lysp_node_list*)node)->uniques);
+ break;
+ case LYS_CHOICE:
+ LY_LIST_FOR_SAFE(((struct lysp_node_choice*)node)->child, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ FREE_STRING(ctx, ((struct lysp_node_choice*)node)->dflt);
+ break;
+ case LYS_CASE:
+ LY_LIST_FOR_SAFE(((struct lysp_node_case*)node)->child, next, child) {
+ lysp_node_free(ctx, child);
+ }
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ FREE_ARRAY(ctx, ((struct lysp_node_anydata*)node)->musts, lysp_restr_free);
+ break;
+ case LYS_USES:
+ FREE_ARRAY(ctx, ((struct lysp_node_uses*)node)->refines, lysp_refine_free);
+ FREE_ARRAY(ctx, ((struct lysp_node_uses*)node)->augments, lysp_augment_free);
+ break;
+ default:
+ LOGINT(ctx);
+ }
+
+ free(node);
+}
+
+API void
+lysp_module_free(struct lysp_module *module)
+{
+ struct ly_ctx *ctx;
+ struct lysp_node *node, *next;
+
+ LY_CHECK_ARG_RET(NULL, module,);
+ ctx = module->ctx;
+
+ FREE_STRING(ctx, module->name);
+ FREE_STRING(ctx, module->filepath);
+ FREE_STRING(ctx, module->ns); /* or belongs-to */
+ FREE_STRING(ctx, module->prefix);
+
+ FREE_ARRAY(ctx, module->imports, lysp_import_free);
+ FREE_ARRAY(ctx, module->includes, lysp_include_free);
+
+ FREE_STRING(ctx, module->org);
+ FREE_STRING(ctx, module->contact);
+ FREE_STRING(ctx, module->dsc);
+ FREE_STRING(ctx, module->ref);
+
+ FREE_ARRAY(ctx, module->revs, lysp_revision_free);
+ FREE_ARRAY(ctx, module->extensions, lysp_ext_free);
+ FREE_ARRAY(ctx, module->features, lysp_feature_free);
+ FREE_ARRAY(ctx, module->identities, lysp_ident_free);
+ FREE_ARRAY(ctx, module->typedefs, lysp_tpdf_free);
+ FREE_ARRAY(ctx, module->groupings, lysp_grp_free);
+ LY_LIST_FOR_SAFE(module->data, next, node) {
+ lysp_node_free(ctx, node);
+ }
+ FREE_ARRAY(ctx, module->augments, lysp_augment_free);
+ FREE_ARRAY(ctx, module->rpcs, lysp_action_free);
+ FREE_ARRAY(ctx, module->notifs, lysp_notif_free);
+ FREE_ARRAY(ctx, module->deviations, lysp_deviation_free);
+ FREE_ARRAY(ctx, module->exts, lysp_ext_instance_free);
+
+ free(module);
+}
+
+static void
+lysc_ext_instance_free(struct ly_ctx *ctx, struct lysc_ext_instance *ext)
+{
+ FREE_STRING(ctx, ext->argument);
+ FREE_ARRAY(ctx, ext->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_iffeature_free(struct ly_ctx *UNUSED(ctx), struct lysc_iffeature *iff)
+{
+ LY_ARRAY_FREE(iff->features);
+ free(iff->expr);
+}
+
+static void
+lysc_when_free(struct ly_ctx *ctx, struct lysc_when *w)
+{
+ lyxp_expr_free(ctx, w->cond);
+ FREE_ARRAY(ctx, w->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_must_free(struct ly_ctx *ctx, struct lysc_must *must)
+{
+ lyxp_expr_free(ctx, must->cond);
+ FREE_STRING(ctx, must->emsg);
+ FREE_STRING(ctx, must->eapptag);
+ FREE_ARRAY(ctx, must->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_import_free(struct ly_ctx *ctx, struct lysc_import *import)
+{
+ /* imported module is freed directly from the context's list */
+ FREE_STRING(ctx, import->prefix);
+ FREE_ARRAY(ctx, import->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_ident_free(struct ly_ctx *ctx, struct lysc_ident *ident)
+{
+ FREE_STRING(ctx, ident->name);
+ FREE_ARRAY(ctx, ident->iffeatures, lysc_iffeature_free);
+ LY_ARRAY_FREE(ident->derived);
+ FREE_ARRAY(ctx, ident->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat)
+{
+ FREE_STRING(ctx, feat->name);
+ FREE_ARRAY(ctx, feat->iffeatures, lysc_iffeature_free);
+ LY_ARRAY_FREE(feat->depfeatures);
+ FREE_ARRAY(ctx, feat->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_range_free(struct ly_ctx *ctx, struct lysc_range *range)
+{
+ LY_ARRAY_FREE(range->parts);
+ FREE_STRING(ctx, range->eapptag);
+ FREE_STRING(ctx, range->emsg);
+ FREE_ARRAY(ctx, range->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_pattern_free(struct ly_ctx *ctx, struct lysc_pattern **pattern)
+{
+ if (--(*pattern)->refcount) {
+ return;
+ }
+ pcre_free((*pattern)->expr);
+ pcre_free_study((*pattern)->expr_extra);
+ FREE_STRING(ctx, (*pattern)->eapptag);
+ FREE_STRING(ctx, (*pattern)->emsg);
+ FREE_ARRAY(ctx, (*pattern)->exts, lysc_ext_instance_free);
+ free(*pattern);
+}
+
+static void
+lysc_enum_item_free(struct ly_ctx *ctx, struct lysc_type_enum_item *item)
+{
+ FREE_STRING(ctx, item->name);
+ FREE_ARRAY(ctx, item->iffeatures, lysc_iffeature_free);
+ FREE_ARRAY(ctx, item->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_type2_free(struct ly_ctx *ctx, struct lysc_type **type)
+{
+ lysc_type_free(ctx, *type);
+}
+void
+lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type)
+{
+ if (--type->refcount) {
+ return;
+ }
+ switch(type->basetype) {
+ case LY_TYPE_BINARY:
+ FREE_MEMBER(ctx, ((struct lysc_type_bin*)type)->length, lysc_range_free);
+ break;
+ case LY_TYPE_BITS:
+ FREE_ARRAY(ctx, (struct lysc_type_enum_item*)((struct lysc_type_bits*)type)->bits, lysc_enum_item_free);
+ break;
+ case LY_TYPE_DEC64:
+ FREE_MEMBER(ctx, ((struct lysc_type_dec*)type)->range, lysc_range_free);
+ break;
+ case LY_TYPE_STRING:
+ FREE_MEMBER(ctx, ((struct lysc_type_str*)type)->length, lysc_range_free);
+ FREE_ARRAY(ctx, ((struct lysc_type_str*)type)->patterns, lysc_pattern_free);
+ break;
+ case LY_TYPE_ENUM:
+ FREE_ARRAY(ctx, ((struct lysc_type_enum*)type)->enums, lysc_enum_item_free);
+ break;
+ case LY_TYPE_INT8:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ FREE_MEMBER(ctx, ((struct lysc_type_num*)type)->range, lysc_range_free);
+ break;
+ case LY_TYPE_IDENT:
+ LY_ARRAY_FREE(((struct lysc_type_identityref*)type)->bases);
+ break;
+ case LY_TYPE_UNION:
+ FREE_ARRAY(ctx, ((struct lysc_type_union*)type)->types, lysc_type2_free);
+ break;
+ case LY_TYPE_LEAFREF:
+ FREE_STRING(ctx, ((struct lysc_type_leafref*)type)->path);
+ break;
+ case LY_TYPE_INST:
+ case LY_TYPE_BOOL:
+ case LY_TYPE_EMPTY:
+ case LY_TYPE_UNKNOWN:
+ /* nothing to do */
+ break;
+ }
+ FREE_ARRAY(ctx, type->exts, lysc_ext_instance_free);
+
+ free(type);
+}
+
+static void
+lysc_node_container_free(struct ly_ctx *ctx, struct lysc_node_container *node)
+{
+ struct lysc_node *child, *child_next;
+
+ LY_LIST_FOR_SAFE(node->child, child_next, child) {
+ lysc_node_free(ctx, child);
+ }
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+
+ /* TODO actions, notifs */
+}
+
+static void
+lysc_node_leaf_free(struct ly_ctx *ctx, struct lysc_node_leaf *node)
+{
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+ if (node->type) {
+ lysc_type_free(ctx, node->type);
+ }
+ FREE_STRING(ctx, node->units);
+ FREE_STRING(ctx, node->dflt);
+}
+
+static void
+lysc_node_leaflist_free(struct ly_ctx *ctx, struct lysc_node_leaflist *node)
+{
+ unsigned int u;
+
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+ if (node->type) {
+ lysc_type_free(ctx, node->type);
+ }
+ FREE_STRING(ctx, node->units);
+ LY_ARRAY_FOR(node->dflts, u) {
+ lydict_remove(ctx, node->dflts[u]);
+ }
+ LY_ARRAY_FREE(node->dflts);
+}
+
+static void
+lysc_node_list_free(struct ly_ctx *ctx, struct lysc_node_list *node)
+{
+ unsigned int u;
+ struct lysc_node *child, *child_next;
+
+ LY_LIST_FOR_SAFE(node->child, child_next, child) {
+ lysc_node_free(ctx, child);
+ }
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+
+ LY_ARRAY_FREE(node->keys);
+ LY_ARRAY_FOR(node->uniques, u) {
+ LY_ARRAY_FREE(node->uniques[u]);
+ }
+ LY_ARRAY_FREE(node->uniques);
+
+ /* TODO actions, notifs */
+}
+
+static void
+lysc_node_choice_free(struct ly_ctx *ctx, struct lysc_node_choice *node)
+{
+ struct lysc_node *child, *child_next;
+
+ FREE_MEMBER(ctx, node->when, lysc_when_free);
+ FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
+ if (node->cases) {
+ LY_LIST_FOR_SAFE(node->cases->child, child_next, child) {
+ lysc_node_free(ctx, child);
+ }
+ LY_LIST_FOR_SAFE((struct lysc_node*)node->cases, child_next, child) {
+ lysc_node_free(ctx, child);
+ }
+ }
+}
+
+static void
+lysc_node_anydata_free(struct ly_ctx *ctx, struct lysc_node_anydata *node)
+{
+ FREE_ARRAY(ctx, node->musts, lysc_must_free);
+}
+
+void
+lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node)
+{
+ /* common part */
+ FREE_STRING(ctx, node->name);
+
+ /* nodetype-specific part */
+ switch(node->nodetype) {
+ case LYS_CONTAINER:
+ lysc_node_container_free(ctx, (struct lysc_node_container*)node);
+ break;
+ case LYS_LEAF:
+ lysc_node_leaf_free(ctx, (struct lysc_node_leaf*)node);
+ break;
+ case LYS_LEAFLIST:
+ lysc_node_leaflist_free(ctx, (struct lysc_node_leaflist*)node);
+ break;
+ case LYS_LIST:
+ lysc_node_list_free(ctx, (struct lysc_node_list*)node);
+ break;
+ case LYS_CHOICE:
+ lysc_node_choice_free(ctx, (struct lysc_node_choice*)node);
+ break;
+ case LYS_CASE:
+ /* nothing specific */
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ lysc_node_anydata_free(ctx, (struct lysc_node_anydata*)node);
+ break;
+ default:
+ LOGINT(ctx);
+ }
+
+ FREE_MEMBER(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);
+}
+
+static void
+lysc_module_free_(struct lysc_module *module)
+{
+ struct ly_ctx *ctx;
+ struct lysc_node *node, *node_next;
+
+ LY_CHECK_ARG_RET(NULL, module,);
+ ctx = module->ctx;
+
+ FREE_STRING(ctx, module->name);
+ FREE_STRING(ctx, module->ns);
+ FREE_STRING(ctx, module->prefix);
+ FREE_STRING(ctx, module->revision);
+
+ FREE_ARRAY(ctx, module->imports, lysc_import_free);
+ FREE_ARRAY(ctx, module->features, lysc_feature_free);
+ FREE_ARRAY(ctx, module->identities, lysc_ident_free);
+
+ LY_LIST_FOR_SAFE(module->data, node_next, node) {
+ lysc_node_free(ctx, node);
+ }
+
+ FREE_ARRAY(ctx, module->exts, lysc_ext_instance_free);
+
+ free(module);
+}
+
+void
+lysc_module_free(struct lysc_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv))
+{
+ if (module) {
+ lysc_module_free_(module);
+ }
+}
+
+void
+lys_module_free(struct lys_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv))
+{
+ if (!module) {
+ return;
+ }
+
+ lysc_module_free(module->compiled, private_destructor);
+ lysp_module_free(module->parsed);
+ free(module);
+}
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 41a1adb..66fcf37 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -11,16 +11,136 @@
*
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _XOPEN_SOURCE
+#include "common.h"
#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
#include <limits.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <time.h>
#include "libyang.h"
-#include "common.h"
#include "tree_schema_internal.h"
+/**
+ * @brief Parse an identifier.
+ *
+ * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
+ * identifier = (ALPHA / "_")
+ * *(ALPHA / DIGIT / "_" / "-" / ".")
+ *
+ * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
+ */
+static LY_ERR
+lys_parse_id(const char **id)
+{
+ assert(id && *id);
+
+ if (!is_yangidentstartchar(**id)) {
+ return LY_EINVAL;
+ }
+ ++(*id);
+
+ while (is_yangidentchar(**id)) {
+ ++(*id);
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
+{
+ assert(id && *id);
+ assert(prefix && prefix_len);
+ assert(name && name_len);
+
+ *prefix = *id;
+ *prefix_len = 0;
+ *name = NULL;
+ *name_len = 0;
+
+ LY_CHECK_RET(lys_parse_id(id));
+ if (**id == ':') {
+ /* there is prefix */
+ *prefix_len = *id - *prefix;
+ ++(*id);
+ *name = *id;
+
+ LY_CHECK_RET(lys_parse_id(id));
+ *name_len = *id - *name;
+ } else {
+ /* there is no prefix, so what we have as prefix now is actually the name */
+ *name = *prefix;
+ *name_len = *id - *name;
+ *prefix = NULL;
+ }
+
+ return 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 ret = LY_EVALID;
+ const char *name, *prefix, *id;
+ const struct lysc_node *context;
+ size_t name_len, prefix_len;
+ const struct lys_module *mod;
+
+ assert(nodeid);
+ assert(context_node);
+ assert(target);
+ *target = NULL;
+
+ id = nodeid;
+ context = context_node;
+ 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);
+ 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->compiled->name);
+ return LY_ENOTFOUND;
+ }
+ } else {
+ mod = context_node->module;
+ }
+ 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);
+ return LY_ENOTFOUND;
+ }
+ if (nodeid_len && ((size_t)(id - nodeid) >= nodeid_len)) {
+ break;
+ }
+ if (id && *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);
+ return LY_EVALID;
+ }
+ ++id;
+ }
+
+ if (ret == LY_SUCCESS) {
+ *target = context;
+ if (nodetype && !(context->nodetype & nodetype)) {
+ return LY_EDENIED;
+ }
+ }
+
+ return ret;
+}
+
LY_ERR
lysp_check_prefix(struct ly_parser_ctx *ctx, struct lysp_module *module, const char **value)
{
@@ -44,14 +164,37 @@
}
LY_ERR
-lysp_check_date(struct ly_ctx *ctx, const char *date, int date_len, const char *stmt)
+lysc_check_status(struct lysc_ctx *ctx,
+ uint16_t flags1, void *mod1, const char *name1,
+ uint16_t flags2, void *mod2, const char *name2)
+{
+ uint16_t flg1, flg2;
+
+ flg1 = (flags1 & LYS_STATUS_MASK) ? (flags1 & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+ flg2 = (flags2 & LYS_STATUS_MASK) ? (flags2 & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+
+ if ((flg1 < flg2) && (mod1 == mod2)) {
+ if (ctx) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "A %s definition \"%s\" is not allowed to reference %s definition \"%s\".",
+ flg1 == LYS_STATUS_CURR ? "current" : "deprecated", name1,
+ flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", name2);
+ }
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_check_date(struct ly_parser_ctx *ctx, const char *date, int date_len, const char *stmt)
{
int i;
struct tm tm, tm_;
char *r;
- LY_CHECK_ARG_RET(ctx, date, LY_EINVAL);
- LY_CHECK_ERR_RET(date_len != LY_REV_SIZE - 1, LOGARG(ctx, date_len), LY_EINVAL);
+ LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, date, LY_EINVAL);
+ LY_CHECK_ERR_RET(date_len != LY_REV_SIZE - 1, LOGARG(ctx ? ctx->ctx : NULL, date_len), LY_EINVAL);
/* check format */
for (i = 0; i < date_len; i++) {
@@ -81,7 +224,13 @@
return LY_SUCCESS;
error:
- LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, date_len, date, stmt);
+ if (stmt) {
+ if (ctx) {
+ LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LY_VCODE_INVAL, date_len, date, stmt);
+ } else {
+ LOGVAL(NULL, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, date_len, date, stmt);
+ }
+ }
return LY_EINVAL;
}
@@ -92,7 +241,7 @@
struct lysp_revision rev;
for (i = 1, r = 0; revs && i < LY_ARRAY_SIZE(revs); i++) {
- if (strcmp(revs[i].rev, revs[r].rev) > 0) {
+ if (strcmp(revs[i].date, revs[r].date) > 0) {
r = i;
}
}
@@ -105,213 +254,1078 @@
}
}
-struct lysc_module *
-lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len)
+static const struct lysp_tpdf *
+lysp_type_match(const char *name, struct lysp_node *node)
{
- struct lysc_import *imp;
+ const struct lysp_tpdf *typedefs;
+ unsigned int u;
- assert(mod);
-
- if (!strncmp(mod->prefix, prefix, len) && mod->prefix[len] == '\0') {
- /* it is the prefix of the module itself */
- return mod;
- }
-
- /* search in imports */
- LY_ARRAY_FOR(mod->imports, struct lysc_import, imp) {
- if (!strncmp(imp->prefix, prefix, len) && mod->prefix[len] == '\0') {
- return imp->module;
+ typedefs = lysp_node_typedefs(node);
+ LY_ARRAY_FOR(typedefs, u) {
+ if (!strcmp(name, typedefs[u].name)) {
+ /* match */
+ return &typedefs[u];
}
}
return NULL;
}
+static LY_DATA_TYPE
+lysp_type_str2builtin(const char *name, size_t len)
+{
+ if (len >= 4) { /* otherwise it does not match any built-in type */
+ if (name[0] == 'b') {
+ if (name[1] == 'i') {
+ if (len == 6 && !strncmp(&name[2], "nary", 4)) {
+ return LY_TYPE_BINARY;
+ } else if (len == 4 && !strncmp(&name[2], "ts", 2)) {
+ return LY_TYPE_BITS;
+ }
+ } else if (len == 7 && !strncmp(&name[1], "oolean", 6)) {
+ return LY_TYPE_BOOL;
+ }
+ } else if (name[0] == 'd') {
+ if (len == 9 && !strncmp(&name[1], "ecimal64", 8)) {
+ return LY_TYPE_DEC64;
+ }
+ } else if (name[0] == 'e') {
+ if (len == 5 && !strncmp(&name[1], "mpty", 4)) {
+ return LY_TYPE_EMPTY;
+ } else if (len == 11 && !strncmp(&name[1], "numeration", 10)) {
+ return LY_TYPE_ENUM;
+ }
+ } else if (name[0] == 'i') {
+ if (name[1] == 'n') {
+ if (len == 4 && !strncmp(&name[2], "t8", 2)) {
+ return LY_TYPE_INT8;
+ } else if (len == 5) {
+ if (!strncmp(&name[2], "t16", 3)) {
+ return LY_TYPE_INT16;
+ } else if (!strncmp(&name[2], "t32", 3)) {
+ return LY_TYPE_INT32;
+ } else if (!strncmp(&name[2], "t64", 3)) {
+ return LY_TYPE_INT64;
+ }
+ } else if (len == 19 && !strncmp(&name[2], "stance-identifier", 17)) {
+ return LY_TYPE_INST;
+ }
+ } else if (len == 11 && !strncmp(&name[1], "dentityref", 10)) {
+ return LY_TYPE_IDENT;
+ }
+ } else if (name[0] == 'l') {
+ if (len == 7 && !strncmp(&name[1], "eafref", 6)) {
+ return LY_TYPE_LEAFREF;
+ }
+ } else if (name[0] == 's') {
+ if (len == 6 && !strncmp(&name[1], "tring", 5)) {
+ return LY_TYPE_STRING;
+ }
+ } else if (name[0] == 'u') {
+ if (name[1] == 'n') {
+ if (len == 5 && !strncmp(&name[2], "ion", 3)) {
+ return LY_TYPE_UNION;
+ }
+ } else if (name[1] == 'i' && name[2] == 'n' && name[3] == 't') {
+ if (len == 5 && name[4] == '8') {
+ return LY_TYPE_UINT8;
+ } else if (len == 6) {
+ if (!strncmp(&name[4], "16", 2)) {
+ return LY_TYPE_UINT16;
+ } else if (!strncmp(&name[4], "32", 2)) {
+ return LY_TYPE_UINT32;
+ } else if (!strncmp(&name[4], "64", 2)) {
+ return LY_TYPE_UINT64;
+ }
+ }
+ }
+ }
+ }
+
+ return LY_TYPE_UNKNOWN;
+}
+
+LY_ERR
+lysp_type_find(const char *id, struct lysp_node *start_node, struct lysp_module *start_module,
+ LY_DATA_TYPE *type, const struct lysp_tpdf **tpdf, struct lysp_node **node, struct lysp_module **module)
+{
+ const char *str, *name;
+ struct lysp_tpdf *typedefs;
+ unsigned int u, v;
+
+ assert(id);
+ assert(start_module);
+ assert(tpdf);
+ assert(node);
+ assert(module);
+
+ *node = NULL;
+ str = strchr(id, ':');
+ if (str) {
+ *module = lysp_module_find_prefix(start_module, id, str - id);
+ name = str + 1;
+ *type = LY_TYPE_UNKNOWN;
+ } else {
+ *module = start_module;
+ name = id;
+
+ /* check for built-in types */
+ *type = lysp_type_str2builtin(name, strlen(name));
+ if (*type) {
+ *tpdf = NULL;
+ return LY_SUCCESS;
+ }
+ }
+ LY_CHECK_RET(!(*module), LY_ENOTFOUND);
+
+ if (start_node && *module == start_module) {
+ /* search typedefs in parent's nodes */
+ *node = start_node;
+ while (*node) {
+ *tpdf = lysp_type_match(name, *node);
+ if (*tpdf) {
+ /* match */
+ return LY_SUCCESS;
+ }
+ *node = (*node)->parent;
+ }
+ }
+
+ /* search in top-level typedefs */
+ if ((*module)->typedefs) {
+ LY_ARRAY_FOR((*module)->typedefs, u) {
+ if (!strcmp(name, (*module)->typedefs[u].name)) {
+ /* match */
+ *tpdf = &(*module)->typedefs[u];
+ return LY_SUCCESS;
+ }
+ }
+ }
+
+ /* search in submodules' typedefs */
+ LY_ARRAY_FOR((*module)->includes, u) {
+ typedefs = (*module)->includes[u].submodule->typedefs;
+ LY_ARRAY_FOR(typedefs, v) {
+ if (!strcmp(name, typedefs[v].name)) {
+ /* match */
+ *tpdf = &typedefs[v];
+ return LY_SUCCESS;
+ }
+ }
+ }
+
+ return LY_ENOTFOUND;
+}
+
+/*
+ * @brief Check name of a new type to avoid name collisions.
+ *
+ * @param[in] ctx Parser context, module where the type is being defined is taken from here.
+ * @param[in] node Schema node where the type is being defined, NULL in case of a top-level typedef.
+ * @param[in] tpdf Typedef definition to check.
+ * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
+ * typedefs are checked, caller is supposed to free the table.
+ * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
+ * typedefs are checked, caller is supposed to free the table.
+ * @return LY_EEXIST in case of collision, LY_SUCCESS otherwise.
+ */
+static LY_ERR
+lysp_check_typedef(struct ly_parser_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
+ struct hash_table *tpdfs_global, struct hash_table *tpdfs_scoped)
+{
+ struct lysp_node *parent;
+ uint32_t hash;
+ size_t name_len;
+ const char *name;
+ unsigned int u;
+ const struct lysp_tpdf *typedefs;
+
+ assert(ctx);
+ assert(tpdf);
+
+ name = tpdf->name;
+ name_len = strlen(name);
+
+ if (lysp_type_str2builtin(name, name_len)) {
+ LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+ "Invalid name \"%s\" of typedef - name collision with a built-in type.", name);
+ return LY_EEXIST;
+ }
+
+ /* check locally scoped typedefs (avoid name shadowing) */
+ if (node) {
+ typedefs = lysp_node_typedefs(node);
+ LY_ARRAY_FOR(typedefs, u) {
+ if (&typedefs[u] == tpdf) {
+ break;
+ }
+ if (!strcmp(name, typedefs[u].name)) {
+ LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+ "Invalid name \"%s\" of typedef - name collision with sibling type.", name);
+ return LY_EEXIST;
+ }
+ }
+ /* search typedefs in parent's nodes */
+ for (parent = node->parent; parent; parent = node->parent) {
+ if (lysp_type_match(name, parent)) {
+ LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+ "Invalid name \"%s\" of typedef - name collision with another scoped type.", name);
+ return LY_EEXIST;
+ }
+ }
+ }
+
+ /* check collision with the top-level typedefs */
+ hash = dict_hash(name, name_len);
+ if (node) {
+ lyht_insert(tpdfs_scoped, &name, hash, NULL);
+ if (!lyht_find(tpdfs_global, &name, hash, NULL)) {
+ LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+ "Invalid name \"%s\" of typedef - scoped type collide with a top-level type.", name);
+ return LY_EEXIST;
+ }
+ } else {
+ if (lyht_insert(tpdfs_global, &name, hash, NULL)) {
+ LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+ "Invalid name \"%s\" of typedef - name collision with another top-level type.", name);
+ return LY_EEXIST;
+ }
+ /* it is not necessary to test collision with the scoped types - in lysp_check_typedefs, all the
+ * top-level typedefs are inserted into the tables before the scoped typedefs, so the collision
+ * is detected in the first branch few lines above */
+ }
+
+ return LY_SUCCESS;
+}
+
+static int
+lysp_id_cmp(void *val1, void *val2, int UNUSED(mod), void *UNUSED(cb_data))
+{
+ return !strcmp(val1, val2);
+}
+
+LY_ERR
+lysp_check_typedefs(struct ly_parser_ctx *ctx)
+{
+ struct hash_table *ids_global;
+ struct hash_table *ids_scoped;
+ const struct lysp_tpdf *typedefs;
+ unsigned int i, u;
+ LY_ERR ret = LY_EVALID;
+
+ /* check name collisions - typedefs and groupings */
+ ids_global = lyht_new(8, sizeof(char*), lysp_id_cmp, NULL, 1);
+ ids_scoped = lyht_new(8, sizeof(char*), lysp_id_cmp, NULL, 1);
+ LY_ARRAY_FOR(ctx->mod->typedefs, i) {
+ if (lysp_check_typedef(ctx, NULL, &ctx->mod->typedefs[i], ids_global, ids_scoped)) {
+ goto cleanup;
+ }
+ }
+ LY_ARRAY_FOR(ctx->mod->includes, i) {
+ LY_ARRAY_FOR(ctx->mod->includes[i].submodule->typedefs, u) {
+ if (lysp_check_typedef(ctx, NULL, &ctx->mod->includes[i].submodule->typedefs[u], ids_global, ids_scoped)) {
+ goto cleanup;
+ }
+ }
+ }
+ for (u = 0; u < ctx->tpdfs_nodes.count; ++u) {
+ typedefs = lysp_node_typedefs((struct lysp_node *)ctx->tpdfs_nodes.objs[u]);
+ LY_ARRAY_FOR(typedefs, i) {
+ if (lysp_check_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[u], &typedefs[i], ids_global, ids_scoped)) {
+ goto cleanup;
+ }
+ }
+ }
+ ret = LY_SUCCESS;
+cleanup:
+ lyht_free(ids_global);
+ lyht_free(ids_scoped);
+ ly_set_erase(&ctx->tpdfs_nodes, NULL);
+
+ return ret;
+}
+
+void
+lys_module_implement(struct lys_module *mod)
+{
+ assert(mod);
+ if (mod->parsed) {
+ mod->parsed->implemented = 1;
+ }
+ if (mod->compiled) {
+ mod->compiled->implemented = 1;
+ }
+}
+
+struct lysp_load_module_check_data {
+ const char *name;
+ const char *revision;
+ const char *path;
+ const char* submoduleof;
+};
+
+static LY_ERR
+lysp_load_module_check(struct ly_ctx *ctx, struct lysp_module *mod, void *data)
+{
+ struct lysp_load_module_check_data *info = data;
+ const char *filename, *dot, *rev;
+ size_t len;
+
+ if (info->name) {
+ /* check name of the parsed model */
+ if (strcmp(info->name, mod->name)) {
+ LOGERR(ctx, LY_EINVAL, "Unexpected module \"%s\" parsed instead of \"%s\").", mod->name, info->name);
+ return LY_EINVAL;
+ }
+ }
+ if (info->revision) {
+ /* check revision of the parsed model */
+ if (!mod->revs || strcmp(info->revision, mod->revs[0].date)) {
+ LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").", mod->name,
+ mod->revs[0].date, info->revision);
+ return LY_EINVAL;
+ }
+ }
+ if (info->submoduleof) {
+ /* check that we have really a submodule */
+ if (!mod->submodule) {
+ /* submodule is not a submodule */
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Included \"%s\" schema from \"%s\" is actually not a submodule.",
+ mod->name, info->submoduleof);
+ return LY_EVALID;
+ }
+ /* check that the submodule belongs-to our module */
+ if (strcmp(info->submoduleof, mod->belongsto)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Included \"%s\" submodule from \"%s\" belongs-to a different module \"%s\".",
+ mod->name, info->submoduleof, mod->belongsto);
+ return LY_EVALID;
+ }
+ /* check circular dependency */
+ if (mod->parsing) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "A circular dependency (include) for module \"%s\".", mod->name);
+ return LY_EVALID;
+ }
+ }
+ if (info->path) {
+ /* check that name and revision match filename */
+ filename = strrchr(info->path, '/');
+ if (!filename) {
+ filename = info->path;
+ } else {
+ filename++;
+ }
+ /* name */
+ len = strlen(mod->name);
+ rev = strchr(filename, '@');
+ dot = strrchr(info->path, '.');
+ if (strncmp(filename, mod->name, len) ||
+ ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
+ LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
+ }
+ /* revision */
+ if (rev) {
+ len = dot - ++rev;
+ if (!mod->revs || len != 10 || strncmp(mod->revs[0].date, rev, len)) {
+ LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
+ mod->revs ? mod->revs[0].date : "none");
+ }
+ }
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement, struct ly_parser_ctx *main_ctx,
+ struct lys_module **result)
+{
+ int fd;
+ char *filepath = NULL;
+ LYS_INFORMAT format;
+ struct lys_module *mod = NULL;
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_load_module_check_data check_data = {0};
+
+ LY_CHECK_RET(lys_search_localfile(ly_ctx_get_searchdirs(ctx), !(ctx->flags & LY_CTX_DISABLE_SEARCHDIR_CWD), name, revision,
+ &filepath, &format));
+ LY_CHECK_ERR_RET(!filepath, LOGERR(ctx, LY_ENOTFOUND, "Data model \"%s%s%s\" not found in local searchdirs.",
+ name, revision ? "@" : "", revision ? revision : ""), LY_ENOTFOUND);
+
+
+ LOGVRB("Loading schema from \"%s\" file.", filepath);
+
+ /* open the file */
+ fd = open(filepath, O_RDONLY);
+ LY_CHECK_ERR_GOTO(fd < 0, LOGERR(ctx, LY_ESYS, "Unable to open data model file \"%s\" (%s).",
+ filepath, strerror(errno)); ret = LY_ESYS, cleanup);
+
+ check_data.name = name;
+ check_data.revision = revision;
+ check_data.path = filepath;
+ mod = lys_parse_fd_(ctx, fd, format, implement, main_ctx,
+ lysp_load_module_check, &check_data);
+ close(fd);
+ LY_CHECK_ERR_GOTO(!mod, ly_errcode(ctx), cleanup);
+
+ if (!mod->parsed->filepath) {
+ char rpath[PATH_MAX];
+ if (realpath(filepath, rpath) != NULL) {
+ mod->parsed->filepath = lydict_insert(ctx, rpath, 0);
+ } else {
+ mod->parsed->filepath = lydict_insert(ctx, filepath, 0);
+ }
+ }
+
+ *result = mod;
+
+ /* success */
+cleanup:
+ free(filepath);
+ return ret;
+}
+
+LY_ERR
+lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, int implement, int require_parsed, struct lys_module **mod)
+{
+ const char *module_data = NULL;
+ LYS_INFORMAT format = LYS_IN_UNKNOWN;
+ void (*module_data_free)(void *module_data, void *user_data) = NULL;
+ struct lysp_load_module_check_data check_data = {0};
+
+ /* try to get the module from the context */
+ if (revision) {
+ *mod = (struct lys_module*)ly_ctx_get_module(ctx, name, revision);
+ } else {
+ *mod = (struct lys_module*)ly_ctx_get_module_latest(ctx, name);
+ }
+
+ if (!(*mod) || (require_parsed && !(*mod)->parsed)) {
+ (*mod) = NULL;
+
+ /* check collision with other implemented revision */
+ if (implement && ly_ctx_get_module_implemented(ctx, name)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
+ "Module \"%s\" is already present in other implemented revision.", name);
+ return LY_EDENIED;
+ }
+
+ /* module not present in the context, get the input data and parse it */
+ if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+search_clb:
+ if (ctx->imp_clb) {
+ if (ctx->imp_clb(name, revision, NULL, NULL, ctx->imp_clb_data,
+ &format, &module_data, &module_data_free) == LY_SUCCESS) {
+ check_data.name = name;
+ check_data.revision = revision;
+ *mod = lys_parse_mem_(ctx, module_data, format, implement, NULL,
+ lysp_load_module_check, &check_data);
+ if (module_data_free) {
+ module_data_free((void*)module_data, ctx->imp_clb_data);
+ }
+ }
+ }
+ if (!(*mod) && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+ goto search_file;
+ }
+ } else {
+search_file:
+ if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
+ /* module was not received from the callback or there is no callback set */
+ lys_module_localfile(ctx, name, revision, implement, NULL, mod);
+ }
+ if (!(*mod) && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+ goto search_clb;
+ }
+ }
+
+ if ((*mod) && !revision && ((*mod)->parsed->latest_revision == 1)) {
+ /* update the latest_revision flag - here we have selected the latest available schema,
+ * consider that even the callback provides correct latest revision */
+ (*mod)->parsed->latest_revision = 2;
+ }
+ } else {
+ /* we have module from the current context */
+ if (implement && (ly_ctx_get_module_implemented(ctx, name) != *mod)) {
+ /* check collision with other implemented revision */
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
+ "Module \"%s\" is already present in other implemented revision.", name);
+ *mod = NULL;
+ return LY_EDENIED;
+ }
+
+ /* circular check */
+ if ((*mod)->parsed && (*mod)->parsed->parsing) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "A circular dependency (import) for module \"%s\".", name);
+ *mod = NULL;
+ return LY_EVALID;
+ }
+ }
+ if (!(*mod)) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "%s \"%s\" module failed.", implement ? "Loading" : "Importing", name);
+ return LY_EVALID;
+ }
+
+ if (implement) {
+ /* mark the module implemented, check for collision was already done */
+ lys_module_implement(*mod);
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_load_submodule(struct ly_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc)
+{
+ struct lys_module *submod;
+ const char *submodule_data = NULL;
+ LYS_INFORMAT format = LYS_IN_UNKNOWN;
+ void (*submodule_data_free)(void *module_data, void *user_data) = NULL;
+ struct lysp_load_module_check_data check_data = {0};
+
+ /* submodule not present in the context, get the input data and parse it */
+ if (!(ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+search_clb:
+ if (ctx->ctx->imp_clb) {
+ if (ctx->ctx->imp_clb(mod->name, NULL, inc->name, inc->rev[0] ? inc->rev : NULL, ctx->ctx->imp_clb_data,
+ &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) {
+ check_data.name = inc->name;
+ check_data.revision = inc->rev[0] ? inc->rev : NULL;
+ check_data.submoduleof = mod->name;
+ submod = lys_parse_mem_(ctx->ctx, submodule_data, format, mod->implemented, ctx,
+ lysp_load_module_check, &check_data);
+ if (submodule_data_free) {
+ submodule_data_free((void*)submodule_data, ctx->ctx->imp_clb_data);
+ }
+ }
+ }
+ if (!submod && !(ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+ goto search_file;
+ }
+ } else {
+search_file:
+ if (!(ctx->ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
+ /* module was not received from the callback or there is no callback set */
+ lys_module_localfile(ctx->ctx, inc->name, inc->rev[0] ? inc->rev : NULL, mod->implemented, ctx, &submod);
+ }
+ if (!submod && (ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+ goto search_clb;
+ }
+ }
+ if (submod) {
+ if (!inc->rev[0] && (submod->parsed->latest_revision == 1)) {
+ /* update the latest_revision flag - here we have selected the latest available schema,
+ * consider that even the callback provides correct latest revision */
+ submod->parsed->latest_revision = 2;
+ }
+
+ inc->submodule = submod->parsed;
+ free(submod);
+ }
+ if (!inc->submodule) {
+ LOGVAL(ctx->ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Including \"%s\" submodule into \"%s\" failed.", inc->name, mod->name);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+#define FIND_MODULE(TYPE, MOD, ID) \
+ TYPE *imp; \
+ if (!strncmp((MOD)->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
+ /* it is the prefix of the module itself */ \
+ m = ly_ctx_get_module((MOD)->ctx, (MOD)->name, ((struct lysc_module*)(MOD))->revision); \
+ } \
+ /* search in imports */ \
+ if (!m) { \
+ LY_ARRAY_FOR((MOD)->imports, TYPE, imp) { \
+ if (!strncmp(imp->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
+ m = imp->module; \
+ break; \
+ } \
+ } \
+ }
+
+struct lysc_module *
+lysc_module_find_prefix(const struct lysc_module *mod, const char *prefix, size_t len)
+{
+ const struct lys_module *m = NULL;
+
+ FIND_MODULE(struct lysc_import, mod, 1);
+ return m ? m->compiled : NULL;
+}
+
+struct lysp_module *
+lysp_module_find_prefix(const struct lysp_module *mod, const char *prefix, size_t len)
+{
+ const struct lys_module *m = NULL;
+
+ FIND_MODULE(struct lysp_import, mod, 1);
+ return m ? m->parsed : NULL;
+}
+
+struct lys_module *
+lys_module_find_prefix(const struct lys_module *mod, const char *prefix, size_t len)
+{
+ const struct lys_module *m = NULL;
+
+ if (mod->compiled) {
+ FIND_MODULE(struct lysc_import, mod->compiled, 1);
+ } else {
+ FIND_MODULE(struct lysp_import, mod->parsed, 2);
+ }
+ return (struct lys_module*)m;
+}
+
+const char *
+lys_nodetype2str(uint16_t nodetype)
+{
+ switch(nodetype) {
+ case LYS_CONTAINER:
+ return "container";
+ case LYS_CHOICE:
+ return "choice";
+ case LYS_LEAF:
+ return "leaf";
+ case LYS_LEAFLIST:
+ return "leaf-list";
+ case LYS_LIST:
+ return "list";
+ case LYS_ANYXML:
+ return "anyxml";
+ case LYS_ANYDATA:
+ return "anydata";
+ default:
+ return "unknown";
+ }
+}
+
+API const struct lysp_tpdf *
+lysp_node_typedefs(const struct lysp_node *node)
+{
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return ((struct lysp_node_container*)node)->typedefs;
+ case LYS_LIST:
+ return ((struct lysp_node_list*)node)->typedefs;
+ case LYS_GROUPING:
+ return ((struct lysp_grp*)node)->typedefs;
+ case LYS_ACTION:
+ return ((struct lysp_action*)node)->typedefs;
+ case LYS_INOUT:
+ return ((struct lysp_action_inout*)node)->typedefs;
+ case LYS_NOTIF:
+ return ((struct lysp_notif*)node)->typedefs;
+ default:
+ return NULL;
+ }
+}
+
+API const struct lysp_grp *
+lysp_node_groupings(const struct lysp_node *node)
+{
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return ((struct lysp_node_container*)node)->groupings;
+ case LYS_LIST:
+ return ((struct lysp_node_list*)node)->groupings;
+ case LYS_GROUPING:
+ return ((struct lysp_grp*)node)->groupings;
+ case LYS_ACTION:
+ return ((struct lysp_action*)node)->groupings;
+ case LYS_INOUT:
+ return ((struct lysp_action_inout*)node)->groupings;
+ case LYS_NOTIF:
+ return ((struct lysp_notif*)node)->groupings;
+ default:
+ return NULL;
+ }
+}
+
+struct lysp_action **
+lysp_node_actions_p(struct lysp_node *node)
+{
+ assert(node);
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysp_node_container*)node)->actions;
+ case LYS_LIST:
+ return &((struct lysp_node_list*)node)->actions;
+ case LYS_GROUPING:
+ return &((struct lysp_grp*)node)->actions;
+ case LYS_AUGMENT:
+ return &((struct lysp_augment*)node)->actions;
+ default:
+ return NULL;
+ }
+}
+
+API const struct lysp_action *
+lysp_node_actions(const struct lysp_node *node)
+{
+ struct lysp_action **actions;
+ actions = lysp_node_actions_p((struct lysp_node*)node);
+ if (actions) {
+ return *actions;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysp_notif **
+lysp_node_notifs_p(struct lysp_node *node)
+{
+ assert(node);
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysp_node_container*)node)->notifs;
+ case LYS_LIST:
+ return &((struct lysp_node_list*)node)->notifs;
+ case LYS_GROUPING:
+ return &((struct lysp_grp*)node)->notifs;
+ case LYS_AUGMENT:
+ return &((struct lysp_augment*)node)->notifs;
+ default:
+ return NULL;
+ }
+}
+
+API const struct lysp_notif *
+lysp_node_notifs(const struct lysp_node *node)
+{
+ struct lysp_notif **notifs;
+ notifs = lysp_node_notifs_p((struct lysp_node*)node);
+ if (notifs) {
+ return *notifs;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysp_node **
+lysp_node_children_p(struct lysp_node *node)
+{
+ assert(node);
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysp_node_container*)node)->child;
+ case LYS_CHOICE:
+ return &((struct lysp_node_choice*)node)->child;
+ case LYS_LIST:
+ return &((struct lysp_node_list*)node)->child;
+ case LYS_CASE:
+ return &((struct lysp_node_case*)node)->child;
+ case LYS_GROUPING:
+ return &((struct lysp_grp*)node)->data;
+ case LYS_AUGMENT:
+ return &((struct lysp_augment*)node)->child;
+ case LYS_INOUT:
+ return &((struct lysp_action_inout*)node)->data;
+ case LYS_NOTIF:
+ return &((struct lysp_notif*)node)->data;
+ default:
+ return NULL;
+ }
+}
+
+API const struct lysp_node *
+lysp_node_children(const struct lysp_node *node)
+{
+ struct lysp_node **children;
+ children = lysp_node_children_p((struct lysp_node*)node);
+ if (children) {
+ return *children;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysc_action **
+lysc_node_actions_p(struct lysc_node *node)
+{
+ assert(node);
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysc_node_container*)node)->actions;
+ case LYS_LIST:
+ return &((struct lysc_node_list*)node)->actions;
+ default:
+ return NULL;
+ }
+}
+
+API const struct lysc_action *
+lysc_node_actions(const struct lysc_node *node)
+{
+ struct lysc_action **actions;
+ actions = lysc_node_actions_p((struct lysc_node*)node);
+ if (actions) {
+ return *actions;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysc_notif **
+lysc_node_notifs_p(struct lysc_node *node)
+{
+ assert(node);
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysc_node_container*)node)->notifs;
+ case LYS_LIST:
+ return &((struct lysc_node_list*)node)->notifs;
+ default:
+ return NULL;
+ }
+}
+
+API const struct lysc_notif *
+lysc_node_notifs(const struct lysc_node *node)
+{
+ struct lysc_notif **notifs;
+ notifs = lysc_node_notifs_p((struct lysc_node*)node);
+ if (notifs) {
+ return *notifs;
+ } else {
+ return NULL;
+ }
+}
+
+struct lysc_node **
+lysc_node_children_p(const struct lysc_node *node)
+{
+ assert(node);
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ 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;
+ } else {
+ return NULL;
+ }
+ case LYS_LIST:
+ return &((struct lysc_node_list*)node)->child;
+/* TODO
+ case LYS_INOUT:
+ return &((struct lysc_action_inout*)node)->child;
+ case LYS_NOTIF:
+ return &((struct lysc_notif*)node)->child;
+*/
+ default:
+ return NULL;
+ }
+}
+
+API const struct lysc_node *
+lysc_node_children(const struct lysc_node *node)
+{
+ struct lysc_node **children;
+ children = lysc_node_children_p((struct lysc_node*)node);
+ if (children) {
+ return *children;
+ } else {
+ return NULL;
+ }
+}
+
+struct lys_module *
+lysp_find_module(struct ly_ctx *ctx, const struct lysp_module *mod)
+{
+ unsigned int u;
+
+ for (u = 0; u < ctx->list.count; ++u) {
+ if (((struct lys_module*)ctx->list.objs[u])->parsed == mod) {
+ return ((struct lys_module*)ctx->list.objs[u]);
+ }
+ }
+ return NULL;
+}
+
enum yang_keyword
-match_keyword(char *data)
+match_keyword(const char *data)
{
/* TODO make this function usable in get_keyword function */
-#define MOVE_INPUT(DATA, COUNT) (data)+=COUNT;
-#define IF_KW(STR, LEN, STMT) if (!strncmp((data), STR, LEN)) {MOVE_INPUT(data, LEN);kw=STMT;}
-#define IF_KW_PREFIX(STR, LEN) if (!strncmp((data), STR, LEN)) {MOVE_INPUT(data, LEN);
-#define IF_KW_PREFIX_END }
+#define MOVE_IN(DATA, COUNT) (data)+=COUNT;
+#define IF_KEYWORD(STR, LEN, STMT) if (!strncmp((data), STR, LEN)) {MOVE_IN(data, LEN);kw=STMT;}
+#define IF_KEYWORD_PREFIX(STR, LEN) if (!strncmp((data), STR, LEN)) {MOVE_IN(data, LEN);
+#define IF_KEYWORD_PREFIX_END }
enum yang_keyword kw = YANG_NONE;
/* read the keyword itself */
switch (*data) {
case 'a':
- MOVE_INPUT(data, 1);
- IF_KW("rgument", 7, YANG_ARGUMENT)
- else IF_KW("ugment", 6, YANG_AUGMENT)
- else IF_KW("ction", 5, YANG_ACTION)
- else IF_KW_PREFIX("ny", 2)
- IF_KW("data", 4, YANG_ANYDATA)
- else IF_KW("xml", 3, YANG_ANYXML)
- IF_KW_PREFIX_END
+ MOVE_IN(data, 1);
+ IF_KEYWORD("rgument", 7, YANG_ARGUMENT)
+ else IF_KEYWORD("ugment", 6, YANG_AUGMENT)
+ else IF_KEYWORD("ction", 5, YANG_ACTION)
+ else IF_KEYWORD_PREFIX("ny", 2)
+ IF_KEYWORD("data", 4, YANG_ANYDATA)
+ else IF_KEYWORD("xml", 3, YANG_ANYXML)
+ IF_KEYWORD_PREFIX_END
break;
case 'b':
- MOVE_INPUT(data, 1);
- IF_KW("ase", 3, YANG_BASE)
- else IF_KW("elongs-to", 9, YANG_BELONGS_TO)
- else IF_KW("it", 2, YANG_BIT)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("ase", 3, YANG_BASE)
+ else IF_KEYWORD("elongs-to", 9, YANG_BELONGS_TO)
+ else IF_KEYWORD("it", 2, YANG_BIT)
break;
case 'c':
- MOVE_INPUT(data, 1);
- IF_KW("ase", 3, YANG_CASE)
- else IF_KW("hoice", 5, YANG_CHOICE)
- else IF_KW_PREFIX("on", 2)
- IF_KW("fig", 3, YANG_CONFIG)
- else IF_KW_PREFIX("ta", 2)
- IF_KW("ct", 2, YANG_CONTACT)
- else IF_KW("iner", 4, YANG_CONTAINER)
- IF_KW_PREFIX_END
- IF_KW_PREFIX_END
+ MOVE_IN(data, 1);
+ IF_KEYWORD("ase", 3, YANG_CASE)
+ else IF_KEYWORD("hoice", 5, YANG_CHOICE)
+ else IF_KEYWORD_PREFIX("on", 2)
+ IF_KEYWORD("fig", 3, YANG_CONFIG)
+ else IF_KEYWORD_PREFIX("ta", 2)
+ IF_KEYWORD("ct", 2, YANG_CONTACT)
+ else IF_KEYWORD("iner", 4, YANG_CONTAINER)
+ IF_KEYWORD_PREFIX_END
+ IF_KEYWORD_PREFIX_END
break;
case 'd':
- MOVE_INPUT(data, 1);
- IF_KW_PREFIX("e", 1)
- IF_KW("fault", 5, YANG_DEFAULT)
- else IF_KW("scription", 9, YANG_DESCRIPTION)
- else IF_KW_PREFIX("viat", 4)
- IF_KW("e", 1, YANG_DEVIATE)
- else IF_KW("ion", 3, YANG_DEVIATION)
- IF_KW_PREFIX_END
- IF_KW_PREFIX_END
+ MOVE_IN(data, 1);
+ IF_KEYWORD_PREFIX("e", 1)
+ IF_KEYWORD("fault", 5, YANG_DEFAULT)
+ else IF_KEYWORD("scription", 9, YANG_DESCRIPTION)
+ else IF_KEYWORD_PREFIX("viat", 4)
+ IF_KEYWORD("e", 1, YANG_DEVIATE)
+ else IF_KEYWORD("ion", 3, YANG_DEVIATION)
+ IF_KEYWORD_PREFIX_END
+ IF_KEYWORD_PREFIX_END
break;
case 'e':
- MOVE_INPUT(data, 1);
- IF_KW("num", 3, YANG_ENUM)
- else IF_KW_PREFIX("rror-", 5)
- IF_KW("app-tag", 7, YANG_ERROR_APP_TAG)
- else IF_KW("message", 7, YANG_ERROR_MESSAGE)
- IF_KW_PREFIX_END
- else IF_KW("xtension", 8, YANG_EXTENSION)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("num", 3, YANG_ENUM)
+ else IF_KEYWORD_PREFIX("rror-", 5)
+ IF_KEYWORD("app-tag", 7, YANG_ERROR_APP_TAG)
+ else IF_KEYWORD("message", 7, YANG_ERROR_MESSAGE)
+ IF_KEYWORD_PREFIX_END
+ else IF_KEYWORD("xtension", 8, YANG_EXTENSION)
break;
case 'f':
- MOVE_INPUT(data, 1);
- IF_KW("eature", 6, YANG_FEATURE)
- else IF_KW("raction-digits", 14, YANG_FRACTION_DIGITS)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("eature", 6, YANG_FEATURE)
+ else IF_KEYWORD("raction-digits", 14, YANG_FRACTION_DIGITS)
break;
case 'g':
- MOVE_INPUT(data, 1);
- IF_KW("rouping", 7, YANG_GROUPING)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("rouping", 7, YANG_GROUPING)
break;
case 'i':
- MOVE_INPUT(data, 1);
- IF_KW("dentity", 7, YANG_IDENTITY)
- else IF_KW("f-feature", 9, YANG_IF_FEATURE)
- else IF_KW("mport", 5, YANG_IMPORT)
- else IF_KW_PREFIX("n", 1)
- IF_KW("clude", 5, YANG_INCLUDE)
- else IF_KW("put", 3, YANG_INPUT)
- IF_KW_PREFIX_END
+ MOVE_IN(data, 1);
+ IF_KEYWORD("dentity", 7, YANG_IDENTITY)
+ else IF_KEYWORD("f-feature", 9, YANG_IF_FEATURE)
+ else IF_KEYWORD("mport", 5, YANG_IMPORT)
+ else IF_KEYWORD_PREFIX("n", 1)
+ IF_KEYWORD("clude", 5, YANG_INCLUDE)
+ else IF_KEYWORD("put", 3, YANG_INPUT)
+ IF_KEYWORD_PREFIX_END
break;
case 'k':
- MOVE_INPUT(data, 1);
- IF_KW("ey", 2, YANG_KEY)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("ey", 2, YANG_KEY)
break;
case 'l':
- MOVE_INPUT(data, 1);
- IF_KW_PREFIX("e", 1)
- IF_KW("af-list", 7, YANG_LEAF_LIST)
- else IF_KW("af", 2, YANG_LEAF)
- else IF_KW("ngth", 4, YANG_LENGTH)
- IF_KW_PREFIX_END
- else IF_KW("ist", 3, YANG_LIST)
+ MOVE_IN(data, 1);
+ IF_KEYWORD_PREFIX("e", 1)
+ IF_KEYWORD("af-list", 7, YANG_LEAF_LIST)
+ else IF_KEYWORD("af", 2, YANG_LEAF)
+ else IF_KEYWORD("ngth", 4, YANG_LENGTH)
+ IF_KEYWORD_PREFIX_END
+ else IF_KEYWORD("ist", 3, YANG_LIST)
break;
case 'm':
- MOVE_INPUT(data, 1);
- IF_KW_PREFIX("a", 1)
- IF_KW("ndatory", 7, YANG_MANDATORY)
- else IF_KW("x-elements", 10, YANG_MAX_ELEMENTS)
- IF_KW_PREFIX_END
- else IF_KW("in-elements", 11, YANG_MIN_ELEMENTS)
- else IF_KW("ust", 3, YANG_MUST)
- else IF_KW_PREFIX("od", 2)
- IF_KW("ule", 3, YANG_MODULE)
- else IF_KW("ifier", 5, YANG_MODIFIER)
- IF_KW_PREFIX_END
+ MOVE_IN(data, 1);
+ IF_KEYWORD_PREFIX("a", 1)
+ IF_KEYWORD("ndatory", 7, YANG_MANDATORY)
+ else IF_KEYWORD("x-elements", 10, YANG_MAX_ELEMENTS)
+ IF_KEYWORD_PREFIX_END
+ else IF_KEYWORD("in-elements", 11, YANG_MIN_ELEMENTS)
+ else IF_KEYWORD("ust", 3, YANG_MUST)
+ else IF_KEYWORD_PREFIX("od", 2)
+ IF_KEYWORD("ule", 3, YANG_MODULE)
+ else IF_KEYWORD("ifier", 5, YANG_MODIFIER)
+ IF_KEYWORD_PREFIX_END
break;
case 'n':
- MOVE_INPUT(data, 1);
- IF_KW("amespace", 8, YANG_NAMESPACE)
- else IF_KW("otification", 11, YANG_NOTIFICATION)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("amespace", 8, YANG_NAMESPACE)
+ else IF_KEYWORD("otification", 11, YANG_NOTIFICATION)
break;
case 'o':
- MOVE_INPUT(data, 1);
- IF_KW_PREFIX("r", 1)
- IF_KW("dered-by", 8, YANG_ORDERED_BY)
- else IF_KW("ganization", 10, YANG_ORGANIZATION)
- IF_KW_PREFIX_END
- else IF_KW("utput", 5, YANG_OUTPUT)
+ MOVE_IN(data, 1);
+ IF_KEYWORD_PREFIX("r", 1)
+ IF_KEYWORD("dered-by", 8, YANG_ORDERED_BY)
+ else IF_KEYWORD("ganization", 10, YANG_ORGANIZATION)
+ IF_KEYWORD_PREFIX_END
+ else IF_KEYWORD("utput", 5, YANG_OUTPUT)
break;
case 'p':
- MOVE_INPUT(data, 1);
- IF_KW("ath", 3, YANG_PATH)
- else IF_KW("attern", 6, YANG_PATTERN)
- else IF_KW("osition", 7, YANG_POSITION)
- else IF_KW_PREFIX("re", 2)
- IF_KW("fix", 3, YANG_PREFIX)
- else IF_KW("sence", 5, YANG_PRESENCE)
- IF_KW_PREFIX_END
+ MOVE_IN(data, 1);
+ IF_KEYWORD("ath", 3, YANG_PATH)
+ else IF_KEYWORD("attern", 6, YANG_PATTERN)
+ else IF_KEYWORD("osition", 7, YANG_POSITION)
+ else IF_KEYWORD_PREFIX("re", 2)
+ IF_KEYWORD("fix", 3, YANG_PREFIX)
+ else IF_KEYWORD("sence", 5, YANG_PRESENCE)
+ IF_KEYWORD_PREFIX_END
break;
case 'r':
- MOVE_INPUT(data, 1);
- IF_KW("ange", 4, YANG_RANGE)
- else IF_KW_PREFIX("e", 1)
- IF_KW_PREFIX("f", 1)
- IF_KW("erence", 6, YANG_REFERENCE)
- else IF_KW("ine", 3, YANG_REFINE)
- IF_KW_PREFIX_END
- else IF_KW("quire-instance", 14, YANG_REQUIRE_INSTANCE)
- else IF_KW("vision-date", 11, YANG_REVISION_DATE)
- else IF_KW("vision", 6, YANG_REVISION)
- IF_KW_PREFIX_END
- else IF_KW("pc", 2, YANG_RPC)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("ange", 4, YANG_RANGE)
+ else IF_KEYWORD_PREFIX("e", 1)
+ IF_KEYWORD_PREFIX("f", 1)
+ IF_KEYWORD("erence", 6, YANG_REFERENCE)
+ else IF_KEYWORD("ine", 3, YANG_REFINE)
+ IF_KEYWORD_PREFIX_END
+ else IF_KEYWORD("quire-instance", 14, YANG_REQUIRE_INSTANCE)
+ else IF_KEYWORD("vision-date", 11, YANG_REVISION_DATE)
+ else IF_KEYWORD("vision", 6, YANG_REVISION)
+ IF_KEYWORD_PREFIX_END
+ else IF_KEYWORD("pc", 2, YANG_RPC)
break;
case 's':
- MOVE_INPUT(data, 1);
- IF_KW("tatus", 5, YANG_STATUS)
- else IF_KW("ubmodule", 8, YANG_SUBMODULE)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("tatus", 5, YANG_STATUS)
+ else IF_KEYWORD("ubmodule", 8, YANG_SUBMODULE)
break;
case 't':
- MOVE_INPUT(data, 1);
- IF_KW("ypedef", 6, YANG_TYPEDEF)
- else IF_KW("ype", 3, YANG_TYPE)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("ypedef", 6, YANG_TYPEDEF)
+ else IF_KEYWORD("ype", 3, YANG_TYPE)
break;
case 'u':
- MOVE_INPUT(data, 1);
- IF_KW_PREFIX("ni", 2)
- IF_KW("que", 3, YANG_UNIQUE)
- else IF_KW("ts", 2, YANG_UNITS)
- IF_KW_PREFIX_END
- else IF_KW("ses", 3, YANG_USES)
+ MOVE_IN(data, 1);
+ IF_KEYWORD_PREFIX("ni", 2)
+ IF_KEYWORD("que", 3, YANG_UNIQUE)
+ else IF_KEYWORD("ts", 2, YANG_UNITS)
+ IF_KEYWORD_PREFIX_END
+ else IF_KEYWORD("ses", 3, YANG_USES)
break;
case 'v':
- MOVE_INPUT(data, 1);
- IF_KW("alue", 4, YANG_VALUE)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("alue", 4, YANG_VALUE)
break;
case 'w':
- MOVE_INPUT(data, 1);
- IF_KW("hen", 3, YANG_WHEN)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("hen", 3, YANG_WHEN)
break;
case 'y':
- MOVE_INPUT(data, 1);
- IF_KW("ang-version", 11, YANG_YANG_VERSION)
- else IF_KW("in-element", 10, YANG_YIN_ELEMENT)
+ MOVE_IN(data, 1);
+ IF_KEYWORD("ang-version", 11, YANG_YANG_VERSION)
+ else IF_KEYWORD("in-element", 10, YANG_YIN_ELEMENT)
break;
case ';':
- MOVE_INPUT(data, 1);
+ MOVE_IN(data, 1);
kw = YANG_SEMICOLON;
//goto success;
break;
case '{':
- MOVE_INPUT(data, 1);
+ MOVE_IN(data, 1);
kw = YANG_LEFT_BRACE;
//goto success;
break;
case '}':
- MOVE_INPUT(data, 1);
+ MOVE_IN(data, 1);
kw = YANG_RIGHT_BRACE;
//goto success;
break;
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index da8789c..26159b1 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -15,6 +15,13 @@
#ifndef LY_TREE_SCHEMA_INTERNAL_H_
#define LY_TREE_SCHEMA_INTERNAL_H_
+#define LOGVAL_YANG(CTX, ...) LOGVAL((CTX)->ctx, LY_VLOG_LINE, &(CTX)->line, __VA_ARGS__)
+
+/* These 2 macros checks YANG's identifier grammar rule */
+#define is_yangidentstartchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
+#define is_yangidentchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || \
+ c == '_' || c == '-' || c == '.')
+
/**
* @brief List of YANG statement groups - the (sub)module's substatements
*/
@@ -31,7 +38,7 @@
*/
enum yang_arg {
Y_IDENTIF_ARG, /**< YANG "identifier-arg-str" rule */
- Y_PREF_IDENTIF_ARG, /**< YANG "identifier-ref-arg-str" rule */
+ Y_PREF_IDENTIF_ARG, /**< YANG "identifier-ref-arg-str" or node-identifier rule */
Y_STR_ARG, /**< YANG "string" rule */
Y_MAYBE_STR_ARG /**< optional YANG "string" rule */
};
@@ -41,14 +48,34 @@
*/
struct ly_parser_ctx {
struct ly_ctx *ctx;
- uint64_t line; /* line number */
- uint64_t indent; /* current position on the line for YANG indentation */
+ struct lysp_module *mod;
+ struct ly_set tpdfs_nodes;
+ struct ly_set grps_nodes;
+ uint64_t line; /**< line number */
+ uint64_t indent; /**< current position on the line for YANG indentation */
+};
+
+/**
+ * @brief internal context for compilation
+ */
+struct lysc_ctx {
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lys_module *mod_def; /* context module for the definitions of the nodes being currently
+ processed - groupings are supposed to be evaluated in place where
+ defined, but its content instances are supposed to be placed into
+ the target module (mod) */
+ struct ly_set groupings; /* stack for groupings circular check */
+ struct ly_set unres; /* to validate leafref's target and xpath of when/must */
+ uint16_t path_len;
+#define LYSC_CTX_BUFSIZE 4078
+ char path[LYSC_CTX_BUFSIZE];
};
/**
* @brief Check the currently present prefixes in the module for collision with the new one.
*
- * @param[in] ctx yang parser context.
+ * @param[in] ctx Context for logging.
* @param[in] module Schema tree to check.
* @param[in] value Newly added prefix value (including its location to distinguish collision with itself).
* @return LY_EEXIST when prefix is already used in the module, LY_SUCCESS otherwise
@@ -58,13 +85,21 @@
/**
* @brief Check date string (4DIGIT "-" 2DIGIT "-" 2DIGIT)
*
- * @param[in] ctx Context to store log message.
+ * @param[in] ctx Optional context for logging.
* @param[in] date Date string to check (non-necessarily terminated by \0)
* @param[in] date_len Length of the date string, 10 expected.
* @param[in] stmt Statement name for error message.
* @return LY_ERR value.
*/
-LY_ERR lysp_check_date(struct ly_ctx *ctx, const char *date, int date_len, const char *stmt);
+LY_ERR lysp_check_date(struct ly_parser_ctx *ctx, const char *date, int date_len, const char *stmt);
+
+/**
+ * @brief Check names of typedefs in the parsed module to detect collisions.
+ *
+ * @param[in] ctx Parser context, module where the type is being defined is taken from here.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_typedefs(struct ly_parser_ctx *ctx);
/**
* @brief Just move the newest revision into the first position, does not sort the rest
@@ -73,6 +108,160 @@
void lysp_sort_revisions(struct lysp_revision *revs);
/**
+ * @brief Find type specified type definition
+ *
+ * @param[in] id Name of the type including possible prefix. Module where the prefix is being searched is start_module.
+ * @param[in] start_node Context node where the type is being instantiated to be able to search typedefs in parents.
+ * @param[in] start_module Module where the type is being instantiated for search for typedefs.
+ * @param[out] type Built-in type identifier of the id. If #LY_TYPE_UNKNOWN, tpdf is expected to contain found YANG schema typedef statement.
+ * @param[out] tpdf Found type definition.
+ * @param[out] node Node where the found typedef is defined, NULL in case of a top-level typedef.
+ * @param[out] module Module where the found typedef is being defined, NULL in case of built-in YANG types.
+ */
+LY_ERR lysp_type_find(const char *id, struct lysp_node *start_node, struct lysp_module *start_module,
+ LY_DATA_TYPE *type, const struct lysp_tpdf **tpdf, struct lysp_node **node, struct lysp_module **module);
+
+/**
+ * @brief Find and parse module of the given name.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] name Name of the module to load.
+ * @param[in] revison Optional revision of the module to load. If NULL, the newest revision is loaded.
+ * @param[in] implement Flag if the loaded module is supposed to be marked as implemented.
+ * @param[in] require_parsed Flag to require parsed module structure in case the module is already in the context,
+ * but only the compiled structure is available.
+ * @param[out] mod Parsed module structure.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, int implement, int require_parsed, struct lys_module **mod);
+
+/**
+ * @brief Parse included submodule into the simply parsed YANG module.
+ *
+ * @param[in] ctx parser context
+ * @param[in] mod Module including a submodule.
+ * @param[in,out] inc Include structure holding all available information about the include statement, the parsed
+ * submodule is stored into this structure.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_load_submodule(struct ly_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc);
+
+/**
+ * @brief Get address of a node's actions list if any.
+ *
+ * Decides the node's type and in case it has an actions list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's actions member if any, NULL otherwise.
+ */
+struct lysp_action **lysp_node_actions_p(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's notifications list if any.
+ *
+ * Decides the node's type and in case it has a notifications list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's notifs member if any, NULL otherwise.
+ */
+struct lysp_notif **lysp_node_notifs_p(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's child pointer if any.
+ *
+ * Decides the node's type and in case it has a children list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's child member if any, NULL otherwise.
+ */
+struct lysp_node **lysp_node_children_p(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's child pointer if any.
+ *
+ * Decides the node's type and in case it has a children list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's child member if any, NULL otherwise.
+ */
+struct lysc_node **lysc_node_children_p(const struct lysc_node *node);
+
+/**
+ * @brief Get the covering schema module structure for the given parsed module structure.
+ * @param[in] ctx libyang context to search.
+ * @param[in] mod Parsed schema structure.
+ * @return Corresponding lys_module structure for the given parsed schema structure.
+ */
+struct lys_module *lysp_find_module(struct ly_ctx *ctx, const struct lysp_module *mod);
+
+/**
+ * @brief Find the module referenced by prefix in the provided parsed mod.
+ *
+ * @param[in] mod Schema module where the prefix was used.
+ * @param[in] prefix Prefix used to reference a module.
+ * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
+ * @return Pointer to the module or NULL if the module is not found.
+ */
+struct lysp_module *lysp_module_find_prefix(const struct lysp_module *mod, const char *prefix, size_t len);
+
+/**
+ * @brief Find the module referenced by prefix in the provided compiled mod.
+ *
+ * @param[in] mod Schema module where the prefix was used.
+ * @param[in] prefix Prefix used to reference a module.
+ * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
+ * @return Pointer to the module or NULL if the module is not found.
+ */
+struct lysc_module *lysc_module_find_prefix(const struct lysc_module *mod, const char *prefix, size_t len);
+
+/**
+ * @brief Check statement's status for invalid combination.
+ *
+ * The modX parameters are used just to determine if both flags are in the same module,
+ * so any of the schema module structure can be used, but both modules must be provided
+ * in the same type.
+ *
+ * @param[in] ctx Compile context for logging.
+ * @param[in] flags1 Flags of the referencing node.
+ * @param[in] mod1 Module of the referencing node,
+ * @param[in] name1 Schema node name of the referencing node.
+ * @param[in] flags2 Flags of the referenced node.
+ * @param[in] mod2 Module of the referenced node,
+ * @param[in] name2 Schema node name of the referenced node.
+ * @return LY_ERR value
+ */
+LY_ERR lysc_check_status(struct lysc_ctx *ctx,
+ uint16_t flags1, void *mod1, const char *name1,
+ uint16_t flags2, void *mod2, const char *name2);
+
+/**
+ * @brief Parse a node-identifier.
+ *
+ * node-identifier = [prefix ":"] identifier
+ *
+ * @param[in, out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @param[out] prefix Node's prefix, NULL if there is not any.
+ * @param[out] prefix_len Length of the node's prefix, 0 if there is not any.
+ * @param[out] name Node's name.
+ * @param[out] nam_len Length of the node's name.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid character in the id.
+ */
+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
+ *
+ * @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.
+ * @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.
+ * @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);
+
+/**
* @brief Find the module referenced by prefix in the provided mod.
*
* @param[in] mod Schema module where the prefix was used.
@@ -80,7 +269,125 @@
* @param[in] len Length of the prefix since it is not necessary NULL-terminated.
* @return Pointer to the module or NULL if the module is not found.
*/
-struct lysc_module *lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len);
+struct lys_module *lys_module_find_prefix(const struct lys_module *mod, const char *prefix, size_t len);
+
+/**
+ * @brief Stringify schema nodetype.
+ * @param[in] nodetype Nodetype to stringify.
+ * @return Constant string with the name of the node's type.
+ */
+const char *lys_nodetype2str(uint16_t nodetype);
+
+/**
+ * @brief Parse YANG module and submodule from a string.
+ *
+ * In contrast to public lys_parse_mem(), also submodules can be parsed here. However,
+ * while the modules are added into the context, submodules not. The latest_revision
+ * flag is updated in both cases.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] data The string containing the dumped data model in the specified
+ * format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @param[in] implement Flag if the schema is supposed to be marked as implemented.
+ * @param[in] main_ctx Parser context of the main module in case of parsing submodule.
+ * @param[in] custom_check Callback to check the parsed schema before it is accepted.
+ * @param[in] check_data Caller's data to pass to the custom_check callback.
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_mem_(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+ LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data);
+
+/**
+ * @brief Parse YANG module and submodule from a file descriptor.
+ *
+ * In contrast to public lys_parse_mem(), also submodules can be parsed here. However,
+ * while the modules are added into the context, submodules not. The latest_revision
+ * flag is updated in both cases.
+ *
+ * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
+ * in the specified format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @param[in] implement Flag if the schema is supposed to be marked as implemented.
+ * @param[in] main_ctx Parser context of the main module in case of parsing submodule.
+ * @param[in] custom_check Callback to check the parsed schema before it is accepted.
+ * @param[in] check_data Caller's data to pass to the custom_check callback.
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+ LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data);
+
+/**
+ * @brief Parse YANG module and submodule from a file descriptor.
+ *
+ * In contrast to public lys_parse_mem(), also submodules can be parsed here. However,
+ * while the modules are added into the context, submodules not. The latest_revision
+ * flag is updated in both cases.
+ *
+ * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
+ *
+ * @brief REad a schema into the specified context from a file.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] path Path to the file with the model in the specified format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @param[in] implement Flag if the schema is supposed to be marked as implemented.
+ * @param[in] main_ctx Parser context of the main module in case of parsing submodule.
+ * @param[in] custom_check Callback to check the parsed schema before it is accepted.
+ * @param[in] check_data Caller's data to pass to the custom_check callback.
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_path_(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+ LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data);
+
+/**
+ * @brief Load the (sub)module into the context.
+ *
+ * This function does not check the presence of the (sub)module in context, it should be done before calling this function.
+ *
+ * module_name and submodule_name are alternatives - only one of the
+ *
+ * @param[in] ctx libyang context where to work.
+ * @param[in] name Name of the (sub)module to load.
+ * @param[in] revision Optional revision of the (sub)module to load, if NULL the newest revision is being loaded.
+ * @param[in] implement Flag if the (sub)module is supposed to be marked as implemented.
+ * @param[in] main_ctx Parser context of the main module in case of loading submodule.
+ * @param[out] result Parsed YANG schema tree of the requested module. If it is a module, it is already in the context!
+ * @return LY_ERR value, in case of LY_SUCCESS, the \arg result is always provided.
+ */
+LY_ERR lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement, struct ly_parser_ctx *main_ctx,
+ struct lys_module **result);
+
+/**
+ * @brief Make the module implemented.
+ * Does not check for collision in context, it must be done before calling the function, this is a simple switch.
+ * @param[in] mod Module to make implemented.
+ */
+void lys_module_implement(struct lys_module *mod);
+
+/**
+ * @brief Free the compiled type structure.
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] type Compiled type structure to be freed. The structure has refcount, so it is freed only in case the value is decreased to 0.
+ */
+void lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type);
+
+/**
+ * @brief Free the compiled node structure.
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] node Compiled node structure to be freed.
+ */
+void lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node);
+
+/**
+ * @brief Free the compiled schema structure.
+ * @param[in,out] module Compiled schema module structure to free.
+ * @param[in] private_destructor Function to remove private data from the compiled schema tree.
+ */
+void lysc_module_free(struct lysc_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv));
/**
* @brief Free the schema structure. It just frees, it does not remove the schema from its context.
@@ -92,6 +399,10 @@
/**
* @brief
*/
-LY_ERR yang_parse(struct ly_ctx *ctx, const char *data, struct lysp_module **mod_p);
+LY_ERR yang_parse(struct ly_parser_ctx *ctx, const char *data, struct lysp_module **mod_p);
+/**
+ * @brief match yang keyword
+ */
+enum yang_keyword match_keyword(const char *data);
#endif /* LY_TREE_SCHEMA_INTERNAL_H_ */
diff --git a/src/xml.c b/src/xml.c
index db25d70..be943cd 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -12,7 +12,7 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _POSIX_C_SOURCE 200809L /* strndup() */
+#include "common.h"
#include <assert.h>
#include <ctype.h>
@@ -22,31 +22,6 @@
#include "libyang.h"
#include "xml.h"
-#include "common.h"
-
-/* Macro to test if character is whitespace */
-#define is_xmlws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
-
-/* Macro to test if character is allowed to be a first character of an qualified identifier */
-#define is_xmlqnamestartchar(c) ((c >= 'a' && c <= 'z') || c == '_' || \
- (c >= 'A' && c <= 'Z') || /* c == ':' || */ \
- (c >= 0x370 && c <= 0x1fff && c != 0x37e ) || \
- (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
- c == 0x200d || (c >= 0x2070 && c <= 0x218f) || \
- (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
- (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
- (c >= 0x10000 && c <= 0xeffff))
-
-/* Macro to test if character is allowed to be used in an qualified identifier */
-#define is_xmlqnamechar(c) ((c >= 'a' && c <= 'z') || c == '_' || c == '-' || \
- (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || /* c == ':' || */ \
- c == '.' || c == 0xb7 || (c >= 0x370 && c <= 0x1fff && c != 0x37e ) ||\
- (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
- c == 0x200d || (c >= 0x300 && c <= 0x36f) || \
- (c >= 0x2070 && c <= 0x218f) || (c >= 0x2030f && c <= 0x2040) || \
- (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
- (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
- (c >= 0x10000 && c <= 0xeffff))
/* Move input p by s characters, if EOF log with lyxml_context c */
#define move_input(c,p,s) p += s; LY_CHECK_ERR_RET(!p[0], LOGVAL(c->ctx, LY_VLOG_LINE, &c->line, LY_VCODE_EOF), LY_EVALID)
diff --git a/src/xml.h b/src/xml.h
index f42b27e..5172c48 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -20,6 +20,30 @@
#include "context.h"
#include "set.h"
+/* Macro to test if character is whitespace */
+#define is_xmlws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
+
+/* Macro to test if character is allowed to be a first character of an qualified identifier */
+#define is_xmlqnamestartchar(c) ((c >= 'a' && c <= 'z') || c == '_' || \
+ (c >= 'A' && c <= 'Z') || /* c == ':' || */ \
+ (c >= 0x370 && c <= 0x1fff && c != 0x37e ) || \
+ (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
+ c == 0x200d || (c >= 0x2070 && c <= 0x218f) || \
+ (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
+ (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
+ (c >= 0x10000 && c <= 0xeffff))
+
+/* Macro to test if character is allowed to be used in an qualified identifier */
+#define is_xmlqnamechar(c) ((c >= 'a' && c <= 'z') || c == '_' || c == '-' || \
+ (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || /* c == ':' || */ \
+ c == '.' || c == 0xb7 || (c >= 0x370 && c <= 0x1fff && c != 0x37e ) ||\
+ (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
+ c == 0x200d || (c >= 0x300 && c <= 0x36f) || \
+ (c >= 0x2070 && c <= 0x218f) || (c >= 0x2030f && c <= 0x2040) || \
+ (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
+ (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
+ (c >= 0x10000 && c <= 0xeffff))
+
struct lyxml_ns {
const char *element; /* element where the namespace is defined */
char *prefix; /* prefix of the namespace, NULL for the default namespace */
diff --git a/src/xpath.c b/src/xpath.c
new file mode 100644
index 0000000..4a3792d
--- /dev/null
+++ b/src/xpath.c
@@ -0,0 +1,362 @@
+/**
+ * @file xpath.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YANG XPath evaluation functions
+ *
+ * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "common.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include <errno.h>
+#include <math.h>
+#include <pcre.h>
+
+#include "xpath.h"
+#include "xml.h"
+
+/**
+ * @brief Parse NCName.
+ *
+ * @param[in] ncname Name to parse.
+ *
+ * @return Length of \p ncname valid bytes.
+ */
+static size_t
+parse_ncname(const char *ncname)
+{
+ unsigned int uc;
+ size_t size, len = 0;
+
+ LY_CHECK_RET(ly_getutf8(&ncname, &uc, &size), 0);
+ if (!is_xmlqnamestartchar(uc) || (uc == ':')) {
+ return len;
+ }
+
+ do {
+ len += size;
+ LY_CHECK_RET(ly_getutf8(&ncname, &uc, &size), 0);
+ } while (is_xmlqnamechar(uc) && (uc != ':'));
+
+ return len;
+}
+
+/**
+ * @brief Add \p token into the expression \p exp.
+ *
+ * @param[in] ctx libyang context to log in.
+ * @param[in] exp Expression to use.
+ * @param[in] token Token to add.
+ * @param[in] expr_pos Token position in the XPath expression.
+ * @param[in] tok_len Token length in the XPath expression.
+ * @return LY_ERR value
+ */
+static LY_ERR
+exp_add_token(struct ly_ctx *ctx, struct lyxp_expr *exp, enum lyxp_token token, uint16_t expr_pos, uint16_t tok_len)
+{
+ uint32_t prev;
+
+ if (exp->used == exp->size) {
+ prev = exp->size;
+ exp->size += LYXP_EXPR_SIZE_STEP;
+ if (prev > exp->size) {
+ LOGINT(ctx);
+ return LY_EINT;
+ }
+
+ exp->tokens = ly_realloc(exp->tokens, exp->size * sizeof *exp->tokens);
+ LY_CHECK_ERR_RET(!exp->tokens, LOGMEM(ctx), LY_EMEM);
+ exp->tok_pos = ly_realloc(exp->tok_pos, exp->size * sizeof *exp->tok_pos);
+ LY_CHECK_ERR_RET(!exp->tok_pos, LOGMEM(ctx), LY_EMEM);
+ exp->tok_len = ly_realloc(exp->tok_len, exp->size * sizeof *exp->tok_len);
+ LY_CHECK_ERR_RET(!exp->tok_len, LOGMEM(ctx), LY_EMEM);
+ }
+
+ exp->tokens[exp->used] = token;
+ exp->tok_pos[exp->used] = expr_pos;
+ exp->tok_len[exp->used] = tok_len;
+ ++exp->used;
+ return LY_SUCCESS;
+}
+
+void
+lyxp_expr_free(struct ly_ctx *ctx, struct lyxp_expr *expr)
+{
+ uint16_t i;
+
+ if (!expr) {
+ return;
+ }
+
+ lydict_remove(ctx, expr->expr);
+ free(expr->tokens);
+ free(expr->tok_pos);
+ free(expr->tok_len);
+ if (expr->repeat) {
+ for (i = 0; i < expr->used; ++i) {
+ free(expr->repeat[i]);
+ }
+ }
+ free(expr->repeat);
+ free(expr);
+}
+
+struct lyxp_expr *
+lyxp_expr_parse(struct ly_ctx *ctx, const char *expr)
+{
+ struct lyxp_expr *ret;
+ size_t parsed = 0, tok_len, ncname_len;
+ enum lyxp_token tok_type;
+ int prev_function_check = 0;
+
+ if (strlen(expr) > UINT16_MAX) {
+ LOGERR(ctx, LY_EINVAL, "XPath expression cannot be longer than %ud characters.", UINT16_MAX);
+ return NULL;
+ }
+
+ /* init lyxp_expr structure */
+ ret = calloc(1, sizeof *ret);
+ LY_CHECK_ERR_GOTO(!ret, LOGMEM(ctx), error);
+ ret->expr = lydict_insert(ctx, expr, strlen(expr));
+ LY_CHECK_ERR_GOTO(!ret->expr, LOGMEM(ctx), error);
+ ret->used = 0;
+ ret->size = LYXP_EXPR_SIZE_START;
+ ret->tokens = malloc(ret->size * sizeof *ret->tokens);
+ LY_CHECK_ERR_GOTO(!ret->tokens, LOGMEM(ctx), error);
+
+ ret->tok_pos = malloc(ret->size * sizeof *ret->tok_pos);
+ LY_CHECK_ERR_GOTO(!ret->tok_pos, LOGMEM(ctx), error);
+
+ ret->tok_len = malloc(ret->size * sizeof *ret->tok_len);
+ LY_CHECK_ERR_GOTO(!ret->tok_len, LOGMEM(ctx), error);
+
+ while (is_xmlws(expr[parsed])) {
+ ++parsed;
+ }
+
+ do {
+ if (expr[parsed] == '(') {
+
+ /* '(' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_PAR1;
+
+ if (prev_function_check && ret->used && (ret->tokens[ret->used - 1] == LYXP_TOKEN_NAMETEST)) {
+ /* it is a NodeType/FunctionName after all */
+ if (((ret->tok_len[ret->used - 1] == 4)
+ && (!strncmp(&expr[ret->tok_pos[ret->used - 1]], "node", 4)
+ || !strncmp(&expr[ret->tok_pos[ret->used - 1]], "text", 4))) ||
+ ((ret->tok_len[ret->used - 1] == 7)
+ && !strncmp(&expr[ret->tok_pos[ret->used - 1]], "comment", 7))) {
+ ret->tokens[ret->used - 1] = LYXP_TOKEN_NODETYPE;
+ } else {
+ ret->tokens[ret->used - 1] = LYXP_TOKEN_FUNCNAME;
+ }
+ prev_function_check = 0;
+ }
+
+ } else if (expr[parsed] == ')') {
+
+ /* ')' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_PAR2;
+
+ } else if (expr[parsed] == '[') {
+
+ /* '[' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_BRACK1;
+
+ } else if (expr[parsed] == ']') {
+
+ /* ']' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_BRACK2;
+
+ } else if (!strncmp(&expr[parsed], "..", 2)) {
+
+ /* '..' */
+ tok_len = 2;
+ tok_type = LYXP_TOKEN_DDOT;
+
+ } else if ((expr[parsed] == '.') && (!isdigit(expr[parsed + 1]))) {
+
+ /* '.' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_DOT;
+
+ } else if (expr[parsed] == '@') {
+
+ /* '@' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_AT;
+
+ } else if (expr[parsed] == ',') {
+
+ /* ',' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_COMMA;
+
+ } else if (expr[parsed] == '\'') {
+
+ /* Literal with ' */
+ for (tok_len = 1; (expr[parsed + tok_len] != '\0') && (expr[parsed + tok_len] != '\''); ++tok_len);
+ LY_CHECK_ERR_GOTO(expr[parsed + tok_len] == '\0',
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOE, expr[parsed], &expr[parsed]), error);
+ ++tok_len;
+ tok_type = LYXP_TOKEN_LITERAL;
+
+ } else if (expr[parsed] == '\"') {
+
+ /* Literal with " */
+ for (tok_len = 1; (expr[parsed + tok_len] != '\0') && (expr[parsed + tok_len] != '\"'); ++tok_len);
+ LY_CHECK_ERR_GOTO(expr[parsed + tok_len] == '\0',
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOE, expr[parsed], &expr[parsed]), error);
+ ++tok_len;
+ tok_type = LYXP_TOKEN_LITERAL;
+
+ } else if ((expr[parsed] == '.') || (isdigit(expr[parsed]))) {
+
+ /* Number */
+ for (tok_len = 0; isdigit(expr[parsed + tok_len]); ++tok_len);
+ if (expr[parsed + tok_len] == '.') {
+ ++tok_len;
+ for (; isdigit(expr[parsed + tok_len]); ++tok_len);
+ }
+ tok_type = LYXP_TOKEN_NUMBER;
+
+ } else if (expr[parsed] == '/') {
+
+ /* Operator '/', '//' */
+ if (!strncmp(&expr[parsed], "//", 2)) {
+ tok_len = 2;
+ } else {
+ tok_len = 1;
+ }
+ tok_type = LYXP_TOKEN_OPERATOR_PATH;
+
+ } else if (!strncmp(&expr[parsed], "!=", 2) || !strncmp(&expr[parsed], "<=", 2)
+ || !strncmp(&expr[parsed], ">=", 2)) {
+
+ /* Operator '!=', '<=', '>=' */
+ tok_len = 2;
+ tok_type = LYXP_TOKEN_OPERATOR_COMP;
+
+ } else if (expr[parsed] == '|') {
+
+ /* Operator '|' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_OPERATOR_UNI;
+
+ } else if ((expr[parsed] == '+') || (expr[parsed] == '-')) {
+
+ /* Operator '+', '-' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_OPERATOR_MATH;
+
+ } else if ((expr[parsed] == '=') || (expr[parsed] == '<') || (expr[parsed] == '>')) {
+
+ /* Operator '=', '<', '>' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_OPERATOR_COMP;
+
+ } else if (ret->used && (ret->tokens[ret->used - 1] != LYXP_TOKEN_AT)
+ && (ret->tokens[ret->used - 1] != LYXP_TOKEN_PAR1)
+ && (ret->tokens[ret->used - 1] != LYXP_TOKEN_BRACK1)
+ && (ret->tokens[ret->used - 1] != LYXP_TOKEN_COMMA)
+ && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPERATOR_LOG)
+ && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPERATOR_COMP)
+ && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPERATOR_MATH)
+ && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPERATOR_UNI)
+ && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPERATOR_PATH)) {
+
+ /* Operator '*', 'or', 'and', 'mod', or 'div' */
+ if (expr[parsed] == '*') {
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_OPERATOR_MATH;
+
+ } else if (!strncmp(&expr[parsed], "or", 2)) {
+ tok_len = 2;
+ tok_type = LYXP_TOKEN_OPERATOR_LOG;
+
+ } else if (!strncmp(&expr[parsed], "and", 3)) {
+ tok_len = 3;
+ tok_type = LYXP_TOKEN_OPERATOR_LOG;
+
+ } else if (!strncmp(&expr[parsed], "mod", 3) || !strncmp(&expr[parsed], "div", 3)) {
+ tok_len = 3;
+ tok_type = LYXP_TOKEN_OPERATOR_MATH;
+
+ } else if (prev_function_check) {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Invalid character 0x%x, perhaps \"%.*s\" is supposed to be a function call.",
+ expr[parsed], &expr[parsed], ret->tok_len[ret->used - 1], &ret->expr[ret->tok_pos[ret->used - 1]]);
+ goto error;
+ } else {
+ LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, expr[parsed], &expr[parsed]);
+ goto error;
+ }
+ } else if (expr[parsed] == '*') {
+
+ /* NameTest '*' */
+ tok_len = 1;
+ tok_type = LYXP_TOKEN_NAMETEST;
+
+ } else {
+
+ /* NameTest (NCName ':' '*' | QName) or NodeType/FunctionName */
+ ncname_len = parse_ncname(&expr[parsed]);
+ LY_CHECK_ERR_GOTO(!ncname_len, LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, expr[parsed], &expr[parsed]), error);
+ tok_len = ncname_len;
+
+ if (expr[parsed + tok_len] == ':') {
+ ++tok_len;
+ if (expr[parsed + tok_len] == '*') {
+ ++tok_len;
+ } else {
+ ncname_len = parse_ncname(&expr[parsed + tok_len]);
+ LY_CHECK_ERR_GOTO(!ncname_len, LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, expr[parsed], &expr[parsed]), error);
+ tok_len += ncname_len;
+ }
+ /* remove old flag to prevent ambiguities */
+ prev_function_check = 0;
+ tok_type = LYXP_TOKEN_NAMETEST;
+ } else {
+ /* there is no prefix so it can still be NodeType/FunctionName, we can't finally decide now */
+ prev_function_check = 1;
+ tok_type = LYXP_TOKEN_NAMETEST;
+ }
+ }
+
+ /* store the token, move on to the next one */
+ LY_CHECK_GOTO(exp_add_token(ctx, ret, tok_type, parsed, tok_len), error);
+ parsed += tok_len;
+ while (is_xmlws(expr[parsed])) {
+ ++parsed;
+ }
+
+ } while (expr[parsed]);
+
+ /* prealloc repeat */
+ ret->repeat = calloc(ret->size, sizeof *ret->repeat);
+ LY_CHECK_ERR_GOTO(!ret->repeat, LOGMEM(ctx), error);
+
+ return ret;
+
+error:
+ lyxp_expr_free(ctx, ret);
+ return NULL;
+}
+
diff --git a/src/xpath.h b/src/xpath.h
new file mode 100644
index 0000000..df3a4f9
--- /dev/null
+++ b/src/xpath.h
@@ -0,0 +1,359 @@
+/**
+ * @file xpath.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YANG XPath evaluation functions header
+ *
+ * Copyright (c) 2015 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef _XPATH_H
+#define _XPATH_H
+
+#include <stdint.h>
+
+#include "libyang.h"
+#include "tree_schema.h"
+#if 0
+#include "tree_data.h"
+#endif
+/*
+ * XPath evaluator fully compliant with http://www.w3.org/TR/1999/REC-xpath-19991116/
+ * except the following restrictions in the grammar.
+ *
+ * PARSED GRAMMAR
+ *
+ * Full axes are not supported, abbreviated forms must be used,
+ * variables are not supported, "id()" function is not supported,
+ * and processing instruction and comment nodes are not supported,
+ * which is also reflected in the grammar. Undefined rules and
+ * constants are tokens.
+ *
+ * Modified full grammar:
+ *
+ * [1] Expr ::= OrExpr // just an alias
+ *
+ * [2] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
+ * [3] AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
+ * [4] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
+ * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
+ * [6] NodeTest ::= NameTest | NodeType '(' ')'
+ * [7] Predicate ::= '[' Expr ']'
+ * [8] PrimaryExpr ::= '(' Expr ')' | Literal | Number | FunctionCall
+ * [9] FunctionCall ::= FunctionName '(' ( Expr ( ',' Expr )* )? ')'
+ * [10] PathExpr ::= LocationPath | PrimaryExpr Predicate*
+ * | PrimaryExpr Predicate* '/' RelativeLocationPath
+ * | PrimaryExpr Predicate* '//' RelativeLocationPath
+ * [11] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
+ * [12] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
+ * [13] EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr
+ * | EqualityExpr '!=' RelationalExpr
+ * [14] RelationalExpr ::= AdditiveExpr
+ * | RelationalExpr '<' AdditiveExpr
+ * | RelationalExpr '>' AdditiveExpr
+ * | RelationalExpr '<=' AdditiveExpr
+ * | RelationalExpr '>=' AdditiveExpr
+ * [15] AdditiveExpr ::= MultiplicativeExpr
+ * | AdditiveExpr '+' MultiplicativeExpr
+ * | AdditiveExpr '-' MultiplicativeExpr
+ * [16] MultiplicativeExpr ::= UnaryExpr
+ * | MultiplicativeExpr '*' UnaryExpr
+ * | MultiplicativeExpr 'div' UnaryExpr
+ * | MultiplicativeExpr 'mod' UnaryExpr
+ * [17] UnaryExpr ::= UnionExpr | '-' UnaryExpr
+ * [18] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
+ */
+
+/* expression tokens allocation */
+#define LYXP_EXPR_SIZE_START 10
+#define LYXP_EXPR_SIZE_STEP 5
+
+/* XPath matches allocation */
+#define LYXP_SET_SIZE_START 2
+#define LYXP_SET_SIZE_STEP 2
+
+/* building string when casting */
+#define LYXP_STRING_CAST_SIZE_START 64
+#define LYXP_STRING_CAST_SIZE_STEP 16
+
+/**
+ * @brief Tokens that can be in an XPath expression.
+ */
+enum lyxp_token {
+ LYXP_TOKEN_NONE = 0,
+ LYXP_TOKEN_PAR1, /* '(' */
+ LYXP_TOKEN_PAR2, /* ')' */
+ LYXP_TOKEN_BRACK1, /* '[' */
+ LYXP_TOKEN_BRACK2, /* ']' */
+ LYXP_TOKEN_DOT, /* '.' */
+ LYXP_TOKEN_DDOT, /* '..' */
+ LYXP_TOKEN_AT, /* '@' */
+ LYXP_TOKEN_COMMA, /* ',' */
+ /* LYXP_TOKEN_DCOLON, * '::' * axes not supported */
+ LYXP_TOKEN_NAMETEST, /* NameTest */
+ LYXP_TOKEN_NODETYPE, /* NodeType */
+ LYXP_TOKEN_FUNCNAME, /* FunctionName */
+ LYXP_TOKEN_OPERATOR_LOG, /* Operator 'and', 'or' */
+ LYXP_TOKEN_OPERATOR_COMP, /* Operator '=', '!=', '<', '<=', '>', '>=' */
+ LYXP_TOKEN_OPERATOR_MATH, /* Operator '+', '-', '*', 'div', 'mod', '-' (unary) */
+ LYXP_TOKEN_OPERATOR_UNI, /* Operator '|' */
+ LYXP_TOKEN_OPERATOR_PATH, /* Operator '/', '//' */
+ /* LYXP_TOKEN_AXISNAME, * AxisName * axes not supported */
+ LYXP_TOKEN_LITERAL, /* Literal - with either single or double quote */
+ LYXP_TOKEN_NUMBER /* Number */
+};
+
+/**
+ * @brief XPath (sub)expressions that can be repeated.
+ */
+enum lyxp_expr_type {
+ LYXP_EXPR_NONE = 0,
+ LYXP_EXPR_OR,
+ LYXP_EXPR_AND,
+ LYXP_EXPR_EQUALITY,
+ LYXP_EXPR_RELATIONAL,
+ LYXP_EXPR_ADDITIVE,
+ LYXP_EXPR_MULTIPLICATIVE,
+ LYXP_EXPR_UNARY,
+ LYXP_EXPR_UNION,
+};
+
+/**
+ * @brief Structure holding a parsed XPath expression.
+ */
+struct lyxp_expr {
+ enum lyxp_token *tokens; /* array of tokens */
+ uint16_t *tok_pos; /* array of the token offsets in expr */
+ uint16_t *tok_len; /* array of token lengths in expr */
+ enum lyxp_expr_type **repeat; /* array of expression types that this token begins and is repeated ended with 0,
+ more in the comment after this declaration */
+ uint16_t used; /* used array items */
+ uint16_t size; /* allocated array items */
+
+ const char *expr; /* the original XPath expression */
+};
+
+/*
+ * lyxp_expr repeat
+ *
+ * This value is NULL for all the tokens that do not begin an
+ * expression which can be repeated. Otherwise it is an array
+ * of expression types that this token begins. These values
+ * are used during evaluation to know whether we need to
+ * duplicate the current context or not and to decide what
+ * the current expression is (for example, if we are only
+ * starting the parsing and the first token has no repeat,
+ * we do not parse it as an OrExpr but directly as PathExpr).
+ * Examples:
+ *
+ * Expression: "/ *[key1 and key2 or key1 < key2]"
+ * Tokens: '/', '*', '[', NameTest, 'and', NameTest, 'or', NameTest, '<', NameTest, ']'
+ * Repeat: NULL, NULL, NULL, [AndExpr, NULL, NULL, NULL, [RelationalExpr, NULL, NULL, NULL
+ * OrExpr, 0],
+ * 0],
+ *
+ * Expression: "//node[key and node2]/key | /cont"
+ * Tokens: '//', 'NameTest', '[', 'NameTest', 'and', 'NameTest', ']', '/', 'NameTest', '|', '/', 'NameTest'
+ * Repeat: [UnionExpr, NULL, NULL, [AndExpr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+ * 0], 0],
+ *
+ * Operators between expressions which this concerns:
+ * 'or', 'and', '=', '!=', '<', '>', '<=', '>=', '+', '-', '*', 'div', 'mod', '|'
+ */
+
+/**
+ * @brief Supported types of (partial) XPath results.
+ */
+enum lyxp_set_type {
+ LYXP_SET_EMPTY = 0,
+ LYXP_SET_NODE_SET,
+ LYXP_SET_SNODE_SET,
+ LYXP_SET_BOOLEAN,
+ LYXP_SET_NUMBER,
+ LYXP_SET_STRING
+};
+#if 0
+#ifdef LY_ENABLED_CACHE
+
+/**
+ * @brief Item stored in an XPath set hash table.
+ */
+struct lyxp_set_hash_node {
+ struct lyd_node *node;
+ enum lyxp_node_type type;
+} _PACKED;
+
+#endif
+
+/**
+ * @brief XPath set - (partial) result.
+ */
+struct lyxp_set {
+ enum lyxp_set_type type;
+ union {
+ struct lyxp_set_node {
+ struct lyd_node *node;
+ enum lyxp_node_type type;
+ uint32_t pos;
+ } *nodes;
+ struct lyxp_set_snode {
+ struct lys_node *snode;
+ enum lyxp_node_type type;
+ /* 0 - snode was traversed, but not currently in the context,
+ * 1 - snode currently in context,
+ * 2 - snode in context and just added, so skip it for the current operation,
+ * >=3 - snode is not in context because we are in a predicate and this snode was used/will be used later */
+ uint32_t in_ctx;
+ } *snodes;
+ struct lyxp_set_attr {
+ struct lyd_attr *attr;
+ enum lyxp_node_type type;
+ uint32_t pos; /* if node_type is LYXP_SET_NODE_ATTR, it is the parent node position */
+ } *attrs;
+ char *str;
+ long double num;
+ int bool;
+ } val;
+
+ /* this is valid only for type LYXP_SET_NODE_SET and LYXP_SET_SNODE_SET */
+ uint32_t used;
+ uint32_t size;
+#ifdef LY_ENABLED_CACHE
+ struct hash_table *ht;
+#endif
+ /* this is valid only for type LYXP_SET_NODE_SET */
+ uint32_t ctx_pos;
+ uint32_t ctx_size;
+};
+
+/**
+ * @brief Evaluate the XPath expression \p expr on data. Be careful when using this function, the result can often
+ * be confusing without thorough understanding of XPath evaluation rules defined in RFC 6020.
+ *
+ * @param[in] expr XPath expression to evaluate. Must be in JSON format (prefixes are model names).
+ * @param[in] cur_node Current (context) data node. If the node has #LYD_VAL_INUSE flag, it is considered dummy (intended
+ * for but not restricted to evaluation with the LYXP_WHEN flag).
+ * @param[in] cur_node_type Current (context) data node type. For every standard case use #LYXP_NODE_ELEM. But there are
+ * cases when the context node \p cur_node is actually supposed to be the XML root, there is no such data node. So, in
+ * this case just pass the first top-level node into \p cur_node and use an enum value for this kind of root
+ * (#LYXP_NODE_ROOT_CONFIG if \p cur_node has config true, otherwise #LYXP_NODE_ROOT). #LYXP_NODE_TEXT and #LYXP_NODE_ATTR can also be used,
+ * but there are no use-cases in YANG.
+ * @param[in] local_mod Local module relative to the \p expr. Used only to determine the internal canonical value for identities.
+ * @param[out] set Result set. Must be valid and in the same libyang context as \p cur_node.
+ * To be safe, always either zero or cast the \p set to empty. After done using, either cast
+ * the \p set to empty (if allocated statically) or free it (if allocated dynamically) to
+ * prevent memory leaks.
+ * @param[in] options Whether to apply some evaluation restrictions.
+ * LYXP_MUST - apply must data tree access restrictions.
+ * LYXP_WHEN - apply when data tree access restrictions and consider LYD_WHEN flags in data nodes.
+ *
+ * @return EXIT_SUCCESS on success, EXIT_FAILURE on unresolved when dependency, -1 on error.
+ */
+int lyxp_eval(const char *expr, const struct lyd_node *cur_node, enum lyxp_node_type cur_node_type,
+ const struct lys_module *local_mod, struct lyxp_set *set, int options);
+
+/**
+ * @brief Get all the partial XPath nodes (atoms) that are required for \p expr to be evaluated.
+ *
+ * If any LYXP_SNODE* options is set, only fatal errors are printed, otherwise they are downgraded
+ * to warnings.
+ *
+ * @param[in] expr XPath expression to be evaluated. Must be in JSON format (prefixes are model names).
+ * @param[in] cur_snode Current (context) schema node.
+ * @param[in] cur_snode_type Current (context) schema node type.
+ * @param[out] set Result set. Must be valid and in the same libyang context as \p cur_snode.
+ * To be safe, always either zero or cast the \p set to empty. After done using, either cast
+ * the \p set to empty (if allocated statically) or free it (if allocated dynamically) to
+ * prevent memory leaks.
+ * @param[in] options Whether to apply some evaluation restrictions, one flag must always be used.
+ * LYXP_SNODE - no special data tree access modifiers.
+ * LYXP_SNODE_MUST - apply must data tree access restrictions.
+ * LYXP_SNODE_WHEN - apply when data tree access restrictions.
+ * LYXP_SNODE_OUTPUT - search RPC/action output instead input
+ * @param[out] ctx_snode Actual context node for the expression (it often changes for "when" expressions).
+ *
+ * @return EXIT_SUCCESS on success, -1 on error.
+ */
+int lyxp_atomize(const char *expr, const struct lys_node *cur_snode, enum lyxp_node_type cur_snode_type,
+ struct lyxp_set *set, int options, const struct lys_node **ctx_snode);
+
+/* these are used only internally */
+#define LYXP_SNODE 0x04
+#define LYXP_SNODE_MUST 0x08
+#define LYXP_SNODE_WHEN 0x10
+#define LYXP_SNODE_OUTPUT 0x20
+
+#define LYXP_SNODE_ALL 0x1C
+
+/**
+ * @brief Works like lyxp_atomize(), but it is executed on all the when and must expressions
+ * which the node has.
+ *
+ * @param[in] node Node to examine.
+ * @param[in,out] set Resulting set of atoms merged from all the expressions.
+ * Will be cleared before use.
+ * @param[in] set_ext_dep_flags Whether to set #LYS_XPCONF_DEP or #LYS_XPSTATE_DEP for conditions that
+ * require foreign configuration or state subtree and also for the node itself, if it has any such condition.
+ *
+ * @return EXIT_SUCCESS on success, -1 on error.
+ */
+int lyxp_node_atomize(const struct lys_node *node, struct lyxp_set *set, int set_ext_dep_flags);
+#endif
+/**
+ * @brief Check syntax of all the XPath expressions of the node.
+ *
+ * @param[in] node Node to examine.
+ *
+ * @return LY_ERR value.
+ */
+LY_ERR lyxp_node_check_syntax(const struct lysc_node *node);
+#if 0
+/**
+ * @brief Cast XPath set to another type.
+ * Indirectly context position aware.
+ *
+ * @param[in] set Set to cast.
+ * @param[in] target Target type to cast \p set into.
+ * @param[in] cur_node Current (context) data node. Cannot be NULL.
+ * @param[in] local_mod Local expression module.
+ * @param[in] options Whether to apply some evaluation restrictions.
+ *
+ * @return EXIT_SUCCESS on success, -1 on error.
+ */
+int lyxp_set_cast(struct lyxp_set *set, enum lyxp_set_type target, const struct lyd_node *cur_node,
+ const struct lys_module *local_mod, int options);
+
+/**
+ * @brief Free contents of an XPath \p set.
+ *
+ * @param[in] set Set to free.
+ */
+void lyxp_set_free(struct lyxp_set *set);
+#endif
+/**
+ * @brief Parse an XPath expression into a structure of tokens.
+ * Logs directly.
+ *
+ * http://www.w3.org/TR/1999/REC-xpath-19991116/ section 3.7
+ *
+ * @param[in] ctx Context for errors.
+ * @param[in] expr XPath expression to parse. It is duplicated.
+ *
+ * @return Filled expression structure or NULL on error.
+ */
+struct lyxp_expr *lyxp_expr_parse(struct ly_ctx *ctx, const char *expr);
+
+/**
+ * @brief Frees a parsed XPath expression. \p expr should not be used afterwards.
+ *
+ * @param[in] ctx libyang context of the expression.
+ * @param[in] expr Expression to free.
+ */
+void lyxp_expr_free(struct ly_ctx *ctx, struct lyxp_expr *expr);
+
+#endif /* _XPATH_H */
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index b2d9894..e8e1395 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -26,10 +26,12 @@
# Set common attributes of all tests
foreach(test_name IN LISTS tests)
- target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} yang)
- list(GET tests_wraps 0 test_wrap)
- set_target_properties(${test_name} PROPERTIES LINK_FLAGS "${test_wrap}")
- list(REMOVE_AT tests_wraps 0)
+ target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${PCRE_LIBRARIES} m)
+ if (NOT APPLE)
+ list(GET tests_wraps 0 test_wrap)
+ set_target_properties(${test_name} PROPERTIES LINK_FLAGS "${test_wrap}")
+ list(REMOVE_AT tests_wraps 0)
+ endif()
add_test(NAME ${test_name} COMMAND ${test_name})
# set_property(TEST ${test_name} PROPERTY ENVIRONMENT "LIBYANG_EXTENSIONS_PLUGINS_DIR=${CMAKE_BINARY_DIR}/src/extensions")
# set_property(TEST ${test_name} APPEND PROPERTY ENVIRONMENT "LIBYANG_USER_TYPES_PLUGINS_DIR=${CMAKE_BINARY_DIR}/src/user_types")
diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt
index 583e1f4..e535650 100644
--- a/tests/src/CMakeLists.txt
+++ b/tests/src/CMakeLists.txt
@@ -11,7 +11,7 @@
set(local_tests_wraps
" "
"-Wl,--wrap=realloc"
- "-Wl,--wrap=ly_set_add"
+ " "
" "
" "
" "
diff --git a/tests/src/test_common.c b/tests/src/test_common.c
index 0499c42..13da39b 100644
--- a/tests/src/test_common.c
+++ b/tests/src/test_common.c
@@ -13,6 +13,7 @@
*/
#include "../../src/common.c"
+#include "../../src/log.c"
#include <stdarg.h>
#include <stddef.h>
@@ -82,6 +83,7 @@
assert_int_equal(LY_EINVAL, ly_getutf8(&str, &c, &len));
}
+#ifndef APPLE
void *__real_realloc(void *ptr, size_t size);
void *__wrap_realloc(void *ptr, size_t size)
{
@@ -118,12 +120,15 @@
/* ptr should be freed by ly_realloc() */
}
+#endif /* not APPLE */
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup(test_utf8, logger_setup),
+#ifndef APPLE
cmocka_unit_test(test_lyrealloc),
+#endif
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/src/test_context.c b/tests/src/test_context.c
index d0fc202..a9dae30 100644
--- a/tests/src/test_context.c
+++ b/tests/src/test_context.c
@@ -13,7 +13,17 @@
*/
#include "tests/config.h"
+#include "../../src/common.c"
+#include "../../src/log.c"
+#include "../../src/set.c"
+#include "../../src/hash_table.c"
+#include "../../src/xpath.c"
+#include "../../src/parser_yang.c"
#include "../../src/context.c"
+#include "../../src/tree_schema_helpers.c"
+#include "../../src/tree_schema_free.c"
+#include "../../src/tree_schema_compile.c"
+#include "../../src/tree_schema.c"
#include <stdarg.h>
#include <stddef.h>
@@ -27,6 +37,7 @@
#define BUFSIZE 1024
char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
/* set to 0 to printing error messages to stderr instead of checking them in code */
#define ENABLE_LOGGER_CHECKING 1
@@ -35,9 +46,16 @@
logger(LY_LOG_LEVEL level, const char *msg, const char *path)
{
(void) level; /* unused */
- (void) path; /* unused */
-
- strncpy(logbuf, msg, BUFSIZE - 1);
+ if (store) {
+ if (path && path[0]) {
+ snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
+ } else {
+ strncpy(logbuf, msg, BUFSIZE - 1);
+ }
+ if (store > 0) {
+ --store;
+ }
+ }
}
static int
@@ -45,7 +63,7 @@
{
(void) state; /* unused */
#if ENABLE_LOGGER_CHECKING
- ly_set_log_clb(logger, 0);
+ ly_set_log_clb(logger, 1);
#endif
return 0;
}
@@ -56,19 +74,6 @@
# define logbuf_assert(str)
#endif
-int __real_ly_set_add(struct ly_set *set, void *object, int options);
-int __wrap_ly_set_add(struct ly_set *set, void *object, int options)
-{
- int wrap = mock_type(int);
-
- if (wrap) {
- /* error */
- return -1;
- } else {
- return __real_ly_set_add(set, object, options);
- }
-}
-
static void
test_searchdirs(void **state)
{
@@ -98,14 +103,10 @@
logbuf_assert("Unable to use search directory \"/nonexistingfile\" (No such file or directory)");
/* ly_set_add() fails */
- will_return(__wrap_ly_set_add, 1);
- assert_int_equal(LY_EMEM, ly_ctx_set_searchdir(ctx, TESTS_BIN"/src"));
-
/* no change */
assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(ctx, NULL));
/* correct path */
- will_return_always(__wrap_ly_set_add, 0);
assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(ctx, TESTS_BIN"/src"));
assert_int_equal(1, ctx->search_paths.count);
assert_string_equal(TESTS_BIN"/src", ctx->search_paths.objs[0]);
@@ -122,7 +123,7 @@
assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(ctx, TESTS_SRC"/../doc"));
assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(ctx, TESTS_SRC));
assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(ctx, TESTS_BIN));
- assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(ctx, "/tmp"));
+ assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(ctx, "/home"));
assert_int_equal(8, ctx->search_paths.count);
/* get searchpaths */
@@ -132,7 +133,7 @@
assert_string_equal(TESTS_BIN"/CMakeFiles", list[1]);
assert_string_equal(TESTS_SRC, list[5]);
assert_string_equal(TESTS_BIN, list[6]);
- assert_string_equal("/tmp", list[7]);
+ assert_string_equal("/home", list[7]);
assert_null(list[8]);
/* removing searchpaths */
@@ -147,7 +148,7 @@
assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdirs(ctx, TESTS_SRC));
assert_int_equal(6, ctx->search_paths.count);
/* last */
- assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdirs(ctx, "/tmp"));
+ assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdirs(ctx, "/home"));
assert_int_equal(5, ctx->search_paths.count);
/* all */
assert_int_equal(LY_SUCCESS, ly_ctx_unset_searchdirs(ctx, NULL));
@@ -162,10 +163,10 @@
/* test searchdir list in ly_ctx_new() */
assert_int_equal(LY_EINVAL, ly_ctx_new("/nonexistingfile", 0, &ctx));
logbuf_assert("Unable to use search directory \"/nonexistingfile\" (No such file or directory)");
- assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_SRC":/tmp:/tmp:"TESTS_SRC, 0, &ctx));
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_SRC":/home:/home:"TESTS_SRC, 0, &ctx));
assert_int_equal(2, ctx->search_paths.count);
assert_string_equal(TESTS_SRC, ctx->search_paths.objs[0]);
- assert_string_equal("/tmp", ctx->search_paths.objs[1]);
+ assert_string_equal("/home", ctx->search_paths.objs[1]);
/* cleanup */
ly_ctx_destroy(ctx, NULL);
@@ -177,6 +178,7 @@
(void) state; /* unused */
struct ly_ctx *ctx;
+
assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0xffffffff, &ctx));
/* invalid arguments */
@@ -250,19 +252,155 @@
ly_ctx_destroy(ctx, NULL);
}
+static LY_ERR test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name),
+ const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format,
+ const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
+{
+ *module_data = user_data;
+ *format = LYS_IN_YANG;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
static void
test_models(void **state)
{
(void) state; /* unused */
+ struct ly_ctx *ctx;
+ const char *str;
+ struct lys_module *mod1, *mod2;
+
/* invalid arguments */
assert_int_equal(0, ly_ctx_get_module_set_id(NULL));
logbuf_assert("Invalid argument ctx (ly_ctx_get_module_set_id()).");
- struct ly_ctx *ctx;
- assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
assert_int_equal(ctx->module_set_id, ly_ctx_get_module_set_id(ctx));
+ assert_null(lys_parse_mem(ctx, "module x {namespace urn:x;prefix x;}", 3));
+ logbuf_assert("Invalid schema input format.");
+
+ /* import callback */
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, (void*)(str = "test"));
+ assert_ptr_equal(test_imp_clb, ctx->imp_clb);
+ assert_ptr_equal(str, ctx->imp_clb_data);
+ assert_ptr_equal(test_imp_clb, ly_ctx_get_module_imp_clb(ctx, (void**)&str));
+ assert_string_equal("test", str);
+
+ ly_ctx_set_module_imp_clb(ctx, NULL, NULL);
+ assert_null(ctx->imp_clb);
+ assert_null(ctx->imp_clb_data);
+
+ /* name collision of module and submodule */
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-30;}");
+ assert_null(lys_parse_mem(ctx, "module y {namespace urn:y;prefix y;include y;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, ly_errcode(ctx));
+ logbuf_assert("Name collision between module and submodule of name \"y\". Line number 1.");
+
+ assert_non_null(lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;include y;revision 2018-10-30; }", LYS_IN_YANG));
+ assert_null(lys_parse_mem(ctx, "module y {namespace urn:y;prefix y;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, ly_errcode(ctx));
+ logbuf_assert("Name collision between module and submodule of name \"y\". Line number 1.");
+
+ store = 1;
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule y {belongs-to b {prefix b;}}");
+ assert_null(lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;include y;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, ly_errcode(ctx));
+ logbuf_assert("Name collision between submodules of name \"y\". Line number 1.");
+ store = -1;
+
+ /* selecting correct revision of the submodules */
+ ly_ctx_reset_latests(ctx);
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-31;}");
+ mod2 = lys_parse_mem_(ctx, "module a {namespace urn:a;prefix a;include y; revision 2018-10-31;}", LYS_IN_YANG, 0, NULL, NULL, NULL);
+ assert_non_null(mod2);
+ assert_string_equal("2018-10-31", mod2->parsed->includes[0].submodule->revs[0].date);
+
+ /* reloading module in case only the compiled module resists in the context */
+ mod1 = lys_parse_mem(ctx, "module w {namespace urn:w;prefix w;revision 2018-10-24;}", LYS_IN_YANG);
+ assert_non_null(mod1);
+ assert_int_equal(LY_SUCCESS, lys_compile(mod1, LYSC_OPT_FREE_SP));
+ assert_non_null(mod1->compiled);
+ assert_null(mod1->parsed);
+ mod2 = lys_parse_mem(ctx, "module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", LYS_IN_YANG);
+ assert_non_null(mod2);
+ /* mod1->parsed is necessary to compile mod2 because of possible groupings, typedefs, ... */
+ ly_ctx_set_module_imp_clb(ctx, NULL, NULL);
+ assert_int_equal(LY_ENOTFOUND, lys_compile(mod2, 0));
+ logbuf_assert("Unable to reload \"w\" module to import it into \"z\", source data not found.");
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "module w {namespace urn:w;prefix w;revision 2018-10-24;}");
+ assert_int_equal(LY_SUCCESS, lys_compile(mod2, 0));
+ assert_non_null(mod1->parsed);
+ assert_string_equal("w", mod1->parsed->name);
+
+ /* cleanup */
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_get_models(void **state)
+{
+ (void) state; /* unused */
+
+ struct ly_ctx *ctx;
+ const struct lys_module *mod, *mod2;
+ const char *str0 = "module a {namespace urn:a;prefix a;}";
+ const char *str1 = "module a {namespace urn:a;prefix a;revision 2018-10-23;}";
+ const char *str2 = "module a {namespace urn:a;prefix a;revision 2018-10-23;revision 2018-10-24;}";
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
+
+ /* invalid arguments */
+ assert_ptr_equal(NULL, ly_ctx_get_module(NULL, NULL, NULL));
+ logbuf_assert("Invalid argument ctx (ly_ctx_get_module()).");
+ assert_ptr_equal(NULL, ly_ctx_get_module(ctx, NULL, NULL));
+ logbuf_assert("Invalid argument name (ly_ctx_get_module()).");
+ assert_ptr_equal(NULL, ly_ctx_get_module_ns(NULL, NULL, NULL));
+ logbuf_assert("Invalid argument ctx (ly_ctx_get_module_ns()).");
+ assert_ptr_equal(NULL, ly_ctx_get_module_ns(ctx, NULL, NULL));
+ logbuf_assert("Invalid argument ns (ly_ctx_get_module_ns()).");
+ assert_null(ly_ctx_get_module(ctx, "nonsence", NULL));
+
+ /* internal modules */
+ assert_null(ly_ctx_get_module_implemented(ctx, "ietf-yang-types"));
+ mod = ly_ctx_get_module_implemented(ctx, "yang");
+ assert_non_null(mod);
+ assert_non_null(mod->parsed);
+ assert_string_equal("yang", mod->parsed->name);
+ mod2 = ly_ctx_get_module_implemented_ns(ctx, mod->parsed->ns);
+ assert_ptr_equal(mod, mod2);
+ assert_non_null(ly_ctx_get_module(ctx, "ietf-yang-metadata", "2016-08-05"));
+ assert_non_null(ly_ctx_get_module(ctx, "ietf-yang-types", "2013-07-15"));
+ assert_non_null(ly_ctx_get_module(ctx, "ietf-inet-types", "2013-07-15"));
+ assert_non_null(ly_ctx_get_module_ns(ctx, "urn:ietf:params:xml:ns:yang:ietf-datastores", "2017-08-17"));
+
+ /* select module by revision */
+ mod = lys_parse_mem(ctx, str1, LYS_IN_YANG);
+ /* invalid attempts - implementing module of the same name and inserting the same module */
+ assert_null(lys_parse_mem(ctx, str2, LYS_IN_YANG));
+ logbuf_assert("Module \"a\" is already implemented in the context.");
+ assert_null(lys_parse_mem_(ctx, str1, LYS_IN_YANG, 0, NULL, NULL, NULL));
+ logbuf_assert("Module \"a\" of revision \"2018-10-23\" is already present in the context.");
+ /* insert the second module only as imported, not implemented */
+ mod2 = lys_parse_mem_(ctx, str2, LYS_IN_YANG, 0, NULL, NULL, NULL);
+ assert_non_null(mod);
+ assert_non_null(mod2);
+ assert_ptr_not_equal(mod, mod2);
+ mod = ly_ctx_get_module_latest(ctx, "a");
+ assert_ptr_equal(mod, mod2);
+ mod2 = ly_ctx_get_module_latest_ns(ctx, mod->parsed->ns);
+ assert_ptr_equal(mod, mod2);
+ /* work with module with no revision */
+ mod = lys_parse_mem_(ctx, str0, LYS_IN_YANG, 0, NULL, NULL, NULL);
+ assert_non_null(mod);
+ assert_ptr_equal(mod, ly_ctx_get_module(ctx, "a", NULL));
+ assert_ptr_not_equal(mod, ly_ctx_get_module_latest(ctx, "a"));
+
+ str1 = "submodule b {belongs-to a {prefix a;}}";
+ assert_null(lys_parse_mem(ctx, str1, LYS_IN_YANG));
+ logbuf_assert("Input data contains submodule \"b\" which cannot be parsed directly without its main module.");
+
/* cleanup */
ly_ctx_destroy(ctx, NULL);
}
@@ -273,6 +411,7 @@
cmocka_unit_test_setup(test_searchdirs, logger_setup),
cmocka_unit_test_setup(test_options, logger_setup),
cmocka_unit_test_setup(test_models, logger_setup),
+ cmocka_unit_test_setup(test_get_models, logger_setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/src/test_hash_table.c b/tests/src/test_hash_table.c
index 1afeb83..2f10f0a 100644
--- a/tests/src/test_hash_table.c
+++ b/tests/src/test_hash_table.c
@@ -12,11 +12,20 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _BSD_SOURCE
-#define _DEFAULT_SOURCE
+#include "common.h"
#include "tests/config.h"
+#include "../../src/common.c"
+#include "../../src/set.c"
+#include "../../src/log.c"
+#include "../../src/xpath.c"
#include "../../src/hash_table.c"
+#include "../../src/parser_yang.c"
+#include "../../src/context.c"
+#include "../../src/tree_schema_helpers.c"
+#include "../../src/tree_schema_free.c"
+#include "../../src/tree_schema_compile.c"
+#include "../../src/tree_schema.c"
#include <stdarg.h>
#include <stddef.h>
diff --git a/tests/src/test_parser_yang.c b/tests/src/test_parser_yang.c
index db73f63..b07725a 100644
--- a/tests/src/test_parser_yang.c
+++ b/tests/src/test_parser_yang.c
@@ -12,8 +12,17 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#include "../../src/tree_schema.c"
+#include "../../src/common.c"
+#include "../../src/set.c"
+#include "../../src/log.c"
+#include "../../src/hash_table.c"
+#include "../../src/xpath.c"
#include "../../src/parser_yang.c"
+#include "../../src/context.c"
+#include "../../src/tree_schema_helpers.c"
+#include "../../src/tree_schema_free.c"
+#include "../../src/tree_schema_compile.c"
+#include "../../src/tree_schema.c"
#include <stdarg.h>
#include <stddef.h>
@@ -27,6 +36,7 @@
#define BUFSIZE 1024
char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
/* set to 0 to printing error messages to stderr instead of checking them in code */
#define ENABLE_LOGGER_CHECKING 1
@@ -36,11 +46,15 @@
logger(LY_LOG_LEVEL level, const char *msg, const char *path)
{
(void) level; /* unused */
-
- if (path) {
- snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
- } else {
- strncpy(logbuf, msg, BUFSIZE - 1);
+ if (store) {
+ if (path && path[0]) {
+ snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
+ } else {
+ strncpy(logbuf, msg, BUFSIZE - 1);
+ }
+ if (store > 0) {
+ --store;
+ }
}
}
#endif
@@ -55,6 +69,18 @@
return 0;
}
+static int
+logger_teardown(void **state)
+{
+ (void) state; /* unused */
+#if ENABLE_LOGGER_CHECKING
+ if (*state) {
+ fprintf(stderr, "%s\n", logbuf);
+ }
+#endif
+ return 0;
+}
+
void
logbuf_clean(void)
{
@@ -642,6 +668,104 @@
assert_ptr_equal(p, word);
}
+static void
+test_minmax(void **state)
+{
+ *state = test_minmax;
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ uint16_t flags = 0;
+ uint32_t value = 0;
+ struct lysp_ext_instance *ext = 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 = &mod;
+ ctx.mod->version = 2; /* simulate YANG 1.1 */
+
+ str = " 1invalid; ...";
+ assert_int_equal(LY_EVALID, parse_minelements(&ctx, &str, &value, &flags, &ext));
+ logbuf_assert("Invalid value \"1invalid\" of \"min-elements\". Line number 1.");
+
+ flags = value = 0;
+ str = " -1; ...";
+ assert_int_equal(LY_EVALID, parse_minelements(&ctx, &str, &value, &flags, &ext));
+ logbuf_assert("Invalid value \"-1\" of \"min-elements\". Line number 1.");
+
+ /* implementation limit */
+ flags = value = 0;
+ str = " 4294967296; ...";
+ assert_int_equal(LY_EVALID, parse_minelements(&ctx, &str, &value, &flags, &ext));
+ logbuf_assert("Value \"4294967296\" is out of \"min-elements\" bounds. Line number 1.");
+
+ flags = value = 0;
+ str = " 1; ...";
+ assert_int_equal(LY_SUCCESS, parse_minelements(&ctx, &str, &value, &flags, &ext));
+ assert_int_equal(LYS_SET_MIN, flags);
+ assert_int_equal(1, value);
+
+ flags = value = 0;
+ str = " 1 {m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_minelements(&ctx, &str, &value, &flags, &ext));
+ assert_int_equal(LYS_SET_MIN, flags);
+ assert_int_equal(1, value);
+ assert_non_null(ext);
+ FREE_ARRAY(ctx.ctx, ext, lysp_ext_instance_free);
+ ext = NULL;
+
+ flags = value = 0;
+ str = " 1 {config true;} ...";
+ assert_int_equal(LY_EVALID, parse_minelements(&ctx, &str, &value, &flags, &ext));
+ logbuf_assert("Invalid keyword \"config\" as a child of \"min-elements\". Line number 1.");
+
+ str = " 1invalid; ...";
+ assert_int_equal(LY_EVALID, parse_maxelements(&ctx, &str, &value, &flags, &ext));
+ logbuf_assert("Invalid value \"1invalid\" of \"max-elements\". Line number 1.");
+
+ flags = value = 0;
+ str = " -1; ...";
+ assert_int_equal(LY_EVALID, parse_maxelements(&ctx, &str, &value, &flags, &ext));
+ logbuf_assert("Invalid value \"-1\" of \"max-elements\". Line number 1.");
+
+ /* implementation limit */
+ flags = value = 0;
+ str = " 4294967296; ...";
+ assert_int_equal(LY_EVALID, parse_maxelements(&ctx, &str, &value, &flags, &ext));
+ logbuf_assert("Value \"4294967296\" is out of \"max-elements\" bounds. Line number 1.");
+
+ flags = value = 0;
+ str = " 1; ...";
+ assert_int_equal(LY_SUCCESS, parse_maxelements(&ctx, &str, &value, &flags, &ext));
+ assert_int_equal(LYS_SET_MAX, flags);
+ assert_int_equal(1, value);
+
+ flags = value = 0;
+ str = " unbounded; ...";
+ assert_int_equal(LY_SUCCESS, parse_maxelements(&ctx, &str, &value, &flags, &ext));
+ assert_int_equal(LYS_SET_MAX, flags);
+ assert_int_equal(0, value);
+
+ flags = value = 0;
+ str = " 1 {m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_maxelements(&ctx, &str, &value, &flags, &ext));
+ assert_int_equal(LYS_SET_MAX, flags);
+ assert_int_equal(1, value);
+ assert_non_null(ext);
+ FREE_ARRAY(ctx.ctx, ext, lysp_ext_instance_free);
+ ext = NULL;
+
+ flags = value = 0;
+ str = " 1 {config true;} ...";
+ assert_int_equal(LY_EVALID, parse_maxelements(&ctx, &str, &value, &flags, &ext));
+ logbuf_assert("Invalid keyword \"config\" as a child of \"max-elements\". Line number 1.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
static struct lysp_module *
mod_renew(struct ly_parser_ctx *ctx, struct lysp_module *mod, uint8_t submodule)
{
@@ -650,9 +774,20 @@
mod->ctx = ctx->ctx;
mod->submodule = submodule;
assert_non_null(mod);
+ ctx->mod = mod;
return mod;
}
+static LY_ERR test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name),
+ const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format,
+ const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
+{
+ *module_data = user_data;
+ *format = LYS_IN_YANG;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
static void
test_module(void **state)
{
@@ -687,7 +822,8 @@
assert_string_equal("x", mod->prefix);
mod = mod_renew(&ctx, mod, 0);
-#define SCHEMA_BEGINNING " name {namespace urn:x;prefix \"x\";"
+#define SCHEMA_BEGINNING " name {yang-version 1.1;namespace urn:x;prefix \"x\";"
+#define SCHEMA_BEGINNING2 " name {namespace urn:x;prefix \"x\";"
#define TEST_NODE(NODETYPE, INPUT, NAME) \
str = SCHEMA_BEGINNING INPUT; \
assert_int_equal(LY_SUCCESS, parse_sub_module(&ctx, &str, mod)); \
@@ -752,22 +888,45 @@
TEST_GENERIC("identity test;}", mod->identities,
assert_string_equal("test", mod->identities[0].name));
/* import */
- TEST_GENERIC("import test {prefix z;}}", mod->imports,
- assert_string_equal("test", mod->imports[0].name));
+ ly_ctx_set_module_imp_clb(ctx.ctx, test_imp_clb, "module zzz { namespace urn:zzz; prefix z;}");
+ TEST_GENERIC("import zzz {prefix z;}}", mod->imports,
+ assert_string_equal("zzz", mod->imports[0].name));
/* import - prefix collision */
- str = SCHEMA_BEGINNING "import test {prefix x;}}";
+ str = SCHEMA_BEGINNING "import zzz {prefix x;}}";
assert_int_equal(LY_EVALID, parse_sub_module(&ctx, &str, mod));
logbuf_assert("Prefix \"x\" already used as module prefix. Line number 2.");
mod = mod_renew(&ctx, mod, 0);
- str = SCHEMA_BEGINNING "import test1 {prefix y;}import test2 {prefix y;}}";
+ str = SCHEMA_BEGINNING "import zzz {prefix y;}import zzz {prefix y;}}";
assert_int_equal(LY_EVALID, parse_sub_module(&ctx, &str, mod));
- logbuf_assert("Prefix \"y\" already used to import \"test1\" module. Line number 2.");
+ logbuf_assert("Prefix \"y\" already used to import \"zzz\" module. Line number 2.");
mod = mod_renew(&ctx, mod, 0);
+ str = "module" SCHEMA_BEGINNING "import zzz {prefix y;}import zzz {prefix z;}}";
+ assert_null(lys_parse_mem(ctx.ctx, str, LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, ly_errcode(ctx.ctx));
+ logbuf_assert("Single revision of the module \"zzz\" referred twice.");
/* include */
- TEST_GENERIC("include test;}", mod->includes,
- assert_string_equal("test", mod->includes[0].name));
+ store = 1;
+ ly_ctx_set_module_imp_clb(ctx.ctx, test_imp_clb, "module xxx { namespace urn:xxx; prefix x;}");
+ str = "module" SCHEMA_BEGINNING "include xxx;}";
+ assert_null(lys_parse_mem(ctx.ctx, str, LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, ly_errcode(ctx.ctx));
+ logbuf_assert("Included \"xxx\" schema from \"name\" is actually not a submodule.");
+ store = -1;
+
+ store = 1;
+ ly_ctx_set_module_imp_clb(ctx.ctx, test_imp_clb, "submodule xxx {belongs-to wrong-name {prefix w;}}");
+ str = "module" SCHEMA_BEGINNING "include xxx;}";
+ assert_null(lys_parse_mem(ctx.ctx, str, LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, ly_errcode(ctx.ctx));
+ logbuf_assert("Included \"xxx\" submodule from \"name\" belongs-to a different module \"wrong-name\".");
+ store = -1;
+
+ ly_ctx_set_module_imp_clb(ctx.ctx, test_imp_clb, "submodule xxx {belongs-to name {prefix x;}}");
+ TEST_GENERIC("include xxx;}", mod->includes,
+ assert_string_equal("xxx", mod->includes[0].name));
+
/* leaf */
TEST_NODE(LYS_LEAF, "leaf test {type string;}}", "test");
/* leaf-list */
@@ -785,7 +944,7 @@
assert_string_equal("RFC7950", mod->ref));
/* revision */
TEST_GENERIC("revision 2018-10-12;}", mod->revs,
- assert_string_equal("2018-10-12", mod->revs[0].rev));
+ assert_string_equal("2018-10-12", mod->revs[0].date));
/* rpc */
TEST_GENERIC("rpc test;}", mod->rpcs,
assert_string_equal("test", mod->rpcs[0].name));
@@ -795,19 +954,19 @@
/* uses */
TEST_NODE(LYS_USES, "uses test;}", "test");
/* yang-version */
- str = SCHEMA_BEGINNING "\n\tyang-version 10;}";
+ str = SCHEMA_BEGINNING2 "\n\tyang-version 10;}";
assert_int_equal(LY_EVALID, parse_sub_module(&ctx, &str, mod));
logbuf_assert("Invalid value \"10\" of \"yang-version\". Line number 3.");
mod = mod_renew(&ctx, mod, 0);
- str = SCHEMA_BEGINNING "yang-version 1.0;yang-version 1.1;}";
+ str = SCHEMA_BEGINNING2 "yang-version 1.0;yang-version 1.1;}";
assert_int_equal(LY_EVALID, parse_sub_module(&ctx, &str, mod));
logbuf_assert("Duplicate keyword \"yang-version\". Line number 3.");
mod = mod_renew(&ctx, mod, 0);
- str = SCHEMA_BEGINNING "yang-version 1.0;}";
+ str = SCHEMA_BEGINNING2 "yang-version 1.0;}";
assert_int_equal(LY_SUCCESS, parse_sub_module(&ctx, &str, mod));
assert_int_equal(1, mod->version);
mod = mod_renew(&ctx, mod, 0);
- str = SCHEMA_BEGINNING "yang-version \"1.1\";}";
+ str = SCHEMA_BEGINNING2 "yang-version \"1.1\";}";
assert_int_equal(LY_SUCCESS, parse_sub_module(&ctx, &str, mod));
assert_int_equal(2, mod->version);
mod = mod_renew(&ctx, mod, 0);
@@ -829,18 +988,20 @@
/* missing mandatory substatements */
str = " subname {}";
+ lydict_remove(ctx.ctx, mod->name);
assert_int_equal(LY_EVALID, parse_sub_module(&ctx, &str, mod));
assert_string_equal("subname", mod->name);
logbuf_assert("Missing mandatory keyword \"belongs-to\" as a child of \"submodule\". Line number 3.");
mod = mod_renew(&ctx, mod, 1);
- str = " subname {belongs-to name;}";
+ str = " subname {belongs-to name {prefix x;}}";
+ lydict_remove(ctx.ctx, mod->name);
assert_int_equal(LY_SUCCESS, parse_sub_module(&ctx, &str, mod));
assert_string_equal("name", mod->belongsto);
mod = mod_renew(&ctx, mod, 1);
#undef SCHEMA_BEGINNING
-#define SCHEMA_BEGINNING " subname {belongs-to name;"
+#define SCHEMA_BEGINNING " subname {belongs-to name {prefix x;}"
/* duplicated namespace, prefix */
TEST_DUP("belongs-to", "module1", "module2", "3", 1);
@@ -868,9 +1029,9 @@
test_identity(void **state)
{
(void) state; /* unused */
- int dict = 1; /* magic variable for FREE macros */
struct ly_parser_ctx ctx;
+ struct lysp_module m = {0};
struct lysp_ident *ident = NULL;
const char *str;
@@ -878,6 +1039,8 @@
assert_non_null(ctx.ctx);
ctx.line = 1;
ctx.indent = 0;
+ ctx.mod = &m;
+ ctx.mod->version = 2; /* simulate YANG 1.1 */
/* invalid cardinality */
#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
@@ -912,7 +1075,6 @@
test_feature(void **state)
{
(void) state; /* unused */
- int dict = 1; /* magic variable for FREE macros */
struct ly_parser_ctx ctx;
struct lysp_feature *features = NULL;
@@ -956,7 +1118,6 @@
test_deviation(void **state)
{
(void) state; /* unused */
- int dict = 1; /* magic variable for FREE macros */
struct ly_parser_ctx ctx;
struct lysp_deviation *d = NULL;
@@ -1019,7 +1180,7 @@
/* invalid cardinality */
#define TEST_DUP(TYPE, MEMBER, VALUE1, VALUE2) \
TEST_DUP_GENERIC(TYPE" {", MEMBER, VALUE1, VALUE2, parse_deviate, \
- &d, "1", lysp_deviate_free(ctx.ctx, d, 1); free(d); d = NULL)
+ &d, "1", lysp_deviate_free(ctx.ctx, d); free(d); d = NULL)
TEST_DUP("add", "config", "true", "false");
TEST_DUP("replace", "default", "int8", "uint8");
@@ -1034,29 +1195,29 @@
assert_int_equal(LY_SUCCESS, parse_deviate(&ctx, &str, &d));
assert_non_null(d);
assert_string_equal(" ...", str);
- lysp_deviate_free(ctx.ctx, d, 1); free(d); d = NULL;
+ lysp_deviate_free(ctx.ctx, d); free(d); d = NULL;
str = " add {units meters; must 1; must 2; unique x; unique y; default a; default b; config true; mandatory true; min-elements 1; max-elements 2; prefix:ext;} ...";
assert_int_equal(LY_SUCCESS, parse_deviate(&ctx, &str, &d));
assert_non_null(d);
assert_string_equal(" ...", str);
- lysp_deviate_free(ctx.ctx, d, 1); free(d); d = NULL;
+ lysp_deviate_free(ctx.ctx, d); free(d); d = NULL;
str = " delete {units meters; must 1; must 2; unique x; unique y; default a; default b; prefix:ext;} ...";
assert_int_equal(LY_SUCCESS, parse_deviate(&ctx, &str, &d));
assert_non_null(d);
assert_string_equal(" ...", str);
- lysp_deviate_free(ctx.ctx, d, 1); free(d); d = NULL;
+ lysp_deviate_free(ctx.ctx, d); free(d); d = NULL;
str = " replace {type string; units meters; default a; config true; mandatory true; min-elements 1; max-elements 2; prefix:ext;} ...";
assert_int_equal(LY_SUCCESS, parse_deviate(&ctx, &str, &d));
assert_non_null(d);
assert_string_equal(" ...", str);
- lysp_deviate_free(ctx.ctx, d, 1); free(d); d = NULL;
+ lysp_deviate_free(ctx.ctx, d); free(d); d = NULL;
/* invalid substatements */
#define TEST_NOT_SUP(DEV, STMT, VALUE) \
str = " "DEV" {"STMT" "VALUE";}..."; \
assert_int_equal(LY_EVALID, parse_deviate(&ctx, &str, &d)); \
logbuf_assert("Deviate \""DEV"\" does not support keyword \""STMT"\". Line number 1."); \
- lysp_deviate_free(ctx.ctx, d, 1); free(d); d = NULL
+ lysp_deviate_free(ctx.ctx, d); free(d); d = NULL
TEST_NOT_SUP("not-supported", "units", "meters");
TEST_NOT_SUP("not-supported", "must", "1");
@@ -1087,6 +1248,621 @@
ly_ctx_destroy(ctx.ctx, NULL);
}
+static void
+test_container(void **state)
+{
+ (void) state; /* unused */
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_node_container *c = 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 = &mod;
+ ctx.mod->version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ str = "cont {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_container(&ctx, &str, NULL, (struct lysp_node**)&c)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ lysp_node_free(ctx.ctx, (struct lysp_node*)c); c = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("presence", "true", "false");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ str = "cont {action x;anydata any;anyxml anyxml; choice ch;config false;container c;description test;grouping g;if-feature f; leaf l {type string;}"
+ "leaf-list ll {type string;} list li;must 'expr';notification not; presence true; reference test;status current;typedef t {type int8;}uses g;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_container(&ctx, &str, NULL, (struct lysp_node**)&c));
+ assert_non_null(c);
+ assert_int_equal(LYS_CONTAINER, c->nodetype);
+ assert_string_equal("cont", c->name);
+ assert_non_null(c->actions);
+ assert_non_null(c->child);
+ assert_string_equal("test", c->dsc);
+ assert_non_null(c->exts);
+ assert_non_null(c->groupings);
+ assert_non_null(c->iffeatures);
+ assert_non_null(c->musts);
+ assert_non_null(c->notifs);
+ assert_string_equal("true", c->presence);
+ assert_string_equal("test", c->ref);
+ assert_non_null(c->typedefs);
+ assert_non_null(c->when);
+ assert_null(c->parent);
+ assert_null(c->next);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR, c->flags);
+ ly_set_erase(&ctx.tpdfs_nodes, NULL);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)c); c = NULL;
+
+ /* invalid */
+ str = " cont {augment /root;} ...";
+ assert_int_equal(LY_EVALID, parse_container(&ctx, &str, NULL, (struct lysp_node**)&c));
+ logbuf_assert("Invalid keyword \"augment\" as a child of \"container\". Line number 1.");
+ lysp_node_free(ctx.ctx, (struct lysp_node*)c); c = NULL;
+ str = " cont {nonsence true;} ...";
+ assert_int_equal(LY_EVALID, parse_container(&ctx, &str, NULL, (struct lysp_node**)&c));
+ logbuf_assert("Invalid character sequence \"nonsence\", expected a keyword. Line number 1.");
+ lysp_node_free(ctx.ctx, (struct lysp_node*)c); c = NULL;
+
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
+test_leaf(void **state)
+{
+ *state = test_leaf;
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_node_leaf *l = 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 = &mod;
+ //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_leaf(&ctx, &str, NULL, (struct lysp_node**)&l)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ lysp_node_free(ctx.ctx, (struct lysp_node*)l); l = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("default", "x", "y");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("mandatory", "true", "false");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("type", "int8", "uint8");
+ TEST_DUP("units", "text1", "text2");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content - without mandatory which is mutual exclusive with default */
+ str = "l {config false;default \"xxx\";description test;if-feature f;"
+ "must 'expr';reference test;status current;type string; units yyy;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_leaf(&ctx, &str, NULL, (struct lysp_node**)&l));
+ assert_non_null(l);
+ assert_int_equal(LYS_LEAF, l->nodetype);
+ assert_string_equal("l", l->name);
+ assert_string_equal("test", l->dsc);
+ assert_string_equal("xxx", l->dflt);
+ assert_string_equal("yyy", l->units);
+ assert_string_equal("string", l->type.name);
+ assert_non_null(l->exts);
+ assert_non_null(l->iffeatures);
+ assert_non_null(l->musts);
+ assert_string_equal("test", l->ref);
+ assert_non_null(l->when);
+ assert_null(l->parent);
+ assert_null(l->next);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR, l->flags);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)l); l = NULL;
+
+ /* full content - now with mandatory */
+ str = "l {mandatory true; type string;} ...";
+ assert_int_equal(LY_SUCCESS, parse_leaf(&ctx, &str, NULL, (struct lysp_node**)&l));
+ assert_non_null(l);
+ assert_int_equal(LYS_LEAF, l->nodetype);
+ assert_string_equal("l", l->name);
+ assert_string_equal("string", l->type.name);
+ assert_int_equal(LYS_MAND_TRUE, l->flags);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)l); l = NULL;
+
+ /* invalid */
+ str = " l {mandatory true; default xx; type string;} ...";
+ assert_int_equal(LY_EVALID, parse_leaf(&ctx, &str, NULL, (struct lysp_node**)&l));
+ logbuf_assert("Invalid combination of keywords \"mandatory\" and \"default\" as substatements of \"leaf\". Line number 1.");
+ lysp_node_free(ctx.ctx, (struct lysp_node*)l); l = NULL;
+
+ str = " l {description \"missing type\";} ...";
+ assert_int_equal(LY_EVALID, parse_leaf(&ctx, &str, NULL, (struct lysp_node**)&l));
+ logbuf_assert("Missing mandatory keyword \"type\" as a child of \"leaf\". Line number 1.");
+ lysp_node_free(ctx.ctx, (struct lysp_node*)l); l = NULL;
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
+test_leaflist(void **state)
+{
+ *state = test_leaf;
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_node_leaflist *ll = 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 = &mod;
+ ctx.mod->version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ str = "ll {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_leaflist(&ctx, &str, NULL, (struct lysp_node**)&ll)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ll); ll = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("max-elements", "10", "20");
+ TEST_DUP("min-elements", "10", "20");
+ TEST_DUP("ordered-by", "user", "system");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("type", "int8", "uint8");
+ TEST_DUP("units", "text1", "text2");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content - without min-elements which is mutual exclusive with default */
+ str = "ll {config false;default \"xxx\"; default \"yyy\";description test;if-feature f;"
+ "max-elements 10;must 'expr';ordered-by user;reference test;"
+ "status current;type string; units zzz;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_leaflist(&ctx, &str, NULL, (struct lysp_node**)&ll));
+ assert_non_null(ll);
+ assert_int_equal(LYS_LEAFLIST, ll->nodetype);
+ assert_string_equal("ll", ll->name);
+ assert_string_equal("test", ll->dsc);
+ assert_non_null(ll->dflts);
+ assert_int_equal(2, LY_ARRAY_SIZE(ll->dflts));
+ assert_string_equal("xxx", ll->dflts[0]);
+ assert_string_equal("yyy", ll->dflts[1]);
+ assert_string_equal("zzz", ll->units);
+ assert_int_equal(10, ll->max);
+ assert_int_equal(0, ll->min);
+ assert_string_equal("string", ll->type.name);
+ assert_non_null(ll->exts);
+ assert_non_null(ll->iffeatures);
+ assert_non_null(ll->musts);
+ assert_string_equal("test", ll->ref);
+ assert_non_null(ll->when);
+ assert_null(ll->parent);
+ assert_null(ll->next);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_USER | LYS_SET_MAX, ll->flags);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ll); ll = NULL;
+
+ /* full content - now with min-elements */
+ str = "ll {min-elements 10; type string;} ...";
+ assert_int_equal(LY_SUCCESS, parse_leaflist(&ctx, &str, NULL, (struct lysp_node**)&ll));
+ assert_non_null(ll);
+ assert_int_equal(LYS_LEAFLIST, ll->nodetype);
+ assert_string_equal("ll", ll->name);
+ assert_string_equal("string", ll->type.name);
+ assert_int_equal(0, ll->max);
+ assert_int_equal(10, ll->min);
+ assert_int_equal(LYS_SET_MIN, ll->flags);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ll); ll = NULL;
+
+ /* invalid */
+ str = " ll {min-elements 1; default xx; type string;} ...";
+ assert_int_equal(LY_EVALID, parse_leaflist(&ctx, &str, NULL, (struct lysp_node**)&ll));
+ logbuf_assert("Invalid combination of keywords \"min-elements\" and \"default\" as substatements of \"leaf-list\". Line number 1.");
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ll); ll = NULL;
+
+ str = " ll {description \"missing type\";} ...";
+ assert_int_equal(LY_EVALID, parse_leaflist(&ctx, &str, NULL, (struct lysp_node**)&ll));
+ logbuf_assert("Missing mandatory keyword \"type\" as a child of \"leaf-list\". Line number 1.");
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ll); ll = NULL;
+
+ str = " ll {type string; min-elements 10; max-elements 1;} ..."; /* invalid combination of min/max */
+ assert_int_equal(LY_EVALID, parse_leaflist(&ctx, &str, NULL, (struct lysp_node**)&ll));
+ logbuf_assert("Invalid combination of min-elements and max-elements: min value 10 is bigger than the max value 1. Line number 1.");
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ll); ll = NULL;
+
+ ctx.mod->version = 1; /* simulate YANG 1.0 - default statement is not allowed */
+ str = " ll {default xx; type string;} ...";
+ assert_int_equal(LY_EVALID, parse_leaflist(&ctx, &str, NULL, (struct lysp_node**)&ll));
+ logbuf_assert("Invalid keyword \"default\" as a child of \"leaf-list\" - the statement is allowed only in YANG 1.1 modules. Line number 1.");
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ll); ll = NULL;
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
+test_list(void **state)
+{
+ *state = test_list;
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_node_list *l = 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 = &mod;
+ 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_list(&ctx, &str, NULL, (struct lysp_node**)&l)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ lysp_node_free(ctx.ctx, (struct lysp_node*)l); l = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("key", "one", "two");
+ TEST_DUP("max-elements", "10", "20");
+ TEST_DUP("min-elements", "10", "20");
+ TEST_DUP("ordered-by", "user", "system");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ str = "l {action x;anydata any;anyxml anyxml; choice ch;config false;container c;description test;grouping g;if-feature f; key l; leaf l {type string;}"
+ "leaf-list ll {type string;} list li;max-elements 10; min-elements 1;must 'expr';notification not; ordered-by system; reference test;"
+ "status current;typedef t {type int8;}unique xxx;unique yyy;uses g;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_list(&ctx, &str, NULL, (struct lysp_node**)&l));
+ assert_non_null(l);
+ assert_int_equal(LYS_LIST, l->nodetype);
+ assert_string_equal("l", l->name);
+ assert_string_equal("test", l->dsc);
+ assert_string_equal("l", l->key);
+ assert_non_null(l->uniques);
+ assert_int_equal(2, LY_ARRAY_SIZE(l->uniques));
+ assert_string_equal("xxx", l->uniques[0]);
+ assert_string_equal("yyy", l->uniques[1]);
+ assert_int_equal(10, l->max);
+ assert_int_equal(1, l->min);
+ assert_non_null(l->exts);
+ assert_non_null(l->iffeatures);
+ assert_non_null(l->musts);
+ assert_string_equal("test", l->ref);
+ assert_non_null(l->when);
+ assert_null(l->parent);
+ assert_null(l->next);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM | LYS_SET_MAX | LYS_SET_MIN, l->flags);
+ ly_set_erase(&ctx.tpdfs_nodes, NULL);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)l); l = NULL;
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
+test_choice(void **state)
+{
+ *state = test_choice;
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_node_choice *ch = 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 = &mod;
+ ctx.mod->version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ str = "ch {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_choice(&ctx, &str, NULL, (struct lysp_node**)&ch)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ch); ch = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("default", "a", "b");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("mandatory", "true", "false");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content - without default due to a collision with mandatory */
+ str = "ch {anydata any;anyxml anyxml; case c;choice ch;config false;container c;description test;if-feature f;leaf l {type string;}"
+ "leaf-list ll {type string;} list li;mandatory true;reference test;status current;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_choice(&ctx, &str, NULL, (struct lysp_node**)&ch));
+ assert_non_null(ch);
+ assert_int_equal(LYS_CHOICE, ch->nodetype);
+ assert_string_equal("ch", ch->name);
+ assert_string_equal("test", ch->dsc);
+ assert_non_null(ch->exts);
+ assert_non_null(ch->iffeatures);
+ assert_string_equal("test", ch->ref);
+ assert_non_null(ch->when);
+ assert_null(ch->parent);
+ assert_null(ch->next);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_MAND_TRUE, ch->flags);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ch); ch = NULL;
+
+ /* full content - the default missing from the previous node */
+ str = "ch {default c;case c;} ...";
+ assert_int_equal(LY_SUCCESS, parse_choice(&ctx, &str, NULL, (struct lysp_node**)&ch));
+ assert_non_null(ch);
+ assert_int_equal(LYS_CHOICE, ch->nodetype);
+ assert_string_equal("ch", ch->name);
+ assert_string_equal("c", ch->dflt);
+ assert_int_equal(0, ch->flags);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ch); ch = NULL;
+
+ /* invalid content */
+ str = "ch {mandatory true; default c1; case c1 {leaf x{type string;}}} ...";
+ assert_int_equal(LY_EVALID, parse_choice(&ctx, &str, NULL, (struct lysp_node**)&ch));
+ logbuf_assert("Invalid combination of keywords \"mandatory\" and \"default\" as substatements of \"choice\". Line number 1.");
+ lysp_node_free(ctx.ctx, (struct lysp_node*)ch); ch = NULL;
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
+test_case(void **state)
+{
+ *state = test_case;
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_node_case *cs = 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 = &mod;
+ ctx.mod->version = 2; /* simulate YANG 1.1 */
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ str = "cs {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_case(&ctx, &str, NULL, (struct lysp_node**)&cs)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ lysp_node_free(ctx.ctx, (struct lysp_node*)cs); cs = 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 = "cs {anydata any;anyxml anyxml; choice ch;container c;description test;if-feature f;leaf l {type string;}"
+ "leaf-list ll {type string;} list li;reference test;status current;uses grp;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_case(&ctx, &str, NULL, (struct lysp_node**)&cs));
+ assert_non_null(cs);
+ assert_int_equal(LYS_CASE, cs->nodetype);
+ assert_string_equal("cs", cs->name);
+ assert_string_equal("test", cs->dsc);
+ assert_non_null(cs->exts);
+ assert_non_null(cs->iffeatures);
+ assert_string_equal("test", cs->ref);
+ assert_non_null(cs->when);
+ assert_null(cs->parent);
+ assert_null(cs->next);
+ assert_int_equal(LYS_STATUS_CURR, cs->flags);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)cs); cs = NULL;
+
+ /* invalid content */
+ str = "cs {config true} ...";
+ assert_int_equal(LY_EVALID, parse_case(&ctx, &str, NULL, (struct lysp_node**)&cs));
+ logbuf_assert("Invalid keyword \"config\" as a child of \"case\". Line number 1.");
+ lysp_node_free(ctx.ctx, (struct lysp_node*)cs); cs = NULL;
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
+test_any(void **state, enum yang_keyword kw)
+{
+ *state = test_any;
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_node_anydata *any = 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 = &mod;
+ if (kw == YANG_ANYDATA) {
+ ctx.mod->version = 2; /* simulate YANG 1.1 */
+ } else {
+ ctx.mod->version = 1; /* simulate YANG 1.0 */
+ }
+
+ /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+ str = "l {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+ assert_int_equal(LY_EVALID, parse_any(&ctx, &str, kw, NULL, (struct lysp_node**)&any)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ lysp_node_free(ctx.ctx, (struct lysp_node*)any); any = NULL;
+
+ TEST_DUP("config", "true", "false");
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("mandatory", "true", "false");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+ TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+ /* full content */
+ str = "any {config true;description test;if-feature f;mandatory true;must 'expr';reference test;status current;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_any(&ctx, &str, kw, NULL, (struct lysp_node**)&any));
+ assert_non_null(any);
+ assert_int_equal(kw == YANG_ANYDATA ? LYS_ANYDATA : LYS_ANYXML, any->nodetype);
+ assert_string_equal("any", any->name);
+ assert_string_equal("test", any->dsc);
+ assert_non_null(any->exts);
+ assert_non_null(any->iffeatures);
+ assert_non_null(any->musts);
+ assert_string_equal("test", any->ref);
+ assert_non_null(any->when);
+ assert_null(any->parent);
+ assert_null(any->next);
+ assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_MAND_TRUE, any->flags);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)any); any = NULL;
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
+test_anydata(void **state)
+{
+ return test_any(state, YANG_ANYDATA);
+}
+
+static void
+test_anyxml(void **state)
+{
+ return test_any(state, YANG_ANYXML);
+}
+
+static void
+test_grouping(void **state)
+{
+ *state = test_grouping;
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_grp *grp = 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 = &mod;
+ 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_grouping(&ctx, &str, NULL, &grp)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ FREE_ARRAY(ctx.ctx, grp, lysp_grp_free); grp = NULL;
+
+ TEST_DUP("description", "text1", "text2");
+ TEST_DUP("reference", "1", "2");
+ TEST_DUP("status", "current", "obsolete");
+#undef TEST_DUP
+
+ /* full content */
+ str = "grp {action x;anydata any;anyxml anyxml; choice ch;container c;description test;grouping g;leaf l {type string;}"
+ "leaf-list ll {type string;} list li;notification not;reference test;status current;typedef t {type int8;}uses g;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_grouping(&ctx, &str, NULL, &grp));
+ assert_non_null(grp);
+ assert_int_equal(LYS_GROUPING, grp->nodetype);
+ assert_string_equal("grp", grp->name);
+ assert_string_equal("test", grp->dsc);
+ assert_non_null(grp->exts);
+ assert_string_equal("test", grp->ref);
+ assert_null(grp->parent);
+ assert_int_equal( LYS_STATUS_CURR, grp->flags);
+ ly_set_erase(&ctx.tpdfs_nodes, NULL);
+ FREE_ARRAY(ctx.ctx, grp, lysp_grp_free); grp = NULL;
+
+ /* invalid content */
+ str = "grp {config true} ...";
+ assert_int_equal(LY_EVALID, parse_grouping(&ctx, &str, NULL, &grp));
+ logbuf_assert("Invalid keyword \"config\" as a child of \"grouping\". Line number 1.");
+ FREE_ARRAY(ctx.ctx, grp, lysp_grp_free); grp = NULL;
+
+ str = "grp {must 'expr'} ...";
+ assert_int_equal(LY_EVALID, parse_grouping(&ctx, &str, NULL, &grp));
+ logbuf_assert("Invalid keyword \"must\" as a child of \"grouping\". Line number 1.");
+ FREE_ARRAY(ctx.ctx, grp, lysp_grp_free); grp = NULL;
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
+test_uses(void **state)
+{
+ *state = test_uses;
+
+ struct lysp_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lysp_node_uses *u = 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 = &mod;
+ 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_uses(&ctx, &str, NULL, (struct lysp_node**)&u)); \
+ logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+ lysp_node_free(ctx.ctx, (struct lysp_node*)u); u = 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 = "grpref {augment some/node;description test;if-feature f;reference test;refine some/other/node;status current;when true;m:ext;} ...";
+ assert_int_equal(LY_SUCCESS, parse_uses(&ctx, &str, NULL, (struct lysp_node**)&u));
+ assert_non_null(u);
+ assert_int_equal(LYS_USES, u->nodetype);
+ assert_string_equal("grpref", u->name);
+ assert_string_equal("test", u->dsc);
+ assert_non_null(u->exts);
+ assert_non_null(u->iffeatures);
+ assert_string_equal("test", u->ref);
+ assert_non_null(u->augments);
+ assert_non_null(u->refines);
+ assert_non_null(u->when);
+ assert_null(u->parent);
+ assert_null(u->next);
+ assert_int_equal(LYS_STATUS_CURR, u->flags);
+ lysp_node_free(ctx.ctx, (struct lysp_node*)u); u = NULL;
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
int main(void)
{
const struct CMUnitTest tests[] = {
@@ -1094,11 +1870,22 @@
cmocka_unit_test_setup(test_comments, logger_setup),
cmocka_unit_test_setup(test_arg, logger_setup),
cmocka_unit_test_setup(test_stmts, logger_setup),
+ cmocka_unit_test_setup_teardown(test_minmax, logger_setup, logger_teardown),
cmocka_unit_test_setup(test_module, logger_setup),
cmocka_unit_test_setup(test_identity, logger_setup),
cmocka_unit_test_setup(test_feature, logger_setup),
cmocka_unit_test_setup(test_deviation, logger_setup),
cmocka_unit_test_setup(test_deviate, logger_setup),
+ cmocka_unit_test_setup(test_container, logger_setup),
+ cmocka_unit_test_setup_teardown(test_leaf, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_leaflist, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_list, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_choice, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_case, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_anydata, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_anyxml, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_grouping, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_uses, logger_setup, logger_teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/src/test_parser_yin.c b/tests/src/test_parser_yin.c
index 8e14839..870e5b4 100644
--- a/tests/src/test_parser_yin.c
+++ b/tests/src/test_parser_yin.c
@@ -1,3 +1,17 @@
+#include "../../src/common.c"
+#include "../../src/set.c"
+#include "../../src/log.c"
+#include "../../src/hash_table.c"
+#include "../../src/xpath.c"
+#include "../../src/parser_yang.c"
+#include "../../src/context.c"
+#include "../../src/tree_schema_helpers.c"
+#include "../../src/tree_schema_free.c"
+#include "../../src/tree_schema_compile.c"
+#include "../../src/tree_schema.c"
+#include "../../src/xml.c"
+#include "../../src/parser_yin.c"
+
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
@@ -7,8 +21,6 @@
#include <string.h>
#include "libyang.h"
-#include "tree_schema_internal.h"
-#include "../../src/parser_yin.c"
static void
test_parse(void **state)
diff --git a/tests/src/test_set.c b/tests/src/test_set.c
index a6c53c4..77c9c0d 100644
--- a/tests/src/test_set.c
+++ b/tests/src/test_set.c
@@ -11,9 +11,8 @@
*
* https://opensource.org/licenses/BSD-3-Clause
*/
+#include "common.h"
-#define _BSD_SOURCE
-#define _DEFAULT_SOURCE
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
@@ -23,6 +22,7 @@
#include "libyang.h"
#include "../../src/set.c"
+#include "../../src/log.c"
#define BUFSIZE 1024
char logbuf[BUFSIZE] = {0};
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index 79c7c42..ea088ed 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -12,8 +12,17 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#include "../../src/tree_schema.c"
+#include "../../src/common.c"
+#include "../../src/log.c"
+#include "../../src/set.c"
+#include "../../src/xpath.c"
#include "../../src/parser_yang.c"
+#include "../../src/tree_schema_helpers.c"
+#include "../../src/tree_schema_free.c"
+#include "../../src/tree_schema_compile.c"
+#include "../../src/tree_schema.c"
+#include "../../src/context.c"
+#include "../../src/hash_table.c"
#include <stdarg.h>
#include <stddef.h>
@@ -37,7 +46,7 @@
{
(void) level; /* unused */
- if (path) {
+ if (path && path[0]) {
snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
} else {
strncpy(logbuf, msg, BUFSIZE - 1);
@@ -55,6 +64,18 @@
return 0;
}
+static int
+logger_teardown(void **state)
+{
+ (void) state; /* unused */
+#if ENABLE_LOGGER_CHECKING
+ if (*state) {
+ fprintf(stderr, "%s\n", logbuf);
+ }
+#endif
+ return 0;
+}
+
void
logbuf_clean(void)
{
@@ -62,32 +83,42 @@
}
#if ENABLE_LOGGER_CHECKING
-# define logbuf_assert(str) assert_string_equal(logbuf, str)
+# define logbuf_assert(str) assert_string_equal(logbuf, str);logbuf_clean()
#else
# define logbuf_assert(str)
#endif
+static LY_ERR test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name),
+ const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format,
+ const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
+{
+ *module_data = user_data;
+ *format = LYS_IN_YANG;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
static void
test_module(void **state)
{
(void) state; /* unused */
const char *str;
- struct ly_ctx *ctx;
+ struct ly_parser_ctx ctx = {0};
struct lys_module mod = {0};
struct lysc_feature *f;
struct lysc_iffeature *iff;
str = "module test {namespace urn:test; prefix t;"
"feature f1;feature f2 {if-feature f1;}}";
- assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx.ctx));
- assert_int_equal(LY_EINVAL, lys_compile(NULL, 0, NULL));
- logbuf_assert("Invalid argument sc (lys_compile()).");
- assert_int_equal(LY_EINVAL, lys_compile(NULL, 0, &mod.compiled));
- logbuf_assert("Invalid argument sp (lys_compile()).");
- assert_int_equal(LY_SUCCESS, yang_parse(ctx, str, &mod.parsed));
- assert_int_equal(LY_SUCCESS, lys_compile(mod.parsed, 0, &mod.compiled));
+ assert_int_equal(LY_EINVAL, lys_compile(NULL, 0));
+ logbuf_assert("Invalid argument mod (lys_compile()).");
+ assert_int_equal(LY_EINVAL, lys_compile(&mod, 0));
+ logbuf_assert("Invalid argument mod->parsed (lys_compile()).");
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, str, &mod.parsed));
+ assert_int_equal(LY_SUCCESS, lys_compile(&mod, 0));
assert_non_null(mod.compiled);
assert_ptr_equal(mod.parsed->name, mod.compiled->name);
assert_ptr_equal(mod.parsed->ns, mod.compiled->ns);
@@ -105,7 +136,7 @@
lysc_module_free(mod.compiled, NULL);
- assert_int_equal(LY_SUCCESS, lys_compile(mod.parsed, LYSC_OPT_FREE_SP, &mod.compiled));
+ assert_int_equal(LY_SUCCESS, lys_compile(&mod, LYSC_OPT_FREE_SP));
assert_non_null(mod.compiled);
assert_string_equal("test", mod.compiled->name);
assert_string_equal("urn:test", mod.compiled->ns);
@@ -115,38 +146,48 @@
/* submodules cannot be compiled directly */
str = "submodule test {belongs-to xxx {prefix x;}}";
- assert_int_equal(LY_SUCCESS, yang_parse(ctx, str, &mod.parsed));
- assert_int_equal(LY_EINVAL, lys_compile(mod.parsed, 0, &mod.compiled));
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, str, &mod.parsed));
+ assert_int_equal(LY_EINVAL, lys_compile(&mod, 0));
logbuf_assert("Submodules (test) are not supposed to be compiled, compile only the main modules.");
assert_null(mod.compiled);
-
lysp_module_free(mod.parsed);
- ly_ctx_destroy(ctx, NULL);
+
+ /* data definition name collision in top level */
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, "module aa {namespace urn:aa;prefix aa;"
+ "leaf a {type string;} container a{presence x;}}", &mod.parsed));
+ assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
+ logbuf_assert("Duplicate identifier \"a\" of data definition statement.");
+ assert_null(mod.compiled);
+ lysp_module_free(mod.parsed);
+
+ ly_ctx_destroy(ctx.ctx, NULL);
}
static void
test_feature(void **state)
{
- (void) state; /* unused */
+ *state = test_feature;
- struct ly_ctx *ctx;
- struct lys_module mod = {0};
+ struct ly_parser_ctx ctx = {0};
+ struct lys_module mod = {0}, *modp;
const char *str;
struct lysc_feature *f, *f1;
str = "module a {namespace urn:a;prefix a;yang-version 1.1;\n"
"feature f1 {description test1;reference test2;status current;} feature f2; feature f3;\n"
- "feature f4 {if-feature \"f1 or f2\";}\n"
- "feature f5 {if-feature \"f1 and f2\";}\n"
+ "feature orfeature {if-feature \"f1 or f2\";}\n"
+ "feature andfeature {if-feature \"f1 and f2\";}\n"
"feature f6 {if-feature \"not f1\";}\n"
- "feature f7 {if-feature \"(f2 and f3) or (not f1)\";}}";
+ "feature f7 {if-feature \"(f2 and f3) or (not f1)\";}\n"
+ "feature f8 {if-feature \"f1 or f2 or f3 or orfeature or andfeature\";}\n"
+ "feature f9 {if-feature \"not not f1\";}}";
- assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
- assert_int_equal(LY_SUCCESS, yang_parse(ctx, str, &mod.parsed));
- assert_int_equal(LY_SUCCESS, lys_compile(mod.parsed, 0, &mod.compiled));
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx.ctx));
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, str, &mod.parsed));
+ assert_int_equal(LY_SUCCESS, lys_compile(&mod, 0));
assert_non_null(mod.compiled);
assert_non_null(mod.compiled->features);
- assert_int_equal(7, LY_ARRAY_SIZE(mod.compiled->features));
+ assert_int_equal(9, LY_ARRAY_SIZE(mod.compiled->features));
/* all features are disabled by default */
LY_ARRAY_FOR(mod.compiled->features, struct lysc_feature, f) {
assert_int_equal(0, lysc_feature_value(f));
@@ -156,22 +197,22 @@
f1 = &mod.compiled->features[0];
assert_int_equal(1, lysc_feature_value(f1));
- /* enable f4 */
+ /* enable orfeature */
f = &mod.compiled->features[3];
assert_int_equal(0, lysc_feature_value(f));
- assert_int_equal(LY_SUCCESS, lys_feature_enable(&mod, "f4"));
+ assert_int_equal(LY_SUCCESS, lys_feature_enable(&mod, "orfeature"));
assert_int_equal(1, lysc_feature_value(f));
- /* enable f5 - no possible since f2 is disabled */
+ /* enable andfeature - no possible since f2 is disabled */
f = &mod.compiled->features[4];
assert_int_equal(0, lysc_feature_value(f));
- assert_int_equal(LY_EDENIED, lys_feature_enable(&mod, "f5"));
- logbuf_assert("Feature \"f5\" cannot be enabled since it is disabled by its if-feature condition(s).");
+ assert_int_equal(LY_EDENIED, lys_feature_enable(&mod, "andfeature"));
+ logbuf_assert("Feature \"andfeature\" cannot be enabled since it is disabled by its if-feature condition(s).");
assert_int_equal(0, lysc_feature_value(f));
/* first enable f2, so f5 can be enabled then */
assert_int_equal(LY_SUCCESS, lys_feature_enable(&mod, "f2"));
- assert_int_equal(LY_SUCCESS, lys_feature_enable(&mod, "f5"));
+ assert_int_equal(LY_SUCCESS, lys_feature_enable(&mod, "andfeature"));
assert_int_equal(1, lysc_feature_value(f));
/* f1 is enabled, so f6 cannot be enabled */
@@ -181,12 +222,12 @@
logbuf_assert("Feature \"f6\" cannot be enabled since it is disabled by its if-feature condition(s).");
assert_int_equal(0, lysc_feature_value(f));
- /* so disable f1 - f5 will became also disabled */
+ /* so disable f1 - andfeature will became also disabled */
assert_int_equal(1, lysc_feature_value(f1));
assert_int_equal(LY_SUCCESS, lys_feature_disable(&mod, "f1"));
assert_int_equal(0, lysc_feature_value(f1));
assert_int_equal(0, lysc_feature_value(&mod.compiled->features[4]));
- /* while f4 is stille enabled */
+ /* while orfeature is stille enabled */
assert_int_equal(1, lysc_feature_value(&mod.compiled->features[3]));
/* and finally f6 can be enabled */
assert_int_equal(LY_SUCCESS, lys_feature_enable(&mod, "f6"));
@@ -194,17 +235,2074 @@
/* complex evaluation of f7: f1 and f3 are disabled, while f2 is enabled */
assert_int_equal(1, lysc_iffeature_value(&mod.compiled->features[6].iffeatures[0]));
+ /* long evaluation of f8 to need to reallocate internal stack for operators */
+ assert_int_equal(1, lysc_iffeature_value(&mod.compiled->features[7].iffeatures[0]));
+
+ /* double negation of disabled f1 -> disabled */
+ assert_int_equal(0, lysc_iffeature_value(&mod.compiled->features[8].iffeatures[0]));
+
+ /* disable all features */
+ assert_int_equal(LY_SUCCESS, lys_feature_disable(&mod, "*"));
+ LY_ARRAY_FOR(mod.compiled->features, struct lysc_feature, f) {
+ assert_int_equal(0, lys_feature_value(&mod, f->name));
+ }
+ /* re-setting already set feature */
+ assert_int_equal(LY_SUCCESS, lys_feature_disable(&mod, "f1"));
+ assert_int_equal(0, lys_feature_value(&mod, "f1"));
+
+ /* enabling feature that cannot be enabled due to its if-features */
+ assert_int_equal(LY_SUCCESS, lys_feature_enable(&mod, "f1"));
+ assert_int_equal(LY_EDENIED, lys_feature_enable(&mod, "andfeature"));
+ logbuf_assert("Feature \"andfeature\" cannot be enabled since it is disabled by its if-feature condition(s).");
+ assert_int_equal(LY_EDENIED, lys_feature_enable(&mod, "*"));
+ logbuf_assert("Feature \"f6\" cannot be enabled since it is disabled by its if-feature condition(s).");
+ /* test if not changed */
+ assert_int_equal(1, lys_feature_value(&mod, "f1"));
+ assert_int_equal(0, lys_feature_value(&mod, "f2"));
+
+ /* invalid reference */
+ assert_int_equal(LY_EINVAL, lys_feature_enable(&mod, "xxx"));
+ logbuf_assert("Feature \"xxx\" not found in module \"a\".");
lysc_module_free(mod.compiled, NULL);
lysp_module_free(mod.parsed);
+
+ /* some invalid expressions */
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, "module b{yang-version 1.1;namespace urn:b; prefix b; feature f{if-feature f1;}}", &mod.parsed));
+ assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
+ logbuf_assert("Invalid value \"f1\" of if-feature - unable to find feature \"f1\".");
+ lysp_module_free(mod.parsed);
+
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, "module b{yang-version 1.1;namespace urn:b; prefix b; feature f1; feature f2{if-feature 'f and';}}", &mod.parsed));
+ assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
+ logbuf_assert("Invalid value \"f and\" of if-feature - unexpected end of expression.");
+ lysp_module_free(mod.parsed);
+
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, "module b{yang-version 1.1;namespace urn:b; prefix b; feature f{if-feature 'or';}}", &mod.parsed));
+ assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
+ logbuf_assert("Invalid value \"or\" of if-feature - unexpected end of expression.");
+ lysp_module_free(mod.parsed);
+
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, "module b{yang-version 1.1;namespace urn:b; prefix b; feature f1; feature f2{if-feature '(f1';}}", &mod.parsed));
+ assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
+ logbuf_assert("Invalid value \"(f1\" of if-feature - non-matching opening and closing parentheses.");
+ lysp_module_free(mod.parsed);
+
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, "module b{yang-version 1.1;namespace urn:b; prefix b; feature f1; feature f2{if-feature 'f1)';}}", &mod.parsed));
+ assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
+ logbuf_assert("Invalid value \"f1)\" of if-feature - non-matching opening and closing parentheses.");
+ lysp_module_free(mod.parsed);
+
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, "module b{yang-version 1.1;namespace urn:b; prefix b; feature f1; feature f2{if-feature ---;}}", &mod.parsed));
+ assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
+ logbuf_assert("Invalid value \"---\" of if-feature - unable to find feature \"---\".");
+ lysp_module_free(mod.parsed);
+
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, "module b{namespace urn:b; prefix b; feature f1; feature f2{if-feature 'not f1';}}", &mod.parsed));
+ assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
+ logbuf_assert("Invalid value \"not f1\" of if-feature - YANG 1.1 expression in YANG 1.0 module.");
+ lysp_module_free(mod.parsed);
+
+ assert_int_equal(LY_SUCCESS, yang_parse(&ctx, "module b{namespace urn:b; prefix b; feature f1; feature f1;}", &mod.parsed));
+ assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
+ logbuf_assert("Duplicate identifier \"f1\" of feature statement.");
+ lysp_module_free(mod.parsed);
+
+ ly_ctx_set_module_imp_clb(ctx.ctx, test_imp_clb, "submodule sb {belongs-to b {prefix b;} feature f1;}");
+ assert_non_null(modp = lys_parse_mem(ctx.ctx, "module b{namespace urn:b; prefix b; include sb;feature f1;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(modp, 0));
+ logbuf_assert("Duplicate identifier \"f1\" of feature statement.");
+
+ /* import reference */
+ assert_non_null(modp = lys_parse_mem(ctx.ctx, str, LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(modp, 0));
+ assert_int_equal(LY_SUCCESS, lys_feature_enable(modp, "f1"));
+ assert_non_null(modp = lys_parse_mem(ctx.ctx, "module c{namespace urn:c; prefix c; import a {prefix a;} feature f1; feature f2{if-feature 'a:f1';}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(modp, 0));
+ assert_int_equal(LY_SUCCESS, lys_feature_enable(modp, "f2"));
+ assert_int_equal(0, lys_feature_value(modp, "f1"));
+ assert_int_equal(1, lys_feature_value(modp, "f2"));
+
+ *state = NULL;
+ ly_ctx_destroy(ctx.ctx, NULL);
+}
+
+static void
+test_identity(void **state)
+{
+ *state = test_identity;
+
+ 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_int_equal(LY_SUCCESS, lys_compile(mod1, 0));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod2, 0));
+
+ assert_non_null(mod1->compiled);
+ assert_non_null(mod1->compiled->identities);
+ assert_non_null(mod2->compiled);
+ assert_non_null(mod2->compiled->identities);
+
+ assert_non_null(mod1->compiled->identities[0].derived);
+ assert_int_equal(1, LY_ARRAY_SIZE(mod1->compiled->identities[0].derived));
+ assert_ptr_equal(mod1->compiled->identities[0].derived[0], &mod2->compiled->identities[2]);
+ assert_non_null(mod2->compiled->identities[0].derived);
+ assert_int_equal(2, LY_ARRAY_SIZE(mod2->compiled->identities[0].derived));
+ assert_ptr_equal(mod2->compiled->identities[0].derived[0], &mod2->compiled->identities[2]);
+ assert_ptr_equal(mod2->compiled->identities[0].derived[1], &mod2->compiled->identities[3]);
+ assert_non_null(mod2->compiled->identities[1].derived);
+ assert_int_equal(1, LY_ARRAY_SIZE(mod2->compiled->identities[1].derived));
+ assert_ptr_equal(mod2->compiled->identities[1].derived[0], &mod2->compiled->identities[2]);
+ assert_non_null(mod2->compiled->identities[2].derived);
+ 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_non_null(mod1 = lys_parse_mem(ctx, "module c{namespace urn:c; prefix c; identity i1;identity i1;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod1, 0));
+ 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_non_null(mod1 = lys_parse_mem(ctx, "module d{namespace urn:d; prefix d; include sd;identity i1;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod1, 0));
+ logbuf_assert("Duplicate identifier \"i1\" of identity statement.");
+
+ *state = NULL;
ly_ctx_destroy(ctx, NULL);
}
+static void
+test_node_container(void **state)
+{
+ (void) state; /* unused */
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_node_container *cont;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;container c;}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ assert_non_null(mod->compiled);
+ assert_non_null((cont = (struct lysc_node_container*)mod->compiled->data));
+ assert_int_equal(LYS_CONTAINER, cont->nodetype);
+ assert_string_equal("c", cont->name);
+ assert_true(cont->flags & LYS_CONFIG_W);
+ assert_true(cont->flags & LYS_STATUS_CURR);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;container c {config false; status deprecated; container child;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ logbuf_assert("Missing explicit \"deprecated\" status that was already specified in parent, inheriting.");
+ assert_non_null(mod->compiled);
+ assert_non_null((cont = (struct lysc_node_container*)mod->compiled->data));
+ assert_true(cont->flags & LYS_CONFIG_R);
+ assert_true(cont->flags & LYS_STATUS_DEPRC);
+ assert_non_null((cont = (struct lysc_node_container*)cont->child));
+ assert_int_equal(LYS_CONTAINER, cont->nodetype);
+ assert_true(cont->flags & LYS_CONFIG_R);
+ assert_true(cont->flags & LYS_STATUS_DEPRC);
+ assert_string_equal("child", cont->name);
+
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_node_leaflist(void **state)
+{
+ *state = test_node_leaflist;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+ struct lysc_node_leaflist *ll;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;"
+ "typedef mytype {type union {type leafref {path ../target;} type string;}}"
+ "leaf-list ll1 {type union {type decimal64 {fraction-digits 2;} type mytype;}}"
+ "leaf-list ll2 {type leafref {path ../target;}}"
+ "leaf target {type int8;}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_UNION, type->basetype);
+ assert_non_null(((struct lysc_type_union*)type)->types);
+ assert_int_equal(3, LY_ARRAY_SIZE(((struct lysc_type_union*)type)->types));
+ assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union*)type)->types[0]->basetype);
+ assert_int_equal(LY_TYPE_LEAFREF, ((struct lysc_type_union*)type)->types[1]->basetype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union*)type)->types[2]->basetype);
+ assert_non_null(((struct lysc_type_leafref*)((struct lysc_type_union*)type)->types[1])->realtype);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref*)((struct lysc_type_union*)type)->types[1])->realtype->basetype);
+ type = ((struct lysc_node_leaf*)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref*)type)->realtype->basetype);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;leaf-list ll {type string;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ assert_non_null(mod->compiled);
+ assert_non_null((ll = (struct lysc_node_leaflist*)mod->compiled->data));
+ assert_int_equal(0, ll->min);
+ assert_int_equal((uint32_t)-1, ll->max);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module c {yang-version 1.1;namespace urn:c;prefix c;typedef mytype {type int8;default 10;}"
+ "leaf-list ll1 {type mytype;default 1; default 1; config false;}"
+ "leaf-list ll2 {type mytype; ordered-by user;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ assert_non_null(mod->compiled);
+ assert_non_null((ll = (struct lysc_node_leaflist*)mod->compiled->data));
+ assert_non_null(ll->dflts);
+ assert_int_equal(3, ll->type->refcount);
+ assert_int_equal(2, LY_ARRAY_SIZE(ll->dflts));
+ assert_string_equal("1", ll->dflts[0]);
+ assert_string_equal("1", ll->dflts[1]);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, ll->flags);
+ assert_non_null((ll = (struct lysc_node_leaflist*)mod->compiled->data->next));
+ assert_non_null(ll->dflts);
+ assert_int_equal(3, ll->type->refcount);
+ assert_int_equal(1, LY_ARRAY_SIZE(ll->dflts));
+ assert_string_equal("10", ll->dflts[0]);
+ assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, ll->flags);
+
+ /* ordered-by is ignored for state data, RPC/action output parameters and notification content
+ * TODO test also, RPC output parameters and notification content */
+ assert_non_null(mod = lys_parse_mem(ctx, "module d {yang-version 1.1;namespace urn:d;prefix d;"
+ "leaf-list ll {config false; type string; ordered-by user;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ /* but warning is present: */
+ logbuf_assert("The ordered-by statement is ignored in lists representing state data, RPC/action output parameters or notification content ().");
+ assert_non_null(mod->compiled);
+ assert_non_null((ll = (struct lysc_node_leaflist*)mod->compiled->data));
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, ll->flags);
+
+ /* invalid */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;leaf-list ll {type empty;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {yang-version 1.1;namespace urn:bb;prefix bb;leaf-list ll {type empty; default x;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Leaf-list of type \"empty\" must not have a default value (x).");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;"
+ "leaf-list ll {config false;type string; default one;default two;default one;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ assert_non_null(mod->compiled);
+ assert_non_null((ll = (struct lysc_node_leaflist*)mod->compiled->data));
+ assert_non_null(ll->dflts);
+ assert_int_equal(3, LY_ARRAY_SIZE(ll->dflts));
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;"
+ "leaf-list ll {type string; default one;default two;default one;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Configuration leaf-list has multiple defaults of the same value \"one\".");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_node_list(void **state)
+{
+ *state = test_node_list;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_node_list *list;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;feature f;"
+ "list l1 {key \"x y\"; ordered-by user; leaf x {type string; when 1;}leaf y{type string;if-feature f;}}"
+ "list l2 {config false;leaf value {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ list = (struct lysc_node_list*)mod->compiled->data;
+ assert_non_null(list);
+ assert_non_null(list->keys);
+ assert_int_equal(2, LY_ARRAY_SIZE(list->keys));
+ assert_string_equal("x", list->keys[0]->name);
+ assert_string_equal("y", list->keys[1]->name);
+ assert_non_null(list->child);
+ assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_USER, list->flags);
+ assert_true(list->child->flags & LYS_KEY);
+ assert_true(list->child->next->flags & LYS_KEY);
+ list = (struct lysc_node_list*)mod->compiled->data->next;
+ assert_non_null(list);
+ assert_null(list->keys);
+ assert_non_null(list->child);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, list->flags);
+ assert_false(list->child->flags & LYS_KEY);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;"
+ "list l {key a; unique \"a c/b:b\"; unique \"c/e d\";"
+ "leaf a {type string; default x;} leaf d {type string;config false;}"
+ "container c {leaf b {type string;}leaf e{type string;config false;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ list = (struct lysc_node_list*)mod->compiled->data;
+ assert_non_null(list);
+ assert_string_equal("l", list->name);
+ assert_non_null(list->keys);
+ assert_int_equal(1, LY_ARRAY_SIZE(list->keys));
+ assert_string_equal("a", list->keys[0]->name);
+ assert_true(list->keys[0]->flags & LYS_KEY);
+ assert_null(list->keys[0]->dflt);
+ assert_non_null(list->uniques);
+ assert_int_equal(2, LY_ARRAY_SIZE(list->uniques));
+ assert_int_equal(2, LY_ARRAY_SIZE(list->uniques[0]));
+ assert_string_equal("a", list->uniques[0][0]->name);
+ assert_true(list->uniques[0][0]->flags & LYS_UNIQUE);
+ assert_string_equal("b", list->uniques[0][1]->name);
+ assert_true(list->uniques[0][1]->flags & LYS_UNIQUE);
+ assert_int_equal(2, LY_ARRAY_SIZE(list->uniques[1]));
+ assert_string_equal("e", list->uniques[1][0]->name);
+ assert_true(list->uniques[1][0]->flags & LYS_UNIQUE);
+ assert_string_equal("d", list->uniques[1][1]->name);
+ assert_true(list->uniques[1][1]->flags & LYS_UNIQUE);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module c {yang-version 1.1;namespace urn:c;prefix c;"
+ "list l {key a;leaf a {type empty;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ list = (struct lysc_node_list*)mod->compiled->data;
+ assert_non_null(list);
+ assert_string_equal("l", list->name);
+ assert_non_null(list->keys);
+ assert_int_equal(1, LY_ARRAY_SIZE(list->keys));
+ assert_string_equal("a", list->keys[0]->name);
+ assert_true(list->keys[0]->flags & LYS_KEY);
+ assert_int_equal(LY_TYPE_EMPTY, list->keys[0]->type->basetype);
+
+ /* invalid */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;list l;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing key in list representing configuration data.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {yang-version 1.1; namespace urn:bb;prefix bb;"
+ "list l {key x; leaf x {type string; when 1;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("List's key \"x\" must not have any \"when\" statement.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;feature f;"
+ "list l {key x; leaf x {type string; if-feature f;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("List's key \"x\" must not have any \"if-feature\" statement.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd;"
+ "list l {key x; leaf x {type string; config false;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Key of the configuration list must not be status leaf.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee;"
+ "list l {config false;key x; leaf x {type string; config true;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Configuration node cannot be child of any state data node.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;"
+ "list l {key x; leaf-list x {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("The list's key \"x\" not found.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;"
+ "list l {key x; unique y;leaf x {type string;} leaf-list y {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Unique's descendant-schema-nodeid \"y\" refers to a leaf-list node instead of a leaf.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module hh {namespace urn:hh;prefix hh;"
+ "list l {key x; unique \"x y\";leaf x {type string;} leaf y {config false; type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Unique statement \"x y\" refers to leafs with different config type.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ii {namespace urn:ii;prefix ii;"
+ "list l {key x; unique a:x;leaf x {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid descendant-schema-nodeid value \"a:x\" - prefix \"a\" not defined in module \"ii\".");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module jj {namespace urn:jj;prefix jj;"
+ "list l {key x; unique c/x;leaf x {type string;}container c {leaf y {type string;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid descendant-schema-nodeid value \"c/x\" - target node not found.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module kk {namespace urn:kk;prefix kk;"
+ "list l {key x; unique c^y;leaf x {type string;}container c {leaf y {type string;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid descendant-schema-nodeid value \"c^\" - missing \"/\" as node-identifier separator.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ll {namespace urn:ll;prefix ll;"
+ "list l {key \"x y x\";leaf x {type string;}leaf y {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Duplicated key identifier \"x\".");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module mm {namespace urn:mm;prefix mm;"
+ "list l {key x;leaf x {type empty;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Key of a list can be of type \"empty\" only in YANG 1.1 modules.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_node_choice(void **state)
+{
+ *state = test_node_choice;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_node_choice *ch;
+ struct lysc_node_case *cs;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;feature f;"
+ "choice ch {default a:b; case a {leaf a1 {type string;}leaf a2 {type string;}}"
+ "leaf b {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ ch = (struct lysc_node_choice*)mod->compiled->data;
+ assert_non_null(ch);
+ assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR | LYS_SET_DFLT, ch->flags);
+ cs = ch->cases;
+ assert_non_null(cs);
+ assert_string_equal("a", cs->name);
+ assert_ptr_equal(ch, cs->parent);
+ assert_non_null(cs->child);
+ assert_string_equal("a1", cs->child->name);
+ assert_non_null(cs->child->next);
+ assert_string_equal("a2", cs->child->next->name);
+ assert_ptr_equal(cs, cs->child->parent);
+ cs = (struct lysc_node_case*)cs->next;
+ assert_non_null(cs);
+ assert_string_equal("b", cs->name);
+ assert_ptr_equal(ch, cs->parent);
+ assert_non_null(cs->child);
+ assert_string_equal("b", cs->child->name);
+ assert_ptr_equal(cs, cs->child->parent);
+ assert_ptr_equal(ch->cases->child->next, cs->child->prev);
+ assert_ptr_equal(ch->cases->child->next->next, cs->child);
+ assert_ptr_equal(ch->cases->child->prev, cs->child);
+ assert_ptr_equal(ch->dflt, cs);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;"
+ "choice ch {case a {leaf x {type string;}}leaf x {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Duplicate identifier \"x\" of data definition statement.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa2 {namespace urn:aa2;prefix aa;"
+ "choice ch {case a {leaf y {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Duplicate identifier \"y\" of data definition statement.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;"
+ "choice ch {case a {leaf x {type string;}}leaf a {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Duplicate identifier \"a\" of case statement.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb2 {namespace urn:bb2;prefix bb;"
+ "choice ch {case b {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Duplicate identifier \"b\" of case statement.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ca {namespace urn:ca;prefix ca;"
+ "choice ch {default c;case a {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Default case \"c\" not found.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module cb {namespace urn:cb;prefix cb; import a {prefix a;}"
+ "choice ch {default a:a;case a {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid default case referencing a case from different YANG module (by prefix \"a\").");
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc;"
+ "choice ch {default a;case a {leaf x {mandatory true;type string;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Mandatory node \"x\" under the default case \"a\".");
+ /* TODO check with mandatory nodes from augment placed into the case */
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_node_anydata(void **state)
+{
+ *state = test_node_anydata;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_node_anydata *any;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {yang-version 1.1;namespace urn:a;prefix a;"
+ "anydata any {config false;mandatory true;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ any = (struct lysc_node_anydata*)mod->compiled->data;
+ assert_non_null(any);
+ assert_int_equal(LYS_ANYDATA, any->nodetype);
+ assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_MAND_TRUE, any->flags);
+
+ logbuf_clean();
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;"
+ "anyxml any;}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ any = (struct lysc_node_anydata*)mod->compiled->data;
+ assert_non_null(any);
+ assert_int_equal(LYS_ANYXML, any->nodetype);
+ assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR, any->flags);
+ logbuf_assert("Use of anyxml to define configuration data is not recommended."); /* warning */
+
+ /* invalid */
+ assert_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;anydata any;}", LYS_IN_YANG));
+ logbuf_assert("Invalid keyword \"anydata\" as a child of \"module\" - the statement is allowed only in YANG 1.1 modules. Line number 1.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+/**
+ * actually the same as length restriction (tested in test_type_length()), so just check the correct handling in appropriate types,
+ * do not test the expression itself
+ */
+static void
+test_type_range(void **state)
+{
+ *state = test_type_range;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;leaf l {type int8 {range min..10|max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INT8, type->basetype);
+ assert_non_null(((struct lysc_type_num*)type)->range);
+ assert_non_null(((struct lysc_type_num*)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_num*)type)->range->parts));
+ assert_int_equal(-128, ((struct lysc_type_num*)type)->range->parts[0].min_64);
+ assert_int_equal(10, ((struct lysc_type_num*)type)->range->parts[0].max_64);
+ assert_int_equal(127, ((struct lysc_type_num*)type)->range->parts[1].min_64);
+ assert_int_equal(127, ((struct lysc_type_num*)type)->range->parts[1].max_64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;leaf l {type int16 {range min..10|max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INT16, type->basetype);
+ assert_non_null(((struct lysc_type_num*)type)->range);
+ assert_non_null(((struct lysc_type_num*)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_num*)type)->range->parts));
+ assert_int_equal(-32768, ((struct lysc_type_num*)type)->range->parts[0].min_64);
+ assert_int_equal(10, ((struct lysc_type_num*)type)->range->parts[0].max_64);
+ assert_int_equal(32767, ((struct lysc_type_num*)type)->range->parts[1].min_64);
+ assert_int_equal(32767, ((struct lysc_type_num*)type)->range->parts[1].max_64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module c {namespace urn:c;prefix c;leaf l {type int32 {range min..10|max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INT32, type->basetype);
+ assert_non_null(((struct lysc_type_num*)type)->range);
+ assert_non_null(((struct lysc_type_num*)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_num*)type)->range->parts));
+ assert_int_equal(INT64_C(-2147483648), ((struct lysc_type_num*)type)->range->parts[0].min_64);
+ assert_int_equal(10, ((struct lysc_type_num*)type)->range->parts[0].max_64);
+ assert_int_equal(INT64_C(2147483647), ((struct lysc_type_num*)type)->range->parts[1].min_64);
+ assert_int_equal(INT64_C(2147483647), ((struct lysc_type_num*)type)->range->parts[1].max_64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module d {namespace urn:d;prefix d;leaf l {type int64 {range min..10|max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INT64, type->basetype);
+ assert_non_null(((struct lysc_type_num*)type)->range);
+ assert_non_null(((struct lysc_type_num*)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_num*)type)->range->parts));
+ assert_int_equal(INT64_C(-9223372036854775807) - INT64_C(1), ((struct lysc_type_num*)type)->range->parts[0].min_64);
+ assert_int_equal(10, ((struct lysc_type_num*)type)->range->parts[0].max_64);
+ assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_num*)type)->range->parts[1].min_64);
+ assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_num*)type)->range->parts[1].max_64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module e {namespace urn:e;prefix e;leaf l {type uint8 {range min..10|max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_UINT8, type->basetype);
+ assert_non_null(((struct lysc_type_num*)type)->range);
+ assert_non_null(((struct lysc_type_num*)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_num*)type)->range->parts));
+ assert_int_equal(0, ((struct lysc_type_num*)type)->range->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_num*)type)->range->parts[0].max_u64);
+ assert_int_equal(255, ((struct lysc_type_num*)type)->range->parts[1].min_u64);
+ assert_int_equal(255, ((struct lysc_type_num*)type)->range->parts[1].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module f {namespace urn:f;prefix f;leaf l {type uint16 {range min..10|max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_UINT16, type->basetype);
+ assert_non_null(((struct lysc_type_num*)type)->range);
+ assert_non_null(((struct lysc_type_num*)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_num*)type)->range->parts));
+ assert_int_equal(0, ((struct lysc_type_num*)type)->range->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_num*)type)->range->parts[0].max_u64);
+ assert_int_equal(65535, ((struct lysc_type_num*)type)->range->parts[1].min_u64);
+ assert_int_equal(65535, ((struct lysc_type_num*)type)->range->parts[1].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module g {namespace urn:g;prefix g;leaf l {type uint32 {range min..10|max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_UINT32, type->basetype);
+ assert_non_null(((struct lysc_type_num*)type)->range);
+ assert_non_null(((struct lysc_type_num*)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_num*)type)->range->parts));
+ assert_int_equal(0, ((struct lysc_type_num*)type)->range->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_num*)type)->range->parts[0].max_u64);
+ assert_int_equal(UINT64_C(4294967295), ((struct lysc_type_num*)type)->range->parts[1].min_u64);
+ assert_int_equal(UINT64_C(4294967295), ((struct lysc_type_num*)type)->range->parts[1].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module h {namespace urn:h;prefix h;leaf l {type uint64 {range min..10|max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_UINT64, type->basetype);
+ assert_non_null(((struct lysc_type_num*)type)->range);
+ assert_non_null(((struct lysc_type_num*)type)->range->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_num*)type)->range->parts));
+ assert_int_equal(0, ((struct lysc_type_num*)type)->range->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_num*)type)->range->parts[0].max_u64);
+ assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_num*)type)->range->parts[1].min_u64);
+ assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_num*)type)->range->parts[1].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module i {namespace urn:i;prefix i;typedef mytype {type uint8 {range 10..100;}}"
+ "typedef mytype2 {type mytype;} leaf l {type mytype2;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(3, type->refcount);
+ assert_int_equal(LY_TYPE_UINT8, type->basetype);
+ assert_non_null(((struct lysc_type_num*)type)->range);
+ assert_non_null(((struct lysc_type_num*)type)->range->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_num*)type)->range->parts));
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_type_length(void **state)
+{
+ *state = test_type_length;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;leaf l {type binary {length min {error-app-tag errortag;error-message error;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_string_equal("errortag", ((struct lysc_type_bin*)type)->length->eapptag);
+ assert_string_equal("error", ((struct lysc_type_bin*)type)->length->emsg);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(0, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(0, ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;leaf l {type binary {length max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module c {namespace urn:c;prefix c;leaf l {type binary {length min..max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(0, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(UINT64_C(18446744073709551615), ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module d {namespace urn:d;prefix d;leaf l {type binary {length 5;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(5, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(5, ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module e {namespace urn:e;prefix e;leaf l {type binary {length 1..10;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(1, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module f {namespace urn:f;prefix f;leaf l {type binary {length 1..10|20..30;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(1, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+ assert_int_equal(20, ((struct lysc_type_bin*)type)->length->parts[1].min_u64);
+ assert_int_equal(30, ((struct lysc_type_bin*)type)->length->parts[1].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module g {namespace urn:g;prefix g;leaf l {type binary {length \"16 | 32\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(16, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(16, ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+ assert_int_equal(32, ((struct lysc_type_bin*)type)->length->parts[1].min_u64);
+ assert_int_equal(32, ((struct lysc_type_bin*)type)->length->parts[1].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module h {namespace urn:h;prefix h;typedef mytype {type binary {length 10;}}"
+ "leaf l {type mytype {length \"10\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module i {namespace urn:i;prefix i;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length \"50\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(50, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(50, ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module j {namespace urn:j;prefix j;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length \"10..30|60..100\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(30, ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+ assert_int_equal(60, ((struct lysc_type_bin*)type)->length->parts[1].min_u64);
+ assert_int_equal(100, ((struct lysc_type_bin*)type)->length->parts[1].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module k {namespace urn:k;prefix k;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length \"10..80\";}}leaf ll {type mytype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(80, ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+ type = ((struct lysc_node_leaf*)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(2, type->refcount);
+ assert_non_null(((struct lysc_type_bin*)type)->length);
+ assert_non_null(((struct lysc_type_bin*)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_bin*)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_bin*)type)->length->parts[0].min_u64);
+ assert_int_equal(100, ((struct lysc_type_bin*)type)->length->parts[0].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module l {namespace urn:l;prefix l;typedef mytype {type string {length 10..100;}}"
+ "typedef mytype2 {type mytype {pattern '[0-9]*';}} leaf l {type mytype2 {pattern '[0-4]*';}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_int_equal(1, type->refcount);
+ assert_non_null(((struct lysc_type_str*)type)->length);
+ assert_non_null(((struct lysc_type_str*)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_str*)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_str*)type)->length->parts[0].min_u64);
+ assert_int_equal(100, ((struct lysc_type_str*)type)->length->parts[0].max_u64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module m {namespace urn:m;prefix m;typedef mytype {type string {length 10;}}"
+ "leaf l {type mytype {length min..max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_int_equal(1, type->refcount);
+ assert_non_null(((struct lysc_type_str*)type)->length);
+ assert_non_null(((struct lysc_type_str*)type)->length->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_str*)type)->length->parts));
+ assert_int_equal(10, ((struct lysc_type_str*)type)->length->parts[0].min_u64);
+ assert_int_equal(10, ((struct lysc_type_str*)type)->length->parts[0].max_u64);
+
+ /* invalid values */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;leaf l {type binary {length -10;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - value \"-10\" does not fit the type limitations.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;leaf l {type binary {length 18446744073709551616;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - invalid value \"18446744073709551616\".");
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc;leaf l {type binary {length \"max .. 10\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - unexpected data after max keyword (.. 10).");
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd;leaf l {type binary {length 50..10;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - values are not in ascending order (10).");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee;leaf l {type binary {length \"50 | 10\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - values are not in ascending order (10).");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;leaf l {type binary {length \"x\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - unexpected data (x).");
+ assert_non_null(mod = lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;leaf l {type binary {length \"50 | min\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - unexpected data before min keyword (50 | ).");
+ assert_non_null(mod = lys_parse_mem(ctx, "module hh {namespace urn:hh;prefix hh;leaf l {type binary {length \"| 50\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - unexpected beginning of the expression (| 50).");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ii {namespace urn:ii;prefix ii;leaf l {type binary {length \"10 ..\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - unexpected end of the expression after \"..\" (10 ..).");
+ assert_non_null(mod = lys_parse_mem(ctx, "module jj {namespace urn:jj;prefix jj;leaf l {type binary {length \".. 10\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - unexpected \"..\" without a lower bound.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module kk {namespace urn:kk;prefix kk;leaf l {type binary {length \"10 |\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - unexpected end of the expression (10 |).");
+ assert_non_null(mod = lys_parse_mem(ctx, "module kl {namespace urn:kl;prefix kl;leaf l {type binary {length \"10..20 | 15..30\";}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - values are not in ascending order (15).");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ll {namespace urn:ll;prefix ll;typedef mytype {type binary {length 10;}}"
+ "leaf l {type mytype {length 11;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - the derived restriction (11) is not equally or more limiting.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module mm {namespace urn:mm;prefix mm;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length 1..11;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - the derived restriction (1..11) is not equally or more limiting.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module nn {namespace urn:nn;prefix nn;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length 20..110;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - the derived restriction (20..110) is not equally or more limiting.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module oo {namespace urn:oo;prefix oo;typedef mytype {type binary {length 10..100;}}"
+ "leaf l {type mytype {length 20..30|110..120;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - the derived restriction (20..30|110..120) is not equally or more limiting.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module pp {namespace urn:pp;prefix pp;typedef mytype {type binary {length 10..11;}}"
+ "leaf l {type mytype {length 15;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - the derived restriction (15) is not equally or more limiting.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module qq {namespace urn:qq;prefix qq;typedef mytype {type binary {length 10..20|30..40;}}"
+ "leaf l {type mytype {length 15..35;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - the derived restriction (15..35) is not equally or more limiting.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module rr {namespace urn:rr;prefix rr;typedef mytype {type binary {length 10;}}"
+ "leaf l {type mytype {length 10..35;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid length restriction - the derived restriction (10..35) is not equally or more limiting.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ss {namespace urn:rr;prefix rr;leaf l {type binary {pattern '[0-9]*';}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid type restrictions for binary type.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_type_pattern(void **state)
+{
+ *state = test_type_pattern;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {yang-version 1.1; namespace urn:a;prefix a;leaf l {type string {"
+ "pattern .* {error-app-tag errortag;error-message error;}"
+ "pattern [0-9].*[0-9] {modifier invert-match;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_str*)type)->patterns);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_str*)type)->patterns));
+ assert_string_equal("errortag", ((struct lysc_type_str*)type)->patterns[0]->eapptag);
+ assert_string_equal("error", ((struct lysc_type_str*)type)->patterns[0]->emsg);
+ assert_int_equal(0, ((struct lysc_type_str*)type)->patterns[0]->inverted);
+ assert_null(((struct lysc_type_str*)type)->patterns[1]->eapptag);
+ assert_null(((struct lysc_type_str*)type)->patterns[1]->emsg);
+ assert_int_equal(1, ((struct lysc_type_str*)type)->patterns[1]->inverted);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;typedef mytype {type string {pattern '[0-9]*';}}"
+ "typedef mytype2 {type mytype {length 10;}} leaf l {type mytype2 {pattern '[0-4]*';}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_int_equal(1, type->refcount);
+ assert_non_null(((struct lysc_type_str*)type)->patterns);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_str*)type)->patterns));
+ assert_int_equal(3, ((struct lysc_type_str*)type)->patterns[0]->refcount);
+ assert_int_equal(1, ((struct lysc_type_str*)type)->patterns[1]->refcount);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module c {namespace urn:c;prefix c;typedef mytype {type string {pattern '[0-9]*';}}"
+ "leaf l {type mytype {length 10;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_int_equal(1, type->refcount);
+ assert_non_null(((struct lysc_type_str*)type)->patterns);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_str*)type)->patterns));
+ assert_int_equal(2, ((struct lysc_type_str*)type)->patterns[0]->refcount);
+
+ /* test substitutions */
+ assert_non_null(mod = lys_parse_mem(ctx, "module d {namespace urn:d;prefix d;leaf l {type string {"
+ "pattern '^\\p{IsLatinExtended-A}$';}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_non_null(((struct lysc_type_str*)type)->patterns);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_str*)type)->patterns));
+ /* TODO check some data "^ř$" */
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_type_enum(void **state)
+{
+ *state = test_type_enum;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {yang-version 1.1; namespace urn:a;prefix a;feature f; leaf l {type enumeration {"
+ "enum automin; enum min {value -2147483648;}enum one {if-feature f; value 1;}"
+ "enum two; enum seven {value 7;}enum eight;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_ENUM, type->basetype);
+ assert_non_null(((struct lysc_type_enum*)type)->enums);
+ assert_int_equal(6, LY_ARRAY_SIZE(((struct lysc_type_enum*)type)->enums));
+ assert_non_null(((struct lysc_type_enum*)type)->enums[2].iffeatures);
+ assert_string_equal("automin", ((struct lysc_type_enum*)type)->enums[0].name);
+ assert_int_equal(0, ((struct lysc_type_enum*)type)->enums[0].value);
+ assert_string_equal("min", ((struct lysc_type_enum*)type)->enums[1].name);
+ assert_int_equal(-2147483648, ((struct lysc_type_enum*)type)->enums[1].value);
+ assert_string_equal("one", ((struct lysc_type_enum*)type)->enums[2].name);
+ assert_int_equal(1, ((struct lysc_type_enum*)type)->enums[2].value);
+ assert_string_equal("two", ((struct lysc_type_enum*)type)->enums[3].name);
+ assert_int_equal(2, ((struct lysc_type_enum*)type)->enums[3].value);
+ assert_string_equal("seven", ((struct lysc_type_enum*)type)->enums[4].name);
+ assert_int_equal(7, ((struct lysc_type_enum*)type)->enums[4].value);
+ assert_string_equal("eight", ((struct lysc_type_enum*)type)->enums[5].name);
+ assert_int_equal(8, ((struct lysc_type_enum*)type)->enums[5].value);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {yang-version 1.1; namespace urn:b;prefix b;feature f; typedef mytype {type enumeration {"
+ "enum 11; enum min {value -2147483648;}enum x$&;"
+ "enum two; enum seven {value 7;}enum eight;}} leaf l { type mytype {enum seven;enum eight;}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_ENUM, type->basetype);
+ assert_non_null(((struct lysc_type_enum*)type)->enums);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_enum*)type)->enums));
+ assert_string_equal("seven", ((struct lysc_type_enum*)type)->enums[0].name);
+ assert_int_equal(7, ((struct lysc_type_enum*)type)->enums[0].value);
+ assert_string_equal("eight", ((struct lysc_type_enum*)type)->enums[1].name);
+ assert_int_equal(8, ((struct lysc_type_enum*)type)->enums[1].value);
+
+
+ /* invalid cases */
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; feature f; leaf l {type enumeration {"
+ "enum one {if-feature f;}}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid keyword \"if-feature\" as a child of \"enum\" - the statement is allowed only in YANG 1.1 modules. Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum one {value -2147483649;}}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid value \"-2147483649\" of \"value\". Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum one {value 2147483648;}}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid value \"2147483648\" of \"value\". Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum one; enum one;}}}", LYS_IN_YANG));
+ logbuf_assert("Duplicate identifier \"one\" of enum statement. Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum '';}}}", LYS_IN_YANG));
+ logbuf_assert("Enum name must not be zero-length. Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum ' x';}}}", LYS_IN_YANG));
+ logbuf_assert("Enum name must not have any leading or trailing whitespaces (\" x\"). Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum 'x ';}}}", LYS_IN_YANG));
+ logbuf_assert("Enum name must not have any leading or trailing whitespaces (\"x \"). Line number 1.");
+ assert_non_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
+ "enum 'inva\nlid';}}}", LYS_IN_YANG));
+ logbuf_assert("Control characters in enum name should be avoided (\"inva\nlid\", character number 5).");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb; leaf l {type enumeration;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing enum substatement for enumeration type.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;typedef mytype {type enumeration {enum one;}}"
+ "leaf l {type mytype {enum two;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid enumeration - derived type adds new item \"two\".");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;typedef mytype {type enumeration {enum one;}}"
+ "leaf l {type mytype {enum one {value 1;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid enumeration - value of the item \"one\" has changed from 0 to 1 in the derived type.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee;leaf l {type enumeration {enum x {value 2147483647;}enum y;}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid enumeration - it is not possible to auto-assign enum value for \"y\" since the highest value is already 2147483647.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;leaf l {type enumeration {enum x {value 1;}enum y {value 1;}}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid enumeration - value 1 collide in items \"y\" and \"x\".");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;typedef mytype {type enumeration;}"
+ "leaf l {type mytype {enum one;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing enum substatement for enumeration type mytype.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module hh {namespace urn:hh;prefix hh; typedef mytype {type enumeration {enum one;}}"
+ "leaf l {type mytype {enum one;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Enumeration type can be subtyped only in YANG 1.1 modules.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_type_bits(void **state)
+{
+ *state = test_type_bits;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {yang-version 1.1; namespace urn:a;prefix a;feature f; leaf l {type bits {"
+ "bit automin; bit one {if-feature f; position 1;}"
+ "bit two; bit seven {position 7;}bit eight;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_BITS, type->basetype);
+ assert_non_null(((struct lysc_type_bits*)type)->bits);
+ assert_int_equal(5, LY_ARRAY_SIZE(((struct lysc_type_bits*)type)->bits));
+ assert_non_null(((struct lysc_type_bits*)type)->bits[1].iffeatures);
+ assert_string_equal("automin", ((struct lysc_type_bits*)type)->bits[0].name);
+ assert_int_equal(0, ((struct lysc_type_bits*)type)->bits[0].position);
+ assert_string_equal("one", ((struct lysc_type_bits*)type)->bits[1].name);
+ assert_int_equal(1, ((struct lysc_type_bits*)type)->bits[1].position);
+ assert_string_equal("two", ((struct lysc_type_bits*)type)->bits[2].name);
+ assert_int_equal(2, ((struct lysc_type_bits*)type)->bits[2].position);
+ assert_string_equal("seven", ((struct lysc_type_bits*)type)->bits[3].name);
+ assert_int_equal(7, ((struct lysc_type_bits*)type)->bits[3].position);
+ assert_string_equal("eight", ((struct lysc_type_bits*)type)->bits[4].name);
+ assert_int_equal(8, ((struct lysc_type_bits*)type)->bits[4].position);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {yang-version 1.1;namespace urn:b;prefix b;feature f; typedef mytype {type bits {"
+ "bit automin; bit one;bit two; bit seven {value 7;}bit eight;}} leaf l { type mytype {bit eight;bit seven;bit automin;}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_BITS, type->basetype);
+ assert_non_null(((struct lysc_type_bits*)type)->bits);
+ assert_int_equal(3, LY_ARRAY_SIZE(((struct lysc_type_bits*)type)->bits));
+ assert_string_equal("automin", ((struct lysc_type_bits*)type)->bits[0].name);
+ assert_int_equal(0, ((struct lysc_type_bits*)type)->bits[0].position);
+ assert_string_equal("seven", ((struct lysc_type_bits*)type)->bits[1].name);
+ assert_int_equal(7, ((struct lysc_type_bits*)type)->bits[1].position);
+ assert_string_equal("eight", ((struct lysc_type_bits*)type)->bits[2].name);
+ assert_int_equal(8, ((struct lysc_type_bits*)type)->bits[2].position);
+
+
+ /* invalid cases */
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; feature f; leaf l {type bits {"
+ "bit one {if-feature f;}}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid keyword \"if-feature\" as a child of \"bit\" - the statement is allowed only in YANG 1.1 modules. Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type bits {"
+ "bit one {position -1;}}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid value \"-1\" of \"position\". Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type bits {"
+ "bit one {value 4294967296;}}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid value \"4294967296\" of \"value\". Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type bits {"
+ "bit one; bit one;}}}", LYS_IN_YANG));
+ logbuf_assert("Duplicate identifier \"one\" of bit statement. Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type bits {"
+ "bit '11';}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid identifier first character '1'. Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type bits {"
+ "bit 'x1$1';}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid identifier character '$'. Line number 1.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb; leaf l {type bits;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing bit substatement for bits type.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;typedef mytype {type bits {bit one;}}"
+ "leaf l {type mytype {bit two;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid bits - derived type adds new item \"two\".");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;typedef mytype {type bits {bit one;}}"
+ "leaf l {type mytype {bit one {position 1;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid bits - position of the item \"one\" has changed from 0 to 1 in the derived type.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee;leaf l {type bits {bit x {position 4294967295;}bit y;}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid bits - it is not possible to auto-assign bit position for \"y\" since the highest value is already 4294967295.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;leaf l {type bits {bit x {value 1;}bit y {value 1;}}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid bits - position 1 collide in items \"y\" and \"x\".");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;typedef mytype {type bits;}"
+ "leaf l {type mytype {bit one;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing bit substatement for bits type mytype.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module hh {namespace urn:hh;prefix hh; typedef mytype {type bits {bit one;}}"
+ "leaf l {type mytype {bit one;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Bits type can be subtyped only in YANG 1.1 modules.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_type_dec64(void **state)
+{
+ *state = test_type_dec64;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;leaf l {type decimal64 {"
+ "fraction-digits 2;range min..max;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_DEC64, type->basetype);
+ assert_int_equal(2, ((struct lysc_type_dec*)type)->fraction_digits);
+ assert_non_null(((struct lysc_type_dec*)type)->range);
+ assert_non_null(((struct lysc_type_dec*)type)->range->parts);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_dec*)type)->range->parts));
+ assert_int_equal(INT64_C(-9223372036854775807) - INT64_C(1), ((struct lysc_type_dec*)type)->range->parts[0].min_64);
+ assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_dec*)type)->range->parts[0].max_64);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;typedef mytype {type decimal64 {"
+ "fraction-digits 2;range '3.14 | 5.1 | 10';}}leaf l {type mytype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_DEC64, type->basetype);
+ assert_int_equal(2, ((struct lysc_type_dec*)type)->fraction_digits);
+ assert_non_null(((struct lysc_type_dec*)type)->range);
+ assert_non_null(((struct lysc_type_dec*)type)->range->parts);
+ assert_int_equal(3, LY_ARRAY_SIZE(((struct lysc_type_dec*)type)->range->parts));
+ assert_int_equal(314, ((struct lysc_type_dec*)type)->range->parts[0].min_64);
+ assert_int_equal(314, ((struct lysc_type_dec*)type)->range->parts[0].max_64);
+ assert_int_equal(510, ((struct lysc_type_dec*)type)->range->parts[1].min_64);
+ assert_int_equal(510, ((struct lysc_type_dec*)type)->range->parts[1].max_64);
+ assert_int_equal(1000, ((struct lysc_type_dec*)type)->range->parts[2].min_64);
+ assert_int_equal(1000, ((struct lysc_type_dec*)type)->range->parts[2].max_64);
+
+ /* invalid cases */
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 0;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid value \"0\" of \"fraction-digits\". Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits -1;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid value \"-1\" of \"fraction-digits\". Line number 1.");
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 19;}}}", LYS_IN_YANG));
+ logbuf_assert("Value \"19\" is out of \"fraction-digits\" bounds. Line number 1.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing fraction-digits substatement for decimal64 type.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ab {namespace urn:ab;prefix ab; typedef mytype {type decimal64;}leaf l {type mytype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing fraction-digits substatement for decimal64 type mytype.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb; leaf l {type decimal64 {fraction-digits 2;"
+ "range '3.142';}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Range boundary \"3.142\" of decimal64 type exceeds defined number (2) of fraction digits.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc; leaf l {type decimal64 {fraction-digits 2;"
+ "range '4 | 3.14';}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid range restriction - values are not in ascending order (3.14).");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd; typedef mytype {type decimal64 {fraction-digits 2;}}"
+ "leaf l {type mytype {fraction-digits 3;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module de {namespace urn:de;prefix de; typedef mytype {type decimal64 {fraction-digits 2;}}"
+ "typedef mytype2 {type mytype {fraction-digits 3;}}leaf l {type mytype2;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid fraction-digits substatement for type \"mytype2\" not directly derived from decimal64 built-in type.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_type_instanceid(void **state)
+{
+ *state = test_type_instanceid;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;typedef mytype {type instance-identifier {require-instance false;}}"
+ "leaf l1 {type instance-identifier {require-instance true;}}"
+ "leaf l2 {type mytype;} leaf l3 {type instance-identifier;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INST, type->basetype);
+ assert_int_equal(1, ((struct lysc_type_instanceid*)type)->require_instance);
+
+ type = ((struct lysc_node_leaf*)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INST, type->basetype);
+ assert_int_equal(0, ((struct lysc_type_instanceid*)type)->require_instance);
+
+ type = ((struct lysc_node_leaf*)mod->compiled->data->next->next)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_INST, type->basetype);
+ assert_int_equal(1, ((struct lysc_type_instanceid*)type)->require_instance);
+
+ /* invalid cases */
+ assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type instance-identifier {require-instance yes;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid value \"yes\" of \"require-instance\". Line number 1.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type instance-identifier {fraction-digits 1;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid type restrictions for instance-identifier type.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_type_identityref(void **state)
+{
+ *state = test_type_identityref;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {yang-version 1.1;namespace urn:a;prefix a;identity i; identity j; identity k {base i;}"
+ "typedef mytype {type identityref {base i;}}"
+ "leaf l1 {type mytype;} leaf l2 {type identityref {base a:k; base j;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_IDENT, type->basetype);
+ assert_non_null(((struct lysc_type_identityref*)type)->bases);
+ assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_identityref*)type)->bases));
+ assert_string_equal("i", ((struct lysc_type_identityref*)type)->bases[0]->name);
+
+ type = ((struct lysc_node_leaf*)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_IDENT, type->basetype);
+ assert_non_null(((struct lysc_type_identityref*)type)->bases);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_identityref*)type)->bases));
+ assert_string_equal("k", ((struct lysc_type_identityref*)type)->bases[0]->name);
+ assert_string_equal("j", ((struct lysc_type_identityref*)type)->bases[1]->name);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {yang-version 1.1;namespace urn:b;prefix b;import a {prefix a;}"
+ "leaf l {type identityref {base a:k; base a:j;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_IDENT, type->basetype);
+ assert_non_null(((struct lysc_type_identityref*)type)->bases);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_identityref*)type)->bases));
+ assert_string_equal("k", ((struct lysc_type_identityref*)type)->bases[0]->name);
+ assert_string_equal("j", ((struct lysc_type_identityref*)type)->bases[1]->name);
+
+ /* invalid cases */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type identityref;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing base substatement for identityref type.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb; typedef mytype {type identityref;}"
+ "leaf l {type mytype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing base substatement for identityref type mytype.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc; identity i; typedef mytype {type identityref {base i;}}"
+ "leaf l {type mytype {base i;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid base substatement for the type not directly derived from identityref built-in type.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd; identity i; typedef mytype {type identityref {base i;}}"
+ "typedef mytype2 {type mytype {base i;}}leaf l {type mytype2;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid base substatement for the type \"mytype2\" not directly derived from identityref built-in type.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee; identity i; identity j;"
+ "leaf l {type identityref {base i;base j;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Multiple bases in identityref type are allowed only in YANG 1.1 modules.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff; identity i;leaf l {type identityref {base j;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Unable to find base (j) of identityref.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;leaf l {type identityref {base x:j;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid prefix used for base (x:j) of identityref.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_type_leafref(void **state)
+{
+ *state = test_type_leafref;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+ const char *path, *name, *prefix;
+ size_t prefix_len, name_len;
+ int parent_times, has_predicate;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ /* lys_path_token() */
+ path = "invalid_path";
+ parent_times = 0;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ path = "..";
+ parent_times = 0;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ path = "..[";
+ parent_times = 0;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ path = "../";
+ parent_times = 0;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ path = "/";
+ parent_times = 0;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+
+ path = "../../pref:id/xxx[predicate]/invalid!!!";
+ parent_times = 0;
+ assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ assert_string_equal("/xxx[predicate]/invalid!!!", path);
+ assert_int_equal(4, prefix_len);
+ assert_int_equal(0, strncmp("pref", prefix, prefix_len));
+ assert_int_equal(2, name_len);
+ assert_int_equal(0, strncmp("id", name, name_len));
+ assert_int_equal(2, parent_times);
+ assert_int_equal(0, has_predicate);
+ assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ assert_string_equal("[predicate]/invalid!!!", path);
+ assert_int_equal(0, prefix_len);
+ assert_null(prefix);
+ assert_int_equal(3, name_len);
+ assert_int_equal(0, strncmp("xxx", name, name_len));
+ assert_int_equal(1, has_predicate);
+ path += 11;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ assert_string_equal("!!!", path);
+ assert_int_equal(0, prefix_len);
+ assert_null(prefix);
+ assert_int_equal(7, name_len);
+ assert_int_equal(0, strncmp("invalid", name, name_len));
+
+ path = "/absolute/prefix:path";
+ parent_times = 0;
+ assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ assert_string_equal("/prefix:path", path);
+ assert_int_equal(0, prefix_len);
+ assert_null(prefix);
+ assert_int_equal(8, name_len);
+ assert_int_equal(0, strncmp("absolute", name, name_len));
+ assert_int_equal(-1, parent_times);
+ assert_int_equal(0, has_predicate);
+ assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ assert_int_equal(0, *path);
+ assert_int_equal(6, prefix_len);
+ assert_int_equal(0, strncmp("prefix", prefix, prefix_len));
+ assert_int_equal(4, name_len);
+ assert_int_equal(0, strncmp("path", name, name_len));
+ assert_int_equal(0, has_predicate);
+
+ /* complete leafref paths */
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {yang-version 1.1;namespace urn:a;prefix a;"
+ "leaf ref1 {type leafref {path /a:target1;}} leaf ref2 {type leafref {path /a/target2; require-instance false;}}"
+ "leaf target1 {type string;}container a {leaf target2 {type uint8;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/a:target1", ((struct lysc_type_leafref*)type)->path);
+ assert_ptr_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+ assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref*)type)->realtype->basetype);
+ assert_int_equal(1, ((struct lysc_type_leafref*)type)->require_instance);
+ type = ((struct lysc_node_leaf*)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/a/target2", ((struct lysc_type_leafref*)type)->path);
+ assert_ptr_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+ assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+ assert_int_equal(LY_TYPE_UINT8, ((struct lysc_type_leafref*)type)->realtype->basetype);
+ assert_int_equal(0, ((struct lysc_type_leafref*)type)->require_instance);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b; typedef mytype {type leafref {path /b:target;}}"
+ "typedef mytype2 {type mytype;} typedef mytype3 {type leafref {path /target;}} leaf ref {type mytype2;}"
+ "leaf target {type leafref {path ../realtarget;}} leaf realtarget {type string;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/b:target", ((struct lysc_type_leafref* )type)->path);
+ assert_ptr_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+ assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref*)type)->realtype->basetype);
+ assert_int_equal(1, ((struct lysc_type_leafref* )type)->require_instance);
+
+ /* prefixes are reversed to check using correct context of the path! */
+ assert_non_null(mod = lys_parse_mem(ctx, "module c {yang-version 1.1;namespace urn:c;prefix b; import b {prefix c;}"
+ "typedef mytype3 {type c:mytype {require-instance false;}}"
+ "leaf ref1 {type b:mytype3;}leaf ref2 {type c:mytype2;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/b:target", ((struct lysc_type_leafref* )type)->path);
+ assert_ptr_not_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+ assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref*)type)->realtype->basetype);
+ assert_int_equal(0, ((struct lysc_type_leafref* )type)->require_instance);
+ type = ((struct lysc_node_leaf*)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/b:target", ((struct lysc_type_leafref* )type)->path);
+ assert_ptr_not_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+ assert_int_equal(1, ((struct lysc_type_leafref* )type)->require_instance);
+
+ /* non-prefixed nodes in path are supposed to be from the module where the leafref type is instantiated */
+ assert_non_null(mod = lys_parse_mem(ctx, "module d {namespace urn:d;prefix d; import b {prefix b;}"
+ "leaf ref {type b:mytype3;}leaf target {type int8;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/target", ((struct lysc_type_leafref* )type)->path);
+ assert_ptr_not_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+ assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref*)type)->realtype->basetype);
+ assert_int_equal(1, ((struct lysc_type_leafref* )type)->require_instance);
+
+ /* conditional leafrefs */
+ assert_non_null(mod = lys_parse_mem(ctx, "module e {yang-version 1.1;namespace urn:e;prefix e;feature f1; feature f2;"
+ "leaf ref1 {if-feature 'f1 and f2';type leafref {path /target;}}"
+ "leaf target {if-feature f1; type boolean;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/target", ((struct lysc_type_leafref* )type)->path);
+ assert_ptr_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+ assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+ assert_int_equal(LY_TYPE_BOOL, ((struct lysc_type_leafref*)type)->realtype->basetype);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module f {namespace urn:f;prefix f;"
+ "list interface{key name;leaf name{type string;}list address {key ip;leaf ip {type string;}}}"
+ "container default-address{leaf ifname{type leafref{ path \"../../interface/name\";}}"
+ "leaf address {type leafref{ path \"../../interface[ name = current()/../ifname ]/address/ip\";}}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)(*lysc_node_children_p(mod->compiled->data->prev))->prev)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("../../interface[ name = current()/../ifname ]/address/ip", ((struct lysc_type_leafref* )type)->path);
+ assert_ptr_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+ assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref*)type)->realtype->basetype);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module g {namespace urn:g;prefix g;"
+ "leaf source {type leafref {path \"/endpoint-parent[id=current()/../field]/endpoint/name\";}}"
+ "leaf field {type int32;}list endpoint-parent {key id;leaf id {type int32;}"
+ "list endpoint {key name;leaf name {type string;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/endpoint-parent[id=current()/../field]/endpoint/name", ((struct lysc_type_leafref* )type)->path);
+ assert_ptr_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+ assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_leafref*)type)->realtype->basetype);
+
+ /* invalid paths */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path ../a/invalid;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path - unable to find \"../a/invalid\".");
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path ../../toohigh;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path \"../../toohigh\" - too many \"..\" in the path.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path /a:invalid;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path - unable to find module connected with the prefix of the node \"/a:invalid\".");
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd;leaf target1 {type string;}container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path '/a[target2 = current()/../target1]/target2';}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path - node \"/a\" is expected to be a list, but it is container.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path /a!invalid;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path at character 3 (/a!invalid).");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path /a;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path \"/a\" - target node is container instead of leaf or leaf-list.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;container a {leaf target2 {type uint8; status deprecated;}}"
+ "leaf ref1 {type leafref {path /a/target2;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("A current definition \"ref1\" is not allowed to reference deprecated definition \"target2\".");
+ assert_non_null(mod = lys_parse_mem(ctx, "module hh {namespace urn:hh;prefix hh;"
+ "leaf ref1 {type leafref;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing path substatement for leafref type.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ii {namespace urn:ii;prefix ii;typedef mytype {type leafref;}"
+ "leaf ref1 {type mytype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing path substatement for leafref type mytype.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module jj {namespace urn:jj;prefix jj;feature f;"
+ "leaf ref {type leafref {path /target;}}leaf target {if-feature f;type string;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path \"/target\" - set of features applicable to the leafref target is not a subset of features "
+ "applicable to the leafref itself.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module kk {namespace urn:kk;prefix kk;"
+ "leaf ref {type leafref {path /target;}}leaf target {type string;config false;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path \"/target\" - target is supposed to represent configuration data (as the leafref does), but it does not.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ll {namespace urn:ll;prefix ll;"
+ "leaf ref {type leafref {path /target; require-instance true;}}leaf target {type string;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module mm {namespace urn:mm;prefix mm;typedef mytype {type leafref {path /target;require-instance false;}}"
+ "leaf ref {type mytype;}leaf target {type string;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Leafref type \"mytype\" can be restricted by require-instance statement only in YANG 1.1 modules.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module nn {namespace urn:nn;prefix nn;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}"
+ "leaf address {type leafref{ path \"/interface[name is current()/../ifname]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name i\" - missing \"=\" after node-identifier.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module oo {namespace urn:oo;prefix oo;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}"
+ "leaf address {type leafref{ path \"/interface[name=current()/../ifname/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name=current()/../ifname/ip\" - missing predicate termination.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module pp {namespace urn:pp;prefix pp;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}"
+ "leaf address {type leafref{ path \"/interface[x:name=current()/../ifname]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[x:name=current()/../ifname]\" - prefix \"x\" not defined in module \"pp\".");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module qq {namespace urn:qq;prefix qq;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}"
+ "leaf address {type leafref{ path \"/interface[id=current()/../ifname]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[id=current()/../ifname]\" - predicate's key node \"id\" not found.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module rr {namespace urn:rr;prefix rr;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}"
+ "leaf address {type leafref{ path \"/interface[name=current() / .. / ifname][name=current()/../test]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name=current()/../test]\" - multiple equality tests for the key \"name\".");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ss {namespace urn:ss;prefix ss;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}"
+ "leaf address {type leafref{ path \"/interface[name = ../ifname]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name = ../ifname]\" - missing current-function-invocation.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module tt {namespace urn:tt;prefix tt;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}"
+ "leaf address {type leafref{ path \"/interface[name = current()../ifname]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name = current()../ifname]\" - missing \"/\" after current-function-invocation.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module uu {namespace urn:uu;prefix uu;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}"
+ "leaf address {type leafref{ path \"/interface[name = current()/..ifname]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name = current()/..ifname]\" - missing \"/\" in \"../\" rel-path-keyexpr pattern.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module vv {namespace urn:vv;prefix vv;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}"
+ "leaf address {type leafref{ path \"/interface[name = current()/ifname]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name = current()/ifname]\" - at least one \"..\" is expected in rel-path-keyexpr.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module ww {namespace urn:ww;prefix ww;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}"
+ "leaf address {type leafref{ path \"/interface[name = current()/../]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name = current()/../]\" - at least one node-identifier is expected in rel-path-keyexpr.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module xx {namespace urn:xx;prefix xx;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}"
+ "leaf address {type leafref{ path \"/interface[name = current()/../$node]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid node identifier in leafref path predicate - character 22 (of [name = current()/../$node]).");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module yy {namespace urn:yy;prefix yy;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}"
+ "leaf address {type leafref{ path \"/interface[name=current()/../x:ifname]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name=current()/../x:ifname]\" - unable to find module of the node \"ifname\" in rel-path_keyexpr.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module zz {namespace urn:zz;prefix zz;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}"
+ "leaf address {type leafref{ path \"/interface[name=current()/../xxx]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name=current()/../xxx]\" - unable to find node \"current()/../xxx\" in the rel-path_keyexpr.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module zza {namespace urn:zza;prefix zza;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}container c;"
+ "leaf address {type leafref{ path \"/interface[name=current()/../c]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[name=current()/../c]\" - rel-path_keyexpr \"current()/../c\" refers container instead of leaf.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module zzb {namespace urn:zzb;prefix zzb;"
+ "list interface{key name;leaf name{type string;}leaf ip {type string;}container c;}"
+ "leaf ifname{type leafref{ path \"../interface/name\";}}"
+ "leaf address {type leafref{ path \"/interface[c=current()/../ifname]/ip\";}}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path predicate \"[c=current()/../ifname]\" - predicate's key node \"c\" not found.");
+
+ /* circular chain */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aaa {namespace urn:aaa;prefix aaa;"
+ "leaf ref1 {type leafref {path /ref2;}}"
+ "leaf ref2 {type leafref {path /ref3;}}"
+ "leaf ref3 {type leafref {path /ref4;}}"
+ "leaf ref4 {type leafref {path /ref1;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path \"/ref1\" - circular chain of leafrefs detected.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_type_empty(void **state)
+{
+ *state = test_type_empty;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ /* invalid */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;"
+ "leaf l {type empty; default x;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Leaf of type \"empty\" must not have a default value (x).");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;typedef mytype {type empty; default x;}"
+ "leaf l {type mytype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid type \"mytype\" - \"empty\" type must not have a default value (x).");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+
+static void
+test_type_union(void **state)
+{
+ *state = test_type_union;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {yang-version 1.1;namespace urn:a;prefix a; typedef mybasetype {type string;}"
+ "typedef mytype {type union {type int8; type mybasetype;}}"
+ "leaf l {type mytype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(2, type->refcount);
+ assert_int_equal(LY_TYPE_UNION, type->basetype);
+ assert_non_null(((struct lysc_type_union*)type)->types);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_union*)type)->types));
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_union*)type)->types[0]->basetype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union*)type)->types[1]->basetype);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {yang-version 1.1;namespace urn:b;prefix b; typedef mybasetype {type string;}"
+ "typedef mytype {type union {type int8; type mybasetype;}}"
+ "leaf l {type union {type decimal64 {fraction-digits 2;} type mytype;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_UNION, type->basetype);
+ assert_non_null(((struct lysc_type_union*)type)->types);
+ assert_int_equal(3, LY_ARRAY_SIZE(((struct lysc_type_union*)type)->types));
+ assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union*)type)->types[0]->basetype);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_union*)type)->types[1]->basetype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union*)type)->types[2]->basetype);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module c {yang-version 1.1;namespace urn:c;prefix c; typedef mybasetype {type string;}"
+ "typedef mytype {type union {type leafref {path ../target;} type mybasetype;}}"
+ "leaf l {type union {type decimal64 {fraction-digits 2;} type mytype;}}"
+ "leaf target {type leafref {path ../realtarget;}} leaf realtarget {type int8;}}",
+ LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_UNION, type->basetype);
+ assert_non_null(((struct lysc_type_union*)type)->types);
+ assert_int_equal(3, LY_ARRAY_SIZE(((struct lysc_type_union*)type)->types));
+ assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union*)type)->types[0]->basetype);
+ assert_int_equal(LY_TYPE_LEAFREF, ((struct lysc_type_union*)type)->types[1]->basetype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union*)type)->types[2]->basetype);
+ assert_non_null(((struct lysc_type_leafref*)((struct lysc_type_union*)type)->types[1])->realtype);
+ assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref*)((struct lysc_type_union*)type)->types[1])->realtype->basetype);
+
+ /* invalid unions */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;typedef mytype {type union;}"
+ "leaf l {type mytype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing type substatement for union type mytype.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;leaf l {type union;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Missing type substatement for union type.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc;typedef mytype {type union{type int8; type string;}}"
+ "leaf l {type mytype {type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid type substatement for the type not directly derived from union built-in type.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd;typedef mytype {type union{type int8; type string;}}"
+ "typedef mytype2 {type mytype {type string;}}leaf l {type mytype2;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid type substatement for the type \"mytype2\" not directly derived from union built-in type.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_type_dflt(void **state)
+{
+ *state = test_type_union;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ /* default is not inherited from union's types */
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a; typedef mybasetype {type string;default hello;units xxx;}"
+ "leaf l {type union {type decimal64 {fraction-digits 2;} type mybasetype;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(1, type->refcount);
+ assert_int_equal(LY_TYPE_UNION, type->basetype);
+ assert_non_null(((struct lysc_type_union*)type)->types);
+ assert_int_equal(2, LY_ARRAY_SIZE(((struct lysc_type_union*)type)->types));
+ assert_int_equal(LY_TYPE_DEC64, ((struct lysc_type_union*)type)->types[0]->basetype);
+ assert_int_equal(LY_TYPE_STRING, ((struct lysc_type_union*)type)->types[1]->basetype);
+ assert_null(((struct lysc_node_leaf*)mod->compiled->data)->dflt);
+ assert_null(((struct lysc_node_leaf*)mod->compiled->data)->units);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b; typedef mybasetype {type string;default hello;units xxx;}"
+ "leaf l {type mybasetype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(2, type->refcount);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_string_equal("hello", ((struct lysc_node_leaf*)mod->compiled->data)->dflt);
+ assert_string_equal("xxx", ((struct lysc_node_leaf*)mod->compiled->data)->units);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module c {namespace urn:c;prefix c; typedef mybasetype {type string;default hello;units xxx;}"
+ "leaf l {type mybasetype; default goodbye;units yyy;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(2, type->refcount);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_string_equal("goodbye", ((struct lysc_node_leaf*)mod->compiled->data)->dflt);
+ assert_string_equal("yyy", ((struct lysc_node_leaf*)mod->compiled->data)->units);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module d {namespace urn:d;prefix d; typedef mybasetype {type string;default hello;units xxx;}"
+ "typedef mytype {type mybasetype;}leaf l1 {type mytype; default goodbye;units yyy;}"
+ "leaf l2 {type mytype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(4, type->refcount);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_string_equal("goodbye", ((struct lysc_node_leaf*)mod->compiled->data)->dflt);
+ assert_string_equal("yyy", ((struct lysc_node_leaf*)mod->compiled->data)->units);
+ type = ((struct lysc_node_leaf*)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(4, type->refcount);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_string_equal("hello", ((struct lysc_node_leaf*)mod->compiled->data->next)->dflt);
+ assert_string_equal("xxx", ((struct lysc_node_leaf*)mod->compiled->data->next)->units);
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module e {namespace urn:e;prefix e; typedef mybasetype {type string;}"
+ "typedef mytype {type mybasetype; default hello;units xxx;}leaf l {type mytype;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(3, type->refcount);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_string_equal("hello", ((struct lysc_node_leaf*)mod->compiled->data)->dflt);
+ assert_string_equal("xxx", ((struct lysc_node_leaf*)mod->compiled->data)->units);
+
+ /* mandatory leaf does not takes default value from type */
+ assert_non_null(mod = lys_parse_mem(ctx, "module f {namespace urn:f;prefix f;typedef mytype {type string; default hello;units xxx;}"
+ "leaf l {type mytype; mandatory true;}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_STRING, type->basetype);
+ assert_null(((struct lysc_node_leaf*)mod->compiled->data)->dflt);
+ assert_string_equal("xxx", ((struct lysc_node_leaf*)mod->compiled->data)->units);
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_status(void **state)
+{
+ *state = test_status;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;"
+ "container c {status deprecated; leaf l {status current; type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("A \"current\" status is in conflict with the parent's \"deprecated\" status.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;"
+ "container c {status obsolete; leaf l {status current; type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("A \"current\" status is in conflict with the parent's \"obsolete\" status.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc;"
+ "container c {status obsolete; leaf l {status deprecated; type string;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("A \"deprecated\" status is in conflict with the parent's \"obsolete\" status.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_uses(void **state)
+{
+ *state = test_uses;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_node *parent, *child;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module grp {namespace urn:grp;prefix g; typedef mytype {type string;}"
+ "grouping grp {leaf x {type mytype;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;import grp {prefix g;}"
+ "grouping grp_a_top {leaf a1 {type int8;}}"
+ "container a {uses grp_a; uses grp_a_top; uses g:grp; grouping grp_a {leaf a2 {type uint8;}}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ assert_non_null((parent = mod->compiled->data));
+ assert_int_equal(LYS_CONTAINER, parent->nodetype);
+ assert_non_null((child = ((struct lysc_node_container*)parent)->child));
+ assert_string_equal("a2", child->name);
+ assert_ptr_equal(mod, child->module);
+ assert_non_null((child = child->next));
+ assert_string_equal("a1", child->name);
+ assert_ptr_equal(mod, child->module);
+ assert_non_null((child = child->next));
+ assert_string_equal("x", child->name);
+ assert_ptr_equal(mod, child->module);
+
+ /* invalid */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;uses missinggrp;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Grouping \"missinggrp\" referenced by a uses statement not found.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;uses grp;"
+ "grouping grp {leaf a{type string;}uses grp1;}"
+ "grouping grp1 {leaf b {type string;}uses grp2;}"
+ "grouping grp2 {leaf c {type string;}uses grp;}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Grouping \"grp\" references itself through a uses statement.");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc;uses a:missingprefix;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid prefix used for grouping reference (a:missingprefix).");
+
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd;grouping grp{leaf a{type string;}}"
+ "leaf a {type string;}uses grp;}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Duplicate identifier \"a\" of data definition statement.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+
int main(void)
{
const struct CMUnitTest tests[] = {
- cmocka_unit_test_setup(test_module, logger_setup),
- cmocka_unit_test_setup(test_feature, logger_setup),
+ cmocka_unit_test_setup_teardown(test_module, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_feature, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_identity, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_length, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_range, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_pattern, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_enum, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_bits, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_dec64, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_instanceid, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_identityref, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_leafref, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_empty, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_union, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_dflt, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_status, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_node_container, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_node_leaflist, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_node_list, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_node_choice, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_node_anydata, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_uses, logger_setup, logger_teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/src/test_tree_schema_helpers.c b/tests/src/test_tree_schema_helpers.c
index 3dc7d7c..1c5ca3d 100644
--- a/tests/src/test_tree_schema_helpers.c
+++ b/tests/src/test_tree_schema_helpers.c
@@ -12,10 +12,20 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
+#include "../../src/common.c"
+#include "../../src/log.c"
+#include "../../src/set.c"
+#include "../../src/parser_yang.c"
+#include "../../src/tree_schema.c"
+#include "../../src/tree_schema_free.c"
#include "../../src/tree_schema_helpers.c"
+#include "../../src/hash_table.c"
+#include "../../src/xpath.c"
+#include "../../src/context.c"
#include <stdarg.h>
#include <stddef.h>
+#include <stdio.h>
#include <setjmp.h>
#include <cmocka.h>
@@ -23,15 +33,28 @@
#define BUFSIZE 1024
char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+/* set to 0 to printing error messages to stderr instead of checking them in code */
+#define ENABLE_LOGGER_CHECKING 1
+
+#if ENABLE_LOGGER_CHECKING
static void
logger(LY_LOG_LEVEL level, const char *msg, const char *path)
{
(void) level; /* unused */
- (void) path; /* unused */
-
- strncpy(logbuf, msg, BUFSIZE - 1);
+ if (store) {
+ if (path && path[0]) {
+ snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
+ } else {
+ strncpy(logbuf, msg, BUFSIZE - 1);
+ }
+ if (store > 0) {
+ --store;
+ }
+ }
}
+#endif
static int
logger_setup(void **state)
@@ -43,6 +66,24 @@
return 0;
}
+static int
+logger_teardown(void **state)
+{
+ (void) state; /* unused */
+#if ENABLE_LOGGER_CHECKING
+ if (*state) {
+ fprintf(stderr, "%s\n", logbuf);
+ }
+#endif
+ return 0;
+}
+
+void
+logbuf_clean(void)
+{
+ logbuf[0] = '\0';
+}
+
#if ENABLE_LOGGER_CHECKING
# define logbuf_assert(str) assert_string_equal(logbuf, str)
#else
@@ -52,7 +93,7 @@
static void
test_date(void **state)
{
- (void) state; /* unused */
+ *state = test_date;
assert_int_equal(LY_EINVAL, lysp_check_date(NULL, NULL, 0, "date"));
logbuf_assert("Invalid argument date (lysp_check_date()).");
@@ -76,6 +117,8 @@
assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-11-11", 10, "date"));
assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-02-28", 10, "date"));
assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2016-02-29", 10, "date"));
+
+ *state = NULL;
}
static void
@@ -85,15 +128,16 @@
struct lysp_revision *revs = NULL, *rev;
+ logbuf_clean();
/* no error, it just does nothing */
lysp_sort_revisions(NULL);
logbuf_assert("");
/* revisions are stored in wrong order - the newest is the last */
LY_ARRAY_NEW_RET(NULL, revs, rev,);
- strcpy(rev->rev, "2018-01-01");
+ strcpy(rev->date, "2018-01-01");
LY_ARRAY_NEW_RET(NULL, revs, rev,);
- strcpy(rev->rev, "2018-12-31");
+ strcpy(rev->date, "2018-12-31");
assert_int_equal(2, LY_ARRAY_SIZE(revs));
assert_string_equal("2018-01-01", &revs[0]);
@@ -106,11 +150,164 @@
LY_ARRAY_FREE(revs);
}
+static LY_ERR test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name),
+ const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format,
+ const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
+{
+ *module_data = user_data;
+ *format = LYS_IN_YANG;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
+static void
+test_typedef(void **state)
+{
+ *state = test_typedef;
+
+ struct ly_ctx *ctx = NULL;
+ const char *str;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ str = "module a {namespace urn:a; prefix a; typedef binary {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"binary\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef bits {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"bits\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef boolean {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"boolean\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef decimal64 {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"decimal64\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef empty {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"empty\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef enumeration {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"enumeration\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef int8 {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"int8\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef int16 {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"int16\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef int32 {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"int32\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef int64 {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"int64\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef instance-identifier {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"instance-identifier\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef identityref {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"identityref\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef leafref {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"leafref\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef string {type int8;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"string\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef union {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"union\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef uint8 {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"uint8\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef uint16 {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"uint16\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef uint32 {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"uint32\" of typedef - name collision with a built-in type.");
+ str = "module a {namespace urn:a; prefix a; typedef uint64 {type string;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"uint64\" of typedef - name collision with a built-in type.");
+
+ str = "module mytypes {namespace urn:types; prefix t; typedef binary_ {type string;} typedef bits_ {type string;} typedef boolean_ {type string;} "
+ "typedef decimal64_ {type string;} typedef empty_ {type string;} typedef enumeration_ {type string;} typedef int8_ {type string;} typedef int16_ {type string;}"
+ "typedef int32_ {type string;} typedef int64_ {type string;} typedef instance-identifier_ {type string;} typedef identityref_ {type string;}"
+ "typedef leafref_ {type string;} typedef string_ {type int8;} typedef union_ {type string;} typedef uint8_ {type string;} typedef uint16_ {type string;}"
+ "typedef uint32_ {type string;} typedef uint64_ {type string;}}";
+ assert_non_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+
+ str = "module a {namespace urn:a; prefix a; typedef test {type string;} typedef test {type int8;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"test\" of typedef - name collision with another top-level type.");
+
+ str = "module a {namespace urn:a; prefix a; typedef x {type string;} container c {typedef x {type int8;}}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"x\" of typedef - scoped type collide with a top-level type.");
+
+ str = "module a {namespace urn:a; prefix a; container c {container d {typedef y {type int8;}} typedef y {type string;}}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"y\" of typedef - name collision with another scoped type.");
+
+ str = "module a {namespace urn:a; prefix a; container c {typedef y {type int8;} typedef y {type string;}}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"y\" of typedef - name collision with sibling type.");
+
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule b {belongs-to a {prefix a;} typedef x {type string;}}");
+ str = "module a {namespace urn:a; prefix a; include b; typedef x {type int8;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"x\" of typedef - name collision with another top-level type.");
+
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule b {belongs-to a {prefix a;} container c {typedef x {type string;}}}");
+ str = "module a {namespace urn:a; prefix a; include b; typedef x {type int8;}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"x\" of typedef - scoped type collide with a top-level type.");
+
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule b {belongs-to a {prefix a;} typedef x {type int8;}}");
+ str = "module a {namespace urn:a; prefix a; include b; container c {typedef x {type string;}}}";
+ assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
+ logbuf_assert("Invalid name \"x\" of typedef - scoped type collide with a top-level type.");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
+static void
+test_parse_nodeid(void **state)
+{
+ (void) state; /* unused */
+ const char *str;
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
+
+ str = "123";
+ assert_int_equal(LY_EINVAL, lys_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+
+ str = "a12_-.!";
+ assert_int_equal(LY_SUCCESS, lys_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+ assert_null(prefix);
+ assert_int_equal(0, prefix_len);
+ assert_non_null(name);
+ assert_int_equal(6, name_len);
+ assert_int_equal(0, strncmp("a12_-.", name, name_len));
+ assert_string_equal("!", str);
+
+ str = "a12_-.:_b2 xxx";
+ assert_int_equal(LY_SUCCESS, lys_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+ assert_non_null(prefix);
+ assert_int_equal(6, prefix_len);
+ assert_int_equal(0, strncmp("a12_-.", prefix, prefix_len));
+ assert_non_null(name);
+ assert_int_equal(3, name_len);
+ assert_int_equal(0, strncmp("_b2", name, name_len));
+ assert_string_equal(" xxx", str);
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
- cmocka_unit_test_setup(test_date, logger_setup),
+ cmocka_unit_test_setup_teardown(test_date, logger_setup, logger_teardown),
cmocka_unit_test_setup(test_revisions, logger_setup),
+ cmocka_unit_test_setup_teardown(test_typedef, logger_setup, logger_teardown),
+ cmocka_unit_test_setup(test_parse_nodeid, logger_setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/src/test_xml.c b/tests/src/test_xml.c
index 568e55e..33a417f 100644
--- a/tests/src/test_xml.c
+++ b/tests/src/test_xml.c
@@ -12,8 +12,12 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _BSD_SOURCE
-#define _DEFAULT_SOURCE
+#include "common.h"
+#include "../../src/set.c"
+#include "../../src/xml.c"
+#include "../../src/common.c"
+#include "../../src/log.c"
+
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
@@ -23,7 +27,6 @@
#include <string.h>
#include "libyang.h"
-#include "../../src/xml.c"
#define BUFSIZE 1024
char logbuf[BUFSIZE] = {0};