summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/common.c183
-rw-r--r--src/common.h113
-rw-r--r--src/context.c69
-rw-r--r--src/context.h20
-rw-r--r--src/dict.c271
-rw-r--r--src/diff.c488
-rw-r--r--src/hash_table.c770
-rw-r--r--src/hash_table.h174
-rw-r--r--src/hash_table_internal.h146
-rw-r--r--src/json.c669
-rw-r--r--src/json.h89
-rw-r--r--src/libyang.h1
-rw-r--r--src/log.c483
-rw-r--r--src/log.h57
-rw-r--r--src/lyb.c8
-rw-r--r--src/lyb.h2
-rw-r--r--src/out.c14
-rw-r--r--src/parser_common.c176
-rw-r--r--src/parser_data.h108
-rw-r--r--src/parser_internal.h62
-rw-r--r--src/parser_json.c1056
-rw-r--r--src/parser_lyb.c49
-rw-r--r--src/parser_xml.c1018
-rw-r--r--src/parser_yang.c63
-rw-r--r--src/parser_yin.c11
-rw-r--r--src/path.c512
-rw-r--r--src/path.h58
-rw-r--r--src/plugins.c2
-rw-r--r--src/plugins_exts.c11
-rw-r--r--src/plugins_exts.h18
-rw-r--r--src/plugins_exts/nacm.c2
-rw-r--r--src/plugins_exts/schema_mount.c2
-rw-r--r--src/plugins_types.c71
-rw-r--r--src/plugins_types.h11
-rw-r--r--src/plugins_types/binary.c4
-rw-r--r--src/plugins_types/bits.c4
-rw-r--r--src/plugins_types/boolean.c4
-rw-r--r--src/plugins_types/date_and_time.c57
-rw-r--r--src/plugins_types/decimal64.c4
-rw-r--r--src/plugins_types/hex_string.c171
-rw-r--r--src/plugins_types/identityref.c26
-rw-r--r--src/plugins_types/instanceid.c90
-rw-r--r--src/plugins_types/instanceid_keys.c44
-rw-r--r--src/plugins_types/integer.c4
-rw-r--r--src/plugins_types/ipv4_address.c4
-rw-r--r--src/plugins_types/ipv4_address_no_zone.c4
-rw-r--r--src/plugins_types/ipv4_prefix.c4
-rw-r--r--src/plugins_types/ipv6_address.c4
-rw-r--r--src/plugins_types/ipv6_address_no_zone.c4
-rw-r--r--src/plugins_types/ipv6_prefix.c4
-rw-r--r--src/plugins_types/node_instanceid.c30
-rw-r--r--src/plugins_types/string.c32
-rw-r--r--src/plugins_types/union.c89
-rw-r--r--src/plugins_types/xpath1.0.c94
-rw-r--r--src/printer_json.c134
-rw-r--r--src/printer_lyb.c53
-rw-r--r--src/printer_schema.c15
-rw-r--r--src/printer_tree.c50
-rw-r--r--src/printer_xml.c47
-rw-r--r--src/printer_yang.c12
-rw-r--r--src/schema_compile.c361
-rw-r--r--src/schema_compile_amend.c9
-rw-r--r--src/schema_compile_node.c540
-rw-r--r--src/schema_features.c16
-rw-r--r--src/set.c4
-rw-r--r--src/set.h33
-rw-r--r--src/tree_data.c914
-rw-r--r--src/tree_data.h222
-rw-r--r--src/tree_data_common.c458
-rw-r--r--src/tree_data_free.c14
-rw-r--r--src/tree_data_hash.c31
-rw-r--r--src/tree_data_internal.h56
-rw-r--r--src/tree_data_new.c458
-rw-r--r--src/tree_schema.c156
-rw-r--r--src/tree_schema.h17
-rw-r--r--src/tree_schema_common.c158
-rw-r--r--src/tree_schema_internal.h10
-rw-r--r--src/validation.c649
-rw-r--r--src/validation.h3
-rw-r--r--src/xml.c45
-rw-r--r--src/xpath.c473
-rw-r--r--src/xpath.h27
82 files changed, 8048 insertions, 4381 deletions
diff --git a/src/common.c b/src/common.c
index 38f51ea..03dd81c 100644
--- a/src/common.c
+++ b/src/common.c
@@ -176,19 +176,14 @@ ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
uint32_t c, aux;
size_t len;
- if (bytes_read) {
- (*bytes_read) = 0;
- }
-
c = (*input)[0];
- LY_CHECK_RET(!c, LY_EINVAL);
if (!(c & 0x80)) {
/* one byte character */
len = 1;
if ((c < 0x20) && (c != 0x9) && (c != 0xa) && (c != 0xd)) {
- return LY_EINVAL;
+ goto error;
}
} else if ((c & 0xe0) == 0xc0) {
/* two bytes character */
@@ -196,12 +191,12 @@ ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
aux = (*input)[1];
if ((aux & 0xc0) != 0x80) {
- return LY_EINVAL;
+ goto error;
}
c = ((c & 0x1f) << 6) | (aux & 0x3f);
if (c < 0x80) {
- return LY_EINVAL;
+ goto error;
}
} else if ((c & 0xf0) == 0xe0) {
/* three bytes character */
@@ -211,14 +206,14 @@ ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
for (uint64_t i = 1; i <= 2; i++) {
aux = (*input)[i];
if ((aux & 0xc0) != 0x80) {
- return LY_EINVAL;
+ goto error;
}
c = (c << 6) | (aux & 0x3f);
}
if ((c < 0x800) || ((c > 0xd7ff) && (c < 0xe000)) || (c > 0xfffd)) {
- return LY_EINVAL;
+ goto error;
}
} else if ((c & 0xf8) == 0xf0) {
/* four bytes character */
@@ -228,17 +223,17 @@ ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
for (uint64_t i = 1; i <= 3; i++) {
aux = (*input)[i];
if ((aux & 0xc0) != 0x80) {
- return LY_EINVAL;
+ goto error;
}
c = (c << 6) | (aux & 0x3f);
}
if ((c < 0x1000) || (c > 0x10ffff)) {
- return LY_EINVAL;
+ goto error;
}
} else {
- return LY_EINVAL;
+ goto error;
}
(*utf8_char) = c;
@@ -247,6 +242,163 @@ ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
(*bytes_read) = len;
}
return LY_SUCCESS;
+
+error:
+ if (bytes_read) {
+ (*bytes_read) = 0;
+ }
+ return LY_EINVAL;
+}
+
+/**
+ * @brief Check whether an UTF-8 string is equal to a hex string after a bitwise and.
+ *
+ * (input & 0x[arg1][arg3][arg5]...) == 0x[arg2][arg4][arg6]...
+ *
+ * @param[in] input UTF-8 string.
+ * @param[in] bytes Number of bytes to compare.
+ * @param[in] ... 2x @p bytes number of bytes to perform bitwise and and equality operations.
+ * @return Result of the operation.
+ */
+static int
+ly_utf8_and_equal(const char *input, uint8_t bytes, ...)
+{
+ va_list ap;
+ int i, and, byte;
+
+ va_start(ap, bytes);
+ for (i = 0; i < bytes; ++i) {
+ and = va_arg(ap, int);
+ byte = va_arg(ap, int);
+
+ /* compare each byte */
+ if (((uint8_t)input[i] & and) != (uint8_t)byte) {
+ return 0;
+ }
+ }
+ va_end(ap);
+
+ return 1;
+}
+
+/**
+ * @brief Check whether an UTF-8 string is smaller than a hex string.
+ *
+ * input < 0x[arg1][arg2]...
+ *
+ * @param[in] input UTF-8 string.
+ * @param[in] bytes Number of bytes to compare.
+ * @param[in] ... @p bytes number of bytes to compare with.
+ * @return Result of the operation.
+ */
+static int
+ly_utf8_less(const char *input, uint8_t bytes, ...)
+{
+ va_list ap;
+ int i, byte;
+
+ va_start(ap, bytes);
+ for (i = 0; i < bytes; ++i) {
+ byte = va_arg(ap, int);
+
+ /* compare until bytes differ */
+ if ((uint8_t)input[i] > (uint8_t)byte) {
+ return 0;
+ } else if ((uint8_t)input[i] < (uint8_t)byte) {
+ return 1;
+ }
+ }
+ va_end(ap);
+
+ /* equals */
+ return 0;
+}
+
+/**
+ * @brief Check whether an UTF-8 string is greater than a hex string.
+ *
+ * input > 0x[arg1][arg2]...
+ *
+ * @param[in] input UTF-8 string.
+ * @param[in] bytes Number of bytes to compare.
+ * @param[in] ... @p bytes number of bytes to compare with.
+ * @return Result of the operation.
+ */
+static int
+ly_utf8_greater(const char *input, uint8_t bytes, ...)
+{
+ va_list ap;
+ int i, byte;
+
+ va_start(ap, bytes);
+ for (i = 0; i < bytes; ++i) {
+ byte = va_arg(ap, int);
+
+ /* compare until bytes differ */
+ if ((uint8_t)input[i] > (uint8_t)byte) {
+ return 1;
+ } else if ((uint8_t)input[i] < (uint8_t)byte) {
+ return 0;
+ }
+ }
+ va_end(ap);
+
+ /* equals */
+ return 0;
+}
+
+LY_ERR
+ly_checkutf8(const char *input, size_t in_len, size_t *utf8_len)
+{
+ size_t len;
+
+ if (!(input[0] & 0x80)) {
+ /* one byte character */
+ len = 1;
+
+ if (ly_utf8_less(input, 1, 0x20) && (input[0] != 0x9) && (input[0] != 0xa) && (input[0] != 0xd)) {
+ /* invalid control characters */
+ return LY_EINVAL;
+ }
+ } else if (((input[0] & 0xe0) == 0xc0) && (in_len > 1)) {
+ /* two bytes character */
+ len = 2;
+
+ /* (input < 0xC280) || (input > 0xDFBF) || ((input & 0xE0C0) != 0xC080) */
+ if (ly_utf8_less(input, 2, 0xC2, 0x80) || ly_utf8_greater(input, 2, 0xDF, 0xBF) ||
+ !ly_utf8_and_equal(input, 2, 0xE0, 0xC0, 0xC0, 0x80)) {
+ return LY_EINVAL;
+ }
+ } else if (((input[0] & 0xf0) == 0xe0) && (in_len > 2)) {
+ /* three bytes character */
+ len = 3;
+
+ /* (input >= 0xEDA080) && (input <= 0xEDBFBF) */
+ if (!ly_utf8_less(input, 3, 0xED, 0xA0, 0x80) && !ly_utf8_greater(input, 3, 0xED, 0xBF, 0xBF)) {
+ /* reject UTF-16 surrogates */
+ return LY_EINVAL;
+ }
+
+ /* (input < 0xE0A080) || (input > 0xEFBFBF) || ((input & 0xF0C0C0) != 0xE08080) */
+ if (ly_utf8_less(input, 3, 0xE0, 0xA0, 0x80) || ly_utf8_greater(input, 3, 0xEF, 0xBF, 0xBF) ||
+ !ly_utf8_and_equal(input, 3, 0xF0, 0xE0, 0xC0, 0x80, 0xC0, 0x80)) {
+ return LY_EINVAL;
+ }
+ } else if (((input[0] & 0xf8) == 0xf0) && (in_len > 3)) {
+ /* four bytes character */
+ len = 4;
+
+ /* (input < 0xF0908080) || (input > 0xF48FBFBF) || ((input & 0xF8C0C0C0) != 0xF0808080) */
+ if (ly_utf8_less(input, 4, 0xF0, 0x90, 0x80, 0x80) || ly_utf8_greater(input, 4, 0xF4, 0x8F, 0xBF, 0xBF) ||
+ !ly_utf8_and_equal(input, 4, 0xF8, 0xF0, 0xC0, 0x80, 0xC0, 0x80, 0xC0, 0x80)) {
+ return LY_EINVAL;
+ }
+ } else {
+ return LY_EINVAL;
+ }
+
+ *utf8_len = len;
+ return LY_SUCCESS;
}
LY_ERR
@@ -258,6 +410,7 @@ ly_pututf8(char *dst, uint32_t value, size_t *bytes_written)
(value != 0x09) &&
(value != 0x0a) &&
(value != 0x0d)) {
+ /* valid UTF8 but not YANG string character */
return LY_EINVAL;
}
@@ -337,10 +490,10 @@ ly_utf8len(const char *str, size_t bytes)
return len;
}
-size_t
+int
LY_VCODE_INSTREXP_len(const char *str)
{
- size_t len = 0;
+ int len = 0;
if (!str) {
return len;
diff --git a/src/common.h b/src/common.h
index 264fe81..0fedeae 100644
--- a/src/common.h
+++ b/src/common.h
@@ -1,9 +1,10 @@
/**
* @file common.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief common internal definitions for libyang
*
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 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.
@@ -23,7 +24,7 @@
#include "compat.h"
#include "config.h"
#include "context.h"
-#include "hash_table.h"
+#include "hash_table_internal.h"
#include "log.h"
#include "schema_compile.h"
#include "set.h"
@@ -45,17 +46,28 @@ struct lysc_node;
# error "Cannot define THREAD_LOCAL"
#endif
+/** platform-specific environment variable path separator */
+#ifndef _WIN32
+# define PATH_SEPARATOR ":"
+#else
+# define PATH_SEPARATOR ";"
+#endif
+
#define GETMACRO1(_1, NAME, ...) NAME
#define GETMACRO2(_1, _2, NAME, ...) NAME
#define GETMACRO3(_1, _2, _3, NAME, ...) NAME
#define GETMACRO4(_1, _2, _3, _4, NAME, ...) NAME
#define GETMACRO5(_1, _2, _3, _4, _5, NAME, ...) NAME
#define GETMACRO6(_1, _2, _3, _4, _5, _6, NAME, ...) NAME
+#define GETMACRO7(_1, _2, _3, _4, _5, _6, _7, NAME, ...) NAME
/******************************************************************************
* Logger
*****************************************************************************/
+/** size of the last message buffer */
+#define LY_LAST_MSG_SIZE 512
+
extern ATOMIC_T ly_ll;
extern ATOMIC_T ly_log_opts;
@@ -75,7 +87,16 @@ struct ly_log_location_s {
* @param[in] no Error type code.
* @param[in] format Format string to print.
*/
-void ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...);
+void ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...) _FORMAT_PRINTF(4, 5);
+
+/**
+ * @brief Generate data path based on the data and schema nodes stored in the log location.
+ *
+ * @param[in] ctx Context for logging.
+ * @param[out] path Generated data path.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_vlog_build_data_path(const struct ly_ctx *ctx, char **path);
/**
* @brief Print Validation error and store it into the context (if provided).
@@ -85,7 +106,15 @@ void ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char
* @param[in] code Validation error code.
* @param[in] format Format string to print.
*/
-void ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char *format, ...);
+void ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char *format, ...) _FORMAT_PRINTF(4, 5);
+
+/**
+ * @brief Move error items from source to target context replacing any previous ones.
+ *
+ * @param[in] src_ctx Source context to read errors from.
+ * @param[in] trg_ctx Target context to set the errors for.
+ */
+void ly_err_move(struct ly_ctx *src_ctx, struct ly_ctx *trg_ctx);
/**
* @brief Logger's location data setter.
@@ -110,6 +139,21 @@ void ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnod
void ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t path_steps, uint32_t in_steps);
/**
+ * @brief Get the stored data node for logging at the index.
+ *
+ * @param[in] idx Index of the data node.
+ * @return Logged data node, NULL if out of range.
+ */
+const struct lyd_node *ly_log_location_dnode(uint32_t idx);
+
+/**
+ * @brief Get the count of stored data nodes for logging.
+ *
+ * @return Count of the data nodes.
+ */
+uint32_t ly_log_location_dnode_count(void);
+
+/**
* @brief Update location data for logger, not provided arguments (NULLs) are kept (does not override).
*
* @param[in] SCNODE Compiled schema node.
@@ -201,8 +245,10 @@ void ly_log_dbg(uint32_t group, const char *format, ...);
LY_CHECK_ARG_RET1(CTX, ARG4, RETVAL)
#define LY_CHECK_ARG_RET5(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL) LY_CHECK_ARG_RET4(CTX, ARG1, ARG2, ARG3, ARG4, RETVAL);\
LY_CHECK_ARG_RET1(CTX, ARG5, RETVAL)
-#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO6(__VA_ARGS__, LY_CHECK_ARG_RET5, LY_CHECK_ARG_RET4, LY_CHECK_ARG_RET3, \
- LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1, DUMMY) (CTX, __VA_ARGS__)
+#define LY_CHECK_ARG_RET6(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, RETVAL) LY_CHECK_ARG_RET5(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL);\
+ LY_CHECK_ARG_RET1(CTX, ARG6, RETVAL)
+#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO7(__VA_ARGS__, LY_CHECK_ARG_RET6, LY_CHECK_ARG_RET5, LY_CHECK_ARG_RET4, \
+ LY_CHECK_ARG_RET3, LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1, DUMMY) (CTX, __VA_ARGS__)
#define LY_CHECK_CTX_EQUAL_RET2(CTX1, CTX2, RETVAL) if ((CTX1) && (CTX2) && ((CTX1) != (CTX2))) \
{LOGERR(CTX1, LY_EINVAL, "Different contexts mixed in a single function call."); return RETVAL;}
@@ -212,11 +258,12 @@ void ly_log_dbg(uint32_t group, const char *format, ...);
DUMMY) (CTX, __VA_ARGS__)
/* count sequence size for LY_VCODE_INCHILDSTMT validation error code */
-size_t LY_VCODE_INSTREXP_len(const char *str);
+int LY_VCODE_INSTREXP_len(const char *str);
+
/* default maximum characters to print in LY_VCODE_INCHILDSTMT */
#define LY_VCODE_INSTREXP_MAXLEN 20
-#define LY_VCODE_INCHAR LYVE_SYNTAX, "Invalid character 0x%x."
+#define LY_VCODE_INCHAR LYVE_SYNTAX, "Invalid character 0x%hhx."
#define LY_VCODE_INSTREXP LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected %s."
#define LY_VCODE_EOF LYVE_SYNTAX, "Unexpected end-of-input."
#define LY_VCODE_NTERM LYVE_SYNTAX, "%s not terminated."
@@ -300,10 +347,18 @@ size_t LY_VCODE_INSTREXP_len(const char *str);
*****************************************************************************/
/**
+ * @brief Context error hash table record.
+ */
+struct ly_ctx_err_rec {
+ struct ly_err_item *err; /** pointer to the error items, if any */
+ pthread_t tid; /** pthread thread ID */
+};
+
+/**
* @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_dict 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 loaded YANG schemas */
ly_module_imp_clb imp_clb; /**< optional callback for retrieving missing included or imported models */
@@ -316,7 +371,7 @@ struct ly_ctx {
ly_ext_data_clb ext_clb; /**< optional callback for providing extension-specific run-time data for extensions */
void *ext_clb_data; /**< optional private data for ::ly_ctx.ext_clb */
- pthread_key_t errlist_key; /**< key for the thread-specific list of errors related to the context */
+ struct ly_ht *err_ht; /**< hash table of thread-specific list of errors related to the context */
pthread_mutex_t lyb_hash_lock; /**< lock for storing LYB schema hashes in schema nodes */
};
@@ -359,7 +414,6 @@ struct lys_module *ly_ctx_get_module_implemented2(const struct ly_ctx *ctx, cons
*
* @param[in] ptr Memory to reallocate.
* @param[in] size New size of the memory block.
- *
* @return Pointer to the new memory, NULL on error.
*/
void *ly_realloc(void *ptr, size_t size);
@@ -428,7 +482,8 @@ LY_ERR ly_strntou8(const char *nptr, size_t len, uint8_t *ret);
* If no string remains, it is set to NULL.
* @return LY_ERR value.
*/
-LY_ERR ly_value_prefix_next(const char *str_begin, const char *str_end, uint32_t *len, ly_bool *is_prefix, const char **str_next);
+LY_ERR ly_value_prefix_next(const char *str_begin, const char *str_end, uint32_t *len, ly_bool *is_prefix,
+ const char **str_next);
/**
* @brief Wrapper around strlen() to handle NULL strings.
@@ -485,7 +540,18 @@ LY_ERR ly_value_prefix_next(const char *str_begin, const char *str_end, uint32_t
LY_ERR ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read);
/**
- * Store UTF-8 character specified as 4byte integer into the dst buffer.
+ * @brief Check an UTF-8 character is valid.
+ *
+ * @param[in] input Input string to process.
+ * @param[in] in_len Bytes left to read in @p input.
+ * @param[out] utf8_len Length of a valid UTF-8 character.
+ * @return LY_SUCCESS on success
+ * @return LY_EINVAL in case of invalid UTF-8 character.
+ */
+LY_ERR ly_checkutf8(const char *input, size_t in_len, size_t *utf8_len);
+
+/**
+ * @brief Store UTF-8 character specified as 4byte integer into the dst buffer.
*
* UTF-8 mapping:
* 00000000 -- 0000007F: 0xxxxxxx
@@ -495,7 +561,7 @@ LY_ERR ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read);
*
* Includes checking for valid characters (following RFC 7950, sec 9.4)
*
- * @param[in, out] dst Destination buffer to store the UTF-8 character, must provide enough space (up to 4 bytes) for storing the UTF-8 character.
+ * @param[in,out] dst Destination buffer to store the UTF-8 character, must provide enough space (up to 4 bytes) for storing the UTF-8 character.
* @param[in] value 32b value of the UTF-8 character to store.
* @param[out] bytes_written Number of bytes written into @p dst (size of the written UTF-8 character).
* @return LY_SUCCESS on success
@@ -505,6 +571,7 @@ LY_ERR ly_pututf8(char *dst, uint32_t value, size_t *bytes_written);
/**
* @brief Get number of characters in the @p str, taking multibyte characters into account.
+ *
* @param[in] str String to examine.
* @param[in] bytes Number of valid bytes that are supposed to be taken into account in @p str.
* This parameter is useful mainly for non NULL-terminated strings. In case of NULL-terminated
@@ -515,6 +582,7 @@ size_t ly_utf8len(const char *str, size_t bytes);
/**
* @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] val_len Length of the @p val_str string.
@@ -533,6 +601,7 @@ LY_ERR ly_parse_int(const char *val_str, size_t val_len, int64_t min, int64_t ma
/**
* @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] val_len Length of the @p val_str string.
@@ -553,7 +622,7 @@ LY_ERR ly_parse_uint(const char *val_str, size_t val_len, uint64_t max, int base
*
* 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[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.
@@ -565,7 +634,7 @@ LY_ERR ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len,
/**
* @brief parse instance-identifier's predicate, supports key-predicate, leaf-list-predicate and pos rules from YANG ABNF Grammar.
*
- * @param[in, out] pred Predicate string (including the leading '[') to parse. The string is updated according to what was parsed
+ * @param[in,out] pred Predicate string (including the leading '[') to parse. The string is updated according to what was parsed
* (even for error case, so it can be used to determine which substring caused failure).
* @param[in] limit Limiting length of the @p pred. Function expects NULL terminated string which is not overread.
* The limit value is not checked with each character, so it can be overread and the failure is detected later.
@@ -610,17 +679,11 @@ LY_ERR ly_munmap(void *addr, size_t length);
/**
* @brief Concatenate formating string to the @p dest.
*
- * @param[in, out] dest String to be concatenated by @p format.
- * Note that the input string can be reallocated during concatenation.
+ * @param[in,out] dest String to be concatenated by @p format.
+ * Note that the input string can be reallocated during concatenation.
* @param[in] format Formating string (as for printf) which is supposed to be added after @p dest.
* @return LY_SUCCESS or LY_EMEM.
*/
-LY_ERR ly_strcat(char **dest, const char *format, ...);
-
-#ifndef _WIN32
-# define PATH_SEPARATOR ":"
-#else
-# define PATH_SEPARATOR ";"
-#endif
+LY_ERR ly_strcat(char **dest, const char *format, ...) _FORMAT_PRINTF(2, 3);
#endif /* LY_COMMON_H_ */
diff --git a/src/context.c b/src/context.c
index 47e63d4..7a14203 100644
--- a/src/context.c
+++ b/src/context.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Context implementations
*
- * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 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.
@@ -228,6 +228,17 @@ cleanup:
return mod;
}
+/**
+ * @brief Hash table value-equal callback for comparing context error hash table record.
+ */
+static ly_bool
+ly_ctx_ht_err_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ struct ly_ctx_err_rec *err1 = val1_p, *err2 = val2_p;
+
+ return !memcmp(&err1->tid, &err2->tid, sizeof err1->tid);
+}
+
LIBYANG_API_DEF LY_ERR
ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx)
{
@@ -251,8 +262,9 @@ ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx)
/* plugins */
LY_CHECK_ERR_GOTO(lyplg_init(), LOGINT(NULL); rc = LY_EINT, cleanup);
- /* initialize thread-specific keys */
- while ((pthread_key_create(&ctx->errlist_key, ly_err_free)) == EAGAIN) {}
+ /* initialize thread-specific error hash table */
+ ctx->err_ht = lyht_new(1, sizeof(struct ly_ctx_err_rec), ly_ctx_ht_err_equal_cb, NULL, 1);
+ LY_CHECK_ERR_GOTO(!ctx->err_ht, rc = LY_EMEM, cleanup);
/* init LYB hash lock */
pthread_mutex_init(&ctx->lyb_hash_lock, NULL);
@@ -657,6 +669,39 @@ ly_ctx_get_change_count(const struct ly_ctx *ctx)
return ctx->change_count;
}
+LIBYANG_API_DEF uint32_t
+ly_ctx_get_modules_hash(const struct ly_ctx *ctx)
+{
+ const struct lys_module *mod;
+ uint32_t i = ly_ctx_internal_modules_count(ctx), hash = 0, fi = 0;
+ struct lysp_feature *f = NULL;
+
+ LY_CHECK_ARG_RET(ctx, ctx, 0);
+
+ while ((mod = ly_ctx_get_module_iter(ctx, &i))) {
+ /* name */
+ hash = lyht_hash_multi(hash, mod->name, strlen(mod->name));
+
+ /* revision */
+ if (mod->revision) {
+ hash = lyht_hash_multi(hash, mod->revision, strlen(mod->revision));
+ }
+
+ /* enabled features */
+ while ((f = lysp_feature_next(f, mod->parsed, &fi))) {
+ if (f->flags & LYS_FENABLED) {
+ hash = lyht_hash_multi(hash, f->name, strlen(f->name));
+ }
+ }
+
+ /* imported/implemented */
+ hash = lyht_hash_multi(hash, (char *)&mod->implemented, sizeof mod->implemented);
+ }
+
+ hash = lyht_hash_multi(hash, NULL, 0);
+ return hash;
+}
+
LIBYANG_API_DEF ly_module_imp_clb
ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data)
{
@@ -1228,6 +1273,19 @@ error:
return ret;
}
+/**
+ * @brief Callback for freeing context error hash table values.
+ *
+ * @param[in] val_p Pointer to a pointer to an error item to free with all the siblings.
+ */
+static void
+ly_ctx_ht_err_rec_free(void *val_p)
+{
+ struct ly_ctx_err_rec *err = val_p;
+
+ ly_err_free(err->err);
+}
+
LIBYANG_API_DEF void
ly_ctx_destroy(struct ly_ctx *ctx)
{
@@ -1260,9 +1318,8 @@ ly_ctx_destroy(struct ly_ctx *ctx)
/* leftover unres */
lys_unres_glob_erase(&ctx->unres);
- /* clean the error list */
- ly_err_clean(ctx, 0);
- pthread_key_delete(ctx->errlist_key);
+ /* clean the error hash table */
+ lyht_free(ctx->err_ht, ly_ctx_ht_err_rec_free);
/* dictionary */
lydict_clean(&ctx->dict);
diff --git a/src/context.h b/src/context.h
index 331a89f..a6367f5 100644
--- a/src/context.h
+++ b/src/context.h
@@ -1,9 +1,10 @@
/**
* @file context.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief internal context structures and functions
*
- * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 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.
@@ -196,6 +197,8 @@ struct ly_ctx;
#define LY_CTX_ENABLE_IMP_FEATURES 0x0100 /**< By default, all features of newly implemented imported modules of
a module that is being loaded are disabled. With this flag they all become
enabled. */
+#define LY_CTX_LEAFREF_EXTENDED 0x0200 /**< By default, path attribute of leafref accepts only path as defined in RFC 7950.
+ By using this option, the path attribute will also allow using XPath functions as deref() */
/** @} contextoptions */
@@ -370,6 +373,18 @@ LIBYANG_API_DECL LY_ERR ly_ctx_unset_options(struct ly_ctx *ctx, uint16_t option
LIBYANG_API_DECL uint16_t ly_ctx_get_change_count(const struct ly_ctx *ctx);
/**
+ * @brief Get the hash of all the modules in the context. Since order of the modules is significant,
+ * even when 2 contexts have the same modules but loaded in a different order, the hash will differ.
+ *
+ * Hash consists of all module names (1), their revisions (2), all enabled features (3), and their
+ * imported/implemented state (4).
+ *
+ * @param[in] ctx Context to be examined.
+ * @return Context modules hash.
+ */
+LIBYANG_API_DECL uint32_t ly_ctx_get_modules_hash(const struct ly_ctx *ctx);
+
+/**
* @brief Callback for freeing returned module data in #ly_module_imp_clb.
*
* @param[in] module_data Data to free.
@@ -630,7 +645,8 @@ LIBYANG_API_DECL struct lys_module *ly_ctx_load_module(struct ly_ctx *ctx, const
* If the data identifier can be limited to the existence and changes of this context, the following
* last 2 parameters can be used:
*
- * "%u" as @p content_id_format and ::ly_ctx_get_change_count() as its parameter.
+ * "%u" as @p content_id_format and ::ly_ctx_get_change_count() as its parameter;
+ * "%u" as @p content_id_format and ::ly_ctx_get_modules_hash() as its parameter.
*
* @param[in] ctx Context with the modules.
* @param[out] root Generated yang-library data.
diff --git a/src/dict.c b/src/dict.c
new file mode 100644
index 0000000..e1426ca
--- /dev/null
+++ b/src/dict.c
@@ -0,0 +1,271 @@
+/**
+ * @file dict.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang dictionary for storing strings
+ *
+ * Copyright (c) 2015 - 2023 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 "dict.h"
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "log.h"
+
+/* starting size of the dictionary */
+#define LYDICT_MIN_SIZE 1024
+
+/**
+ * @brief Comparison callback for dictionary's hash table
+ *
+ * Implementation of ::lyht_value_equal_cb.
+ */
+static ly_bool
+lydict_val_eq(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
+{
+ const char *str1, *str2;
+ size_t *len1;
+
+ LY_CHECK_ARG_RET(NULL, val1_p, val2_p, cb_data, 0);
+
+ str1 = ((struct ly_dict_rec *)val1_p)->value;
+ str2 = ((struct ly_dict_rec *)val2_p)->value;
+ len1 = cb_data;
+
+ LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
+ LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
+
+ if (!strncmp(str1, str2, *len1) && !str2[*len1]) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+lydict_init(struct ly_dict *dict)
+{
+ LY_CHECK_ARG_RET(NULL, dict, );
+
+ dict->hash_tab = lyht_new(LYDICT_MIN_SIZE, sizeof(struct ly_dict_rec), lydict_val_eq, NULL, 1);
+ LY_CHECK_ERR_RET(!dict->hash_tab, LOGINT(NULL), );
+ pthread_mutex_init(&dict->lock, NULL);
+}
+
+void
+lydict_clean(struct ly_dict *dict)
+{
+ struct ly_dict_rec *dict_rec = NULL;
+ struct ly_ht_rec *rec = NULL;
+ uint32_t hlist_idx;
+ uint32_t rec_idx;
+
+ LY_CHECK_ARG_RET(NULL, dict, );
+
+ LYHT_ITER_ALL_RECS(dict->hash_tab, hlist_idx, rec_idx, rec) {
+ /*
+ * this should not happen, all records inserted into
+ * dictionary are supposed to be removed using lydict_remove()
+ * before calling lydict_clean()
+ */
+ dict_rec = (struct ly_dict_rec *)rec->val;
+ LOGWRN(NULL, "String \"%s\" not freed from the dictionary, refcount %d", dict_rec->value, dict_rec->refcount);
+ /* if record wasn't removed before free string allocated for that record */
+#ifdef NDEBUG
+ free(dict_rec->value);
+#endif
+ }
+
+ /* free table and destroy mutex */
+ lyht_free(dict->hash_tab, NULL);
+ pthread_mutex_destroy(&dict->lock);
+}
+
+static ly_bool
+lydict_resize_val_eq(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(cb_data))
+{
+ const char *str1, *str2;
+
+ LY_CHECK_ARG_RET(NULL, val1_p, val2_p, 0);
+
+ str1 = ((struct ly_dict_rec *)val1_p)->value;
+ str2 = ((struct ly_dict_rec *)val2_p)->value;
+
+ LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
+ LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
+
+ if (mod) {
+ /* used when inserting new values */
+ if (strcmp(str1, str2) == 0) {
+ return 1;
+ }
+ } else {
+ /* used when finding the original value again in the resized table */
+ if (str1 == str2) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_remove(const struct ly_ctx *ctx, const char *value)
+{
+ LY_ERR ret = LY_SUCCESS;
+ size_t len;
+ uint32_t hash;
+ struct ly_dict_rec rec, *match = NULL;
+ char *val_p;
+
+ if (!ctx || !value) {
+ return LY_SUCCESS;
+ }
+
+ LOGDBG(LY_LDGDICT, "removing \"%s\"", value);
+
+ len = strlen(value);
+ hash = lyht_hash(value, len);
+
+ /* create record for lyht_find call */
+ rec.value = (char *)value;
+ rec.refcount = 0;
+
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+ /* set len as data for compare callback */
+ lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
+ /* check if value is already inserted */
+ ret = lyht_find(ctx->dict.hash_tab, &rec, hash, (void **)&match);
+
+ if (ret == LY_SUCCESS) {
+ LY_CHECK_ERR_GOTO(!match, LOGINT(ctx), finish);
+
+ /* if value is already in dictionary, decrement reference counter */
+ match->refcount--;
+ if (match->refcount == 0) {
+ /*
+ * remove record
+ * save pointer to stored string before lyht_remove to
+ * free it after it is removed from hash table
+ */
+ val_p = match->value;
+ ret = lyht_remove_with_resize_cb(ctx->dict.hash_tab, &rec, hash, lydict_resize_val_eq);
+ free(val_p);
+ LY_CHECK_ERR_GOTO(ret, LOGINT(ctx), finish);
+ }
+ } else if (ret == LY_ENOTFOUND) {
+ LOGERR(ctx, LY_ENOTFOUND, "Value \"%s\" was not found in the dictionary.", value);
+ } else {
+ LOGINT(ctx);
+ }
+
+finish:
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+ return ret;
+}
+
+LY_ERR
+dict_insert(const struct ly_ctx *ctx, char *value, size_t len, ly_bool zerocopy, const char **str_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_dict_rec *match = NULL, rec;
+ uint32_t hash;
+
+ LOGDBG(LY_LDGDICT, "inserting \"%.*s\"", (int)len, value);
+
+ hash = lyht_hash(value, len);
+ /* set len as data for compare callback */
+ lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
+ /* create record for lyht_insert */
+ rec.value = value;
+ rec.refcount = 1;
+
+ ret = lyht_insert_with_resize_cb(ctx->dict.hash_tab, (void *)&rec, hash, lydict_resize_val_eq, (void **)&match);
+ if (ret == LY_EEXIST) {
+ match->refcount++;
+ if (zerocopy) {
+ free(value);
+ }
+ ret = LY_SUCCESS;
+ } else if (ret == LY_SUCCESS) {
+ if (!zerocopy) {
+ /*
+ * allocate string for new record
+ * record is already inserted in hash table
+ */
+ match->value = malloc(sizeof *match->value * (len + 1));
+ LY_CHECK_ERR_RET(!match->value, LOGMEM(ctx), LY_EMEM);
+ if (len) {
+ memcpy(match->value, value, len);
+ }
+ match->value[len] = '\0';
+ }
+ } else {
+ /* lyht_insert returned error */
+ if (zerocopy) {
+ free(value);
+ }
+ return ret;
+ }
+
+ if (str_p) {
+ *str_p = match->value;
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
+{
+ LY_ERR result;
+
+ LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
+
+ if (!value) {
+ *str_p = NULL;
+ return LY_SUCCESS;
+ }
+
+ if (!len) {
+ len = strlen(value);
+ }
+
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+ result = dict_insert(ctx, (char *)value, len, 0, str_p);
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+
+ return result;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
+{
+ LY_ERR result;
+
+ LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
+
+ if (!value) {
+ *str_p = NULL;
+ return LY_SUCCESS;
+ }
+
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+ result = dict_insert(ctx, value, strlen(value), 1, str_p);
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+
+ return result;
+}
diff --git a/src/diff.c b/src/diff.c
index edfcb34..f30d44a 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -161,6 +161,127 @@ cleanup:
return rc;
}
+/**
+ * @brief Find metadata/an attribute of a node.
+ *
+ * @param[in] node Node to search.
+ * @param[in] name Metadata/attribute name.
+ * @param[out] meta Metadata found, NULL if not found.
+ * @param[out] attr Attribute found, NULL if not found.
+ */
+static void
+lyd_diff_find_meta(const struct lyd_node *node, const char *name, struct lyd_meta **meta, struct lyd_attr **attr)
+{
+ struct lyd_meta *m;
+ struct lyd_attr *a;
+
+ if (meta) {
+ *meta = NULL;
+ }
+ if (attr) {
+ *attr = NULL;
+ }
+
+ if (node->schema) {
+ assert(meta);
+
+ LY_LIST_FOR(node->meta, m) {
+ if (!strcmp(m->name, name) && !strcmp(m->annotation->module->name, "yang")) {
+ *meta = m;
+ break;
+ }
+ }
+ } else {
+ assert(attr);
+
+ LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, a) {
+ /* name */
+ if (strcmp(a->name.name, name)) {
+ continue;
+ }
+
+ /* module */
+ switch (a->format) {
+ case LY_VALUE_JSON:
+ if (strcmp(a->name.module_name, "yang")) {
+ continue;
+ }
+ break;
+ case LY_VALUE_XML:
+ if (strcmp(a->name.module_ns, "urn:ietf:params:xml:ns:yang:1")) {
+ continue;
+ }
+ break;
+ default:
+ LOGINT(LYD_CTX(node));
+ return;
+ }
+
+ *attr = a;
+ break;
+ }
+ }
+}
+
+/**
+ * @brief Learn operation of a diff node.
+ *
+ * @param[in] diff_node Diff node.
+ * @param[out] op Operation.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
+{
+ struct lyd_meta *meta = NULL;
+ struct lyd_attr *attr = NULL;
+ const struct lyd_node *diff_parent;
+ const char *str;
+ char *path;
+
+ for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
+ lyd_diff_find_meta(diff_parent, "operation", &meta, &attr);
+ if (!meta && !attr) {
+ continue;
+ }
+
+ str = meta ? lyd_get_meta_value(meta) : attr->value;
+ if ((str[0] == 'r') && (diff_parent != diff_node)) {
+ /* we do not care about this operation if it's in our parent */
+ continue;
+ }
+ *op = lyd_diff_str2op(str);
+ return LY_SUCCESS;
+ }
+
+ /* operation not found */
+ path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
+ LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
+ free(path);
+ return LY_EINT;
+}
+
+/**
+ * @brief Remove metadata/an attribute from a node.
+ *
+ * @param[in] node Node to update.
+ * @param[in] name Metadata/attribute name.
+ */
+static void
+lyd_diff_del_meta(struct lyd_node *node, const char *name)
+{
+ struct lyd_meta *meta;
+ struct lyd_attr *attr;
+
+ lyd_diff_find_meta(node, name, &meta, &attr);
+
+ if (meta) {
+ lyd_free_meta_single(meta);
+ } else if (attr) {
+ lyd_free_attr_single(LYD_CTX(node), attr);
+ }
+}
+
LY_ERR
lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
@@ -168,6 +289,9 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_
{
struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem;
const struct lyd_node *parent = NULL;
+ enum lyd_diff_op cur_op;
+ struct lyd_meta *meta;
+ uint32_t diff_opts;
assert(diff);
@@ -189,14 +313,16 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_
/* find the first existing parent */
siblings = *diff;
- while (1) {
+ do {
/* find next node parent */
parent = node;
while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
parent = lyd_parent(parent);
}
- if (parent == node) {
- /* no more parents to find */
+
+ if (lysc_is_dup_inst_list(parent->schema)) {
+ /* assume it never exists, we are not able to distinguish whether it does or not */
+ match = NULL;
break;
}
@@ -210,36 +336,75 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_
/* move down in the diff */
siblings = lyd_child_no_keys(match);
- }
+ } while (parent != node);
+
+ if (match && (parent == node)) {
+ /* special case when there is already an operation on our descendant */
+ assert(!lyd_diff_get_op(diff_parent, &cur_op) && (cur_op == LYD_DIFF_OP_NONE));
+ (void)cur_op;
+
+ /* move it to the end where it is expected (matters for user-ordered lists) */
+ if (lysc_is_userordered(diff_parent->schema)) {
+ for (elem = diff_parent; elem->next && (elem->next->schema == elem->schema); elem = elem->next) {}
+ if (elem != diff_parent) {
+ LY_CHECK_RET(lyd_insert_after(elem, diff_parent));
+ }
+ }
- /* duplicate the subtree (and connect to the diff if possible) */
- LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
- LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
+ /* will be replaced by the new operation but keep the current op for descendants */
+ lyd_diff_del_meta(diff_parent, "operation");
+ LY_LIST_FOR(lyd_child_no_keys(diff_parent), elem) {
+ lyd_diff_find_meta(elem, "operation", &meta, NULL);
+ if (meta) {
+ /* explicit operation, fine */
+ continue;
+ }
- /* find the first duplicated parent */
- if (!diff_parent) {
- diff_parent = lyd_parent(dup);
- while (diff_parent && diff_parent->parent) {
- diff_parent = lyd_parent(diff_parent);
+ /* set the none operation */
+ LY_CHECK_RET(lyd_new_meta(NULL, elem, NULL, "yang:operation", "none", 0, NULL));
}
+
+ dup = diff_parent;
} else {
- diff_parent = dup;
- while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
- diff_parent = lyd_parent(diff_parent);
+ diff_opts = LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS;
+ if ((op != LYD_DIFF_OP_REPLACE) || !lysc_is_userordered(node->schema) || (node->schema->flags & LYS_CONFIG_R)) {
+ /* move applies only to the user-ordered list, no descendants */
+ diff_opts |= LYD_DUP_RECURSIVE;
}
- }
- /* no parent existed, must be manually connected */
- if (!diff_parent) {
- /* there actually was no parent to duplicate */
- lyd_insert_sibling(*diff, dup, diff);
- } else if (!diff_parent->parent) {
- lyd_insert_sibling(*diff, diff_parent, diff);
- }
+ /* duplicate the subtree (and connect to the diff if possible) */
+ if (diff_parent) {
+ LY_CHECK_RET(lyd_dup_single_to_ctx(node, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
+ diff_opts, &dup));
+ } else {
+ LY_CHECK_RET(lyd_dup_single(node, NULL, diff_opts, &dup));
+ }
+
+ /* find the first duplicated parent */
+ if (!diff_parent) {
+ diff_parent = lyd_parent(dup);
+ while (diff_parent && diff_parent->parent) {
+ diff_parent = lyd_parent(diff_parent);
+ }
+ } else {
+ diff_parent = dup;
+ while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
+ diff_parent = lyd_parent(diff_parent);
+ }
+ }
+
+ /* no parent existed, must be manually connected */
+ if (!diff_parent) {
+ /* there actually was no parent to duplicate */
+ lyd_insert_sibling(*diff, dup, diff);
+ } else if (!diff_parent->parent) {
+ lyd_insert_sibling(*diff, diff_parent, diff);
+ }
- /* add parent operation, if any */
- if (diff_parent && (diff_parent != dup)) {
- LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
+ /* add parent operation, if any */
+ if (diff_parent && (diff_parent != dup)) {
+ LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
+ }
}
/* add subtree operation */
@@ -361,7 +526,7 @@ lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *seco
LY_ERR rc = LY_SUCCESS;
const struct lysc_node *schema;
size_t buflen, bufused;
- uint32_t first_pos, second_pos;
+ uint32_t first_pos, second_pos, comp_opts;
assert(first || second);
@@ -397,7 +562,8 @@ lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *seco
} else if (!first) {
*op = LYD_DIFF_OP_CREATE;
} else {
- if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
+ comp_opts = lysc_is_dup_inst_list(second->schema) ? LYD_COMPARE_FULL_RECURSION : 0;
+ if (lyd_compare_single(second, userord_item->inst[second_pos], comp_opts)) {
/* in first, there is a different instance on the second position, we are going to move 'first' node */
*op = LYD_DIFF_OP_REPLACE;
} else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
@@ -648,17 +814,20 @@ lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, uint
* @param[in] siblings Siblings to search in.
* @param[in] target Target node to search for.
* @param[in] defaults Whether to consider (or ignore) default values.
- * @param[in,out] dup_inst_cache Duplicate instance cache.
+ * @param[in,out] dup_inst_ht Duplicate instance cache.
* @param[out] match Found match, NULL if no matching node found.
* @return LY_ERR value.
*/
static LY_ERR
lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
- struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
+ struct ly_ht **dup_inst_ht, struct lyd_node **match)
{
LY_ERR r;
- if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+ if (!target->schema) {
+ /* try to find the same opaque node */
+ r = lyd_find_sibling_opaq_next(siblings, LYD_NAME(target), match);
+ } else if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
/* try to find the exact instance */
r = lyd_find_sibling_first(siblings, target, match);
} else {
@@ -670,7 +839,7 @@ lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *targ
}
/* update match as needed */
- LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
+ LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_ht));
if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
/* ignore default nodes */
@@ -728,7 +897,7 @@ lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second,
const struct lyd_node *iter_first, *iter_second;
struct lyd_node *match_second, *match_first;
struct lyd_diff_userord *userord = NULL, *userord_item;
- struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
+ struct ly_ht *dup_inst_first = NULL, *dup_inst_second = NULL;
LY_ARRAY_COUNT_TYPE u;
enum lyd_diff_op op;
const char *orig_default;
@@ -920,48 +1089,6 @@ lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, u
}
/**
- * @brief Learn operation of a diff node.
- *
- * @param[in] diff_node Diff node.
- * @param[out] op Operation.
- * @return LY_ERR value.
- */
-static LY_ERR
-lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
-{
- struct lyd_meta *meta = NULL;
- const struct lyd_node *diff_parent;
- const char *str;
- char *path;
-
- for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
- LY_LIST_FOR(diff_parent->meta, meta) {
- if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
- str = lyd_get_meta_value(meta);
- if ((str[0] == 'r') && (diff_parent != diff_node)) {
- /* we do not care about this operation if it's in our parent */
- continue;
- }
- *op = lyd_diff_str2op(str);
- break;
- }
- }
- if (meta) {
- break;
- }
- }
-
- if (!meta) {
- path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
- LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
- free(path);
- return LY_EINT;
- }
-
- return LY_SUCCESS;
-}
-
-/**
* @brief Insert a diff node into a data tree.
*
* @param[in,out] first_node First sibling of the data tree.
@@ -1079,14 +1206,14 @@ lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, stru
*/
static LY_ERR
lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
- lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
+ lyd_diff_cb diff_cb, void *cb_data, struct ly_ht **dup_inst)
{
LY_ERR ret;
struct lyd_node *match, *diff_child;
const char *str_val, *meta_str;
enum lyd_diff_op op;
struct lyd_meta *meta;
- struct lyd_dup_inst *child_dup_inst = NULL;
+ struct ly_ht *child_dup_inst = NULL;
const struct ly_ctx *ctx = LYD_CTX(diff_node);
/* read all the valid attributes */
@@ -1242,7 +1369,7 @@ lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const
lyd_diff_cb diff_cb, void *cb_data)
{
const struct lyd_node *root;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
LY_ERR ret = LY_SUCCESS;
LY_LIST_FOR(diff, root) {
@@ -1299,27 +1426,6 @@ lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const
}
/**
- * @brief Remove an attribute from a node.
- *
- * @param[in] node Node with the metadata.
- * @param[in] name Metadata name.
- */
-static void
-lyd_diff_del_meta(struct lyd_node *node, const char *name)
-{
- struct lyd_meta *meta;
-
- LY_LIST_FOR(node->meta, meta) {
- if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
- lyd_free_meta_single(meta);
- return;
- }
- }
-
- assert(0);
-}
-
-/**
* @brief Set a specific operation of a node. Delete the previous operation, if any.
* Does not change the default flag.
*
@@ -1330,16 +1436,13 @@ lyd_diff_del_meta(struct lyd_node *node, const char *name)
static LY_ERR
lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
{
- struct lyd_meta *meta;
+ lyd_diff_del_meta(node, "operation");
- LY_LIST_FOR(node->meta, meta) {
- if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
- lyd_free_meta_single(meta);
- break;
- }
+ if (node->schema) {
+ return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
+ } else {
+ return lyd_new_attr(node, "yang", "operation", lyd_diff_op2str(op), NULL);
}
-
- return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
}
/**
@@ -1431,28 +1534,43 @@ lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, con
}
break;
case LYD_DIFF_OP_NONE:
- /* it is moved now */
- assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
+ switch (diff_match->schema->nodetype) {
+ case LYS_LIST:
+ /* it is moved now */
+ assert(lysc_is_userordered(diff_match->schema));
- /* change the operation */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+ /* change the operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
- /* set orig-meta and meta */
- if (lysc_is_dup_inst_list(diff_match->schema)) {
- meta_name = "position";
- orig_meta_name = "orig-position";
- } else {
- meta_name = "key";
- orig_meta_name = "orig-key";
- }
+ /* set orig-meta and meta */
+ if (lysc_is_dup_inst_list(diff_match->schema)) {
+ meta_name = "position";
+ orig_meta_name = "orig-position";
+ } else {
+ meta_name = "key";
+ orig_meta_name = "orig-key";
+ }
+
+ meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
+ LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
- meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
- LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
- LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ meta = lyd_find_meta(src_diff->meta, mod, meta_name);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
+ LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ break;
+ case LYS_LEAF:
+ /* only dflt flag changed, now value changed as well, update the operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
- meta = lyd_find_meta(src_diff->meta, mod, meta_name);
- LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
- LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ /* modify the node value */
+ if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
+ LOGINT_RET(LYD_CTX(src_diff));
+ }
+ break;
+ default:
+ LOGINT_RET(LYD_CTX(src_diff));
+ }
break;
default:
/* delete operation is not valid */
@@ -1466,33 +1584,41 @@ lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, con
/**
* @brief Update operations in a diff node when the new operation is CREATE.
*
- * @param[in] diff_match Node from the diff.
+ * @param[in,out] diff_match Node from the diff, may be replaced.
+ * @param[in,out] diff Diff root node, may be updated.
* @param[in] cur_op Current operation of @p diff_match.
* @param[in] src_diff Current source diff node.
* @param[in] options Diff merge options.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff, uint16_t options)
+lyd_diff_merge_create(struct lyd_node **diff_match, struct lyd_node **diff, enum lyd_diff_op cur_op,
+ const struct lyd_node *src_diff, uint16_t options)
{
- struct lyd_node *child;
+ struct lyd_node *child, *src_dup, *to_free = NULL;
const struct lysc_node_leaf *sleaf = NULL;
uint32_t trg_flags;
const char *meta_name, *orig_meta_name;
struct lyd_meta *meta, *orig_meta;
- const struct ly_ctx *ctx = LYD_CTX(diff_match);
+ const struct ly_ctx *ctx = LYD_CTX(*diff_match);
+ LY_ERR r;
+
+ /* create operation is valid only for data nodes */
+ LY_CHECK_ERR_RET(!src_diff->schema, LOGINT(ctx), LY_EINT);
switch (cur_op) {
case LYD_DIFF_OP_DELETE:
/* remember current flags */
- trg_flags = diff_match->flags;
+ trg_flags = (*diff_match)->flags;
+
+ if (lysc_is_userordered(src_diff->schema)) {
+ assert((*diff_match)->schema);
- if (lysc_is_userordered(diff_match->schema)) {
/* get anchor metadata */
- if (lysc_is_dup_inst_list(diff_match->schema)) {
+ if (lysc_is_dup_inst_list((*diff_match)->schema)) {
meta_name = "yang:position";
orig_meta_name = "yang:orig-position";
- } else if (diff_match->schema->nodetype == LYS_LIST) {
+ } else if ((*diff_match)->schema->nodetype == LYS_LIST) {
meta_name = "yang:key";
orig_meta_name = "yang:orig-key";
} else {
@@ -1501,71 +1627,86 @@ lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, cons
}
meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
- orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
- LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
+ orig_meta = lyd_find_meta((*diff_match)->meta, NULL, orig_meta_name);
+ LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, *diff_match), LY_EINVAL);
/* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
* the anchors stored in the metadata */
if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
/* deleted + created at another position -> operation REPLACE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE));
/* add anchor metadata */
- LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ LY_CHECK_RET(lyd_dup_meta_single(meta, *diff_match, NULL));
} else {
/* deleted + created at the same position -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
/* delete anchor metadata */
lyd_free_meta_single(orig_meta);
}
- } else if (diff_match->schema->nodetype == LYS_LEAF) {
+ } else if (src_diff->schema->nodetype == LYS_LEAF) {
if (options & LYD_DIFF_MERGE_DEFAULTS) {
/* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
- sleaf = (struct lysc_node_leaf *)diff_match->schema;
+ sleaf = (struct lysc_node_leaf *)src_diff->schema;
}
if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
&((struct lyd_node_term *)src_diff)->value)) {
/* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
- } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
+ } else if (!lyd_compare_single(*diff_match, src_diff, 0)) {
/* deleted + created -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
- } else {
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
+ } else if ((*diff_match)->schema) {
/* we deleted it, but it was created with a different value -> operation REPLACE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE));
/* current value is the previous one (meta) */
- LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
- lyd_get_value(diff_match), 0, NULL));
+ LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-value",
+ lyd_get_value(*diff_match), 0, NULL));
/* update the value itself */
- LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
+ LY_CHECK_RET(lyd_change_term(*diff_match, lyd_get_value(src_diff)));
+ } else {
+ /* also operation REPLACE but we need to change an opaque node into a data node */
+ LY_CHECK_RET(lyd_dup_single(src_diff, (*diff_match)->parent, LYD_DUP_NO_META | LYD_DUP_WITH_FLAGS, &src_dup));
+ if (!(*diff_match)->parent) {
+ /* will always be inserted before diff_match, which is opaque */
+ LY_CHECK_RET(lyd_insert_sibling(*diff_match, src_dup, diff));
+ }
+ to_free = *diff_match;
+ *diff_match = src_dup;
+
+ r = lyd_new_meta(ctx, src_dup, NULL, "yang:orig-value", lyd_get_value(to_free), 0, NULL);
+ lyd_free_tree(to_free);
+ LY_CHECK_RET(r);
+ LY_CHECK_RET(lyd_new_meta(ctx, src_dup, NULL, "yang:operation", lyd_diff_op2str(LYD_DIFF_OP_REPLACE), 0, NULL));
}
} else {
/* deleted + created -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
}
- if (diff_match->schema->nodetype & LYD_NODE_TERM) {
+ assert((*diff_match)->schema);
+ if ((*diff_match)->schema->nodetype & LYD_NODE_TERM) {
/* add orig-dflt metadata */
- LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
+ LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-default",
trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
/* update dflt flag itself */
- diff_match->flags &= ~LYD_DEFAULT;
- diff_match->flags |= src_diff->flags & LYD_DEFAULT;
+ (*diff_match)->flags &= ~LYD_DEFAULT;
+ (*diff_match)->flags |= src_diff->flags & LYD_DEFAULT;
}
/* but the operation of its children should remain DELETE */
- LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
+ LY_LIST_FOR(lyd_child_no_keys(*diff_match), child) {
LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
}
break;
default:
/* create and replace operations are not valid */
- LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
+ LOGERR_MERGEOP(LYD_CTX(src_diff), *diff_match, cur_op, LYD_DIFF_OP_CREATE);
return LY_EINVAL;
}
@@ -1599,7 +1740,7 @@ lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, cons
if (diff_match->schema->nodetype & LYD_NODE_TERM) {
/* add orig-default meta because it is expected */
LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
- diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
+ src_diff->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
} else if (!lysc_is_dup_inst_list(diff_match->schema)) {
/* keep operation for all descendants (for now) */
LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
@@ -1723,17 +1864,24 @@ lyd_diff_is_redundant(struct lyd_node *diff)
*/
return 1;
}
- } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
- /* check whether at least the default flags are different */
- meta = lyd_find_meta(diff->meta, mod, "orig-default");
- assert(meta);
- str = lyd_get_meta_value(meta);
-
- /* if previous and current dflt flags are the same, this node is redundant */
- if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
+ } else if (op == LYD_DIFF_OP_NONE) {
+ if (!diff->schema) {
+ /* opaque node with none must be redundant */
return 1;
}
- return 0;
+
+ if (diff->schema->nodetype & LYD_NODE_TERM) {
+ /* check whether at least the default flags are different */
+ meta = lyd_find_meta(diff->meta, mod, "orig-default");
+ assert(meta);
+ str = lyd_get_meta_value(meta);
+
+ /* if previous and current dflt flags are the same, this node is redundant */
+ if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
+ return 1;
+ }
+ return 0;
+ }
}
if (!child && (op == LYD_DIFF_OP_NONE)) {
@@ -1757,12 +1905,12 @@ lyd_diff_is_redundant(struct lyd_node *diff)
*/
static LY_ERR
lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
- struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
+ struct ly_ht **dup_inst, uint16_t options, struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *child, *diff_node = NULL;
enum lyd_diff_op src_op, cur_op;
- struct lyd_dup_inst *child_dup_inst = NULL;
+ struct ly_ht *child_dup_inst = NULL;
/* get source node operation */
LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
@@ -1785,7 +1933,7 @@ lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent,
goto add_diff;
}
- ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
+ ret = lyd_diff_merge_create(&diff_node, diff, cur_op, src_diff, options);
break;
case LYD_DIFF_OP_DELETE:
ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
@@ -1868,7 +2016,7 @@ lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, c
lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
{
const struct lyd_node *src_root;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
LY_ERR ret = LY_SUCCESS;
LY_LIST_FOR(src_diff, src_root) {
@@ -1891,7 +2039,7 @@ lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent,
lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
{
LY_ERR ret;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
if (!src_sibling) {
return LY_SUCCESS;
diff --git a/src/hash_table.c b/src/hash_table.c
index 4f9dec3..9655bd6 100644
--- a/src/hash_table.c
+++ b/src/hash_table.c
@@ -1,9 +1,10 @@
/**
* @file hash_table.c
* @author Radek Krejci <rkrejci@cesnet.cz>
- * @brief libyang dictionary for storing strings and generic hash table
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang generic hash table implementation
*
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 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.
@@ -25,80 +26,8 @@
#include "dict.h"
#include "log.h"
-#define LYDICT_MIN_SIZE 1024
-
-/**
- * @brief Comparison callback for dictionary's hash table
- *
- * Implementation of ::lyht_value_equal_cb.
- */
-static ly_bool
-lydict_val_eq(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
-{
- LY_CHECK_ARG_RET(NULL, val1_p, val2_p, cb_data, 0);
-
- const char *str1 = ((struct dict_rec *)val1_p)->value;
- const char *str2 = ((struct dict_rec *)val2_p)->value;
-
- LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
- LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
-
- if (strncmp(str1, str2, *(size_t *)cb_data) == 0) {
- return 1;
- }
-
- return 0;
-}
-
-void
-lydict_init(struct dict_table *dict)
-{
- LY_CHECK_ARG_RET(NULL, dict, );
-
- dict->hash_tab = lyht_new(LYDICT_MIN_SIZE, sizeof(struct dict_rec), lydict_val_eq, NULL, 1);
- LY_CHECK_ERR_RET(!dict->hash_tab, LOGINT(NULL), );
- pthread_mutex_init(&dict->lock, NULL);
-}
-
-void
-lydict_clean(struct dict_table *dict)
-{
- struct dict_rec *dict_rec = NULL;
- struct ht_rec *rec = NULL;
-
- LY_CHECK_ARG_RET(NULL, dict, );
-
- for (uint32_t i = 0; i < dict->hash_tab->size; i++) {
- /* get ith record */
- rec = (struct ht_rec *)&dict->hash_tab->recs[i * dict->hash_tab->rec_size];
- if (rec->hits == 1) {
- /*
- * this should not happen, all records inserted into
- * dictionary are supposed to be removed using lydict_remove()
- * before calling lydict_clean()
- */
- dict_rec = (struct dict_rec *)rec->val;
- LOGWRN(NULL, "String \"%s\" not freed from the dictionary, refcount %d", dict_rec->value, dict_rec->refcount);
- /* if record wasn't removed before free string allocated for that record */
-#ifdef NDEBUG
- free(dict_rec->value);
-#endif
- }
- }
-
- /* free table and destroy mutex */
- lyht_free(dict->hash_tab);
- pthread_mutex_destroy(&dict->lock);
-}
-
-/*
- * Usage:
- * - init hash to 0
- * - repeatedly call dict_hash_multi(), provide hash from the last call
- * - call dict_hash_multi() with key_part = NULL to finish the hash
- */
-uint32_t
-dict_hash_multi(uint32_t hash, const char *key_part, size_t len)
+LIBYANG_API_DEF uint32_t
+lyht_hash_multi(uint32_t hash, const char *key_part, size_t len)
{
uint32_t i;
@@ -117,203 +46,47 @@ dict_hash_multi(uint32_t hash, const char *key_part, size_t len)
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)
+LIBYANG_API_DEF uint32_t
+lyht_hash(const char *key, size_t len)
{
uint32_t hash;
- hash = dict_hash_multi(0, key, len);
- return dict_hash_multi(hash, NULL, len);
+ hash = lyht_hash_multi(0, key, len);
+ return lyht_hash_multi(hash, NULL, len);
}
-static ly_bool
-lydict_resize_val_eq(void *val1_p, void *val2_p, ly_bool mod, void *cb_data)
-{
- LY_CHECK_ARG_RET(NULL, val1_p, val2_p, 0);
-
- const char *str1 = ((struct dict_rec *)val1_p)->value;
- const char *str2 = ((struct dict_rec *)val2_p)->value;
-
- LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
- LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
-
- if (mod) {
- /* used when inserting new values */
- if (strcmp(str1, str2) == 0) {
- return 1;
- }
- } else {
- /* used when finding the original value again in the resized table */
- return lydict_val_eq(val1_p, val2_p, mod, cb_data);
- }
-
- return 0;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_remove(const struct ly_ctx *ctx, const char *value)
-{
- LY_ERR ret = LY_SUCCESS;
- size_t len;
- uint32_t hash;
- struct dict_rec rec, *match = NULL;
- char *val_p;
-
- if (!ctx || !value) {
- return LY_SUCCESS;
- }
-
- LOGDBG(LY_LDGDICT, "removing \"%s\"", value);
-
- len = strlen(value);
- hash = dict_hash(value, len);
-
- /* create record for lyht_find call */
- rec.value = (char *)value;
- rec.refcount = 0;
-
- pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
- /* set len as data for compare callback */
- lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
- /* check if value is already inserted */
- ret = lyht_find(ctx->dict.hash_tab, &rec, hash, (void **)&match);
-
- if (ret == LY_SUCCESS) {
- LY_CHECK_ERR_GOTO(!match, LOGINT(ctx), finish);
-
- /* if value is already in dictionary, decrement reference counter */
- match->refcount--;
- if (match->refcount == 0) {
- /*
- * remove record
- * save pointer to stored string before lyht_remove to
- * free it after it is removed from hash table
- */
- val_p = match->value;
- ret = lyht_remove_with_resize_cb(ctx->dict.hash_tab, &rec, hash, lydict_resize_val_eq);
- free(val_p);
- LY_CHECK_ERR_GOTO(ret, LOGINT(ctx), finish);
- }
- } else if (ret == LY_ENOTFOUND) {
- LOGERR(ctx, LY_ENOTFOUND, "Value \"%s\" was not found in the dictionary.", value);
- } else {
- LOGINT(ctx);
- }
-
-finish:
- pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
- return ret;
-}
-
-LY_ERR
-dict_insert(const struct ly_ctx *ctx, char *value, size_t len, ly_bool zerocopy, const char **str_p)
+static LY_ERR
+lyht_init_hlists_and_records(struct ly_ht *ht)
{
- LY_ERR ret = LY_SUCCESS;
- struct dict_rec *match = NULL, rec;
- uint32_t hash;
-
- LOGDBG(LY_LDGDICT, "inserting \"%.*s\"", (int)len, value);
-
- hash = dict_hash(value, len);
- /* set len as data for compare callback */
- lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
- /* create record for lyht_insert */
- rec.value = value;
- rec.refcount = 1;
+ struct ly_ht_rec *rec;
+ uint32_t i;
- ret = lyht_insert_with_resize_cb(ctx->dict.hash_tab, (void *)&rec, hash, lydict_resize_val_eq, (void **)&match);
- if (ret == LY_EEXIST) {
- match->refcount++;
- if (zerocopy) {
- free(value);
- }
- ret = LY_SUCCESS;
- } else if (ret == LY_SUCCESS) {
- if (!zerocopy) {
- /*
- * allocate string for new record
- * record is already inserted in hash table
- */
- match->value = malloc(sizeof *match->value * (len + 1));
- LY_CHECK_ERR_RET(!match->value, LOGMEM(ctx), LY_EMEM);
- if (len) {
- memcpy(match->value, value, len);
- }
- match->value[len] = '\0';
- }
- } else {
- /* lyht_insert returned error */
- if (zerocopy) {
- free(value);
+ ht->recs = calloc(ht->size, ht->rec_size);
+ LY_CHECK_ERR_RET(!ht->recs, LOGMEM(NULL), LY_EMEM);
+ for (i = 0; i < ht->size; i++) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ if (i != ht->size) {
+ rec->next = i + 1;
+ } else {
+ rec->next = LYHT_NO_RECORD;
}
- return ret;
- }
-
- if (str_p) {
- *str_p = match->value;
- }
-
- return ret;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
-{
- LY_ERR result;
-
- LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
-
- if (!value) {
- *str_p = NULL;
- return LY_SUCCESS;
- }
-
- if (!len) {
- len = strlen(value);
}
- pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
- result = dict_insert(ctx, (char *)value, len, 0, str_p);
- pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
-
- return result;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
-{
- LY_ERR result;
-
- LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
-
- if (!value) {
- *str_p = NULL;
- return LY_SUCCESS;
+ ht->hlists = malloc(sizeof(ht->hlists[0]) * ht->size);
+ LY_CHECK_ERR_RET(!ht->hlists, free(ht->recs); LOGMEM(NULL), LY_EMEM);
+ for (i = 0; i < ht->size; i++) {
+ ht->hlists[i].first = LYHT_NO_RECORD;
+ ht->hlists[i].last = LYHT_NO_RECORD;
}
+ ht->first_free_rec = 0;
- pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
- result = dict_insert(ctx, value, strlen(value), 1, str_p);
- pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
-
- return result;
-}
-
-struct ht_rec *
-lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx)
-{
- return (struct ht_rec *)&recs[idx * rec_size];
+ return LY_SUCCESS;
}
-struct hash_table *
+LIBYANG_API_DEF struct ly_ht *
lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data, uint16_t resize)
{
- struct hash_table *ht;
+ struct ly_ht *ht;
/* check that 2^x == size (power of 2) */
assert(size && !(size & (size - 1)));
@@ -329,21 +102,21 @@ lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *
ht->used = 0;
ht->size = size;
- ht->invalid = 0;
ht->val_equal = val_equal;
ht->cb_data = cb_data;
ht->resize = resize;
- ht->rec_size = (sizeof(struct ht_rec) - 1) + val_size;
- /* allocate the records correctly */
- ht->recs = calloc(size, ht->rec_size);
- LY_CHECK_ERR_RET(!ht->recs, free(ht); LOGMEM(NULL), NULL);
+ ht->rec_size = SIZEOF_LY_HT_REC + val_size;
+ if (lyht_init_hlists_and_records(ht) != LY_SUCCESS) {
+ free(ht);
+ return NULL;
+ }
return ht;
}
-lyht_value_equal_cb
-lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_val_equal)
+LIBYANG_API_DEF lyht_value_equal_cb
+lyht_set_cb(struct ly_ht *ht, lyht_value_equal_cb new_val_equal)
{
lyht_value_equal_cb prev;
@@ -352,8 +125,8 @@ lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_val_equal)
return prev;
}
-void *
-lyht_set_cb_data(struct hash_table *ht, void *new_cb_data)
+LIBYANG_API_DEF void *
+lyht_set_cb_data(struct ly_ht *ht, void *new_cb_data)
{
void *prev;
@@ -362,31 +135,43 @@ lyht_set_cb_data(struct hash_table *ht, void *new_cb_data)
return prev;
}
-struct hash_table *
-lyht_dup(const struct hash_table *orig)
+LIBYANG_API_DEF struct ly_ht *
+lyht_dup(const struct ly_ht *orig)
{
- struct hash_table *ht;
+ struct ly_ht *ht;
LY_CHECK_ARG_RET(NULL, orig, NULL);
- ht = lyht_new(orig->size, orig->rec_size - (sizeof(struct ht_rec) - 1), orig->val_equal, orig->cb_data, orig->resize ? 1 : 0);
+ ht = lyht_new(orig->size, orig->rec_size - SIZEOF_LY_HT_REC, orig->val_equal, orig->cb_data, orig->resize ? 1 : 0);
if (!ht) {
return NULL;
}
- memcpy(ht->recs, orig->recs, (size_t)orig->used * (size_t)orig->rec_size);
+ memcpy(ht->hlists, orig->hlists, sizeof(ht->hlists[0]) * orig->size);
+ memcpy(ht->recs, orig->recs, (size_t)orig->size * orig->rec_size);
ht->used = orig->used;
- ht->invalid = orig->invalid;
return ht;
}
-void
-lyht_free(struct hash_table *ht)
+LIBYANG_API_DEF void
+lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p))
{
- if (ht) {
- free(ht->recs);
- free(ht);
+ struct ly_ht_rec *rec;
+ uint32_t hlist_idx;
+ uint32_t rec_idx;
+
+ if (!ht) {
+ return;
+ }
+
+ if (val_free) {
+ LYHT_ITER_ALL_RECS(ht, hlist_idx, rec_idx, rec) {
+ val_free(&rec->val);
+ }
}
+ free(ht->hlists);
+ free(ht->recs);
+ free(ht);
}
/**
@@ -397,14 +182,19 @@ lyht_free(struct hash_table *ht)
* @return LY_ERR value.
*/
static LY_ERR
-lyht_resize(struct hash_table *ht, int operation)
+lyht_resize(struct ly_ht *ht, int operation, int check)
{
- struct ht_rec *rec;
+ struct ly_ht_rec *rec;
+ struct ly_ht_hlist *old_hlists;
unsigned char *old_recs;
+ uint32_t old_first_free_rec;
uint32_t i, old_size;
+ uint32_t rec_idx;
+ old_hlists = ht->hlists;
old_recs = ht->recs;
old_size = ht->size;
+ old_first_free_rec = ht->first_free_rec;
if (operation > 0) {
/* double the size */
@@ -414,18 +204,29 @@ lyht_resize(struct hash_table *ht, int operation)
ht->size >>= 1;
}
- ht->recs = calloc(ht->size, ht->rec_size);
- LY_CHECK_ERR_RET(!ht->recs, LOGMEM(NULL); ht->recs = old_recs; ht->size = old_size, LY_EMEM);
+ if (lyht_init_hlists_and_records(ht) != LY_SUCCESS) {
+ ht->hlists = old_hlists;
+ ht->recs = old_recs;
+ ht->size = old_size;
+ ht->first_free_rec = old_first_free_rec;
+ return LY_EMEM;
+ }
- /* reset used and invalid, it will increase again */
+ /* reset used, it will increase again */
ht->used = 0;
- ht->invalid = 0;
/* add all the old records into the new records array */
- for (i = 0; i < old_size; ++i) {
- rec = lyht_get_rec(old_recs, ht->rec_size, i);
- if (rec->hits > 0) {
- LY_ERR ret = lyht_insert(ht, rec->val, rec->hash, NULL);
+ for (i = 0; i < old_size; i++) {
+ for (rec_idx = old_hlists[i].first, rec = lyht_get_rec(old_recs, ht->rec_size, rec_idx);
+ rec_idx != LYHT_NO_RECORD;
+ rec_idx = rec->next, rec = lyht_get_rec(old_recs, ht->rec_size, rec_idx)) {
+ LY_ERR ret;
+
+ if (check) {
+ ret = lyht_insert(ht, rec->val, rec->hash, NULL);
+ } else {
+ ret = lyht_insert_no_check(ht, rec->val, rec->hash, NULL);
+ }
assert(!ret);
(void)ret;
@@ -434,117 +235,18 @@ lyht_resize(struct hash_table *ht, int operation)
/* final touches */
free(old_recs);
+ free(old_hlists);
return LY_SUCCESS;
}
/**
- * @brief Search for the first match.
- *
- * @param[in] ht Hash table to search in.
- * @param[in] hash Hash to find.
- * @param[out] rec_p Optional found record.
- * @return LY_SUCCESS hash found, returned its record,
- * @return LY_ENOTFOUND hash not found, returned the record where it would be inserted.
- */
-static LY_ERR
-lyht_find_first(struct hash_table *ht, uint32_t hash, struct ht_rec **rec_p)
-{
- struct ht_rec *rec;
- uint32_t i, idx;
-
- if (rec_p) {
- *rec_p = NULL;
- }
-
- idx = i = hash & (ht->size - 1);
- rec = lyht_get_rec(ht->recs, ht->rec_size, idx);
-
- /* skip through overflow and deleted records */
- while ((rec->hits != 0) && ((rec->hits == -1) || ((rec->hash & (ht->size - 1)) != idx))) {
- if ((rec->hits == -1) && rec_p && !(*rec_p)) {
- /* remember this record for return */
- *rec_p = rec;
- }
- i = (i + 1) % ht->size;
- if (i == idx) {
- /* we went through all the records (very unlikely, but possible when many records are invalid),
- * just return not found */
- assert(!rec_p || *rec_p);
- return LY_ENOTFOUND;
- }
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- }
- if (rec->hits == 0) {
- /* we could not find the value */
- if (rec_p && !*rec_p) {
- *rec_p = rec;
- }
- return LY_ENOTFOUND;
- }
-
- /* we have found a record with equal (shortened) hash */
- if (rec_p) {
- *rec_p = rec;
- }
- return LY_SUCCESS;
-}
-
-/**
- * @brief Search for the next collision.
- *
- * @param[in] ht Hash table to search in.
- * @param[in,out] last Last returned collision record.
- * @param[in] first First collision record (hits > 1).
- * @return LY_SUCCESS when hash collision found, \p last points to this next collision,
- * @return LY_ENOTFOUND when hash collision not found, \p last points to the record where it would be inserted.
- */
-static LY_ERR
-lyht_find_collision(struct hash_table *ht, struct ht_rec **last, struct ht_rec *first)
-{
- struct ht_rec *empty = NULL;
- uint32_t i, idx;
-
- assert(last && *last);
-
- idx = (*last)->hash & (ht->size - 1);
- i = (((unsigned char *)*last) - ht->recs) / ht->rec_size;
-
- do {
- i = (i + 1) % ht->size;
- *last = lyht_get_rec(ht->recs, ht->rec_size, i);
- if (*last == first) {
- /* we went through all the records (very unlikely, but possible when many records are invalid),
- * just return an invalid record */
- assert(empty);
- *last = empty;
- return LY_ENOTFOUND;
- }
-
- if (((*last)->hits == -1) && !empty) {
- empty = *last;
- }
- } while (((*last)->hits != 0) && (((*last)->hits == -1) || (((*last)->hash & (ht->size - 1)) != idx)));
-
- if ((*last)->hits > 0) {
- /* we found a collision */
- assert((*last)->hits == 1);
- return LY_SUCCESS;
- }
-
- /* no next collision found, return the record where it would be inserted */
- if (empty) {
- *last = empty;
- } /* else (*last)->hits == 0, it is already correct */
- return LY_ENOTFOUND;
-}
-
-/**
* @brief Search for a record with specific value and hash.
*
* @param[in] ht Hash table to search in.
* @param[in] val_p Pointer to the value to find.
* @param[in] hash Hash to find.
* @param[in] mod Whether the operation modifies the hash table (insert or remove) or not (find).
+ * @param[in] val_equal Callback for checking value equivalence.
* @param[out] crec_p Optional found first record.
* @param[out] col Optional collision number of @p rec_p, 0 for no collision.
* @param[out] rec_p Found exact matching record, may be a collision of @p crec_p.
@@ -552,12 +254,12 @@ lyht_find_collision(struct hash_table *ht, struct ht_rec **last, struct ht_rec *
* @return LY_SUCCESS if record was found.
*/
static LY_ERR
-lyht_find_rec(struct hash_table *ht, void *val_p, uint32_t hash, ly_bool mod, struct ht_rec **crec_p, uint32_t *col,
- struct ht_rec **rec_p)
+lyht_find_rec(const struct ly_ht *ht, void *val_p, uint32_t hash, ly_bool mod, lyht_value_equal_cb val_equal,
+ struct ly_ht_rec **crec_p, uint32_t *col, struct ly_ht_rec **rec_p)
{
- struct ht_rec *rec, *crec;
- uint32_t i, c;
- LY_ERR r;
+ uint32_t hlist_idx = hash & (ht->size - 1);
+ struct ly_ht_rec *rec;
+ uint32_t rec_idx;
if (crec_p) {
*crec_p = NULL;
@@ -567,53 +269,43 @@ lyht_find_rec(struct hash_table *ht, void *val_p, uint32_t hash, ly_bool mod, st
}
*rec_p = NULL;
- if (lyht_find_first(ht, hash, &rec)) {
- /* not found */
- return LY_ENOTFOUND;
- }
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, mod, ht->cb_data)) {
- /* even the value matches */
- if (crec_p) {
- *crec_p = rec;
- }
- if (col) {
- *col = 0;
- }
- *rec_p = rec;
- return LY_SUCCESS;
- }
-
- /* some collisions, we need to go through them, too */
- crec = rec;
- c = crec->hits;
- for (i = 1; i < c; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
- (void)r;
-
- /* compare values */
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, mod, ht->cb_data)) {
+ LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
+ if ((rec->hash == hash) && val_equal(val_p, &rec->val, mod, ht->cb_data)) {
if (crec_p) {
- *crec_p = crec;
- }
- if (col) {
- *col = i;
+ *crec_p = rec;
}
*rec_p = rec;
return LY_SUCCESS;
}
+
+ if (col) {
+ *col = *col + 1;
+ }
}
/* not found even in collisions */
return LY_ENOTFOUND;
}
-LY_ERR
-lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+LIBYANG_API_DEF LY_ERR
+lyht_find(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
+{
+ struct ly_ht_rec *rec;
+
+ lyht_find_rec(ht, val_p, hash, 0, ht->val_equal, NULL, NULL, &rec);
+
+ if (rec && match_p) {
+ *match_p = rec->val;
+ }
+ return rec ? LY_SUCCESS : LY_ENOTFOUND;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyht_find_with_val_cb(const struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb val_equal, void **match_p)
{
- struct ht_rec *rec;
+ struct ly_ht_rec *rec;
- lyht_find_rec(ht, val_p, hash, 0, NULL, NULL, &rec);
+ lyht_find_rec(ht, val_p, hash, 0, val_equal ? val_equal : ht->val_equal, NULL, NULL, &rec);
if (rec && match_p) {
*match_p = rec->val;
@@ -621,26 +313,23 @@ lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
return rec ? LY_SUCCESS : LY_ENOTFOUND;
}
-LY_ERR
-lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint32_t hash,
+LIBYANG_API_DEF LY_ERR
+lyht_find_next_with_collision_cb(const struct ly_ht *ht, void *val_p, uint32_t hash,
lyht_value_equal_cb collision_val_equal, void **match_p)
{
- struct ht_rec *rec, *crec;
- uint32_t i, c;
- LY_ERR r;
+ struct ly_ht_rec *rec, *crec;
+ uint32_t rec_idx;
+ uint32_t i;
/* find the record of the previously found value */
- if (lyht_find_rec(ht, val_p, hash, 1, &crec, &i, &rec)) {
+ if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, &crec, &i, &rec)) {
/* not found, cannot happen */
LOGINT_RET(NULL);
}
- /* go through collisions and find the next one after the previous one */
- c = crec->hits;
- for (++i; i < c; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
- (void)r;
+ for (rec_idx = rec->next, rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
+ rec_idx != LYHT_NO_RECORD;
+ rec_idx = rec->next, rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx)) {
if (rec->hash != hash) {
continue;
@@ -667,71 +356,51 @@ lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint32_t ha
return LY_ENOTFOUND;
}
-LY_ERR
-lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+LIBYANG_API_DEF LY_ERR
+lyht_find_next(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
{
return lyht_find_next_with_collision_cb(ht, val_p, hash, NULL, match_p);
}
-LY_ERR
-lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
- void **match_p)
+static LY_ERR
+_lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
+ void **match_p, int check)
{
+ uint32_t hlist_idx = hash & (ht->size - 1);
LY_ERR r, ret = LY_SUCCESS;
- struct ht_rec *rec, *crec = NULL;
- int32_t i;
+ struct ly_ht_rec *rec, *prev_rec;
lyht_value_equal_cb old_val_equal = NULL;
+ uint32_t rec_idx;
- if (!lyht_find_first(ht, hash, &rec)) {
- /* we found matching shortened hash */
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
- /* even the value matches */
- if (match_p) {
- *match_p = (void *)&rec->val;
+ if (check) {
+ if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, NULL, NULL, &rec) == LY_SUCCESS) {
+ if (rec && match_p) {
+ *match_p = rec->val;
}
return LY_EEXIST;
}
+ }
- /* some collisions, we need to go through them, too */
- crec = rec;
- for (i = 1; i < crec->hits; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
-
- /* compare values */
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
- if (match_p) {
- *match_p = (void *)&rec->val;
- }
- return LY_EEXIST;
- }
- }
+ rec_idx = ht->first_free_rec;
+ assert(rec_idx < ht->size);
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
+ ht->first_free_rec = rec->next;
- /* value not found, get the record where it will be inserted */
- r = lyht_find_collision(ht, &rec, crec);
- assert(r);
+ if (ht->hlists[hlist_idx].first == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].first = rec_idx;
+ } else {
+ prev_rec = lyht_get_rec(ht->recs, ht->rec_size, ht->hlists[hlist_idx].last);
+ prev_rec->next = rec_idx;
}
+ rec->next = LYHT_NO_RECORD;
+ ht->hlists[hlist_idx].last = rec_idx;
- /* insert it into the returned record */
- assert(rec->hits < 1);
- if (rec->hits < 0) {
- --ht->invalid;
- }
rec->hash = hash;
- rec->hits = 1;
- memcpy(&rec->val, val_p, ht->rec_size - (sizeof(struct ht_rec) - 1));
+ memcpy(&rec->val, val_p, ht->rec_size - SIZEOF_LY_HT_REC);
if (match_p) {
*match_p = (void *)&rec->val;
}
- if (crec) {
- /* there was a collision, increase hits */
- if (crec->hits == INT32_MAX) {
- LOGINT(NULL);
- }
- ++crec->hits;
- }
-
/* check size & enlarge if needed */
++ht->used;
if (ht->resize) {
@@ -746,10 +415,11 @@ lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
}
/* enlarge */
- ret = lyht_resize(ht, 1);
+ ret = lyht_resize(ht, 1, check);
/* if hash_table was resized, we need to find new matching value */
if ((ret == LY_SUCCESS) && match_p) {
- lyht_find(ht, val_p, hash, match_p);
+ ret = lyht_find(ht, val_p, hash, match_p);
+ assert(!ret);
}
if (resize_val_equal) {
@@ -760,61 +430,66 @@ lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
return ret;
}
-LY_ERR
-lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+LIBYANG_API_DEF LY_ERR
+lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
+ void **match_p)
{
- return lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p);
+ return _lyht_insert_with_resize_cb(ht, val_p, hash, resize_val_equal, match_p, 1);
}
-LY_ERR
-lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal)
+LIBYANG_API_DEF LY_ERR
+lyht_insert(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
{
- struct ht_rec *rec, *crec;
- int32_t i;
- ly_bool first_matched = 0;
- LY_ERR r, ret = LY_SUCCESS;
- lyht_value_equal_cb old_val_equal;
+ return _lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p, 1);
+}
- LY_CHECK_ERR_RET(lyht_find_first(ht, hash, &rec), LOGARG(NULL, hash), LY_ENOTFOUND); /* hash not found */
+LIBYANG_API_DEF LY_ERR
+lyht_insert_no_check(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
+{
+ return _lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p, 0);
+}
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
- /* even the value matches */
- first_matched = 1;
- }
+LIBYANG_API_DEF LY_ERR
+lyht_remove_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal)
+{
+ struct ly_ht_rec *found_rec, *prev_rec, *rec;
+ uint32_t hlist_idx = hash & (ht->size - 1);
+ LY_ERR r, ret = LY_SUCCESS;
+ lyht_value_equal_cb old_val_equal = NULL;
+ uint32_t prev_rec_idx;
+ uint32_t rec_idx;
- /* we always need to go through collisions */
- crec = rec;
- for (i = 1; i < crec->hits; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
+ if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, NULL, NULL, &found_rec)) {
+ LOGARG(NULL, hash);
+ return LY_ENOTFOUND;
+ }
- /* compare values */
- if (!first_matched && (rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
+ prev_rec_idx = LYHT_NO_RECORD;
+ LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
+ if (rec == found_rec) {
break;
}
+ prev_rec_idx = rec_idx;
}
- if (i < crec->hits) {
- /* one of collisions matched, reduce collision count, remove the record */
- assert(!first_matched);
- --crec->hits;
- rec->hits = -1;
- } else if (first_matched) {
- /* the first record matches */
- if (crec != rec) {
- /* ... so put the last collision in its place */
- rec->hits = crec->hits - 1;
- memcpy(crec, rec, ht->rec_size);
+ if (prev_rec_idx == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].first = rec->next;
+ if (rec->next == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].last = LYHT_NO_RECORD;
}
- rec->hits = -1;
} else {
- /* value not found even in collisions */
- return LY_ENOTFOUND;
+ prev_rec = lyht_get_rec(ht->recs, ht->rec_size, prev_rec_idx);
+ prev_rec->next = rec->next;
+ if (rec->next == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].last = prev_rec_idx;
+ }
}
+ rec->next = ht->first_free_rec;
+ ht->first_free_rec = rec_idx;
+
/* check size & shrink if needed */
--ht->used;
- ++ht->invalid;
if (ht->resize == 2) {
r = (ht->used * LYHT_HUNDRED_PERCENTAGE) / ht->size;
if ((r < LYHT_SHRINK_PERCENTAGE) && (ht->size > LYHT_MIN_SIZE)) {
@@ -823,7 +498,7 @@ lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
}
/* shrink */
- ret = lyht_resize(ht, -1);
+ ret = lyht_resize(ht, -1, 1);
if (resize_val_equal) {
lyht_set_cb(ht, old_val_equal);
@@ -831,50 +506,29 @@ lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
}
}
- /* rehash all records if needed */
- r = ((ht->size - ht->used - ht->invalid) * 100) / ht->size;
- if (r < LYHT_REHASH_PERCENTAGE) {
- if (resize_val_equal) {
- old_val_equal = lyht_set_cb(ht, resize_val_equal);
- }
-
- /* rehash */
- ret = lyht_resize(ht, 0);
-
- if (resize_val_equal) {
- lyht_set_cb(ht, old_val_equal);
- }
- }
-
return ret;
}
-LY_ERR
-lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash)
+LIBYANG_API_DEF LY_ERR
+lyht_remove(struct ly_ht *ht, void *val_p, uint32_t hash)
{
return lyht_remove_with_resize_cb(ht, val_p, hash, NULL);
}
-uint32_t
+LIBYANG_API_DEF uint32_t
lyht_get_fixed_size(uint32_t item_count)
{
- uint32_t i, size = 0;
-
- /* detect number of upper zero bits in the items' counter value ... */
- for (i = (sizeof item_count * CHAR_BIT) - 1; i > 0; i--) {
- size = item_count << i;
- size = size >> i;
- if (size == item_count) {
- break;
- }
+ if (item_count == 0) {
+ return 1;
}
- assert(i);
-
- /* ... and then we convert it to the position of the highest non-zero bit ... */
- i = (sizeof item_count * CHAR_BIT) - i;
- /* ... and by using it to shift 1 to the left we get the closest sufficient hash table size */
- size = 1 << i;
+ /* return next power of 2 (greater or equal) */
+ item_count--;
+ item_count |= item_count >> 1;
+ item_count |= item_count >> 2;
+ item_count |= item_count >> 4;
+ item_count |= item_count >> 8;
+ item_count |= item_count >> 16;
- return size;
+ return item_count + 1;
}
diff --git a/src/hash_table.h b/src/hash_table.h
index 91ae63d..5780f1e 100644
--- a/src/hash_table.h
+++ b/src/hash_table.h
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libyang hash table
*
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 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.
@@ -16,27 +16,49 @@
#ifndef LY_HASH_TABLE_H_
#define LY_HASH_TABLE_H_
-#include <pthread.h>
#include <stddef.h>
#include <stdint.h>
-#include "compat.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include "log.h"
/**
+ * @struct ly_ht
+ * @brief libyang hash table.
+ */
+struct ly_ht;
+
+/**
* @brief Compute hash from (several) string(s).
*
* Usage:
* - init hash to 0
- * - repeatedly call ::dict_hash_multi(), provide hash from the last call
- * - call ::dict_hash_multi() with key_part = NULL to finish the hash
+ * - repeatedly call ::lyht_hash_multi(), provide hash from the last call
+ * - call ::lyht_hash_multi() with key_part = NULL to finish the hash
+ *
+ * @param[in] hash Previous hash.
+ * @param[in] key_part Next key to hash,
+ * @param[in] len Length of @p key_part.
+ * @return Hash with the next key.
*/
-uint32_t dict_hash_multi(uint32_t hash, const char *key_part, size_t len);
+LIBYANG_API_DECL uint32_t lyht_hash_multi(uint32_t hash, const char *key_part, size_t len);
/**
* @brief Compute hash from a string.
+ *
+ * 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.
+ *
+ * @param[in] key Key to hash.
+ * @param[in] len Length of @p key.
+ * @return Hash of the key.
*/
-uint32_t dict_hash(const char *key, size_t len);
+LIBYANG_API_DECL uint32_t lyht_hash(const char *key, size_t len);
/**
* @brief Callback for checking hash table values equivalence.
@@ -49,82 +71,6 @@ uint32_t dict_hash(const char *key, size_t len);
*/
typedef ly_bool (*lyht_value_equal_cb)(void *val1_p, void *val2_p, ly_bool mod, void *cb_data);
-/** reference value for 100% */
-#define LYHT_HUNDRED_PERCENTAGE 100
-
-/** when the table is at least this much percent full, it is enlarged (double the size) */
-#define LYHT_ENLARGE_PERCENTAGE 75
-
-/** only once the table is this much percent full, enable shrinking */
-#define LYHT_FIRST_SHRINK_PERCENTAGE 50
-
-/** when the table is less than this much percent full, it is shrunk (half the size) */
-#define LYHT_SHRINK_PERCENTAGE 25
-
-/** when the table has less than this much percent empty records, it is rehashed to get rid of all the invalid records */
-#define LYHT_REHASH_PERCENTAGE 2
-
-/** never shrink beyond this size */
-#define LYHT_MIN_SIZE 8
-
-/**
- * @brief Generic hash table record.
- */
-struct ht_rec {
- uint32_t hash; /* hash of the value */
- int32_t hits; /* collision/overflow value count - 1 (a filled entry has 1 hit,
- * special value -1 means a deleted record) */
- unsigned char val[1]; /* arbitrary-size value */
-} _PACKED;
-
-/**
- * @brief (Very) generic hash table.
- *
- * Hash table with open addressing collision resolution and
- * linear probing of interval 1 (next free record is used).
- * Removal is lazy (removed records are only marked), but
- * if possible, they are fully emptied.
- */
-struct hash_table {
- uint32_t used; /* number of values stored in the hash table (filled records) */
- uint32_t size; /* always holds 2^x == size (is power of 2), actually number of records allocated */
- uint32_t invalid; /* number of invalid records (deleted) */
- lyht_value_equal_cb val_equal; /* callback for testing value equivalence */
- void *cb_data; /* user data callback arbitrary value */
- uint16_t resize; /* 0 - resizing is disabled, *
- * 1 - enlarging is enabled, *
- * 2 - both shrinking and enlarging is enabled */
- uint16_t rec_size; /* real size (in bytes) of one record for accessing recs array */
- unsigned char *recs; /* pointer to the hash table itself (array of struct ht_rec) */
-};
-
-struct dict_rec {
- char *value;
- uint32_t refcount;
-};
-
-/**
- * dictionary to store repeating strings
- */
-struct dict_table {
- struct hash_table *hash_tab;
- pthread_mutex_t lock;
-};
-
-/**
- * @brief Initiate content (non-zero values) of the dictionary
- *
- * @param[in] dict Dictionary table to initiate
- */
-void lydict_init(struct dict_table *dict);
-
-/**
- * @brief Cleanup the dictionary content
- *
- * @param[in] dict Dictionary table to cleanup
- */
-void lydict_clean(struct dict_table *dict);
-
/**
* @brief Create new hash table.
*
@@ -135,7 +81,8 @@ void lydict_clean(struct dict_table *dict);
* @param[in] resize Whether to resize the table on too few/too many records taken.
* @return Empty hash table, NULL on error.
*/
-struct hash_table *lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data, uint16_t resize);
+LIBYANG_API_DECL struct ly_ht *lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data,
+ uint16_t resize);
/**
* @brief Set hash table value equal callback.
@@ -144,7 +91,7 @@ struct hash_table *lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_c
* @param[in] new_val_equal New callback for checking value equivalence.
* @return Previous callback for checking value equivalence.
*/
-lyht_value_equal_cb lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_val_equal);
+LIBYANG_API_DECL lyht_value_equal_cb lyht_set_cb(struct ly_ht *ht, lyht_value_equal_cb new_val_equal);
/**
* @brief Set hash table value equal callback user data.
@@ -153,7 +100,7 @@ lyht_value_equal_cb lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_v
* @param[in] new_cb_data New data for values callback.
* @return Previous data for values callback.
*/
-void *lyht_set_cb_data(struct hash_table *ht, void *new_cb_data);
+LIBYANG_API_DECL void *lyht_set_cb_data(struct ly_ht *ht, void *new_cb_data);
/**
* @brief Make a duplicate of an existing hash table.
@@ -161,14 +108,15 @@ void *lyht_set_cb_data(struct hash_table *ht, void *new_cb_data);
* @param[in] orig Original hash table to duplicate.
* @return Duplicated hash table @p orig, NULL on error.
*/
-struct hash_table *lyht_dup(const struct hash_table *orig);
+LIBYANG_API_DECL struct ly_ht *lyht_dup(const struct ly_ht *orig);
/**
* @brief Free a hash table.
*
* @param[in] ht Hash table to be freed.
+ * @param[in] val_free Optional callback for freeing all the stored values, @p val_p is a pointer to a stored value.
*/
-void lyht_free(struct hash_table *ht);
+LIBYANG_API_DECL void lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p));
/**
* @brief Find a value in a hash table.
@@ -180,7 +128,21 @@ void lyht_free(struct hash_table *ht);
* @return LY_SUCCESS if value was found,
* @return LY_ENOTFOUND if not found.
*/
-LY_ERR lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_find(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
+
+/**
+ * @brief Find a value in a hash table but use a custom val_equal callback.
+ *
+ * @param[in] ht Hash table to search in.
+ * @param[in] val_p Pointer to the value to find.
+ * @param[in] hash Hash of the stored value.
+ * @param[in] val_equal Callback for checking value equivalence.
+ * @param[out] match_p Pointer to the matching value, optional.
+ * @return LY_SUCCESS if value was found,
+ * @return LY_ENOTFOUND if not found.
+ */
+LIBYANG_API_DECL LY_ERR lyht_find_with_val_cb(const struct ly_ht *ht, void *val_p, uint32_t hash,
+ lyht_value_equal_cb val_equal, void **match_p);
/**
* @brief Find another equal value in the hash table.
@@ -192,7 +154,7 @@ LY_ERR lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match
* @return LY_SUCCESS if value was found,
* @return LY_ENOTFOUND if not found.
*/
-LY_ERR lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_find_next(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
/**
* @brief Find another equal value in the hash table. Same functionality as ::lyht_find_next()
@@ -206,7 +168,7 @@ LY_ERR lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **
* @return LY_SUCCESS if value was found,
* @return LY_ENOTFOUND if not found.
*/
-LY_ERR lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint32_t hash,
+LIBYANG_API_DECL LY_ERR lyht_find_next_with_collision_cb(const struct ly_ht *ht, void *val_p, uint32_t hash,
lyht_value_equal_cb collision_val_equal, void **match_p);
/**
@@ -221,7 +183,20 @@ LY_ERR lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint
* @return LY_EEXIST in case the value is already present.
* @return LY_EMEM in case of memory allocation failure.
*/
-LY_ERR lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_insert(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
+
+/**
+ * @brief Insert a value into a hash table, without checking whether the value has already been inserted.
+ *
+ * @param[in] ht Hash table to insert into.
+ * @param[in] val_p Pointer to the value to insert. Be careful, if the values stored in the hash table
+ * are pointers, @p val_p must be a pointer to a pointer.
+ * @param[in] hash Hash of the stored value.
+ * @param[out] match_p Pointer to the stored value, optional
+ * @return LY_SUCCESS on success,
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+LIBYANG_API_DECL LY_ERR lyht_insert_no_check(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
/**
* @brief Insert a value into hash table. Same functionality as ::lyht_insert()
@@ -238,8 +213,8 @@ LY_ERR lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **mat
* @return LY_EEXIST in case the value is already present.
* @return LY_EMEM in case of memory allocation failure.
*/
-LY_ERR lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
- void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash,
+ lyht_value_equal_cb resize_val_equal, void **match_p);
/**
* @brief Remove a value from a hash table.
@@ -251,7 +226,7 @@ LY_ERR lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t h
* @return LY_SUCCESS on success,
* @return LY_ENOTFOUND if value was not found.
*/
-LY_ERR lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash);
+LIBYANG_API_DECL LY_ERR lyht_remove(struct ly_ht *ht, void *val_p, uint32_t hash);
/**
* @brief Remove a value from a hash table. Same functionality as ::lyht_remove()
@@ -266,7 +241,8 @@ LY_ERR lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash);
* @return LY_SUCCESS on success,
* @return LY_ENOTFOUND if value was not found.
*/
-LY_ERR lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal);
+LIBYANG_API_DECL LY_ERR lyht_remove_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash,
+ lyht_value_equal_cb resize_val_equal);
/**
* @brief Get suitable size of a hash table for a fixed number of items.
@@ -274,6 +250,10 @@ LY_ERR lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t h
* @param[in] item_count Number of stored items.
* @return Hash table size.
*/
-uint32_t lyht_get_fixed_size(uint32_t item_count);
+LIBYANG_API_DECL uint32_t lyht_get_fixed_size(uint32_t item_count);
+
+#ifdef __cplusplus
+}
+#endif
#endif /* LY_HASH_TABLE_H_ */
diff --git a/src/hash_table_internal.h b/src/hash_table_internal.h
new file mode 100644
index 0000000..22b6cf4
--- /dev/null
+++ b/src/hash_table_internal.h
@@ -0,0 +1,146 @@
+/**
+ * @file hash_table_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang hash table internal header
+ *
+ * Copyright (c) 2015 - 2023 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_HASH_TABLE_INTERNAL_H_
+#define LY_HASH_TABLE_INTERNAL_H_
+
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "compat.h"
+#include "hash_table.h"
+
+/** reference value for 100% */
+#define LYHT_HUNDRED_PERCENTAGE 100
+
+/** when the table is at least this much percent full, it is enlarged (double the size) */
+#define LYHT_ENLARGE_PERCENTAGE 75
+
+/** only once the table is this much percent full, enable shrinking */
+#define LYHT_FIRST_SHRINK_PERCENTAGE 50
+
+/** when the table is less than this much percent full, it is shrunk (half the size) */
+#define LYHT_SHRINK_PERCENTAGE 25
+
+/** never shrink beyond this size */
+#define LYHT_MIN_SIZE 8
+
+/**
+ * @brief Generic hash table record.
+ */
+struct ly_ht_rec {
+ uint32_t hash; /* hash of the value */
+ uint32_t next; /* index of next collision */
+ unsigned char val[1]; /* arbitrary-size value */
+} _PACKED;
+
+/* real size, without taking the val[1] in account */
+#define SIZEOF_LY_HT_REC (sizeof(struct ly_ht_rec) - 1)
+
+struct ly_ht_hlist {
+ uint32_t first;
+ uint32_t last;
+};
+
+/**
+ * @brief (Very) generic hash table.
+ *
+ * This structure implements a hash table, providing fast accesses to stored
+ * values from their hash.
+ *
+ * The hash table structure contains 2 pointers to tables that are allocated
+ * at initialization:
+ * - a table of records: each record is composed of a struct ly_ht_rec header,
+ * followed by the user value. The header contains the hash value and a
+ * next index that can be used to chain records.
+ * - a table of list heads: each list head entry contains the index of the
+ * first record in the records table whose hash (modulo hash table size)
+ * is equal to the index of the list head entry. The other matching records
+ * are chained together.
+ *
+ * The unused records are chained in first_free_rec, which contains the index
+ * of the first unused record entry in the records table.
+ *
+ * The LYHT_NO_RECORD magic value is used when an index points to nothing.
+ */
+struct ly_ht {
+ uint32_t used; /* number of values stored in the hash table (filled records) */
+ uint32_t size; /* always holds 2^x == size (is power of 2), actually number of records allocated */
+ lyht_value_equal_cb val_equal; /* callback for testing value equivalence */
+ void *cb_data; /* user data callback arbitrary value */
+ uint16_t resize; /* 0 - resizing is disabled, *
+ * 1 - enlarging is enabled, *
+ * 2 - both shrinking and enlarging is enabled */
+ uint16_t rec_size; /* real size (in bytes) of one record for accessing recs array */
+ uint32_t first_free_rec; /* index of the first free record */
+ struct ly_ht_hlist *hlists; /* pointer to the hlists table */
+ unsigned char *recs; /* pointer to the hash table itself (array of struct ht_rec) */
+};
+
+/* index that points to nothing */
+#define LYHT_NO_RECORD UINT32_MAX
+
+/* get the record associated to */
+static inline struct ly_ht_rec *
+lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx)
+{
+ return (struct ly_ht_rec *)&recs[idx * rec_size];
+}
+
+/* Iterate all records in a hlist */
+#define LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) \
+ for (rec_idx = ht->hlists[hlist_idx].first, \
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx); \
+ rec_idx != LYHT_NO_RECORD; \
+ rec_idx = rec->next, \
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx))
+
+/* Iterate all records in the hash table */
+#define LYHT_ITER_ALL_RECS(ht, hlist_idx, rec_idx, rec) \
+ for (hlist_idx = 0; hlist_idx < ht->size; hlist_idx++) \
+ LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec)
+
+/**
+ * @brief Dictionary hash table record.
+ */
+struct ly_dict_rec {
+ char *value; /**< stored string */
+ uint32_t refcount; /**< reference count of the string */
+};
+
+/**
+ * @brief Dictionary for storing repeated strings.
+ */
+struct ly_dict {
+ struct ly_ht *hash_tab;
+ pthread_mutex_t lock;
+};
+
+/**
+ * @brief Initiate content (non-zero values) of the dictionary
+ *
+ * @param[in] dict Dictionary table to initiate
+ */
+void lydict_init(struct ly_dict *dict);
+
+/**
+ * @brief Cleanup the dictionary content
+ *
+ * @param[in] dict Dictionary table to cleanup
+ */
+void lydict_clean(struct ly_dict *dict);
+
+#endif /* LY_HASH_TABLE_INTERNAL_H_ */
diff --git a/src/json.c b/src/json.c
index 5c45c8c..53f41dc 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1,9 +1,10 @@
/**
* @file json.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Generic JSON format parser for libyang
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 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.
@@ -30,30 +31,30 @@ lyjson_token2str(enum LYJSON_PARSER_STATUS status)
switch (status) {
case LYJSON_ERROR:
return "error";
- case LYJSON_ROOT:
- return "document root";
- case LYJSON_FALSE:
- return "false";
- case LYJSON_TRUE:
- return "true";
- case LYJSON_NULL:
- return "null";
case LYJSON_OBJECT:
return "object";
+ case LYJSON_OBJECT_NEXT:
+ return "object next";
case LYJSON_OBJECT_CLOSED:
return "object closed";
- case LYJSON_OBJECT_EMPTY:
- return "empty object";
case LYJSON_ARRAY:
return "array";
+ case LYJSON_ARRAY_NEXT:
+ return "array next";
case LYJSON_ARRAY_CLOSED:
return "array closed";
- case LYJSON_ARRAY_EMPTY:
- return "empty array";
+ case LYJSON_OBJECT_NAME:
+ return "object name";
case LYJSON_NUMBER:
return "number";
case LYJSON_STRING:
return "string";
+ case LYJSON_TRUE:
+ return "true";
+ case LYJSON_FALSE:
+ return "false";
+ case LYJSON_NULL:
+ return "null";
case LYJSON_END:
return "end of input";
}
@@ -61,25 +62,48 @@ lyjson_token2str(enum LYJSON_PARSER_STATUS status)
return "";
}
-static LY_ERR
-skip_ws(struct lyjson_ctx *jsonctx)
+enum LYJSON_PARSER_STATUS
+lyjson_ctx_status(struct lyjson_ctx *jsonctx)
{
- /* skip leading whitespaces */
- while (*jsonctx->in->current != '\0' && is_jsonws(*jsonctx->in->current)) {
+ assert(jsonctx);
+
+ if (!jsonctx->status.count) {
+ return LYJSON_END;
+ }
+
+ return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - 1];
+}
+
+uint32_t
+lyjson_ctx_depth(struct lyjson_ctx *jsonctx)
+{
+ return jsonctx->status.count;
+}
+
+/**
+ * @brief Skip WS in the JSON context.
+ *
+ * @param[in] jsonctx JSON parser context.
+ */
+static void
+lyjson_skip_ws(struct lyjson_ctx *jsonctx)
+{
+ /* skip whitespaces */
+ while (is_jsonws(*jsonctx->in->current)) {
if (*jsonctx->in->current == '\n') {
LY_IN_NEW_LINE(jsonctx->in);
}
ly_in_skip(jsonctx->in, 1);
}
- if (*jsonctx->in->current == '\0') {
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_END);
- }
-
- return LY_SUCCESS;
}
-/*
- * @brief Set value corresponding to the current context's status
+/**
+ * @brief Set value in the JSON context.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @param[in] value Value to set.
+ * @param[in] value_len Length of @p value.
+ * @param[in] dynamic Whether @p value is dynamically-allocated.
*/
static void
lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic)
@@ -94,48 +118,24 @@ lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value
jsonctx->dynamic = dynamic;
}
-static LY_ERR
-lyjson_check_next(struct lyjson_ctx *jsonctx)
-{
- if (jsonctx->status.count == 1) {
- /* top level value (JSON-text), ws expected */
- if ((*jsonctx->in->current == '\0') || is_jsonws(*jsonctx->in->current)) {
- return LY_SUCCESS;
- }
- } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_OBJECT) {
- LY_CHECK_RET(skip_ws(jsonctx));
- if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == '}')) {
- return LY_SUCCESS;
- }
- } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_ARRAY) {
- LY_CHECK_RET(skip_ws(jsonctx));
- if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == ']')) {
- return LY_SUCCESS;
- }
- }
-
- LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Unexpected character \"%c\" after JSON %s.",
- *jsonctx->in->current, lyjson_token2str(lyjson_ctx_status(jsonctx, 0)));
- return LY_EVALID;
-}
-
/**
- * Input is expected to start after the opening quotation-mark.
- * When succeeds, input is moved after the closing quotation-mark.
+ * @brief Parse a JSON string (starting after double quotes) and store it in the context.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @return LY_ERR value.
*/
static LY_ERR
-lyjson_string_(struct lyjson_ctx *jsonctx)
+lyjson_string(struct lyjson_ctx *jsonctx)
{
-#define BUFSIZE 24
-#define BUFSIZE_STEP 128
-
- const char *in = jsonctx->in->current, *start;
+ const char *in = jsonctx->in->current, *start, *c;
char *buf = NULL;
size_t offset; /* read offset in input buffer */
size_t len; /* length of the output string (write offset in output buffer) */
size_t size = 0; /* size of the output buffer */
size_t u;
uint64_t start_line;
+ uint32_t value;
+ uint8_t i;
assert(jsonctx);
@@ -146,17 +146,15 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
/* parse */
while (in[offset]) {
- if (in[offset] == '\\') {
+ switch (in[offset]) {
+ case '\\':
/* escape sequence */
- const char *slash = &in[offset];
- uint32_t value;
- uint8_t i = 1;
-
+ c = &in[offset];
if (!buf) {
/* prepare output buffer */
- buf = malloc(BUFSIZE);
+ buf = malloc(LYJSON_STRING_BUF_START);
LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
- size = BUFSIZE;
+ size = LYJSON_STRING_BUF_START;
}
/* allocate enough for the offset and next character,
@@ -165,10 +163,10 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
if (len + offset + 4 >= size) {
size_t increment;
- for (increment = BUFSIZE_STEP; len + offset + 4 >= size + increment; increment += BUFSIZE_STEP) {}
+ for (increment = LYJSON_STRING_BUF_STEP; len + offset + 4 >= size + increment; increment += LYJSON_STRING_BUF_STEP) {}
buf = ly_realloc(buf, size + increment);
LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
- size += BUFSIZE_STEP;
+ size += LYJSON_STRING_BUF_STEP;
}
if (offset) {
@@ -179,6 +177,7 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
offset = 0;
}
+ i = 1;
switch (in[++offset]) {
case '"':
/* quotation mark */
@@ -217,7 +216,7 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
offset++;
for (value = i = 0; i < 4; i++) {
if (!in[offset + i]) {
- LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", slash);
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", c);
goto error;
} else if (isdigit(in[offset + i])) {
u = (in[offset + i] - '0');
@@ -239,13 +238,14 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
offset += i; /* add read escaped characters */
LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).",
- (int)(&in[offset] - slash), slash, value),
+ (int)(&in[offset] - c), c, value),
error);
len += u; /* update number of bytes in buffer */
in += offset; /* move the input by the processed bytes stored in the buffer ... */
offset = 0; /* ... and reset the offset index for future moving data into buffer */
+ break;
- } else if (in[offset] == '"') {
+ case '"':
/* end of string */
if (buf) {
/* realloc exact size string */
@@ -263,22 +263,21 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
++offset;
in += offset;
goto success;
- } else {
- /* get it as UTF-8 character for check */
- const char *c = &in[offset];
- uint32_t code = 0;
- size_t code_len = 0;
- LY_CHECK_ERR_GOTO(ly_getutf8(&c, &code, &code_len),
+ default:
+ /* get it as UTF-8 character for check */
+ c = &in[offset];
+ LY_CHECK_ERR_GOTO(ly_getutf8(&c, &value, &u),
LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error);
- LY_CHECK_ERR_GOTO(!is_jsonstrchar(code),
+ LY_CHECK_ERR_GOTO(!is_jsonstrchar(value),
LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08x).",
- (int)(&in[offset] - start + code_len), start, code),
+ (int)(&in[offset] - start + u), start, value),
error);
/* character is ok, continue */
- offset += code_len;
+ offset += u;
+ break;
}
}
@@ -299,24 +298,6 @@ success:
}
return LY_SUCCESS;
-
-#undef BUFSIZE
-#undef BUFSIZE_STEP
-}
-
-/*
- *
- * Wrapper around lyjson_string_() adding LYJSON_STRING status into context to allow using lyjson_string_() for parsing object's name.
- */
-static LY_ERR
-lyjson_string(struct lyjson_ctx *jsonctx)
-{
- LY_CHECK_RET(lyjson_string_(jsonctx));
-
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_STRING);
- LY_CHECK_RET(lyjson_check_next(jsonctx));
-
- return LY_SUCCESS;
}
/**
@@ -414,8 +395,7 @@ lyjson_get_buffer_for_number(const struct ly_ctx *ctx, uint64_t num_len, char **
* @return Number of characters written to the @p dst.
*/
static uint32_t
-lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len,
- char *dec_point, int32_t dp_position, char *dst)
+lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len, char *dec_point, int32_t dp_position, char *dst)
{
int32_t dec_point_idx;
int32_t n, d;
@@ -456,8 +436,8 @@ lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len,
* @return LY_ERR value.
*/
static LY_ERR
-lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent,
- uint64_t total_len, char **res, size_t *res_len)
+lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent, uint64_t total_len, char **res,
+ size_t *res_len)
{
#define MAYBE_WRITE_MINUS(ARRAY, INDEX, FLAG) \
@@ -642,6 +622,12 @@ lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent
return LY_SUCCESS;
}
+/**
+ * @brief Parse a JSON number and store it in the context.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @return LY_ERR value.
+ */
static LY_ERR
lyjson_number(struct lyjson_ctx *jsonctx)
{
@@ -713,140 +699,270 @@ invalid_character:
}
ly_in_skip(jsonctx->in, offset);
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NUMBER);
- LY_CHECK_RET(lyjson_check_next(jsonctx));
-
return LY_SUCCESS;
}
-static LY_ERR
-lyjson_object_name(struct lyjson_ctx *jsonctx)
+LY_ERR
+lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
{
- if (*jsonctx->in->current != '"') {
- LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
- jsonctx->in->current, "a JSON object's member");
- return LY_EVALID;
- }
- ly_in_skip(jsonctx->in, 1);
+ LY_ERR ret = LY_SUCCESS;
+ struct lyjson_ctx *jsonctx;
- LY_CHECK_RET(lyjson_string_(jsonctx));
- LY_CHECK_RET(skip_ws(jsonctx));
- if (*jsonctx->in->current != ':') {
- LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
- "a JSON object's name-separator ':'");
- return LY_EVALID;
+ assert(ctx && in && jsonctx_p);
+
+ /* new context */
+ jsonctx = calloc(1, sizeof *jsonctx);
+ LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
+ jsonctx->ctx = ctx;
+ jsonctx->in = in;
+
+ LOG_LOCSET(NULL, NULL, NULL, in);
+
+ /* WS are always expected to be skipped */
+ lyjson_skip_ws(jsonctx);
+
+ if (jsonctx->in->current[0] == '\0') {
+ /* empty file, invalid */
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON file.");
+ ret = LY_EVALID;
+ goto cleanup;
}
- ly_in_skip(jsonctx->in, 1);
- LY_CHECK_RET(skip_ws(jsonctx));
- return LY_SUCCESS;
+ /* start JSON parsing */
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(jsonctx, NULL), cleanup);
+
+cleanup:
+ if (ret) {
+ lyjson_ctx_free(jsonctx);
+ } else {
+ *jsonctx_p = jsonctx;
+ }
+ return ret;
}
+/**
+ * @brief Parse next JSON token, object-name is expected.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @return LY_ERR value.
+ */
static LY_ERR
-lyjson_object(struct lyjson_ctx *jsonctx)
+lyjson_next_object_name(struct lyjson_ctx *jsonctx)
{
- LY_CHECK_RET(skip_ws(jsonctx));
+ switch (*jsonctx->in->current) {
+ case '\0':
+ /* EOF */
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
- if (*jsonctx->in->current == '}') {
- assert(jsonctx->depth);
- jsonctx->depth--;
- /* empty object */
+ case '"':
+ /* object name */
ly_in_skip(jsonctx->in, 1);
- lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_EMPTY);
- return LY_SUCCESS;
- }
+ LY_CHECK_RET(lyjson_string(jsonctx));
+ lyjson_skip_ws(jsonctx);
- LY_CHECK_RET(lyjson_object_name(jsonctx));
+ if (*jsonctx->in->current != ':') {
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
+ "a JSON value name-separator ':'");
+ return LY_EVALID;
+ }
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NAME);
+ break;
- /* output data are set by lyjson_string_() */
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT);
+ case '}':
+ /* object end */
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
+ break;
+
+ default:
+ /* unexpected value */
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
+ jsonctx->in->current, "a JSON object name");
+ return LY_EVALID;
+ }
return LY_SUCCESS;
}
/**
- * @brief Process JSON array envelope
+ * @brief Parse next JSON token, value is expected.
*
- * @param[in] jsonctx JSON parser context
- * @return LY_SUCCESS or LY_EMEM
+ * @param[in] jsonctx JSON parser context.
+ * @param[in] array_end Whether array-end is accepted or not.
+ * @return LY_ERR value.
*/
static LY_ERR
-lyjson_array(struct lyjson_ctx *jsonctx)
+lyjson_next_value(struct lyjson_ctx *jsonctx, ly_bool array_end)
{
- LY_CHECK_RET(skip_ws(jsonctx));
+ switch (*jsonctx->in->current) {
+ case '\0':
+ /* EOF */
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
- if (*jsonctx->in->current == ']') {
- /* empty array */
+ case '"':
+ /* string */
+ ly_in_skip(jsonctx->in, 1);
+ LY_CHECK_RET(lyjson_string(jsonctx));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_STRING);
+ break;
+
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* number */
+ LY_CHECK_RET(lyjson_number(jsonctx));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NUMBER);
+ break;
+
+ case '{':
+ /* object */
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT);
+ break;
+
+ case '[':
+ /* array */
ly_in_skip(jsonctx->in, 1);
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_EMPTY);
- } else {
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY);
- }
+ break;
- /* erase previous values, array has no value on its own */
- lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
+ case 't':
+ if (strncmp(jsonctx->in->current + 1, "rue", ly_strlen_const("rue"))) {
+ goto unexpected_value;
+ }
- return LY_SUCCESS;
-}
+ /* true */
+ lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
+ ly_in_skip(jsonctx->in, ly_strlen_const("true"));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_TRUE);
+ break;
-static LY_ERR
-lyjson_value(struct lyjson_ctx *jsonctx)
-{
- if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
- return LY_SUCCESS;
- }
+ case 'f':
+ if (strncmp(jsonctx->in->current + 1, "alse", ly_strlen_const("alse"))) {
+ goto unexpected_value;
+ }
- if ((*jsonctx->in->current == 'f') && !strncmp(jsonctx->in->current, "false", ly_strlen_const("false"))) {
/* false */
lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
ly_in_skip(jsonctx->in, ly_strlen_const("false"));
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_FALSE);
- LY_CHECK_RET(lyjson_check_next(jsonctx));
+ break;
- } else if ((*jsonctx->in->current == 't') && !strncmp(jsonctx->in->current, "true", ly_strlen_const("true"))) {
- /* true */
- lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
- ly_in_skip(jsonctx->in, ly_strlen_const("true"));
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_TRUE);
- LY_CHECK_RET(lyjson_check_next(jsonctx));
+ case 'n':
+ if (strncmp(jsonctx->in->current + 1, "ull", ly_strlen_const("ull"))) {
+ goto unexpected_value;
+ }
- } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) {
- /* none */
+ /* null */
lyjson_ctx_set_value(jsonctx, "", 0, 0);
ly_in_skip(jsonctx->in, ly_strlen_const("null"));
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NULL);
- LY_CHECK_RET(lyjson_check_next(jsonctx));
+ break;
- } else if (*jsonctx->in->current == '"') {
- /* string */
+ case ']':
+ if (!array_end) {
+ goto unexpected_value;
+ }
+
+ /* array end */
ly_in_skip(jsonctx->in, 1);
- LY_CHECK_RET(lyjson_string(jsonctx));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
+ break;
- } else if (*jsonctx->in->current == '[') {
- /* array */
+ default:
+unexpected_value:
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
+ jsonctx->in->current, "a JSON value");
+ return LY_EVALID;
+ }
+
+ if (jsonctx->status.count > LY_MAX_BLOCK_DEPTH * 10) {
+ LOGERR(jsonctx->ctx, LY_EINVAL, "Maximum number %d of nestings has been exceeded.", LY_MAX_BLOCK_DEPTH * 10);
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse next JSON token, object-next-item is expected.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyjson_next_object_item(struct lyjson_ctx *jsonctx)
+{
+ switch (*jsonctx->in->current) {
+ case '\0':
+ /* EOF */
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
+
+ case '}':
+ /* object end */
ly_in_skip(jsonctx->in, 1);
- LY_CHECK_RET(lyjson_array(jsonctx));
-
- } else if (*jsonctx->in->current == '{') {
- jsonctx->depth++;
- if (jsonctx->depth > LY_MAX_BLOCK_DEPTH) {
- LOGERR(jsonctx->ctx, LY_EINVAL,
- "The maximum number of block nestings has been exceeded.");
- return LY_EINVAL;
- }
- /* object */
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
+ break;
+
+ case ',':
+ /* next object item */
ly_in_skip(jsonctx->in, 1);
- LY_CHECK_RET(lyjson_object(jsonctx));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NEXT);
+ break;
- } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) {
- /* number */
- LY_CHECK_RET(lyjson_number(jsonctx));
+ default:
+ /* unexpected value */
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
+ jsonctx->in->current, "a JSON object-end or next item");
+ return LY_EVALID;
+ }
- } else {
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse next JSON token, array-next-item is expected.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyjson_next_array_item(struct lyjson_ctx *jsonctx)
+{
+ switch (*jsonctx->in->current) {
+ case '\0':
+ /* EOF */
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
+
+ case ']':
+ /* array end */
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
+ break;
+
+ case ',':
+ /* next array item */
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_NEXT);
+ break;
+
+ default:
/* unexpected value */
LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
- jsonctx->in->current, "a JSON value");
+ jsonctx->in->current, "a JSON array-end or next item");
return LY_EVALID;
}
@@ -854,46 +970,72 @@ lyjson_value(struct lyjson_ctx *jsonctx)
}
LY_ERR
-lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, ly_bool subtree, struct lyjson_ctx **jsonctx_p)
+lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
{
LY_ERR ret = LY_SUCCESS;
- struct lyjson_ctx *jsonctx;
+ enum LYJSON_PARSER_STATUS cur;
- assert(ctx);
- assert(in);
- assert(jsonctx_p);
-
- /* new context */
- jsonctx = calloc(1, sizeof *jsonctx);
- LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
- jsonctx->ctx = ctx;
- jsonctx->in = in;
+ assert(jsonctx);
- LOG_LOCSET(NULL, NULL, NULL, in);
+ cur = lyjson_ctx_status(jsonctx);
+ switch (cur) {
+ case LYJSON_OBJECT:
+ LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup);
+ break;
+ case LYJSON_ARRAY:
+ LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 1), cleanup);
+ break;
+ case LYJSON_OBJECT_NEXT:
+ LYJSON_STATUS_POP(jsonctx);
+ LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup);
+ break;
+ case LYJSON_ARRAY_NEXT:
+ LYJSON_STATUS_POP(jsonctx);
+ LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
+ break;
+ case LYJSON_OBJECT_NAME:
+ lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
+ LYJSON_STATUS_POP(jsonctx);
+ LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
+ break;
+ case LYJSON_OBJECT_CLOSED:
+ case LYJSON_ARRAY_CLOSED:
+ LYJSON_STATUS_POP(jsonctx);
+ /* fallthrough */
+ case LYJSON_NUMBER:
+ case LYJSON_STRING:
+ case LYJSON_TRUE:
+ case LYJSON_FALSE:
+ case LYJSON_NULL:
+ lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
+ LYJSON_STATUS_POP(jsonctx);
+ cur = lyjson_ctx_status(jsonctx);
+
+ if (cur == LYJSON_OBJECT) {
+ LY_CHECK_GOTO(ret = lyjson_next_object_item(jsonctx), cleanup);
+ break;
+ } else if (cur == LYJSON_ARRAY) {
+ LY_CHECK_GOTO(ret = lyjson_next_array_item(jsonctx), cleanup);
+ break;
+ }
- /* parse JSON value, if any */
- LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup);
- if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) {
- /* empty data input */
+ assert(cur == LYJSON_END);
+ goto cleanup;
+ case LYJSON_END:
+ LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
+ break;
+ case LYJSON_ERROR:
+ LOGINT(jsonctx->ctx);
+ ret = LY_EINT;
goto cleanup;
}
- if (subtree) {
- ret = lyjson_object(jsonctx);
- jsonctx->depth++;
- } else {
- ret = lyjson_value(jsonctx);
- }
- if ((jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
- LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
- ret = LY_EVALID;
- }
+ /* skip WS */
+ lyjson_skip_ws(jsonctx);
cleanup:
- if (ret) {
- lyjson_ctx_free(jsonctx);
- } else {
- *jsonctx_p = jsonctx;
+ if (!ret && status) {
+ *status = lyjson_ctx_status(jsonctx);
}
return ret;
}
@@ -904,13 +1046,12 @@ lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
if (jsonctx->backup.dynamic) {
free((char *)jsonctx->backup.value);
}
- jsonctx->backup.status = lyjson_ctx_status(jsonctx, 0);
+ jsonctx->backup.status = lyjson_ctx_status(jsonctx);
jsonctx->backup.status_count = jsonctx->status.count;
jsonctx->backup.value = jsonctx->value;
jsonctx->backup.value_len = jsonctx->value_len;
jsonctx->backup.input = jsonctx->in->current;
jsonctx->backup.dynamic = jsonctx->dynamic;
- jsonctx->backup.depth = jsonctx->depth;
jsonctx->dynamic = 0;
}
@@ -926,105 +1067,9 @@ lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
jsonctx->value_len = jsonctx->backup.value_len;
jsonctx->in->current = jsonctx->backup.input;
jsonctx->dynamic = jsonctx->backup.dynamic;
- jsonctx->depth = jsonctx->backup.depth;
jsonctx->backup.dynamic = 0;
}
-LY_ERR
-lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
-{
- LY_ERR ret = LY_SUCCESS;
- ly_bool toplevel = 0;
- enum LYJSON_PARSER_STATUS prev;
-
- assert(jsonctx);
-
- prev = lyjson_ctx_status(jsonctx, 0);
-
- if ((prev == LYJSON_OBJECT) || (prev == LYJSON_ARRAY)) {
- /* get value for the object's member OR the first value in the array */
- ret = lyjson_value(jsonctx);
- goto result;
- } else {
- /* the previous token is closed and should be completely processed */
- LYJSON_STATUS_POP_RET(jsonctx);
- prev = lyjson_ctx_status(jsonctx, 0);
- }
-
- if (!jsonctx->status.count) {
- /* we are done with the top level value */
- toplevel = 1;
- }
- LY_CHECK_RET(skip_ws(jsonctx));
- if (toplevel && !jsonctx->status.count) {
- /* EOF expected, but there are some data after the top level token */
- LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Expecting end-of-input, but some data follows the top level JSON value.");
- return LY_EVALID;
- }
-
- if (toplevel) {
- /* we are done */
- goto result;
- }
-
- /* continue with the next token */
- assert(prev == LYJSON_OBJECT || prev == LYJSON_ARRAY);
-
- if (*jsonctx->in->current == ',') {
- /* sibling item in the ... */
- ly_in_skip(jsonctx->in, 1);
- LY_CHECK_RET(skip_ws(jsonctx));
-
- if (prev == LYJSON_OBJECT) {
- /* ... object - get another object's member */
- ret = lyjson_object_name(jsonctx);
- } else { /* LYJSON_ARRAY */
- /* ... array - get another complete value */
- ret = lyjson_value(jsonctx);
- }
- } else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) ||
- ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) {
- if (*jsonctx->in->current == '}') {
- assert(jsonctx->depth);
- jsonctx->depth--;
- }
- ly_in_skip(jsonctx->in, 1);
- LYJSON_STATUS_POP_RET(jsonctx);
- LYJSON_STATUS_PUSH_RET(jsonctx, prev + 1);
- } else {
- /* unexpected value */
- LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
- prev == LYJSON_ARRAY ? "another JSON value in array" : "another JSON object's member");
- return LY_EVALID;
- }
-
-result:
- if ((ret == LY_SUCCESS) && (jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
- LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
- ret = LY_EVALID;
- }
-
- if ((ret == LY_SUCCESS) && status) {
- *status = lyjson_ctx_status(jsonctx, 0);
- }
-
- return ret;
-}
-
-enum LYJSON_PARSER_STATUS
-lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index)
-{
- assert(jsonctx);
-
- if (jsonctx->status.count < index) {
- return LYJSON_ERROR;
- } else if (jsonctx->status.count == index) {
- return LYJSON_ROOT;
- } else {
- return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - (index + 1)];
- }
-}
-
void
lyjson_ctx_free(struct lyjson_ctx *jsonctx)
{
diff --git a/src/json.h b/src/json.h
index 53efe2a..61c561a 100644
--- a/src/json.h
+++ b/src/json.h
@@ -1,9 +1,10 @@
/**
* @file json.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Generic JSON format parser routines.
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 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.
@@ -24,6 +25,9 @@
struct ly_ctx;
struct ly_in;
+#define LYJSON_STRING_BUF_START 24
+#define LYJSON_STRING_BUF_STEP 128
+
/* Macro to test if character is whitespace */
#define is_jsonws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
@@ -35,27 +39,27 @@ struct ly_in;
LY_CHECK_RET(ly_set_add(&CTX->status, (void *)(uintptr_t)(STATUS), 1, NULL))
/* Macro to pop JSON parser status */
-#define LYJSON_STATUS_POP_RET(CTX) \
+#define LYJSON_STATUS_POP(CTX) \
assert(CTX->status.count); CTX->status.count--;
/**
* @brief Status of the parser providing information what is expected next (which function is supposed to be called).
*/
enum LYJSON_PARSER_STATUS {
- LYJSON_ERROR, /* JSON parser error - value is used as an error return code */
- LYJSON_ROOT, /* JSON document root, used internally */
- LYJSON_OBJECT, /* JSON object */
- LYJSON_OBJECT_CLOSED, /* JSON object closed */
- LYJSON_OBJECT_EMPTY, /* empty JSON object { } */
- LYJSON_ARRAY, /* JSON array */
- LYJSON_ARRAY_CLOSED, /* JSON array closed */
- LYJSON_ARRAY_EMPTY, /* empty JSON array */
- LYJSON_NUMBER, /* JSON number value */
- LYJSON_STRING, /* JSON string value */
- LYJSON_FALSE, /* JSON false value */
- LYJSON_TRUE, /* JSON true value */
- LYJSON_NULL, /* JSON null value */
- LYJSON_END /* end of input data */
+ LYJSON_ERROR = 0, /* JSON parser error - value is used as an error return code */
+ LYJSON_OBJECT, /* JSON object */
+ LYJSON_OBJECT_NEXT, /* JSON object next item */
+ LYJSON_OBJECT_CLOSED, /* JSON object closed */
+ LYJSON_ARRAY, /* JSON array */
+ LYJSON_ARRAY_NEXT, /* JSON array next item */
+ LYJSON_ARRAY_CLOSED, /* JSON array closed */
+ LYJSON_OBJECT_NAME, /* JSON object name */
+ LYJSON_NUMBER, /* JSON number value */
+ LYJSON_STRING, /* JSON string value */
+ LYJSON_TRUE, /* JSON true value */
+ LYJSON_FALSE, /* JSON false value */
+ LYJSON_NULL, /* JSON null value */
+ LYJSON_END /* end of input data */
};
struct lyjson_ctx {
@@ -64,10 +68,9 @@ struct lyjson_ctx {
struct ly_set status; /* stack of ::LYJSON_PARSER_STATUS values corresponding to the JSON items being processed */
- const char *value; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
- size_t value_len; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
- ly_bool dynamic; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
- uint32_t depth; /* current number of nested blocks, see ::LY_MAX_BLOCK_DEPTH */
+ const char *value; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT_NAME */
+ size_t value_len; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT_NAME */
+ ly_bool dynamic; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT_NAME */
struct {
enum LYJSON_PARSER_STATUS status;
@@ -75,38 +78,44 @@ struct lyjson_ctx {
const char *value;
size_t value_len;
ly_bool dynamic;
- uint32_t depth;
const char *input;
} backup;
};
/**
- * @brief Create a new JSON parser context and start parsing.
+ * @brief Get string representation of the JSON context status (token).
*
- * @param[in] ctx libyang context.
- * @param[in] in JSON string data to parse.
- * @param[in] subtree Whether this is a special case of parsing a subtree (starting with object name).
- * @param[out] jsonctx New JSON context with status referring the parsed value.
- * @return LY_ERR value.
+ * @param[in] status Context status (aka JSON token)
+ * @return String representation of the @p status.
*/
-LY_ERR lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, ly_bool subtree, struct lyjson_ctx **jsonctx);
+const char *lyjson_token2str(enum LYJSON_PARSER_STATUS status);
/**
- * @brief Get status of the parser as the last/previous parsed token
+ * @brief Get current status of the parser.
*
- * @param[in] jsonctx JSON context to check.
- * @param[in] index Index of the token, starting by 0 for the last token
- * @return ::LYJSON_ERROR in case of invalid index, other ::LYJSON_PARSER_STATUS corresponding to the token.
+ * @param[in] jsonctx JSON parser context to check.
+ * @return ::LYJSON_PARSER_STATUS according to the last parsed token.
*/
-enum LYJSON_PARSER_STATUS lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index);
+enum LYJSON_PARSER_STATUS lyjson_ctx_status(struct lyjson_ctx *jsonctx);
/**
- * @brief Get string representation of the JSON context status (token).
+ * @brief Get current nesting (object/array) depth.
*
- * @param[in] status Context status (aka JSON token)
- * @return String representation of the @p status.
+ * @param[in] jsonctx JSON parser context to check.
+ * @return Current nesting depth.
*/
-const char *lyjson_token2str(enum LYJSON_PARSER_STATUS status);
+uint32_t lyjson_ctx_depth(struct lyjson_ctx *jsonctx);
+
+/**
+ * @brief Create a new JSON parser context and start parsing.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] in JSON string data to parse.
+ * @param[in] subtree Whether this is a special case of parsing a subtree (starting with object name).
+ * @param[out] jsonctx New JSON parser context with status referring the parsed value.
+ * @return LY_ERR value.
+ */
+LY_ERR lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx);
/**
* @brief Move to the next JSON artifact and update parser status.
@@ -119,12 +128,14 @@ LY_ERR lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *st
/**
* @brief Backup the JSON parser context's state To restore the backup, use ::lyjson_ctx_restore().
+ *
* @param[in] jsonctx JSON parser context to backup.
*/
void lyjson_ctx_backup(struct lyjson_ctx *jsonctx);
/**
- * @brief REstore the JSON parser context's state from the backup created by ::lyjson_ctx_backup().
+ * @brief Restore the JSON parser context's state from the backup created by ::lyjson_ctx_backup().
+ *
* @param[in] jsonctx JSON parser context to restore.
*/
void lyjson_ctx_restore(struct lyjson_ctx *jsonctx);
@@ -132,7 +143,7 @@ void lyjson_ctx_restore(struct lyjson_ctx *jsonctx);
/**
* @brief Remove the allocated working memory of the context.
*
- * @param[in] jsonctx JSON context to clear.
+ * @param[in] jsonctx JSON parser context to clear.
*/
void lyjson_ctx_free(struct lyjson_ctx *jsonctx);
diff --git a/src/libyang.h b/src/libyang.h
index 2bfc6be..f992a78 100644
--- a/src/libyang.h
+++ b/src/libyang.h
@@ -39,6 +39,7 @@ extern "C" {
/*
* The following headers are supposed to be included explicitly:
+ * - hash_table.h
* - metadata.h
* - plugins_types.h
* - plugins_exts.h
diff --git a/src/log.c b/src/log.c
index 2a1f862..41c8e90 100644
--- a/src/log.c
+++ b/src/log.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Logger routines implementations
*
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 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.
@@ -41,15 +41,13 @@ ATOMIC_T ly_log_opts = (uint_fast32_t)(LY_LOLOG | LY_LOSTORE_LAST);
THREAD_LOCAL uint32_t *temp_ly_log_opts;
static ly_log_clb log_clb;
static ATOMIC_T path_flag = 1;
+THREAD_LOCAL char last_msg[LY_LAST_MSG_SIZE];
#ifndef NDEBUG
ATOMIC_T ly_ldbg_groups = 0;
#endif
THREAD_LOCAL struct ly_log_location_s log_location = {0};
-/* how many bytes add when enlarging buffers */
-#define LY_BUF_STEP 128
-
LIBYANG_API_DEF LY_ERR
ly_errcode(const struct ly_ctx *ctx)
{
@@ -63,6 +61,47 @@ ly_errcode(const struct ly_ctx *ctx)
return LY_SUCCESS;
}
+LIBYANG_API_DEF const char *
+ly_strerrcode(LY_ERR err)
+{
+ /* ignore plugin flag */
+ err &= ~LY_EPLUGIN;
+
+ switch (err) {
+ case LY_SUCCESS:
+ return "Success";
+ case LY_EMEM:
+ return "Out of memory";
+ case LY_ESYS:
+ return "System call failed";
+ case LY_EINVAL:
+ return "Invalid value";
+ case LY_EEXIST:
+ return "Already exists";
+ case LY_ENOTFOUND:
+ return "Not found";
+ case LY_EINT:
+ return "Internal error";
+ case LY_EVALID:
+ return "Validation failed";
+ case LY_EDENIED:
+ return "Operation denied";
+ case LY_EINCOMPLETE:
+ return "Operation incomplete";
+ case LY_ERECOMPILE:
+ return "Recompilation required";
+ case LY_ENOT:
+ return "Negative result";
+ case LY_EOTHER:
+ return "Another failure reason";
+ case LY_EPLUGIN:
+ break;
+ }
+
+ /* unreachable */
+ return "Unknown";
+}
+
LIBYANG_API_DEF LY_VECODE
ly_vecode(const struct ly_ctx *ctx)
{
@@ -77,6 +116,38 @@ ly_vecode(const struct ly_ctx *ctx)
}
LIBYANG_API_DEF const char *
+ly_strvecode(LY_VECODE vecode)
+{
+ switch (vecode) {
+ case LYVE_SUCCESS:
+ return "Success";
+ case LYVE_SYNTAX:
+ return "General syntax error";
+ case LYVE_SYNTAX_YANG:
+ return "YANG syntax error";
+ case LYVE_SYNTAX_YIN:
+ return "YIN syntax error";
+ case LYVE_REFERENCE:
+ return "Reference error";
+ case LYVE_XPATH:
+ return "XPath error";
+ case LYVE_SEMANTICS:
+ return "Semantic error";
+ case LYVE_SYNTAX_XML:
+ return "XML syntax error";
+ case LYVE_SYNTAX_JSON:
+ return "JSON syntax error";
+ case LYVE_DATA:
+ return "YANG data error";
+ case LYVE_OTHER:
+ return "Another error";
+ }
+
+ /* unreachable */
+ return "Unknown";
+}
+
+LIBYANG_API_DEF const char *
ly_errmsg(const struct ly_ctx *ctx)
{
struct ly_err_item *i;
@@ -92,6 +163,12 @@ ly_errmsg(const struct ly_ctx *ctx)
}
LIBYANG_API_DEF const char *
+ly_last_errmsg(void)
+{
+ return last_msg;
+}
+
+LIBYANG_API_DEF const char *
ly_errpath(const struct ly_ctx *ctx)
{
struct ly_err_item *i;
@@ -169,61 +246,148 @@ ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path,
return e->no;
}
+/**
+ * @brief Get error record from error hash table of a context for the current thread.
+ *
+ * @param[in] ctx Context to use.
+ * @return Thread error record, if any.
+ */
+static struct ly_ctx_err_rec *
+ly_err_get_rec(const struct ly_ctx *ctx)
+{
+ struct ly_ctx_err_rec rec, *match;
+
+ /* prepare record */
+ rec.tid = pthread_self();
+
+ /* get the pointer to the matching record */
+ if (lyht_find(ctx->err_ht, &rec, lyht_hash((void *)&rec.tid, sizeof rec.tid), (void **)&match)) {
+ return NULL;
+ }
+
+ return match;
+}
+
+/**
+ * @brief Insert new error record to error hash table of a context for the current thread.
+ *
+ * @param[in] ctx Context to use.
+ * @return Thread error record.
+ */
+static struct ly_ctx_err_rec *
+ly_err_new_rec(const struct ly_ctx *ctx)
+{
+ struct ly_ctx_err_rec new, *rec;
+ LY_ERR r;
+
+ /* insert a new record */
+ new.err = NULL;
+ new.tid = pthread_self();
+
+ /* reuse lock */
+ /* LOCK */
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->lyb_hash_lock);
+
+ r = lyht_insert(ctx->err_ht, &new, lyht_hash((void *)&new.tid, sizeof new.tid), (void **)&rec);
+
+ /* UNLOCK */
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->lyb_hash_lock);
+
+ return r ? NULL : rec;
+}
+
LIBYANG_API_DEF struct ly_err_item *
ly_err_first(const struct ly_ctx *ctx)
{
+ struct ly_ctx_err_rec *rec;
+
LY_CHECK_ARG_RET(NULL, ctx, NULL);
- return pthread_getspecific(ctx->errlist_key);
+ /* get the pointer to the matching record */
+ rec = ly_err_get_rec(ctx);
+
+ return rec ? rec->err : NULL;
}
LIBYANG_API_DEF struct ly_err_item *
ly_err_last(const struct ly_ctx *ctx)
{
- const struct ly_err_item *e;
+ struct ly_ctx_err_rec *rec;
LY_CHECK_ARG_RET(NULL, ctx, NULL);
- e = pthread_getspecific(ctx->errlist_key);
- return e ? e->prev : NULL;
+ /* get the pointer to the matching record */
+ if (!(rec = ly_err_get_rec(ctx))) {
+ return NULL;
+ }
+
+ return rec->err ? rec->err->prev : NULL;
+}
+
+void
+ly_err_move(struct ly_ctx *src_ctx, struct ly_ctx *trg_ctx)
+{
+ struct ly_ctx_err_rec *rec;
+ struct ly_err_item *err = NULL;
+
+ /* get and remove the errors from src */
+ rec = ly_err_get_rec(src_ctx);
+ if (rec) {
+ err = rec->err;
+ rec->err = NULL;
+ }
+
+ /* set them for trg */
+ if (!(rec = ly_err_get_rec(trg_ctx))) {
+ if (!(rec = ly_err_new_rec(trg_ctx))) {
+ LOGINT(NULL);
+ ly_err_free(err);
+ return;
+ }
+ }
+ ly_err_free(rec->err);
+ rec->err = err;
}
LIBYANG_API_DEF void
ly_err_free(void *ptr)
{
- struct ly_err_item *i, *next;
+ struct ly_err_item *e, *next;
/* clean the error list */
- for (i = (struct ly_err_item *)ptr; i; i = next) {
- next = i->next;
- free(i->msg);
- free(i->path);
- free(i->apptag);
- free(i);
+ LY_LIST_FOR_SAFE(ptr, next, e) {
+ free(e->msg);
+ free(e->path);
+ free(e->apptag);
+ free(e);
}
}
LIBYANG_API_DEF void
ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
{
- struct ly_err_item *i, *first;
+ struct ly_ctx_err_rec *rec;
+ struct ly_err_item *e;
- first = ly_err_first(ctx);
- if (first == eitem) {
+ if (!(rec = ly_err_get_rec(ctx))) {
+ return;
+ }
+ if (rec->err == eitem) {
eitem = NULL;
}
- if (eitem) {
+
+ if (!eitem) {
+ /* free all err */
+ ly_err_free(rec->err);
+ rec->err = NULL;
+ } else {
/* disconnect the error */
- for (i = first; i && (i->next != eitem); i = i->next) {}
- assert(i);
- i->next = NULL;
- first->prev = i;
+ for (e = rec->err; e && (e->next != eitem); e = e->next) {}
+ assert(e);
+ e->next = NULL;
+ rec->err->prev = e;
/* free this err and newer */
ly_err_free(eitem);
- } else {
- /* free all err */
- ly_err_free(first);
- pthread_setspecific(ctx->errlist_key, NULL);
}
}
@@ -336,64 +500,99 @@ ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t pat
}
}
+const struct lyd_node *
+ly_log_location_dnode(uint32_t idx)
+{
+ if (idx < log_location.dnodes.count) {
+ return log_location.dnodes.dnodes[idx];
+ }
+
+ return NULL;
+}
+
+uint32_t
+ly_log_location_dnode_count(void)
+{
+ return log_location.dnodes.count;
+}
+
+/**
+ * @brief Store generated error in a context.
+ *
+ * @param[in] ctx Context to use.
+ * @param[in] level Message log level.
+ * @param[in] no Error number.
+ * @param[in] vecode Error validation error code.
+ * @param[in] msg Error message, always spent.
+ * @param[in] path Error path, always spent.
+ * @param[in] apptag Error app tag, always spent.
+ * @return LY_ERR value.
+ */
static LY_ERR
log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
{
- struct ly_err_item *eitem, *last;
+ struct ly_ctx_err_rec *rec;
+ struct ly_err_item *e, *last;
assert(ctx && (level < LY_LLVRB));
- eitem = pthread_getspecific(ctx->errlist_key);
- if (!eitem) {
+ if (!(rec = ly_err_get_rec(ctx))) {
+ if (!(rec = ly_err_new_rec(ctx))) {
+ goto mem_fail;
+ }
+ }
+
+ e = rec->err;
+ if (!e) {
/* if we are only to fill in path, there must have been an error stored */
assert(msg);
- eitem = malloc(sizeof *eitem);
- LY_CHECK_GOTO(!eitem, mem_fail);
- eitem->prev = eitem;
- eitem->next = NULL;
+ e = malloc(sizeof *e);
+ LY_CHECK_GOTO(!e, mem_fail);
+ e->prev = e;
+ e->next = NULL;
- pthread_setspecific(ctx->errlist_key, eitem);
+ rec->err = e;
} else if (!msg) {
/* only filling the path */
assert(path);
/* find last error */
- eitem = eitem->prev;
+ e = e->prev;
do {
- if (eitem->level == LY_LLERR) {
+ if (e->level == LY_LLERR) {
/* fill the path */
- free(eitem->path);
- eitem->path = path;
+ free(e->path);
+ e->path = path;
return LY_SUCCESS;
}
- eitem = eitem->prev;
- } while (eitem->prev->next);
+ e = e->prev;
+ } while (e->prev->next);
/* last error was not found */
assert(0);
} else if ((temp_ly_log_opts && ((*temp_ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) ||
(!temp_ly_log_opts && ((ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE_LAST) == LY_LOSTORE_LAST))) {
/* overwrite last message */
- free(eitem->msg);
- free(eitem->path);
- free(eitem->apptag);
+ free(e->msg);
+ free(e->path);
+ free(e->apptag);
} else {
/* store new message */
- last = eitem->prev;
- eitem->prev = malloc(sizeof *eitem);
- LY_CHECK_GOTO(!eitem->prev, mem_fail);
- eitem = eitem->prev;
- eitem->prev = last;
- eitem->next = NULL;
- last->next = eitem;
+ last = e->prev;
+ e->prev = malloc(sizeof *e);
+ LY_CHECK_GOTO(!e->prev, mem_fail);
+ e = e->prev;
+ e->prev = last;
+ e->next = NULL;
+ last->next = e;
}
/* fill in the information */
- eitem->level = level;
- eitem->no = no;
- eitem->vecode = vecode;
- eitem->msg = msg;
- eitem->path = path;
- eitem->apptag = apptag;
+ e->level = level;
+ e->no = no;
+ e->vecode = vecode;
+ e->msg = msg;
+ e->path = path;
+ e->apptag = apptag;
return LY_SUCCESS;
mem_fail:
@@ -445,14 +644,19 @@ log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE v
return;
}
- /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
+ /* print into a single message */
+ if (vasprintf(&msg, format, args) == -1) {
+ LOGMEM(ctx);
+ free(path);
+ return;
+ }
+
+ /* store as the last message */
+ strncpy(last_msg, msg, LY_LAST_MSG_SIZE - 1);
+
+ /* store the error/warning in the context (if we need to store errors internally, it does not matter what are
+ * the user log options) */
if ((level < LY_LLVRB) && ctx && lostore) {
- assert(format);
- if (vasprintf(&msg, format, args) == -1) {
- LOGMEM(ctx);
- free(path);
- return;
- }
if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
/* assume we are inheriting the error, so inherit vecode as well */
vecode = ly_vecode(ctx);
@@ -462,11 +666,6 @@ log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE v
}
free_strs = 0;
} else {
- if (vasprintf(&msg, format, args) == -1) {
- LOGMEM(ctx);
- free(path);
- return;
- }
free_strs = 1;
}
@@ -524,6 +723,8 @@ ly_log_dbg(uint32_t group, const char *format, ...)
va_start(ap, format);
log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, NULL, dbg_format, ap);
va_end(ap);
+
+ free(dbg_format);
}
#endif
@@ -556,20 +757,20 @@ ly_vlog_build_path_append(char **str, const struct lysc_node *snode, const struc
if (snode->nodetype & (LYS_CHOICE | LYS_CASE)) {
/* schema-only node */
return LY_SUCCESS;
- } else if (lysc_data_parent(snode) != parent->schema) {
+ } else if (lysc_data_parent(snode) != lyd_node_schema(parent)) {
/* not a direct descendant node */
return LY_SUCCESS;
}
/* get module to print, if any */
mod = snode->module;
- prev_mod = (parent->schema) ? parent->schema->module : lyd_owner_module(parent);
+ prev_mod = lyd_node_module(parent);
if (prev_mod == mod) {
mod = NULL;
}
/* realloc string */
- len = strlen(*str);
+ len = *str ? strlen(*str) : 0;
new_len = len + 1 + (mod ? strlen(mod->name) + 1 : 0) + strlen(snode->name);
mem = realloc(*str, new_len + 1);
LY_CHECK_ERR_RET(!mem, LOGMEM(LYD_CTX(parent)), LY_EMEM);
@@ -580,6 +781,41 @@ ly_vlog_build_path_append(char **str, const struct lysc_node *snode, const struc
return LY_SUCCESS;
}
+LY_ERR
+ly_vlog_build_data_path(const struct ly_ctx *ctx, char **path)
+{
+ LY_ERR rc = LY_SUCCESS;
+ const struct lyd_node *dnode = NULL;
+
+ *path = NULL;
+
+ if (log_location.dnodes.count) {
+ dnode = log_location.dnodes.objs[log_location.dnodes.count - 1];
+ if (dnode->parent || !lysc_data_parent(dnode->schema)) {
+ /* data node with all of its parents */
+ *path = lyd_path(log_location.dnodes.objs[log_location.dnodes.count - 1], LYD_PATH_STD, NULL, 0);
+ LY_CHECK_ERR_GOTO(!*path, LOGMEM(ctx); rc = LY_EMEM, cleanup);
+ } else {
+ /* data parsers put all the parent nodes in the set, but they are not connected */
+ *path = lyd_path_set(&log_location.dnodes, LYD_PATH_STD);
+ LY_CHECK_ERR_GOTO(!*path, LOGMEM(ctx); rc = LY_EMEM, cleanup);
+ }
+ }
+
+ /* sometimes the last node is not created yet and we only have the schema node */
+ if (log_location.scnodes.count) {
+ rc = ly_vlog_build_path_append(path, log_location.scnodes.objs[log_location.scnodes.count - 1], dnode);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+
+cleanup:
+ if (rc) {
+ free(*path);
+ *path = NULL;
+ }
+ return rc;
+}
+
/**
* @brief Build log path from the stored log location information.
*
@@ -592,32 +828,17 @@ ly_vlog_build_path(const struct ly_ctx *ctx, char **path)
{
int r;
char *str = NULL, *prev = NULL;
- const struct lyd_node *dnode;
*path = NULL;
if (log_location.paths.count && ((const char *)(log_location.paths.objs[log_location.paths.count - 1]))[0]) {
/* simply get what is in the provided path string */
- *path = strdup((const char *)log_location.paths.objs[log_location.paths.count - 1]);
- LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
+ r = asprintf(path, "Path \"%s\"", (const char *)log_location.paths.objs[log_location.paths.count - 1]);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
} else {
/* data/schema node */
if (log_location.dnodes.count) {
- dnode = log_location.dnodes.objs[log_location.dnodes.count - 1];
- if (dnode->parent || !lysc_data_parent(dnode->schema)) {
- /* data node with all of its parents */
- str = lyd_path(log_location.dnodes.objs[log_location.dnodes.count - 1], LYD_PATH_STD, NULL, 0);
- LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
- } else {
- /* data parsers put all the parent nodes in the set, but they are not connected */
- str = lyd_path_set(&log_location.dnodes, LYD_PATH_STD);
- LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
- }
-
- /* sometimes the last node is not created yet and we only have the schema node */
- if (log_location.scnodes.count) {
- ly_vlog_build_path_append(&str, log_location.scnodes.objs[log_location.scnodes.count - 1], dnode);
- }
+ LY_CHECK_RET(ly_vlog_build_data_path(ctx, &str));
r = asprintf(path, "Data location \"%s\"", str);
free(str);
@@ -630,28 +851,28 @@ ly_vlog_build_path(const struct ly_ctx *ctx, char **path)
free(str);
LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
}
+ }
- /* line */
- prev = *path;
- if (log_location.line) {
- r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", log_location.line);
- free(prev);
- LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
+ /* line */
+ prev = *path;
+ if (log_location.line) {
+ r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", log_location.line);
+ free(prev);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
- log_location.line = 0;
- } else if (log_location.inputs.count) {
- r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L",
- ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line);
- free(prev);
- LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
- }
+ log_location.line = 0;
+ } else if (log_location.inputs.count) {
+ r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L",
+ ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line);
+ free(prev);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
+ }
- if (*path) {
- prev = *path;
- r = asprintf(path, "%s.", prev);
- free(prev);
- LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
- }
+ if (*path) {
+ prev = *path;
+ r = asprintf(path, "%s.", prev);
+ free(prev);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
}
return LY_SUCCESS;
@@ -680,12 +901,12 @@ ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char
* @param[in] plugin_name Name of the plugin generating the message.
* @param[in] level Log message level (error, warning, etc.)
* @param[in] err_no Error type code.
- * @param[in] path Optional path of the error.
+ * @param[in] path Optional path of the error, used if set.
* @param[in] format Format string to print.
* @param[in] ap Var arg list for @p format.
*/
static void
-ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level, LY_ERR err_no, const char *path,
+ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level, LY_ERR err_no, char *path,
const char *format, va_list ap)
{
char *plugin_msg;
@@ -698,8 +919,7 @@ ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level
return;
}
- log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path ? strdup(path) : NULL, NULL,
- plugin_msg, ap);
+ log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path, NULL, plugin_msg, ap);
free(plugin_msg);
}
@@ -717,8 +937,6 @@ lyplg_ext_parse_log(const struct lysp_ctx *pctx, const struct lysp_ext_instance
va_start(ap, format);
ly_ext_log(PARSER_CTX(pctx), ext->record->plugin.id, level, err_no, path, format, ap);
va_end(ap);
-
- free(path);
}
LIBYANG_API_DEF void
@@ -726,9 +944,15 @@ lyplg_ext_compile_log(const struct lysc_ctx *cctx, const struct lysc_ext_instanc
const char *format, ...)
{
va_list ap;
+ char *path = NULL;
+
+ if (cctx && (asprintf(&path, "Path \"%s\".", cctx->path) == -1)) {
+ LOGMEM(cctx->ctx);
+ return;
+ }
va_start(ap, format);
- ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, cctx ? cctx->path : NULL, format, ap);
+ ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, path, format, ap);
va_end(ap);
}
@@ -737,12 +961,37 @@ lyplg_ext_compile_log_path(const char *path, const struct lysc_ext_instance *ext
const char *format, ...)
{
va_list ap;
+ char *log_path = NULL;
+
+ if (path && (asprintf(&log_path, "Path \"%s\".", path) == -1)) {
+ LOGMEM(ext->module->ctx);
+ return;
+ }
va_start(ap, format);
- ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, path, format, ap);
+ ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, log_path, format, ap);
+ va_end(ap);
+}
+
+/**
+ * @brief Serves only for creating ap.
+ */
+static void
+_lyplg_ext_compile_log_err(const struct ly_err_item *err, const struct lysc_ext_instance *ext, ...)
+{
+ va_list ap;
+
+ va_start(ap, ext);
+ ly_ext_log(ext->module->ctx, ext->def->plugin->id, err->level, err->no, err->path ? strdup(err->path) : NULL, "%s", ap);
va_end(ap);
}
+LIBYANG_API_DEF void
+lyplg_ext_compile_log_err(const struct ly_err_item *err, const struct lysc_ext_instance *ext)
+{
+ _lyplg_ext_compile_log_err(err, ext, err->msg);
+}
+
/**
* @brief Exact same functionality as ::ly_err_print() but has variable arguments so log_vprintf() can be called.
*/
diff --git a/src/log.h b/src/log.h
index 2144668..c6c1851 100644
--- a/src/log.h
+++ b/src/log.h
@@ -141,8 +141,6 @@ LIBYANG_API_DECL uint32_t ly_log_options(uint32_t opts);
*/
LIBYANG_API_DECL void ly_temp_log_options(uint32_t *opts);
-#ifndef NDEBUG
-
/**
* @ingroup log
* @defgroup dbggroup Debug messages groups
@@ -166,13 +164,12 @@ LIBYANG_API_DECL void ly_temp_log_options(uint32_t *opts);
* @brief Enable specific debugging messages (independent of log level).
*
* To get the current value, the function must be called twice resetting the level by the received value.
+ * Note: does not have any effect on non-debug (Release) builds
*
* @param[in] dbg_groups Bitfield of enabled debug message groups (see @ref dbggroup).
* @return Previous options bitfield.
*/
-uint32_t ly_log_dbg_groups(uint32_t dbg_groups);
-
-#endif
+LIBYANG_API_DECL uint32_t ly_log_dbg_groups(uint32_t dbg_groups);
/**
* @brief Logger callback.
@@ -294,17 +291,33 @@ typedef enum {
* @brief Libyang full error structure.
*/
struct ly_err_item {
- LY_LOG_LEVEL level;
- LY_ERR no;
- LY_VECODE vecode;
- char *msg;
- char *path;
- char *apptag;
- struct ly_err_item *next;
- struct ly_err_item *prev; /* first item's prev points to the last item */
+ LY_LOG_LEVEL level; /**< error (message) log level */
+ LY_ERR no; /**< error code */
+ LY_VECODE vecode; /**< validation error code, if any */
+ char *msg; /**< error message */
+ char *path; /**< error path that caused the error, if any */
+ char *apptag; /**< error-app-tag, if any */
+ struct ly_err_item *next; /**< next error item */
+ struct ly_err_item *prev; /**< previous error item, points to the last item for the ifrst item */
};
/**
+ * @brief Get the last (thread, context-specific) error code.
+ *
+ * @param[in] ctx Relative context.
+ * @return LY_ERR value of the last error code.
+ */
+LIBYANG_API_DECL LY_ERR ly_errcode(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get human-readable error message for an error code.
+ *
+ * @param[in] err Error code.
+ * @return String error message.
+ */
+LIBYANG_API_DECL const char *ly_strerrcode(LY_ERR err);
+
+/**
* @brief Get the last (thread, context-specific) validation error code.
*
* This value is set only if ly_errno is #LY_EVALID.
@@ -315,12 +328,12 @@ struct ly_err_item {
LIBYANG_API_DECL LY_VECODE ly_vecode(const struct ly_ctx *ctx);
/**
- * @brief Get the last (thread, context-specific) error code.
+ * @brief Get human-readable error message for a validation error code.
*
- * @param[in] ctx Relative context.
- * @return LY_ERR value of the last error code.
+ * @param[in] vecode Validation error code.
+ * @return String error message.
*/
-LIBYANG_API_DECL LY_ERR ly_errcode(const struct ly_ctx *ctx);
+LIBYANG_API_DECL const char *ly_strvecode(LY_VECODE vecode);
/**
* @brief Get the last (thread, context-specific) error message. If the coresponding module defined
@@ -335,6 +348,16 @@ LIBYANG_API_DECL LY_ERR ly_errcode(const struct ly_ctx *ctx);
LIBYANG_API_DECL const char *ly_errmsg(const struct ly_ctx *ctx);
/**
+ * @brief Get the last (thread-specific) error message.
+ *
+ * ::ly_errmsg() should be used instead of this function but this one is useful for getting
+ * errors from functions that do not have any context accessible. Or as a simple unified logging API.
+ *
+ * @return Last generated error message.
+ */
+LIBYANG_API_DECL const char *ly_last_errmsg(void);
+
+/**
* @brief Get the last (thread, context-specific) path of the element where was an error.
*
* The path always corresponds to the error message available via ::ly_errmsg(), so
diff --git a/src/lyb.c b/src/lyb.c
index 87806c8..59d379c 100644
--- a/src/lyb.c
+++ b/src/lyb.c
@@ -39,8 +39,8 @@ lyb_generate_hash(const struct lysc_node *node, uint8_t collision_id)
LYB_HASH hash;
/* generate full hash */
- full_hash = dict_hash_multi(0, mod->name, strlen(mod->name));
- full_hash = dict_hash_multi(full_hash, node->name, strlen(node->name));
+ full_hash = lyht_hash_multi(0, mod->name, strlen(mod->name));
+ full_hash = lyht_hash_multi(full_hash, node->name, strlen(node->name));
if (collision_id) {
size_t ext_len;
@@ -51,9 +51,9 @@ lyb_generate_hash(const struct lysc_node *node, uint8_t collision_id)
/* use one more byte from the module name than before */
ext_len = collision_id;
}
- full_hash = dict_hash_multi(full_hash, mod->name, ext_len);
+ full_hash = lyht_hash_multi(full_hash, mod->name, ext_len);
}
- full_hash = dict_hash_multi(full_hash, NULL, 0);
+ full_hash = lyht_hash_multi(full_hash, NULL, 0);
/* use the shortened hash */
hash = full_hash & (LYB_HASH_MASK >> collision_id);
diff --git a/src/lyb.h b/src/lyb.h
index b123ee5..70de5b7 100644
--- a/src/lyb.h
+++ b/src/lyb.h
@@ -95,7 +95,7 @@ struct lylyb_ctx {
/* LYB printer only */
struct lyd_lyb_sib_ht {
struct lysc_node *first_sibling;
- struct hash_table *ht;
+ struct ly_ht *ht;
} *sib_hts;
};
diff --git a/src/out.c b/src/out.c
index 5567387..1e66ed9 100644
--- a/src/out.c
+++ b/src/out.c
@@ -417,7 +417,7 @@ ly_vprint_(struct ly_out *out, const char *format, va_list ap)
{
LY_ERR ret;
int written = 0;
- char *msg = NULL, *aux;
+ char *msg = NULL;
switch (out->type) {
case LY_OUT_FD:
@@ -433,15 +433,13 @@ ly_vprint_(struct ly_out *out, const char *format, va_list ap)
break;
}
if (out->method.mem.len + written + 1 > out->method.mem.size) {
- aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
- if (!aux) {
- out->method.mem.buf = NULL;
+ *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
+ if (!*out->method.mem.buf) {
out->method.mem.len = 0;
out->method.mem.size = 0;
LOGMEM(NULL);
return LY_EMEM;
}
- *out->method.mem.buf = aux;
out->method.mem.size = out->method.mem.len + written + 1;
}
if (written) {
@@ -630,9 +628,9 @@ repeat:
}
LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
written = 0;
- } else if ((size_t)written != len) {
- LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
- len - (size_t)written, len);
+ } else if (written != len) {
+ LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %" PRIu32 " from %" PRIu32 " data).", __func__,
+ (uint32_t)(len - written), (uint32_t)len);
ret = LY_ESYS;
} else {
if (out->type == LY_OUT_FDSTREAM) {
diff --git a/src/parser_common.c b/src/parser_common.c
index 6fe068b..3215275 100644
--- a/src/parser_common.c
+++ b/src/parser_common.c
@@ -46,6 +46,7 @@
#include "parser_data.h"
#include "path.h"
#include "plugins_exts/metadata.h"
+#include "schema_compile_node.h"
#include "schema_features.h"
#include "set.h"
#include "tree.h"
@@ -65,6 +66,50 @@ lyd_ctx_free(struct lyd_ctx *lydctx)
}
LY_ERR
+lyd_parser_notif_eventtime_validate(const struct lyd_node *node)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct ly_ctx *ctx = (struct ly_ctx *)LYD_CTX(node);
+ struct lysc_ctx cctx;
+ const struct lys_module *mod;
+ LY_ARRAY_COUNT_TYPE u;
+ struct ly_err_item *err = NULL;
+ struct lysp_type *type_p = NULL;
+ struct lysc_pattern **patterns = NULL;
+ const char *value;
+
+ LYSC_CTX_INIT_CTX(cctx, ctx);
+
+ /* get date-and-time parsed type */
+ mod = ly_ctx_get_module_latest(ctx, "ietf-yang-types");
+ assert(mod);
+ LY_ARRAY_FOR(mod->parsed->typedefs, u) {
+ if (!strcmp(mod->parsed->typedefs[u].name, "date-and-time")) {
+ type_p = &mod->parsed->typedefs[u].type;
+ break;
+ }
+ }
+ assert(type_p);
+
+ /* compile patterns */
+ assert(type_p->patterns);
+ LY_CHECK_GOTO(rc = lys_compile_type_patterns(&cctx, type_p->patterns, NULL, &patterns), cleanup);
+
+ /* validate */
+ value = lyd_get_value(node);
+ rc = lyplg_type_validate_patterns(patterns, value, strlen(value), &err);
+
+cleanup:
+ FREE_ARRAY(&cctx.free_ctx, patterns, lysc_pattern_free);
+ if (rc && err) {
+ LOGVAL_ERRITEM(ctx, err);
+ ly_err_free(err);
+ LOGVAL(ctx, LYVE_DATA, "Invalid \"eventTime\" in the notification.");
+ }
+ return rc;
+}
+
+LY_ERR
lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op)
{
const struct lyd_node *iter;
@@ -112,6 +157,63 @@ lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, stru
return LY_SUCCESS;
}
+const struct lysc_node *
+lyd_parser_node_schema(const struct lyd_node *node)
+{
+ uint32_t i;
+ const struct lyd_node *iter;
+ const struct lysc_node *schema = NULL;
+ const struct lys_module *mod;
+
+ if (!node) {
+ return NULL;
+ } else if (node->schema) {
+ /* simplest case */
+ return node->schema;
+ }
+
+ /* find the first schema node in the parsed nodes */
+ i = ly_log_location_dnode_count();
+ if (i) {
+ do {
+ --i;
+ if (ly_log_location_dnode(i)->schema) {
+ /* this node is processed */
+ schema = ly_log_location_dnode(i)->schema;
+ ++i;
+ break;
+ }
+ } while (i);
+ }
+
+ /* get schema node of an opaque node */
+ do {
+ /* get next data node */
+ if (i == ly_log_location_dnode_count()) {
+ iter = node;
+ } else {
+ iter = ly_log_location_dnode(i);
+ }
+ assert(!iter->schema);
+
+ /* get module */
+ mod = lyd_node_module(iter);
+ if (!mod) {
+ /* unknown module, no schema node */
+ schema = NULL;
+ break;
+ }
+
+ /* get schema node */
+ schema = lys_find_child(schema, mod, LYD_NAME(iter), 0, 0, 0);
+
+ /* move to the descendant */
+ ++i;
+ } while (schema && (iter != node));
+
+ return schema;
+}
+
LY_ERR
lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode)
{
@@ -180,9 +282,16 @@ LY_ERR
lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const void *value, size_t value_len,
ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, struct lyd_node **node)
{
+ LY_ERR r;
ly_bool incomplete;
- LY_CHECK_RET(lyd_create_term(schema, value, value_len, dynamic, format, prefix_data, hints, &incomplete, node));
+ if ((r = lyd_create_term(schema, value, value_len, 1, dynamic, format, prefix_data, hints, &incomplete, node))) {
+ if (lydctx->data_ctx->ctx != schema->module->ctx) {
+ /* move errors to the main context */
+ ly_err_move(schema->module->ctx, (struct ly_ctx *)lydctx->data_ctx->ctx);
+ }
+ return r;
+ }
if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
LY_CHECK_RET(ly_set_add(&lydctx->node_types, *node, 1, NULL));
@@ -195,6 +304,8 @@ lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct l
const char *name, size_t name_len, const void *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node)
{
+ LY_ERR rc = LY_SUCCESS;
+ char *dpath = NULL, *path = NULL;
ly_bool incomplete;
struct lyd_meta *first = NULL;
@@ -203,11 +314,20 @@ lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct l
first = *meta;
}
- LY_CHECK_RET(lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, format, prefix_data,
- hints, ctx_node, 0, &incomplete));
+ /* generate path to the metadata */
+ LY_CHECK_RET(ly_vlog_build_data_path(lydctx->data_ctx->ctx, &dpath));
+ if (asprintf(&path, "%s/@%s:%.*s", dpath, mod->name, (int)name_len, name) == -1) {
+ LOGMEM(lydctx->data_ctx->ctx);
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ LOG_LOCSET(NULL, NULL, path, NULL);
+
+ LY_CHECK_GOTO(rc = lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, 1, dynamic, format,
+ prefix_data, hints, ctx_node, 0, &incomplete), cleanup);
if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
- LY_CHECK_RET(ly_set_add(&lydctx->meta_types, *meta, 1, NULL));
+ LY_CHECK_GOTO(rc = ly_set_add(&lydctx->meta_types, *meta, 1, NULL), cleanup);
}
if (first) {
@@ -215,7 +335,11 @@ lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct l
*meta = first;
}
- return LY_SUCCESS;
+cleanup:
+ LOG_LOCBACK(0, 0, 1, 0);
+ free(dpath);
+ free(path);
+ return rc;
}
LY_ERR
@@ -387,6 +511,16 @@ lysp_stmt_validate_value(struct lysp_ctx *ctx, enum yang_arg val_type, const cha
uint32_t c;
size_t utf8_char_len;
+ if (!val) {
+ if (val_type == Y_MAYBE_STR_ARG) {
+ /* fine */
+ return LY_SUCCESS;
+ }
+
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Missing an expected string.");
+ return LY_EVALID;
+ }
+
while (*val) {
LY_CHECK_ERR_RET(ly_getutf8(&val, &c, &utf8_char_len),
LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, (val)[-utf8_char_len]), LY_EVALID);
@@ -605,7 +739,7 @@ lysp_stmt_text_fields(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, const
static LY_ERR
lysp_stmt_status(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
if (*flags & LYS_STATUS_MASK) {
LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "status");
@@ -698,7 +832,7 @@ lysp_stmt_when(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_w
static LY_ERR
lysp_stmt_config(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
if (*flags & LYS_CONFIG_MASK) {
LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "config");
@@ -744,7 +878,7 @@ static LY_ERR
lysp_stmt_mandatory(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
if (*flags & LYS_MAND_MASK) {
LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "mandatory");
@@ -914,7 +1048,7 @@ static LY_ERR
lysp_stmt_type_enum_value_pos(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, int64_t *value, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
char *ptr = NULL;
long long num = 0;
unsigned long long unum = 0;
@@ -949,7 +1083,7 @@ lysp_stmt_type_enum_value_pos(struct lysp_ctx *ctx, const struct lysp_stmt *stmt
}
}
/* we have not parsed the whole argument */
- if ((size_t)(ptr - stmt->arg) != arg_len) {
+ if (ptr - stmt->arg != arg_len) {
LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, lyplg_ext_stmt2str(stmt->kw));
goto error;
}
@@ -1056,7 +1190,7 @@ lysp_stmt_type_fracdigits(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, ui
struct lysp_ext_instance **exts)
{
char *ptr;
- size_t arg_len;
+ int arg_len;
unsigned long long num;
if (*fracdig) {
@@ -1074,7 +1208,7 @@ lysp_stmt_type_fracdigits(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, ui
errno = 0;
num = strtoull(stmt->arg, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
- if ((size_t)(ptr - stmt->arg) != arg_len) {
+ if (ptr - stmt->arg != arg_len) {
LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "fraction-digits");
return LY_EVALID;
}
@@ -1112,7 +1246,7 @@ static LY_ERR
lysp_stmt_type_reqinstance(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint8_t *reqinst, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
if (*flags & LYS_SET_REQINST) {
LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "require-instance");
@@ -1155,7 +1289,7 @@ static LY_ERR
lysp_stmt_type_pattern_modifier(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, const char **pat,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
char *buf;
if ((*pat)[0] == LYSP_RESTR_PATTERN_NACK) {
@@ -1338,7 +1472,7 @@ lysp_stmt_yangver(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint8_t *v
} else if (!strcmp(stmt->arg, "1.1")) {
*version = LYS_VERSION_1_1;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, strlen(stmt->arg), stmt->arg, "yang-version");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)strlen(stmt->arg), stmt->arg, "yang-version");
return LY_EVALID;
}
@@ -1418,7 +1552,7 @@ lysp_stmt_yinelem(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *
} else if (!strcmp(stmt->arg, "false")) {
*flags |= LYS_YINELEM_FALSE;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, strlen(stmt->arg), stmt->arg, "yin-element");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)strlen(stmt->arg), stmt->arg, "yin-element");
return LY_EVALID;
}
@@ -1946,7 +2080,7 @@ static LY_ERR
lysp_stmt_maxelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32_t *max, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
char *ptr;
unsigned long long num;
@@ -1969,7 +2103,7 @@ lysp_stmt_maxelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32
errno = 0;
num = strtoull(stmt->arg, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
- if ((size_t)(ptr - stmt->arg) != arg_len) {
+ if (ptr - stmt->arg != arg_len) {
LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "max-elements");
return LY_EVALID;
}
@@ -2012,7 +2146,7 @@ static LY_ERR
lysp_stmt_minelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32_t *min, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
char *ptr;
unsigned long long num;
@@ -2034,7 +2168,7 @@ lysp_stmt_minelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32
errno = 0;
num = strtoull(stmt->arg, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
- if ((size_t)(ptr - stmt->arg) != arg_len) {
+ if (ptr - stmt->arg != arg_len) {
LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "min-elements");
return LY_EVALID;
}
@@ -2070,7 +2204,7 @@ lysp_stmt_minelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32
static LY_ERR
lysp_stmt_orderedby(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
if (*flags & LYS_ORDBY_MASK) {
LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "ordered-by");
diff --git a/src/parser_data.h b/src/parser_data.h
index 050ced3..feedb14 100644
--- a/src/parser_data.h
+++ b/src/parser_data.h
@@ -1,9 +1,10 @@
/**
* @file parser_data.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Data parsers for libyang
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 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.
@@ -156,9 +157,14 @@ struct ly_in;
modified manually. If this flag is used incorrectly (for unordered data),
the behavior is undefined and most functions executed with these
data will not work correctly. */
-#define LYD_PARSE_SUBTREE 0x400000 /**< Parse only the current data subtree with any descendants, no siblings.
+#define LYD_PARSE_SUBTREE 0x400000 /**< Parse only the first child item along with any descendants, but no
+ siblings. This flag is not required when parsing data which do not
+ start at the schema root; for that purpose, use lyd_parse_data's parent
+ argument.
Also, a new return value ::LY_ENOT is returned if there is a sibling
- subtree following in the input data. */
+ subtree following in the input data. Note that if validation is requested,
+ only the newly parsed subtree is validated. This might result in
+ an invalid datastore content. */
#define LYD_PARSE_WHEN_TRUE 0x800000 /**< Mark all the parsed nodes dependend on a when condition with the flag
that means the condition was satisifed before. This allows for
auto-deletion of these nodes during validation. */
@@ -196,6 +202,15 @@ struct ly_in;
#define LYD_VALIDATE_NO_STATE 0x0001 /**< Consider state data not allowed and raise an error if they are found.
Also, no implicit state data are added. */
#define LYD_VALIDATE_PRESENT 0x0002 /**< Validate only modules whose data actually exist. */
+#define LYD_VALIDATE_MULTI_ERROR 0x0004 /**< Do not stop validation on the first error but generate all the detected errors. */
+#define LYD_VALIDATE_OPERATIONAL 0x0008 /**< Semantic constraint violations are reported only as warnings instead of
+ errors (see [RFC 8342 sec. 5.3](https://datatracker.ietf.org/doc/html/rfc8342#section-5.3)). */
+#define LYD_VALIDATE_NO_DEFAULTS 0x0010 /**< Do not add any default nodes during validation, other implicit nodes
+ (such as NP containers) are still added. Validation will fail if a
+ default node is required for it to pass. */
+#define LYD_VALIDATE_NOT_FINAL 0x0020 /**< Skip final validation tasks that require for all the data nodes to
+ either exist or not, based on the YANG constraints. Once the data
+ satisfy this requirement, the final validation should be performed. */
#define LYD_VALIDATE_OPTS_MASK 0x0000FFFF /**< Mask for all the LYD_VALIDATE_* options. */
@@ -205,7 +220,8 @@ struct ly_in;
* @brief Parse (and validate) data from the input handler as a YANG data tree.
*
* @param[in] ctx Context to connect with the tree being built here.
- * @param[in] parent Optional parent to connect the parsed nodes to.
+ * @param[in] parent Optional parent to connect the parsed nodes to. If provided, the data are expected to describe
+ * a subtree of the YANG model instead of starting at the schema root.
* @param[in] in The input handle to provide the dumped data in the specified @p format to parse (and validate).
* @param[in] format Format of the input data to be parsed. Can be 0 to try to detect format from the input handler.
* @param[in] parse_options Options for parser, see @ref dataparseroptions.
@@ -213,6 +229,11 @@ struct ly_in;
* @param[out] tree Full parsed data tree, note that NULL can be a valid tree. If @p parent is set, set to NULL.
* @return LY_SUCCESS in case of successful parsing (and validation).
* @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
+ *
+ * When parsing subtrees (i.e., when @p parent is non-NULL), validation is only performed on the newly parsed data.
+ * This might result in allowing invalid datastore content when the schema contains cross-branch constraints,
+ * complicated `must` statements, etc. When a full-datastore validation is desirable, parse all subtrees
+ * first, and then request validation of the complete datastore content.
*/
LIBYANG_API_DECL LY_ERR lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree);
@@ -303,26 +324,35 @@ LIBYANG_API_DECL LY_ERR lyd_parse_ext_data(const struct lysc_ext_instance *ext,
* @{
*/
enum lyd_type {
- LYD_TYPE_DATA_YANG = 0, /* generic YANG instance data */
- LYD_TYPE_RPC_YANG, /* instance of a YANG RPC/action request with only "input" data children,
- including all parents in case of an action */
- LYD_TYPE_NOTIF_YANG, /* instance of a YANG notification, including all parents in case of a nested one */
- LYD_TYPE_REPLY_YANG, /* instance of a YANG RPC/action reply with only "output" data children,
- including all parents in case of an action */
+ LYD_TYPE_DATA_YANG = 0, /* generic YANG instance data */
+ LYD_TYPE_RPC_YANG, /* instance of a YANG RPC/action request with only "input" data children,
+ including all parents and optional top-level "action" element in case of an action */
+ LYD_TYPE_NOTIF_YANG, /* instance of a YANG notification, including all parents in case of a nested one */
+ LYD_TYPE_REPLY_YANG, /* instance of a YANG RPC/action reply with only "output" data children,
+ including all parents in case of an action */
+
+ LYD_TYPE_RPC_NETCONF, /* complete NETCONF RPC invocation as defined for
+ [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+ [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+ LYD_TYPE_NOTIF_NETCONF, /* complete NETCONF notification message as defined for
+ [notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
+ LYD_TYPE_REPLY_NETCONF, /* complete NETCONF RPC reply as defined for
+ [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+ [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
- LYD_TYPE_RPC_NETCONF, /* complete NETCONF RPC invocation as defined for
- [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
- [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
- LYD_TYPE_NOTIF_NETCONF, /* complete NETCONF notification message as defined for
- [notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
- LYD_TYPE_REPLY_NETCONF /* complete NETCONF RPC reply as defined for
- [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
- [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+ LYD_TYPE_RPC_RESTCONF, /* message-body of a RESTCONF operation input parameters
+ ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.1)) */
+ LYD_TYPE_NOTIF_RESTCONF, /* RESTCONF JSON notification data
+ ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-6.4)), to parse
+ a notification in XML, use ::LYD_TYPE_NOTIF_NETCONF */
+ LYD_TYPE_REPLY_RESTCONF /* message-body of a RESTCONF operation output parameters
+ ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.2)) */
};
/** @} datatype */
/**
- * @brief Parse YANG data into an operation data tree.
+ * @brief Parse YANG data into an operation data tree. Specific parsing flags ::LYD_PARSE_ONLY, ::LYD_PARSE_STRICT and
+ * no validation flags are used.
*
* At least one of @p parent, @p tree, or @p op must always be set.
*
@@ -349,6 +379,28 @@ enum lyd_type {
* - @p op - must be NULL, the reply is appended to the RPC;
* Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
*
+ * - ::LYD_TYPE_RPC_RESTCONF:
+ * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ * - @p format - can be both ::LYD_JSON and ::LYD_XML;
+ * - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be NULL, @p parent points to the operation;
+ *
+ * - ::LYD_TYPE_NOTIF_RESTCONF:
+ * - @p parent - must be NULL, the whole notification is expected;
+ * - @p format - must be ::LYD_JSON, XML-formatted notifications are parsed using ::LYD_TYPE_NOTIF_NETCONF;
+ * - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_REPLY_RESTCONF:
+ * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ * - @p format - can be both ::LYD_JSON and ::LYD_XML;
+ * - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be NULL, @p parent points to the operation;
+ * Note that error reply should be parsed as 'yang-data' extension data.
+ *
* @param[in] ctx libyang context.
* @param[in] parent Optional parent to connect the parsed nodes to.
* @param[in] in Input handle to read the input from.
@@ -427,6 +479,10 @@ LIBYANG_API_DECL LY_ERR lyd_validate_all(struct lyd_node **tree, const struct ly
* The data tree is modified in-place. As a result of the validation, some data might be removed
* from the tree. In that case, the removed items are freed, not just unlinked.
*
+ * If several modules need to be validated, the flag ::LYD_VALIDATE_NOT_FINAL should be used first for validation
+ * of each module and then ::lyd_validate_module_final() should be called also for each module. Otherwise,
+ * false-positive validation errors for foreign dependencies may occur.
+ *
* @param[in,out] tree Data tree to recursively validate. May be changed by validation, might become NULL.
* @param[in] module Module whose data (and schema restrictions) to validate.
* @param[in] val_opts Validation options (@ref datavalidationoptions).
@@ -438,6 +494,20 @@ LIBYANG_API_DECL LY_ERR lyd_validate_module(struct lyd_node **tree, const struct
struct lyd_node **diff);
/**
+ * @brief Finish validation of a module data that have previously been validated with ::LYD_VALIDATE_NOT_FINAL flag.
+ *
+ * This final validation will not add or remove any nodes.
+ *
+ * @param[in] tree Data tree to recursively validate.
+ * @param[in] module Module whose data (and schema restrictions) to validate.
+ * @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_validate_module_final(struct lyd_node *tree, const struct lys_module *module,
+ uint32_t val_opts);
+
+/**
* @brief Validate an RPC/action request, reply, or notification. Only the operation data tree (input/output/notif)
* is validate, any parents are ignored.
*
diff --git a/src/parser_internal.h b/src/parser_internal.h
index 458d297..92412e2 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -1,9 +1,10 @@
/**
* @file parser_internal.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Internal structures and functions for libyang parsers
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 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.
@@ -27,6 +28,23 @@ struct lysp_yin_ctx;
struct lysp_ctx;
/**
+ * @brief Check data parser error taking into account multi-error validation.
+ *
+ * @param[in] r Local return value.
+ * @param[in] err_cmd Command to perform on any error.
+ * @param[in] lydctx Data parser context.
+ * @param[in] label Label to go to on fatal error.
+ */
+#define LY_DPARSER_ERR_GOTO(r, err_cmd, lydctx, label) \
+ if (r) { \
+ err_cmd; \
+ if ((r != LY_EVALID) || !lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || \
+ (ly_vecode(((struct lyd_ctx *)lydctx)->data_ctx->ctx) == LYVE_SYNTAX)) { \
+ goto label; \
+ } \
+ }
+
+/**
* @brief Callback for ::lyd_ctx to free the structure
*
* @param[in] ctx Data parser context to free.
@@ -43,6 +61,7 @@ typedef void (*lyd_ctx_free_clb)(struct lyd_ctx *ctx);
#define LYD_INTOPT_ANY 0x10 /**< Anydata/anyxml content is being parsed, there can be anything. */
#define LYD_INTOPT_WITH_SIBLINGS 0x20 /**< Parse the whole input with any siblings. */
#define LYD_INTOPT_NO_SIBLINGS 0x40 /**< If there are any siblings, return an error. */
+#define LYD_INTOPT_EVENTTIME 0x80 /**< Parse notification eventTime node. */
/**
* @brief Internal (common) context for YANG data parsers.
@@ -118,7 +137,8 @@ struct lyd_json_ctx {
/* callbacks */
lyd_ctx_free_clb free;
- struct lyjson_ctx *jsonctx; /**< JSON context */
+ struct lyjson_ctx *jsonctx; /**< JSON context */
+ const struct lysc_node *any_schema; /**< parent anyxml/anydata schema node if parsing nested data tree */
};
/**
@@ -279,6 +299,27 @@ LY_ERR lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
/**
+ * @brief Parse JSON string as a RESTCONF message.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input structure.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] data_type Expected RESTCONF data type of the data.
+ * @param[out] envp Individual parsed envelopes tree, may be returned possibly even on an error.
+ * @param[out] parsed Set to add all the parsed siblings into.
+ * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type,
+ struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p);
+
+/**
* @brief Parse binary LYB data as a YANG data tree.
*
* @param[in] ctx libyang context.
@@ -299,6 +340,15 @@ LY_ERR lyd_parse_lyb(const struct ly_ctx *ctx, const struct lysc_ext_instance *e
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
/**
+ * @brief Validate eventTime date-and-time value.
+ *
+ * @param[in] node Opaque eventTime node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_parser_notif_eventtime_validate(const struct lyd_node *node);
+
+/**
* @brief Search all the parents for an operation node, check validity based on internal parser flags.
*
* @param[in] parent Parent to connect the parsed nodes to.
@@ -309,6 +359,14 @@ LY_ERR lyd_parse_lyb(const struct ly_ctx *ctx, const struct lysc_ext_instance *e
LY_ERR lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op);
/**
+ * @brief Get schema node of a node being parsed, use nodes stored for logging.
+ *
+ * @param[in] node Node whose schema node to get.
+ * @return Schema node even for an opaque node, NULL if none found.
+ */
+const struct lysc_node *lyd_parser_node_schema(const struct lyd_node *node);
+
+/**
* @brief Check that a data node representing the @p snode is suitable based on options.
*
* @param[in] lydctx Common data parsers context.
diff --git a/src/parser_json.c b/src/parser_json.c
index 6219c6e..3655d4c 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief JSON data parser for libyang
*
- * Copyright (c) 2020 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 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.
@@ -161,29 +161,32 @@ lydjson_get_node_prefix(struct lyd_node *node, const char *local_prefix, size_t
return LY_SUCCESS;
}
- *prefix_p = NULL;
while (node) {
if (node->schema) {
- *prefix_p = node->schema->module->name;
+ module_name = node->schema->module->name;
break;
}
onode = (struct lyd_node_opaq *)node;
if (onode->name.module_name) {
- *prefix_p = onode->name.module_name;
+ module_name = onode->name.module_name;
break;
} else if (onode->name.prefix) {
- *prefix_p = onode->name.prefix;
+ module_name = onode->name.prefix;
break;
}
node = lyd_parent(node);
}
- *prefix_len_p = ly_strlen(module_name);
+ *prefix_p = module_name;
+ *prefix_len_p = ly_strlen(module_name);
return LY_SUCCESS;
}
/**
- * @brief Skip the current JSON object/array.
+ * @brief Skip the current JSON item (based on status).
+ *
+ * The JSON context is moved to the last "status" of the JSON item so to completely
+ * finish the skip, one more JSON context move is required.
*
* @param[in] jsonctx JSON context with the input data to skip.
* @return LY_ERR value.
@@ -192,34 +195,39 @@ static LY_ERR
lydjson_data_skip(struct lyjson_ctx *jsonctx)
{
enum LYJSON_PARSER_STATUS status, current;
- uint32_t orig_depth;
+ uint32_t depth;
- status = lyjson_ctx_status(jsonctx, 0);
- assert((status == LYJSON_OBJECT) || (status == LYJSON_ARRAY));
- orig_depth = jsonctx->depth;
+ status = lyjson_ctx_status(jsonctx);
+ depth = lyjson_ctx_depth(jsonctx);
- /* next */
- LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
-
- if ((status == LYJSON_OBJECT) && (current != LYJSON_OBJECT) && (current != LYJSON_ARRAY)) {
- /* no nested objects */
- LY_CHECK_RET(lyjson_ctx_next(jsonctx, NULL));
- return LY_SUCCESS;
- }
+ switch (status) {
+ case LYJSON_OBJECT:
+ ++depth;
- /* skip after the content */
- while ((jsonctx->depth > orig_depth) || (current != status + 1)) {
- if (current == LYJSON_ARRAY) {
- /* skip the array separately */
- LY_CHECK_RET(lydjson_data_skip(jsonctx));
- current = lyjson_ctx_status(jsonctx, 0);
- } else {
+ /* skip until object closes */
+ do {
LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
- }
+ } while ((current != LYJSON_OBJECT_CLOSED) || (depth != lyjson_ctx_depth(jsonctx)));
+ break;
+ case LYJSON_ARRAY:
+ ++depth;
- if (current == LYJSON_END) {
- break;
+ /* skip until array closes */
+ do {
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
+ } while ((current != LYJSON_ARRAY_CLOSED) || (depth != lyjson_ctx_depth(jsonctx)));
+ break;
+ case LYJSON_OBJECT_NAME:
+ /* just get to the value */
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
+ if ((current == LYJSON_OBJECT) || (current == LYJSON_ARRAY)) {
+ LY_CHECK_RET(lydjson_data_skip(jsonctx));
}
+ break;
+ default:
+ /* no other status really expected, just go to next */
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
+ break;
}
return LY_SUCCESS;
@@ -280,14 +288,6 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
ret = LY_EVALID;
goto cleanup;
}
- if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
- /* skip element with children */
- ret = lydjson_data_skip(lydctx->jsonctx);
- LY_CHECK_GOTO(ret, cleanup);
-
- ret = LY_ENOT;
- goto cleanup;
- }
}
/* get the schema node */
@@ -295,7 +295,7 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
if (!parent && lydctx->ext) {
*snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
} else {
- *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
+ *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts);
}
if (!*snode) {
/* check for extension data */
@@ -326,13 +326,6 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
}
ret = LY_EVALID;
goto cleanup;
- } else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
- /* skip element with children */
- ret = lydjson_data_skip(lydctx->jsonctx);
- LY_CHECK_GOTO(ret, cleanup);
-
- ret = LY_ENOT;
- goto cleanup;
}
} else {
/* check that schema node is valid and can be used */
@@ -345,6 +338,55 @@ cleanup:
}
/**
+ * @brief Get the hint for the data type parsers according to the current JSON parser context.
+ *
+ * @param[in] jsonctx JSON parser context. The context is supposed to be on a value.
+ * @param[in,out] status Pointer to the current context status,
+ * in some circumstances the function manipulates with the context so the status is updated.
+ * @param[out] type_hint_p Pointer to the variable to store the result.
+ * @return LY_SUCCESS in case of success.
+ * @return LY_EINVAL in case of invalid context status not referring to a value.
+ */
+static LY_ERR
+lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
+{
+ *type_hint_p = 0;
+
+ if (*status_p == LYJSON_ARRAY) {
+ /* only [null] */
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, status_p));
+ if (*status_p != LYJSON_NULL) {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON,
+ "Expected JSON name/value or special name/[null], but input data contains name/[%s].",
+ lyjson_token2str(*status_p));
+ return LY_EINVAL;
+ }
+
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, NULL));
+ if (lyjson_ctx_status(jsonctx) != LYJSON_ARRAY_CLOSED) {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Expected array end, but input data contains %s.",
+ lyjson_token2str(*status_p));
+ return LY_EINVAL;
+ }
+
+ *type_hint_p = LYD_VALHINT_EMPTY;
+ } else if (*status_p == LYJSON_STRING) {
+ *type_hint_p = LYD_VALHINT_STRING | LYD_VALHINT_NUM64;
+ } else if (*status_p == LYJSON_NUMBER) {
+ *type_hint_p = LYD_VALHINT_DECNUM;
+ } else if ((*status_p == LYJSON_FALSE) || (*status_p == LYJSON_TRUE)) {
+ *type_hint_p = LYD_VALHINT_BOOLEAN;
+ } else if (*status_p == LYJSON_NULL) {
+ *type_hint_p = 0;
+ } else {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Unexpected input data %s.", lyjson_token2str(*status_p));
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
* @brief Check that the input data are parseable as the @p list.
*
* Checks for all the list's keys. Function does not revert the context state.
@@ -357,128 +399,85 @@ cleanup:
static LY_ERR
lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
{
- LY_ERR ret = LY_SUCCESS;
- enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx, 0);
+ LY_ERR rc = LY_SUCCESS;
+ enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
struct ly_set key_set = {0};
const struct lysc_node *snode;
- uint32_t i, status_count;
+ uint32_t i, hints;
assert(list && (list->nodetype == LYS_LIST));
/* get all keys into a set (keys do not have if-features or anything) */
snode = NULL;
while ((snode = lys_getnext(snode, list, NULL, 0)) && (snode->flags & LYS_KEY)) {
- ret = ly_set_add(&key_set, (void *)snode, 1, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = ly_set_add(&key_set, (void *)snode, 1, NULL);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ if (!key_set.count) {
+ /* no keys */
+ goto cleanup;
}
if (status == LYJSON_OBJECT) {
- status_count = jsonctx->status.count;
-
- while (key_set.count && (status != LYJSON_OBJECT_CLOSED)) {
+ do {
const char *name, *prefix;
size_t name_len, prefix_len;
ly_bool is_attr;
/* match the key */
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(jsonctx, &status), cleanup);
+ if (status != LYJSON_OBJECT_NAME) {
+ break;
+ }
snode = NULL;
lydjson_parse_name(jsonctx->value, jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
if (!is_attr && !prefix) {
for (i = 0; i < key_set.count; ++i) {
- snode = (const struct lysc_node *)key_set.objs[i];
- if (!ly_strncmp(snode->name, name, name_len)) {
+ if (!ly_strncmp(key_set.snodes[i]->name, name, name_len)) {
+ snode = key_set.snodes[i];
break;
}
}
- /* go into the item to a) process it as a key or b) start skipping it as another list child */
- ret = lyjson_ctx_next(jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the value */
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(jsonctx, &status), cleanup);
if (snode) {
/* we have the key, validate the value */
- if (status < LYJSON_NUMBER) {
+ if ((status < LYJSON_NUMBER) || (status > LYJSON_NULL)) {
/* not a terminal */
- ret = LY_ENOT;
+ rc = LY_ENOT;
goto cleanup;
}
- ret = lys_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lydjson_value_type_hint(jsonctx, &status, &hints);
+ LY_CHECK_GOTO(rc, cleanup);
+ rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, hints);
+ LY_CHECK_GOTO(rc, cleanup);
/* key with a valid value, remove from the set */
ly_set_rm_index(&key_set, i, NULL);
}
+
+ /* next object */
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(jsonctx, &status), cleanup);
} else {
- /* start skipping the member we are not interested in */
- ret = lyjson_ctx_next(jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
- }
- /* move to the next child */
- while (status_count < jsonctx->status.count) {
- ret = lyjson_ctx_next(jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
+ /* skip the uninteresting object */
+ LY_CHECK_GOTO(rc = lydjson_data_skip(jsonctx), cleanup);
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(jsonctx, &status), cleanup);
}
- }
+ } while (key_set.count && (status == LYJSON_OBJECT_NEXT));
}
if (key_set.count) {
/* some keys are missing/did not validate */
- ret = LY_ENOT;
+ rc = LY_ENOT;
}
cleanup:
ly_set_erase(&key_set, NULL);
- return ret;
-}
-
-/**
- * @brief Get the hint for the data type parsers according to the current JSON parser context.
- *
- * @param[in] lydctx JSON data parser context. The context is supposed to be on a value.
- * @param[in,out] status Pointer to the current context status,
- * in some circumstances the function manipulates with the context so the status is updated.
- * @param[out] type_hint_p Pointer to the variable to store the result.
- * @return LY_SUCCESS in case of success.
- * @return LY_EINVAL in case of invalid context status not referring to a value.
- */
-static LY_ERR
-lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
-{
- *type_hint_p = 0;
-
- if (*status_p == LYJSON_ARRAY) {
- /* only [null] */
- LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_p));
- if (*status_p != LYJSON_NULL) {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON,
- "Expected JSON name/value or special name/[null], but input data contains name/[%s].",
- lyjson_token2str(*status_p));
- return LY_EINVAL;
- }
-
- LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, NULL));
- if (lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_ARRAY_CLOSED) {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Expected array end, but input data contains %s.",
- lyjson_token2str(*status_p));
- return LY_EINVAL;
- }
-
- *type_hint_p = LYD_VALHINT_EMPTY;
- } else if (*status_p == LYJSON_STRING) {
- *type_hint_p = LYD_VALHINT_STRING | LYD_VALHINT_NUM64;
- } else if (*status_p == LYJSON_NUMBER) {
- *type_hint_p = LYD_VALHINT_DECNUM;
- } else if ((*status_p == LYJSON_FALSE) || (*status_p == LYJSON_TRUE)) {
- *type_hint_p = LYD_VALHINT_BOOLEAN;
- } else if (*status_p == LYJSON_NULL) {
- *type_hint_p = 0;
- } else {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Unexpected input data %s.", lyjson_token2str(*status_p));
- return LY_EINVAL;
- }
-
- return LY_SUCCESS;
+ return rc;
}
/**
@@ -512,7 +511,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
/* backup parser */
lyjson_ctx_backup(jsonctx);
- status = lyjson_ctx_status(jsonctx, 0);
+ status = lyjson_ctx_status(jsonctx);
if (lydctx->parse_opts & LYD_PARSE_OPAQ) {
/* check if the node is parseable. if not, NULL the snode to announce that it is supposed to be parsed
@@ -521,12 +520,10 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
case LYS_LEAFLIST:
case LYS_LEAF:
/* value may not be valid in which case we parse it as an opaque node */
- ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
- if (ret) {
+ if ((ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p))) {
break;
}
-
- if (lys_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL)) {
+ if (ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, *type_hint_p)) {
ret = LY_ENOT;
}
break;
@@ -539,8 +536,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
break;
}
} else if (snode->nodetype & LYD_NODE_TERM) {
- status = lyjson_ctx_status(jsonctx, 0);
- ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
+ ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p);
}
/* restore parser */
@@ -725,7 +721,7 @@ cleanup:
static LY_ERR
lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lyd_node *node)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR rc = LY_SUCCESS, r;
enum LYJSON_PARSER_STATUS status;
const char *expected;
ly_bool in_parent = 0;
@@ -745,33 +741,29 @@ lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, str
LOG_LOCSET(snode, NULL, NULL, NULL);
/* move to the second item in the name/X pair */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
/* check attribute encoding */
switch (nodetype) {
case LYS_LEAFLIST:
expected = "@name/array of objects/nulls";
-
LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
next_entry:
- instance++;
-
- /* move into array/next entry */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
-
if (status == LYJSON_ARRAY_CLOSED) {
/* no more metadata */
goto cleanup;
}
+
+ /* move into the array/next item */
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ instance++;
LY_CHECK_GOTO((status != LYJSON_OBJECT) && (status != LYJSON_NULL), representation_error);
if (!node || (node->schema != prev->schema)) {
LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance #%u of %s:%s to be coupled with metadata.",
instance, prev->schema->module->name, prev->schema->name);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
@@ -779,6 +771,8 @@ next_entry:
/* continue with the next entry in the leaf-list array */
prev = node;
node = node->next;
+
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
goto next_entry;
}
break;
@@ -800,31 +794,32 @@ next_entry:
break;
default:
LOGINT(ctx);
- ret = LY_EINT;
+ rc = LY_EINT;
goto cleanup;
}
/* process all the members inside a single metadata object */
assert(status == LYJSON_OBJECT);
- while (status != LYJSON_OBJECT_CLOSED) {
- LY_CHECK_GOTO(status != LYJSON_OBJECT, representation_error);
+ do {
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ LY_CHECK_GOTO(status != LYJSON_OBJECT_NAME, representation_error);
lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &dynamic_prefname);
if (!name_len) {
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON found with an empty name, followed by: %.10s", name);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
} else if (!prefix_len) {
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON must be namespace-qualified, missing prefix for \"%.*s\".",
(int)lydctx->jsonctx->value_len, lydctx->jsonctx->value);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
} else if (is_attr) {
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Invalid format of the Metadata identifier in JSON, unexpected '@' in \"%.*s\"",
(int)lydctx->jsonctx->value_len, lydctx->jsonctx->value);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
@@ -834,14 +829,13 @@ next_entry:
if (lydctx->parse_opts & LYD_PARSE_STRICT) {
LOGVAL(ctx, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.",
(int)prefix_len, prefix, (int)name_len, name);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
if (node->schema) {
/* skip element with children */
- ret = lydjson_data_skip(lydctx->jsonctx);
- LY_CHECK_GOTO(ret, cleanup);
- status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ LY_CHECK_GOTO(rc = lydjson_data_skip(lydctx->jsonctx), cleanup);
+ status = lyjson_ctx_status(lydctx->jsonctx);
/* end of the item */
continue;
}
@@ -849,22 +843,20 @@ next_entry:
}
/* get the value */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
/* get value hints */
- ret = lydjson_value_type_hint(lydctx, &status, &val_hints);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx->jsonctx, &status, &val_hints), cleanup);
if (node->schema) {
/* create metadata */
- ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, lydctx->jsonctx->value,
+ rc = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, lydctx->jsonctx->value,
lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, val_hints, node->schema);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(rc, cleanup);
/* add/correct flags */
- ret = lyd_parse_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lyd_parse_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL);
+ LY_CHECK_GOTO(rc, cleanup);
} else {
/* create attribute */
const char *module_name;
@@ -873,22 +865,24 @@ next_entry:
lydjson_get_node_prefix(node, prefix, prefix_len, &module_name, &module_name_len);
/* attr2 is always changed to the created attribute */
- ret = lyd_create_attr(node, NULL, lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, module_name,
+ rc = lyd_create_attr(node, NULL, lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, module_name,
module_name_len, lydctx->jsonctx->value, lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic,
LY_VALUE_JSON, NULL, val_hints);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(rc, cleanup);
}
+
/* next member */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
- LY_CHECK_GOTO((status != LYJSON_OBJECT) && (status != LYJSON_OBJECT_CLOSED), representation_error);
- }
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ } while (status == LYJSON_OBJECT_NEXT);
+ LY_CHECK_GOTO(status != LYJSON_OBJECT_CLOSED, representation_error);
if (nodetype == LYS_LEAFLIST) {
/* continue by processing another metadata object for the following
* leaf-list instance since they are always instantiated in JSON array */
prev = node;
node = node->next;
+
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
goto next_entry;
}
@@ -900,12 +894,18 @@ representation_error:
"The attribute(s) of %s \"%s\" is expected to be represented as JSON %s, but input data contains @%s/%s.",
lys_nodetype2str(nodetype), node ? LYD_NAME(node) : LYD_NAME(prev), expected, lyjson_token2str(status),
in_parent ? "" : "name");
- ret = LY_EVALID;
+ rc = LY_EVALID;
cleanup:
+ if ((rc == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) {
+ /* try to skip the invalid data */
+ if ((r = lydjson_data_skip(lydctx->jsonctx))) {
+ rc = r;
+ }
+ }
free(dynamic_prefname);
LOG_LOCBACK(1, 0, 0, 0);
- return ret;
+ return rc;
}
/**
@@ -922,24 +922,26 @@ static void
lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last,
struct lysc_ext_instance *ext)
{
- if (*node_p) {
- /* insert, keep first pointer correct */
- if (ext) {
- lyplg_ext_insert(parent, *node_p);
+ if (!*node_p) {
+ return;
+ }
+
+ /* insert, keep first pointer correct */
+ if (ext) {
+ lyplg_ext_insert(parent, *node_p);
+ } else {
+ lyd_insert_node(parent, first_p, *node_p, last);
+ }
+ if (first_p) {
+ if (parent) {
+ *first_p = lyd_child(parent);
} else {
- lyd_insert_node(parent, first_p, *node_p, last);
- }
- if (first_p) {
- if (parent) {
- *first_p = lyd_child(parent);
- } else {
- while ((*first_p)->prev->next) {
- *first_p = (*first_p)->prev;
- }
+ while ((*first_p)->prev->next) {
+ *first_p = (*first_p)->prev;
}
}
- *node_p = NULL;
}
+ *node_p = NULL;
}
/**
@@ -950,8 +952,7 @@ lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, st
* @param[in] name_len Length of the @p name string.
* @param[in] prefix Prefix of the opaq node to create.
* @param[in] prefix_len Length of the @p prefx string.
- * @param[in] parent Data parent of the opaq node to create, can be NULL in case of top level,
- * but must be set if @p first is not.
+ * @param[in] parent Data parent of the opaq node, to inherit module name from.
* @param[in,out] status_inner_p In case of processing JSON array, this parameter points to a standalone
* context status of the array content. Otherwise, it is supposed to be the same as @p status_p.
* @param[out] node_p Pointer to the created opaq node.
@@ -967,18 +968,25 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l
ly_bool dynamic = 0;
uint32_t type_hint = 0;
- if ((*status_inner_p != LYJSON_OBJECT) && (*status_inner_p != LYJSON_OBJECT_EMPTY)) {
+ if (*status_inner_p != LYJSON_OBJECT) {
/* prepare for creating opaq node with a value */
value = lydctx->jsonctx->value;
value_len = lydctx->jsonctx->value_len;
dynamic = lydctx->jsonctx->dynamic;
lydctx->jsonctx->dynamic = 0;
- LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint));
+ LY_CHECK_RET(lydjson_value_type_hint(lydctx->jsonctx, status_inner_p, &type_hint));
}
- /* create node */
+ /* get the module name */
lydjson_get_node_prefix(parent, prefix, prefix_len, &module_name, &module_name_len);
+ if (!module_name && !parent && lydctx->any_schema) {
+ /* in an anyxml/anydata tree, parsing first node, use the previous any schema node */
+ module_name = lydctx->any_schema->module->name;
+ module_name_len = strlen(module_name);
+ }
+
+ /* create node */
ret = lyd_create_opaq(lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, module_name, module_name_len, value,
value_len, &dynamic, LY_VALUE_JSON, NULL, type_hint, node_p);
if (dynamic) {
@@ -1016,61 +1024,80 @@ lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_le
struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p, enum LYJSON_PARSER_STATUS *status_inner_p,
struct lyd_node **first_p, struct lyd_node **node_p)
{
- LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p), cleanup);
+
+ assert(*node_p);
+ LOG_LOCSET(NULL, *node_p, NULL, NULL);
if ((*status_p == LYJSON_ARRAY) && (*status_inner_p == LYJSON_NULL)) {
/* special array null value */
((struct lyd_node_opaq *)*node_p)->hints |= LYD_VALHINT_EMPTY;
/* must be the only item */
- LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup);
if (*status_inner_p != LYJSON_ARRAY_CLOSED) {
LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX, "Array \"null\" member with another member.");
- return LY_EVALID;
+ ret = LY_EVALID;
+ goto cleanup;
}
goto finish;
}
- while ((*status_p == LYJSON_ARRAY) || (*status_p == LYJSON_ARRAY_EMPTY)) {
+ while (*status_p == LYJSON_ARRAY) {
/* process another instance of the same node */
-
- if ((*status_inner_p == LYJSON_OBJECT) || (*status_inner_p == LYJSON_OBJECT_EMPTY)) {
+ if (*status_inner_p == LYJSON_OBJECT) {
/* array with objects, list */
((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST;
/* but first process children of the object in the array */
- while ((*status_inner_p != LYJSON_OBJECT_CLOSED) && (*status_inner_p != LYJSON_OBJECT_EMPTY)) {
- LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
- *status_inner_p = lyjson_ctx_status(lydctx->jsonctx, 0);
- }
+ do {
+ LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL), cleanup);
+ *status_inner_p = lyjson_ctx_status(lydctx->jsonctx);
+ } while (*status_inner_p == LYJSON_OBJECT_NEXT);
} else {
/* array with values, leaf-list */
((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LEAFLIST;
}
- LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup);
if (*status_inner_p == LYJSON_ARRAY_CLOSED) {
goto finish;
}
+ assert(*status_inner_p == LYJSON_ARRAY_NEXT);
/* continue with the next instance */
- assert(node_p);
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup);
+ assert(*node_p);
lydjson_maintain_children(parent, first_p, node_p, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, NULL);
- LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
+
+ LOG_LOCBACK(0, 1, 0, 0);
+
+ LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p), cleanup);
+
+ assert(*node_p);
+ LOG_LOCSET(NULL, *node_p, NULL, NULL);
}
- if ((*status_p == LYJSON_OBJECT) || (*status_p == LYJSON_OBJECT_EMPTY)) {
+ if (*status_p == LYJSON_OBJECT) {
/* process children */
- while (*status_p != LYJSON_OBJECT_CLOSED && *status_p != LYJSON_OBJECT_EMPTY) {
- LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
- *status_p = lyjson_ctx_status(lydctx->jsonctx, 0);
- }
+ do {
+ LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL), cleanup);
+ *status_p = lyjson_ctx_status(lydctx->jsonctx);
+ } while (*status_p == LYJSON_OBJECT_NEXT);
}
finish:
/* finish linking metadata */
- return lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p));
+ ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p));
+
+cleanup:
+ if (*node_p) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ return ret;
}
/**
@@ -1094,8 +1121,8 @@ finish:
* @return LY_ERR value.
*/
static LY_ERR
-lydjson_ctx_next_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len,
- const char *prefix, size_t prefix_len, struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p,
+lydjson_ctx_next_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len, const char *prefix,
+ size_t prefix_len, struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p,
struct lyd_node **first_p, struct lyd_node **node_p)
{
enum LYJSON_PARSER_STATUS status_inner = 0;
@@ -1232,79 +1259,94 @@ static LY_ERR
lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext,
enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
{
- LY_ERR rc = LY_SUCCESS;
- uint32_t prev_parse_opts, prev_int_opts;
+ LY_ERR r, rc = LY_SUCCESS;
+ uint32_t prev_parse_opts = lydctx->parse_opts, prev_int_opts = lydctx->int_opts;
struct ly_in in_start;
char *val = NULL;
- struct lyd_node *tree = NULL;
+ const char *end;
+ struct lyd_node *child = NULL;
+ ly_bool log_node = 0;
assert(snode->nodetype & LYD_NODE_ANY);
+ *node = NULL;
+
/* status check according to allowed JSON types */
if (snode->nodetype == LYS_ANYXML) {
- LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY) && (*status != LYJSON_ARRAY) &&
- (*status != LYJSON_ARRAY_EMPTY) && (*status != LYJSON_NUMBER) && (*status != LYJSON_STRING) &&
- (*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) && (*status != LYJSON_NULL), LY_ENOT);
+ LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_ARRAY) && (*status != LYJSON_NUMBER) &&
+ (*status != LYJSON_STRING) && (*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) &&
+ (*status != LYJSON_NULL), LY_ENOT);
} else {
- LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_ENOT);
+ LY_CHECK_RET(*status != LYJSON_OBJECT, LY_ENOT);
}
/* create any node */
switch (*status) {
case LYJSON_OBJECT:
+ /* create node */
+ r = lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ assert(*node);
+ LOG_LOCSET(NULL, *node, NULL, NULL);
+ log_node = 1;
+
/* parse any data tree with correct options, first backup the current options and then make the parser
* process data as opaq nodes */
- prev_parse_opts = lydctx->parse_opts;
lydctx->parse_opts &= ~LYD_PARSE_STRICT;
lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
- prev_int_opts = lydctx->int_opts;
lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
+ lydctx->any_schema = snode;
/* process the anydata content */
- while (*status != LYJSON_OBJECT_CLOSED) {
- LY_CHECK_RET(lydjson_subtree_r(lydctx, NULL, &tree, NULL));
- *status = lyjson_ctx_status(lydctx->jsonctx, 0);
- }
+ do {
+ r = lydjson_subtree_r(lydctx, NULL, &child, NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
- /* restore parser options */
- lydctx->parse_opts = prev_parse_opts;
- lydctx->int_opts = prev_int_opts;
+ *status = lyjson_ctx_status(lydctx->jsonctx);
+ } while (*status == LYJSON_OBJECT_NEXT);
/* finish linking metadata */
- LY_CHECK_RET(lydjson_metadata_finish(lydctx, &tree));
+ r = lydjson_metadata_finish(lydctx, &child);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- LY_CHECK_RET(lyd_create_any(snode, tree, LYD_ANYDATA_DATATREE, 1, node));
- break;
- case LYJSON_ARRAY_EMPTY:
- /* store the empty array */
- if (asprintf(&val, "[]") == -1) {
- LOGMEM(lydctx->jsonctx->ctx);
- return LY_EMEM;
- }
- LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+ /* assign the data tree */
+ ((struct lyd_node_any *)*node)->value.tree = child;
+ child = NULL;
break;
case LYJSON_ARRAY:
/* skip until the array end */
in_start = *lydctx->jsonctx->in;
- LY_CHECK_RET(lydjson_data_skip(lydctx->jsonctx));
+ LY_CHECK_GOTO(rc = lydjson_data_skip(lydctx->jsonctx), cleanup);
+
+ /* return back by all the WS */
+ end = lydctx->jsonctx->in->current;
+ while (is_jsonws(end[-1])) {
+ --end;
+ }
/* make a copy of the whole array and store it */
- if (asprintf(&val, "[%.*s", (int)(lydctx->jsonctx->in->current - in_start.current), in_start.current) == -1) {
+ if (asprintf(&val, "[%.*s", (int)(end - in_start.current), in_start.current) == -1) {
LOGMEM(lydctx->jsonctx->ctx);
- return LY_EMEM;
+ rc = LY_EMEM;
+ goto cleanup;
}
- LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+ r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ val = NULL;
break;
case LYJSON_STRING:
/* string value */
if (lydctx->jsonctx->dynamic) {
- LY_CHECK_RET(lyd_create_any(snode, lydctx->jsonctx->value, LYD_ANYDATA_STRING, 1, node));
+ LY_CHECK_GOTO(rc = lyd_create_any(snode, lydctx->jsonctx->value, LYD_ANYDATA_STRING, 1, node), cleanup);
lydctx->jsonctx->dynamic = 0;
} else {
val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len);
- LY_CHECK_ERR_RET(!val, LOGMEM(lydctx->jsonctx->ctx), LY_EMEM);
+ LY_CHECK_ERR_GOTO(!val, LOGMEM(lydctx->jsonctx->ctx); rc = LY_EMEM, cleanup);
- LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node), val_err);
+ r = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ val = NULL;
}
break;
case LYJSON_NUMBER:
@@ -1313,26 +1355,32 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st
/* JSON value */
assert(!lydctx->jsonctx->dynamic);
val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len);
- LY_CHECK_ERR_RET(!val, LOGMEM(lydctx->jsonctx->ctx), LY_EMEM);
+ LY_CHECK_ERR_GOTO(!val, LOGMEM(lydctx->jsonctx->ctx); rc = LY_EMEM, cleanup);
- LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+ r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ val = NULL;
break;
case LYJSON_NULL:
/* no value */
- LY_CHECK_RET(lyd_create_any(snode, NULL, LYD_ANYDATA_JSON, 1, node));
- break;
- case LYJSON_OBJECT_EMPTY:
- /* empty object */
- LY_CHECK_RET(lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node));
+ r = lyd_create_any(snode, NULL, LYD_ANYDATA_JSON, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
break;
default:
- LOGINT_RET(lydctx->jsonctx->ctx);
+ LOGINT(lydctx->jsonctx->ctx);
+ rc = LY_EINT;
+ goto cleanup;
}
- return LY_SUCCESS;
-
-val_err:
+cleanup:
+ if (log_node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ lydctx->parse_opts = prev_parse_opts;
+ lydctx->int_opts = prev_int_opts;
+ lydctx->any_schema = NULL;
free(val);
+ lyd_free_tree(child);
return rc;
}
@@ -1352,10 +1400,10 @@ static LY_ERR
lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext,
enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
uint32_t prev_parse_opts = lydctx->parse_opts;
- LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_ENOT);
+ LY_CHECK_RET(*status != LYJSON_OBJECT, LY_ENOT);
/* create inner node */
LY_CHECK_RET(lyd_create_inner(snode, node));
@@ -1369,38 +1417,43 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node
}
/* process children */
- while ((*status != LYJSON_OBJECT_CLOSED) && (*status != LYJSON_OBJECT_EMPTY)) {
- ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
- LY_CHECK_GOTO(ret, cleanup);
- *status = lyjson_ctx_status(lydctx->jsonctx, 0);
- }
+ do {
+ r = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
+ *status = lyjson_ctx_status(lydctx->jsonctx);
+ } while (*status == LYJSON_OBJECT_NEXT);
/* finish linking metadata */
- ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node));
- LY_CHECK_GOTO(ret, cleanup);
+ r = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node));
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (snode->nodetype == LYS_LIST) {
/* check all keys exist */
- ret = lyd_parse_check_keys(*node);
- LY_CHECK_GOTO(ret, cleanup);
+ r = lyd_parse_check_keys(*node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
- if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
- /* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) {
+ /* new node validation, autodelete CANNOT occur (it can if multi-error), all nodes are new */
+ r = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, lydctx->val_opts, NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
/* add any missing default children */
- ret = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when,
- &lydctx->node_types, &lydctx->ext_node,
- (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ r = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
+ &lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
cleanup:
lydctx->parse_opts = prev_parse_opts;
LOG_LOCBACK(0, 1, 0, 0);
- return ret;
+ if (!(*node)->hash) {
+ /* list without keys is unusable */
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+ return rc;
}
/**
@@ -1427,52 +1480,61 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str
const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len,
const char *prefix, size_t prefix_len, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
uint32_t type_hints = 0;
LOG_LOCSET(snode, NULL, NULL, NULL);
- ret = lydjson_data_check_opaq(lydctx, snode, &type_hints);
- if (ret == LY_SUCCESS) {
+ r = lydjson_data_check_opaq(lydctx, snode, &type_hints);
+ if (r == LY_SUCCESS) {
assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY));
if (snode->nodetype & LYD_NODE_TERM) {
if ((*status != LYJSON_ARRAY) && (*status != LYJSON_NUMBER) && (*status != LYJSON_STRING) &&
(*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) && (*status != LYJSON_NULL)) {
- ret = LY_ENOT;
+ rc = LY_ENOT;
goto cleanup;
}
/* create terminal node */
- LY_CHECK_GOTO(ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value,
- lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node), cleanup);
+ r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value,
+ lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
/* move JSON parser */
if (*status == LYJSON_ARRAY) {
/* only [null], 2 more moves are needed */
- LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status), cleanup);
+ r = lyjson_ctx_next(lydctx->jsonctx, status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
assert(*status == LYJSON_NULL);
- LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status), cleanup);
+
+ r = lyjson_ctx_next(lydctx->jsonctx, status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
assert(*status == LYJSON_ARRAY_CLOSED);
}
} else if (snode->nodetype & LYD_NODE_INNER) {
/* create inner node */
- LY_CHECK_GOTO(ret = lydjson_parse_instance_inner(lydctx, snode, ext, status, node), cleanup);
+ r = lydjson_parse_instance_inner(lydctx, snode, ext, status, node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
} else {
/* create any node */
- LY_CHECK_GOTO(ret = lydjson_parse_any(lydctx, snode, ext, status, node), cleanup);
+ r = lydjson_parse_any(lydctx, snode, ext, status, node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
+ LY_CHECK_GOTO(!*node, cleanup);
/* add/correct flags */
- LY_CHECK_GOTO(ret = lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext), cleanup);
+ r = lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
/* store for ext instance node validation, if needed */
- LY_CHECK_GOTO(ret = lyd_validate_node_ext(*node, &lydctx->ext_node), cleanup);
+ r = lyd_validate_node_ext(*node, &lydctx->ext_node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
- } else if (ret == LY_ENOT) {
+ } else if (r == LY_ENOT) {
/* parse it again as an opaq node */
- LY_CHECK_GOTO(ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status,
- first_p, node), cleanup);
+ r = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status, first_p, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (snode->nodetype == LYS_LIST) {
((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LIST;
@@ -1481,12 +1543,13 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str
}
} else {
/* error */
+ rc = r;
goto cleanup;
}
cleanup:
LOG_LOCBACK(1, 0, 0, 0);
- return ret;
+ return rc;
}
/**
@@ -1501,36 +1564,80 @@ cleanup:
static LY_ERR
lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
{
- LY_ERR ret = LY_SUCCESS, r;
- enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ LY_ERR r, rc = LY_SUCCESS;
+ enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx);
const char *name, *prefix = NULL, *expected = NULL;
size_t name_len, prefix_len = 0;
ly_bool is_meta = 0, parse_subtree;
const struct lysc_node *snode = NULL;
- struct lysc_ext_instance *ext;
+ struct lysc_ext_instance *ext = NULL;
struct lyd_node *node = NULL, *attr_node = NULL;
const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
char *value = NULL;
assert(parent || first_p);
- assert(status == LYJSON_OBJECT);
+ assert((status == LYJSON_OBJECT) || (status == LYJSON_OBJECT_NEXT));
parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
/* all descendants should be parsed */
lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ if (status == LYJSON_OBJECT_CLOSED) {
+ /* empty object, fine... */
+ goto cleanup;
+ }
+
/* process the node name */
+ assert(status == LYJSON_OBJECT_NAME);
lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_meta);
lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &value);
- if (!is_meta || name_len || prefix_len) {
+ if ((lydctx->int_opts & LYD_INTOPT_EVENTTIME) && !parent && !is_meta && name_len && !prefix_len &&
+ !ly_strncmp("eventTime", name, name_len)) {
+ /* parse eventTime */
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ if (status != LYJSON_STRING) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_STRING),
+ lyjson_token2str(status));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* create node */
+ r = lyd_create_opaq(lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, prefix, prefix_len,
+ lydctx->jsonctx->value, lydctx->jsonctx->value_len, NULL, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, &node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* validate the value */
+ r = lyd_parser_notif_eventtime_validate(node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ goto node_parsed;
+ } else if (!is_meta || name_len || prefix_len) {
/* get the schema node */
r = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode, &ext);
if (r == LY_ENOT) {
/* data parsed */
goto cleanup;
+ } else if ((r == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) {
+ rc = r;
+
+ /* skip the invalid data */
+ if ((r = lydjson_data_skip(lydctx->jsonctx))) {
+ rc = r;
+ }
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ goto cleanup;
+ } else if (r) {
+ /* error */
+ rc = r;
+ goto cleanup;
}
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
if (!snode) {
/* we will not be parsing it as metadata */
@@ -1540,39 +1647,44 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
if (is_meta) {
/* parse as metadata */
- if (!name_len && !prefix_len) {
+ if (!name_len && !prefix_len && !parent) {
+ LOGVAL(ctx, LYVE_SYNTAX_JSON,
+ "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ } else if (!name_len && !prefix_len) {
/* parent's metadata without a name - use the schema from the parent */
- if (!parent) {
- LOGVAL(ctx, LYVE_SYNTAX_JSON,
- "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries.");
- ret = LY_EVALID;
- goto cleanup;
- }
attr_node = parent;
snode = attr_node->schema;
}
- ret = lydjson_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, &status,
+ r = lydjson_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, &status,
first_p, &node);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
} else if (!snode) {
- /* parse as an opaq node */
- assert((lydctx->parse_opts & LYD_PARSE_OPAQ) || (lydctx->int_opts));
+ if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+ /* skip element with children */
+ r = lydjson_data_skip(lydctx->jsonctx);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ } else {
+ /* parse as an opaq node */
- /* opaq node cannot have an empty string as the name. */
- if (name_len == 0) {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "A JSON object member name cannot be a zero-length string.");
- ret = LY_EVALID;
- goto cleanup;
- }
+ /* opaq node cannot have an empty string as the name. */
+ if (name_len == 0) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "JSON object member name cannot be a zero-length string.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
- /* move to the second item in the name/X pair and parse opaq */
- ret = lydjson_ctx_next_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, &status, first_p, &node);
- LY_CHECK_GOTO(ret, cleanup);
+ /* move to the second item in the name/X pair and parse opaq */
+ r = lydjson_ctx_next_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, &status, first_p, &node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
} else {
/* parse as a standard lyd_node but it can still turn out to be an opaque node */
/* move to the second item in the name/X pair */
- LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
/* set expected representation */
switch (snode->nodetype) {
@@ -1609,30 +1721,31 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
switch (snode->nodetype) {
case LYS_LEAFLIST:
case LYS_LIST:
- if (status == LYJSON_ARRAY_EMPTY) {
- /* no instances, skip */
- break;
- }
LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
- /* move into array */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
-
/* process all the values/objects */
do {
- ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
+ /* move into array/next value */
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ if (status == LYJSON_ARRAY_CLOSED) {
+ /* empty array, fine... */
+ break;
+ }
+
+ r = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
&status, &node);
- if (ret == LY_ENOT) {
+ if (r == LY_ENOT) {
goto representation_error;
- } else if (ret) {
- goto cleanup;
}
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
/* move after the item(s) */
- LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
- } while (status != LYJSON_ARRAY_CLOSED);
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ } while (status == LYJSON_ARRAY_NEXT);
break;
case LYS_LEAF:
@@ -1643,13 +1756,12 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
case LYS_ANYDATA:
case LYS_ANYXML:
/* process the value/object */
- ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
+ r = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
&status, &node);
- if (ret == LY_ENOT) {
+ if (r == LY_ENOT) {
goto representation_error;
- } else if (ret) {
- goto cleanup;
}
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
/* rememeber the RPC/action/notification */
@@ -1659,31 +1771,39 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
}
}
- /* finally connect the parsed node */
- lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
-
+node_parsed:
/* rememeber a successfully parsed node */
if (parsed && node) {
ly_set_add(parsed, node, 1, NULL);
}
+ /* finally connect the parsed node, is zeroed */
+ lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
+
if (!parse_subtree) {
/* move after the item(s) */
- LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
/* success */
goto cleanup;
representation_error:
- LOGVAL(ctx, LYVE_SYNTAX_JSON, "The %s \"%s\" is expected to be represented as JSON %s, but input data contains name/%s.",
- lys_nodetype2str(snode->nodetype), snode->name, expected, lyjson_token2str(status));
- ret = LY_EVALID;
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as name/%s.",
+ expected, lys_nodetype2str(snode->nodetype), snode->name, lyjson_token2str(status));
+ rc = LY_EVALID;
+ if (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) {
+ /* try to skip the invalid data */
+ if ((r = lydjson_data_skip(lydctx->jsonctx))) {
+ rc = r;
+ }
+ }
cleanup:
free(value);
lyd_free_tree(node);
- return ret;
+ return rc;
}
/**
@@ -1694,20 +1814,17 @@ cleanup:
* @param[in] parse_opts Options for parser, see @ref dataparseroptions.
* @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
* @param[out] lydctx_p Data parser context to finish validation.
- * @param[out] status Storage for the current context's status
* @return LY_ERR value.
*/
static LY_ERR
lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts,
- struct lyd_json_ctx **lydctx_p, enum LYJSON_PARSER_STATUS *status)
+ struct lyd_json_ctx **lydctx_p)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_json_ctx *lydctx;
- size_t i;
- ly_bool subtree;
+ enum LYJSON_PARSER_STATUS status;
assert(lydctx_p);
- assert(status);
/* init context */
lydctx = calloc(1, sizeof *lydctx);
@@ -1716,28 +1833,20 @@ lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_o
lydctx->val_opts = val_opts;
lydctx->free = lyd_json_ctx_free;
- /* starting top-level */
- for (i = 0; in->current[i] != '\0' && is_jsonws(in->current[i]); i++) {
- if (in->current[i] == '\n') {
- /* new line */
- LY_IN_NEW_LINE(in);
- }
- }
-
- subtree = (parse_opts & LYD_PARSE_SUBTREE) ? 1 : 0;
- LY_CHECK_ERR_RET(ret = lyjson_ctx_new(ctx, in, subtree, &lydctx->jsonctx), free(lydctx), ret);
- *status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ LY_CHECK_ERR_RET(ret = lyjson_ctx_new(ctx, in, &lydctx->jsonctx), free(lydctx), ret);
+ status = lyjson_ctx_status(lydctx->jsonctx);
- if ((*status == LYJSON_END) || (*status == LYJSON_OBJECT_EMPTY) || (*status == LYJSON_OBJECT)) {
- *lydctx_p = lydctx;
- return LY_SUCCESS;
- } else {
+ /* parse_opts & LYD_PARSE_SUBTREE not implemented */
+ if (status != LYJSON_OBJECT) {
/* expecting top-level object */
- LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.", lyjson_token2str(*status));
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.", lyjson_token2str(status));
*lydctx_p = NULL;
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
return LY_EVALID;
}
+
+ *lydctx_p = lydctx;
+ return LY_SUCCESS;
}
LY_ERR
@@ -1745,14 +1854,12 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
{
- LY_ERR rc = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
struct lyd_json_ctx *lydctx = NULL;
enum LYJSON_PARSER_STATUS status;
- rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx, &status);
- LY_CHECK_GOTO(rc || status == LYJSON_END || status == LYJSON_OBJECT_EMPTY, cleanup);
-
- assert(status == LYJSON_OBJECT);
+ rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
+ LY_CHECK_GOTO(rc, cleanup);
lydctx->int_opts = int_opts;
lydctx->ext = ext;
@@ -1761,37 +1868,36 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
/* read subtree(s) */
- while (lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) {
- rc = lydjson_subtree_r(lydctx, parent, first_p, parsed);
- LY_CHECK_GOTO(rc, cleanup);
+ do {
+ r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
- status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ status = lyjson_ctx_status(lydctx->jsonctx);
if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
break;
}
- }
+ } while (status == LYJSON_OBJECT_NEXT);
- if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] &&
- (lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_OBJECT_CLOSED)) {
+ if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) {
LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
- rc = LY_EVALID;
- goto cleanup;
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
- rc = LY_EVALID;
- goto cleanup;
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
/* finish linking metadata */
- rc = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
- LY_CHECK_GOTO(rc, cleanup);
+ r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (parse_opts & LYD_PARSE_SUBTREE) {
/* check for a sibling object */
assert(subtree_sibling);
- if (lydctx->jsonctx->in->current[0] == ',') {
+ if (status == LYJSON_OBJECT_NEXT) {
*subtree_sibling = 1;
/* move to the next object */
@@ -1806,6 +1912,198 @@ cleanup:
assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count &&
!lydctx->node_when.count));
+ if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
+ lyd_json_ctx_free((struct lyd_ctx *)lydctx);
+ } else {
+ *lydctx_p = (struct lyd_ctx *)lydctx;
+
+ /* the JSON context is no more needed, freeing it also stops logging line numbers which would be confusing now */
+ lyjson_ctx_free(lydctx->jsonctx);
+ lydctx->jsonctx = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse a specific JSON object into an opaque node.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @param[in] name Name of the object.
+ * @param[in] module Module name of the object, NULL if none expected.
+ * @param[out] evnp Parsed envelope (opaque node).
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the specified object did not match.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *module, struct lyd_node **envp)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
+ const char *nam, *prefix;
+ size_t nam_len, prefix_len;
+ ly_bool is_meta;
+
+ assert(status == LYJSON_OBJECT);
+
+ *envp = NULL;
+
+ r = lyjson_ctx_next(jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ if (status == LYJSON_OBJECT_CLOSED) {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON object.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* process the name */
+ assert(status == LYJSON_OBJECT_NAME);
+ lydjson_parse_name(jsonctx->value, jsonctx->value_len, &nam, &nam_len, &prefix, &prefix_len, &is_meta);
+ if (is_meta) {
+ LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected metadata.");
+ rc = LY_EVALID;
+ goto cleanup;
+ } else if (module && ly_strncmp(module, prefix, prefix_len)) {
+ LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected module \"%.*s\" instead of \"%s\".", (int)prefix_len, prefix, module);
+ rc = LY_EVALID;
+ goto cleanup;
+ } else if (ly_strncmp(name, nam, nam_len)) {
+ LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected object \"%.*s\" instead of \"%s\".", (int)nam_len, nam, name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ r = lyjson_ctx_next(jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* create node */
+ rc = lyd_create_opaq(jsonctx->ctx, name, strlen(name), prefix, prefix_len, prefix, prefix_len, NULL, 0, NULL,
+ LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+LY_ERR
+lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type,
+ struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ struct lyd_json_ctx *lydctx = NULL;
+ struct lyd_node *node;
+ uint32_t i, int_opts = 0, close_elem = 0;
+
+ assert(ctx && in && lydctx_p);
+ assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
+ assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
+
+ assert((data_type == LYD_TYPE_RPC_RESTCONF) || (data_type == LYD_TYPE_NOTIF_RESTCONF) ||
+ (data_type == LYD_TYPE_REPLY_RESTCONF));
+ assert(!(parse_opts & LYD_PARSE_SUBTREE));
+
+ /* init context */
+ rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
+ LY_CHECK_GOTO(rc, cleanup);
+ lydctx->ext = ext;
+
+ switch (data_type) {
+ case LYD_TYPE_RPC_RESTCONF:
+ assert(parent);
+
+ /* parse "input" */
+ rc = lydjson_envelope(lydctx->jsonctx, "input", lyd_node_module(parent)->name, envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
+ close_elem = 1;
+ break;
+ case LYD_TYPE_NOTIF_RESTCONF:
+ assert(!parent);
+
+ /* parse "notification" */
+ rc = lydjson_envelope(lydctx->jsonctx, "notification", "ietf-restconf", envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* RESTCONF notification and eventTime */
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME;
+ close_elem = 1;
+ break;
+ case LYD_TYPE_REPLY_RESTCONF:
+ assert(parent);
+
+ /* parse "output" */
+ rc = lydjson_envelope(lydctx->jsonctx, "output", lyd_node_module(parent)->name, envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
+ close_elem = 1;
+ break;
+ default:
+ LOGINT(ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+
+ lydctx->int_opts = int_opts;
+
+ /* find the operation node if it exists already */
+ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+
+ /* read subtree(s) */
+ do {
+ r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ } while (lyjson_ctx_status(lydctx->jsonctx) == LYJSON_OBJECT_NEXT);
+
+ /* close all opened elements */
+ for (i = 0; i < close_elem; ++i) {
+ if (lyjson_ctx_status(lydctx->jsonctx) != LYJSON_OBJECT_CLOSED) {
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_OBJECT_CLOSED),
+ lyjson_token2str(lyjson_ctx_status(lydctx->jsonctx)));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ r = lyjson_ctx_next(lydctx->jsonctx, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
+
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
+ LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+ if (int_opts & LYD_INTOPT_EVENTTIME) {
+ /* parse as a child of the envelope */
+ node = (*first_p)->prev;
+ if (node->schema) {
+ LOGVAL(ctx, LYVE_DATA, "Missing notification \"eventTime\" node.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ } else {
+ /* can be the only opaque node and an operation had to be parsed */
+ assert(!strcmp(LYD_NAME(node), "eventTime") && (*first_p)->next);
+ lyd_unlink(node);
+ assert(*envp);
+ lyd_insert_child(*envp, node);
+ }
+ }
+
+ /* finish linking metadata */
+ r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+cleanup:
+ /* there should be no unres stored if validation should be skipped */
+ assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count &&
+ !lydctx->node_when.count));
+
if (rc) {
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
} else {
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index f898085..788be94 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -49,11 +49,15 @@ lylyb_ctx_free(struct lylyb_ctx *ctx)
{
LY_ARRAY_COUNT_TYPE u;
+ if (!ctx) {
+ return;
+ }
+
LY_ARRAY_FREE(ctx->siblings);
LY_ARRAY_FREE(ctx->models);
LY_ARRAY_FOR(ctx->sib_hts, u) {
- lyht_free(ctx->sib_hts[u].ht);
+ lyht_free(ctx->sib_hts[u].ht, NULL);
}
LY_ARRAY_FREE(ctx->sib_hts);
@@ -65,6 +69,10 @@ lyd_lyb_ctx_free(struct lyd_ctx *lydctx)
{
struct lyd_lyb_ctx *ctx = (struct lyd_lyb_ctx *)lydctx;
+ if (!lydctx) {
+ return;
+ }
+
lyd_ctx_free(lydctx);
lylyb_ctx_free(ctx->lybctx);
free(ctx);
@@ -1082,7 +1090,7 @@ lyb_validate_node_inner(struct lyd_lyb_ctx *lybctx, const struct lysc_node *snod
if (!(lybctx->parse_opts & LYD_PARSE_ONLY)) {
/* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, NULL);
+ ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, 0, NULL);
LY_CHECK_RET(ret);
/* add any missing default children */
@@ -1160,9 +1168,12 @@ lyb_parse_node_opaq(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct
/* create node */
ret = lyd_create_opaq(ctx, name, strlen(name), prefix, ly_strlen(prefix), module_key, ly_strlen(module_key),
- value, strlen(value), &dynamic, format, val_prefix_data, 0, &node);
+ value, strlen(value), &dynamic, format, val_prefix_data, LYD_HINT_DATA, &node);
LY_CHECK_GOTO(ret, cleanup);
+ assert(node);
+ LOG_LOCSET(NULL, node, NULL, NULL);
+
/* process children */
ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
LY_CHECK_GOTO(ret, cleanup);
@@ -1170,8 +1181,12 @@ lyb_parse_node_opaq(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct
/* register parsed opaq node */
lyb_finish_opaq(lybctx, parent, flags, &attr, &node, first_p, parsed);
assert(!attr && !node);
+ LOG_LOCBACK(0, 1, 0, 0);
cleanup:
+ if (node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
free(prefix);
free(module_key);
free(name);
@@ -1257,9 +1272,13 @@ lyb_parse_node_any(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const st
goto error;
}
+ assert(node);
+ LOG_LOCSET(NULL, node, NULL, NULL);
+
/* register parsed anydata node */
lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+ LOG_LOCBACK(0, 1, 0, 0);
return LY_SUCCESS;
error:
@@ -1296,6 +1315,9 @@ lyb_parse_node_inner(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const
ret = lyd_create_inner(snode, &node);
LY_CHECK_GOTO(ret, error);
+ assert(node);
+ LOG_LOCSET(NULL, node, NULL, NULL);
+
/* process children */
ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
LY_CHECK_GOTO(ret, error);
@@ -1312,9 +1334,13 @@ lyb_parse_node_inner(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const
/* register parsed node */
lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+ LOG_LOCBACK(0, 1, 0, 0);
return LY_SUCCESS;
error:
+ if (node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
lyd_free_meta_siblings(meta);
lyd_free_tree(node);
return ret;
@@ -1347,8 +1373,12 @@ lyb_parse_node_leaf(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
ret = lyb_create_term(lybctx, snode, &node);
LY_CHECK_GOTO(ret, error);
+ assert(node);
+ LOG_LOCSET(NULL, node, NULL, NULL);
+
lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+ LOG_LOCBACK(0, 1, 0, 0);
return LY_SUCCESS;
error:
@@ -1408,6 +1438,7 @@ lyb_parse_node_list(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
struct lyd_node *node = NULL;
struct lyd_meta *meta = NULL;
uint32_t flags;
+ ly_bool log_node = 0;
/* register a new sibling */
ret = lyb_read_start_siblings(lybctx->lybctx);
@@ -1422,6 +1453,10 @@ lyb_parse_node_list(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
ret = lyd_create_inner(snode, &node);
LY_CHECK_GOTO(ret, error);
+ assert(node);
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ log_node = 1;
+
/* process children */
ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
LY_CHECK_GOTO(ret, error);
@@ -1437,6 +1472,9 @@ lyb_parse_node_list(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
/* register parsed list node */
lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+
+ LOG_LOCBACK(0, 1, 0, 0);
+ log_node = 0;
}
/* end the sibling */
@@ -1446,6 +1484,9 @@ lyb_parse_node_list(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
return LY_SUCCESS;
error:
+ if (log_node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
lyd_free_meta_siblings(meta);
lyd_free_tree(node);
return ret;
@@ -1484,7 +1525,7 @@ lyb_parse_node(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct lyd_n
case LYB_NODE_CHILD:
case LYB_NODE_OPAQ:
/* read hash, find the schema node starting from parent schema, if any */
- LY_CHECK_GOTO(ret = lyb_parse_schema_hash(lybctx, parent ? parent->schema : NULL, NULL, &snode), cleanup);
+ LY_CHECK_GOTO(ret = lyb_parse_schema_hash(lybctx, lyd_parser_node_schema(parent), NULL, &snode), cleanup);
break;
case LYB_NODE_EXT:
/* ext, read module name */
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 5a929da..5d97c8e 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -40,6 +40,9 @@
#include "validation.h"
#include "xml.h"
+static LY_ERR lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p,
+ struct ly_set *parsed);
+
void
lyd_xml_ctx_free(struct lyd_ctx *lydctx)
{
@@ -72,6 +75,8 @@ lydxml_metadata(struct lyd_xml_ctx *lydctx, const struct lysc_node *sparent, str
*meta = NULL;
+ LOG_LOCSET(sparent, NULL, NULL, NULL);
+
/* check for NETCONF filter unqualified attributes */
if (!strcmp(sparent->module->name, "notifications")) {
/* ancient module that does not even use the extension */
@@ -160,6 +165,7 @@ create_meta:
}
cleanup:
+ LOG_LOCBACK(1, 0, 0, 0);
if (ret) {
lyd_free_meta_siblings(*meta);
*meta = NULL;
@@ -283,7 +289,7 @@ lydxml_check_list(struct lyxml_ctx *xmlctx, const struct lysc_node *list)
assert(xmlctx->status == LYXML_ELEM_CONTENT);
if (i < key_set.count) {
/* validate the value */
- r = lys_value_validate(NULL, snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns);
+ r = ly_value_validate(NULL, snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA);
if (!r) {
/* key with a valid value, remove from the set */
ly_set_rm_index(&key_set, i, NULL);
@@ -389,8 +395,8 @@ lydxml_data_check_opaq(struct lyd_xml_ctx *lydctx, const struct lysc_node **snod
if ((*snode)->nodetype & LYD_NODE_TERM) {
/* value may not be valid in which case we parse it as an opaque node */
- if (lys_value_validate(NULL, *snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns)) {
- LOGVRB("Parsing opaque term node \"%s\" with invalid value \"%.*s\".", (*snode)->name, xmlctx->value_len,
+ if (ly_value_validate(NULL, *snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA)) {
+ LOGVRB("Parsing opaque term node \"%s\" with invalid value \"%.*s\".", (*snode)->name, (int)xmlctx->value_len,
xmlctx->value);
*snode = NULL;
}
@@ -430,7 +436,7 @@ restore:
* @param[out] anchor Anchor to insert after in case of a list.
*/
static void
-lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size_t value_len, struct lyd_node *first,
+lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size_t value_len, const struct lyd_node *first,
const char *ns, uint32_t *hints, struct lyd_node **anchor)
{
struct lyd_node_opaq *opaq;
@@ -441,8 +447,8 @@ lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size
*anchor = NULL;
if (!value_len) {
- /* no value */
- *hints |= LYD_VALHINT_EMPTY;
+ /* no value but it may also be zero-length string */
+ *hints |= LYD_VALHINT_EMPTY | LYD_VALHINT_STRING;
} else if (!strncmp(value, "true", value_len) || !strncmp(value, "false", value_len)) {
/* boolean value */
*hints |= LYD_VALHINT_BOOLEAN;
@@ -485,7 +491,7 @@ lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size
opaq->hints |= LYD_NODEHINT_LIST;
*hints |= LYD_NODEHINT_LIST;
}
- *anchor = first;
+ *anchor = (struct lyd_node *)first;
break;
}
} while (first->prev->next);
@@ -506,7 +512,7 @@ lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size
* @return LY_ERR on error.
*/
static LY_ERR
-lydxml_subtree_snode(struct lyd_xml_ctx *lydctx, const struct lyd_node *parent, const char *prefix, size_t prefix_len,
+lydxml_subtree_get_snode(struct lyd_xml_ctx *lydctx, const struct lyd_node *parent, const char *prefix, size_t prefix_len,
const char *name, size_t name_len, const struct lysc_node **snode, struct lysc_ext_instance **ext)
{
LY_ERR r;
@@ -551,338 +557,569 @@ lydxml_subtree_snode(struct lyd_xml_ctx *lydctx, const struct lyd_node *parent,
unknown_module:
if (lydctx->parse_opts & LYD_PARSE_STRICT) {
- LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.", ns->uri);
+ if (ns) {
+ LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.", ns->uri);
+ } else if (prefix_len) {
+ LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%.*s\" in the context.", (int)prefix_len, prefix);
+ } else {
+ LOGVAL(ctx, LYVE_REFERENCE, "No default namespace in the context.");
+ }
return LY_EVALID;
}
return LY_SUCCESS;
}
/* get the schema node */
- if (mod) {
- if (!parent && lydctx->ext) {
- *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
- } else {
- *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
- }
- if (!*snode) {
- /* check for extension data */
- r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name,
- name_len, snode, ext);
- if (r != LY_ENOT) {
- /* success or error */
- return r;
- }
+ if (!parent && lydctx->ext) {
+ *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
+ } else {
+ /* try to find parent schema node even if it is an opaque node (not connected to the parent) */
+ *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts);
+ }
+ if (!*snode) {
+ /* check for extension data */
+ r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name,
+ name_len, snode, ext);
+ if (r != LY_ENOT) {
+ /* success or error */
+ return r;
+ }
- /* unknown data node */
- if (lydctx->parse_opts & LYD_PARSE_STRICT) {
- if (parent) {
- LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.",
- (int)name_len, name, LYD_NAME(parent));
- } else if (lydctx->ext) {
- if (lydctx->ext->argument) {
- LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
- (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
- } else {
- LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
- (int)name_len, name, lydctx->ext->def->name);
- }
+ /* unknown data node */
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ if (parent) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.",
+ (int)name_len, name, LYD_NAME(parent));
+ } else if (lydctx->ext) {
+ if (lydctx->ext->argument) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
+ (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
} else {
- LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
- (int)name_len, name, mod->name);
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
+ (int)name_len, name, lydctx->ext->def->name);
}
- return LY_EVALID;
+ } else {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
+ (int)name_len, name, mod->name);
}
- return LY_SUCCESS;
- } else {
- /* check that schema node is valid and can be used */
- LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode));
- LY_CHECK_RET(lydxml_data_check_opaq(lydctx, snode));
+ return LY_EVALID;
}
+ return LY_SUCCESS;
+ } else {
+ /* check that schema node is valid and can be used */
+ LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode));
+ LY_CHECK_RET(lydxml_data_check_opaq(lydctx, snode));
}
return LY_SUCCESS;
}
/**
- * @brief Parse XML subtree.
+ * @brief Parse an XML opque node.
*
* @param[in] lydctx XML YANG data parser context.
- * @param[in,out] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
- * @param[in,out] first_p Pointer to the first (@p parent or top-level) child. In case there were already some siblings,
- * this may point to a previously existing node.
- * @param[in,out] parsed Optional set to add all the parsed siblings into.
+ * @param[in] sibling Existing sibling node, if any.
+ * @param[in] prefix Parsed node prefix.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] name Parsed node name.
+ * @param[in] name_len Length of @p name.
+ * @param[out] insert_anchor Optional anchor node for inserting this node.
+ * @param[out] node Created node.
* @return LY_ERR value.
*/
static LY_ERR
-lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
+lydxml_subtree_opaq(struct lyd_xml_ctx *lydctx, const struct lyd_node *sibling, const char *prefix, uint32_t prefix_len,
+ const char *name, uint32_t name_len, struct lyd_node **insert_anchor, struct lyd_node **node)
{
- LY_ERR ret = LY_SUCCESS;
- const char *prefix, *name, *ns_uri;
- size_t prefix_len, name_len;
- struct lyxml_ctx *xmlctx;
- const struct ly_ctx *ctx;
+ LY_ERR rc = LY_SUCCESS;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+ struct lyd_node_opaq *opaq;
+ const char *ns_uri, *value = NULL;
+ size_t value_len;
+ ly_bool ws_only, dynamic = 0;
const struct lyxml_ns *ns;
- struct lyd_meta *meta = NULL;
- struct lyd_attr *attr = NULL;
- const struct lysc_node *snode;
- struct lysc_ext_instance *ext;
- uint32_t prev_parse_opts, orig_parse_opts, prev_int_opts, hints;
- struct lyd_node *node = NULL, *anchor, *insert_anchor = NULL;
+ uint32_t hints;
void *val_prefix_data = NULL;
LY_VALUE_FORMAT format;
- ly_bool parse_subtree;
- char *val;
- assert(parent || first_p);
+ assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
- xmlctx = lydctx->xmlctx;
- ctx = xmlctx->ctx;
+ *node = NULL;
- parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
- /* all descendants should be parsed */
- lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
- orig_parse_opts = lydctx->parse_opts;
+ /* remember the value */
+ value = xmlctx->value;
+ value_len = xmlctx->value_len;
+ ws_only = xmlctx->ws_only;
+ dynamic = xmlctx->dynamic;
+ if (dynamic) {
+ xmlctx->dynamic = 0;
+ }
- assert(xmlctx->status == LYXML_ELEMENT);
+ /* get value prefixes, if any */
+ rc = ly_store_prefix_data(xmlctx->ctx, value, value_len, LY_VALUE_XML, &xmlctx->ns, &format, &val_prefix_data);
+ LY_CHECK_GOTO(rc, cleanup);
- /* remember element prefix and name */
- prefix = xmlctx->prefix;
- prefix_len = xmlctx->prefix_len;
- name = xmlctx->name;
- name_len = xmlctx->name_len;
+ /* get NS again, it may have been backed up and restored */
+ ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
+ ns_uri = ns ? ns->uri : NULL;
- /* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ /* get best-effort node hints */
+ lydxml_get_hints_opaq(name, name_len, xmlctx->value, xmlctx->value_len, sibling, ns_uri, &hints, insert_anchor);
- /* get the schema node */
- LY_CHECK_GOTO(ret = lydxml_subtree_snode(lydctx, parent, prefix, prefix_len, name, name_len, &snode, &ext), error);
+ /* create the node without value */
+ rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len, ns_uri, ns_uri ? strlen(ns_uri) : 0, NULL, 0,
+ NULL, format, NULL, hints, node);
+ LY_CHECK_GOTO(rc, cleanup);
- if (!snode && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
- LOGVRB("Skipping parsing of unknown node \"%.*s\".", name_len, name);
+ assert(*node);
+ LOG_LOCSET(NULL, *node, NULL, NULL);
- /* skip element with children */
- LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), error);
- return LY_SUCCESS;
+ /* parser next */
+ rc = lyxml_ctx_next(xmlctx);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* process children */
+ while (xmlctx->status == LYXML_ELEMENT) {
+ rc = lydxml_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
+ LY_CHECK_GOTO(rc, cleanup);
}
- /* create metadata/attributes */
- if (xmlctx->status == LYXML_ATTRIBUTE) {
- if (snode) {
- ret = lydxml_metadata(lydctx, snode, &meta);
- LY_CHECK_GOTO(ret, error);
+ /* update the value */
+ opaq = (struct lyd_node_opaq *)*node;
+ if (opaq->child) {
+ if (!ws_only) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX_XML, "Mixed XML content node \"%s\" found, not supported.", LYD_NAME(opaq));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ } else if (value_len) {
+ lydict_remove(xmlctx->ctx, opaq->value);
+ if (dynamic) {
+ LY_CHECK_GOTO(rc = lydict_insert_zc(xmlctx->ctx, (char *)value, &opaq->value), cleanup);
+ dynamic = 0;
} else {
- assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
- ret = lydxml_attrs(xmlctx, &attr);
- LY_CHECK_GOTO(ret, error);
+ LY_CHECK_GOTO(rc = lydict_insert(xmlctx->ctx, value, value_len, &opaq->value), cleanup);
}
}
- assert(xmlctx->status == LYXML_ELEM_CONTENT);
- if (!snode) {
- assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
+ /* always store val_prefix_data because the format requires them */
+ assert(!opaq->val_prefix_data);
+ opaq->val_prefix_data = val_prefix_data;
+ val_prefix_data = NULL;
- if (xmlctx->ws_only) {
- /* ignore WS-only value */
- if (xmlctx->dynamic) {
- free((char *)xmlctx->value);
- }
- xmlctx->dynamic = 0;
- xmlctx->value = "";
- xmlctx->value_len = 0;
- format = LY_VALUE_XML;
- } else {
- /* get value prefixes */
- ret = ly_store_prefix_data(xmlctx->ctx, xmlctx->value, xmlctx->value_len, LY_VALUE_XML,
- &xmlctx->ns, &format, &val_prefix_data);
- LY_CHECK_GOTO(ret, error);
- }
+cleanup:
+ if (*node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ ly_free_prefix_data(format, val_prefix_data);
+ if (dynamic) {
+ free((char *)value);
+ }
+ if (rc) {
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+ return rc;
+}
- /* get NS again, it may have been backed up and restored */
- ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
- ns_uri = ns ? ns->uri : NULL;
+/**
+ * @brief Parse an XML leaf/leaf-list node.
+ *
+ * @param[in] lydctx XML YANG data parser context.
+ * @param[in] parent Parent node, if any.
+ * @param[in] snode Schema node of the new node.
+ * @param[out] node Created node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_subtree_term(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, const struct lysc_node *snode,
+ struct lyd_node **node)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+ struct lyd_node *anchor;
- /* get best-effort node hints */
- lydxml_get_hints_opaq(name, name_len, xmlctx->value, xmlctx->value_len, parent ? lyd_child(parent) : *first_p,
- ns_uri, &hints, &insert_anchor);
+ *node = NULL;
- /* create node */
- ret = lyd_create_opaq(ctx, name, name_len, prefix, prefix_len, ns_uri, ns_uri ? strlen(ns_uri) : 0,
- xmlctx->value, xmlctx->value_len, &xmlctx->dynamic, format, val_prefix_data, hints, &node);
- LY_CHECK_GOTO(ret, error);
+ /* create node */
+ r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic,
+ LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA, node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
- /* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ if (*node) {
+ LOG_LOCSET(NULL, *node, NULL, NULL);
+ }
- /* process children */
- while (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_subtree_r(lydctx, node, lyd_node_child_p(node), NULL);
- LY_CHECK_GOTO(ret, error);
- }
- } else if (snode->nodetype & LYD_NODE_TERM) {
- /* create node */
- LY_CHECK_GOTO(ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, xmlctx->value, xmlctx->value_len,
- &xmlctx->dynamic, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA, &node), error);
- LOG_LOCSET(snode, node, NULL, NULL);
-
- if (parent && (node->schema->flags & LYS_KEY)) {
- /* check the key order, the anchor must never be a key */
- anchor = lyd_insert_get_next_anchor(lyd_child(parent), node);
- if (anchor && anchor->schema && (anchor->schema->flags & LYS_KEY)) {
- if (lydctx->parse_opts & LYD_PARSE_STRICT) {
- LOGVAL(ctx, LYVE_DATA, "Invalid position of the key \"%s\" in a list.", node->schema->name);
- ret = LY_EVALID;
- goto error;
- } else {
- LOGWRN(ctx, "Invalid position of the key \"%s\" in a list.", node->schema->name);
- }
+ if (*node && parent && (snode->flags & LYS_KEY)) {
+ /* check the key order, the anchor must never be a key */
+ anchor = lyd_insert_get_next_anchor(lyd_child(parent), *node);
+ if (anchor && anchor->schema && (anchor->schema->flags & LYS_KEY)) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ LOGVAL(xmlctx->ctx, LYVE_DATA, "Invalid position of the key \"%s\" in a list.", snode->name);
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ } else {
+ LOGWRN(xmlctx->ctx, "Invalid position of the key \"%s\" in a list.", snode->name);
}
}
+ }
- /* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ /* parser next */
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- /* no children expected */
- if (xmlctx->status == LYXML_ELEMENT) {
- LOGVAL(ctx, LYVE_SYNTAX, "Child element \"%.*s\" inside a terminal node \"%s\" found.",
- (int)xmlctx->name_len, xmlctx->name, snode->name);
- ret = LY_EVALID;
- goto error;
- }
- } else if (snode->nodetype & LYD_NODE_INNER) {
- if (!xmlctx->ws_only) {
- /* value in inner node */
- LOGVAL(ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an inner node \"%s\" found.",
- (int)xmlctx->value_len, xmlctx->value, snode->name);
- ret = LY_EVALID;
- goto error;
- }
+ /* no children expected */
+ if (xmlctx->status == LYXML_ELEMENT) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Child element \"%.*s\" inside a terminal node \"%s\" found.",
+ (int)xmlctx->name_len, xmlctx->name, snode->name);
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+
+cleanup:
+ if (*node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse an XML inner node.
+ *
+ * @param[in] lydctx XML YANG data parser context.
+ * @param[in] snode Schema node of the new node.
+ * @param[in] ext Extension instance of @p snode, if any.
+ * @param[out] node Created node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_subtree_inner(struct lyd_xml_ctx *lydctx, const struct lysc_node *snode, const struct lysc_ext_instance *ext,
+ struct lyd_node **node)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+ uint32_t prev_parse_opts = lydctx->parse_opts;
+
+ *node = NULL;
+
+ if (!xmlctx->ws_only) {
+ /* value in inner node */
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an inner node \"%s\" found.",
+ (int)xmlctx->value_len, xmlctx->value, snode->name);
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+
+ /* create node */
+ rc = lyd_create_inner(snode, node);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ assert(*node);
+ LOG_LOCSET(NULL, *node, NULL, NULL);
+
+ /* parser next */
+ rc = lyxml_ctx_next(xmlctx);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if (ext) {
+ /* only parse these extension data and validate afterwards */
+ lydctx->parse_opts |= LYD_PARSE_ONLY;
+ }
+
+ /* process children */
+ while (xmlctx->status == LYXML_ELEMENT) {
+ r = lydxml_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+
+ /* restore options */
+ lydctx->parse_opts = prev_parse_opts;
+
+ if (snode->nodetype == LYS_LIST) {
+ /* check all keys exist */
+ r = lyd_parse_check_keys(*node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
+
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) {
+ /* new node validation, autodelete CANNOT occur (it can if multi-error), all nodes are new */
+ r = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, lydctx->val_opts, NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
+ /* add any missing default children */
+ r = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
+ &lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
+
+ if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+ /* rememeber the RPC/action/notification */
+ lydctx->op_node = *node;
+ }
+
+cleanup:
+ if (*node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ lydctx->parse_opts = prev_parse_opts;
+ if (rc && ((*node && !(*node)->hash) || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
+ /* list without keys is unusable or an error */
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse an XML anyxml/anydata node.
+ *
+ * @param[in] lydctx XML YANG data parser context.
+ * @param[in] snode Schema node of the new node.
+ * @param[in] ext Extension instance of @p snode, if any.
+ * @param[out] node Created node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_subtree_any(struct lyd_xml_ctx *lydctx, const struct lysc_node *snode, const struct lysc_ext_instance *ext,
+ struct lyd_node **node)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+ uint32_t prev_parse_opts = lydctx->parse_opts, prev_int_opts = lydctx->int_opts;
+ struct lyd_node *child = NULL;
+ char *val = NULL;
+ ly_bool log_node = 0;
+
+ *node = NULL;
+ if ((snode->nodetype == LYS_ANYDATA) && !xmlctx->ws_only) {
+ /* value in anydata node, we expect a tree */
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an anydata node \"%s\" found.",
+ xmlctx->value_len < 20 ? (int)xmlctx->value_len : 20, xmlctx->value, snode->name);
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+
+ if (!xmlctx->ws_only) {
+ /* use an arbitrary text value for anyxml */
+ val = strndup(xmlctx->value, xmlctx->value_len);
+ LY_CHECK_ERR_GOTO(!val, LOGMEM(xmlctx->ctx); rc = LY_EMEM, cleanup);
+
+ /* parser next */
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* create node */
+ r = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ val = NULL;
+ } else {
/* create node */
- ret = lyd_create_inner(snode, &node);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- LOG_LOCSET(snode, node, NULL, NULL);
+ assert(*node);
+ LOG_LOCSET(NULL, *node, NULL, NULL);
+ log_node = 1;
/* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- prev_parse_opts = lydctx->parse_opts;
- if (ext) {
- /* only parse these extension data and validate afterwards */
- lydctx->parse_opts |= LYD_PARSE_ONLY;
- }
+ /* update options so that generic data can be parsed */
+ lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
+ lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
- /* process children */
+ /* parse any data tree */
while (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_subtree_r(lydctx, node, lyd_node_child_p(node), NULL);
- LY_CHECK_GOTO(ret, error);
+ r = lydxml_subtree_r(lydctx, NULL, &child, NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
- /* restore options */
- lydctx->parse_opts = prev_parse_opts;
+ /* assign the data tree */
+ ((struct lyd_node_any *)*node)->value.tree = child;
+ child = NULL;
+ }
- if (snode->nodetype == LYS_LIST) {
- /* check all keys exist */
- LY_CHECK_GOTO(ret = lyd_parse_check_keys(node), error);
- }
+cleanup:
+ if (log_node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ lydctx->parse_opts = prev_parse_opts;
+ lydctx->int_opts = prev_int_opts;
+ free(val);
+ lyd_free_tree(child);
+ if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+ return rc;
+}
- if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
- /* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, NULL);
- LY_CHECK_GOTO(ret, error);
-
- /* add any missing default children */
- ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
- &lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
- LY_CHECK_GOTO(ret, error);
- }
-
- if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
- /* rememeber the RPC/action/notification */
- lydctx->op_node = node;
- }
- } else if (snode->nodetype & LYD_NODE_ANY) {
- if ((snode->nodetype == LYS_ANYDATA) && !xmlctx->ws_only) {
- /* value in anydata node, we expect a tree */
- LOGVAL(ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an anydata node \"%s\" found.",
- (int)xmlctx->value_len < 20 ? xmlctx->value_len : 20, xmlctx->value, snode->name);
- ret = LY_EVALID;
- goto error;
- }
+/**
+ * @brief Parse an XML subtree, recursively.
+ *
+ * @param[in] lydctx XML YANG data parser context.
+ * @param[in,out] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
+ * @param[in,out] first_p Pointer to the first (@p parent or top-level) child.
+ * @param[in,out] parsed Optional set to add all the parsed siblings into.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
+ struct lyxml_ctx *xmlctx;
+ const struct ly_ctx *ctx;
+ struct lyd_meta *meta = NULL;
+ struct lyd_attr *attr = NULL;
+ const struct lysc_node *snode = NULL;
+ struct lysc_ext_instance *ext = NULL;
+ uint32_t orig_parse_opts;
+ struct lyd_node *node = NULL, *insert_anchor = NULL;
+ ly_bool parse_subtree;
- if (!xmlctx->ws_only) {
- /* use an arbitrary text value for anyxml */
- val = strndup(xmlctx->value, xmlctx->value_len);
- LY_CHECK_ERR_GOTO(!val, LOGMEM(xmlctx->ctx); ret = LY_EMEM, error);
+ assert(parent || first_p);
- /* parser next */
- LY_CHECK_ERR_GOTO(ret = lyxml_ctx_next(xmlctx), free(val), error);
+ xmlctx = lydctx->xmlctx;
+ ctx = xmlctx->ctx;
- /* create node */
- ret = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, &node);
- LY_CHECK_ERR_GOTO(ret, free(val), error);
- } else {
- /* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
-
- /* update options so that generic data can be parsed */
- prev_parse_opts = lydctx->parse_opts;
- lydctx->parse_opts &= ~LYD_PARSE_STRICT;
- lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
- prev_int_opts = lydctx->int_opts;
- lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
-
- /* parse any data tree */
- anchor = NULL;
- while (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_subtree_r(lydctx, NULL, &anchor, NULL);
- if (ret) {
- lyd_free_siblings(anchor);
- break;
- }
- }
+ parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
+ /* all descendants should be parsed */
+ lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
+ orig_parse_opts = lydctx->parse_opts;
+
+ assert(xmlctx->status == LYXML_ELEMENT);
+
+ /* remember element prefix and name */
+ prefix = xmlctx->prefix;
+ prefix_len = xmlctx->prefix_len;
+ name = xmlctx->name;
+ name_len = xmlctx->name_len;
+
+ /* parser next */
+ rc = lyxml_ctx_next(xmlctx);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if ((lydctx->int_opts & LYD_INTOPT_EVENTTIME) && !parent && name_len && !prefix_len &&
+ !ly_strncmp("eventTime", name, name_len)) {
+ /* parse eventTime, create node */
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len,
+ "urn:ietf:params:xml:ns:netconf:notification:1.0", 47, xmlctx->value,
+ xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_VALUE_XML, NULL, LYD_HINT_DATA, &node);
+ LY_CHECK_GOTO(rc, cleanup);
- /* restore options */
- lydctx->parse_opts = prev_parse_opts;
- lydctx->int_opts = prev_int_opts;
+ /* validate the value */
+ r = lyd_parser_notif_eventtime_validate(node);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
- LY_CHECK_GOTO(ret, error);
+ /* parser next */
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
+ if (xmlctx->status != LYXML_ELEM_CLOSE) {
+ LOGVAL(ctx, LYVE_DATA, "Unexpected notification \"eventTime\" node children.");
+ rc = LY_EVALID;
+ lyd_free_tree(node);
+ goto cleanup;
+ }
+
+ goto node_parsed;
+ }
+
+ /* get the schema node */
+ r = lydxml_subtree_get_snode(lydctx, parent, prefix, prefix_len, name, name_len, &snode, &ext);
+ if (r) {
+ rc = r;
+ if ((r == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) {
+ /* skip the invalid data */
+ if ((r = lydxml_data_skip(xmlctx))) {
+ rc = r;
+ }
+ }
+ goto cleanup;
+ } else if (!snode && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+ LOGVRB("Skipping parsing of unknown node \"%.*s\".", (int)name_len, name);
- /* create node */
- ret = lyd_create_any(snode, anchor, LYD_ANYDATA_DATATREE, 1, &node);
- LY_CHECK_GOTO(ret, error);
+ /* skip element with children */
+ rc = lydxml_data_skip(xmlctx);
+ goto cleanup;
+ }
+
+ /* create metadata/attributes */
+ if (xmlctx->status == LYXML_ATTRIBUTE) {
+ if (snode) {
+ rc = lydxml_metadata(lydctx, snode, &meta);
+ LY_CHECK_GOTO(rc, cleanup);
+ } else {
+ assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
+ rc = lydxml_attrs(xmlctx, &attr);
+ LY_CHECK_GOTO(rc, cleanup);
}
}
- assert(node);
- if (snode) {
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ if (!snode) {
+ /* opaque */
+ r = lydxml_subtree_opaq(lydctx, parent ? lyd_child(parent) : *first_p, prefix, prefix_len, name, name_len,
+ &insert_anchor, &node);
+ } else if (snode->nodetype & LYD_NODE_TERM) {
+ /* term */
+ r = lydxml_subtree_term(lydctx, parent, snode, &node);
+ } else if (snode->nodetype & LYD_NODE_INNER) {
+ /* inner */
+ r = lydxml_subtree_inner(lydctx, snode, ext, &node);
+ } else {
+ /* any */
+ assert(snode->nodetype & LYD_NODE_ANY);
+ r = lydxml_subtree_any(lydctx, snode, ext, &node);
+ }
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
+node_parsed:
+ if (node && snode) {
/* add/correct flags */
- LY_CHECK_GOTO(ret = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext), error);
+ r = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
/* store for ext instance node validation, if needed */
- LY_CHECK_GOTO(ret = lyd_validate_node_ext(node, &lydctx->ext_node), error);
+ r = lyd_validate_node_ext(node, &lydctx->ext_node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
}
/* parser next */
assert(xmlctx->status == LYXML_ELEM_CLOSE);
if (!parse_subtree) {
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
}
+ LY_CHECK_GOTO(!node, cleanup);
+
/* add metadata/attributes */
if (snode) {
lyd_insert_meta(node, meta, 0);
+ meta = NULL;
} else {
lyd_insert_attr(node, attr);
+ attr = NULL;
}
/* insert, keep first pointer correct */
if (insert_anchor) {
lyd_insert_after(insert_anchor, node);
} else if (ext) {
- LY_CHECK_GOTO(ret = lyplg_ext_insert(parent, node), error);
+ r = lyplg_ext_insert(parent, node);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
} else {
lyd_insert_node(parent, first_p, node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
}
@@ -895,17 +1132,11 @@ lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd
ly_set_add(parsed, node, 1, NULL);
}
+cleanup:
lydctx->parse_opts = orig_parse_opts;
- LOG_LOCBACK(node ? 1 : 0, node ? 1 : 0, 0, 0);
- return LY_SUCCESS;
-
-error:
- lydctx->parse_opts = orig_parse_opts;
- LOG_LOCBACK(node ? 1 : 0, node ? 1 : 0, 0, 0);
lyd_free_meta_siblings(meta);
lyd_free_attr_siblings(ctx, attr);
- lyd_free_tree(node);
- return ret;
+ return rc;
}
/**
@@ -929,7 +1160,11 @@ lydxml_envelope(struct lyxml_ctx *xmlctx, const char *name, const char *uri, ly_
const char *prefix;
size_t prefix_len;
- assert(xmlctx->status == LYXML_ELEMENT);
+ if (xmlctx->status != LYXML_ELEMENT) {
+ /* nothing to parse */
+ return LY_ENOT;
+ }
+
if (ly_strncmp(name, xmlctx->name, xmlctx->name_len)) {
/* not the expected element */
return LY_ENOT;
@@ -987,9 +1222,10 @@ lyd_parse_xml(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
{
- LY_ERR rc = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
struct lyd_xml_ctx *lydctx;
- ly_bool parsed_data_nodes = 0;
+ ly_bool parsed_data_nodes = 0, close_elem = 0;
+ struct lyd_node *act = NULL;
enum LYXML_PARSER_STATUS status;
assert(ctx && in && lydctx_p);
@@ -1009,9 +1245,19 @@ lyd_parse_xml(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
/* find the operation node if it exists already */
LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+ if ((int_opts & LYD_INTOPT_RPC) && (int_opts & LYD_INTOPT_ACTION)) {
+ /* can be either, try to parse "action" */
+ if (!lydxml_envelope(lydctx->xmlctx, "action", "urn:ietf:params:xml:ns:yang:1", 0, &act)) {
+ close_elem = 1;
+ int_opts &= ~LYD_INTOPT_RPC;
+ }
+ }
+
/* parse XML data */
while (lydctx->xmlctx->status == LYXML_ELEMENT) {
- LY_CHECK_GOTO(rc = lydxml_subtree_r(lydctx, parent, first_p, parsed), cleanup);
+ r = lydxml_subtree_r(lydctx, parent, first_p, parsed);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
parsed_data_nodes = 1;
if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
@@ -1019,16 +1265,29 @@ lyd_parse_xml(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
}
}
+ /* close an opened element */
+ if (close_elem) {
+ if (lydctx->xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(lydctx->xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(lydctx->xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\".",
+ (int)lydctx->xmlctx->name_len, lydctx->xmlctx->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(lydctx->xmlctx), cleanup);
+ }
+
/* check final state */
if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && (lydctx->xmlctx->status == LYXML_ELEMENT)) {
LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
- rc = LY_EVALID;
- goto cleanup;
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
- rc = LY_EVALID;
- goto cleanup;
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
if (!parsed_data_nodes) {
@@ -1051,7 +1310,8 @@ cleanup:
assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
!lydctx->node_when.count));
- if (rc) {
+ lyd_free_tree(act);
+ if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
lyd_xml_ctx_free((struct lyd_ctx *)lydctx);
} else {
*lydctx_p = (struct lyd_ctx *)lydctx;
@@ -1081,12 +1341,6 @@ lydxml_env_netconf_rpc(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_
assert(envp && !*envp);
- if (xmlctx->status != LYXML_ELEMENT) {
- /* nothing to parse */
- assert(xmlctx->status == LYXML_END);
- goto cleanup;
- }
-
/* parse "rpc" */
r = lydxml_envelope(xmlctx, "rpc", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, envp);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
@@ -1118,123 +1372,6 @@ cleanup:
}
/**
- * @brief Validate eventTime date-and-time value.
- *
- * @param[in] node Opaque eventTime node.
- * @return LY_SUCCESS on success.
- * @return LY_ERR value on error.
- */
-static LY_ERR
-lydxml_env_netconf_eventtime_validate(const struct lyd_node *node)
-{
- LY_ERR rc = LY_SUCCESS;
- struct ly_ctx *ctx = (struct ly_ctx *)LYD_CTX(node);
- struct lysc_ctx cctx;
- const struct lys_module *mod;
- LY_ARRAY_COUNT_TYPE u;
- struct ly_err_item *err = NULL;
- struct lysp_type *type_p = NULL;
- struct lysc_pattern **patterns = NULL;
- const char *value;
-
- LYSC_CTX_INIT_CTX(cctx, ctx);
-
- /* get date-and-time parsed type */
- mod = ly_ctx_get_module_latest(ctx, "ietf-yang-types");
- assert(mod);
- LY_ARRAY_FOR(mod->parsed->typedefs, u) {
- if (!strcmp(mod->parsed->typedefs[u].name, "date-and-time")) {
- type_p = &mod->parsed->typedefs[u].type;
- break;
- }
- }
- assert(type_p);
-
- /* compile patterns */
- assert(type_p->patterns);
- LY_CHECK_GOTO(rc = lys_compile_type_patterns(&cctx, type_p->patterns, NULL, &patterns), cleanup);
-
- /* validate */
- value = lyd_get_value(node);
- rc = lyplg_type_validate_patterns(patterns, value, strlen(value), &err);
-
-cleanup:
- FREE_ARRAY(&cctx.free_ctx, patterns, lysc_pattern_free);
- if (rc && err) {
- LOGVAL_ERRITEM(ctx, err);
- ly_err_free(err);
- LOGVAL(ctx, LYVE_DATA, "Invalid \"eventTime\" in the notification.");
- }
- return rc;
-}
-
-/**
- * @brief Parse all expected non-data XML elements of a NETCONF notification message.
- *
- * @param[in] xmlctx XML parser context.
- * @param[out] evnp Parsed envelope(s) (opaque node).
- * @param[out] int_opts Internal options for parsing the rest of YANG data.
- * @param[out] close_elem Number of parsed opened elements that need to be closed.
- * @return LY_SUCCESS on success.
- * @return LY_ERR value on error.
- */
-static LY_ERR
-lydxml_env_netconf_notif(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
-{
- LY_ERR rc = LY_SUCCESS, r;
- struct lyd_node *child;
-
- assert(envp && !*envp);
-
- if (xmlctx->status != LYXML_ELEMENT) {
- /* nothing to parse */
- assert(xmlctx->status == LYXML_END);
- goto cleanup;
- }
-
- /* parse "notification" */
- r = lydxml_envelope(xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, envp);
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* parse "eventTime" */
- r = lydxml_envelope(xmlctx, "eventTime", "urn:ietf:params:xml:ns:netconf:notification:1.0", 1, &child);
- if (r == LY_ENOT) {
- LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unexpected element \"%.*s\" instead of \"eventTime\".",
- (int)xmlctx->name_len, xmlctx->name);
- r = LY_EVALID;
- }
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* insert */
- lyd_insert_node(*envp, NULL, child, 0);
-
- /* validate value */
- r = lydxml_env_netconf_eventtime_validate(child);
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* finish child parsing */
- if (xmlctx->status != LYXML_ELEM_CLOSE) {
- assert(xmlctx->status == LYXML_ELEMENT);
- LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"eventTime\".",
- (int)xmlctx->name_len, xmlctx->name);
- rc = LY_EVALID;
- goto cleanup;
- }
- LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
-
- /* NETCONF notification */
- *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_NOTIF;
- *close_elem = 1;
-
-cleanup:
- if (rc) {
- lyd_free_tree(*envp);
- *envp = NULL;
- }
- return rc;
-}
-
-/**
* @brief Parse an XML element as an opaque node subtree.
*
* @param[in] xmlctx XML parser context.
@@ -1247,9 +1384,11 @@ lydxml_opaq_r(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
LY_ERR rc = LY_SUCCESS;
const struct lyxml_ns *ns;
struct lyd_attr *attr = NULL;
- struct lyd_node *child = NULL;
- const char *name, *prefix;
- size_t name_len, prefix_len;
+ struct lyd_node *node = NULL;
+ struct lyd_node_opaq *opaq;
+ const char *name, *prefix, *value = NULL;
+ size_t name_len, prefix_len, value_len;
+ ly_bool ws_only, dynamic = 0;
assert(xmlctx->status == LYXML_ELEMENT);
@@ -1270,14 +1409,23 @@ lydxml_opaq_r(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
LY_CHECK_RET(lydxml_attrs(xmlctx, &attr));
}
- /* create node */
+ /* remember the value */
assert(xmlctx->status == LYXML_ELEM_CONTENT);
- rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len, ns->uri, strlen(ns->uri), xmlctx->value,
- xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_VALUE_XML, NULL, 0, &child);
+ value = xmlctx->value;
+ value_len = xmlctx->value_len;
+ ws_only = xmlctx->ws_only;
+ dynamic = xmlctx->dynamic;
+ if (dynamic) {
+ xmlctx->dynamic = 0;
+ }
+
+ /* create the node without value */
+ rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len, ns->uri, strlen(ns->uri), NULL, 0, NULL,
+ LY_VALUE_XML, NULL, 0, &node);
LY_CHECK_GOTO(rc, cleanup);
/* assign atributes */
- ((struct lyd_node_opaq *)child)->attr = attr;
+ ((struct lyd_node_opaq *)node)->attr = attr;
attr = NULL;
/* parser next element */
@@ -1285,17 +1433,38 @@ lydxml_opaq_r(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
/* parse all the descendants */
while (xmlctx->status == LYXML_ELEMENT) {
- rc = lydxml_opaq_r(xmlctx, child);
+ rc = lydxml_opaq_r(xmlctx, node);
LY_CHECK_GOTO(rc, cleanup);
}
/* insert */
- lyd_insert_node(parent, NULL, child, 1);
+ lyd_insert_node(parent, NULL, node, 1);
+
+ /* update the value */
+ opaq = (struct lyd_node_opaq *)node;
+ if (opaq->child) {
+ if (!ws_only) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX_XML, "Mixed XML content node \"%s\" found, not supported.", LYD_NAME(node));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ } else if (value_len) {
+ lydict_remove(xmlctx->ctx, opaq->value);
+ if (dynamic) {
+ LY_CHECK_GOTO(rc = lydict_insert_zc(xmlctx->ctx, (char *)value, &opaq->value), cleanup);
+ dynamic = 0;
+ } else {
+ LY_CHECK_GOTO(rc = lydict_insert(xmlctx->ctx, value, value_len, &opaq->value), cleanup);
+ }
+ }
cleanup:
lyd_free_attr_siblings(xmlctx->ctx, attr);
+ if (dynamic) {
+ free((char *)value);
+ }
if (rc) {
- lyd_free_tree(child);
+ lyd_free_tree(node);
}
return rc;
}
@@ -1607,12 +1776,6 @@ lydxml_env_netconf_reply(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint3
assert(envp && !*envp);
- if (xmlctx->status != LYXML_ELEMENT) {
- /* nothing to parse */
- assert(xmlctx->status == LYXML_END);
- goto cleanup;
- }
-
/* parse "rpc-reply" */
r = lydxml_envelope(xmlctx, "rpc-reply", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, envp);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
@@ -1702,15 +1865,13 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *
{
LY_ERR rc = LY_SUCCESS;
struct lyd_xml_ctx *lydctx;
+ struct lyd_node *node;
uint32_t i, int_opts = 0, close_elem = 0;
ly_bool parsed_data_nodes = 0;
assert(ctx && in && lydctx_p);
assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
-
- assert((data_type == LYD_TYPE_RPC_NETCONF) || (data_type == LYD_TYPE_NOTIF_NETCONF) ||
- (data_type == LYD_TYPE_REPLY_NETCONF));
assert(!(parse_opts & LYD_PARSE_SUBTREE));
/* init context */
@@ -1733,11 +1894,17 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *
break;
case LYD_TYPE_NOTIF_NETCONF:
assert(!parent);
- rc = lydxml_env_netconf_notif(lydctx->xmlctx, envp, &int_opts, &close_elem);
+
+ /* parse "notification" */
+ rc = lydxml_envelope(lydctx->xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, envp);
if (rc == LY_ENOT) {
LOGVAL(ctx, LYVE_DATA, "Missing NETCONF <notification> envelope or in incorrect namespace.");
}
LY_CHECK_GOTO(rc, cleanup);
+
+ /* NETCONF notification */
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME;
+ close_elem = 1;
break;
case LYD_TYPE_REPLY_NETCONF:
assert(parent);
@@ -1747,6 +1914,32 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *
}
LY_CHECK_GOTO(rc, cleanup);
break;
+ case LYD_TYPE_RPC_RESTCONF:
+ assert(parent);
+
+ /* parse "input" */
+ rc = lydxml_envelope(lydctx->xmlctx, "input", lyd_node_module(parent)->ns, 0, envp);
+ if (rc == LY_ENOT) {
+ LOGVAL(ctx, LYVE_DATA, "Missing RESTCONF \"input\" object or in incorrect namespace.");
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
+ close_elem = 1;
+ break;
+ case LYD_TYPE_REPLY_RESTCONF:
+ assert(parent);
+
+ /* parse "output" */
+ rc = lydxml_envelope(lydctx->xmlctx, "output", lyd_node_module(parent)->ns, 0, envp);
+ if (rc == LY_ENOT) {
+ LOGVAL(ctx, LYVE_DATA, "Missing RESTCONF \"output\" object or in incorrect namespace.");
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
+ close_elem = 1;
+ break;
default:
LOGINT(ctx);
rc = LY_EINT;
@@ -1792,6 +1985,21 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *
rc = LY_EVALID;
goto cleanup;
}
+ if (int_opts & LYD_INTOPT_EVENTTIME) {
+ /* parse as a child of the envelope */
+ node = (*first_p)->prev;
+ if (node->schema) {
+ LOGVAL(ctx, LYVE_DATA, "Missing notification \"eventTime\" node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ } else {
+ /* can be the only opaque node and an operation had to be parsed */
+ assert(!strcmp(LYD_NAME(node), "eventTime") && (*first_p)->next);
+ lyd_unlink(node);
+ assert(*envp);
+ lyd_insert_child(*envp, node);
+ }
+ }
if (!parsed_data_nodes) {
/* no data nodes were parsed */
diff --git a/src/parser_yang.c b/src/parser_yang.c
index dd84480..e18ed17 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -416,10 +416,14 @@ read_qstring(struct lysp_yang_ctx *ctx, enum yang_arg arg, char **word_p, char *
}
break;
case '\r':
- if (ctx->in->current[1] != '\n') {
+ /* newline may be escaped */
+ if ((ctx->in->current[1] != '\n') && strncmp(&ctx->in->current[1], "\\n", 2)) {
LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, ctx->in->current[0]);
return LY_EVALID;
}
+
+ /* skip this character, do not store it */
+ ++ctx->in->current;
/* fallthrough */
case '\n':
if (block_indent) {
@@ -804,7 +808,8 @@ keyword_start:
MOVE_INPUT(ctx, 1);
goto extension;
case '{':
- /* allowed only for input and output statements which can be without arguments */
+ case ';':
+ /* allowed only for input and output statements which are without arguments */
if ((*kw == LY_STMT_INPUT) || (*kw == LY_STMT_OUTPUT)) {
break;
}
@@ -932,7 +937,8 @@ parse_ext(struct lysp_yang_ctx *ctx, const char *ext_name, size_t ext_name_len,
LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *exts, e, LY_EMEM);
if (!ly_strnchr(ext_name, ':', ext_name_len)) {
- LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%*.s\" without the mandatory prefix.", ext_name_len, ext_name);
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%.*s\" without the mandatory prefix.",
+ (int)ext_name_len, ext_name);
return LY_EVALID;
}
@@ -1039,7 +1045,7 @@ parse_yangversion(struct lysp_yang_ctx *ctx, struct lysp_module *mod)
} else if ((word_len == ly_strlen_const("1.1")) && !strncmp(word, "1.1", word_len)) {
mod->version = LYS_VERSION_1_1;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "yang-version");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "yang-version");
free(buf);
return LY_EVALID;
}
@@ -1450,7 +1456,7 @@ parse_config(struct lysp_yang_ctx *ctx, uint16_t *flags, struct lysp_ext_instanc
} else if ((word_len == ly_strlen_const("false")) && !strncmp(word, "false", word_len)) {
*flags |= LYS_CONFIG_R;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "config");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "config");
free(buf);
return LY_EVALID;
}
@@ -1501,7 +1507,7 @@ parse_mandatory(struct lysp_yang_ctx *ctx, uint16_t *flags, struct lysp_ext_inst
} else if ((word_len == ly_strlen_const("false")) && !strncmp(word, "false", word_len)) {
*flags |= LYS_MAND_FALSE;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "mandatory");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "mandatory");
free(buf);
return LY_EVALID;
}
@@ -1622,7 +1628,7 @@ parse_status(struct lysp_yang_ctx *ctx, uint16_t *flags, struct lysp_ext_instanc
} else if ((word_len == ly_strlen_const("obsolete")) && !strncmp(word, "obsolete", word_len)) {
*flags |= LYS_STATUS_OBSLT;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "status");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "status");
free(buf);
return LY_EVALID;
}
@@ -1803,7 +1809,7 @@ parse_type_enum_value_pos(struct lysp_yang_ctx *ctx, enum ly_stmt val_kw, struct
LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
if (!word_len || (word[0] == '+') || ((word[0] == '0') && (word_len > 1)) || ((val_kw == LY_STMT_POSITION) && !strncmp(word, "-0", 2))) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, lyplg_ext_stmt2str(val_kw));
ret = LY_EVALID;
goto cleanup;
}
@@ -1812,26 +1818,26 @@ parse_type_enum_value_pos(struct lysp_yang_ctx *ctx, enum ly_stmt val_kw, struct
if (val_kw == LY_STMT_VALUE) {
num = strtoll(word, &ptr, LY_BASE_DEC);
if ((num < INT64_C(-2147483648)) || (num > INT64_C(2147483647))) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, lyplg_ext_stmt2str(val_kw));
ret = LY_EVALID;
goto cleanup;
}
} else {
unum = strtoull(word, &ptr, LY_BASE_DEC);
if (unum > UINT64_C(4294967295)) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, lyplg_ext_stmt2str(val_kw));
ret = LY_EVALID;
goto cleanup;
}
}
/* we have not parsed the whole argument */
if ((size_t)(ptr - word) != word_len) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, lyplg_ext_stmt2str(val_kw));
ret = LY_EVALID;
goto cleanup;
}
if (errno == ERANGE) {
- LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, lyplg_ext_stmt2str(val_kw));
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, (int)word_len, word, lyplg_ext_stmt2str(val_kw));
ret = LY_EVALID;
goto cleanup;
}
@@ -1953,7 +1959,7 @@ parse_type_fracdigits(struct lysp_yang_ctx *ctx, struct lysp_type *type)
LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
if (!word_len || (word[0] == '0') || !isdigit(word[0])) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "fraction-digits");
ret = LY_EVALID;
goto cleanup;
}
@@ -1962,12 +1968,12 @@ parse_type_fracdigits(struct lysp_yang_ctx *ctx, struct lysp_type *type)
num = strtoull(word, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
if ((size_t)(ptr - word) != word_len) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "fraction-digits");
ret = LY_EVALID;
goto cleanup;
}
if ((errno == ERANGE) || (num > LY_TYPE_DEC64_FD_MAX)) {
- LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "fraction-digits");
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, (int)word_len, word, "fraction-digits");
ret = LY_EVALID;
goto cleanup;
}
@@ -2018,7 +2024,7 @@ parse_type_reqinstance(struct lysp_yang_ctx *ctx, struct lysp_type *type)
if ((word_len == ly_strlen_const("true")) && !strncmp(word, "true", word_len)) {
type->require_instance = 1;
} else if ((word_len != ly_strlen_const("false")) || strncmp(word, "false", word_len)) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "require-instance");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "require-instance");
ret = LY_EVALID;
goto cleanup;
}
@@ -2065,7 +2071,7 @@ parse_type_pattern_modifier(struct lysp_yang_ctx *ctx, struct lysp_restr *restr)
LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
if ((word_len != ly_strlen_const("invert-match")) || strncmp(word, "invert-match", word_len)) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "modifier");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "modifier");
ret = LY_EVALID;
goto cleanup;
}
@@ -2395,7 +2401,7 @@ parse_maxelements(struct lysp_yang_ctx *ctx, uint32_t *max, uint16_t *flags, str
LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
if (!word_len || (word[0] == '0') || ((word[0] != 'u') && !isdigit(word[0]))) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "max-elements");
ret = LY_EVALID;
goto cleanup;
}
@@ -2405,12 +2411,12 @@ parse_maxelements(struct lysp_yang_ctx *ctx, uint32_t *max, uint16_t *flags, str
num = strtoull(word, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
if ((size_t)(ptr - word) != word_len) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "max-elements");
ret = LY_EVALID;
goto cleanup;
}
if ((errno == ERANGE) || (num > UINT32_MAX)) {
- LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "max-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, (int)word_len, word, "max-elements");
ret = LY_EVALID;
goto cleanup;
}
@@ -2467,7 +2473,7 @@ parse_minelements(struct lysp_yang_ctx *ctx, uint32_t *min, uint16_t *flags, str
LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
if (!word_len || !isdigit(word[0]) || ((word[0] == '0') && (word_len > 1))) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "min-elements");
ret = LY_EVALID;
goto cleanup;
}
@@ -2476,12 +2482,12 @@ parse_minelements(struct lysp_yang_ctx *ctx, uint32_t *min, uint16_t *flags, str
num = strtoull(word, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
if ((size_t)(ptr - word) != word_len) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "min-elements");
ret = LY_EVALID;
goto cleanup;
}
if ((errno == ERANGE) || (num > UINT32_MAX)) {
- LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "min-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, (int)word_len, word, "min-elements");
ret = LY_EVALID;
goto cleanup;
}
@@ -2533,7 +2539,7 @@ parse_orderedby(struct lysp_yang_ctx *ctx, struct lysp_node *llist)
} else if ((word_len == ly_strlen_const("user")) && !strncmp(word, "user", word_len)) {
llist->flags |= LYS_ORDBY_USER;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "ordered-by");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "ordered-by");
ret = LY_EVALID;
goto cleanup;
}
@@ -2860,11 +2866,6 @@ parse_inout(struct lysp_yang_ctx *ctx, enum ly_stmt inout_kw, struct lysp_node *
YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, inout_p->exts, ret, cleanup);
}
- if (!inout_p->child) {
- LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "data-def-stmt", lyplg_ext_stmt2str(inout_kw));
- return LY_EVALID;
- }
-
cleanup:
return ret;
}
@@ -3715,7 +3716,7 @@ parse_yinelement(struct lysp_yang_ctx *ctx, struct lysp_ext *ext)
} else if ((word_len == ly_strlen_const("false")) && !strncmp(word, "false", word_len)) {
ext->flags |= LYS_YINELEM_FALSE;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "yin-element");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "yin-element");
free(buf);
return LY_EVALID;
}
@@ -3868,7 +3869,7 @@ parse_deviate(struct lysp_yang_ctx *ctx, struct lysp_deviate **deviates)
} else if ((word_len == ly_strlen_const("delete")) && !strncmp(word, "delete", word_len)) {
dev_mod = LYS_DEV_DELETE;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "deviate");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "deviate");
ret = LY_EVALID;
goto cleanup;
}
diff --git a/src/parser_yin.c b/src/parser_yin.c
index fa44968..36b49f1 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -488,7 +488,7 @@ yin_parse_attribute(struct lysp_yin_ctx *ctx, enum yin_argument arg_type, const
INSERT_STRING_RET(ctx->xmlctx->ctx, ctx->xmlctx->value, ctx->xmlctx->value_len, ctx->xmlctx->dynamic, *arg_val);
LY_CHECK_RET(!(*arg_val), LY_EMEM);
} else {
- LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_ATTR, ctx->xmlctx->name_len,
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_ATTR, (int)ctx->xmlctx->name_len,
ctx->xmlctx->name, lyplg_ext_stmt2str(current_element));
return LY_EVALID;
}
@@ -3185,7 +3185,7 @@ yin_parse_extension_instance_arg(struct lysp_yin_ctx *ctx, enum ly_stmt parent_s
ctx->xmlctx->prefix_len, parent_stmt);
if (((parent_stmt == LY_STMT_ERROR_MESSAGE) && (child != LY_STMT_ARG_VALUE)) ||
((parent_stmt != LY_STMT_ERROR_MESSAGE) && (child != LY_STMT_ARG_TEXT))) {
- LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, ctx->xmlctx->name_len, ctx->xmlctx->name,
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, (int)ctx->xmlctx->name_len, ctx->xmlctx->name,
lyplg_ext_stmt2str(parent_stmt));
return LY_EVALID;
}
@@ -3256,7 +3256,7 @@ yin_parse_element_generic(struct lysp_yin_ctx *ctx, enum ly_stmt parent_stmt, st
last = (*element)->child;
if ((*element)->kw == LY_STMT_NONE) {
/* unrecognized element */
- LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, ctx->xmlctx->name_len, ctx->xmlctx->name,
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, (int)ctx->xmlctx->name_len, ctx->xmlctx->name,
lyplg_ext_stmt2str(parent_stmt));
ret = LY_EVALID;
goto cleanup;
@@ -3422,7 +3422,7 @@ yin_parse_extension_instance(struct lysp_yin_ctx *ctx, const void *parent, enum
}
} else if (ctx->xmlctx->value_len) {
/* invalid text content */
- LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%s\" with unexpected text content \".*s\".", ext_name,
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%s\" with unexpected text content \"%.*s\".", ext_name,
(int)ctx->xmlctx->value_len, ctx->xmlctx->value);
return LY_EVALID;
}
@@ -3479,7 +3479,7 @@ yin_parse_content(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info,
if ((parent_stmt == LY_STMT_DEVIATE) && isdevsub(cur_stmt)) {
LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INDEV_YIN, lyplg_ext_stmt2str(cur_stmt));
} else {
- LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, ctx->xmlctx->name_len,
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, (int)ctx->xmlctx->name_len,
ctx->xmlctx->name, lyplg_ext_stmt2str(parent_stmt));
}
ret = LY_EVALID;
@@ -3718,7 +3718,6 @@ yin_parse_content(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info,
ret = LY_EINT;
}
LY_CHECK_GOTO(ret, cleanup);
- subelem = NULL;
LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
}
diff --git a/src/path.c b/src/path.c
index 72e5fb5..73883d7 100644
--- a/src/path.c
+++ b/src/path.c
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Path functions
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 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.
@@ -11,6 +11,9 @@
*
* https://opensource.org/licenses/BSD-3-Clause
*/
+
+#define _GNU_SOURCE
+
#include "path.h"
#include <assert.h>
@@ -45,7 +48,7 @@
*/
static LY_ERR
ly_path_check_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lyxp_expr *exp,
- uint32_t *tok_idx, uint8_t prefix, uint8_t pred)
+ uint32_t *tok_idx, uint16_t prefix, uint16_t pred)
{
LY_ERR ret = LY_SUCCESS;
struct ly_set *set = NULL;
@@ -106,8 +109,19 @@ ly_path_check_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_no
/* '=' */
LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error);
- /* Literal or Number */
- LY_CHECK_GOTO(lyxp_next_token2(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL, LYXP_TOKEN_NUMBER), token_error);
+ /* fill repeat */
+ exp->repeat[*tok_idx - 2] = calloc(2, sizeof *exp->repeat[*tok_idx]);
+ LY_CHECK_ERR_GOTO(!exp->repeat[*tok_idx - 2], LOGMEM(NULL); ret = LY_EMEM, cleanup);
+ exp->repeat[*tok_idx - 2][0] = LYXP_EXPR_EQUALITY;
+
+ /* Literal, Number, or VariableReference */
+ if (lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_LITERAL) &&
+ lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_NUMBER) &&
+ lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_VARREF)) {
+ /* error */
+ lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL);
+ goto token_error;
+ }
/* ']' */
LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), token_error);
@@ -121,6 +135,11 @@ ly_path_check_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_no
/* '=' */
LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error);
+ /* fill repeat */
+ exp->repeat[*tok_idx - 2] = calloc(2, sizeof *exp->repeat[*tok_idx]);
+ LY_CHECK_ERR_GOTO(!exp->repeat[*tok_idx - 2], LOGMEM(NULL); ret = LY_EMEM, cleanup);
+ exp->repeat[*tok_idx - 2][0] = LYXP_EXPR_EQUALITY;
+
/* Literal or Number */
LY_CHECK_GOTO(lyxp_next_token2(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL, LYXP_TOKEN_NUMBER), token_error);
@@ -178,6 +197,11 @@ ly_path_check_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_no
/* '=' */
LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error);
+ /* fill repeat */
+ exp->repeat[*tok_idx - 2] = calloc(2, sizeof *exp->repeat[*tok_idx]);
+ LY_CHECK_ERR_GOTO(!exp->repeat[*tok_idx - 2], LOGMEM(NULL); ret = LY_EMEM, cleanup);
+ exp->repeat[*tok_idx - 2][0] = LYXP_EXPR_EQUALITY;
+
/* FuncName */
LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_FUNCNAME), token_error);
if ((exp->tok_len[*tok_idx] != ly_strlen_const("current")) ||
@@ -240,45 +264,121 @@ token_error:
return LY_EVALID;
}
+/**
+ * @brief Parse deref XPath function and perform all additional checks.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Optional context node, used for logging.
+ * @param[in] exp Parsed path.
+ * @param[in,out] tok_idx Index in @p exp, is adjusted.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_parse_deref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const struct lyxp_expr *exp,
+ uint32_t *tok_idx)
+{
+ size_t arg_len;
+ uint32_t begin_token, end_token;
+ struct lyxp_expr *arg_expr = NULL;
+
+ /* mandatory FunctionName */
+ LY_CHECK_RET(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_FUNCNAME), LY_EVALID);
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
+ LOGVAL(ctx, LYVE_XPATH, "Unexpected XPath function \"%.*s\" in path, expected \"deref(...)\"",
+ exp->tok_len[*tok_idx], exp->expr + exp->tok_pos[*tok_idx]);
+ return LY_EVALID;
+ }
+
+ /* mandatory '(' */
+ LY_CHECK_RET(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR1), LY_EVALID);
+ begin_token = *tok_idx;
+
+ /* count tokens till ')' */
+ while (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_PAR2) && *tok_idx < exp->used) {
+ /* emebedded functions are not allowed */
+ if (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_FUNCNAME)) {
+ LOGVAL(ctx, LYVE_XPATH, "Embedded function XPath function inside deref function within the path"
+ "is not allowed");
+ return LY_EVALID;
+ }
+
+ (*tok_idx)++;
+ }
+
+ /* mandatory ')' */
+ LY_CHECK_RET(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR2), LY_EVALID);
+ end_token = *tok_idx - 1;
+
+ /* parse the path of deref argument */
+ arg_len = exp->tok_pos[end_token] - exp->tok_pos[begin_token];
+ LY_CHECK_RET(ly_path_parse(ctx, ctx_node, &exp->expr[exp->tok_pos[begin_token]], arg_len, 1,
+ LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &arg_expr), LY_EVALID);
+ lyxp_expr_free(ctx, arg_expr);
+
+ return LY_SUCCESS;
+}
+
LY_ERR
ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *str_path, size_t path_len,
- ly_bool lref, uint8_t begin, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr)
+ ly_bool lref, uint16_t begin, uint16_t prefix, uint16_t pred, struct lyxp_expr **expr)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *exp = NULL;
uint32_t tok_idx, cur_len;
const char *cur_node, *prev_prefix = NULL, *ptr;
+ ly_bool is_abs;
assert((begin == LY_PATH_BEGIN_ABSOLUTE) || (begin == LY_PATH_BEGIN_EITHER));
assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY) ||
- (prefix == LY_PATH_PREFIX_STRICT_INHERIT));
+ (prefix == LY_PATH_PREFIX_FIRST) || (prefix == LY_PATH_PREFIX_STRICT_INHERIT));
assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF));
LOG_LOCSET(ctx_node, NULL, NULL, NULL);
- /* parse as a generic XPath expression */
- LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 1, &exp), error);
+ /* parse as a generic XPath expression, reparse is performed manually */
+ LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 0, &exp), error);
tok_idx = 0;
+ /* alloc empty repeat (only '=', filled manually) */
+ exp->repeat = calloc(exp->size, sizeof *exp->repeat);
+ LY_CHECK_ERR_GOTO(!exp->repeat, LOGMEM(ctx); ret = LY_EMEM, error);
+
if (begin == LY_PATH_BEGIN_EITHER) {
/* is the path relative? */
if (lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH)) {
/* relative path check specific to leafref */
if (lref) {
+ /* optional function 'deref..' */
+ if ((ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_EXTENDED) &&
+ !lyxp_check_token(NULL, exp, tok_idx, LYXP_TOKEN_FUNCNAME)) {
+ LY_CHECK_ERR_GOTO(ly_path_parse_deref(ctx, ctx_node, exp, &tok_idx), ret = LY_EVALID, error);
+
+ /* '/' */
+ LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID,
+ error);
+ }
+
/* mandatory '..' */
LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_DDOT), ret = LY_EVALID, error);
do {
/* '/' */
- LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error);
+ LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID,
+ error);
/* optional '..' */
} while (!lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_DDOT));
}
+
+ is_abs = 0;
+ } else {
+ is_abs = 1;
}
} else {
/* '/' */
LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error);
+
+ is_abs = 1;
}
do {
@@ -294,8 +394,8 @@ ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
ret = LY_EVALID;
goto error;
}
- } else if (prefix == LY_PATH_PREFIX_STRICT_INHERIT) {
- if (!prev_prefix) {
+ } else if ((prefix == LY_PATH_PREFIX_FIRST) || (prefix == LY_PATH_PREFIX_STRICT_INHERIT)) {
+ if (!prev_prefix && is_abs) {
/* the first node must have a prefix */
if (!strnstr(cur_node, ":", cur_len)) {
LOGVAL(ctx, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", cur_len, cur_node);
@@ -305,7 +405,7 @@ ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
/* remember the first prefix */
prev_prefix = cur_node;
- } else {
+ } else if (prev_prefix && (prefix == LY_PATH_PREFIX_STRICT_INHERIT)) {
/* the prefix must be different, if any */
ptr = strnstr(cur_node, ":", cur_len);
if (ptr) {
@@ -349,7 +449,7 @@ error:
LY_ERR
ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const char *str_path,
- size_t path_len, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr)
+ size_t path_len, uint16_t prefix, uint16_t pred, struct lyxp_expr **expr)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *exp = NULL;
@@ -360,10 +460,14 @@ ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_no
LOG_LOCSET(cur_node, NULL, NULL, NULL);
- /* parse as a generic XPath expression */
+ /* parse as a generic XPath expression, reparse is performed manually */
LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 0, &exp), error);
tok_idx = 0;
+ /* alloc empty repeat (only '=', filled manually) */
+ exp->repeat = calloc(exp->size, sizeof *exp->repeat);
+ LY_CHECK_ERR_GOTO(!exp->repeat, LOGMEM(ctx); ret = LY_EMEM, error);
+
LY_CHECK_GOTO(ret = ly_path_check_predicate(ctx, cur_node, exp, &tok_idx, prefix, pred), error);
/* trailing token check */
@@ -521,7 +625,7 @@ error:
LY_ERR
ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
const struct lysc_node *ctx_node, const struct lyxp_expr *expr, uint32_t *tok_idx, LY_VALUE_FORMAT format,
- void *prefix_data, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
+ void *prefix_data, struct ly_path_predicate **predicates)
{
LY_ERR ret = LY_SUCCESS;
struct ly_path_predicate *p;
@@ -533,7 +637,7 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
LOG_LOCSET(cur_node, NULL, NULL, NULL);
- *pred_type = 0;
+ *predicates = NULL;
if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) {
/* '[', no predicate */
@@ -565,11 +669,7 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
}
++(*tok_idx);
- if (!*pred_type) {
- /* new predicate */
- *pred_type = LY_PATH_PREDTYPE_LIST;
- }
- assert(*pred_type == LY_PATH_PREDTYPE_LIST);
+ /* new predicate */
LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup);
p->key = key;
@@ -577,27 +677,38 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
++(*tok_idx);
- /* Literal or Number */
- assert((expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL) || (expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER));
- if (expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL) {
- /* skip quotes */
- val = expr->expr + expr->tok_pos[*tok_idx] + 1;
- val_len = expr->tok_len[*tok_idx] - 2;
+ /* Literal, Number, or VariableReference */
+ if (expr->tokens[*tok_idx] == LYXP_TOKEN_VARREF) {
+ /* store the variable name */
+ p->variable = strndup(expr->expr + expr->tok_pos[*tok_idx], expr->tok_len[*tok_idx]);
+ LY_CHECK_ERR_GOTO(!p->variable, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+
+ p->type = LY_PATH_PREDTYPE_LIST_VAR;
+ ++(*tok_idx);
} else {
- val = expr->expr + expr->tok_pos[*tok_idx];
- val_len = expr->tok_len[*tok_idx];
- }
+ if (expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL) {
+ /* skip quotes */
+ val = expr->expr + expr->tok_pos[*tok_idx] + 1;
+ val_len = expr->tok_len[*tok_idx] - 2;
+ } else {
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER);
+ val = expr->expr + expr->tok_pos[*tok_idx];
+ val_len = expr->tok_len[*tok_idx];
+ }
- /* store the value */
- LOG_LOCSET(key, NULL, NULL, NULL);
- ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaf *)key)->type, val, val_len, NULL, format,
- prefix_data, LYD_HINT_DATA, key, NULL);
- LOG_LOCBACK(key ? 1 : 0, 0, 0, 0);
- LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup);
- ++(*tok_idx);
+ /* store the value */
+ LOG_LOCSET(key, NULL, NULL, NULL);
+ ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaf *)key)->type, val, val_len, 0, NULL,
+ format, prefix_data, LYD_HINT_DATA, key, NULL);
+ LOG_LOCBACK(key ? 1 : 0, 0, 0, 0);
+ LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup);
+
+ /* "allocate" the type to avoid problems when freeing the value after the type was freed */
+ LY_ATOMIC_INC_BARRIER(((struct lysc_type *)p->value.realtype)->refcount);
- /* "allocate" the type to avoid problems when freeing the value after the type was freed */
- LY_ATOMIC_INC_BARRIER(((struct lysc_type *)p->value.realtype)->refcount);
+ p->type = LY_PATH_PREDTYPE_LIST;
+ ++(*tok_idx);
+ }
/* ']' */
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
@@ -615,7 +726,7 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
/* names (keys) are unique - it was checked when parsing */
LOGVAL(ctx, LYVE_XPATH, "Predicate missing for a key of %s \"%s\" in path.",
lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
- ly_path_predicates_free(ctx, LY_PATH_PREDTYPE_LIST, *predicates);
+ ly_path_predicates_free(ctx, *predicates);
*predicates = NULL;
ret = LY_EVALID;
goto cleanup;
@@ -631,8 +742,8 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
++(*tok_idx);
/* new predicate */
- *pred_type = LY_PATH_PREDTYPE_LEAFLIST;
LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup);
+ p->type = LY_PATH_PREDTYPE_LEAFLIST;
/* '=' */
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
@@ -651,8 +762,8 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
/* store the value */
LOG_LOCSET(ctx_node, NULL, NULL, NULL);
- ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaflist *)ctx_node)->type, val, val_len, NULL, format,
- prefix_data, LYD_HINT_DATA, ctx_node, NULL);
+ ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaflist *)ctx_node)->type, val, val_len, 0, NULL,
+ format, prefix_data, LYD_HINT_DATA, ctx_node, NULL);
LOG_LOCBACK(ctx_node ? 1 : 0, 0, 0, 0);
LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup);
++(*tok_idx);
@@ -678,8 +789,8 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
}
/* new predicate */
- *pred_type = LY_PATH_PREDTYPE_POSITION;
LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup);
+ p->type = LY_PATH_PREDTYPE_POSITION;
/* syntax was already checked */
p->position = strtoull(expr->expr + expr->tok_pos[*tok_idx], (char **)&val, LY_BASE_DEC);
@@ -820,6 +931,197 @@ cleanup:
}
/**
+ * @brief Duplicate ly_path_predicate structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] pred The array of path predicates.
+ * @param[out] dup Duplicated predicates.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_dup_predicates(const struct ly_ctx *ctx, const struct ly_path_predicate *pred, struct ly_path_predicate **dup)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (!pred) {
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_CREATE_RET(ctx, *dup, LY_ARRAY_COUNT(pred), LY_EMEM);
+ LY_ARRAY_FOR(pred, u) {
+ LY_ARRAY_INCREMENT(*dup);
+ (*dup)[u].type = pred->type;
+
+ switch (pred[u].type) {
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position-predicate */
+ (*dup)[u].position = pred[u].position;
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* key-predicate or leaf-list-predicate */
+ (*dup)[u].key = pred[u].key;
+ pred[u].value.realtype->plugin->duplicate(ctx, &pred[u].value, &(*dup)[u].value);
+ LY_ATOMIC_INC_BARRIER(((struct lysc_type *)pred[u].value.realtype)->refcount);
+ break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ /* key-predicate with a variable */
+ (*dup)[u].key = pred[u].key;
+ (*dup)[u].variable = strdup(pred[u].variable);
+ break;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Appends path elements from source to destination array
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] src The source path
+ * @param[in,out] dst The destination path
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_append(const struct ly_ctx *ctx, const struct ly_path *src, struct ly_path **dst)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
+ struct ly_path *p;
+
+ if (!src) {
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_CREATE_RET(ctx, *dst, LY_ARRAY_COUNT(src), LY_EMEM);
+ LY_ARRAY_FOR(src, u) {
+ LY_ARRAY_NEW_GOTO(ctx, *dst, p, ret, cleanup);
+ p->node = src[u].node;
+ p->ext = src[u].ext;
+ LY_CHECK_GOTO(ret = ly_path_dup_predicates(ctx, src[u].predicates, &p->predicates), cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Compile deref XPath function into ly_path structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Optional context node, mandatory of @p lref.
+ * @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find
+ * the top-level node inside the extension instance instead of a module. Note that this is the case not only if
+ * the @p ctx_node is NULL, but also if the relative path starting in @p ctx_node reaches the document root
+ * via double dots.
+ * @param[in] expr Parsed path.
+ * @param[in] oper Oper option (@ref path_oper_options).
+ * @param[in] target Target option (@ref path_target_options).
+ * @param[in] format Format of the path.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in,out] tok_idx Index in @p exp, is adjusted.
+ * @param[out] path Compiled path.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_compile_deref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
+ LY_VALUE_FORMAT format, void *prefix_data, uint32_t *tok_idx, struct ly_path **path)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr expr2;
+ struct ly_path *path2 = NULL;
+ const struct lysc_node *node2;
+ const struct lysc_node_leaf *deref_leaf_node;
+ const struct lysc_type_leafref *lref;
+ uint32_t begin_token;
+
+ *path = NULL;
+
+ /* properly parsed path must always starts with 'deref' and '(' */
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_FUNCNAME));
+ assert(!strncmp(&expr->expr[expr->tok_pos[*tok_idx]], "deref", 5));
+ (*tok_idx)++;
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR1));
+ (*tok_idx)++;
+ begin_token = *tok_idx;
+
+ /* emebedded functions were already identified count tokens till ')' */
+ while (lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2) && (*tok_idx < expr->used)) {
+ (*tok_idx)++;
+ }
+
+ /* properly parsed path must have ')' within the tokens */
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2));
+
+ /* prepare expr representing just deref arg */
+ expr2.tokens = &expr->tokens[begin_token];
+ expr2.tok_pos = &expr->tok_pos[begin_token];
+ expr2.tok_len = &expr->tok_len[begin_token];
+ expr2.repeat = &expr->repeat[begin_token];
+ expr2.used = *tok_idx - begin_token;
+ expr2.size = expr->size - begin_token;
+ expr2.expr = expr->expr;
+
+ /* compile just deref arg, append it to the path and find dereferenced lref for next operations */
+ LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, ctx_node, top_ext, &expr2, oper, target, format, prefix_data,
+ &path2), cleanup);
+ node2 = path2[LY_ARRAY_COUNT(path2) - 1].node;
+ if ((node2->nodetype != LYS_LEAF) && (node2->nodetype != LYS_LEAFLIST)) {
+ LOGVAL(ctx, LYVE_XPATH, "The deref function target node \"%s\" is not leaf nor leaflist", node2->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ deref_leaf_node = (const struct lysc_node_leaf *)node2;
+ if (deref_leaf_node->type->basetype != LY_TYPE_LEAFREF) {
+ LOGVAL(ctx, LYVE_XPATH, "The deref function target node \"%s\" is not leafref", node2->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ lref = (const struct lysc_type_leafref *)deref_leaf_node->type;
+ LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
+ ly_path_free(ctx, path2);
+ path2 = NULL;
+
+ /* compile dereferenced leafref expression and append it to the path */
+ LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, node2, top_ext, lref->path, oper, target, format, prefix_data,
+ &path2), cleanup);
+ node2 = path2[LY_ARRAY_COUNT(path2) - 1].node;
+ LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
+ ly_path_free(ctx, path2);
+ path2 = NULL;
+
+ /* properly parsed path must always continue with ')' and '/' */
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2));
+ (*tok_idx)++;
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_OPER_PATH));
+ (*tok_idx)++;
+
+ /* prepare expr representing rest of the path after deref */
+ expr2.tokens = &expr->tokens[*tok_idx];
+ expr2.tok_pos = &expr->tok_pos[*tok_idx];
+ expr2.tok_len = &expr->tok_len[*tok_idx];
+ expr2.repeat = &expr->repeat[*tok_idx];
+ expr2.used = expr->used - *tok_idx;
+ expr2.size = expr->size - *tok_idx;
+ expr2.expr = expr->expr;
+
+ /* compile rest of the path and append it to the path */
+ LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, node2, top_ext, &expr2, oper, target, format, prefix_data, &path2),
+ cleanup);
+ LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
+
+cleanup:
+ ly_path_free(ctx, path2);
+ if (ret) {
+ ly_path_free(ctx, *path);
+ *path = NULL;
+ }
+ return ret;
+}
+
+/**
* @brief Compile path into ly_path structure. Any predicates of a leafref are only checked, not compiled.
*
* @param[in] ctx libyang context.
@@ -843,7 +1145,7 @@ cleanup:
*/
static LY_ERR
_ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
- const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, ly_bool lref, uint8_t oper, uint8_t target,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, ly_bool lref, uint16_t oper, uint16_t target,
ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path)
{
LY_ERR ret = LY_SUCCESS;
@@ -876,7 +1178,12 @@ _ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, con
getnext_opts = 0;
}
- if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
+ if (lref && (ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_EXTENDED) &&
+ (expr->tokens[tok_idx] == LYXP_TOKEN_FUNCNAME)) {
+ /* deref function */
+ ret = ly_path_compile_deref(ctx, ctx_node, top_ext, expr, oper, target, format, prefix_data, &tok_idx, path);
+ goto cleanup;
+ } else if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
/* absolute path */
ctx_node = NULL;
@@ -940,7 +1247,7 @@ _ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, con
ret = ly_path_compile_predicate_leafref(ctx_node, cur_node, expr, &tok_idx, format, prefix_data);
} else {
ret = ly_path_compile_predicate(ctx, cur_node, cur_mod, ctx_node, expr, &tok_idx, format, prefix_data,
- &p->predicates, &p->pred_type);
+ &p->predicates);
}
LY_CHECK_GOTO(ret, cleanup);
} while (!lyxp_next_token(NULL, expr, &tok_idx, LYXP_TOKEN_OPER_PATH));
@@ -971,7 +1278,7 @@ cleanup:
LY_ERR
ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
- const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path)
{
return _ly_path_compile(ctx, cur_mod, ctx_node, top_ext, expr, 0, oper, target, limit_access_tree, format,
@@ -980,15 +1287,15 @@ ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, cons
LY_ERR
ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const struct lysc_ext_instance *top_ext,
- const struct lyxp_expr *expr, uint8_t oper, uint8_t target, LY_VALUE_FORMAT format, void *prefix_data,
+ const struct lyxp_expr *expr, uint16_t oper, uint16_t target, LY_VALUE_FORMAT format, void *prefix_data,
struct ly_path **path)
{
return _ly_path_compile(ctx, ctx_node->module, ctx_node, top_ext, expr, 1, oper, target, 1, format, prefix_data, path);
}
LY_ERR
-ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_COUNT_TYPE *path_idx,
- struct lyd_node **match)
+ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, const struct lyxp_var *vars,
+ ly_bool with_opaq, LY_ARRAY_COUNT_TYPE *path_idx, struct lyd_node **match)
{
LY_ARRAY_COUNT_TYPE u;
struct lyd_node *prev_node = NULL, *elem, *node = NULL, *target;
@@ -1010,35 +1317,43 @@ ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, L
}
LY_ARRAY_FOR(path, u) {
- switch (path[u].pred_type) {
- case LY_PATH_PREDTYPE_POSITION:
- /* we cannot use hashes and want an instance on a specific position */
- pos = 1;
- node = NULL;
- LYD_LIST_FOR_INST(start, path[u].node, elem) {
- if (pos == path[u].predicates[0].position) {
- node = elem;
- break;
+ if (path[u].predicates) {
+ switch (path[u].predicates[0].type) {
+ case LY_PATH_PREDTYPE_POSITION:
+ /* we cannot use hashes and want an instance on a specific position */
+ pos = 1;
+ node = NULL;
+ LYD_LIST_FOR_INST(start, path[u].node, elem) {
+ if (pos == path[u].predicates[0].position) {
+ node = elem;
+ break;
+ }
+ ++pos;
}
- ++pos;
+ break;
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* we will use hashes to find one leaf-list instance */
+ LY_CHECK_RET(lyd_create_term2(path[u].node, &path[u].predicates[0].value, &target));
+ lyd_find_sibling_first(start, target, &node);
+ lyd_free_tree(target);
+ break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ case LY_PATH_PREDTYPE_LIST:
+ /* we will use hashes to find one list instance */
+ LY_CHECK_RET(lyd_create_list(path[u].node, path[u].predicates, vars, &target));
+ lyd_find_sibling_first(start, target, &node);
+ lyd_free_tree(target);
+ break;
}
- break;
- case LY_PATH_PREDTYPE_LEAFLIST:
- /* we will use hashes to find one leaf-list instance */
- LY_CHECK_RET(lyd_create_term2(path[u].node, &path[u].predicates[0].value, &target));
- lyd_find_sibling_first(start, target, &node);
- lyd_free_tree(target);
- break;
- case LY_PATH_PREDTYPE_LIST:
- /* we will use hashes to find one list instance */
- LY_CHECK_RET(lyd_create_list(path[u].node, path[u].predicates, &target));
- lyd_find_sibling_first(start, target, &node);
- lyd_free_tree(target);
- break;
- case LY_PATH_PREDTYPE_NONE:
+ } else {
/* we will use hashes to find one any/container/leaf instance */
- lyd_find_sibling_val(start, path[u].node, NULL, 0, &node);
- break;
+ if (lyd_find_sibling_val(start, path[u].node, NULL, 0, &node) && with_opaq) {
+ if (!lyd_find_sibling_opaq_next(start, path[u].node->name, &node) &&
+ (lyd_node_module(node) != path[u].node->module)) {
+ /* non-matching opaque node */
+ node = NULL;
+ }
+ }
}
if (!node) {
@@ -1085,12 +1400,12 @@ ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, L
}
LY_ERR
-ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match)
+ly_path_eval(const struct ly_path *path, const struct lyd_node *start, const struct lyxp_var *vars, struct lyd_node **match)
{
LY_ERR ret;
struct lyd_node *m;
- ret = ly_path_eval_partial(path, start, NULL, &m);
+ ret = ly_path_eval_partial(path, start, vars, 0, NULL, &m);
if (ret == LY_SUCCESS) {
/* last node was found */
@@ -1110,7 +1425,8 @@ ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct ly
LY_ERR
ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path **dup)
{
- LY_ARRAY_COUNT_TYPE u, v;
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
if (!path) {
return LY_SUCCESS;
@@ -1120,37 +1436,15 @@ ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path
LY_ARRAY_FOR(path, u) {
LY_ARRAY_INCREMENT(*dup);
(*dup)[u].node = path[u].node;
- if (path[u].predicates) {
- LY_ARRAY_CREATE_RET(ctx, (*dup)[u].predicates, LY_ARRAY_COUNT(path[u].predicates), LY_EMEM);
- (*dup)[u].pred_type = path[u].pred_type;
- LY_ARRAY_FOR(path[u].predicates, v) {
- struct ly_path_predicate *pred = &path[u].predicates[v];
-
- LY_ARRAY_INCREMENT((*dup)[u].predicates);
- switch (path[u].pred_type) {
- case LY_PATH_PREDTYPE_POSITION:
- /* position-predicate */
- (*dup)[u].predicates[v].position = pred->position;
- break;
- case LY_PATH_PREDTYPE_LIST:
- case LY_PATH_PREDTYPE_LEAFLIST:
- /* key-predicate or leaf-list-predicate */
- (*dup)[u].predicates[v].key = pred->key;
- pred->value.realtype->plugin->duplicate(ctx, &pred->value, &(*dup)[u].predicates[v].value);
- LY_ATOMIC_INC_BARRIER(((struct lysc_type *)pred->value.realtype)->refcount);
- break;
- case LY_PATH_PREDTYPE_NONE:
- break;
- }
- }
- }
+ (*dup)[u].ext = path[u].ext;
+ LY_CHECK_RET(ret = ly_path_dup_predicates(ctx, path[u].predicates, &(*dup)[u].predicates), ret);
}
return LY_SUCCESS;
}
void
-ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_type, struct ly_path_predicate *predicates)
+ly_path_predicates_free(const struct ly_ctx *ctx, struct ly_path_predicate *predicates)
{
LY_ARRAY_COUNT_TYPE u;
struct lysf_ctx fctx = {.ctx = (struct ly_ctx *)ctx};
@@ -1160,9 +1454,8 @@ ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_ty
}
LY_ARRAY_FOR(predicates, u) {
- switch (pred_type) {
+ switch (predicates[u].type) {
case LY_PATH_PREDTYPE_POSITION:
- case LY_PATH_PREDTYPE_NONE:
/* nothing to free */
break;
case LY_PATH_PREDTYPE_LIST:
@@ -1172,6 +1465,9 @@ ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_ty
lysc_type_free(&fctx, (struct lysc_type *)predicates[u].value.realtype);
}
break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ free(predicates[u].variable);
+ break;
}
}
LY_ARRAY_FREE(predicates);
@@ -1187,7 +1483,7 @@ ly_path_free(const struct ly_ctx *ctx, struct ly_path *path)
}
LY_ARRAY_FOR(path, u) {
- ly_path_predicates_free(ctx, path[u].pred_type, path[u].predicates);
+ ly_path_predicates_free(ctx, path[u].predicates);
}
LY_ARRAY_FREE(path);
}
diff --git a/src/path.h b/src/path.h
index ca1c84a..9a9e342 100644
--- a/src/path.h
+++ b/src/path.h
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Path structure and manipulation routines.
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 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.
@@ -29,24 +29,25 @@ struct lysc_node;
struct lyxp_expr;
enum ly_path_pred_type {
- LY_PATH_PREDTYPE_NONE = 0, /**< no predicate */
LY_PATH_PREDTYPE_POSITION, /**< position predicate - [2] */
LY_PATH_PREDTYPE_LIST, /**< keys predicate - [key1='val1'][key2='val2']... */
- LY_PATH_PREDTYPE_LEAFLIST /**< leaflist value predicate - [.='value'] */
+ LY_PATH_PREDTYPE_LEAFLIST, /**< leaflist value predicate - [.='value'] */
+ LY_PATH_PREDTYPE_LIST_VAR /**< keys predicate with variable instead of value - [key1=$USER]... */
};
/**
* @brief Structure for simple path predicate.
*/
struct ly_path_predicate {
+ enum ly_path_pred_type type; /**< Predicate type (see YANG ABNF) */
union {
- uint64_t position; /**< position value for the position-predicate */
-
+ uint64_t position; /**< position value for the position-predicate */
struct {
- const struct lysc_node *key; /**< key node of the predicate, NULL in
- case of a leaf-list predicate */
- struct lyd_value value; /**< value representation according to the
- key's type, its realtype is allocated */
+ const struct lysc_node *key; /**< key node of the predicate, NULL in case of a leaf-list predicate */
+ union {
+ struct lyd_value value; /**< stored value representation according to the key's type (realtype ref) */
+ char *variable; /**< XPath variable used instead of the value */
+ };
};
};
};
@@ -61,7 +62,6 @@ struct ly_path {
- is inner node - path is relative */
const struct lysc_ext_instance *ext; /**< Extension instance of @p node, if any */
struct ly_path_predicate *predicates; /**< [Sized array](@ref sizedarrays) of the path segment's predicates */
- enum ly_path_pred_type pred_type; /**< Predicate type (see YANG ABNF) */
};
/**
@@ -76,9 +76,10 @@ struct ly_path {
* @defgroup path_prefix_options Path prefix options.
* @{
*/
-#define LY_PATH_PREFIX_OPTIONAL 0x10 /**< prefixes in the path are optional */
+#define LY_PATH_PREFIX_OPTIONAL 0x10 /**< prefixes in the path are optional (XML path) */
#define LY_PATH_PREFIX_MANDATORY 0x20 /**< prefixes in the path are mandatory (XML instance-identifier) */
-#define LY_PATH_PREFIX_STRICT_INHERIT 0x30 /**< prefixes in the path are mandatory in case they differ from the
+#define LY_PATH_PREFIX_FIRST 0x40 /**< prefixes in the path are mandatory only in the first node of absolute path (JSON path) */
+#define LY_PATH_PREFIX_STRICT_INHERIT 0x80 /**< prefixes in the path are mandatory in case they differ from the
previous prefixes, otherwise they are prohibited (JSON instance-identifier) */
/** @} */
@@ -86,10 +87,10 @@ struct ly_path {
* @defgroup path_pred_options Path predicate options.
* @{
*/
-#define LY_PATH_PRED_KEYS 0x40 /* expected predicate only - [node='value']* */
-#define LY_PATH_PRED_SIMPLE 0x80 /* expected predicates - [node='value']*; [.='value']; [1] */
-#define LY_PATH_PRED_LEAFREF 0xC0 /* expected predicates only leafref - [node=current()/../../../node/node];
- at least 1 ".." and 1 "node" after */
+#define LY_PATH_PRED_KEYS 0x0100 /** expected predicate only - [node='value']* */
+#define LY_PATH_PRED_SIMPLE 0x0200 /** expected predicates - ( [node='value'] | [node=$VAR] )*; [.='value']; [1] */
+#define LY_PATH_PRED_LEAFREF 0x0400 /** expected predicates only leafref - [node=current()/../../../node/node];
+ at least 1 ".." and 1 "node" after */
/** @} */
/**
@@ -107,7 +108,7 @@ struct ly_path {
* @return LY_ERR value.
*/
LY_ERR ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *str_path, size_t path_len,
- ly_bool lref, uint8_t begin, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr);
+ ly_bool lref, uint16_t begin, uint16_t prefix, uint16_t pred, struct lyxp_expr **expr);
/**
* @brief Parse predicate into XPath token structure and perform all additional checks.
@@ -122,7 +123,7 @@ LY_ERR ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
* @return LY_ERR value.
*/
LY_ERR ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const char *str_path,
- size_t path_len, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr);
+ size_t path_len, uint16_t prefix, uint16_t pred, struct lyxp_expr **expr);
/**
* @defgroup path_oper_options Path operation options.
@@ -162,7 +163,7 @@ LY_ERR ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node
* @return LY_ERR value.
*/
LY_ERR ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
- const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path);
/**
@@ -182,7 +183,7 @@ LY_ERR ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mo
* @return LY_ERR value.
*/
LY_ERR ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
- const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path);
/**
@@ -198,18 +199,19 @@ LY_ERR ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node
* @param[in] format Format of the path.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
* @param[out] predicates Compiled predicates.
- * @param[out] pred_type Type of the compiled predicate(s).
* @return LY_ERR value.
*/
LY_ERR ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
const struct lysc_node *ctx_node, const struct lyxp_expr *expr, uint32_t *tok_idx, LY_VALUE_FORMAT format,
- void *prefix_data, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type);
+ void *prefix_data, struct ly_path_predicate **predicates);
/**
* @brief Resolve at least partially the target defined by ly_path structure. Not supported for leafref!
*
* @param[in] path Path structure specifying the target.
* @param[in] start Starting node for relative paths, can be any for absolute paths.
+ * @param[in] vars Array of defined variables to use in predicates, may be NULL.
+ * @param[in] with_opaq Whether to consider opaque nodes or not.
* @param[out] path_idx Last found path segment index, can be NULL, set to 0 if not found.
* @param[out] match Last found matching node, can be NULL, set to NULL if not found.
* @return LY_ENOTFOUND if no nodes were found,
@@ -217,20 +219,22 @@ LY_ERR ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_nod
* @return LY_SUCCESS when the last node in the path was found,
* @return LY_ERR on another error.
*/
-LY_ERR ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_COUNT_TYPE *path_idx,
- struct lyd_node **match);
+LY_ERR ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, const struct lyxp_var *vars,
+ ly_bool with_opaq, LY_ARRAY_COUNT_TYPE *path_idx, struct lyd_node **match);
/**
* @brief Resolve the target defined by ly_path structure. Not supported for leafref!
*
* @param[in] path Path structure specifying the target.
* @param[in] start Starting node for relative paths, can be any for absolute paths.
+ * @param[in] vars Array of defined variables to use in predicates, may be NULL.
* @param[out] match Found matching node, can be NULL, set to NULL if not found.
* @return LY_ENOTFOUND if no nodes were found,
* @return LY_SUCCESS when the last node in the path was found,
* @return LY_ERR on another error.
*/
-LY_ERR ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match);
+LY_ERR ly_path_eval(const struct ly_path *path, const struct lyd_node *start, const struct lyxp_var *vars,
+ struct lyd_node **match);
/**
* @brief Duplicate ly_path structure.
@@ -246,11 +250,9 @@ LY_ERR ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct
* @brief Free ly_path_predicate structure.
*
* @param[in] ctx libyang context.
- * @param[in] pred_type Predicate type.
* @param[in] predicates Predicates ([sized array](@ref sizedarrays)) to free.
*/
-void ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_type,
- struct ly_path_predicate *predicates);
+void ly_path_predicates_free(const struct ly_ctx *ctx, struct ly_path_predicate *predicates);
/**
* @brief Free ly_path structure.
diff --git a/src/plugins.c b/src/plugins.c
index d62da1c..011e464 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -70,6 +70,7 @@ extern const struct lyplg_type_record plugins_ipv6_prefix[];
* ietf-yang-types
*/
extern const struct lyplg_type_record plugins_date_and_time[];
+extern const struct lyplg_type_record plugins_hex_string[];
extern const struct lyplg_type_record plugins_xpath10[];
/*
@@ -475,6 +476,7 @@ lyplg_init(void)
/* ietf-yang-types */
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_date_and_time), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_hex_string), error);
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_xpath10), error);
/* ietf-netconf-acm */
diff --git a/src/plugins_exts.c b/src/plugins_exts.c
index 00970fa..f8cdeff 100644
--- a/src/plugins_exts.c
+++ b/src/plugins_exts.c
@@ -94,6 +94,8 @@ lys_compile_ext_instance_stmt(struct lysc_ctx *ctx, const void *parsed, struct l
ly_bool length_restr = 0;
LY_DATA_TYPE basetype;
+ assert(parsed);
+
/* compilation wthout any storage */
if (substmt->stmt == LY_STMT_IF_FEATURE) {
ly_bool enabled;
@@ -106,8 +108,8 @@ lys_compile_ext_instance_stmt(struct lysc_ctx *ctx, const void *parsed, struct l
}
}
- if (!substmt->storage || !parsed) {
- /* nothing to store or nothing parsed to compile */
+ if (!substmt->storage) {
+ /* nothing to store */
goto cleanup;
}
@@ -268,6 +270,7 @@ lys_compile_ext_instance_stmt(struct lysc_ctx *ctx, const void *parsed, struct l
/* compile */
LY_CHECK_GOTO(rc = lys_compile_type(ctx, NULL, flags, ext->def->name, ptype, substmt->storage, &units, NULL), cleanup);
+ LY_ATOMIC_INC_BARRIER((*(struct lysc_type **)substmt->storage)->refcount);
break;
}
case LY_STMT_EXTENSION_INSTANCE: {
@@ -335,8 +338,8 @@ lyplg_ext_compile_extension_instance(struct lysc_ctx *ctx, const struct lysp_ext
stmtp = extp->substmts[u].stmt;
storagep = *(void **)extp->substmts[u].storage;
- if (ly_set_contains(&storagep_compiled, storagep, NULL)) {
- /* this parsed statement has already been compiled (for example, if it is a linked list of parsed nodes) */
+ if (!storagep || ly_set_contains(&storagep_compiled, storagep, NULL)) {
+ /* nothing parsed or already compiled (for example, if it is a linked list of parsed nodes) */
continue;
}
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
index f005391..a2783ff 100644
--- a/src/plugins_exts.h
+++ b/src/plugins_exts.h
@@ -594,6 +594,14 @@ LIBYANG_API_DECL void lyplg_ext_compile_log_path(const char *path, const struct
LY_LOG_LEVEL level, LY_ERR err_no, const char *format, ...);
/**
+ * @brief Log a message from an extension plugin using the compiled extension instance and a generated error item.
+ *
+ * @param[in] err Error item to log.
+ * @param[in] ext Compiled extension instance.
+ */
+LIBYANG_API_DEF void lyplg_ext_compile_log_err(const struct ly_err_item *err, const struct lysc_ext_instance *ext);
+
+/**
* @brief YANG schema compilation context getter for libyang context.
*
* @param[in] ctx YANG schema compilation context.
@@ -915,7 +923,10 @@ typedef void (*lyplg_ext_compile_free_clb)(const struct ly_ctx *ctx, struct lysc
LIBYANG_API_DECL void lyplg_ext_cfree_instance_substatements(const struct ly_ctx *ctx, struct lysc_ext_substmt *substmts);
/**
- * @brief Extension plugin implementing various aspects of a YANG extension
+ * @brief Extension plugin implementing various aspects of a YANG extension.
+ *
+ * Every plugin should have at least either ::parse() or ::compile() callback defined but other than that **all**
+ * the callbacks are **optional**.
*/
struct lyplg_ext {
const char *id; /**< plugin identification (mainly for distinguish incompatible versions
@@ -1030,11 +1041,10 @@ LIBYANG_API_DECL LY_ERR lyplg_ext_schema_mount_get_parent_ref(const struct lysc_
/**
* @brief Allocate a new context for a particular instance of the yangmnt:mount-point extension.
- * Caller is responsible for destroying the resulting context.
+ * Caller is responsible for **freeing** the created context.
*
* @param[in] ext Compiled extension instance.
- * @param[out] ctx A context with modules loaded from the list found in
- * the extension data.
+ * @param[out] ctx Context with modules loaded from the list found in the extension data.
* @return LY_ERR value.
*/
LIBYANG_API_DECL LY_ERR lyplg_ext_schema_mount_create_context(const struct lysc_ext_instance *ext, struct ly_ctx **ctx);
diff --git a/src/plugins_exts/nacm.c b/src/plugins_exts/nacm.c
index 5ab8daa..df49721 100644
--- a/src/plugins_exts/nacm.c
+++ b/src/plugins_exts/nacm.c
@@ -101,7 +101,7 @@ nacm_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
/* check for duplication */
LY_ARRAY_FOR(parent->exts, u) {
- if ((&parent->exts[u] != ext) && parent->exts[u].record && (parent->exts[u].record->plugin.id == ext->record->plugin.id)) {
+ if ((&parent->exts[u] != ext) && parent->exts[u].record && !strcmp(parent->exts[u].record->plugin.id, ext->record->plugin.id)) {
/* duplication of a NACM extension on a single node
* We check for all NACM plugins since we want to catch even the situation that there is default-deny-all
* AND default-deny-write */
diff --git a/src/plugins_exts/schema_mount.c b/src/plugins_exts/schema_mount.c
index 9800760..cc63431 100644
--- a/src/plugins_exts/schema_mount.c
+++ b/src/plugins_exts/schema_mount.c
@@ -982,7 +982,7 @@ schema_mount_validate(struct lysc_ext_instance *ext, struct lyd_node *sibling, c
if (!err) {
lyplg_ext_compile_log(NULL, ext, LY_LLERR, ret, "Unknown validation error (err code %d).", ret);
} else {
- lyplg_ext_compile_log_path(err->path, ext, LY_LLERR, err->no, "%s", err->msg);
+ lyplg_ext_compile_log_err(err, ext);
}
goto cleanup;
}
diff --git a/src/plugins_types.c b/src/plugins_types.c
index cb4b896..de7273d 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -224,24 +224,52 @@ ly_schema_resolved_get_prefix(const struct lys_module *mod, void *prefix_data)
}
/**
- * @brief Simply return module local prefix. Also, store the module in a set.
+ * @brief Get prefix for XML print.
+ *
+ * @param[in] mod Module whose prefix to get.
+ * @param[in,out] prefix_data Set of used modules in the print. If @p mod is found in this set, no string (prefix) is
+ * returned.
+ * @return Prefix to print, may be NULL if the default namespace should be used.
*/
static const char *
ly_xml_get_prefix(const struct lys_module *mod, void *prefix_data)
{
- struct ly_set *ns_list = prefix_data;
+ struct ly_set *mods = prefix_data;
+ uint32_t i;
+
+ /* first is the local module */
+ assert(mods->count);
+ if (mods->objs[0] == mod) {
+ return NULL;
+ }
- LY_CHECK_RET(ly_set_add(ns_list, (void *)mod, 0, NULL), NULL);
+ /* check for duplicates in the rest of the modules and add there */
+ for (i = 1; i < mods->count; ++i) {
+ if (mods->objs[i] == mod) {
+ break;
+ }
+ }
+ if (i == mods->count) {
+ LY_CHECK_RET(ly_set_add(mods, (void *)mod, 1, NULL), NULL);
+ }
+
+ /* return the prefix */
return mod->prefix;
}
/**
- * @brief Simply return module name.
+ * @brief Get prefix for JSON print.
+ *
+ * @param[in] mod Module whose prefix to get.
+ * @param[in] prefix_data Current local module, may be NULL. If it matches @p mod, no string (preifx) is returned.
+ * @return Prefix (module name) to print, may be NULL if the default module should be used.
*/
static const char *
-ly_json_get_prefix(const struct lys_module *mod, void *UNUSED(prefix_data))
+ly_json_get_prefix(const struct lys_module *mod, void *prefix_data)
{
- return mod->name;
+ const struct lys_module *local_mod = prefix_data;
+
+ return (local_mod == mod) ? NULL : mod->name;
}
const char *
@@ -279,10 +307,6 @@ lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_simple(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
if (val1->_canonical == val2->_canonical) {
return LY_SUCCESS;
}
@@ -783,6 +807,8 @@ lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, size_t value_
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *exp = NULL;
uint32_t prefix_opt = 0;
+ struct ly_err_item *e;
+ const char *err_fmt = NULL;
LY_CHECK_ARG_RET(ctx, ctx, value, ctx_node, path, err, LY_EINVAL);
@@ -803,31 +829,43 @@ lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, size_t value_
break;
}
+ /* remember the current last error */
+ e = ly_err_last(ctx);
+
/* parse the value */
ret = ly_path_parse(ctx, ctx_node, value, value_len, 0, LY_PATH_BEGIN_ABSOLUTE, prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
if (ret) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
- "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, value);
+ err_fmt = "Invalid instance-identifier \"%.*s\" value - syntax error%s%s";
goto cleanup;
}
if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
/* implement all prefixes */
- LY_CHECK_GOTO(ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, unres, NULL), cleanup);
+ ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, unres, NULL);
+ if (ret) {
+ err_fmt = "Failed to implement a module referenced by instance-identifier \"%.*s\"%s%s";
+ goto cleanup;
+ }
}
/* resolve it on schema tree */
ret = ly_path_compile(ctx, NULL, ctx_node, NULL, exp, (ctx_node->flags & LYS_IS_OUTPUT) ?
LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, 1, format, prefix_data, path);
if (ret) {
- ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL,
- "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, value);
+ err_fmt = "Invalid instance-identifier \"%.*s\" value - semantic error%s%s";
goto cleanup;
}
cleanup:
lyxp_expr_free(ctx, exp);
if (ret) {
+ /* generate error, spend the context error, if any */
+ e = e ? e->next : ly_err_last(ctx);
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, err_fmt, (int)value_len, value, e ? ": " : ".", e ? e->msg : "");
+ if (e) {
+ ly_err_clean((struct ly_ctx *)ctx, e);
+ }
+
ly_path_free(ctx, *path);
*path = NULL;
}
@@ -1016,6 +1054,9 @@ lyplg_type_resolve_leafref(const struct lysc_type_leafref *lref, const struct ly
if (set.val.nodes[i].type != LYXP_NODE_ELEM) {
continue;
}
+ if (((struct lyd_node_term *)set.val.nodes[i].node)->value.realtype != value->realtype) {
+ continue;
+ }
if (!lref->plugin->compare(&((struct lyd_node_term *)set.val.nodes[i].node)->value, value)) {
break;
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 3ec1d3b..b4ecd43 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -367,7 +367,7 @@ LIBYANG_API_DEF LY_ERR lyplg_type_xpath10_print_token(const char *token, uint16_
* @param[in] format Format of the prefix (::lyplg_type_print_clb's format parameter).
* @param[in] prefix_data Format-specific data (::lyplg_type_print_clb's prefix_data parameter).
* @return Module prefix to print.
- * @return NULL on error.
+ * @return NULL on using the current module/namespace.
*/
LIBYANG_API_DECL const char *lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data);
@@ -473,6 +473,7 @@ LIBYANG_API_DECL LY_ERR lyplg_type_print_xpath10_value(const struct lyd_value_xp
value after calling the type's store callback with this option. */
#define LYPLG_TYPE_STORE_IMPLEMENT 0x02 /**< If a foreign module is needed to be implemented to successfully instantiate
the value, make the module implemented. */
+#define LYPLG_TYPE_STORE_IS_UTF8 0x04 /**< The value is guaranteed to be a valid UTF-8 string, if applicable for the type. */
/** @} plugintypestoreopts */
/**
@@ -522,7 +523,7 @@ LIBYANG_API_DECL typedef LY_ERR (*lyplg_type_store_clb)(const struct ly_ctx *ctx
* @param[in] tree External data tree (e.g. when validating RPC/Notification) with possibly referenced data.
* @param[in,out] storage Storage of the value successfully filled by ::lyplg_type_store_clb. May be modified.
* @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic
- * error message is prepared instead. The error structure can be created by ::ly_err_new().
+ * error message is prepared instead. The error structure can be created by ::ly_err_new().
* @return LY_SUCCESS on success,
* @return LY_ERR value on error.
*/
@@ -532,12 +533,12 @@ LIBYANG_API_DECL typedef LY_ERR (*lyplg_type_validate_clb)(const struct ly_ctx *
/**
* @brief Callback for comparing 2 values of the same type.
*
- * In case the value types (::lyd_value.realtype) are different, ::LY_ENOT must always be returned.
- * It can be assumed that the same context (dictionary) was used for storing both values.
+ * It can be assumed that the same context (dictionary) was used for storing both values and the realtype
+ * member of both the values is the same.
*
* @param[in] val1 First value to compare.
* @param[in] val2 Second value to compare.
- * @return LY_SUCCESS if values are same (according to the type's definition of being same).
+ * @return LY_SUCCESS if values are considered equal.
* @return LY_ENOT if values differ.
*/
typedef LY_ERR (*lyplg_type_compare_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
diff --git a/src/plugins_types/binary.c b/src/plugins_types/binary.c
index 519ec2e..542b54b 100644
--- a/src/plugins_types/binary.c
+++ b/src/plugins_types/binary.c
@@ -340,10 +340,6 @@ lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *
{
struct lyd_value_binary *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/bits.c b/src/plugins_types/bits.c
index 04adace..2f87ed3 100644
--- a/src/plugins_types/bits.c
+++ b/src/plugins_types/bits.c
@@ -373,10 +373,6 @@ lyplg_type_compare_bits(const struct lyd_value *val1, const struct lyd_value *va
struct lyd_value_bits *v1, *v2;
struct lysc_type_bits *type_bits = (struct lysc_type_bits *)val1->realtype;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/boolean.c b/src/plugins_types/boolean.c
index f8b19f6..415b8b9 100644
--- a/src/plugins_types/boolean.c
+++ b/src/plugins_types/boolean.c
@@ -106,10 +106,6 @@ cleanup:
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_boolean(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
if (val1->boolean != val2->boolean) {
return LY_ENOT;
}
diff --git a/src/plugins_types/date_and_time.c b/src/plugins_types/date_and_time.c
index 5ccb86d..594b0a8 100644
--- a/src/plugins_types/date_and_time.c
+++ b/src/plugins_types/date_and_time.c
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief ietf-yang-types date-and-time type plugin.
*
- * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ * Copyright (c) 2019-2023 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.
@@ -53,7 +53,6 @@ lyplg_type_store_date_and_time(const struct ly_ctx *ctx, const struct lysc_type
LY_ERR ret = LY_SUCCESS;
struct lysc_type_str *type_dat = (struct lysc_type_str *)type;
struct lyd_value_date_and_time *val;
- struct tm tm;
uint32_t i;
char c;
@@ -111,34 +110,17 @@ lyplg_type_store_date_and_time(const struct ly_ctx *ctx, const struct lysc_type
ret = lyplg_type_validate_patterns(type_dat->patterns, value, value_len, err);
LY_CHECK_GOTO(ret, cleanup);
- /* pattern validation succeeded, convert to UNIX time and fractions of second */
+ /* convert to UNIX time and fractions of second */
ret = ly_time_str2time(value, &val->time, &val->fractions_s);
- LY_CHECK_GOTO(ret, cleanup);
+ if (ret) {
+ ret = ly_err_new(err, ret, 0, NULL, NULL, "%s", ly_last_errmsg());
+ goto cleanup;
+ }
-#ifdef HAVE_TIME_H_TIMEZONE
if (!strncmp(((char *)value + value_len) - 6, "-00:00", 6)) {
- /* unknown timezone, move the timestamp to UTC */
- tzset();
- val->time += (time_t)timezone;
+ /* unknown timezone */
val->unknown_tz = 1;
-
- /* DST may apply, adjust accordingly */
- if (!localtime_r(&val->time, &tm)) {
- ret = ly_err_new(err, LY_ESYS, LYVE_DATA, NULL, NULL, "localtime_r() call failed (%s).", strerror(errno));
- goto cleanup;
- } else if (tm.tm_isdst < 0) {
- ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Failed to get DST information.");
- goto cleanup;
- }
- if (tm.tm_isdst) {
- /* move an hour back */
- val->time -= 3600;
- }
}
-#else
- (void)tm;
- val->unknown_tz = 1;
-#endif
if (format == LY_VALUE_CANON) {
/* store canonical value */
@@ -171,10 +153,6 @@ lyplg_type_compare_date_and_time(const struct lyd_value *val1, const struct lyd_
{
struct lyd_value_date_and_time *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
@@ -199,6 +177,7 @@ lyplg_type_print_date_and_time(const struct ly_ctx *ctx, const struct lyd_value
void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
{
struct lyd_value_date_and_time *val;
+ struct tm tm;
char *ret;
LYD_VALUE_GET(value, val);
@@ -229,14 +208,20 @@ lyplg_type_print_date_and_time(const struct ly_ctx *ctx, const struct lyd_value
/* generate canonical value if not already */
if (!value->_canonical) {
- /* get the canonical value */
- if (ly_time_time2str(val->time, val->fractions_s, &ret)) {
- return NULL;
- }
-
if (val->unknown_tz) {
- /* date and time is correct, fix only the timezone */
- strcpy((ret + strlen(ret)) - 6, "-00:00");
+ /* ly_time_time2str but always using GMT */
+ if (!gmtime_r(&val->time, &tm)) {
+ return NULL;
+ }
+ if (asprintf(&ret, "%04d-%02d-%02dT%02d:%02d:%02d%s%s-00:00",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+ val->fractions_s ? "." : "", val->fractions_s ? val->fractions_s : "") == -1) {
+ return NULL;
+ }
+ } else {
+ if (ly_time_time2str(val->time, val->fractions_s, &ret)) {
+ return NULL;
+ }
}
/* store it */
diff --git a/src/plugins_types/decimal64.c b/src/plugins_types/decimal64.c
index 25a88d9..cbd6934 100644
--- a/src/plugins_types/decimal64.c
+++ b/src/plugins_types/decimal64.c
@@ -161,10 +161,6 @@ cleanup:
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_decimal64(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
/* if type is the same, the fraction digits are, too */
if (val1->dec64 != val2->dec64) {
return LY_ENOT;
diff --git a/src/plugins_types/hex_string.c b/src/plugins_types/hex_string.c
new file mode 100644
index 0000000..f72ce66
--- /dev/null
+++ b/src/plugins_types/hex_string.c
@@ -0,0 +1,171 @@
+/**
+ * @file hex_string.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Built-in hex-string and associated types plugin.
+ *
+ * Copyright (c) 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "plugins_types.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesHexString phys-address, mac-address, hex-string, uuid (ietf-yang-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string itself |
+ */
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_hex_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ uint32_t i;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* make a copy, it is needed for canonization */
+ if ((format != LY_VALUE_CANON) && !(options & LYPLG_TYPE_STORE_DYNAMIC)) {
+ value = strndup(value, value_len);
+ LY_CHECK_ERR_GOTO(!value, ret = LY_EMEM, cleanup);
+ options |= LYPLG_TYPE_STORE_DYNAMIC;
+ }
+
+ /* store canonical value */
+ if (format != LY_VALUE_CANON) {
+ /* make lowercase and store, the value must be dynamic */
+ for (i = 0; i < value_len; ++i) {
+ ((char *)value)[i] = tolower(((char *)value)[i]);
+ }
+
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ /* store directly */
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for string type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_hex_string[] = {
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "phys-address",
+
+ .plugin.id = "libyang 2 - hex-string, version 1",
+ .plugin.store = lyplg_type_store_hex_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "mac-address",
+
+ .plugin.id = "libyang 2 - hex-string, version 1",
+ .plugin.store = lyplg_type_store_hex_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "hex-string",
+
+ .plugin.id = "libyang 2 - hex-string, version 1",
+ .plugin.store = lyplg_type_store_hex_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "uuid",
+
+ .plugin.id = "libyang 2 - hex-string, version 1",
+ .plugin.store = lyplg_type_store_hex_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/identityref.c b/src/plugins_types/identityref.c
index 8b7985d..588a969 100644
--- a/src/plugins_types/identityref.c
+++ b/src/plugins_types/identityref.c
@@ -1,9 +1,10 @@
/**
* @file identityref.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Built-in identityref type plugin.
*
- * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ * Copyright (c) 2019-2023 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.
@@ -50,8 +51,16 @@ static LY_ERR
identityref_ident2str(const struct lysc_ident *ident, LY_VALUE_FORMAT format, void *prefix_data, char **str, size_t *str_len)
{
int len;
+ const char *prefix;
- len = asprintf(str, "%s:%s", lyplg_type_get_prefix(ident->module, format, prefix_data), ident->name);
+ /* get the prefix, may be NULL for no prefix and the default namespace */
+ prefix = lyplg_type_get_prefix(ident->module, format, prefix_data);
+
+ if (prefix) {
+ len = asprintf(str, "%s:%s", prefix, ident->name);
+ } else {
+ len = asprintf(str, "%s", ident->name);
+ }
if (len == -1) {
return LY_EMEM;
}
@@ -270,8 +279,11 @@ lyplg_type_store_identityref(const struct ly_ctx *ctx, const struct lysc_type *t
}
} else {
/* JSON format with prefix is the canonical one */
- ret = identityref_ident2str(ident, LY_VALUE_JSON, NULL, &canon, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ if (asprintf(&canon, "%s:%s", ident->module->name, ident->name) == -1) {
+ LOGMEM(ctx);
+ ret = LY_EMEM;
+ goto cleanup;
+ }
ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
LY_CHECK_GOTO(ret, cleanup);
@@ -291,10 +303,6 @@ cleanup:
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_identityref(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
if (val1->ident == val2->ident) {
return LY_SUCCESS;
}
@@ -307,7 +315,7 @@ lyplg_type_print_identityref(const struct ly_ctx *UNUSED(ctx), const struct lyd_
{
char *ret;
- if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
+ if (format == LY_VALUE_CANON) {
if (dynamic) {
*dynamic = 0;
}
diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c
index d151d0a..c15ff64 100644
--- a/src/plugins_types/instanceid.c
+++ b/src/plugins_types/instanceid.c
@@ -50,12 +50,19 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u, v;
char *result = NULL, quot;
- const struct lys_module *mod = NULL;
+ const struct lys_module *mod = NULL, *local_mod = NULL;
+ struct ly_set *mods = NULL;
ly_bool inherit_prefix = 0, d;
const char *strval;
switch (format) {
case LY_VALUE_XML:
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+
+ /* fallthrough */
case LY_VALUE_SCHEMA:
case LY_VALUE_SCHEMA_RESOLVED:
/* everything is prefixed */
@@ -84,9 +91,7 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr
LY_ARRAY_FOR(path[u].predicates, v) {
struct ly_path_predicate *pred = &path[u].predicates[v];
- switch (path[u].pred_type) {
- case LY_PATH_PREDTYPE_NONE:
- break;
+ switch (pred->type) {
case LY_PATH_PREDTYPE_POSITION:
/* position predicate */
ret = ly_strcat(&result, "[%" PRIu64 "]", pred->position);
@@ -127,6 +132,10 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr
free((char *)strval);
}
break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ LOGINT(path[u].node->module->ctx);
+ ret = LY_EINT;
+ goto cleanup;
}
LY_CHECK_GOTO(ret, cleanup);
@@ -134,6 +143,9 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr
}
cleanup:
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
if (ret) {
free(result);
} else {
@@ -162,7 +174,7 @@ lyplg_type_store_instanceid(const struct ly_ctx *ctx, const struct lysc_type *ty
/* compile instance-identifier into path */
if (format == LY_VALUE_LYB) {
- /* The @p value in LYB format is the same as in JSON format. */
+ /* value in LYB format is the same as in JSON format. */
ret = lyplg_type_lypath_new(ctx, value, value_len, options, LY_VALUE_JSON, prefix_data, ctx_node,
unres, &path, err);
} else {
@@ -231,7 +243,7 @@ lyplg_type_validate_instanceid(const struct ly_ctx *ctx, const struct lysc_type
}
/* find the target in data */
- if ((ret = ly_path_eval(storage->target, tree, NULL))) {
+ if ((ret = ly_path_eval(storage->target, tree, NULL, NULL))) {
value = lyplg_type_print_instanceid(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
path = lyd_path(ctx_node, LYD_PATH_STD, NULL, 0);
return ly_err_new(err, ret, LYVE_DATA, path, strdup("instance-required"), LY_ERRMSG_NOINST, value);
@@ -245,10 +257,6 @@ lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_val
{
LY_ARRAY_COUNT_TYPE u, v;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
if (val1 == val2) {
return LY_SUCCESS;
} else if (LY_ARRAY_COUNT(val1->target) != LY_ARRAY_COUNT(val2->target)) {
@@ -259,37 +267,43 @@ lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_val
struct ly_path *s1 = &val1->target[u];
struct ly_path *s2 = &val2->target[u];
- if ((s1->node != s2->node) || (s1->pred_type != s2->pred_type) ||
- (s1->predicates && (LY_ARRAY_COUNT(s1->predicates) != LY_ARRAY_COUNT(s2->predicates)))) {
+ if ((s1->node != s2->node) || (s1->predicates && (LY_ARRAY_COUNT(s1->predicates) != LY_ARRAY_COUNT(s2->predicates)))) {
return LY_ENOT;
}
- if (s1->predicates) {
- LY_ARRAY_FOR(s1->predicates, v) {
- struct ly_path_predicate *pred1 = &s1->predicates[v];
- struct ly_path_predicate *pred2 = &s2->predicates[v];
-
- switch (s1->pred_type) {
- case LY_PATH_PREDTYPE_NONE:
- break;
- case LY_PATH_PREDTYPE_POSITION:
- /* position predicate */
- if (pred1->position != pred2->position) {
- return LY_ENOT;
- }
- break;
- case LY_PATH_PREDTYPE_LIST:
- /* key-predicate */
- if ((pred1->key != pred2->key) ||
- ((struct lysc_node_leaf *)pred1->key)->type->plugin->compare(&pred1->value, &pred2->value)) {
- return LY_ENOT;
- }
- break;
- case LY_PATH_PREDTYPE_LEAFLIST:
- /* leaf-list predicate */
- if (((struct lysc_node_leaflist *)s1->node)->type->plugin->compare(&pred1->value, &pred2->value)) {
- return LY_ENOT;
- }
+ LY_ARRAY_FOR(s1->predicates, v) {
+ struct ly_path_predicate *pred1 = &s1->predicates[v];
+ struct ly_path_predicate *pred2 = &s2->predicates[v];
+
+ if (pred1->type != pred2->type) {
+ return LY_ENOT;
+ }
+
+ switch (pred1->type) {
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position predicate */
+ if (pred1->position != pred2->position) {
+ return LY_ENOT;
}
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ /* key-predicate */
+ if ((pred1->key != pred2->key) ||
+ ((struct lysc_node_leaf *)pred1->key)->type->plugin->compare(&pred1->value, &pred2->value)) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* leaf-list predicate */
+ if (((struct lysc_node_leaflist *)s1->node)->type->plugin->compare(&pred1->value, &pred2->value)) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ /* key-predicate with a variable */
+ if ((pred1->key != pred2->key) || strcmp(pred1->variable, pred2->variable)) {
+ return LY_ENOT;
+ }
+ break;
}
}
}
diff --git a/src/plugins_types/instanceid_keys.c b/src/plugins_types/instanceid_keys.c
index 0cd08f7..ab7751c 100644
--- a/src/plugins_types/instanceid_keys.c
+++ b/src/plugins_types/instanceid_keys.c
@@ -67,10 +67,18 @@ instanceid_keys_print_value(const struct lyd_value_instance_identifier_keys *val
void *mem;
const char *cur_exp_ptr;
ly_bool is_nt;
- const struct lys_module *context_mod = NULL;
+ const struct lys_module *context_mod = NULL, *local_mod = NULL;
+ struct ly_set *mods;
*str_value = NULL;
+ if (format == LY_VALUE_XML) {
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+ }
+
while (cur_idx < val->keys->used) {
cur_tok = val->keys->tokens[cur_idx];
cur_exp_ptr = val->keys->expr + val->keys->tok_pos[cur_idx];
@@ -79,11 +87,15 @@ instanceid_keys_print_value(const struct lyd_value_instance_identifier_keys *val
/* tokens that may include prefixes, get them in the target format */
is_nt = (cur_tok == LYXP_TOKEN_NAMETEST) ? 1 : 0;
LY_CHECK_GOTO(ret = lyplg_type_xpath10_print_token(cur_exp_ptr, val->keys->tok_len[cur_idx], is_nt, &context_mod,
- val->ctx, val->format, val->prefix_data, format, prefix_data, &str_tok, err), error);
+ val->ctx, val->format, val->prefix_data, format, prefix_data, &str_tok, err), cleanup);
/* append the converted token */
mem = realloc(*str_value, str_len + strlen(str_tok) + 1);
- LY_CHECK_ERR_GOTO(!mem, free(str_tok), error_mem);
+ if (!mem) {
+ free(str_tok);
+ ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
+ goto cleanup;
+ }
*str_value = mem;
str_len += sprintf(*str_value + str_len, "%s", str_tok);
free(str_tok);
@@ -93,7 +105,10 @@ instanceid_keys_print_value(const struct lyd_value_instance_identifier_keys *val
} else {
/* just copy the token */
mem = realloc(*str_value, str_len + val->keys->tok_len[cur_idx] + 1);
- LY_CHECK_GOTO(!mem, error_mem);
+ if (!mem) {
+ ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
+ goto cleanup;
+ }
*str_value = mem;
str_len += sprintf(*str_value + str_len, "%.*s", (int)val->keys->tok_len[cur_idx], cur_exp_ptr);
@@ -102,13 +117,14 @@ instanceid_keys_print_value(const struct lyd_value_instance_identifier_keys *val
}
}
- return LY_SUCCESS;
-
-error_mem:
- ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
-
-error:
- free(*str_value);
+cleanup:
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
+ if (ret) {
+ free(*str_value);
+ *str_value = NULL;
+ }
return ret;
}
@@ -123,6 +139,7 @@ lyplg_type_store_instanceid_keys(const struct ly_ctx *ctx, const struct lysc_typ
LY_ERR ret = LY_SUCCESS;
struct lysc_type_str *type_str = (struct lysc_type_str *)type;
struct lyd_value_instance_identifier_keys *val;
+ uint32_t log_opts = LY_LOSTORE;
char *canon;
/* init storage */
@@ -152,10 +169,15 @@ lyplg_type_store_instanceid_keys(const struct ly_ctx *ctx, const struct lysc_typ
((char *)value)[0]);
goto cleanup;
}
+
+ /* do not log */
+ ly_temp_log_options(&log_opts);
ret = ly_path_parse_predicate(ctx, NULL, value_len ? value : "", value_len, LY_PATH_PREFIX_OPTIONAL,
LY_PATH_PRED_KEYS, &val->keys);
+ ly_temp_log_options(NULL);
if (ret) {
ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL, "%s", ly_errmsg(ctx));
+ ly_err_clean((struct ly_ctx *)ctx, NULL);
goto cleanup;
}
val->ctx = ctx;
diff --git a/src/plugins_types/integer.c b/src/plugins_types/integer.c
index 0903365..05d6801 100644
--- a/src/plugins_types/integer.c
+++ b/src/plugins_types/integer.c
@@ -373,10 +373,6 @@ cleanup:
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_uint(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
switch (val1->realtype->basetype) {
case LY_TYPE_UINT8:
if (val1->uint8 != val2->uint8) {
diff --git a/src/plugins_types/ipv4_address.c b/src/plugins_types/ipv4_address.c
index f7b297c..9e54a0b 100644
--- a/src/plugins_types/ipv4_address.c
+++ b/src/plugins_types/ipv4_address.c
@@ -214,10 +214,6 @@ lyplg_type_compare_ipv4_address(const struct lyd_value *val1, const struct lyd_v
{
struct lyd_value_ipv4_address *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/ipv4_address_no_zone.c b/src/plugins_types/ipv4_address_no_zone.c
index 91fe677..a693912 100644
--- a/src/plugins_types/ipv4_address_no_zone.c
+++ b/src/plugins_types/ipv4_address_no_zone.c
@@ -132,10 +132,6 @@ lyplg_type_compare_ipv4_address_no_zone(const struct lyd_value *val1, const stru
{
struct lyd_value_ipv4_address_no_zone *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/ipv4_prefix.c b/src/plugins_types/ipv4_prefix.c
index 6f13eee..b8d77b5 100644
--- a/src/plugins_types/ipv4_prefix.c
+++ b/src/plugins_types/ipv4_prefix.c
@@ -203,10 +203,6 @@ lyplg_type_compare_ipv4_prefix(const struct lyd_value *val1, const struct lyd_va
{
struct lyd_value_ipv4_prefix *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/ipv6_address.c b/src/plugins_types/ipv6_address.c
index 74f5c62..344a7fc 100644
--- a/src/plugins_types/ipv6_address.c
+++ b/src/plugins_types/ipv6_address.c
@@ -216,10 +216,6 @@ lyplg_type_compare_ipv6_address(const struct lyd_value *val1, const struct lyd_v
{
struct lyd_value_ipv6_address *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/ipv6_address_no_zone.c b/src/plugins_types/ipv6_address_no_zone.c
index 26fbf80..98ca754 100644
--- a/src/plugins_types/ipv6_address_no_zone.c
+++ b/src/plugins_types/ipv6_address_no_zone.c
@@ -180,10 +180,6 @@ lyplg_type_compare_ipv6_address_no_zone(const struct lyd_value *val1, const stru
{
struct lyd_value_ipv6_address_no_zone *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/ipv6_prefix.c b/src/plugins_types/ipv6_prefix.c
index 8e62311..aaec395 100644
--- a/src/plugins_types/ipv6_prefix.c
+++ b/src/plugins_types/ipv6_prefix.c
@@ -217,10 +217,6 @@ lyplg_type_compare_ipv6_prefix(const struct lyd_value *val1, const struct lyd_va
{
struct lyd_value_ipv6_prefix *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/node_instanceid.c b/src/plugins_types/node_instanceid.c
index 04fb164..7833263 100644
--- a/src/plugins_types/node_instanceid.c
+++ b/src/plugins_types/node_instanceid.c
@@ -50,7 +50,8 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u, v;
char *result = NULL, quot;
- const struct lys_module *mod = NULL;
+ const struct lys_module *mod = NULL, *local_mod = NULL;
+ struct ly_set *mods;
ly_bool inherit_prefix = 0, d;
const char *strval;
@@ -62,6 +63,12 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi
switch (format) {
case LY_VALUE_XML:
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+
+ /* fallthrough */
case LY_VALUE_SCHEMA:
case LY_VALUE_SCHEMA_RESOLVED:
/* everything is prefixed */
@@ -90,9 +97,7 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi
LY_ARRAY_FOR(path[u].predicates, v) {
struct ly_path_predicate *pred = &path[u].predicates[v];
- switch (path[u].pred_type) {
- case LY_PATH_PREDTYPE_NONE:
- break;
+ switch (pred->type) {
case LY_PATH_PREDTYPE_POSITION:
/* position predicate */
ret = ly_strcat(&result, "[%" PRIu64 "]", pred->position);
@@ -133,6 +138,16 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi
free((char *)strval);
}
break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ /* key-predicate with a variable */
+ if (inherit_prefix) {
+ /* always the same prefix as the parent */
+ ret = ly_strcat(&result, "[%s=$%s]", pred->key->name, pred->variable);
+ } else {
+ ret = ly_strcat(&result, "[%s:%s=$%s]", lyplg_type_get_prefix(pred->key->module, format, prefix_data),
+ pred->key->name, pred->variable);
+ }
+ break;
}
LY_CHECK_GOTO(ret, cleanup);
@@ -140,6 +155,9 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi
}
cleanup:
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
if (ret) {
free(result);
} else {
@@ -193,7 +211,7 @@ lyplg_type_store_node_instanceid(const struct ly_ctx *ctx, const struct lysc_typ
ret = ly_path_parse(ctx, ctx_node, value, value_len, 0, LY_PATH_BEGIN_ABSOLUTE, prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
if (ret) {
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
- "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, (char *)value);
+ "Invalid node-instance-identifier \"%.*s\" value - syntax error.", (int)value_len, (char *)value);
goto cleanup;
}
@@ -209,7 +227,7 @@ lyplg_type_store_node_instanceid(const struct ly_ctx *ctx, const struct lysc_typ
LY_VALUE_JSON : format, prefix_data, &path);
if (ret) {
ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL,
- "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, (char *)value);
+ "Invalid node-instance-identifier \"%.*s\" value - semantic error.", (int)value_len, (char *)value);
goto cleanup;
}
diff --git a/src/plugins_types/string.c b/src/plugins_types/string.c
index 4f988ef..f8143d0 100644
--- a/src/plugins_types/string.c
+++ b/src/plugins_types/string.c
@@ -1,9 +1,10 @@
/**
* @file string.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Built-in string type plugin.
*
- * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ * Copyright (c) 2019 - 2023 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.
@@ -33,6 +34,29 @@
* | string length | yes | `char *` | string itself |
*/
+/**
+ * @brief Check string value for invalid characters.
+ *
+ * @param[in] value String to check.
+ * @param[in] value_len Length of @p value.
+ * @param[out] err Generated error on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+string_check_chars(const char *value, size_t value_len, struct ly_err_item **err)
+{
+ size_t len, parsed = 0;
+
+ while (value_len - parsed) {
+ if (ly_checkutf8(value + parsed, value_len - parsed, &len)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character 0x%hhx.", value[parsed]);
+ }
+ parsed += len;
+ }
+
+ return LY_SUCCESS;
+}
+
LIBYANG_API_DEF LY_ERR
lyplg_type_store_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
@@ -46,6 +70,12 @@ lyplg_type_store_string(const struct ly_ctx *ctx, const struct lysc_type *type,
memset(storage, 0, sizeof *storage);
storage->realtype = type;
+ if (!(options & LYPLG_TYPE_STORE_IS_UTF8)) {
+ /* check the UTF-8 encoding */
+ ret = string_check_chars(value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
/* check hints */
ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
LY_CHECK_GOTO(ret, cleanup);
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index 6e31d1e..a5b7610 100644
--- a/src/plugins_types/union.c
+++ b/src/plugins_types/union.c
@@ -172,6 +172,8 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, struct lyd_va
const void *value = NULL;
size_t value_len = 0;
+ *err = NULL;
+
if (subvalue->format == LY_VALUE_LYB) {
lyb_parse_union(subvalue->original, subvalue->orig_len, NULL, &value, &value_len);
} else {
@@ -220,36 +222,63 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_v
{
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u;
+ struct ly_err_item **errs = NULL, *e;
uint32_t temp_lo = 0;
+ char *msg = NULL;
+ int msg_len = 0;
+
+ *err = NULL;
if (!types || !LY_ARRAY_COUNT(types)) {
return LY_EINVAL;
}
- *err = NULL;
+ /* alloc errors */
+ errs = calloc(LY_ARRAY_COUNT(types), sizeof *errs);
+ LY_CHECK_RET(!errs, LY_EMEM);
/* turn logging temporarily off */
ly_temp_log_options(&temp_lo);
/* use the first usable subtype to store the value */
for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
- ret = union_store_type(ctx, types[u], subvalue, resolve, ctx_node, tree, unres, err);
+ ret = union_store_type(ctx, types[u], subvalue, resolve, ctx_node, tree, unres, &e);
if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) {
break;
}
- ly_err_free(*err);
- *err = NULL;
+ errs[u] = e;
}
if (u == LY_ARRAY_COUNT(types)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid union value \"%.*s\" - no matching subtype found.",
+ /* create the full error */
+ msg_len = asprintf(&msg, "Invalid union value \"%.*s\" - no matching subtype found:\n",
(int)subvalue->orig_len, (char *)subvalue->original);
+ if (msg_len == -1) {
+ LY_CHECK_ERR_GOTO(!errs, ret = LY_EMEM, cleanup);
+ }
+ for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+ if (!errs[u]) {
+ /* no error for some reason */
+ continue;
+ }
+
+ msg = ly_realloc(msg, msg_len + 4 + strlen(types[u]->plugin->id) + 2 + strlen(errs[u]->msg) + 2);
+ LY_CHECK_ERR_GOTO(!msg, ret = LY_EMEM, cleanup);
+ msg_len += sprintf(msg + msg_len, " %s: %s\n", types[u]->plugin->id, errs[u]->msg);
+ }
+
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "%s", msg);
} else if (type_idx) {
*type_idx = u;
}
- /* restore logging */
+cleanup:
+ for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+ ly_err_free(errs[u]);
+ }
+ free(errs);
+ free(msg);
ly_temp_log_options(NULL);
return ret;
}
@@ -281,29 +310,26 @@ lyb_fill_subvalue(const struct ly_ctx *ctx, struct lysc_type_union *type_u, cons
ret = lyb_union_validate(lyb_data, lyb_data_len, type_u, err);
LY_CHECK_RET(ret);
- /* Parse lyb_data and set the lyb_value and lyb_value_len. */
+ /* parse lyb_data and set the lyb_value and lyb_value_len */
lyb_parse_union(lyb_data, lyb_data_len, &type_idx, &lyb_value, &lyb_value_len);
LY_CHECK_RET(ret);
- /* Store lyb_data to subvalue. */
- ret = union_subvalue_assignment(lyb_data, lyb_data_len,
- &subvalue->original, &subvalue->orig_len, options);
+ /* store lyb_data to subvalue */
+ ret = union_subvalue_assignment(lyb_data, lyb_data_len, &subvalue->original, &subvalue->orig_len, options);
LY_CHECK_RET(ret);
if (lyb_value) {
- /* Resolve prefix_data and set format. */
+ /* resolve prefix_data and set format */
ret = lyplg_type_prefix_data_new(ctx, lyb_value, lyb_value_len, LY_VALUE_LYB, prefix_data, &subvalue->format,
&subvalue->prefix_data);
LY_CHECK_RET(ret);
assert(subvalue->format == LY_VALUE_LYB);
} else {
- /* The lyb_parse_union() did not find lyb_value.
- * Just set format.
- */
+ /* lyb_parse_union() did not find lyb_value, just set format */
subvalue->format = LY_VALUE_LYB;
}
- /* Use the specific type to store the value. */
+ /* use the specific type to store the value */
ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 0, NULL, NULL, unres, err);
return ret;
@@ -329,13 +355,11 @@ lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, c
subvalue->ctx_node = ctx_node;
if (format == LY_VALUE_LYB) {
- ret = lyb_fill_subvalue(ctx, type_u, value, value_len,
- prefix_data, subvalue, &options, unres, err);
+ ret = lyb_fill_subvalue(ctx, type_u, value, value_len, prefix_data, subvalue, &options, unres, err);
LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
} else {
/* Store @p value to subvalue. */
- ret = union_subvalue_assignment(value, value_len,
- &subvalue->original, &subvalue->orig_len, &options);
+ ret = union_subvalue_assignment(value, value_len, &subvalue->original, &subvalue->orig_len, &options);
LY_CHECK_GOTO(ret, cleanup);
/* store format-specific data for later prefix resolution */
@@ -370,6 +394,7 @@ lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type
LY_ERR ret = LY_SUCCESS;
struct lysc_type_union *type_u = (struct lysc_type_union *)type;
struct lyd_value_union *subvalue = storage->subvalue;
+ uint32_t type_idx;
*err = NULL;
@@ -378,9 +403,16 @@ lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type
* we have to perform union value storing again from scratch */
subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
- /* use the first usable subtype to store the value */
- ret = union_find_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, NULL, err);
- LY_CHECK_RET(ret);
+ if (subvalue->format == LY_VALUE_LYB) {
+ /* use the specific type to store the value */
+ lyb_parse_union(subvalue->original, 0, &type_idx, NULL, NULL);
+ ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 1, ctx_node, tree, NULL, err);
+ LY_CHECK_RET(ret);
+ } else {
+ /* use the first usable subtype to store the value */
+ ret = union_find_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, NULL, err);
+ LY_CHECK_RET(ret);
+ }
/* success, update the canonical value, if any generated */
lydict_remove(ctx, storage->_canonical);
@@ -391,10 +423,6 @@ lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_union(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
if (val1->subvalue->value.realtype != val2->subvalue->value.realtype) {
return LY_ENOT;
}
@@ -418,10 +446,10 @@ lyb_union_print(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct
void *prefix_data, size_t *value_len)
{
void *ret = NULL;
- LY_ERR retval;
+ LY_ERR r;
struct ly_err_item *err;
uint64_t num = 0;
- uint32_t type_idx;
+ uint32_t type_idx = 0;
ly_bool dynamic;
size_t pval_len;
void *pval;
@@ -435,8 +463,9 @@ lyb_union_print(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct
ctx = subvalue->ctx_node->module->ctx;
}
subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
- retval = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, &type_idx, NULL, &err);
- LY_CHECK_RET((retval != LY_SUCCESS) && (retval != LY_EINCOMPLETE), NULL);
+ r = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, &type_idx, NULL, &err);
+ ly_err_free(err);
+ LY_CHECK_RET((r != LY_SUCCESS) && (r != LY_EINCOMPLETE), NULL);
/* Print subvalue in LYB format. */
pval = (void *)subvalue->value.realtype->plugin->print(NULL, &subvalue->value, LY_VALUE_LYB, prefix_data, &dynamic,
diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c
index a15e5b7..fdfc2a9 100644
--- a/src/plugins_types/xpath1.0.c
+++ b/src/plugins_types/xpath1.0.c
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief ietf-yang-types xpath1.0 type plugin.
*
- * Copyright (c) 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2021 - 2023 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.
@@ -57,12 +57,9 @@ lyplg_type_xpath10_print_token(const char *token, uint16_t tok_len, ly_bool is_n
while (!(ret = ly_value_prefix_next(str_begin, token + tok_len, &len, &is_prefix, &str_next)) && len) {
if (!is_prefix) {
if (!has_prefix && is_nametest && (get_format == LY_VALUE_XML) && *context_mod) {
- /* prefix is always needed, get it in the target format */
+ /* get the prefix */
prefix = lyplg_type_get_prefix(*context_mod, get_format, get_prefix_data);
- if (!prefix) {
- ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Internal error.");
- goto cleanup;
- }
+ assert(prefix);
/* append the nametest and prefix */
mem = realloc(str, str_len + strlen(prefix) + 1 + len + 1);
@@ -94,10 +91,7 @@ lyplg_type_xpath10_print_token(const char *token, uint16_t tok_len, ly_bool is_n
if (mod) {
/* get the prefix in the target format */
prefix = lyplg_type_get_prefix(mod, get_format, get_prefix_data);
- if (!prefix) {
- ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Internal error.");
- goto cleanup;
- }
+ assert(prefix);
pref_len = strlen(prefix);
} else {
/* invalid prefix, just copy it */
@@ -223,13 +217,25 @@ lyplg_type_print_xpath10_value(const struct lyd_value_xpath10 *xp_val, LY_VALUE_
LY_ERR ret = LY_SUCCESS;
uint16_t expr_idx = 0;
uint32_t str_len = 0;
+ const struct lys_module *local_mod = NULL;
+ struct ly_set *mods;
*str_value = NULL;
*err = NULL;
+ if (format == LY_VALUE_XML) {
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+ }
+
/* recursively print the expression */
ret = xpath10_print_subexpr_r(&expr_idx, 0, NULL, xp_val, format, prefix_data, str_value, &str_len, err);
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
if (ret) {
free(*str_value);
*str_value = NULL;
@@ -326,6 +332,49 @@ cleanup:
}
/**
+ * @brief Create a namespace and add it into a set.
+ *
+ * @param[in] set Set of namespaces to add to.
+ * @param[in] pref Namespace prefix.
+ * @param[in] uri Namespace URI.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xpath10_add_ns(struct ly_set *set, const char *pref, const char *uri)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyxml_ns *ns = NULL;
+
+ /* create new ns */
+ ns = calloc(1, sizeof *ns);
+ if (!ns) {
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ ns->prefix = strdup(pref);
+ ns->uri = strdup(uri);
+ if (!ns->prefix || !ns->uri) {
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ ns->depth = 1;
+
+ /* add into the XML namespace set */
+ if ((rc = ly_set_add(set, ns, 1, NULL))) {
+ goto cleanup;
+ }
+ ns = NULL;
+
+cleanup:
+ if (ns) {
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
+ }
+ return rc;
+}
+
+/**
* @brief Implementation of ::lyplg_type_validate_clb for the xpath1.0 ietf-yang-types type.
*/
static LY_ERR
@@ -338,7 +387,6 @@ lyplg_type_validate_xpath10(const struct ly_ctx *UNUSED(ctx), const struct lysc_
struct ly_set *set = NULL;
uint32_t i;
const char *pref, *uri;
- struct lyxml_ns *ns;
*err = NULL;
LYD_VALUE_GET(storage, val);
@@ -371,28 +419,8 @@ lyplg_type_validate_xpath10(const struct ly_ctx *UNUSED(ctx), const struct lysc_
assert(!strcmp(LYD_NAME(lyd_child(set->dnodes[i])->next), "uri"));
uri = lyd_get_value(lyd_child(set->dnodes[i])->next);
- /* create new ns */
- ns = calloc(1, sizeof *ns);
- if (!ns) {
- ret = LY_EMEM;
- goto cleanup;
- }
- ns->prefix = strdup(pref);
- ns->uri = strdup(uri);
- if (!ns->prefix || !ns->uri) {
- free(ns->prefix);
- free(ns->uri);
- free(ns);
- ret = LY_EMEM;
- goto cleanup;
- }
- ns->depth = 1;
-
- /* add into the XML namespace set */
- if ((ret = ly_set_add(val->prefix_data, ns, 1, NULL))) {
- free(ns->prefix);
- free(ns->uri);
- free(ns);
+ /* new NS */
+ if ((ret = xpath10_add_ns(val->prefix_data, pref, uri))) {
goto cleanup;
}
}
diff --git a/src/printer_json.c b/src/printer_json.c
index 327799b..66ae154 100644
--- a/src/printer_json.c
+++ b/src/printer_json.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief JSON printer for libyang data structure
*
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 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.
@@ -14,6 +14,7 @@
*/
#include <assert.h>
+#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
@@ -45,7 +46,7 @@ struct jsonpr_ctx {
uint16_t level_printed; /* level where some data were already printed */
struct ly_set open; /* currently open array(s) */
- const struct lyd_node *print_sibling_metadata;
+ const struct lyd_node *first_leaflist; /**< first printed leaf-list instance, used when printing its metadata/attributes */
};
/**
@@ -219,31 +220,38 @@ json_nscmp(const struct lyd_node *node1, const struct lyd_node *node2)
static LY_ERR
json_print_string(struct ly_out *out, const char *text)
{
- uint64_t i, n;
+ uint64_t i;
if (!text) {
return LY_SUCCESS;
}
ly_write_(out, "\"", 1);
- for (i = n = 0; text[i]; i++) {
- const unsigned char ascii = text[i];
+ for (i = 0; text[i]; i++) {
+ const unsigned char byte = text[i];
- if (ascii < 0x20) {
- /* control character */
- ly_print_(out, "\\u%.4X", ascii);
- } else {
- switch (ascii) {
- case '"':
- ly_print_(out, "\\\"");
- break;
- case '\\':
- ly_print_(out, "\\\\");
- break;
- default:
+ switch (byte) {
+ case '"':
+ ly_print_(out, "\\\"");
+ break;
+ case '\\':
+ ly_print_(out, "\\\\");
+ break;
+ case '\r':
+ ly_print_(out, "\\r");
+ break;
+ case '\t':
+ ly_print_(out, "\\t");
+ break;
+ default:
+ if (iscntrl(byte)) {
+ /* control character */
+ ly_print_(out, "\\u%.4X", byte);
+ } else {
+ /* printable character (even non-ASCII UTF8) */
ly_write_(out, &text[i], 1);
- n++;
}
+ break;
}
}
ly_write_(out, "\"", 1);
@@ -294,18 +302,21 @@ json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VA
/* determine prefix string */
if (name) {
- const struct lys_module *mod;
-
switch (format) {
case LY_VALUE_JSON:
module_name = name->module_name;
break;
- case LY_VALUE_XML:
- mod = ly_ctx_get_module_implemented_ns(pctx->ctx, name->module_ns);
+ case LY_VALUE_XML: {
+ const struct lys_module *mod = NULL;
+
+ if (name->module_ns) {
+ mod = ly_ctx_get_module_implemented_ns(pctx->ctx, name->module_ns);
+ }
if (mod) {
module_name = mod->name;
}
break;
+ }
default:
/* cannot be created */
LOGINT_RET(pctx->ctx);
@@ -332,15 +343,19 @@ json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VA
* @param[in] pctx JSON printer context.
* @param[in] ctx Context used to print the value.
* @param[in] val Data value to be printed.
+ * @param[in] local_mod Module of the current node.
* @return LY_ERR value.
*/
static LY_ERR
-json_print_value(struct jsonpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val)
+json_print_value(struct jsonpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val,
+ const struct lys_module *local_mod)
{
ly_bool dynamic;
LY_DATA_TYPE basetype;
- const char *value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, NULL, &dynamic, NULL);
+ const char *value;
+ value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, (void *)local_mod, &dynamic, NULL);
+ LY_CHECK_RET(!value, LY_EINVAL);
basetype = val->realtype->basetype;
print_val:
@@ -348,7 +363,8 @@ print_val:
switch (basetype) {
case LY_TYPE_UNION:
/* use the resolved type */
- basetype = val->subvalue->value.realtype->basetype;
+ val = &val->subvalue->value;
+ basetype = val->realtype->basetype;
goto print_val;
case LY_TYPE_BINARY:
@@ -394,19 +410,13 @@ print_val:
*
* @param[in] ctx JSON printer context.
* @param[in] node Opaq node where the attributes are placed.
- * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed.
* @return LY_ERR value.
*/
static LY_ERR
-json_print_attribute(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node, const struct lys_module *wdmod)
+json_print_attribute(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node)
{
struct lyd_attr *attr;
- if (wdmod) {
- ly_print_(pctx->out, "%*s\"%s:default\":true", INDENT, wdmod->name);
- LEVEL_PRINTED;
- }
-
for (attr = node->attr; attr; attr = attr->next) {
json_print_member2(pctx, &node->node, attr->format, &attr->name, 0);
@@ -440,14 +450,14 @@ json_print_metadata(struct jsonpr_ctx *pctx, const struct lyd_node *node, const
struct lyd_meta *meta;
if (wdmod) {
- ly_print_(pctx->out, "%*s\"%s:default\":true", INDENT, wdmod->name);
+ ly_print_(pctx->out, "%*s\"%s:default\":%strue", INDENT, wdmod->name, DO_FORMAT ? " " : "");
LEVEL_PRINTED;
}
for (meta = node->meta; meta; meta = meta->next) {
PRINT_COMMA;
ly_print_(pctx->out, "%*s\"%s:%s\":%s", INDENT, meta->annotation->module->name, meta->name, DO_FORMAT ? " " : "");
- LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &meta->value));
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &meta->value, NULL));
LEVEL_PRINTED;
}
@@ -495,7 +505,7 @@ json_print_attributes(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_b
}
ly_print_(pctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
LEVEL_INC;
- LY_CHECK_RET(json_print_attribute(pctx, (struct lyd_node_opaq *)node, wdmod));
+ LY_CHECK_RET(json_print_attribute(pctx, (struct lyd_node_opaq *)node));
LEVEL_DEC;
ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
LEVEL_PRINTED;
@@ -515,7 +525,7 @@ static LY_ERR
json_print_leaf(struct jsonpr_ctx *pctx, const struct lyd_node *node)
{
LY_CHECK_RET(json_print_member(pctx, node, 0));
- LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, node->schema->module));
LEVEL_PRINTED;
/* print attributes as sibling */
@@ -780,16 +790,16 @@ json_print_leaf_list(struct jsonpr_ctx *pctx, const struct lyd_node *node)
} else {
assert(node->schema->nodetype == LYS_LEAFLIST);
- LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, node->schema->module));
- if (!pctx->print_sibling_metadata) {
+ if (!pctx->first_leaflist) {
if ((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) {
/* we have implicit OR explicit default node, get with-defaults module */
wdmod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-netconf-with-defaults");
}
if (node->meta || wdmod) {
/* we will be printing metadata for these siblings */
- pctx->print_sibling_metadata = node;
+ pctx->first_leaflist = node;
}
}
}
@@ -802,21 +812,20 @@ json_print_leaf_list(struct jsonpr_ctx *pctx, const struct lyd_node *node)
}
/**
- * @brief Print leaf-list's metadata in case they were marked in the last call to json_print_leaf_list().
+ * @brief Print leaf-list's metadata or opaque nodes attributes.
* This function is supposed to be called when the leaf-list array is closed.
*
* @param[in] ctx JSON printer context.
* @return LY_ERR value.
*/
static LY_ERR
-json_print_metadata_leaflist(struct jsonpr_ctx *pctx)
+json_print_meta_attr_leaflist(struct jsonpr_ctx *pctx)
{
const struct lyd_node *prev, *node, *iter;
const struct lys_module *wdmod = NULL;
+ const struct lyd_node_opaq *opaq = NULL;
- if (!pctx->print_sibling_metadata) {
- return LY_SUCCESS;
- }
+ assert(pctx->first_leaflist);
if (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG)) {
/* get with-defaults module */
@@ -824,19 +833,31 @@ json_print_metadata_leaflist(struct jsonpr_ctx *pctx)
}
/* node is the first instance of the leaf-list */
- for (node = pctx->print_sibling_metadata, prev = pctx->print_sibling_metadata->prev;
+ for (node = pctx->first_leaflist, prev = pctx->first_leaflist->prev;
prev->next && matching_node(node, prev);
node = prev, prev = node->prev) {}
- LY_CHECK_RET(json_print_member(pctx, node, 1));
+ if (node->schema) {
+ LY_CHECK_RET(json_print_member(pctx, node, 1));
+ } else {
+ opaq = (struct lyd_node_opaq *)node;
+ LY_CHECK_RET(json_print_member2(pctx, lyd_parent(node), opaq->format, &opaq->name, 1));
+ }
+
ly_print_(pctx->out, "[%s", (DO_FORMAT ? "\n" : ""));
LEVEL_INC;
LY_LIST_FOR(node, iter) {
PRINT_COMMA;
- if (iter->meta || (iter->flags & LYD_DEFAULT)) {
+ if ((iter->schema && (iter->meta || (iter->flags & LYD_DEFAULT))) || (opaq && opaq->attr)) {
ly_print_(pctx->out, "%*s%s", INDENT, DO_FORMAT ? "{\n" : "{");
LEVEL_INC;
- LY_CHECK_RET(json_print_metadata(pctx, iter, (iter->flags & LYD_DEFAULT) ? wdmod : NULL));
+
+ if (iter->schema) {
+ LY_CHECK_RET(json_print_metadata(pctx, iter, (iter->flags & LYD_DEFAULT) ? wdmod : NULL));
+ } else {
+ LY_CHECK_RET(json_print_attribute(pctx, (struct lyd_node_opaq *)iter));
+ }
+
LEVEL_DEC;
ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
} else {
@@ -897,12 +918,17 @@ json_print_opaq(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node)
ly_print_(pctx->out, "%s", node->value);
} else {
/* string or a large number */
- ly_print_(pctx->out, "\"%s\"", node->value);
+ json_print_string(pctx->out, node->value);
}
LEVEL_PRINTED;
- /* attributes */
- json_print_attributes(pctx, (const struct lyd_node *)node, 0);
+ if (!(node->hints & LYD_NODEHINT_LEAFLIST)) {
+ /* attributes */
+ json_print_attributes(pctx, (const struct lyd_node *)node, 0);
+ } else if (!pctx->first_leaflist && node->attr) {
+ /* attributes printed later */
+ pctx->first_leaflist = &node->node;
+ }
}
if (last && (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) {
@@ -959,9 +985,9 @@ json_print_node(struct jsonpr_ctx *pctx, const struct lyd_node *node)
pctx->level_printed = pctx->level;
- if (pctx->print_sibling_metadata && !matching_node(node->next, pctx->print_sibling_metadata)) {
- json_print_metadata_leaflist(pctx);
- pctx->print_sibling_metadata = NULL;
+ if (pctx->first_leaflist && !matching_node(node->next, pctx->first_leaflist)) {
+ json_print_meta_attr_leaflist(pctx);
+ pctx->first_leaflist = NULL;
}
return LY_SUCCESS;
diff --git a/src/printer_lyb.c b/src/printer_lyb.c
index 686c2d8..820e81e 100644
--- a/src/printer_lyb.c
+++ b/src/printer_lyb.c
@@ -81,7 +81,7 @@ lyb_ptr_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(c
* @return LY_EEXIST when the whole hash sequence sollides.
*/
static LY_ERR
-lyb_hash_sequence_check(struct hash_table *ht, struct lysc_node *sibling, LYB_HASH ht_col_id, LYB_HASH compare_col_id)
+lyb_hash_sequence_check(struct ly_ht *ht, struct lysc_node *sibling, LYB_HASH ht_col_id, LYB_HASH compare_col_id)
{
struct lysc_node **col_node;
@@ -123,9 +123,9 @@ lyb_hash_sequence_check(struct hash_table *ht, struct lysc_node *sibling, LYB_HA
* @return LY_ERR value.
*/
static LY_ERR
-lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p)
+lyb_hash_siblings(struct lysc_node *sibling, struct ly_ht **ht_p)
{
- struct hash_table *ht;
+ struct ly_ht *ht;
const struct lysc_node *parent;
const struct lys_module *mod;
LYB_HASH i;
@@ -172,7 +172,7 @@ lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p)
if (lyht_insert(ht, &sibling, lyb_get_hash(sibling, i), NULL)) {
LOGINT(sibling->module->ctx);
lyht_set_cb(ht, lyb_hash_equal_cb);
- lyht_free(ht);
+ lyht_free(ht, NULL);
return LY_EINT;
}
lyht_set_cb(ht, lyb_hash_equal_cb);
@@ -184,7 +184,7 @@ lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p)
if (i == LYB_HASH_BITS) {
/* wow */
LOGINT(sibling->module->ctx);
- lyht_free(ht);
+ lyht_free(ht, NULL);
return LY_EINT;
}
}
@@ -205,7 +205,7 @@ lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p)
* @return LY_ERR value.
*/
static LY_ERR
-lyb_hash_find(struct hash_table *ht, struct lysc_node *node, LYB_HASH *hash_p)
+lyb_hash_find(struct ly_ht *ht, struct lysc_node *node, LYB_HASH *hash_p)
{
LYB_HASH hash;
uint32_t i;
@@ -701,7 +701,7 @@ lyb_print_term_value(struct lyd_node_term *term, struct ly_out *out, struct lyly
if (value_len > UINT32_MAX) {
LOGERR(lybctx->ctx, LY_EINT, "The maximum length of the LYB data "
- "from a term node must not exceed %lu.", UINT32_MAX);
+ "from a term node must not exceed %" PRIu32 ".", UINT32_MAX);
ret = LY_EINT;
goto cleanup;
}
@@ -856,7 +856,7 @@ lyb_print_attributes(struct ly_out *out, const struct lyd_node_opaq *node, struc
* @return LY_ERR value.
*/
static LY_ERR
-lyb_print_schema_hash(struct ly_out *out, struct lysc_node *schema, struct hash_table **sibling_ht, struct lylyb_ctx *lybctx)
+lyb_print_schema_hash(struct ly_out *out, struct lysc_node *schema, struct ly_ht **sibling_ht, struct lylyb_ctx *lybctx)
{
LY_ARRAY_COUNT_TYPE u;
uint32_t i;
@@ -1205,7 +1205,7 @@ lyb_print_node_list(struct ly_out *out, const struct lyd_node *node, struct lyd_
* @return LY_ERR value.
*/
static LY_ERR
-lyb_print_node(struct ly_out *out, const struct lyd_node **printed_node, struct hash_table **sibling_ht,
+lyb_print_node(struct ly_out *out, const struct lyd_node **printed_node, struct ly_ht **sibling_ht,
struct lyd_lyb_ctx *lybctx)
{
const struct lyd_node *node = *printed_node;
@@ -1256,32 +1256,23 @@ lyb_print_node(struct ly_out *out, const struct lyd_node **printed_node, struct
static LY_ERR
lyb_print_siblings(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx)
{
- struct hash_table *sibling_ht = NULL;
+ struct ly_ht *sibling_ht = NULL;
const struct lys_module *prev_mod = NULL;
- ly_bool top_level;
-
- top_level = !LY_ARRAY_COUNT(lybctx->lybctx->siblings);
LY_CHECK_RET(lyb_write_start_siblings(out, lybctx->lybctx));
- if (top_level) {
- /* write all the siblings */
- LY_LIST_FOR(node, node) {
- /* do not reuse sibling hash tables from different modules */
- if (!node->schema || (node->schema->module != prev_mod)) {
- sibling_ht = NULL;
- prev_mod = node->schema ? node->schema->module : NULL;
- }
+ /* write all the siblings */
+ LY_LIST_FOR(node, node) {
+ /* do not reuse top-level sibling hash tables from different modules */
+ if (!node->schema || (!lysc_data_parent(node->schema) && (node->schema->module != prev_mod))) {
+ sibling_ht = NULL;
+ prev_mod = node->schema ? node->schema->module : NULL;
+ }
- LY_CHECK_RET(lyb_print_node(out, &node, &sibling_ht, lybctx));
+ LY_CHECK_RET(lyb_print_node(out, &node, &sibling_ht, lybctx));
- if (!(lybctx->print_options & LYD_PRINT_WITHSIBLINGS)) {
- break;
- }
- }
- } else {
- LY_LIST_FOR(node, node) {
- LY_CHECK_RET(lyb_print_node(out, &node, &sibling_ht, lybctx));
+ if (!lyd_parent(node) && !(lybctx->print_options & LYD_PRINT_WITHSIBLINGS)) {
+ break;
}
}
@@ -1299,9 +1290,9 @@ lyb_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options
const struct ly_ctx *ctx = root ? LYD_CTX(root) : NULL;
lybctx = calloc(1, sizeof *lybctx);
- LY_CHECK_ERR_RET(!lybctx, LOGMEM(ctx), LY_EMEM);
+ LY_CHECK_ERR_GOTO(!lybctx, LOGMEM(ctx); ret = LY_EMEM, cleanup);
lybctx->lybctx = calloc(1, sizeof *lybctx->lybctx);
- LY_CHECK_ERR_RET(!lybctx->lybctx, LOGMEM(ctx), LY_EMEM);
+ LY_CHECK_ERR_GOTO(!lybctx->lybctx, LOGMEM(ctx); ret = LY_EMEM, cleanup);
lybctx->print_options = options;
if (root) {
diff --git a/src/printer_schema.c b/src/printer_schema.c
index 075c519..12213bb 100644
--- a/src/printer_schema.c
+++ b/src/printer_schema.c
@@ -70,11 +70,6 @@ lys_print_module(struct ly_out *out, const struct lys_module *module, LYS_OUTFOR
}
ret = tree_print_module(out, module, options, line_length);
break;
- /* TODO not yet implemented
- case LYS_OUT_INFO:
- ret = info_print_model(out, module, target_node);
- break;
- */
default:
LOGERR(module->ctx, LY_EINVAL, "Unsupported output format.");
ret = LY_EINVAL;
@@ -105,11 +100,6 @@ lys_print_submodule(struct ly_out *out, const struct lysp_submodule *submodule,
case LYS_OUT_TREE:
ret = tree_print_parsed_submodule(out, submodule, options, line_length);
break;
- /* TODO not yet implemented
- case LYS_OUT_INFO:
- ret = info_print_model(out, module, target_node);
- break;
- */
default:
LOGERR(submodule->mod->ctx, LY_EINVAL, "Unsupported output format.");
ret = LY_EINVAL;
@@ -204,11 +194,6 @@ lys_print_node(struct ly_out *out, const struct lysc_node *node, LYS_OUTFORMAT f
case LYS_OUT_YANG_COMPILED:
ret = yang_print_compiled_node(out, node, options);
break;
- /* TODO not yet implemented
- case LYS_OUT_YIN:
- ret = yin_print_parsed(out, module);
- break;
- */
case LYS_OUT_TREE:
ret = tree_print_compiled_node(out, node, options, line_length);
break;
diff --git a/src/printer_tree.c b/src/printer_tree.c
index 6a7e7ce..6aa2814 100644
--- a/src/printer_tree.c
+++ b/src/printer_tree.c
@@ -1873,7 +1873,7 @@ trp_ext_is_present(ly_bool lysc_tree, const void *node)
} else {
pn = (const struct lysp_node *)node;
LY_ARRAY_FOR(pn->exts, i) {
- if (!(pn->exts && pn->exts->record->plugin.printer_ptree)) {
+ if (!(pn->exts && pn->exts->record && pn->exts->record->plugin.printer_ptree)) {
continue;
}
if (!trp_ext_parent_is_valid(0, &pn->exts[i])) {
@@ -3426,7 +3426,7 @@ troc_modi_first_sibling(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
/* current node is top-node */
switch (tc->section) {
case TRD_SECT_MODULE:
- tc->cn = tc->cmod->data;
+ tc->cn = tc->cn->module->compiled->data;
break;
case TRD_SECT_RPCS:
tc->cn = (const struct lysc_node *)tc->cmod->rpcs;
@@ -3486,7 +3486,8 @@ trb_gap_to_opts(const struct trt_node *node)
}
if (node->name.module_prefix) {
- len += strlen(node->name.module_prefix);
+ /* prefix_name and ':' */
+ len += strlen(node->name.module_prefix) + 1;
}
if (node->name.str) {
len += strlen(node->name.str);
@@ -3753,7 +3754,10 @@ trb_print_parents(const struct lysc_node *node, struct trt_wrapper *wr_in, struc
/* print node */
ly_print_(pc->out, "\n");
print_node = pc->fp.read.node(TRP_EMPTY_PARENT_CACHE, tc);
+ /* siblings do not print, so the node is always considered the last */
+ print_node.last_one = 1;
max_gap_before_type = trb_max_gap_to_type(TRP_EMPTY_PARENT_CACHE, pc, tc);
+ tc->cn = node;
trb_print_entire_node(&print_node, max_gap_before_type, wr, pc, tc);
}
@@ -3856,22 +3860,36 @@ trb_ext_iter(const struct trt_tree_ctx *tc, uint64_t *i)
* @param[in] compiled if @p ext is lysc structure.
* @param[in] ext current processed extension.
* @param[out] plug_ctx is plugin context which will be initialized.
+ * @param[out] ignore plugin callback is NULL.
* @return LY_ERR value.
*/
static LY_ERR
-tro_ext_printer_tree(ly_bool compiled, void *ext, const struct lyspr_tree_ctx *plug_ctx)
+tro_ext_printer_tree(ly_bool compiled, void *ext, const struct lyspr_tree_ctx *plug_ctx, ly_bool *ignore)
{
struct lysc_ext_instance *ext_comp;
struct lysp_ext_instance *ext_pars;
+ const struct lyplg_ext *plugin;
const char *flags = NULL, *add_opts = NULL;
if (compiled) {
ext_comp = ext;
- return ext_comp->def->plugin->printer_ctree(ext, plug_ctx, &flags, &add_opts);
+ plugin = ext_comp->def->plugin;
+ if (!plugin->printer_ctree) {
+ *ignore = 1;
+ return LY_SUCCESS;
+ }
+ return plugin->printer_ctree(ext, plug_ctx, &flags, &add_opts);
} else {
ext_pars = ext;
- return ext_pars->record->plugin.printer_ptree(ext, plug_ctx, &flags, &add_opts);
+ plugin = &ext_pars->record->plugin;
+ if (!plugin->printer_ptree) {
+ *ignore = 1;
+ return LY_SUCCESS;
+ }
+ return plugin->printer_ptree(ext, plug_ctx, &flags, &add_opts);
}
+
+ return LY_SUCCESS;
}
/**
@@ -3986,7 +4004,7 @@ trb_ext_print_instances(struct trt_wrapper wr, struct trt_parent_cache ca, struc
LY_ARRAY_COUNT_TYPE i;
uint64_t last_instance = UINT64_MAX;
void *ext;
- ly_bool child_exists;
+ ly_bool child_exists, ignore = 0;
uint32_t max, max_gap_before_type = 0;
ca = tro_parent_cache_for_child(ca, tc);
@@ -4004,8 +4022,12 @@ trb_ext_print_instances(struct trt_wrapper wr, struct trt_parent_cache ca, struc
while ((ext = trb_ext_iter(tc, &i))) {
struct lyspr_tree_ctx plug_ctx = {0};
- rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx);
+ rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx, &ignore);
LY_CHECK_ERR_GOTO(rc, tc->last_error = rc, end);
+ if (ignore) {
+ ignore = 0;
+ continue;
+ }
trb_ext_try_unified_indent(&plug_ctx, ca, &max_gap_before_type, pc, tc);
if (plug_ctx.schemas) {
last_instance = i;
@@ -4024,8 +4046,12 @@ trb_ext_print_instances(struct trt_wrapper wr, struct trt_parent_cache ca, struc
while ((ext = trb_ext_iter(tc, &i))) {
struct lyspr_tree_ctx plug_ctx = {0};
- rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx);
+ rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx, &ignore);
LY_CHECK_ERR_GOTO(rc, tc->last_error = rc, end);
+ if (ignore) {
+ ignore = 0;
+ continue;
+ }
if (!child_exists && (last_instance == i)) {
trb_ext_print_schemas(&plug_ctx, 1, max_gap_before_type, wr, ca, pc, tc);
} else {
@@ -4491,6 +4517,7 @@ trm_print_plugin_ext(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
struct trt_printer_ctx pc_dupl;
struct trt_tree_ctx tc_dupl;
struct trt_node node;
+ ly_bool ignore = 0;
uint32_t max_gap_before_type;
void *ext;
@@ -4504,9 +4531,10 @@ trm_print_plugin_ext(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
while ((ext = trb_mod_ext_iter(tc, &i))) {
struct lyspr_tree_ctx plug_ctx = {0};
- rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx);
+ rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx, &ignore);
LY_CHECK_ERR_GOTO(rc, tc->last_error = rc, end);
- if (!plug_ctx.schemas) {
+ if (!plug_ctx.schemas || ignore) {
+ ignore = 0;
continue;
}
diff --git a/src/printer_xml.c b/src/printer_xml.c
index a7f4c73..9cd6774 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -110,7 +110,9 @@ xml_print_ns_opaq(struct xmlpr_ctx *pctx, LY_VALUE_FORMAT format, const struct l
{
switch (format) {
case LY_VALUE_XML:
- return xml_print_ns(pctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
+ if (name->module_ns) {
+ return xml_print_ns(pctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
+ }
break;
case LY_VALUE_JSON:
if (name->module_name) {
@@ -178,6 +180,8 @@ xml_print_meta(struct xmlpr_ctx *pctx, const struct lyd_node *node)
struct ly_set ns_list = {0};
LY_ARRAY_COUNT_TYPE u;
ly_bool dynamic, filter_attrs = 0;
+ const char *value;
+ uint32_t i;
/* with-defaults */
if (node->schema->nodetype & LYD_NODE_TERM) {
@@ -205,11 +209,14 @@ xml_print_meta(struct xmlpr_ctx *pctx, const struct lyd_node *node)
}
for (meta = node->meta; meta; meta = meta->next) {
- const char *value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list,
- &dynamic, NULL);
+ /* store the module of the default namespace, NULL because there is none */
+ ly_set_add(&ns_list, NULL, 0, NULL);
+
+ /* print the value */
+ value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list, &dynamic, NULL);
/* print namespaces connected with the value's prefixes */
- for (uint32_t i = 0; i < ns_list.count; ++i) {
+ for (i = 1; i < ns_list.count; ++i) {
mod = ns_list.objs[i];
xml_print_ns(pctx, mod->ns, mod->prefix, 1);
}
@@ -314,22 +321,32 @@ static LY_ERR xml_print_node(struct xmlpr_ctx *pctx, const struct lyd_node *node
static LY_ERR
xml_print_term(struct xmlpr_ctx *pctx, const struct lyd_node_term *node)
{
+ LY_ERR rc = LY_SUCCESS;
struct ly_set ns_list = {0};
- ly_bool dynamic;
- const char *value;
+ ly_bool dynamic = 0;
+ const char *value = NULL;
+ const struct lys_module *mod;
+ uint32_t i;
- xml_print_node_open(pctx, &node->node);
+ /* store the module of the default namespace */
+ if ((rc = ly_set_add(&ns_list, node->schema->module, 0, NULL))) {
+ LOGMEM(pctx->ctx);
+ goto cleanup;
+ }
+
+ /* print the value */
value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(LYD_CTX(node), &node->value, LY_VALUE_XML,
&ns_list, &dynamic, NULL);
- LY_CHECK_RET(!value, LY_EINVAL);
+ LY_CHECK_ERR_GOTO(!value, rc = LY_EINVAL, cleanup);
- /* print namespaces connected with the values's prefixes */
- for (uint32_t u = 0; u < ns_list.count; ++u) {
- const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
+ /* print node opening */
+ xml_print_node_open(pctx, &node->node);
+ /* print namespaces connected with the values's prefixes */
+ for (i = 1; i < ns_list.count; ++i) {
+ mod = ns_list.objs[i];
ly_print_(pctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
}
- ly_set_erase(&ns_list, NULL);
if (!value[0]) {
ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
@@ -338,11 +355,13 @@ xml_print_term(struct xmlpr_ctx *pctx, const struct lyd_node_term *node)
lyxml_dump_text(pctx->out, value, 0);
ly_print_(pctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
}
+
+cleanup:
+ ly_set_erase(&ns_list, NULL);
if (dynamic) {
free((void *)value);
}
-
- return LY_SUCCESS;
+ return rc;
}
/**
diff --git a/src/printer_yang.c b/src/printer_yang.c
index ea643ac..8e0af18 100644
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -628,9 +628,7 @@ yprc_must(struct lys_ypr_ctx *pctx, const struct lysc_must *must, ly_bool *flag)
ly_bool inner_flag = 0;
ypr_open(pctx->out, flag);
- ly_print_(pctx->out, "%*smust \"", INDENT);
- ypr_encode(pctx->out, must->cond->expr, -1);
- ly_print_(pctx->out, "\"");
+ ypr_text(pctx, "must", must->cond->expr, 1, 0);
LEVEL++;
yprc_extension_instances(pctx, LY_STMT_MUST, 0, must->exts, &inner_flag);
@@ -773,9 +771,7 @@ yprp_when(struct lys_ypr_ctx *pctx, const struct lysp_when *when, ly_bool *flag)
}
ypr_open(pctx->out, flag);
- ly_print_(pctx->out, "%*swhen \"", INDENT);
- ypr_encode(pctx->out, when->cond, -1);
- ly_print_(pctx->out, "\"");
+ ypr_text(pctx, "when", when->cond, 1, 0);
LEVEL++;
yprp_extension_instances(pctx, LY_STMT_WHEN, 0, when->exts, &inner_flag);
@@ -795,9 +791,7 @@ yprc_when(struct lys_ypr_ctx *pctx, const struct lysc_when *when, ly_bool *flag)
}
ypr_open(pctx->out, flag);
- ly_print_(pctx->out, "%*swhen \"", INDENT);
- ypr_encode(pctx->out, when->cond->expr, -1);
- ly_print_(pctx->out, "\"");
+ ypr_text(pctx, "when", when->cond->expr, 1, 0);
LEVEL++;
yprc_extension_instances(pctx, LY_STMT_WHEN, 0, when->exts, &inner_flag);
diff --git a/src/schema_compile.c b/src/schema_compile.c
index ed768ba..1232228 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -437,6 +437,10 @@ lys_compile_expr_implement(const struct ly_ctx *ctx, const struct lyxp_expr *exp
assert(implement || mod_p);
+ if (mod_p) {
+ *mod_p = NULL;
+ }
+
for (i = 0; i < expr->used; ++i) {
if ((expr->tokens[i] != LYXP_TOKEN_NAMETEST) && (expr->tokens[i] != LYXP_TOKEN_LITERAL)) {
/* token cannot have a prefix */
@@ -476,38 +480,6 @@ lys_compile_expr_implement(const struct ly_ctx *ctx, const struct lyxp_expr *exp
}
/**
- * @brief Check and optionally implement modules referenced by a when expression.
- *
- * @param[in] ctx Compile context.
- * @param[in] when When to check.
- * @param[in,out] unres Global unres structure.
- * @return LY_ERECOMPILE if the whole dep set needs to be recompiled for these whens to evaluate.
- * @return LY_ENOT if full check of this when should be skipped.
- * @return LY_ERR value on error.
- */
-static LY_ERR
-lys_compile_unres_when_implement(struct lysc_ctx *ctx, const struct lysc_when *when, struct lys_glob_unres *unres)
-{
- LY_ERR rc = LY_SUCCESS;
- const struct lys_module *mod = NULL;
-
- /* check whether all the referenced modules are implemented */
- rc = lys_compile_expr_implement(ctx->ctx, when->cond, LY_VALUE_SCHEMA_RESOLVED, when->prefixes,
- ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED, unres, &mod);
- if (rc) {
- goto cleanup;
- } else if (mod) {
- LOGWRN(ctx->ctx, "When condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
- when->cond->expr, mod->name);
- rc = LY_ENOT;
- goto cleanup;
- }
-
-cleanup:
- return rc;
-}
-
-/**
* @brief Check when for cyclic dependencies.
*
* @param[in] set Set with all the referenced nodes.
@@ -519,7 +491,7 @@ lys_compile_unres_when_cyclic(struct lyxp_set *set, const struct lysc_node *node
{
struct lyxp_set tmp_set;
struct lyxp_set_scnode *xp_scnode;
- uint32_t i, j;
+ uint32_t i, j, idx;
LY_ARRAY_COUNT_TYPE u;
LY_ERR ret = LY_SUCCESS;
@@ -565,36 +537,46 @@ lys_compile_unres_when_cyclic(struct lyxp_set *set, const struct lysc_node *node
}
for (j = 0; j < tmp_set.used; ++j) {
- /* skip roots'n'stuff */
- if (tmp_set.val.scnodes[j].type == LYXP_NODE_ELEM) {
- /* try to find this node in our set */
- uint32_t idx;
-
- if (lyxp_set_scnode_contains(set, tmp_set.val.scnodes[j].scnode, LYXP_NODE_ELEM, -1, &idx) &&
- (set->val.scnodes[idx].in_ctx == LYXP_SET_SCNODE_START_USED)) {
- LOGVAL(set->ctx, LYVE_SEMANTICS, "When condition cyclic dependency on the node \"%s\".",
- tmp_set.val.scnodes[j].scnode->name);
- ret = LY_EVALID;
- LOG_LOCBACK(1, 0, 0, 0);
- goto cleanup;
- }
-
- /* needs to be checked, if in both sets, will be ignored */
- tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
- } else {
- /* no when, nothing to check */
+ if (tmp_set.val.scnodes[j].type != LYXP_NODE_ELEM) {
+ /* skip roots'n'stuff, no when, nothing to check */
tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
+ continue;
+ }
+
+ /* try to find this node in our set */
+ if (lyxp_set_scnode_contains(set, tmp_set.val.scnodes[j].scnode, LYXP_NODE_ELEM, -1, &idx) &&
+ (set->val.scnodes[idx].in_ctx == LYXP_SET_SCNODE_START_USED)) {
+ LOGVAL(set->ctx, LYVE_SEMANTICS, "When condition cyclic dependency on the node \"%s\".",
+ tmp_set.val.scnodes[j].scnode->name);
+ ret = LY_EVALID;
+ LOG_LOCBACK(1, 0, 0, 0);
+ goto cleanup;
+ }
+
+ /* needs to be checked, if in both sets, will be ignored */
+ tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ }
+
+ if (when->context != node) {
+ /* node actually depends on this "when", not the context node */
+ assert(tmp_set.val.scnodes[0].scnode == when->context);
+ if (tmp_set.val.scnodes[0].in_ctx == LYXP_SET_SCNODE_START_USED) {
+ /* replace the non-traversed context node with the dependent node */
+ tmp_set.val.scnodes[0].scnode = (struct lysc_node *)node;
+ } else {
+ /* context node was traversed, so just add the dependent node */
+ ret = lyxp_set_scnode_insert_node(&tmp_set, node, LYXP_SET_SCNODE_START_USED, LYXP_AXIS_CHILD, NULL);
+ LY_CHECK_ERR_GOTO(ret, LOG_LOCBACK(1, 0, 0, 0), cleanup);
}
}
/* merge this set into the global when set */
lyxp_set_scnode_merge(set, &tmp_set);
}
+ LOG_LOCBACK(1, 0, 0, 0);
/* check when of non-data parents as well */
node = node->parent;
-
- LOG_LOCBACK(1, 0, 0, 0);
} while (node && (node->nodetype & (LYS_CASE | LYS_CHOICE)));
/* this node when was checked (xp_scnode could have been reallocd) */
@@ -640,6 +622,7 @@ lys_compile_unres_when(struct lysc_ctx *ctx, const struct lysc_when *when, const
{
struct lyxp_set tmp_set = {0};
uint32_t i, opts;
+ struct lysc_node *schema;
LY_ERR ret = LY_SUCCESS;
opts = LYXP_SCNODE_SCHEMA | ((node->flags & LYS_IS_OUTPUT) ? LYXP_SCNODE_OUTPUT : 0);
@@ -655,28 +638,45 @@ lys_compile_unres_when(struct lysc_ctx *ctx, const struct lysc_when *when, const
ctx->path[0] = '\0';
lysc_path(node, LYSC_PATH_LOG, ctx->path, LYSC_CTX_BUFSIZE);
for (i = 0; i < tmp_set.used; ++i) {
- /* skip roots'n'stuff */
- if ((tmp_set.val.scnodes[i].type == LYXP_NODE_ELEM) &&
- (tmp_set.val.scnodes[i].in_ctx != LYXP_SET_SCNODE_START_USED)) {
- struct lysc_node *schema = tmp_set.val.scnodes[i].scnode;
-
- /* XPath expression cannot reference "lower" status than the node that has the definition */
- if (lysc_check_status(NULL, when->flags, node->module, node->name, schema->flags, schema->module,
- schema->name)) {
- LOGWRN(ctx->ctx, "When condition \"%s\" may be referencing %s node \"%s\".", when->cond->expr,
- (schema->flags == LYS_STATUS_OBSLT) ? "obsolete" : "deprecated", schema->name);
- }
+ if (tmp_set.val.scnodes[i].type != LYXP_NODE_ELEM) {
+ /* skip roots'n'stuff */
+ continue;
+ } else if (tmp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_START_USED) {
+ /* context node not actually traversed */
+ continue;
+ }
- /* check dummy node children/value accessing */
- if (lysc_data_parent(schema) == node) {
- LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node children.");
- ret = LY_EVALID;
- goto cleanup;
- } else if ((schema == node) && (tmp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL)) {
- LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node value.");
- ret = LY_EVALID;
- goto cleanup;
- }
+ schema = tmp_set.val.scnodes[i].scnode;
+
+ /* XPath expression cannot reference "lower" status than the node that has the definition */
+ if (lysc_check_status(NULL, when->flags, node->module, node->name, schema->flags, schema->module,
+ schema->name)) {
+ LOGWRN(ctx->ctx, "When condition \"%s\" may be referencing %s node \"%s\".", when->cond->expr,
+ (schema->flags == LYS_STATUS_OBSLT) ? "obsolete" : "deprecated", schema->name);
+ }
+
+ /* check dummy node children/value accessing */
+ if (lysc_data_parent(schema) == node) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node children.");
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if ((schema == node) && (tmp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node value.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ if (when->context != node) {
+ /* node actually depends on this "when", not the context node */
+ assert(tmp_set.val.scnodes[0].scnode == when->context);
+ if (tmp_set.val.scnodes[0].in_ctx == LYXP_SET_SCNODE_START_USED) {
+ /* replace the non-traversed context node with the dependent node */
+ tmp_set.val.scnodes[0].scnode = (struct lysc_node *)node;
+ } else {
+ /* context node was traversed, so just add the dependent node */
+ ret = lyxp_set_scnode_insert_node(&tmp_set, node, LYXP_SET_SCNODE_START_USED, LYXP_AXIS_CHILD, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
}
}
@@ -695,20 +695,16 @@ cleanup:
* @param[in] ctx Compile context.
* @param[in] node Node to check.
* @param[in] local_mods Sized array of local modules for musts of @p node at the same index.
- * @param[in,out] unres Global unres structure.
- * @return LY_ERECOMPILE
- * @return LY_ERR value
+ * @return LY_ERR value.
*/
static LY_ERR
-lys_compile_unres_must(struct lysc_ctx *ctx, const struct lysc_node *node, const struct lysp_module **local_mods,
- struct lys_glob_unres *unres)
+lys_compile_unres_must(struct lysc_ctx *ctx, const struct lysc_node *node, const struct lysp_module **local_mods)
{
struct lyxp_set tmp_set;
uint32_t i, opts;
LY_ARRAY_COUNT_TYPE u;
- struct lysc_must *musts = NULL;
+ struct lysc_must *musts;
LY_ERR ret = LY_SUCCESS;
- const struct lys_module *mod;
uint16_t flg;
LOG_LOCSET(node, NULL, NULL, NULL);
@@ -718,18 +714,6 @@ lys_compile_unres_must(struct lysc_ctx *ctx, const struct lysc_node *node, const
musts = lysc_node_musts(node);
LY_ARRAY_FOR(musts, u) {
- /* first check whether all the referenced modules are implemented */
- mod = NULL;
- ret = lys_compile_expr_implement(ctx->ctx, musts[u].cond, LY_VALUE_SCHEMA_RESOLVED, musts[u].prefixes,
- ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED, unres, &mod);
- if (ret) {
- goto cleanup;
- } else if (mod) {
- LOGWRN(ctx->ctx, "Must condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
- musts[u].cond->expr, mod->name);
- continue;
- }
-
/* check "must" */
ret = lyxp_atomize(ctx->ctx, musts[u].cond, node->module, LY_VALUE_SCHEMA_RESOLVED, musts[u].prefixes, node,
node, &tmp_set, opts);
@@ -815,6 +799,7 @@ lys_compile_unres_disabled_bitenum(struct lysc_ctx *ctx, struct lysc_node_leaf *
struct lysc_type **t;
LY_ARRAY_COUNT_TYPE u, count;
struct lysc_type_enum *ent;
+ ly_bool has_value = 0;
if (leaf->type->basetype == LY_TYPE_UNION) {
t = ((struct lysc_type_union *)leaf->type)->types;
@@ -829,14 +814,19 @@ lys_compile_unres_disabled_bitenum(struct lysc_ctx *ctx, struct lysc_node_leaf *
ent = (struct lysc_type_enum *)(t[u]);
lys_compile_unres_disabled_bitenum_remove(&ctx->free_ctx, ent->enums);
- if (!LY_ARRAY_COUNT(ent->enums)) {
- LOGVAL(ctx->ctx, LYVE_SEMANTICS, "%s type of node \"%s\" without any (or all disabled) valid values.",
- (ent->basetype == LY_TYPE_BITS) ? "Bits" : "Enumeration", leaf->name);
- return LY_EVALID;
+ if (LY_ARRAY_COUNT(ent->enums)) {
+ has_value = 1;
}
+ } else {
+ has_value = 1;
}
}
+ if (!has_value) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Node \"%s\" without any (or all disabled) valid values.", leaf->name);
+ return LY_EVALID;
+ }
+
return LY_SUCCESS;
}
@@ -847,13 +837,11 @@ lys_compile_unres_disabled_bitenum(struct lysc_ctx *ctx, struct lysc_node_leaf *
* @param[in] node Context node for the leafref.
* @param[in] lref Leafref to check/resolve.
* @param[in] local_mod Local module for the leafref type.
- * @param[in,out] unres Global unres structure.
- * @return LY_ERECOMPILE if context recompilation is needed,
* @return LY_ERR value.
*/
static LY_ERR
lys_compile_unres_leafref(struct lysc_ctx *ctx, const struct lysc_node *node, struct lysc_type_leafref *lref,
- const struct lysp_module *local_mod, struct lys_glob_unres *unres)
+ const struct lysp_module *local_mod)
{
const struct lysc_node *target = NULL;
struct ly_path *p;
@@ -862,8 +850,10 @@ lys_compile_unres_leafref(struct lysc_ctx *ctx, const struct lysc_node *node, st
assert(node->nodetype & (LYS_LEAF | LYS_LEAFLIST));
- /* first implement all the modules in the path */
- LY_CHECK_RET(lys_compile_expr_implement(ctx->ctx, lref->path, LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, 1, unres, NULL));
+ if (lref->realtype) {
+ /* already resolved, may happen (shared union typedef with a leafref) */
+ return LY_SUCCESS;
+ }
/* try to find the target, current module is that of the context node (RFC 7950 6.4.1 second bullet) */
LY_CHECK_RET(ly_path_compile_leafref(ctx->ctx, node, ctx->ext, lref->path,
@@ -934,6 +924,7 @@ lys_compile_unres_leafref(struct lysc_ctx *ctx, const struct lysc_node *node, st
* @param[in] dflt_pmod Parsed module of the @p dflt to resolve possible prefixes.
* @param[in,out] storage Storage for the compiled default value.
* @param[in,out] unres Global unres structure for newly implemented modules.
+ * @return LY_ERECOMPILE if the whole dep set needs to be recompiled for the value to be checked.
* @return LY_ERR value.
*/
static LY_ERR
@@ -1057,7 +1048,8 @@ lys_compile_unres_llist_dflts(struct lysc_ctx *ctx, struct lysc_node_leaflist *l
if (!llist->dflts[u]->realtype->plugin->compare(llist->dflts[u], llist->dflts[v])) {
lysc_update_path(ctx, llist->parent ? llist->parent->module : NULL, llist->name);
LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Configuration leaf-list has multiple defaults of the same value \"%s\".",
- llist->dflts[u]->realtype->plugin->print(ctx->ctx, llist->dflts[u], LY_VALUE_CANON, NULL, NULL, NULL));
+ (char *)llist->dflts[u]->realtype->plugin->print(ctx->ctx, llist->dflts[u], LY_VALUE_CANON,
+ NULL, NULL, NULL));
lysc_update_path(ctx, NULL, NULL);
return LY_EVALID;
}
@@ -1114,6 +1106,108 @@ lys_type_leafref_next(const struct lysc_node *node, uint64_t *index)
}
/**
+ * @brief Implement all referenced modules by leafrefs, when and must conditions.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] unres Global unres structure with the sets to resolve.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERECOMPILE if the whole dep set needs to be recompiled with the new implemented modules.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lys_compile_unres_depset_implement(struct ly_ctx *ctx, struct lys_glob_unres *unres)
+{
+ struct lys_depset_unres *ds_unres = &unres->ds_unres;
+ struct lysc_type_leafref *lref;
+ const struct lys_module *mod;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_unres_leafref *l;
+ struct lysc_unres_when *w;
+ struct lysc_unres_must *m;
+ struct lysc_must *musts;
+ ly_bool not_implemented;
+ uint32_t di = 0, li = 0, wi = 0, mi = 0;
+
+implement_all:
+ /* disabled leafrefs - even those because we need to check their target exists */
+ while (di < ds_unres->disabled_leafrefs.count) {
+ l = ds_unres->disabled_leafrefs.objs[di];
+
+ u = 0;
+ while ((lref = lys_type_leafref_next(l->node, &u))) {
+ LY_CHECK_RET(lys_compile_expr_implement(ctx, lref->path, LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, 1, unres, NULL));
+ }
+
+ ++di;
+ }
+
+ /* leafrefs */
+ while (li < ds_unres->leafrefs.count) {
+ l = ds_unres->leafrefs.objs[li];
+
+ u = 0;
+ while ((lref = lys_type_leafref_next(l->node, &u))) {
+ LY_CHECK_RET(lys_compile_expr_implement(ctx, lref->path, LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, 1, unres, NULL));
+ }
+
+ ++li;
+ }
+
+ /* when conditions */
+ while (wi < ds_unres->whens.count) {
+ w = ds_unres->whens.objs[wi];
+
+ LY_CHECK_RET(lys_compile_expr_implement(ctx, w->when->cond, LY_VALUE_SCHEMA_RESOLVED, w->when->prefixes,
+ ctx->flags & LY_CTX_REF_IMPLEMENTED, unres, &mod));
+ if (mod) {
+ LOGWRN(ctx, "When condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
+ w->when->cond->expr, mod->name);
+
+ /* remove from the set to skip the check */
+ ly_set_rm_index(&ds_unres->whens, wi, free);
+ continue;
+ }
+
+ ++wi;
+ }
+
+ /* must conditions */
+ while (mi < ds_unres->musts.count) {
+ m = ds_unres->musts.objs[mi];
+
+ not_implemented = 0;
+ musts = lysc_node_musts(m->node);
+ LY_ARRAY_FOR(musts, u) {
+ LY_CHECK_RET(lys_compile_expr_implement(ctx, musts[u].cond, LY_VALUE_SCHEMA_RESOLVED, musts[u].prefixes,
+ ctx->flags & LY_CTX_REF_IMPLEMENTED, unres, &mod));
+ if (mod) {
+ LOGWRN(ctx, "Must condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
+ musts[u].cond->expr, mod->name);
+
+ /* need to implement modules from all the expressions */
+ not_implemented = 1;
+ }
+ }
+
+ if (not_implemented) {
+ /* remove from the set to skip the check */
+ lysc_unres_must_free(m);
+ ly_set_rm_index(&ds_unres->musts, mi, NULL);
+ continue;
+ }
+
+ ++mi;
+ }
+
+ if ((di < ds_unres->disabled_leafrefs.count) || (li < ds_unres->leafrefs.count) || (wi < ds_unres->whens.count)) {
+ /* new items in the sets */
+ goto implement_all;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
* @brief Finish dependency set compilation by resolving all the unres sets.
*
* @param[in] ctx libyang context.
@@ -1125,7 +1219,7 @@ lys_type_leafref_next(const struct lysc_node *node, uint64_t *index)
static LY_ERR
lys_compile_unres_depset(struct ly_ctx *ctx, struct lys_glob_unres *unres)
{
- LY_ERR ret = LY_SUCCESS, r;
+ LY_ERR ret = LY_SUCCESS;
struct lysc_node *node;
struct lysc_type *typeiter;
struct lysc_type_leafref *lref;
@@ -1140,7 +1234,12 @@ lys_compile_unres_depset(struct ly_ctx *ctx, struct lys_glob_unres *unres)
uint32_t i, processed_leafrefs = 0;
resolve_all:
- /* check disabled leafrefs first */
+ /* implement all referenced modules to get final ds_unres set */
+ if ((ret = lys_compile_unres_depset_implement(ctx, unres))) {
+ goto cleanup;
+ }
+
+ /* check disabled leafrefs */
while (ds_unres->disabled_leafrefs.count) {
/* remember index, it can change before we get to free this item */
i = ds_unres->disabled_leafrefs.count - 1;
@@ -1150,7 +1249,7 @@ resolve_all:
LOG_LOCSET(l->node, NULL, NULL, NULL);
v = 0;
while ((ret == LY_SUCCESS) && (lref = lys_type_leafref_next(l->node, &v))) {
- ret = lys_compile_unres_leafref(&cctx, l->node, lref, l->local_mod, unres);
+ ret = lys_compile_unres_leafref(&cctx, l->node, lref, l->local_mod);
}
LOG_LOCBACK(1, 0, 0, 0);
LY_CHECK_GOTO(ret, cleanup);
@@ -1169,7 +1268,7 @@ resolve_all:
LOG_LOCSET(l->node, NULL, NULL, NULL);
v = 0;
while ((ret == LY_SUCCESS) && (lref = lys_type_leafref_next(l->node, &v))) {
- ret = lys_compile_unres_leafref(&cctx, l->node, lref, l->local_mod, unres);
+ ret = lys_compile_unres_leafref(&cctx, l->node, lref, l->local_mod);
}
LOG_LOCBACK(1, 0, 0, 0);
LY_CHECK_GOTO(ret, cleanup);
@@ -1193,29 +1292,7 @@ resolve_all:
processed_leafrefs++;
}
- /* check when, first implement all the referenced modules (for the cyclic check in the next loop to work) */
- i = 0;
- while (i < ds_unres->whens.count) {
- w = ds_unres->whens.objs[i];
- LYSC_CTX_INIT_PMOD(cctx, w->node->module->parsed, NULL);
-
- LOG_LOCSET(w->node, NULL, NULL, NULL);
- r = lys_compile_unres_when_implement(&cctx, w->when, unres);
- LOG_LOCBACK(w->node ? 1 : 0, 0, 0, 0);
-
- if (r == LY_ENOT) {
- /* skip full when check, remove from the set */
- free(w);
- ly_set_rm_index(&ds_unres->whens, i, NULL);
- continue;
- } else if (r) {
- /* error */
- ret = r;
- goto cleanup;
- }
-
- ++i;
- }
+ /* check when, the referenced modules must be implemented now */
while (ds_unres->whens.count) {
i = ds_unres->whens.count - 1;
w = ds_unres->whens.objs[i];
@@ -1237,7 +1314,7 @@ resolve_all:
LYSC_CTX_INIT_PMOD(cctx, m->node->module->parsed, m->ext);
LOG_LOCSET(m->node, NULL, NULL, NULL);
- ret = lys_compile_unres_must(&cctx, m->node, m->local_mods, unres);
+ ret = lys_compile_unres_must(&cctx, m->node, m->local_mods);
LOG_LOCBACK(1, 0, 0, 0);
LY_CHECK_GOTO(ret, cleanup);
@@ -1278,7 +1355,7 @@ resolve_all:
ly_set_rm_index(&ds_unres->dflts, i, NULL);
}
- /* some unres items may have been added */
+ /* some unres items may have been added by the default values */
if ((processed_leafrefs != ds_unres->leafrefs.count) || ds_unres->disabled_leafrefs.count ||
ds_unres->whens.count || ds_unres->musts.count || ds_unres->dflts.count) {
goto resolve_all;
@@ -1748,7 +1825,7 @@ lys_has_compiled_import_r(struct lys_module *mod)
LY_ERR
lys_implement(struct lys_module *mod, const char **features, struct lys_glob_unres *unres)
{
- LY_ERR ret;
+ LY_ERR r;
struct lys_module *m;
assert(!mod->implemented);
@@ -1757,21 +1834,15 @@ lys_implement(struct lys_module *mod, const char **features, struct lys_glob_unr
m = ly_ctx_get_module_implemented(mod->ctx, mod->name);
if (m) {
assert(m != mod);
- if (!strcmp(mod->name, "yang") && (strcmp(m->revision, mod->revision) > 0)) {
- /* special case for newer internal module, continue */
- LOGVRB("Internal module \"%s@%s\" is already implemented in revision \"%s\", using it instead.",
- mod->name, mod->revision ? mod->revision : "<none>", m->revision ? m->revision : "<none>");
- } else {
- LOGERR(mod->ctx, LY_EDENIED, "Module \"%s@%s\" is already implemented in revision \"%s\".",
- mod->name, mod->revision ? mod->revision : "<none>", m->revision ? m->revision : "<none>");
- return LY_EDENIED;
- }
+ LOGERR(mod->ctx, LY_EDENIED, "Module \"%s@%s\" is already implemented in revision \"%s\".",
+ mod->name, mod->revision ? mod->revision : "<none>", m->revision ? m->revision : "<none>");
+ return LY_EDENIED;
}
/* set features */
- ret = lys_set_features(mod->parsed, features);
- if (ret && (ret != LY_EEXIST)) {
- return ret;
+ r = lys_set_features(mod->parsed, features);
+ if (r && (r != LY_EEXIST)) {
+ return r;
}
/*
diff --git a/src/schema_compile_amend.c b/src/schema_compile_amend.c
index 9ca4e2e..8b570f6 100644
--- a/src/schema_compile_amend.c
+++ b/src/schema_compile_amend.c
@@ -1351,7 +1351,7 @@ lys_apply_deviate_replace(struct lysc_ctx *ctx, struct lysp_deviate_rpl *d, stru
LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &d->dflt, &((struct lysp_node_leaf *)target)->dflt), cleanup);
break;
case LYS_CHOICE:
- DEV_CHECK_PRESENCE(struct lysp_node_choice *, dflt.str, "replacing", "default", d->dflt);
+ DEV_CHECK_PRESENCE(struct lysp_node_choice *, dflt.str, "replacing", "default", d->dflt.str);
lydict_remove(ctx->ctx, ((struct lysp_node_choice *)target)->dflt.str);
LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &d->dflt, &((struct lysp_node_choice *)target)->dflt), cleanup);
@@ -1376,13 +1376,6 @@ lys_apply_deviate_replace(struct lysc_ctx *ctx, struct lysp_deviate_rpl *d, stru
AMEND_WRONG_NODETYPE("deviation", "replace", "config");
}
- if (!(target->flags & LYS_CONFIG_MASK)) {
- LOGVAL(ctx->ctx, LY_VCODE_DEV_NOT_PRESENT, "replacing", "config",
- d->flags & LYS_CONFIG_W ? "config true" : "config false");
- ret = LY_EVALID;
- goto cleanup;
- }
-
target->flags &= ~LYS_CONFIG_MASK;
target->flags |= d->flags & LYS_CONFIG_MASK;
}
diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c
index 0b64dcb..2723716 100644
--- a/src/schema_compile_node.c
+++ b/src/schema_compile_node.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Schema compilation of common nodes.
*
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 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.
@@ -43,14 +43,13 @@
#include "tree_schema_internal.h"
#include "xpath.h"
-static struct lysc_ext_instance *
-lysc_ext_instance_dup(struct ly_ctx *ctx, struct lysc_ext_instance *orig)
-{
- /* TODO - extensions, increase refcount */
- (void) ctx;
- (void) orig;
- return NULL;
-}
+/**
+ * @brief Item for storing typedef chain item.
+ */
+struct lys_type_item {
+ const struct lysp_tpdf *tpdf;
+ struct lysp_node *node;
+};
/**
* @brief Add a node with a when to unres.
@@ -346,31 +345,48 @@ lysc_patterns_dup(struct ly_ctx *ctx, struct lysc_pattern **orig)
/**
* @brief Duplicate compiled range structure.
*
- * @param[in] ctx Libyang context for logging.
+ * @param[in] ctx Compile context.
* @param[in] orig The range structure to be duplicated.
+ * @param[in] tpdf_chain Chain of the used typedefs, traversed backwards.
+ * @param[in] tpdf_chain_last Index of the last (backwards) typedef in @p tpdf_chain to use.
* @return New compiled range structure as a copy of @p orig.
* @return NULL in case of memory allocation error.
*/
static struct lysc_range *
-lysc_range_dup(struct ly_ctx *ctx, const struct lysc_range *orig)
+lysc_range_dup(struct lysc_ctx *ctx, const struct lysc_range *orig, struct ly_set *tpdf_chain, uint32_t tpdf_chain_last)
{
struct lysc_range *dup;
LY_ERR ret;
+ struct lys_type_item *tpdf_item;
+ uint32_t i;
assert(orig);
dup = calloc(1, sizeof *dup);
- LY_CHECK_ERR_RET(!dup, LOGMEM(ctx), NULL);
+ LY_CHECK_ERR_RET(!dup, LOGMEM(ctx->ctx), NULL);
if (orig->parts) {
- LY_ARRAY_CREATE_GOTO(ctx, dup->parts, LY_ARRAY_COUNT(orig->parts), ret, cleanup);
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, dup->parts, LY_ARRAY_COUNT(orig->parts), ret, cleanup);
(*((LY_ARRAY_COUNT_TYPE *)(dup->parts) - 1)) = LY_ARRAY_COUNT(orig->parts);
memcpy(dup->parts, orig->parts, LY_ARRAY_COUNT(dup->parts) * sizeof *dup->parts);
}
- DUP_STRING_GOTO(ctx, orig->eapptag, dup->eapptag, ret, cleanup);
- DUP_STRING_GOTO(ctx, orig->emsg, dup->emsg, ret, cleanup);
- dup->exts = lysc_ext_instance_dup(ctx, orig->exts);
+ DUP_STRING_GOTO(ctx->ctx, orig->eapptag, dup->eapptag, ret, cleanup);
+ DUP_STRING_GOTO(ctx->ctx, orig->emsg, dup->emsg, ret, cleanup);
+
+ /* collect all range extensions */
+ if (tpdf_chain->count > tpdf_chain_last) {
+ i = tpdf_chain->count;
+ do {
+ --i;
+ tpdf_item = tpdf_chain->objs[i];
+ if (!tpdf_item->tpdf->type.range) {
+ continue;
+ }
+ COMPILE_EXTS_GOTO(ctx, tpdf_item->tpdf->type.range->exts, dup->exts, dup, ret, cleanup);
+ } while (i > tpdf_chain_last);
+ }
return dup;
+
cleanup:
free(dup);
(void) ret; /* set but not used due to the return type */
@@ -1595,6 +1611,17 @@ done:
return ret;
}
+/**
+ * @brief Compile union type.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] ptypes Parsed union types.
+ * @param[in] context_pnode 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_name Name of the context node or referencing typedef for logging.
+ * @param[out] utypes_p Array of compiled union types.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_type_union(struct lysc_ctx *ctx, struct lysp_type *ptypes, struct lysp_node *context_pnode, uint16_t context_flags,
const char *context_name, struct lysc_type ***utypes_p)
@@ -1608,6 +1635,8 @@ lys_compile_type_union(struct lysc_ctx *ctx, struct lysp_type *ptypes, struct ly
ret = lys_compile_type(ctx, context_pnode, context_flags, context_name, &ptypes[u], &utypes[u + additional],
NULL, NULL);
LY_CHECK_GOTO(ret, error);
+ LY_ATOMIC_INC_BARRIER(utypes[u + additional]->refcount);
+
if (utypes[u + additional]->basetype == LY_TYPE_UNION) {
/* add space for additional types from the union subtype */
un_aux = (struct lysc_type_union *)utypes[u + additional];
@@ -1616,28 +1645,8 @@ lys_compile_type_union(struct lysc_ctx *ctx, struct lysp_type *ptypes, struct ly
/* copy subtypes of the subtype union */
for (LY_ARRAY_COUNT_TYPE v = 0; v < LY_ARRAY_COUNT(un_aux->types); ++v) {
- if (un_aux->types[v]->basetype == LY_TYPE_LEAFREF) {
- struct lysc_type_leafref *lref;
-
- /* duplicate the whole structure because of the instance-specific path resolving for realtype */
- utypes[u + additional] = calloc(1, sizeof(struct lysc_type_leafref));
- LY_CHECK_ERR_GOTO(!utypes[u + additional], LOGMEM(ctx->ctx); ret = LY_EMEM, error);
- lref = (struct lysc_type_leafref *)utypes[u + additional];
-
- lref->basetype = LY_TYPE_LEAFREF;
- ret = lyxp_expr_dup(ctx->ctx, ((struct lysc_type_leafref *)un_aux->types[v])->path, 0, 0, &lref->path);
- LY_CHECK_GOTO(ret, error);
- lref->refcount = 1;
- lref->require_instance = ((struct lysc_type_leafref *)un_aux->types[v])->require_instance;
- ret = lyplg_type_prefix_data_dup(ctx->ctx, LY_VALUE_SCHEMA_RESOLVED,
- ((struct lysc_type_leafref *)un_aux->types[v])->prefixes, (void **)&lref->prefixes);
- LY_CHECK_GOTO(ret, error);
- /* TODO extensions */
-
- } else {
- utypes[u + additional] = un_aux->types[v];
- LY_ATOMIC_INC_BARRIER(un_aux->types[v]->refcount);
- }
+ utypes[u + additional] = un_aux->types[v];
+ LY_ATOMIC_INC_BARRIER(un_aux->types[v]->refcount);
++additional;
LY_ARRAY_INCREMENT(utypes);
}
@@ -1664,7 +1673,68 @@ error:
}
/**
+ * @brief Allocate a new specific type structure according to the basetype.
+ *
+ * @param[in] basetype Base type of the new type.
+ * @return Specific type structure.
+ */
+static struct lysc_type *
+lys_new_type(LY_DATA_TYPE basetype)
+{
+ struct lysc_type *type = NULL;
+
+ 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_DEC64:
+ type = calloc(1, sizeof(struct lysc_type_dec));
+ break;
+ case LY_TYPE_STRING:
+ type = calloc(1, sizeof(struct lysc_type_str));
+ break;
+ case LY_TYPE_ENUM:
+ 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:
+ type = calloc(1, sizeof(struct lysc_type_num));
+ break;
+ case LY_TYPE_IDENT:
+ type = calloc(1, sizeof(struct lysc_type_identityref));
+ break;
+ case LY_TYPE_LEAFREF:
+ type = calloc(1, sizeof(struct lysc_type_leafref));
+ break;
+ case LY_TYPE_INST:
+ type = calloc(1, sizeof(struct lysc_type_instanceid));
+ break;
+ case LY_TYPE_UNION:
+ type = calloc(1, sizeof(struct lysc_type_union));
+ break;
+ case LY_TYPE_BOOL:
+ case LY_TYPE_EMPTY:
+ type = calloc(1, sizeof(struct lysc_type));
+ break;
+ case LY_TYPE_UNKNOWN:
+ break;
+ }
+
+ return type;
+}
+
+/**
* @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_pnode 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.
@@ -1672,16 +1742,19 @@ error:
* @param[in] type_p Parsed type to compile.
* @param[in] basetype Base YANG built-in type of the type to compile.
* @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.
+ * @param[in] base Latest base (compiled) type from which the current type is being derived.
+ * @param[in] plugin Type plugin to use.
+ * @param[in] tpdf_chain Chain of the used typedefs, traversed backwards.
+ * @param[in] tpdf_chain_last Index of the last (backwards) typedef in @p tpdf_chain to use.
+ * @param[out] type Compiled type.
* @return LY_ERR value.
*/
static LY_ERR
lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t context_flags, const char *context_name,
- struct lysp_type *type_p, LY_DATA_TYPE basetype, const char *tpdfname, const struct lysc_type *base,
- struct lysc_type **type)
+ const struct lysp_type *type_p, LY_DATA_TYPE basetype, const char *tpdfname, const struct lysc_type *base,
+ struct lyplg_type *plugin, struct ly_set *tpdf_chain, uint32_t tpdf_chain_last, struct lysc_type **type)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR rc = LY_SUCCESS;
struct lysc_type_bin *bin;
struct lysc_type_num *num;
struct lysc_type_str *str;
@@ -1691,41 +1764,69 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
struct lysc_type_identityref *idref;
struct lysc_type_leafref *lref;
struct lysc_type_union *un;
+ struct lys_type_item *tpdf_item;
+ const struct lysp_type *base_type_p;
+ uint32_t i;
+
+ /* alloc and init */
+ *type = lys_new_type(basetype);
+ LY_CHECK_ERR_GOTO(!(*type), LOGMEM(ctx->ctx), cleanup);
+
+ (*type)->basetype = basetype;
+ (*type)->plugin = plugin;
switch (basetype) {
case LY_TYPE_BINARY:
- bin = (struct lysc_type_bin *)(*type);
+ bin = (struct lysc_type_bin *)*type;
/* RFC 7950 9.8.1, 9.4.4 - length, number of octets it contains */
if (type_p->length) {
- LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
- base ? ((struct lysc_type_bin *)base)->length : NULL, &bin->length));
+ LY_CHECK_GOTO(rc = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+ base ? ((struct lysc_type_bin *)base)->length : NULL, &bin->length), cleanup);
if (!tpdfname) {
- COMPILE_EXTS_GOTO(ctx, type_p->length->exts, bin->length->exts, bin->length, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, type_p->length->exts, bin->length->exts, bin->length, rc, cleanup);
}
}
break;
case LY_TYPE_BITS:
/* RFC 7950 9.7 - bits */
- bits = (struct lysc_type_bits *)(*type);
+ bits = (struct lysc_type_bits *)*type;
if (type_p->bits) {
- LY_CHECK_RET(lys_compile_type_enums(ctx, type_p->bits, basetype,
+ /* compile bits from this type */
+ LY_CHECK_GOTO(rc = lys_compile_type_enums(ctx, type_p->bits, basetype,
base ? (struct lysc_type_bitenum_item *)((struct lysc_type_bits *)base)->bits : NULL,
- (struct lysc_type_bitenum_item **)&bits->bits));
- }
+ (struct lysc_type_bitenum_item **)&bits->bits), cleanup);
+ } else if (base) {
+ /* recompile bits from the first superior type with bits */
+ assert(tpdf_chain->count > tpdf_chain_last);
+ base_type_p = NULL;
+ i = tpdf_chain->count;
+ do {
+ --i;
+ tpdf_item = tpdf_chain->objs[i];
+
+ if (tpdf_item->tpdf->type.bits) {
+ base_type_p = &tpdf_item->tpdf->type;
+ break;
+ }
+ } while (i > tpdf_chain_last);
+ assert(base_type_p);
- if (!base && !type_p->flags) {
+ LY_CHECK_GOTO(rc = lys_compile_type_enums(ctx, base_type_p->bits, basetype, NULL,
+ (struct lysc_type_bitenum_item **)&bits->bits), cleanup);
+ } else {
/* type derived from bits built-in type must contain at least one bit */
if (tpdfname) {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "bit", "bits type ", tpdfname);
} else {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "bit", "bits type", "");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
break;
case LY_TYPE_DEC64:
- dec = (struct lysc_type_dec *)(*type);
+ dec = (struct lysc_type_dec *)*type;
/* RFC 7950 9.3.4 - fraction-digits */
if (!base) {
@@ -1735,7 +1836,8 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
} else {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "fraction-digits", "decimal64 type", "");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
dec->fraction_digits = type_p->fraction_digits;
} else {
@@ -1749,59 +1851,76 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
"Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
dec->fraction_digits = ((struct lysc_type_dec *)base)->fraction_digits;
}
/* RFC 7950 9.2.4 - range */
if (type_p->range) {
- LY_CHECK_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_GOTO(rc = lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
+ base ? ((struct lysc_type_dec *)base)->range : NULL, &dec->range), cleanup);
if (!tpdfname) {
- COMPILE_EXTS_GOTO(ctx, type_p->range->exts, dec->range->exts, dec->range, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, type_p->range->exts, dec->range->exts, dec->range, rc, cleanup);
}
}
break;
case LY_TYPE_STRING:
- str = (struct lysc_type_str *)(*type);
+ str = (struct lysc_type_str *)*type;
/* RFC 7950 9.4.4 - length */
if (type_p->length) {
- LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
- base ? ((struct lysc_type_str *)base)->length : NULL, &str->length));
+ LY_CHECK_GOTO(rc = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+ base ? ((struct lysc_type_str *)base)->length : NULL, &str->length), cleanup);
if (!tpdfname) {
- COMPILE_EXTS_GOTO(ctx, type_p->length->exts, str->length->exts, str->length, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, type_p->length->exts, str->length->exts, str->length, rc, cleanup);
}
} else if (base && ((struct lysc_type_str *)base)->length) {
- str->length = lysc_range_dup(ctx->ctx, ((struct lysc_type_str *)base)->length);
+ str->length = lysc_range_dup(ctx, ((struct lysc_type_str *)base)->length, tpdf_chain, tpdf_chain_last);
}
/* RFC 7950 9.4.5 - pattern */
if (type_p->patterns) {
- LY_CHECK_RET(lys_compile_type_patterns(ctx, type_p->patterns,
- base ? ((struct lysc_type_str *)base)->patterns : NULL, &str->patterns));
+ LY_CHECK_GOTO(rc = lys_compile_type_patterns(ctx, type_p->patterns,
+ base ? ((struct lysc_type_str *)base)->patterns : NULL, &str->patterns), cleanup);
} else if (base && ((struct lysc_type_str *)base)->patterns) {
str->patterns = lysc_patterns_dup(ctx->ctx, ((struct lysc_type_str *)base)->patterns);
}
break;
case LY_TYPE_ENUM:
- enumeration = (struct lysc_type_enum *)(*type);
+ enumeration = (struct lysc_type_enum *)*type;
/* RFC 7950 9.6 - enum */
if (type_p->enums) {
- LY_CHECK_RET(lys_compile_type_enums(ctx, type_p->enums, basetype,
- base ? ((struct lysc_type_enum *)base)->enums : NULL, &enumeration->enums));
- }
+ LY_CHECK_GOTO(rc = lys_compile_type_enums(ctx, type_p->enums, basetype,
+ base ? ((struct lysc_type_enum *)base)->enums : NULL, &enumeration->enums), cleanup);
+ } else if (base) {
+ /* recompile enums from the first superior type with enums */
+ assert(tpdf_chain->count > tpdf_chain_last);
+ base_type_p = NULL;
+ i = tpdf_chain->count;
+ do {
+ --i;
+ tpdf_item = tpdf_chain->objs[i];
+
+ if (tpdf_item->tpdf->type.enums) {
+ base_type_p = &tpdf_item->tpdf->type;
+ break;
+ }
+ } while (i > tpdf_chain_last);
+ assert(base_type_p);
- if (!base && !type_p->flags) {
+ LY_CHECK_GOTO(rc = lys_compile_type_enums(ctx, base_type_p->enums, basetype, NULL, &enumeration->enums), cleanup);
+ } else {
/* type derived from enumerations built-in type must contain at least one enum */
if (tpdfname) {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "enum", "enumeration type ", tpdfname);
} else {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "enum", "enumeration type", "");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
break;
case LY_TYPE_INT8:
@@ -1812,19 +1931,19 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
case LY_TYPE_UINT32:
case LY_TYPE_INT64:
case LY_TYPE_UINT64:
- num = (struct lysc_type_num *)(*type);
+ num = (struct lysc_type_num *)*type;
/* RFC 6020 9.2.4 - range */
if (type_p->range) {
- LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
- base ? ((struct lysc_type_num *)base)->range : NULL, &num->range));
+ LY_CHECK_GOTO(rc = lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
+ base ? ((struct lysc_type_num *)base)->range : NULL, &num->range), cleanup);
if (!tpdfname) {
- COMPILE_EXTS_GOTO(ctx, type_p->range->exts, num->range->exts, num->range, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, type_p->range->exts, num->range->exts, num->range, rc, cleanup);
}
}
break;
case LY_TYPE_IDENT:
- idref = (struct lysc_type_identityref *)(*type);
+ idref = (struct lysc_type_identityref *)*type;
/* RFC 7950 9.10.2 - base */
if (type_p->bases) {
@@ -1838,19 +1957,29 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
"Invalid base substatement for the type not directly derived from identityref built-in type.");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
- LY_CHECK_RET(lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases));
- }
-
- if (!base && !type_p->flags) {
+ LY_CHECK_GOTO(rc = lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases), cleanup);
+ } else if (base) {
+ /* copy all the bases */
+ const struct lysc_type_identityref *idref_base = (struct lysc_type_identityref *)base;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, idref->bases, LY_ARRAY_COUNT(idref_base->bases), rc, cleanup);
+ LY_ARRAY_FOR(idref_base->bases, u) {
+ idref->bases[u] = idref_base->bases[u];
+ LY_ARRAY_INCREMENT(idref->bases);
+ }
+ } else {
/* type derived from identityref built-in type must contain at least one base */
if (tpdfname) {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "base", "identityref type ", tpdfname);
} else {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "base", "identityref type", "");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
break;
case LY_TYPE_LEAFREF:
@@ -1866,7 +1995,8 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
LOGVAL(ctx->ctx, LYVE_SEMANTICS,
"Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
lref->require_instance = type_p->require_instance;
} else if (base) {
@@ -1879,32 +2009,35 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
if (type_p->path) {
LY_VALUE_FORMAT format;
- LY_CHECK_RET(lyxp_expr_dup(ctx->ctx, type_p->path, 0, 0, &lref->path));
- LY_CHECK_RET(lyplg_type_prefix_data_new(ctx->ctx, type_p->path->expr, strlen(type_p->path->expr),
- LY_VALUE_SCHEMA, type_p->pmod, &format, (void **)&lref->prefixes));
+ LY_CHECK_GOTO(rc = lyxp_expr_dup(ctx->ctx, type_p->path, 0, 0, &lref->path), cleanup);
+ LY_CHECK_GOTO(lyplg_type_prefix_data_new(ctx->ctx, type_p->path->expr, strlen(type_p->path->expr),
+ LY_VALUE_SCHEMA, type_p->pmod, &format, (void **)&lref->prefixes), cleanup);
} else if (base) {
- LY_CHECK_RET(lyxp_expr_dup(ctx->ctx, ((struct lysc_type_leafref *)base)->path, 0, 0, &lref->path));
- LY_CHECK_RET(lyplg_type_prefix_data_dup(ctx->ctx, LY_VALUE_SCHEMA_RESOLVED,
- ((struct lysc_type_leafref *)base)->prefixes, (void **)&lref->prefixes));
- } else if (tpdfname) {
- LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "path", "leafref type ", tpdfname);
- return LY_EVALID;
+ LY_CHECK_GOTO(rc = lyxp_expr_dup(ctx->ctx, ((struct lysc_type_leafref *)base)->path, 0, 0, &lref->path), cleanup);
+ LY_CHECK_GOTO(rc = lyplg_type_prefix_data_dup(ctx->ctx, LY_VALUE_SCHEMA_RESOLVED,
+ ((struct lysc_type_leafref *)base)->prefixes, (void **)&lref->prefixes), cleanup);
} else {
- LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "path", "leafref type", "");
- return LY_EVALID;
+ /* type derived from leafref built-in type must contain path */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "path", "leafref type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "path", "leafref type", "");
+ }
+ rc = LY_EVALID;
+ goto cleanup;
}
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;
+ ((struct lysc_type_instanceid *)*type)->require_instance = type_p->require_instance;
} else {
/* default is true */
- ((struct lysc_type_instanceid *)(*type))->require_instance = 1;
+ ((struct lysc_type_instanceid *)*type)->require_instance = 1;
}
break;
case LY_TYPE_UNION:
- un = (struct lysc_type_union *)(*type);
+ un = (struct lysc_type_union *)*type;
/* RFC 7950 7.4 - type */
if (type_p->types) {
@@ -1918,20 +2051,32 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
"Invalid type substatement for the type not directly derived from union built-in type.");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
/* compile the type */
- LY_CHECK_RET(lys_compile_type_union(ctx, type_p->types, context_pnode, context_flags, context_name, &un->types));
- }
-
- if (!base && !type_p->flags) {
+ LY_CHECK_GOTO(rc = lys_compile_type_union(ctx, type_p->types, context_pnode, context_flags, context_name,
+ &un->types), cleanup);
+ } else if (base) {
+ /* copy all the types */
+ const struct lysc_type_union *un_base = (struct lysc_type_union *)base;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, un->types, LY_ARRAY_COUNT(un_base->types), rc, cleanup);
+ LY_ARRAY_FOR(un_base->types, u) {
+ un->types[u] = un_base->types[u];
+ LY_ATOMIC_INC_BARRIER(un->types[u]->refcount);
+ LY_ARRAY_INCREMENT(un->types);
+ }
+ } else {
/* type derived from union built-in type must contain at least one type */
if (tpdfname) {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "type", "union type ", tpdfname);
} else {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "type", "union type", "");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
break;
case LY_TYPE_BOOL:
@@ -1940,65 +2085,27 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
break;
}
- if (tpdfname) {
- switch (basetype) {
- case LY_TYPE_BINARY:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_bin));
- break;
- case LY_TYPE_BITS:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_bits));
- break;
- case LY_TYPE_DEC64:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_dec));
- break;
- case LY_TYPE_STRING:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_str));
- break;
- case LY_TYPE_ENUM:
- 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:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_num));
- break;
- case LY_TYPE_IDENT:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_identityref));
- break;
- case LY_TYPE_LEAFREF:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_leafref));
- break;
- case LY_TYPE_INST:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_instanceid));
- break;
- case LY_TYPE_UNION:
- 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;
- }
+ if (tpdf_chain->count > tpdf_chain_last) {
+ i = tpdf_chain->count;
+ do {
+ --i;
+ tpdf_item = tpdf_chain->objs[i];
+
+ /* compile previous typedefs extensions */
+ COMPILE_EXTS_GOTO(ctx, tpdf_item->tpdf->type.exts, (*type)->exts, *type, rc, cleanup);
+ } while (i > tpdf_chain_last);
}
- LY_CHECK_ERR_RET(!(*type), LOGMEM(ctx->ctx), LY_EMEM);
+
+ /* compile new parsed extensions */
+ COMPILE_EXTS_GOTO(ctx, type_p->exts, (*type)->exts, *type, rc, cleanup);
cleanup:
- return ret;
+ if (rc) {
+ LY_ATOMIC_INC_BARRIER((*type)->refcount);
+ lysc_type_free(&ctx->free_ctx, *type);
+ *type = NULL;
+ }
+ return rc;
}
LY_ERR
@@ -2007,16 +2114,13 @@ lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t
{
LY_ERR ret = LY_SUCCESS;
ly_bool dummyloops = 0;
- struct type_context {
- const struct lysp_tpdf *tpdf;
- struct lysp_node *node;
- } *tctx, *tctx_prev = NULL, *tctx_iter;
+ struct lys_type_item *tctx, *tctx_prev = NULL, *tctx_iter;
LY_DATA_TYPE basetype = LY_TYPE_UNKNOWN;
- struct lysc_type *base = NULL, *prev_type;
+ struct lysc_type *base = NULL;
struct ly_set tpdf_chain = {0};
struct lyplg_type *plugin;
- (*type) = NULL;
+ *type = NULL;
if (dflt) {
*dflt = NULL;
}
@@ -2046,7 +2150,7 @@ lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t
*dflt = (struct lysp_qname *)&tctx->tpdf->dflt;
}
if (dummyloops && (!units || *units) && dflt && *dflt) {
- basetype = ((struct type_context *)tpdf_chain.objs[tpdf_chain.count - 1])->tpdf->type.compiled->basetype;
+ basetype = ((struct lys_type_item *)tpdf_chain.objs[tpdf_chain.count - 1])->tpdf->type.compiled->basetype;
break;
}
@@ -2076,7 +2180,7 @@ lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t
/* circular typedef reference detection */
for (uint32_t u = 0; u < tpdf_chain.count; u++) {
/* local part */
- tctx_iter = (struct type_context *)tpdf_chain.objs[u];
+ tctx_iter = (struct lys_type_item *)tpdf_chain.objs[u];
if (tctx_iter->tpdf == tctx->tpdf) {
LOGVAL(ctx->ctx, LYVE_REFERENCE,
"Invalid \"%s\" type reference - circular chain of types detected.", tctx->tpdf->name);
@@ -2087,7 +2191,7 @@ lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t
}
for (uint32_t u = 0; u < ctx->tpdf_chain.count; u++) {
/* global part for unions corner case */
- tctx_iter = (struct type_context *)ctx->tpdf_chain.objs[u];
+ tctx_iter = (struct lys_type_item *)ctx->tpdf_chain.objs[u];
if (tctx_iter->tpdf == tctx->tpdf) {
LOGVAL(ctx->ctx, LYVE_REFERENCE,
"Invalid \"%s\" type reference - circular chain of types detected.", tctx->tpdf->name);
@@ -2109,68 +2213,23 @@ preparenext:
}
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:
+ /* basic checks */
+ if (basetype == LY_TYPE_UNKNOWN) {
LOGVAL(ctx->ctx, 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, 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 (uint32_t u = tpdf_chain.count - 1; u + 1 > 0; --u) {
- tctx = (struct type_context *)tpdf_chain.objs[u];
+ tctx = (struct lys_type_item *)tpdf_chain.objs[u];
/* remember the typedef context for circular check */
ret = ly_set_add(&ctx->tpdf_chain, tctx, 1, NULL);
@@ -2195,15 +2254,14 @@ preparenext:
}
assert(plugin);
- if ((basetype != LY_TYPE_LEAFREF) && (u != tpdf_chain.count - 1) && !(tctx->tpdf->type.flags) &&
- (plugin == base->plugin)) {
+ if ((basetype != LY_TYPE_LEAFREF) && (u != tpdf_chain.count - 1) && !tctx->tpdf->type.flags &&
+ !tctx->tpdf->type.exts && (plugin == base->plugin)) {
/* no change, reuse the compiled base */
((struct lysp_tpdf *)tctx->tpdf)->type.compiled = base;
LY_ATOMIC_INC_BARRIER(base->refcount);
continue;
}
- LY_ATOMIC_INC_BARRIER((*type)->refcount);
if (~type_substmt_map[basetype] & tctx->tpdf->type.flags) {
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Invalid type \"%s\" restriction(s) for %s type.",
tctx->tpdf->name, ly_data_type2str[basetype]);
@@ -2217,40 +2275,31 @@ preparenext:
goto cleanup;
}
- (*type)->basetype = basetype;
- (*type)->plugin = plugin;
-
- /* collect extensions */
- COMPILE_EXTS_GOTO(ctx, tctx->tpdf->type.exts, (*type)->exts, (*type), ret, cleanup);
-
- /* compile the new typedef */
- prev_type = *type;
- ret = lys_compile_type_(ctx, tctx->node, tctx->tpdf->flags, tctx->tpdf->name,
- &((struct lysp_tpdf *)tctx->tpdf)->type, basetype, tctx->tpdf->name, base, type);
+ /* compile the typedef type */
+ ret = lys_compile_type_(ctx, tctx->node, tctx->tpdf->flags, tctx->tpdf->name, &tctx->tpdf->type, basetype,
+ tctx->tpdf->name, base, plugin, &tpdf_chain, u + 1, &base);
LY_CHECK_GOTO(ret, cleanup);
- base = prev_type;
+
+ /* store separately compiled typedef type to be reused */
+ ((struct lysp_tpdf *)tctx->tpdf)->type.compiled = base;
+ LY_ATOMIC_INC_BARRIER(base->refcount);
}
+
/* remove the processed typedef contexts from the stack for circular check */
ctx->tpdf_chain.count = ctx->tpdf_chain.count - tpdf_chain.count;
/* 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)->plugin = base ? base->plugin : lyplg_type_plugin_find("", NULL, ly_data_type2str[basetype]);
- LY_ATOMIC_INC_BARRIER((*type)->refcount);
+ if (type_p->flags || type_p->exts || !base || (basetype == LY_TYPE_LEAFREF)) {
+ /* leaf type has changes that need to be compiled into the type */
+ plugin = base ? base->plugin : lyplg_type_plugin_find("", NULL, ly_data_type2str[basetype]);
ret = lys_compile_type_(ctx, context_pnode, context_flags, context_name, (struct lysp_type *)type_p, basetype,
- NULL, base, type);
+ NULL, base, plugin, &tpdf_chain, 0, type);
LY_CHECK_GOTO(ret, cleanup);
- } else if ((basetype != LY_TYPE_BOOL) && (basetype != LY_TYPE_EMPTY)) {
- /* no specific restriction in leaf's type definition, copy from the base */
- free(*type);
- (*type) = base;
- LY_ATOMIC_INC_BARRIER((*type)->refcount);
+ } else {
+ /* no changes of the type in the leaf, just use the base compiled type */
+ *type = base;
}
- COMPILE_EXTS_GOTO(ctx, type_p->exts, (*type)->exts, (*type), ret, cleanup);
-
cleanup:
ly_set_erase(&tpdf_chain, free);
return ret;
@@ -2874,6 +2923,7 @@ lys_compile_node_type(struct lysc_ctx *ctx, struct lysp_node *context_node, stru
LY_CHECK_RET(lys_compile_type(ctx, context_node, leaf->flags, leaf->name, type_p, &leaf->type,
leaf->units ? NULL : &leaf->units, &dflt));
+ LY_ATOMIC_INC_BARRIER(leaf->type->refcount);
/* store default value, if any */
if (dflt && !(leaf->flags & LYS_SET_DFLT)) {
@@ -3367,7 +3417,7 @@ lys_compile_node_list(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc
/* YANG 1.0 denies key to be of empty type */
if (key->type->basetype == LY_TYPE_EMPTY) {
LOGVAL(ctx->ctx, LYVE_SEMANTICS,
- "List's key cannot be of \"empty\" type until it is in YANG 1.1 module.");
+ "List key of the \"empty\" type is allowed only in YANG 1.1 modules.");
return LY_EVALID;
}
} else {
diff --git a/src/schema_features.c b/src/schema_features.c
index dca998e..b4273df 100644
--- a/src/schema_features.c
+++ b/src/schema_features.c
@@ -411,7 +411,7 @@ lys_compile_iffeature(const struct ly_ctx *ctx, const struct lysp_qname *qname,
LY_ARRAY_CREATE_RET(ctx, iff->features, f_size, LY_EMEM);
iff->expr = calloc((j = (expr_size / IFF_RECORDS_IN_BYTE) + ((expr_size % IFF_RECORDS_IN_BYTE) ? 1 : 0)), sizeof *iff->expr);
stack.stack = malloc(expr_size * sizeof *stack.stack);
- LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx); rc = LY_EMEM, error);
+ LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx); rc = LY_EMEM, cleanup);
stack.size = expr_size;
f_size--; expr_size--; /* used as indexes from now */
@@ -473,7 +473,7 @@ lys_compile_iffeature(const struct ly_ctx *ctx, const struct lysp_qname *qname,
LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".",
qname->str, (int)(j - i), &c[i]);
rc = LY_EVALID;
- goto error;
+ goto cleanup;
}
iff->features[f_size] = f;
LY_ARRAY_INCREMENT(iff->features);
@@ -489,14 +489,16 @@ lys_compile_iffeature(const struct ly_ctx *ctx, const struct lysp_qname *qname,
/* not all expected operators and operands found */
LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - processing error.", qname->str);
rc = LY_EINT;
- } else {
- rc = LY_SUCCESS;
}
-error:
- /* cleanup */
+cleanup:
+ if (rc) {
+ LY_ARRAY_FREE(iff->features);
+ iff->features = NULL;
+ free(iff->expr);
+ iff->expr = NULL;
+ }
iff_stack_clean(&stack);
-
return rc;
}
diff --git a/src/set.c b/src/set.c
index 1b8bfa5..f36ab5f 100644
--- a/src/set.c
+++ b/src/set.c
@@ -227,9 +227,11 @@ ly_set_rm(struct ly_set *set, void *object, void (*destructor)(void *obj))
return ly_set_rm_index(set, i, destructor);
}
-LY_ERR
+LIBYANG_API_DEF LY_ERR
ly_set_rm_index_ordered(struct ly_set *set, uint32_t index, void (*destructor)(void *obj))
{
+ LY_CHECK_ARG_RET(NULL, set, set->count, LY_EINVAL);
+
if (destructor) {
destructor(set->objs[index]);
}
diff --git a/src/set.h b/src/set.h
index 3f79916..08b2782 100644
--- a/src/set.h
+++ b/src/set.h
@@ -1,9 +1,10 @@
/**
* @file set.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Generic set structure and manipulation routines.
*
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 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.
@@ -81,8 +82,8 @@ LIBYANG_API_DECL LY_ERR ly_set_dup(const struct ly_set *set, void *(*duplicator)
/**
* @brief Add an object into the set
*
- * @param[in] set Set where the \p object will be added.
- * @param[in] object Object to be added into the \p set;
+ * @param[in] set Set where the @p object will be added.
+ * @param[in] object Object to be added into the @p set;
* @param[in] list flag to handle set as a list (without checking for (ignoring) duplicit items)
* @param[out] index_p Optional pointer to return index of the added @p object. Usually it is the last index (::ly_set::count - 1),
* but in case the duplicities are checked and the object is already in the set, the @p object is not added and index of the
@@ -94,7 +95,7 @@ LIBYANG_API_DECL LY_ERR ly_set_dup(const struct ly_set *set, void *(*duplicator)
LIBYANG_API_DECL LY_ERR ly_set_add(struct ly_set *set, const void *object, ly_bool list, uint32_t *index_p);
/**
- * @brief Add all objects from \p src to \p trg.
+ * @brief Add all objects from @p src to @p trg.
*
* Since it is a set, the function checks for duplicities.
*
@@ -102,8 +103,8 @@ LIBYANG_API_DECL LY_ERR ly_set_add(struct ly_set *set, const void *object, ly_bo
* @param[in] src Source set.
* @param[in] list flag to handle set as a list (without checking for (ignoring) duplicit items)
* @param[in] duplicator Optional pointer to function that duplicates the objects being added
- * from \p src into \p trg set. If not provided, the \p trg set will point to the exact same
- * objects as the \p src set.
+ * from @p src into @p trg set. If not provided, the @p trg set will point to the exact same
+ * objects as the @p src set.
* @return LY_SUCCESS in case of success
* @return LY_EINVAL in case of invalid input parameters.
* @return LY_EMEM in case of memory allocation failure.
@@ -134,8 +135,8 @@ LIBYANG_API_DECL void ly_set_clean(struct ly_set *set, void (*destructor)(void *
* Note that after removing the object from a set, indexes of other objects in the set can change
* (the last object is placed instead of the removed object).
*
- * @param[in] set Set from which the \p node will be removed.
- * @param[in] object The object to be removed from the \p set.
+ * @param[in] set Set from which to remove.
+ * @param[in] object The object to be removed from the @p set.
* @param[in] destructor Optional function to free the objects being removed.
* @return LY_ERR return value.
*/
@@ -147,14 +148,26 @@ LIBYANG_API_DECL LY_ERR ly_set_rm(struct ly_set *set, void *object, void (*destr
* Note that after removing the object from a set, indexes of other nodes in the set can change
* (the last object is placed instead of the removed object).
*
- * @param[in] set Set from which a node will be removed.
- * @param[in] index Index of the object to remove in the \p set.
+ * @param[in] set Set from which to remove.
+ * @param[in] index Index of the object to remove in the @p set.
* @param[in] destructor Optional function to free the objects being removed.
* @return LY_ERR return value.
*/
LIBYANG_API_DECL LY_ERR ly_set_rm_index(struct ly_set *set, uint32_t index, void (*destructor)(void *obj));
/**
+ * @brief Remove an object on the specific set index.
+ *
+ * Unlike ::ly_set_rm_indes(), this function moves all the items following the removed one.
+ *
+ * @param[in] set Set from which to remove.
+ * @param[in] index Index of the object to remove in the @p set.
+ * @param[in] destructor Optional function to free the objects being removed.
+ * @return LY_ERR return value.
+ */
+LIBYANG_API_DECL LY_ERR ly_set_rm_index_ordered(struct ly_set *set, uint32_t index, void (*destructor)(void *obj));
+
+/**
* @brief Free the ::ly_set data. If the destructor is not provided, it frees only the set structure
* content, not the referred data.
*
diff --git a/src/tree_data.c b/src/tree_data.c
index d6a04ff..82a1ae8 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -52,6 +52,9 @@
#include "xml.h"
#include "xpath.h"
+static LY_ERR lyd_compare_siblings_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
+ ly_bool parental_schemas_checked);
+
static LYD_FORMAT
lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format)
{
@@ -96,10 +99,9 @@ static LY_ERR
lyd_parse(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, struct lyd_node **first_p,
struct ly_in *in, LYD_FORMAT format, uint32_t parse_opts, uint32_t val_opts, struct lyd_node **op)
{
- LY_ERR rc = LY_SUCCESS;
+ LY_ERR r = LY_SUCCESS, rc = LY_SUCCESS;
struct lyd_ctx *lydctx = NULL;
struct ly_set parsed = {0};
- struct lyd_node *first;
uint32_t i, int_opts = 0;
ly_bool subtree_sibling = 0;
@@ -121,36 +123,40 @@ lyd_parse(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct
/* parse the data */
switch (format) {
case LYD_XML:
- rc = lyd_parse_xml(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
+ r = lyd_parse_xml(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
&subtree_sibling, &lydctx);
break;
case LYD_JSON:
- rc = lyd_parse_json(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
+ r = lyd_parse_json(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
&subtree_sibling, &lydctx);
break;
case LYD_LYB:
- rc = lyd_parse_lyb(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
+ r = lyd_parse_lyb(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
&subtree_sibling, &lydctx);
break;
case LYD_UNKNOWN:
LOGARG(ctx, format);
- rc = LY_EINVAL;
+ r = LY_EINVAL;
break;
}
- LY_CHECK_GOTO(rc, cleanup);
+ if (r) {
+ rc = r;
+ if ((r != LY_EVALID) || !lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) ||
+ (ly_vecode(ctx) == LYVE_SYNTAX)) {
+ goto cleanup;
+ }
+ }
- if (parent) {
- /* get first top-level sibling */
- for (first = parent; first->parent; first = lyd_parent(first)) {}
- first = lyd_first_sibling(first);
- first_p = &first;
+ if (parent && parsed.count) {
+ /* use the first parsed node */
+ first_p = &parsed.dnodes[0];
}
if (!(parse_opts & LYD_PARSE_ONLY)) {
/* validate data */
- rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types,
+ r = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types,
&lydctx->ext_node, &lydctx->ext_val, NULL);
- LY_CHECK_GOTO(rc, cleanup);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
/* set the operation node */
@@ -252,28 +258,7 @@ lyd_parse_data_path(const struct ly_ctx *ctx, const char *path, LYD_FORMAT forma
*
* At least one of @p parent, @p tree, or @p op must always be set.
*
- * Specific @p data_type values have different parameter meaning as follows:
- * - ::LYD_TYPE_RPC_NETCONF:
- * - @p parent - must be NULL, the whole RPC is expected;
- * - @p format - must be ::LYD_XML, NETCONF supports only this format;
- * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- * a separate opaque data tree, even if the function fails, this may be returned;
- * - @p op - must be provided, the RPC/action data tree itself will be returned here, pointing to the operation;
- *
- * - ::LYD_TYPE_NOTIF_NETCONF:
- * - @p parent - must be NULL, the whole notification is expected;
- * - @p format - must be ::LYD_XML, NETCONF supports only this format;
- * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- * a separate opaque data tree, even if the function fails, this may be returned;
- * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
- *
- * - ::LYD_TYPE_REPLY_NETCONF:
- * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
- * - @p format - must be ::LYD_XML, NETCONF supports only this format;
- * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- * a separate opaque data tree, even if the function fails, this may be returned;
- * - @p op - must be NULL, the reply is appended to the RPC;
- * Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
+ * Specific @p data_type values have different parameter meaning as mentioned for ::lyd_parse_op().
*
* @param[in] ctx libyang context.
* @param[in] ext Extension instance providing the specific schema tree to match with the data being parsed.
@@ -295,6 +280,7 @@ lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
struct ly_set parsed = {0};
struct lyd_node *first = NULL, *envp = NULL;
uint32_t i, parse_opts, val_opts, int_opts = 0;
+ ly_bool proto_msg = 0;
if (!ctx) {
ctx = LYD_CTX(parent);
@@ -316,20 +302,51 @@ lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
val_opts = 0;
switch (data_type) {
-
- /* special XML NETCONF data types */
case LYD_TYPE_RPC_NETCONF:
case LYD_TYPE_NOTIF_NETCONF:
LY_CHECK_ARG_RET(ctx, format == LYD_XML, !parent, tree, op, LY_EINVAL);
- /* fallthrough */
+ proto_msg = 1;
+ break;
case LYD_TYPE_REPLY_NETCONF:
- if (data_type == LYD_TYPE_REPLY_NETCONF) {
- LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op,
- LY_EINVAL);
- }
+ LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION),
+ tree, !op, LY_EINVAL);
+ proto_msg = 1;
+ break;
+ case LYD_TYPE_RPC_RESTCONF:
+ case LYD_TYPE_REPLY_RESTCONF:
+ LY_CHECK_ARG_RET(ctx, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op, LY_EINVAL);
+ proto_msg = 1;
+ break;
+ case LYD_TYPE_NOTIF_RESTCONF:
+ LY_CHECK_ARG_RET(ctx, format == LYD_JSON, !parent, tree, op, LY_EINVAL);
+ proto_msg = 1;
+ break;
- /* parse the NETCONF message */
- rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ /* set internal opts */
+ case LYD_TYPE_RPC_YANG:
+ int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS);
+ break;
+ case LYD_TYPE_NOTIF_YANG:
+ int_opts = LYD_INTOPT_NOTIF | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS);
+ break;
+ case LYD_TYPE_REPLY_YANG:
+ int_opts = LYD_INTOPT_REPLY | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS);
+ break;
+ case LYD_TYPE_DATA_YANG:
+ LOGINT(ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+
+ /* parse a full protocol message */
+ if (proto_msg) {
+ if (format == LYD_XML) {
+ /* parse the NETCONF (or RESTCONF XML) message */
+ rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ } else {
+ /* parse the RESTCONF message */
+ rc = lyd_parse_json_restconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ }
if (rc) {
if (envp) {
/* special situation when the envelopes were parsed successfully */
@@ -349,21 +366,6 @@ lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
*op = lydctx->op_node;
}
goto cleanup;
-
- /* set internal opts */
- case LYD_TYPE_RPC_YANG:
- int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
- break;
- case LYD_TYPE_NOTIF_YANG:
- int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS;
- break;
- case LYD_TYPE_REPLY_YANG:
- int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS;
- break;
- case LYD_TYPE_DATA_YANG:
- LOGINT(ctx);
- rc = LY_EINT;
- goto cleanup;
}
/* parse the data */
@@ -405,7 +407,7 @@ cleanup:
lyd_free_tree(parsed.dnodes[i]);
} while (i);
}
- if (tree && ((format != LYD_XML) || !envp)) {
+ if (tree && !envp) {
*tree = NULL;
}
if (op) {
@@ -484,6 +486,10 @@ lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct ly
/* get the first schema sibling */
schema = lys_getnext(NULL, sparent, new_node->schema->module->compiled, getnext_opts);
+ if (!schema) {
+ /* must be a top-level extension instance data, no anchor */
+ return NULL;
+ }
found = 0;
LY_LIST_FOR(match, match) {
@@ -506,8 +512,12 @@ lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct ly
/* current node (match) is a data node still before the new node, continue search in data */
break;
}
+
schema = lys_getnext(schema, sparent, new_node->schema->module->compiled, getnext_opts);
- assert(schema);
+ if (!schema) {
+ /* must be a top-level extension instance data, no anchor */
+ return NULL;
+ }
}
if (found && (match->schema != new_node->schema)) {
@@ -660,6 +670,14 @@ lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling_p, stru
} else {
/* find the anchor, our next node, so we can insert before it */
anchor = lyd_insert_get_next_anchor(first_sibling, node);
+
+ /* cannot insert data node after opaque nodes */
+ if (!anchor && node->schema && first_sibling && !first_sibling->prev->schema) {
+ anchor = first_sibling->prev;
+ while ((anchor != first_sibling) && !anchor->prev->schema) {
+ anchor = anchor->prev;
+ }
+ }
}
if (anchor) {
@@ -684,7 +702,7 @@ lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling_p, stru
lyd_insert_hash(node);
/* finish hashes for our parent, if needed and possible */
- if (node->schema && (node->schema->flags & LYS_KEY) && parent && lyd_insert_has_keys(parent)) {
+ if (node->schema && (node->schema->flags & LYS_KEY) && parent && parent->schema && lyd_insert_has_keys(parent)) {
lyd_hash(parent);
/* now we can insert even the list into its parent HT */
@@ -756,12 +774,12 @@ lyd_insert_child(struct lyd_node *parent, struct lyd_node *node)
}
if (node->parent || node->prev->next) {
- lyd_unlink_tree(node);
+ lyd_unlink(node);
}
while (node) {
iter = node->next;
- lyd_unlink_tree(node);
+ lyd_unlink(node);
lyd_insert_node(parent, NULL, node, 0);
node = iter;
}
@@ -783,7 +801,7 @@ lyplg_ext_insert(struct lyd_node *parent, struct lyd_node *first)
while (first) {
iter = first->next;
- lyd_unlink_tree(first);
+ lyd_unlink(first);
lyd_insert_node(parent, NULL, first, 1);
first = iter;
}
@@ -807,7 +825,7 @@ lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_n
}
if (node->parent || node->prev->next) {
- lyd_unlink_tree(node);
+ lyd_unlink(node);
}
while (node) {
@@ -817,7 +835,7 @@ lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_n
}
iter = node->next;
- lyd_unlink_tree(node);
+ lyd_unlink(node);
lyd_insert_node(NULL, &sibling, node, 0);
node = iter;
}
@@ -850,7 +868,7 @@ lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node)
return LY_EINVAL;
}
- lyd_unlink_tree(node);
+ lyd_unlink(node);
lyd_insert_before_node(sibling, node);
lyd_insert_hash(node);
@@ -874,26 +892,15 @@ lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node)
return LY_EINVAL;
}
- lyd_unlink_tree(node);
+ lyd_unlink(node);
lyd_insert_after_node(sibling, node);
lyd_insert_hash(node);
return LY_SUCCESS;
}
-LIBYANG_API_DEF void
-lyd_unlink_siblings(struct lyd_node *node)
-{
- struct lyd_node *next, *elem, *first = NULL;
-
- LY_LIST_FOR_SAFE(node, next, elem) {
- lyd_unlink_tree(elem);
- lyd_insert_node(NULL, &first, elem, 1);
- }
-}
-
-LIBYANG_API_DEF void
-lyd_unlink_tree(struct lyd_node *node)
+void
+lyd_unlink(struct lyd_node *node)
{
struct lyd_node *iter;
@@ -941,6 +948,35 @@ lyd_unlink_tree(struct lyd_node *node)
node->prev = node;
}
+LIBYANG_API_DEF void
+lyd_unlink_siblings(struct lyd_node *node)
+{
+ struct lyd_node *next, *elem, *first = NULL;
+
+ LY_LIST_FOR_SAFE(node, next, elem) {
+ if (lysc_is_key(elem->schema) && elem->parent) {
+ LOGERR(LYD_CTX(elem), LY_EINVAL, "Cannot unlink a list key \"%s\", unlink the list instance instead.",
+ LYD_NAME(elem));
+ return;
+ }
+
+ lyd_unlink(elem);
+ lyd_insert_node(NULL, &first, elem, 1);
+ }
+}
+
+LIBYANG_API_DEF void
+lyd_unlink_tree(struct lyd_node *node)
+{
+ if (node && lysc_is_key(node->schema) && node->parent) {
+ LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot unlink a list key \"%s\", unlink the list instance instead.",
+ LYD_NAME(node));
+ return;
+ }
+
+ lyd_unlink(node);
+}
+
void
lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_dflt)
{
@@ -973,7 +1009,7 @@ lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_df
LY_ERR
lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
- size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
+ size_t name_len, const char *value, size_t value_len, ly_bool is_utf8, ly_bool *dynamic, LY_VALUE_FORMAT format,
void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, ly_bool clear_dflt, ly_bool *incomplete)
{
LY_ERR ret = LY_SUCCESS;
@@ -1005,7 +1041,7 @@ lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct ly
mt->parent = parent;
mt->annotation = ant;
lyplg_ext_get_storage(ant, LY_STMT_TYPE, sizeof ant_type, (const void **)&ant_type);
- ret = lyd_value_store(mod->ctx, &mt->value, ant_type, value, value_len, dynamic, format, prefix_data, hints,
+ ret = lyd_value_store(mod->ctx, &mt->value, ant_type, value, value_len, is_utf8, dynamic, format, prefix_data, hints,
ctx_node, incomplete);
LY_CHECK_ERR_GOTO(ret, free(mt), cleanup);
ret = lydict_insert(mod->ctx, name, name_len, &mt->name);
@@ -1110,11 +1146,9 @@ finish:
LIBYANG_API_DEF const struct lyd_node_term *
lyd_target(const struct ly_path *path, const struct lyd_node *tree)
{
- struct lyd_node *target;
+ struct lyd_node *target = NULL;
- if (ly_path_eval(path, tree, &target)) {
- return NULL;
- }
+ lyd_find_target(path, tree, &target);
return (struct lyd_node_term *)target;
}
@@ -1149,15 +1183,6 @@ lyd_compare_schema_equal(const struct lysc_node *schema1, const struct lysc_node
return 0;
}
- if (schema1->module->revision || schema2->module->revision) {
- if (!schema1->module->revision || !schema2->module->revision) {
- return 0;
- }
- if (strcmp(schema1->module->revision, schema2->module->revision)) {
- return 0;
- }
- }
-
return 1;
}
@@ -1269,30 +1294,21 @@ lyd_compare_single_value(const struct lyd_node *node1, const struct lyd_node *no
}
/**
- * @brief Internal implementation of @ref lyd_compare_single.
- * @copydoc lyd_compare_single
- * @param[in] parental_schemas_checked Flag used for optimization.
- * When this function is called for the first time, the flag must be set to 0.
- * The @ref lyd_compare_schema_parents_equal should be called only once during
- * recursive calls, and this is accomplished by setting to 1 in the lyd_compare_single_ body.
+ * @brief Compare 2 data nodes if they are equivalent regarding the schema tree.
+ *
+ * Works correctly even if @p node1 and @p node2 have different contexts.
+ *
+ * @param[in] node1 The first node to compare.
+ * @param[in] node2 The second node to compare.
+ * @param[in] options Various @ref datacompareoptions.
+ * @param[in] parental_schemas_checked Flag set if parent schemas were checked for match.
+ * @return LY_SUCCESS if the nodes are equivalent.
+ * @return LY_ENOT if the nodes are not equivalent.
*/
static LY_ERR
-lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
+lyd_compare_single_schema(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
ly_bool parental_schemas_checked)
{
- const struct lyd_node *iter1, *iter2;
- struct lyd_node_any *any1, *any2;
- int len1, len2;
- LY_ERR r;
-
- if (!node1 || !node2) {
- if (node1 == node2) {
- return LY_SUCCESS;
- } else {
- return LY_ENOT;
- }
- }
-
if (LYD_CTX(node1) == LYD_CTX(node2)) {
/* same contexts */
if (options & LYD_COMPARE_OPAQ) {
@@ -1317,6 +1333,28 @@ lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2,
}
}
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compare 2 data nodes if they are equivalent regarding the data they contain.
+ *
+ * Works correctly even if @p node1 and @p node2 have different contexts.
+ *
+ * @param[in] node1 The first node to compare.
+ * @param[in] node2 The second node to compare.
+ * @param[in] options Various @ref datacompareoptions.
+ * @return LY_SUCCESS if the nodes are equivalent.
+ * @return LY_ENOT if the nodes are not equivalent.
+ */
+static LY_ERR
+lyd_compare_single_data(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+{
+ const struct lyd_node *iter1, *iter2;
+ struct lyd_node_any *any1, *any2;
+ int len1, len2;
+ LY_ERR r;
+
if (!(options & LYD_COMPARE_OPAQ) && (node1->hash != node2->hash)) {
return LY_ENOT;
}
@@ -1326,14 +1364,16 @@ lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2,
if (!(options & LYD_COMPARE_OPAQ) && ((node1->schema && !node2->schema) || (!node1->schema && node2->schema))) {
return LY_ENOT;
}
- if ((r = lyd_compare_single_value(node1, node2))) {
- return r;
+ if ((!node1->schema && !node2->schema) || (node1->schema && (node1->schema->nodetype & LYD_NODE_TERM)) ||
+ (node2->schema && (node2->schema->nodetype & LYD_NODE_TERM))) {
+ /* compare values only if there are any to compare */
+ if ((r = lyd_compare_single_value(node1, node2))) {
+ return r;
+ }
}
if (options & LYD_COMPARE_FULL_RECURSION) {
- iter1 = lyd_child(node1);
- iter2 = lyd_child(node2);
- goto all_children_compare;
+ return lyd_compare_siblings_(lyd_child(node1), lyd_child(node2), options, 1);
}
return LY_SUCCESS;
} else {
@@ -1354,50 +1394,38 @@ lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2,
case LYS_RPC:
case LYS_ACTION:
case LYS_NOTIF:
- if (options & LYD_COMPARE_DEFAULTS) {
- if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) {
- return LY_ENOT;
- }
- }
+ /* implicit container is always equal to a container with non-default descendants */
if (options & LYD_COMPARE_FULL_RECURSION) {
- iter1 = lyd_child(node1);
- iter2 = lyd_child(node2);
- goto all_children_compare;
+ return lyd_compare_siblings_(lyd_child(node1), lyd_child(node2), options, 1);
}
return LY_SUCCESS;
case LYS_LIST:
iter1 = lyd_child(node1);
iter2 = lyd_child(node2);
- if (!(node1->schema->flags & LYS_KEYLESS) && !(options & LYD_COMPARE_FULL_RECURSION)) {
- /* lists with keys, their equivalence is based on their keys */
- for (const struct lysc_node *key = lysc_node_child(node1->schema);
- key && (key->flags & LYS_KEY);
- key = key->next) {
- if (lyd_compare_single_(iter1, iter2, options, parental_schemas_checked)) {
- return LY_ENOT;
- }
- iter1 = iter1->next;
- iter2 = iter2->next;
- }
- } else {
- /* lists without keys, their equivalence is based on equivalence of all the children (both direct and indirect) */
+ if (options & LYD_COMPARE_FULL_RECURSION) {
+ return lyd_compare_siblings_(iter1, iter2, options, 1);
+ } else if (node1->schema->flags & LYS_KEYLESS) {
+ /* always equal */
+ return LY_SUCCESS;
+ }
-all_children_compare:
- if (!iter1 && !iter2) {
- /* no children, nothing to compare */
- return LY_SUCCESS;
+ /* lists with keys, their equivalence is based on their keys */
+ for (const struct lysc_node *key = lysc_node_child(node1->schema);
+ key && (key->flags & LYS_KEY);
+ key = key->next) {
+ if (!iter1 || !iter2) {
+ return (iter1 == iter2) ? LY_SUCCESS : LY_ENOT;
}
+ r = lyd_compare_single_schema(iter1, iter2, options, 1);
+ LY_CHECK_RET(r);
+ r = lyd_compare_single_data(iter1, iter2, options);
+ LY_CHECK_RET(r);
- for ( ; iter1 && iter2; iter1 = iter1->next, iter2 = iter2->next) {
- if (lyd_compare_single_(iter1, iter2, options | LYD_COMPARE_FULL_RECURSION, parental_schemas_checked)) {
- return LY_ENOT;
- }
- }
- if (iter1 || iter2) {
- return LY_ENOT;
- }
+ iter1 = iter1->next;
+ iter2 = iter2->next;
}
+
return LY_SUCCESS;
case LYS_ANYXML:
case LYS_ANYDATA:
@@ -1409,9 +1437,7 @@ all_children_compare:
}
switch (any1->value_type) {
case LYD_ANYDATA_DATATREE:
- iter1 = any1->value.tree;
- iter2 = any2->value.tree;
- goto all_children_compare;
+ return lyd_compare_siblings_(any1->value.tree, any2->value.tree, options, 1);
case LYD_ANYDATA_STRING:
case LYD_ANYDATA_XML:
case LYD_ANYDATA_JSON:
@@ -1441,23 +1467,77 @@ all_children_compare:
return LY_EINT;
}
-LIBYANG_API_DEF LY_ERR
-lyd_compare_single(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+/**
+ * @brief Compare all siblings at a node level.
+ *
+ * @param[in] node1 First sibling list.
+ * @param[in] node2 Second sibling list.
+ * @param[in] options Various @ref datacompareoptions.
+ * @param[in] parental_schemas_checked Flag set if parent schemas were checked for match.
+ * @return LY_SUCCESS if equal.
+ * @return LY_ENOT if not equal.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lyd_compare_siblings_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
+ ly_bool parental_schemas_checked)
{
- return lyd_compare_single_(node1, node2, options, 0);
+ LY_ERR r;
+ const struct lyd_node *iter2;
+
+ while (node1 && node2) {
+ /* schema match */
+ r = lyd_compare_single_schema(node1, node2, options, parental_schemas_checked);
+ LY_CHECK_RET(r);
+
+ if (node1->schema && (((node1->schema->nodetype == LYS_LIST) && !(node1->schema->flags & LYS_KEYLESS)) ||
+ ((node1->schema->nodetype == LYS_LEAFLIST) && (node1->schema->flags & LYS_CONFIG_W))) &&
+ (node1->schema->flags & LYS_ORDBY_SYSTEM)) {
+ /* find a matching instance in case they are ordered differently */
+ r = lyd_find_sibling_first(node2, node1, (struct lyd_node **)&iter2);
+ if (r == LY_ENOTFOUND) {
+ /* no matching instance, data not equal */
+ r = LY_ENOT;
+ }
+ LY_CHECK_RET(r);
+ } else {
+ /* compare with the current node */
+ iter2 = node2;
+ }
+
+ /* data match */
+ r = lyd_compare_single_data(node1, iter2, options | LYD_COMPARE_FULL_RECURSION);
+ LY_CHECK_RET(r);
+
+ node1 = node1->next;
+ node2 = node2->next;
+ }
+
+ return (node1 || node2) ? LY_ENOT : LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
-lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+lyd_compare_single(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
{
- for ( ; node1 && node2; node1 = node1->next, node2 = node2->next) {
- LY_CHECK_RET(lyd_compare_single(node1, node2, options));
+ LY_ERR r;
+
+ if (!node1 || !node2) {
+ return (node1 == node2) ? LY_SUCCESS : LY_ENOT;
}
- if (node1 == node2) {
- return LY_SUCCESS;
+ /* schema match */
+ if ((r = lyd_compare_single_schema(node1, node2, options, 0))) {
+ return r;
}
- return LY_ENOT;
+
+ /* data match */
+ return lyd_compare_single_data(node1, node2, options);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+{
+ return lyd_compare_siblings_(node1, node2, options, 0);
}
LIBYANG_API_DEF LY_ERR
@@ -1676,6 +1756,9 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_
} else {
dup->flags = (node->flags & (LYD_DEFAULT | LYD_EXT)) | LYD_NEW;
}
+ if (options & LYD_DUP_WITH_PRIV) {
+ dup->priv = node->priv;
+ }
if (trg_ctx == LYD_CTX(node)) {
dup->schema = node->schema;
} else {
@@ -1736,7 +1819,7 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_
/* store canonical value in the target context */
val_can = lyd_get_value(node);
type = ((struct lysc_node_leaf *)term->schema)->type;
- ret = lyd_value_store(trg_ctx, &term->value, type, val_can, strlen(val_can), NULL, LY_VALUE_CANON, NULL,
+ ret = lyd_value_store(trg_ctx, &term->value, type, val_can, strlen(val_can), 1, NULL, LY_VALUE_CANON, NULL,
LYD_HINT_DATA, term->schema, NULL);
LY_CHECK_GOTO(ret, error);
}
@@ -1787,10 +1870,11 @@ error:
* @return LY_ERR value.
*/
static LY_ERR
-lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_ctx, const struct lyd_node_inner *parent,
- uint32_t options, struct lyd_node **dup_parent, struct lyd_node_inner **local_parent)
+lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent,
+ uint32_t options, struct lyd_node **dup_parent, struct lyd_node **local_parent)
{
- const struct lyd_node_inner *orig_parent, *iter;
+ const struct lyd_node *orig_parent;
+ struct lyd_node *iter = NULL;
ly_bool repeat = 1, ext_parent = 0;
*dup_parent = NULL;
@@ -1799,38 +1883,38 @@ lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_c
if (node->flags & LYD_EXT) {
ext_parent = 1;
}
- for (orig_parent = node->parent; repeat && orig_parent; orig_parent = orig_parent->parent) {
+ for (orig_parent = lyd_parent(node); repeat && orig_parent; orig_parent = lyd_parent(orig_parent)) {
if (ext_parent) {
/* use the standard context */
trg_ctx = LYD_CTX(orig_parent);
}
- if (parent && (parent->schema == orig_parent->schema)) {
+ if (parent && (LYD_CTX(parent) == LYD_CTX(orig_parent)) && (parent->schema == orig_parent->schema)) {
/* stop creating parents, connect what we have into the provided parent */
iter = parent;
repeat = 0;
+ } else if (parent && (LYD_CTX(parent) != LYD_CTX(orig_parent)) &&
+ lyd_compare_schema_equal(parent->schema, orig_parent->schema) &&
+ lyd_compare_schema_parents_equal(parent, orig_parent)) {
+ iter = parent;
+ repeat = 0;
} else {
iter = NULL;
- LY_CHECK_RET(lyd_dup_r((struct lyd_node *)orig_parent, trg_ctx, NULL, 0, (struct lyd_node **)&iter, options,
- (struct lyd_node **)&iter));
- }
- if (!*local_parent) {
- *local_parent = (struct lyd_node_inner *)iter;
- }
- if (iter->child) {
- /* 1) list - add after keys
- * 2) provided parent with some children */
- iter->child->prev->next = *dup_parent;
+ LY_CHECK_RET(lyd_dup_r(orig_parent, trg_ctx, NULL, 0, &iter, options, &iter));
+
+ /* insert into the previous duplicated parent */
if (*dup_parent) {
- (*dup_parent)->prev = iter->child->prev;
- iter->child->prev = *dup_parent;
+ lyd_insert_node(iter, NULL, *dup_parent, 0);
}
- } else {
- ((struct lyd_node_inner *)iter)->child = *dup_parent;
+
+ /* update the last duplicated parent */
+ *dup_parent = iter;
}
- if (*dup_parent) {
- (*dup_parent)->parent = (struct lyd_node_inner *)iter;
+
+ /* set the first parent */
+ if (!*local_parent) {
+ *local_parent = iter;
}
- *dup_parent = (struct lyd_node *)iter;
+
if (orig_parent->flags & LYD_EXT) {
ext_parent = 1;
}
@@ -1838,23 +1922,27 @@ lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_c
if (repeat && parent) {
/* given parent and created parents chain actually do not interconnect */
- LOGERR(trg_ctx, LY_EINVAL,
- "Invalid argument parent (%s()) - does not interconnect with the created node's parents chain.", __func__);
+ LOGERR(trg_ctx, LY_EINVAL, "None of the duplicated node \"%s\" schema parents match the provided parent \"%s\".",
+ LYD_NAME(node), LYD_NAME(parent));
return LY_EINVAL;
}
+ if (*dup_parent && parent) {
+ /* last insert into a prevously-existing parent */
+ lyd_insert_node(parent, NULL, *dup_parent, 0);
+ }
return LY_SUCCESS;
}
static LY_ERR
-lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent, uint32_t options,
+lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, uint32_t options,
ly_bool nosiblings, struct lyd_node **dup)
{
LY_ERR rc;
const struct lyd_node *orig; /* original node to be duplicated */
struct lyd_node *first = NULL; /* the first duplicated node, this is returned */
struct lyd_node *top = NULL; /* the most higher created node */
- struct lyd_node_inner *local_parent = NULL; /* the direct parent node for the duplicated node(s) */
+ struct lyd_node *local_parent = NULL; /* the direct parent node for the duplicated node(s) */
assert(node && trg_ctx);
@@ -1869,7 +1957,7 @@ lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_no
if (lysc_is_key(orig->schema)) {
if (local_parent) {
/* the key must already exist in the parent */
- rc = lyd_find_sibling_schema(local_parent->child, orig->schema, first ? NULL : &first);
+ rc = lyd_find_sibling_schema(lyd_child(local_parent), orig->schema, first ? NULL : &first);
LY_CHECK_ERR_GOTO(rc, LOGINT(trg_ctx), error);
} else {
assert(!(options & LYD_DUP_WITH_PARENTS));
@@ -1879,8 +1967,7 @@ lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_no
}
} else {
/* if there is no local parent, it will be inserted into first */
- rc = lyd_dup_r(orig, trg_ctx, local_parent ? &local_parent->node : NULL, 0, &first, options,
- first ? NULL : &first);
+ rc = lyd_dup_r(orig, trg_ctx, local_parent, 0, &first, options, first ? NULL : &first);
LY_CHECK_GOTO(rc, error);
}
if (nosiblings) {
@@ -1923,7 +2010,7 @@ lyd_dup_ctx_check(const struct lyd_node *node, const struct lyd_node_inner *pare
for (iter = node; iter && !(iter->flags & LYD_EXT); iter = lyd_parent(iter)) {}
if (!iter || !lyd_parent(iter) || (LYD_CTX(lyd_parent(iter)) != LYD_CTX(parent))) {
- LOGERR(NULL, LY_EINVAL, "Different contexts used in node duplication.");
+ LOGERR(LYD_CTX(node), LY_EINVAL, "Different contexts used in node duplication.");
return LY_EINVAL;
}
}
@@ -1937,7 +2024,7 @@ lyd_dup_single(const struct lyd_node *node, struct lyd_node_inner *parent, uint3
LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
LY_CHECK_RET(lyd_dup_ctx_check(node, parent));
- return lyd_dup(node, LYD_CTX(node), parent, options, 1, dup);
+ return lyd_dup(node, LYD_CTX(node), (struct lyd_node *)parent, options, 1, dup);
}
LIBYANG_API_DEF LY_ERR
@@ -1946,7 +2033,7 @@ lyd_dup_single_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx,
{
LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL);
- return lyd_dup(node, trg_ctx, parent, options, 1, dup);
+ return lyd_dup(node, trg_ctx, (struct lyd_node *)parent, options, 1, dup);
}
LIBYANG_API_DEF LY_ERR
@@ -1955,7 +2042,7 @@ lyd_dup_siblings(const struct lyd_node *node, struct lyd_node_inner *parent, uin
LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
LY_CHECK_RET(lyd_dup_ctx_check(node, parent));
- return lyd_dup(node, LYD_CTX(node), parent, options, 0, dup);
+ return lyd_dup(node, LYD_CTX(node), (struct lyd_node *)parent, options, 0, dup);
}
LIBYANG_API_DEF LY_ERR
@@ -1964,7 +2051,7 @@ lyd_dup_siblings_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ct
{
LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL);
- return lyd_dup(node, trg_ctx, parent, options, 0, dup);
+ return lyd_dup(node, trg_ctx, (struct lyd_node *)parent, options, 0, dup);
}
LIBYANG_API_DEF LY_ERR
@@ -2019,13 +2106,13 @@ finish:
*/
static LY_ERR
lyd_merge_sibling_r(struct lyd_node **first_trg, struct lyd_node *parent_trg, const struct lyd_node **sibling_src_p,
- lyd_merge_cb merge_cb, void *cb_data, uint16_t options, struct lyd_dup_inst **dup_inst)
+ lyd_merge_cb merge_cb, void *cb_data, uint16_t options, struct ly_ht **dup_inst)
{
const struct lyd_node *child_src, *tmp, *sibling_src;
struct lyd_node *match_trg, *dup_src, *elem;
struct lyd_node_opaq *opaq_trg, *opaq_src;
struct lysc_type *type;
- struct lyd_dup_inst *child_dup_inst = NULL;
+ struct ly_ht *child_dup_inst = NULL;
LY_ERR ret;
ly_bool first_inst = 0;
@@ -2110,7 +2197,7 @@ lyd_merge_sibling_r(struct lyd_node **first_trg, struct lyd_node *parent_trg, co
/* node not found, merge it */
if (options & LYD_MERGE_DESTRUCT) {
dup_src = (struct lyd_node *)sibling_src;
- lyd_unlink_tree(dup_src);
+ lyd_unlink(dup_src);
/* spend it */
*sibling_src_p = NULL;
} else {
@@ -2147,7 +2234,7 @@ lyd_merge(struct lyd_node **target, const struct lyd_node *source, const struct
lyd_merge_cb merge_cb, void *cb_data, uint16_t options, ly_bool nosiblings)
{
const struct lyd_node *sibling_src, *tmp;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
ly_bool first;
LY_ERR ret = LY_SUCCESS;
@@ -2357,9 +2444,9 @@ lyd_path(const struct lyd_node *node, LYD_PATH_TYPE pathtype, char *buffer, size
for (iter = node, i = 1; i < depth; iter = lyd_parent(iter), ++i) {}
iter_print:
/* get the module */
- mod = iter->schema ? iter->schema->module : lyd_owner_module(iter);
+ mod = lyd_node_module(iter);
parent = lyd_parent(iter);
- prev_mod = (parent && parent->schema) ? parent->schema->module : lyd_owner_module(parent);
+ prev_mod = lyd_node_module(parent);
if (prev_mod == mod) {
mod = NULL;
}
@@ -2429,15 +2516,17 @@ lyd_path_set(const struct ly_set *dnodes, LYD_PATH_TYPE pathtype)
for (depth = 1; depth <= dnodes->count; ++depth) {
/* current node */
iter = dnodes->dnodes[depth - 1];
- mod = iter->schema ? iter->schema->module : lyd_owner_module(iter);
+ mod = lyd_node_module(iter);
/* parent */
parent = (depth > 1) ? dnodes->dnodes[depth - 2] : NULL;
- assert(!parent || !iter->schema || !parent->schema || (lysc_data_parent(iter->schema) == parent->schema) ||
- (!lysc_data_parent(iter->schema) && (LYD_CTX(iter) != LYD_CTX(parent))));
+ assert(!parent || !iter->schema || !parent->schema || (parent->schema->nodetype & LYD_NODE_ANY) ||
+ (lysc_data_parent(iter->schema) == parent->schema) ||
+ (!lysc_data_parent(iter->schema) && (LYD_CTX(iter) != LYD_CTX(parent))) ||
+ (parent->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)));
/* get module to print, if any */
- prev_mod = (parent && parent->schema) ? parent->schema->module : lyd_owner_module(parent);
+ prev_mod = lyd_node_module(parent);
if (prev_mod == mod) {
mod = NULL;
}
@@ -2567,14 +2656,14 @@ lyd_find_sibling_first(const struct lyd_node *siblings, const struct lyd_node *t
siblings = lyd_first_sibling(siblings);
parent = siblings->parent;
- if (parent && parent->schema && parent->children_ht) {
+ if (target->schema && parent && parent->schema && parent->children_ht) {
assert(target->hash);
if (lysc_is_dup_inst_list(target->schema)) {
/* we must search the instances from beginning to find the first matching one */
found = 0;
LYD_LIST_FOR_INST(siblings, target->schema, iter) {
- if (!lyd_compare_single(target, iter, 0)) {
+ if (!lyd_compare_single(target, iter, LYD_COMPARE_FULL_RECURSION)) {
found = 1;
break;
}
@@ -2594,10 +2683,16 @@ lyd_find_sibling_first(const struct lyd_node *siblings, const struct lyd_node *t
}
}
} else {
- /* no children hash table */
+ /* no children hash table or cannot be used */
for ( ; siblings; siblings = siblings->next) {
- if (!lyd_compare_single(siblings, target, LYD_COMPARE_OPAQ)) {
- break;
+ if (lysc_is_dup_inst_list(target->schema)) {
+ if (!lyd_compare_single(siblings, target, LYD_COMPARE_FULL_RECURSION)) {
+ break;
+ }
+ } else {
+ if (!lyd_compare_single(siblings, target, 0)) {
+ break;
+ }
}
}
}
@@ -2661,7 +2756,7 @@ lyd_find_sibling_val(const struct lyd_node *siblings, const struct lysc_node *sc
/* create a data node and find the instance */
if (schema->nodetype == LYS_LEAFLIST) {
/* target used attributes: schema, hash, value */
- rc = lyd_create_term(schema, key_or_value, val_len, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &target);
+ rc = lyd_create_term(schema, key_or_value, val_len, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &target);
LY_CHECK_RET(rc);
} else {
/* target used attributes: schema, hash, child (all keys) */
@@ -2684,6 +2779,7 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_
{
struct lyd_node **match_p, *first, *iter;
struct lyd_node_inner *parent;
+ uint32_t comp_opts;
LY_CHECK_ARG_RET(NULL, target, set, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(siblings ? LYD_CTX(siblings) : NULL, LYD_CTX(target), LY_EINVAL);
@@ -2695,6 +2791,9 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_
return LY_ENOTFOUND;
}
+ /* set options */
+ comp_opts = (lysc_is_dup_inst_list(target->schema) ? LYD_COMPARE_FULL_RECURSION : 0);
+
/* get first sibling */
siblings = lyd_first_sibling(siblings);
@@ -2719,7 +2818,7 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_
}
while (iter) {
/* add all found nodes into the set */
- if ((iter != first) && !lyd_compare_single(iter, target, 0) && ly_set_add(*set, iter, 1, NULL)) {
+ if ((iter != first) && !lyd_compare_single(iter, target, comp_opts) && ly_set_add(*set, iter, 1, NULL)) {
goto error;
}
@@ -2734,7 +2833,7 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_
} else {
/* no children hash table */
LY_LIST_FOR(siblings, siblings) {
- if (!lyd_compare_single(target, siblings, LYD_COMPARE_OPAQ)) {
+ if (!lyd_compare_single(target, siblings, comp_opts)) {
ly_set_add(*set, (void *)siblings, 1, NULL);
}
}
@@ -2756,8 +2855,22 @@ lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struc
{
LY_CHECK_ARG_RET(NULL, name, LY_EINVAL);
+ if (first && first->schema) {
+ first = first->prev;
+ if (first->schema) {
+ /* no opaque nodes */
+ first = NULL;
+ } else {
+ /* opaque nodes are at the end, find quickly the first */
+ while (!first->prev->schema) {
+ first = first->prev;
+ }
+ }
+ }
+
for ( ; first; first = first->next) {
- if (!first->schema && !strcmp(LYD_NAME(first), name)) {
+ assert(!first->schema);
+ if (!strcmp(LYD_NAME(first), name)) {
break;
}
}
@@ -2769,58 +2882,19 @@ lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struc
}
LIBYANG_API_DEF LY_ERR
-lyd_find_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath, LY_VALUE_FORMAT format,
- void *prefix_data, const struct lyxp_var *vars, struct ly_set **set)
+lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, struct ly_set **set)
{
- LY_ERR ret = LY_SUCCESS;
- struct lyxp_set xp_set = {0};
- struct lyxp_expr *exp = NULL;
- uint32_t i;
-
- LY_CHECK_ARG_RET(NULL, tree, xpath, format, set, LY_EINVAL);
-
- *set = NULL;
-
- /* parse expression */
- ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(tree), xpath, 0, 1, &exp);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* evaluate expression */
- ret = lyxp_eval(LYD_CTX(tree), exp, NULL, format, prefix_data, ctx_node, ctx_node, tree, vars, &xp_set,
- LYXP_IGNORE_WHEN);
- LY_CHECK_GOTO(ret, cleanup);
-
- if (xp_set.type != LYXP_SET_NODE_SET) {
- LOGERR(LYD_CTX(tree), LY_EINVAL, "XPath \"%s\" result is not a node set.", xpath);
- ret = LY_EINVAL;
- goto cleanup;
- }
-
- /* allocate return set */
- ret = ly_set_new(set);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
- /* transform into ly_set, allocate memory for all the elements once (even though not all items must be
- * elements but most likely will be) */
- (*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
- LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(LYD_CTX(tree)); ret = LY_EMEM, cleanup);
- (*set)->size = xp_set.used;
+ return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set);
+}
- for (i = 0; i < xp_set.used; ++i) {
- if (xp_set.val.nodes[i].type == LYXP_NODE_ELEM) {
- ret = ly_set_add(*set, xp_set.val.nodes[i].node, 1, NULL);
- LY_CHECK_GOTO(ret, cleanup);
- }
- }
+LIBYANG_API_DEF LY_ERR
+lyd_find_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, struct ly_set **set)
+{
+ LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
-cleanup:
- lyxp_set_free_content(&xp_set);
- lyxp_expr_free((struct ly_ctx *)LYD_CTX(tree), exp);
- if (ret) {
- ly_set_free(*set, NULL);
- *set = NULL;
- }
- return ret;
+ return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set);
}
LIBYANG_API_DEF LY_ERR
@@ -2833,63 +2907,263 @@ lyd_find_xpath3(const struct lyd_node *ctx_node, const struct lyd_node *tree, co
}
LIBYANG_API_DEF LY_ERR
-lyd_find_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, struct ly_set **set)
+lyd_find_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath, LY_VALUE_FORMAT format,
+ void *prefix_data, const struct lyxp_var *vars, struct ly_set **set)
{
- LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, tree, xpath, set, LY_EINVAL);
- return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set);
+ *set = NULL;
+
+ return lyd_eval_xpath4(ctx_node, tree, NULL, xpath, format, prefix_data, vars, NULL, set, NULL, NULL, NULL);
}
LIBYANG_API_DEF LY_ERR
-lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, struct ly_set **set)
+lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result)
{
- LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
+ return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result);
+}
- return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set);
+LIBYANG_API_DEF LY_ERR
+lyd_eval_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, ly_bool *result)
+{
+ return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, vars, result);
}
LIBYANG_API_DEF LY_ERR
lyd_eval_xpath3(const struct lyd_node *ctx_node, const struct lys_module *cur_mod, const char *xpath,
LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, ly_bool *result)
{
+ return lyd_eval_xpath4(ctx_node, ctx_node, cur_mod, xpath, format, prefix_data, vars, NULL, NULL, NULL, NULL, result);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_eval_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const struct lys_module *cur_mod,
+ const char *xpath, LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, LY_XPATH_TYPE *ret_type,
+ struct ly_set **node_set, char **string, long double *number, ly_bool *boolean)
+{
LY_ERR ret = LY_SUCCESS;
struct lyxp_set xp_set = {0};
struct lyxp_expr *exp = NULL;
+ uint32_t i;
- LY_CHECK_ARG_RET(NULL, ctx_node, xpath, result, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, tree, xpath, ((ret_type && node_set && string && number && boolean) ||
+ (node_set && !string && !number && !boolean) || (!node_set && string && !number && !boolean) ||
+ (!node_set && !string && number && !boolean) || (!node_set && !string && !number && boolean)), LY_EINVAL);
- /* compile expression */
- ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(ctx_node), xpath, 0, 1, &exp);
+ /* parse expression */
+ ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(tree), xpath, 0, 1, &exp);
LY_CHECK_GOTO(ret, cleanup);
/* evaluate expression */
- ret = lyxp_eval(LYD_CTX(ctx_node), exp, cur_mod, format, prefix_data, ctx_node, ctx_node, ctx_node, vars, &xp_set,
+ ret = lyxp_eval(LYD_CTX(tree), exp, cur_mod, format, prefix_data, ctx_node, ctx_node, tree, vars, &xp_set,
LYXP_IGNORE_WHEN);
LY_CHECK_GOTO(ret, cleanup);
- /* transform into boolean */
- ret = lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
- LY_CHECK_GOTO(ret, cleanup);
+ /* return expected result type without or with casting */
+ if (node_set) {
+ /* node set */
+ if (xp_set.type == LYXP_SET_NODE_SET) {
+ /* transform into a set */
+ LY_CHECK_GOTO(ret = ly_set_new(node_set), cleanup);
+ (*node_set)->objs = malloc(xp_set.used * sizeof *(*node_set)->objs);
+ LY_CHECK_ERR_GOTO(!(*node_set)->objs, LOGMEM(LYD_CTX(tree)); ret = LY_EMEM, cleanup);
+ (*node_set)->size = xp_set.used;
+ for (i = 0; i < xp_set.used; ++i) {
+ if (xp_set.val.nodes[i].type == LYXP_NODE_ELEM) {
+ ret = ly_set_add(*node_set, xp_set.val.nodes[i].node, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+ if (ret_type) {
+ *ret_type = LY_XPATH_NODE_SET;
+ }
+ } else if (!string && !number && !boolean) {
+ LOGERR(LYD_CTX(tree), LY_EINVAL, "XPath \"%s\" result is not a node set.", xpath);
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+ }
- /* set result */
- *result = xp_set.val.bln;
+ if (string) {
+ if ((xp_set.type != LYXP_SET_STRING) && !node_set) {
+ /* cast into string */
+ LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_STRING), cleanup);
+ }
+ if (xp_set.type == LYXP_SET_STRING) {
+ /* string */
+ *string = xp_set.val.str;
+ xp_set.val.str = NULL;
+ if (ret_type) {
+ *ret_type = LY_XPATH_STRING;
+ }
+ }
+ }
+
+ if (number) {
+ if ((xp_set.type != LYXP_SET_NUMBER) && !node_set) {
+ /* cast into number */
+ LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_NUMBER), cleanup);
+ }
+ if (xp_set.type == LYXP_SET_NUMBER) {
+ /* number */
+ *number = xp_set.val.num;
+ if (ret_type) {
+ *ret_type = LY_XPATH_NUMBER;
+ }
+ }
+ }
+
+ if (boolean) {
+ if ((xp_set.type != LYXP_SET_BOOLEAN) && !node_set) {
+ /* cast into boolean */
+ LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN), cleanup);
+ }
+ if (xp_set.type == LYXP_SET_BOOLEAN) {
+ /* boolean */
+ *boolean = xp_set.val.bln;
+ if (ret_type) {
+ *ret_type = LY_XPATH_BOOLEAN;
+ }
+ }
+ }
cleanup:
lyxp_set_free_content(&xp_set);
- lyxp_expr_free((struct ly_ctx *)LYD_CTX(ctx_node), exp);
+ lyxp_expr_free((struct ly_ctx *)LYD_CTX(tree), exp);
return ret;
}
-LIBYANG_API_DEF LY_ERR
-lyd_eval_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, ly_bool *result)
+/**
+ * @brief Hash table node equal callback.
+ */
+static ly_bool
+lyd_trim_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
{
- return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, vars, result);
+ struct lyd_node *node1, *node2;
+
+ node1 = *(struct lyd_node **)val1_p;
+ node2 = *(struct lyd_node **)val2_p;
+
+ return node1 == node2;
}
LIBYANG_API_DEF LY_ERR
-lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result)
+lyd_trim_xpath(struct lyd_node **tree, const char *xpath, const struct lyxp_var *vars)
{
- return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result);
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_ctx *ctx;
+ struct lyxp_set xp_set = {0};
+ struct lyxp_expr *exp = NULL;
+ struct lyd_node *node, *parent;
+ struct lyxp_set_hash_node hnode;
+ struct ly_ht *parent_ht = NULL;
+ struct ly_set free_set = {0};
+ uint32_t i, hash;
+ ly_bool is_result;
+
+ LY_CHECK_ARG_RET(NULL, tree, xpath, LY_EINVAL);
+
+ if (!*tree) {
+ /* nothing to do */
+ goto cleanup;
+ }
+
+ *tree = lyd_first_sibling(*tree);
+ ctx = (struct ly_ctx *)LYD_CTX(*tree);
+
+ /* parse expression */
+ ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* evaluate expression */
+ ret = lyxp_eval(ctx, exp, NULL, LY_VALUE_JSON, NULL, *tree, *tree, *tree, vars, &xp_set, LYXP_IGNORE_WHEN);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* create hash table for all the parents of results */
+ parent_ht = lyht_new(32, sizeof node, lyd_trim_equal_cb, NULL, 1);
+ LY_CHECK_GOTO(!parent_ht, cleanup);
+
+ for (i = 0; i < xp_set.used; ++i) {
+ if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) {
+ /* ignore */
+ continue;
+ }
+
+ for (parent = lyd_parent(xp_set.val.nodes[i].node); parent; parent = lyd_parent(parent)) {
+ /* add the parent into parent_ht */
+ ret = lyht_insert(parent_ht, &parent, parent->hash, NULL);
+ if (ret == LY_EEXIST) {
+ /* shared parent, we are done */
+ break;
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+ hnode.type = LYXP_NODE_ELEM;
+ LY_LIST_FOR(*tree, parent) {
+ LYD_TREE_DFS_BEGIN(parent, node) {
+ if (lysc_is_key(node->schema)) {
+ /* ignore */
+ goto next_iter;
+ }
+
+ /* check the results */
+ is_result = 0;
+ if (xp_set.ht) {
+ hnode.node = node;
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
+
+ if (!lyht_find(xp_set.ht, &hnode, hash, NULL)) {
+ is_result = 1;
+ }
+ } else {
+ /* not enough elements for a hash table */
+ for (i = 0; i < xp_set.used; ++i) {
+ if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) {
+ /* ignore */
+ continue;
+ }
+
+ if (xp_set.val.nodes[i].node == node) {
+ is_result = 1;
+ break;
+ }
+ }
+ }
+
+ if (is_result) {
+ /* keep the whole subtree if the node is in the results */
+ LYD_TREE_DFS_continue = 1;
+ } else if (lyht_find(parent_ht, &node, node->hash, NULL)) {
+ /* free the whole subtree if the node is not even among the selected parents */
+ ret = ly_set_add(&free_set, node, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ LYD_TREE_DFS_continue = 1;
+ } /* else keep the parent node because a subtree is in the results */
+
+next_iter:
+ LYD_TREE_DFS_END(parent, node);
+ }
+ }
+
+ /* free */
+ for (i = 0; i < free_set.count; ++i) {
+ node = free_set.dnodes[i];
+ if (*tree == node) {
+ *tree = (*tree)->next;
+ }
+ lyd_free_tree(node);
+ }
+
+cleanup:
+ lyxp_set_free_content(&xp_set);
+ lyxp_expr_free(ctx, exp);
+ lyht_free(parent_ht, NULL);
+ ly_set_erase(&free_set, NULL);
+ return ret;
}
LIBYANG_API_DEF LY_ERR
@@ -2903,7 +3177,7 @@ lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output,
/* parse the path */
ret = ly_path_parse(LYD_CTX(ctx_node), ctx_node->schema, path, strlen(path), 0, LY_PATH_BEGIN_EITHER,
- LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_SIMPLE, &expr);
+ LY_PATH_PREFIX_FIRST, LY_PATH_PRED_SIMPLE, &expr);
LY_CHECK_GOTO(ret, cleanup);
/* compile the path */
@@ -2912,7 +3186,7 @@ lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output,
LY_CHECK_GOTO(ret, cleanup);
/* evaluate the path */
- ret = ly_path_eval_partial(lypath, ctx_node, NULL, match);
+ ret = ly_path_eval_partial(lypath, ctx_node, NULL, 0, NULL, match);
cleanup:
lyxp_expr_free(LYD_CTX(ctx_node), expr);
@@ -2928,7 +3202,7 @@ lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct
LY_CHECK_ARG_RET(NULL, path, LY_EINVAL);
- ret = ly_path_eval(path, tree, &m);
+ ret = ly_path_eval(path, tree, NULL, &m);
if (ret) {
if (match) {
*match = NULL;
@@ -2941,3 +3215,55 @@ lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct
}
return LY_SUCCESS;
}
+
+LIBYANG_API_DEF struct lyd_node *
+lyd_parent(const struct lyd_node *node)
+{
+ if (!node || !node->parent) {
+ return NULL;
+ }
+
+ return &node->parent->node;
+}
+
+LIBYANG_API_DEF struct lyd_node *
+lyd_child(const struct lyd_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ if (!node->schema) {
+ /* opaq node */
+ return ((const struct lyd_node_opaq *)node)->child;
+ }
+
+ switch (node->schema->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_LIST:
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ return ((const struct lyd_node_inner *)node)->child;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF const char *
+lyd_get_value(const struct lyd_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ if (!node->schema) {
+ return ((const struct lyd_node_opaq *)node)->value;
+ } else if (node->schema->nodetype & LYD_NODE_TERM) {
+ const struct lyd_value *value = &((const struct lyd_node_term *)node)->value;
+
+ return value->_canonical ? value->_canonical : lyd_value_get_canonical(LYD_CTX(node), value);
+ }
+
+ return NULL;
+}
diff --git a/src/tree_data.h b/src/tree_data.h
index ff98f60..4d87fba 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -822,7 +822,7 @@ struct lyd_node_inner {
}; /**< common part corresponding to ::lyd_node */
struct lyd_node *child; /**< pointer to the first child node. */
- struct hash_table *children_ht; /**< hash table with all the direct children (except keys for a list, lists without keys) */
+ struct ly_ht *children_ht; /**< hash table with all the direct children (except keys for a list, lists without keys) */
#define LYD_HT_MIN_ITEMS 4 /**< minimal number of children to create ::lyd_node_inner.children_ht hash table. */
};
@@ -1004,15 +1004,7 @@ struct lyd_node_opaq {
* @return Pointer to the parent node of the @p node.
* @return NULL in case of the top-level node or if the @p node is NULL itself.
*/
-static inline struct lyd_node *
-lyd_parent(const struct lyd_node *node)
-{
- if (!node || !node->parent) {
- return NULL;
- }
-
- return &node->parent->node;
-}
+LIBYANG_API_DECL struct lyd_node *lyd_parent(const struct lyd_node *node);
/**
* @brief Get the child pointer of a generic data node.
@@ -1024,29 +1016,7 @@ lyd_parent(const struct lyd_node *node)
* @param[in] node Node to use.
* @return Pointer to the first child node (if any) of the @p node.
*/
-static inline struct lyd_node *
-lyd_child(const struct lyd_node *node)
-{
- if (!node) {
- return NULL;
- }
-
- if (!node->schema) {
- /* opaq node */
- return ((const struct lyd_node_opaq *)node)->child;
- }
-
- switch (node->schema->nodetype) {
- case LYS_CONTAINER:
- case LYS_LIST:
- case LYS_RPC:
- case LYS_ACTION:
- case LYS_NOTIF:
- return ((const struct lyd_node_inner *)node)->child;
- default:
- return NULL;
- }
-}
+LIBYANG_API_DECL struct lyd_node *lyd_child(const struct lyd_node *node);
/**
* @brief Get the child pointer of a generic data node but skip its keys in case it is ::LYS_LIST.
@@ -1072,6 +1042,14 @@ LIBYANG_API_DECL struct lyd_node *lyd_child_no_keys(const struct lyd_node *node)
LIBYANG_API_DECL const struct lys_module *lyd_owner_module(const struct lyd_node *node);
/**
+ * @brief Get the module of a node. Useful mainly for opaque nodes.
+ *
+ * @param[in] node Node to examine.
+ * @return Module of the node.
+ */
+LIBYANG_API_DECL const struct lys_module *lyd_node_module(const struct lyd_node *node);
+
+/**
* @brief Check whether a node value equals to its default one.
*
* @param[in] node Term node to test.
@@ -1133,23 +1111,7 @@ LIBYANG_API_DECL const char *lyd_value_get_canonical(const struct ly_ctx *ctx, c
* @param[in] node Data node to use.
* @return Canonical value.
*/
-static inline const char *
-lyd_get_value(const struct lyd_node *node)
-{
- if (!node) {
- return NULL;
- }
-
- if (!node->schema) {
- return ((const struct lyd_node_opaq *)node)->value;
- } else if (node->schema->nodetype & LYD_NODE_TERM) {
- const struct lyd_value *value = &((const struct lyd_node_term *)node)->value;
-
- return value->_canonical ? value->_canonical : lyd_value_get_canonical(LYD_CTX(node), value);
- }
-
- return NULL;
-}
+LIBYANG_API_DECL const char *lyd_get_value(const struct lyd_node *node);
/**
* @brief Get anydata string value.
@@ -1172,6 +1134,14 @@ LIBYANG_API_DECL LY_ERR lyd_any_copy_value(struct lyd_node *trg, const union lyd
LYD_ANYDATA_VALUETYPE value_type);
/**
+ * @brief Get schema node of a data node. Useful especially for opaque nodes.
+ *
+ * @param[in] node Data node to use.
+ * @return Schema node represented by data @p node, NULL if there is none.
+ */
+LIBYANG_API_DECL const struct lysc_node *lyd_node_schema(const struct lyd_node *node);
+
+/**
* @brief Create a new inner node in the data tree.
*
* To create list, use ::lyd_new_list() or ::lyd_new_list2().
@@ -1287,6 +1257,57 @@ LIBYANG_API_DECL LY_ERR lyd_new_list2(struct lyd_node *parent, const struct lys_
const char *keys, ly_bool output, struct lyd_node **node);
/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] key_values Ordered key string values of the new list instance, all must be set.
+ * @param[in] value_lengths Array of lengths of each @p key_values, may be NULL if @p key_values are 0-terminated strings.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_list3(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const char **key_values, uint32_t *value_lengths, ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] key_values Ordered key binary (LYB) values of the new list instance, all must be set.
+ * @param[in] value_lengths Array of lengths of each @p key_values.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_list3_bin(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const void **key_values, uint32_t *value_lengths, ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] key_values Ordered key canonical values of the new list instance, all must be set.
+ * @param[in] value_lengths Array of lengths of each @p key_values, may be NULL if @p key_values are 0-terminated strings.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_list3_canon(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const char **key_values, uint32_t *value_lengths, ly_bool output, struct lyd_node **node);
+
+/**
* @brief Create a new term node in the data tree.
*
* To create a top-level term node defined in an extension instance, use ::lyd_new_ext_term().
@@ -1506,6 +1527,7 @@ LIBYANG_API_DECL LY_ERR lyd_new_attr2(struct lyd_node *parent, const char *modul
#define LYD_NEW_PATH_CANON_VALUE 0x10 /**< Interpret the provided leaf/leaf-list @p value as being in the canonical
(or JSON if no defined) ::LY_VALUE_CANON format. If it is not, it may lead
to unexpected behavior. */
+#define LYD_NEW_PATH_WITH_OPAQ 0x20 /**< Consider opaque nodes normally when searching for existing nodes. */
/** @} pathoptions */
@@ -1519,7 +1541,8 @@ LIBYANG_API_DECL LY_ERR lyd_new_attr2(struct lyd_node *parent, const char *modul
* Also, if a leaf-list is being created and both a predicate is defined in @p path
* and @p value is set, the predicate is preferred.
*
- * For key-less lists and non-configuration leaf-lists, positional predicates should be used (indices starting from 1).
+ * For key-less lists, positional predicates must be used (indices starting from 1). For non-configuration leaf-lists
+ * either positional predicate can be used or leaf-list predicate, when an instance is always created at the end.
* If no predicate is used for these nodes, they are always created.
*
* @param[in] parent Data parent to add to/modify, can be NULL. Note that in case a first top-level sibling is used,
@@ -1531,7 +1554,11 @@ LIBYANG_API_DECL LY_ERR lyd_new_attr2(struct lyd_node *parent, const char *modul
* For other node types, it should be NULL.
* @param[in] options Bitmask of options, see @ref pathoptions.
* @param[out] node Optional first created node.
- * @return LY_ERR value.
+ * @return LY_SUCCESS on success.
+ * @return LY_EEXIST if the final node to create exists (unless ::LYD_NEW_PATH_UPDATE is used).
+ * @return LY_EINVAL on invalid arguments including invalid @p path.
+ * @return LY_EVALID on invalid @p value.
+ * @return LY_ERR on other errors.
*/
LIBYANG_API_DECL LY_ERR lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const char *value,
uint32_t options, struct lyd_node **node);
@@ -1554,7 +1581,11 @@ LIBYANG_API_DECL LY_ERR lyd_new_path(struct lyd_node *parent, const struct ly_ct
* @param[in] options Bitmask of options, see @ref pathoptions.
* @param[out] new_parent Optional first parent node created. If only one node was created, equals to @p new_node.
* @param[out] new_node Optional last node created.
- * @return LY_ERR value.
+ * @return LY_SUCCESS on success.
+ * @return LY_EEXIST if the final node to create exists (unless ::LYD_NEW_PATH_UPDATE is used).
+ * @return LY_EINVAL on invalid arguments including invalid @p path.
+ * @return LY_EVALID on invalid @p value.
+ * @return LY_ERR on other errors.
*/
LIBYANG_API_DECL LY_ERR lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
size_t value_len, LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent,
@@ -1576,7 +1607,11 @@ LIBYANG_API_DECL LY_ERR lyd_new_path2(struct lyd_node *parent, const struct ly_c
* @param[in] value Value of the new leaf/leaf-list. For other node types, it should be NULL.
* @param[in] options Bitmask of options, see @ref pathoptions.
* @param[out] node Optional first created node.
- * @return LY_ERR value.
+ * @return LY_SUCCESS on success.
+ * @return LY_EEXIST if the final node to create exists (unless ::LYD_NEW_PATH_UPDATE is used).
+ * @return LY_EINVAL on invalid arguments including invalid @p path.
+ * @return LY_EVALID on invalid @p value.
+ * @return LY_ERR on other errors.
*/
LIBYANG_API_DECL LY_ERR lyd_new_ext_path(struct lyd_node *parent, const struct lysc_ext_instance *ext, const char *path,
const void *value, uint32_t options, struct lyd_node **node);
@@ -1662,7 +1697,7 @@ LIBYANG_API_DECL LY_ERR lyd_change_term(struct lyd_node *term, const char *val_s
* is always cleared.
*
* @param[in] term Term node to change.
- * @param[in] value New value to set in binary format, see @ref howtoDataLYB.
+ * @param[in] value New value to set in binary format (usually a pointer), see @ref howtoDataLYB.
* @param[in] value_len Length of @p value.
* @return LY_SUCCESS if value was changed,
* @return LY_EEXIST if value was the same and only the default flag was cleared,
@@ -1927,6 +1962,8 @@ LIBYANG_API_DECL LY_ERR lyd_compare_meta(const struct lyd_meta *meta1, const str
#define LYD_DUP_WITH_FLAGS 0x08 /**< Also copy any data node flags. That will cause the duplicated data to preserve
its validation/default node state. */
#define LYD_DUP_NO_EXT 0x10 /**< Do not duplicate nodes with the ::LYD_EXT flag (nested extension instance data). */
+#define LYD_DUP_WITH_PRIV 0x20 /**< Also copy data node private pointer. Only the pointer is copied, it still points
+ to the same data. */
/** @} dupoptions */
@@ -2131,7 +2168,7 @@ LIBYANG_API_DECL LY_ERR lyd_merge_module(struct lyd_node **target, const struct
* @param[in] first First data tree.
* @param[in] second Second data tree.
* @param[in] options Bitmask of options flags, see @ref diffoptions.
- * @param[out] diff Generated diff.
+ * @param[out] diff Generated diff, NULL if there are no differences.
* @return LY_SUCCESS on success,
* @return LY_ERR on error.
*/
@@ -2146,7 +2183,7 @@ LIBYANG_API_DECL LY_ERR lyd_diff_tree(const struct lyd_node *first, const struct
* @param[in] first First data tree.
* @param[in] second Second data tree.
* @param[in] options Bitmask of options flags, see @ref diffoptions.
- * @param[out] diff Generated diff.
+ * @param[out] diff Generated diff, NULL if there are no differences.
* @return LY_SUCCESS on success,
* @return LY_ERR on error.
*/
@@ -2302,6 +2339,10 @@ typedef enum {
/**
* @brief Generate path of the given node in the requested format.
*
+ * The path is constructed based on the parent node(s) of this node. When run on a node which is disconnected
+ * from its parent(s), this function might yield unexpected results such as `/example:b` instead of the expected
+ * `/example:a/b`.
+ *
* @param[in] node Data path of this node will be generated.
* @param[in] pathtype Format of the path to generate.
* @param[in,out] buffer Prepared buffer of the @p buflen length to store the generated path.
@@ -2419,6 +2460,8 @@ LIBYANG_API_DECL void lyxp_vars_free(struct lyxp_var *vars);
* `leaf-list[.=...]`, these instances are found using hashes with constant (*O(1)*) complexity
* (unless they are defined in top-level). Other predicates can still follow the aforementioned ones.
*
+ * Opaque nodes are part of the evaluation.
+ *
* @param[in] ctx_node XPath context node.
* @param[in] xpath [XPath](@ref howtoXPath) to select in JSON format. It must evaluate into a node set.
* @param[out] set Set of found data nodes. In case the result is a number, a string, or a boolean,
@@ -2527,11 +2570,61 @@ LIBYANG_API_DECL LY_ERR lyd_eval_xpath3(const struct lyd_node *ctx_node, const s
const char *xpath, LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, ly_bool *result);
/**
+ * @brief XPath result type.
+ */
+typedef enum {
+ LY_XPATH_NODE_SET, /**< XPath node set */
+ LY_XPATH_STRING, /**< XPath string */
+ LY_XPATH_NUMBER, /**< XPath number */
+ LY_XPATH_BOOLEAN /**< XPath boolean */
+} LY_XPATH_TYPE;
+
+/**
+ * @brief Evaluate an XPath on data and return the result or convert it first to an expected result type.
+ *
+ * Either all return type parameters @p node_set, @p string, @p number, and @p boolean with @p ret_type
+ * are provided or exactly one of @p node_set, @p string, @p number, and @p boolean is provided with @p ret_type
+ * being obvious and hence optional.
+ *
+ * @param[in] ctx_node XPath context node, NULL for the root node.
+ * @param[in] tree Data tree to evaluate on.
+ * @param[in] cur_mod Current module of @p xpath, needed for some kinds of @p format.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select.
+ * @param[in] format Format of any prefixes in @p xpath.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[in] vars Optional [sized array](@ref sizedarrays) of XPath variables.
+ * @param[out] ret_type XPath type of the result selecting which of @p node_set, @p string, @p number, and @p boolean to use.
+ * @param[out] node_set XPath node set result.
+ * @param[out] string XPath string result.
+ * @param[out] number XPath number result.
+ * @param[out] boolean XPath boolean result.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_eval_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree,
+ const struct lys_module *cur_mod, const char *xpath, LY_VALUE_FORMAT format, void *prefix_data,
+ const struct lyxp_var *vars, LY_XPATH_TYPE *ret_type, struct ly_set **node_set, char **string,
+ long double *number, ly_bool *boolean);
+
+/**
+ * @brief Evaluate an XPath on data and free all the nodes except the subtrees selected by the expression.
+ *
+ * @param[in,out] tree Data tree to evaluate on and trim.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select in JSON format.
+ * @param[in] vars Optional [sized array](@ref sizedarrays) of XPath variables.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DEF LY_ERR lyd_trim_xpath(struct lyd_node **tree, const char *xpath, const struct lyxp_var *vars);
+
+/**
* @brief Search in given data for a node uniquely identified by a path.
*
* Always works in constant (*O(1)*) complexity. To be exact, it is *O(n)* where *n* is the depth
* of the path used.
*
+ * Opaque nodes are NEVER found/traversed.
+ *
* @param[in] ctx_node Path context node.
* @param[in] path [Path](@ref howtoXPath) to find.
* @param[in] output Whether to search in RPC/action output nodes or in input nodes.
@@ -2557,6 +2650,21 @@ LIBYANG_API_DECL LY_ERR lyd_find_path(const struct lyd_node *ctx_node, const cha
LIBYANG_API_DECL LY_ERR lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct lyd_node **match);
/**
+ * @brief Get current timezone (including DST setting) UTC (GMT) time offset in seconds.
+ *
+ * @return Timezone shift in seconds.
+ */
+LIBYANG_API_DECL int ly_time_tz_offset(void);
+
+/**
+ * @brief Get UTC (GMT) timezone offset in seconds at a specific timestamp (including DST setting).
+ *
+ * @param[in] time Timestamp to get the offset at.
+ * @return Timezone shift in seconds.
+ */
+LIBYANG_API_DECL int ly_time_tz_offset_at(time_t time);
+
+/**
* @brief Convert date-and-time from string to UNIX timestamp and fractions of a second.
*
* @param[in] value Valid string date-and-time value.
diff --git a/src/tree_data_common.c b/src/tree_data_common.c
index f35f8f5..672f720 100644
--- a/src/tree_data_common.c
+++ b/src/tree_data_common.c
@@ -44,32 +44,65 @@
#include "xpath.h"
/**
+ * @brief Callback for checking first instance hash table values equivalence.
+ *
+ * @param[in] val1_p If not @p mod, pointer to the first instance.
+ * @param[in] val2_p If not @p mod, pointer to the found dup inst item.
+ */
+static ly_bool
+lyht_dup_inst_ht_equal_cb(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(cb_data))
+{
+ if (mod) {
+ struct lyd_dup_inst **item1 = val1_p, **item2 = val2_p;
+
+ /* equal on 2 dup inst items */
+ return *item1 == *item2 ? 1 : 0;
+ } else {
+ struct lyd_node **first_inst = val1_p;
+ struct lyd_dup_inst **item = val2_p;
+
+ /* equal on dup inst item and a first instance */
+ return (*item)->set->dnodes[0] == *first_inst ? 1 : 0;
+ }
+}
+
+/**
* @brief Find an entry in duplicate instance cache for an instance. Create it if it does not exist.
*
- * @param[in] first_inst Instance of the cache entry.
- * @param[in,out] dup_inst_cache Duplicate instance cache.
+ * @param[in] first_inst First instance of the cache entry.
+ * @param[in] dup_inst_ht Duplicate instance cache hash table.
* @return Instance cache entry.
*/
static struct lyd_dup_inst *
-lyd_dup_inst_get(const struct lyd_node *first_inst, struct lyd_dup_inst **dup_inst_cache)
+lyd_dup_inst_get(const struct lyd_node *first_inst, struct ly_ht **dup_inst_ht)
{
- struct lyd_dup_inst *item;
- LY_ARRAY_COUNT_TYPE u;
+ struct lyd_dup_inst **item_p, *item;
- LY_ARRAY_FOR(*dup_inst_cache, u) {
- if ((*dup_inst_cache)[u].inst_set->dnodes[0] == first_inst) {
- return &(*dup_inst_cache)[u];
+ if (*dup_inst_ht) {
+ /* find the item of the first instance */
+ if (!lyht_find(*dup_inst_ht, &first_inst, first_inst->hash, (void **)&item_p)) {
+ return *item_p;
}
+ } else {
+ /* create the hash table */
+ *dup_inst_ht = lyht_new(2, sizeof item, lyht_dup_inst_ht_equal_cb, NULL, 1);
+ LY_CHECK_RET(!*dup_inst_ht, NULL);
}
- /* it was not added yet, add it now */
- LY_ARRAY_NEW_RET(LYD_CTX(first_inst), *dup_inst_cache, item, NULL);
+ /* first instance has no dup inst item, create it */
+ item = calloc(1, sizeof *item);
+ LY_CHECK_RET(!item, NULL);
+
+ /* add into the hash table */
+ if (lyht_insert(*dup_inst_ht, &item, first_inst->hash, NULL)) {
+ return NULL;
+ }
return item;
}
LY_ERR
-lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struct lyd_dup_inst **dup_inst_cache)
+lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struct ly_ht **dup_inst_ht)
{
struct lyd_dup_inst *dup_inst;
@@ -80,40 +113,47 @@ lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struc
/* there can be more exact same instances (even if not allowed in invalid data) and we must make sure we do not
* match a single node more times */
- dup_inst = lyd_dup_inst_get(*inst, dup_inst_cache);
+ dup_inst = lyd_dup_inst_get(*inst, dup_inst_ht);
LY_CHECK_ERR_RET(!dup_inst, LOGMEM(LYD_CTX(siblings)), LY_EMEM);
if (!dup_inst->used) {
/* we did not cache these instances yet, do so */
- lyd_find_sibling_dup_inst_set(siblings, *inst, &dup_inst->inst_set);
- assert(dup_inst->inst_set->count && (dup_inst->inst_set->dnodes[0] == *inst));
+ lyd_find_sibling_dup_inst_set(siblings, *inst, &dup_inst->set);
+ assert(dup_inst->set->count && (dup_inst->set->dnodes[0] == *inst));
}
- if (dup_inst->used == dup_inst->inst_set->count) {
+ if (dup_inst->used == dup_inst->set->count) {
if (lysc_is_dup_inst_list((*inst)->schema)) {
/* we have used all the instances */
*inst = NULL;
} /* else just keep using the last (ideally only) instance */
} else {
- assert(dup_inst->used < dup_inst->inst_set->count);
+ assert(dup_inst->used < dup_inst->set->count);
/* use another instance */
- *inst = dup_inst->inst_set->dnodes[dup_inst->used];
+ *inst = dup_inst->set->dnodes[dup_inst->used];
++dup_inst->used;
}
return LY_SUCCESS;
}
-void
-lyd_dup_inst_free(struct lyd_dup_inst *dup_inst)
+/**
+ * @brief Callback for freeing first instance hash table values.
+ */
+static void
+lyht_dup_inst_ht_free_cb(void *val_p)
{
- LY_ARRAY_COUNT_TYPE u;
+ struct lyd_dup_inst **item = val_p;
- LY_ARRAY_FOR(dup_inst, u) {
- ly_set_free(dup_inst[u].inst_set, NULL);
- }
- LY_ARRAY_FREE(dup_inst);
+ ly_set_free((*item)->set, NULL);
+ free(*item);
+}
+
+void
+lyd_dup_inst_free(struct ly_ht *dup_inst_ht)
+{
+ lyht_free(dup_inst_ht, lyht_dup_inst_ht_free_cb);
}
struct lyd_node *
@@ -180,12 +220,12 @@ lyxp_vars_set(struct lyxp_var **vars, const char *name, const char *value)
return LY_EINVAL;
}
- /* If variable is already defined then change its value. */
- if (*vars && !lyxp_vars_find(*vars, name, 0, &item)) {
+ /* if variable is already defined then change its value */
+ if (*vars && !lyxp_vars_find(NULL, *vars, name, 0, &item)) {
var_value = strdup(value);
LY_CHECK_RET(!var_value, LY_EMEM);
- /* Set new value. */
+ /* update value */
free(item->value);
item->value = var_value;
} else {
@@ -193,7 +233,7 @@ lyxp_vars_set(struct lyxp_var **vars, const char *name, const char *value)
var_value = strdup(value);
LY_CHECK_ERR_GOTO(!var_name || !var_value, ret = LY_EMEM, error);
- /* Add new variable. */
+ /* add new variable */
LY_ARRAY_NEW_GOTO(NULL, *vars, item, ret, error);
item->name = var_name;
item->value = var_value;
@@ -260,21 +300,68 @@ lyd_owner_module(const struct lyd_node *node)
return NULL;
}
+ while (!node->schema && node->parent) {
+ node = lyd_parent(node);
+ }
+
if (!node->schema) {
+ /* top-level opaque node */
opaq = (struct lyd_node_opaq *)node;
switch (opaq->format) {
case LY_VALUE_XML:
- return opaq->name.module_ns ? ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns) : NULL;
+ if (opaq->name.module_ns) {
+ return ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns);
+ }
+ break;
case LY_VALUE_JSON:
- return opaq->name.module_name ? ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name) : NULL;
+ if (opaq->name.module_name) {
+ return ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name);
+ }
+ break;
default:
return NULL;
}
+
+ return NULL;
}
return lysc_owner_module(node->schema);
}
+LIBYANG_API_DEF const struct lys_module *
+lyd_node_module(const struct lyd_node *node)
+{
+ const struct lyd_node_opaq *opaq;
+
+ while (node) {
+ /* data node */
+ if (node->schema) {
+ return node->schema->module;
+ }
+
+ /* opaque node */
+ opaq = (struct lyd_node_opaq *)node;
+ switch (opaq->format) {
+ case LY_VALUE_XML:
+ if (opaq->name.module_ns) {
+ return ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns);
+ }
+ break;
+ case LY_VALUE_JSON:
+ if (opaq->name.module_name) {
+ return ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name);
+ }
+ break;
+ default:
+ break;
+ }
+
+ node = lyd_parent(node);
+ }
+
+ return NULL;
+}
+
void
lyd_first_module_sibling(struct lyd_node **node, const struct lys_module *mod)
{
@@ -389,12 +476,12 @@ lyd_data_next_module(struct lyd_node **next, struct lyd_node **first)
LY_ERR
lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct lysc_type *type, const void *value,
- size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ size_t value_len, ly_bool is_utf8, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
const struct lysc_node *ctx_node, ly_bool *incomplete)
{
LY_ERR ret;
struct ly_err_item *err = NULL;
- uint32_t options = (dynamic && *dynamic ? LYPLG_TYPE_STORE_DYNAMIC : 0);
+ uint32_t options = 0;
if (!value) {
value = "";
@@ -403,6 +490,13 @@ lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct ly
*incomplete = 0;
}
+ if (dynamic && *dynamic) {
+ options |= LYPLG_TYPE_STORE_DYNAMIC;
+ }
+ if (is_utf8) {
+ options |= LYPLG_TYPE_STORE_IS_UTF8;
+ }
+
ret = type->plugin->store(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node, val, NULL, &err);
if (dynamic) {
*dynamic = 0;
@@ -440,8 +534,8 @@ lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc_type *
LOGVAL_ERRITEM(ctx, err);
ly_err_free(err);
} else {
- LOGVAL(ctx, LYVE_OTHER, "Resolving value \"%s\" failed.", type->plugin->print(ctx, val, LY_VALUE_CANON,
- NULL, NULL, NULL));
+ LOGVAL(ctx, LYVE_OTHER, "Resolving value \"%s\" failed.",
+ (char *)type->plugin->print(ctx, val, LY_VALUE_CANON, NULL, NULL, NULL));
}
return ret;
}
@@ -450,15 +544,15 @@ lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc_type *
}
LY_ERR
-lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
- LY_VALUE_FORMAT format, void *prefix_data)
+ly_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
+ LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints)
{
LY_ERR rc = LY_SUCCESS;
struct ly_err_item *err = NULL;
struct lyd_value storage;
struct lysc_type *type;
- LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
+ LY_CHECK_ARG_RET(ctx, node, LY_EINVAL);
if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
LOGARG(ctx, node);
@@ -466,8 +560,8 @@ lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const
}
type = ((struct lysc_node_leaf *)node)->type;
- rc = type->plugin->store(ctx ? ctx : node->module->ctx, type, value, value_len, 0, format, prefix_data,
- LYD_HINT_SCHEMA, node, &storage, NULL, &err);
+ rc = type->plugin->store(ctx ? ctx : node->module->ctx, type, value, value_len, 0, format, prefix_data, hints, node,
+ &storage, NULL, &err);
if (rc == LY_EINCOMPLETE) {
/* actually success since we do not provide the context tree and call validation with
* LY_TYPE_OPTS_INCOMPLETE_DATA */
@@ -478,14 +572,13 @@ lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const
if (err->path) {
LOG_LOCSET(NULL, NULL, err->path, NULL);
} else {
- /* use at least the schema path */
LOG_LOCSET(node, NULL, NULL, NULL);
}
LOGVAL_ERRITEM(ctx, err);
if (err->path) {
LOG_LOCBACK(0, 0, 1, 0);
} else {
- LOG_LOCBACK(1, 0, 0, 0);
+ LOG_LOCBACK(1, 0, 1, 0);
}
}
ly_err_free(err);
@@ -590,7 +683,7 @@ lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t va
/* store the value */
LOG_LOCSET(node->schema, &node->node, NULL, NULL);
- ret = lyd_value_store(ctx, &val, type, value, value_len, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, node->schema, NULL);
+ ret = lyd_value_store(ctx, &val, type, value, value_len, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, node->schema, NULL);
LOG_LOCBACK(1, 1, 0, 0);
LY_CHECK_RET(ret);
@@ -704,24 +797,15 @@ lyd_parse_opaq_list_error(const struct lyd_node *node, const struct lysc_node *s
assert(!node->schema);
/* get all keys into a set */
- while ((key = lys_getnext(key, snode, NULL, 0)) && (snode->flags & LYS_KEY)) {
- LY_CHECK_GOTO(ret = ly_set_add(&key_set, (void *)snode, 1, NULL), cleanup);
+ while ((key = lys_getnext(key, snode, NULL, 0)) && (key->flags & LYS_KEY)) {
+ LY_CHECK_GOTO(ret = ly_set_add(&key_set, (void *)key, 1, NULL), cleanup);
}
LY_LIST_FOR(lyd_child(node), child) {
- if (child->schema) {
- LOGERR(LYD_CTX(node), LY_EINVAL, "Unexpected node %s \"%s\".", lys_nodetype2str(child->schema->nodetype),
- LYD_NAME(child));
- ret = LY_EINVAL;
- goto cleanup;
- }
-
- opaq_k = (struct lyd_node_opaq *)child;
-
/* find the key schema node */
for (i = 0; i < key_set.count; ++i) {
key = key_set.snodes[i];
- if (!strcmp(key->name, opaq_k->name.name)) {
+ if (!strcmp(key->name, LYD_NAME(child))) {
break;
}
}
@@ -733,9 +817,15 @@ lyd_parse_opaq_list_error(const struct lyd_node *node, const struct lysc_node *s
/* key found */
ly_set_rm_index(&key_set, i, NULL);
+ if (child->schema) {
+ /* valid key */
+ continue;
+ }
+
/* check value */
- ret = lys_value_validate(LYD_CTX(node), key, opaq_k->value, strlen(opaq_k->value), opaq_k->format,
- opaq_k->val_prefix_data);
+ opaq_k = (struct lyd_node_opaq *)child;
+ ret = ly_value_validate(LYD_CTX(node), key, opaq_k->value, strlen(opaq_k->value), opaq_k->format,
+ opaq_k->val_prefix_data, opaq_k->hints);
LY_CHECK_GOTO(ret, cleanup);
}
@@ -754,93 +844,124 @@ cleanup:
LIBYANG_API_DEF LY_ERR
lyd_parse_opaq_error(const struct lyd_node *node)
{
+ LY_ERR rc = LY_SUCCESS;
const struct ly_ctx *ctx;
const struct lyd_node_opaq *opaq;
const struct lyd_node *parent;
const struct lys_module *mod;
- const struct lysc_node *snode;
+ const struct lysc_node *sparent, *snode;
+ uint32_t loc_node = 0, loc_path = 0;
- LY_CHECK_ARG_RET(LYD_CTX(node), node, !node->schema, !lyd_parent(node) || lyd_parent(node)->schema, LY_EINVAL);
+ LY_CHECK_ARG_RET(LYD_CTX(node), node, !node->schema, LY_EINVAL);
ctx = LYD_CTX(node);
opaq = (struct lyd_node_opaq *)node;
parent = lyd_parent(node);
+ sparent = lyd_node_schema(parent);
+
+ if (parent) {
+ LOG_LOCSET(NULL, parent, NULL, NULL);
+ ++loc_node;
+ } else {
+ LOG_LOCSET(NULL, NULL, "/", NULL);
+ ++loc_path;
+ }
if (!opaq->name.module_ns) {
LOGVAL(ctx, LYVE_REFERENCE, "Unknown module of node \"%s\".", opaq->name.name);
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
/* module */
switch (opaq->format) {
case LY_VALUE_XML:
- if (!parent || strcmp(opaq->name.module_ns, parent->schema->module->ns)) {
+ if (!sparent || strcmp(opaq->name.module_ns, sparent->module->ns)) {
mod = ly_ctx_get_module_implemented_ns(ctx, opaq->name.module_ns);
if (!mod) {
LOGVAL(ctx, LYVE_REFERENCE, "No (implemented) module with namespace \"%s\" of node \"%s\" in the context.",
opaq->name.module_ns, opaq->name.name);
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
} else {
/* inherit */
- mod = parent->schema->module;
+ mod = sparent->module;
}
break;
case LY_VALUE_JSON:
case LY_VALUE_LYB:
- if (!parent || strcmp(opaq->name.module_name, parent->schema->module->name)) {
+ if (!sparent || strcmp(opaq->name.module_name, sparent->module->name)) {
mod = ly_ctx_get_module_implemented(ctx, opaq->name.module_name);
if (!mod) {
LOGVAL(ctx, LYVE_REFERENCE, "No (implemented) module named \"%s\" of node \"%s\" in the context.",
opaq->name.module_name, opaq->name.name);
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
} else {
/* inherit */
- mod = parent->schema->module;
+ mod = sparent->module;
}
break;
default:
LOGERR(ctx, LY_EINVAL, "Unsupported value format.");
- return LY_EINVAL;
+ rc = LY_EINVAL;
+ goto cleanup;
}
/* schema */
- snode = lys_find_child(parent ? parent->schema : NULL, mod, opaq->name.name, 0, 0, 0);
- if (!snode && parent && parent->schema && (parent->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
+ snode = lys_find_child(sparent, mod, opaq->name.name, 0, 0, 0);
+ if (!snode && sparent && (sparent->nodetype & (LYS_RPC | LYS_ACTION))) {
/* maybe output node */
- snode = lys_find_child(parent->schema, mod, opaq->name.name, 0, 0, LYS_GETNEXT_OUTPUT);
+ snode = lys_find_child(sparent, mod, opaq->name.name, 0, 0, LYS_GETNEXT_OUTPUT);
}
if (!snode) {
- if (parent) {
+ if (sparent) {
LOGVAL(ctx, LYVE_REFERENCE, "Node \"%s\" not found as a child of \"%s\" node.", opaq->name.name,
- LYD_NAME(parent));
+ sparent->name);
} else {
LOGVAL(ctx, LYVE_REFERENCE, "Node \"%s\" not found in the \"%s\" module.", opaq->name.name, mod->name);
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
+ /* schema node exists */
+ LOG_LOCBACK(0, loc_node, loc_path, 0);
+ loc_node = 0;
+ loc_path = 0;
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ ++loc_node;
+
if (snode->nodetype & LYD_NODE_TERM) {
/* leaf / leaf-list */
- LY_CHECK_RET(lys_value_validate(ctx, snode, opaq->value, strlen(opaq->value), opaq->format, opaq->val_prefix_data));
+ rc = ly_value_validate(ctx, snode, opaq->value, strlen(opaq->value), opaq->format, opaq->val_prefix_data, opaq->hints);
+ LY_CHECK_GOTO(rc, cleanup);
} else if (snode->nodetype == LYS_LIST) {
/* list */
- LY_CHECK_RET(lyd_parse_opaq_list_error(node, snode));
+ rc = lyd_parse_opaq_list_error(node, snode);
+ LY_CHECK_GOTO(rc, cleanup);
} else if (snode->nodetype & LYD_NODE_INNER) {
/* inner node */
if (opaq->value) {
LOGVAL(ctx, LYVE_DATA, "Invalid value \"%s\" for %s \"%s\".", opaq->value,
lys_nodetype2str(snode->nodetype), snode->name);
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
} else {
LOGERR(ctx, LY_EINVAL, "Unexpected opaque schema node %s \"%s\".", lys_nodetype2str(snode->nodetype), snode->name);
- return LY_EINVAL;
+ rc = LY_EINVAL;
+ goto cleanup;
}
LOGERR(ctx, LY_EINVAL, "Unexpected valid opaque node %s \"%s\".", lys_nodetype2str(snode->nodetype), snode->name);
- return LY_EINVAL;
+ rc = LY_EINVAL;
+
+cleanup:
+ LOG_LOCBACK(0, loc_node, loc_path, 0);
+ return rc;
}
LIBYANG_API_DEF const char *
@@ -970,7 +1091,7 @@ lyd_any_copy_value(struct lyd_node *trg, const union lyd_any_value *value, LYD_A
return LY_SUCCESS;
}
-const struct lysc_node *
+LIBYANG_API_DEF const struct lysc_node *
lyd_node_schema(const struct lyd_node *node)
{
const struct lysc_node *schema = NULL;
@@ -983,27 +1104,30 @@ lyd_node_schema(const struct lyd_node *node)
return node->schema;
}
+ /* find the first schema node in the parents */
+ for (iter = lyd_parent(node); iter && !iter->schema; iter = lyd_parent(iter)) {}
+ if (iter) {
+ prev_iter = iter;
+ schema = prev_iter->schema;
+ }
+
/* get schema node of an opaque node */
do {
/* get next data node */
for (iter = node; lyd_parent(iter) != prev_iter; iter = lyd_parent(iter)) {}
- /* get equivalent schema node */
- if (iter->schema) {
- schema = iter->schema;
- } else {
- /* get module */
- mod = lyd_owner_module(iter);
- if (!mod && !schema) {
- /* top-level opaque node has unknown module */
- break;
- }
-
- /* get schema node */
- schema = lys_find_child(schema, mod ? mod : schema->module, LYD_NAME(iter), 0, 0, 0);
+ /* get module */
+ mod = lyd_node_module(iter);
+ if (!mod) {
+ /* unknown module, no schema node */
+ schema = NULL;
+ break;
}
- /* remember to move to the descendant */
+ /* get schema node */
+ schema = lys_find_child(schema, mod, LYD_NAME(iter), 0, 0, 0);
+
+ /* move to the descendant */
prev_iter = iter;
} while (schema && (iter != node));
@@ -1068,9 +1192,7 @@ lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node
{
struct lyd_node **match_p;
struct lyd_node_inner *parent;
- const struct lysc_node *cur_schema;
uint32_t hash;
- lyht_value_equal_cb ht_cb;
assert(schema);
if (!siblings) {
@@ -1084,23 +1206,17 @@ lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node
parent = siblings->parent;
if (parent && parent->schema && parent->children_ht) {
/* calculate our hash */
- hash = dict_hash_multi(0, schema->module->name, strlen(schema->module->name));
- hash = dict_hash_multi(hash, schema->name, strlen(schema->name));
- hash = dict_hash_multi(hash, NULL, 0);
-
- /* use special hash table function */
- ht_cb = lyht_set_cb(parent->children_ht, lyd_hash_table_schema_val_equal);
+ hash = lyht_hash_multi(0, schema->module->name, strlen(schema->module->name));
+ hash = lyht_hash_multi(hash, schema->name, strlen(schema->name));
+ hash = lyht_hash_multi(hash, NULL, 0);
- /* find by hash */
- if (!lyht_find(parent->children_ht, &schema, hash, (void **)&match_p)) {
+ /* find by hash but use special hash table function (and stay thread-safe) */
+ if (!lyht_find_with_val_cb(parent->children_ht, &schema, hash, lyd_hash_table_schema_val_equal, (void **)&match_p)) {
siblings = *match_p;
} else {
/* not found */
siblings = NULL;
}
-
- /* set the original hash table compare function back */
- lyht_set_cb(parent->children_ht, ht_cb);
} else {
/* find first sibling */
if (siblings->parent) {
@@ -1111,25 +1227,22 @@ lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node
}
}
- /* search manually without hashes */
- for ( ; siblings; siblings = siblings->next) {
- cur_schema = lyd_node_schema(siblings);
- if (!cur_schema) {
- /* some unknown opaque node */
- continue;
- }
-
+ /* search manually without hashes and ignore opaque nodes (cannot be found by hashes) */
+ for ( ; siblings && siblings->schema; siblings = siblings->next) {
/* schema match is enough */
- if (cur_schema->module->ctx == schema->module->ctx) {
- if (cur_schema == schema) {
+ if (LYD_CTX(siblings) == schema->module->ctx) {
+ if (siblings->schema == schema) {
break;
}
} else {
- if (!strcmp(cur_schema->name, schema->name) && !strcmp(cur_schema->module->name, schema->module->name)) {
+ if (!strcmp(LYD_NAME(siblings), schema->name) && !strcmp(siblings->schema->module->name, schema->module->name)) {
break;
}
}
}
+ if (siblings && !siblings->schema) {
+ siblings = NULL;
+ }
}
if (!siblings) {
@@ -1468,6 +1581,52 @@ ly_format2str(LY_VALUE_FORMAT format)
return NULL;
}
+LIBYANG_API_DEF int
+ly_time_tz_offset(void)
+{
+ return ly_time_tz_offset_at(time(NULL));
+}
+
+LIBYANG_API_DEF int
+ly_time_tz_offset_at(time_t time)
+{
+ struct tm tm_local, tm_utc;
+ int result = 0;
+
+ /* init timezone */
+ tzset();
+
+ /* get local and UTC time */
+ localtime_r(&time, &tm_local);
+ gmtime_r(&time, &tm_utc);
+
+ /* account for year/month/day change by adding/subtracting from the hours, the change cannot be more than 1 day */
+ if (tm_local.tm_year < tm_utc.tm_year) {
+ tm_utc.tm_hour += 24;
+ } else if (tm_local.tm_year > tm_utc.tm_year) {
+ tm_local.tm_hour += 24;
+ } else if (tm_local.tm_mon < tm_utc.tm_mon) {
+ tm_utc.tm_hour += 24;
+ } else if (tm_local.tm_mon > tm_utc.tm_mon) {
+ tm_local.tm_hour += 24;
+ } else if (tm_local.tm_mday < tm_utc.tm_mday) {
+ tm_utc.tm_hour += 24;
+ } else if (tm_local.tm_mday > tm_utc.tm_mday) {
+ tm_local.tm_hour += 24;
+ }
+
+ /* hours shift in seconds */
+ result += (tm_local.tm_hour - tm_utc.tm_hour) * 3600;
+
+ /* minutes shift in seconds */
+ result += (tm_local.tm_min - tm_utc.tm_min) * 60;
+
+ /* seconds shift */
+ result += tm_local.tm_sec - tm_utc.tm_sec;
+
+ return result;
+}
+
LIBYANG_API_DEF LY_ERR
ly_time_str2time(const char *value, time_t *time, char **fractions_s)
{
@@ -1486,6 +1645,28 @@ ly_time_str2time(const char *value, time_t *time, char **fractions_s)
tm.tm_min = atoi(&value[14]);
tm.tm_sec = atoi(&value[17]);
+ /* explicit checks for some gross errors */
+ if (tm.tm_mon > 11) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time month \"%d\".", tm.tm_mon);
+ return LY_EINVAL;
+ }
+ if ((tm.tm_mday < 1) || (tm.tm_mday > 31)) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time day of month \"%d\".", tm.tm_mday);
+ return LY_EINVAL;
+ }
+ if (tm.tm_hour > 23) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time hours \"%d\".", tm.tm_hour);
+ return LY_EINVAL;
+ }
+ if (tm.tm_min > 59) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time minutes \"%d\".", tm.tm_min);
+ return LY_EINVAL;
+ }
+ if (tm.tm_sec > 60) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time seconds \"%d\".", tm.tm_sec);
+ return LY_EINVAL;
+ }
+
t = timegm(&tm);
i = 19;
@@ -1506,12 +1687,24 @@ ly_time_str2time(const char *value, time_t *time, char **fractions_s)
shift = 0;
} else {
shift = strtol(&value[i], NULL, 10);
+ if (shift > 23) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time timezone hour \"%" PRIi64 "\".", shift);
+ return LY_EINVAL;
+ }
shift = shift * 60 * 60; /* convert from hours to seconds */
- shift_m = strtol(&value[i + 4], NULL, 10) * 60; /* includes conversion from minutes to seconds */
+
+ shift_m = strtol(&value[i + 4], NULL, 10);
+ if (shift_m > 59) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time timezone minutes \"%" PRIi64 "\".", shift_m);
+ return LY_EINVAL;
+ }
+ shift_m *= 60; /* convert from minutes to seconds */
+
/* correct sign */
if (shift < 0) {
shift_m *= -1;
}
+
/* connect hours and minutes of the shift */
shift = shift + shift_m;
}
@@ -1535,41 +1728,24 @@ LIBYANG_API_DEF LY_ERR
ly_time_time2str(time_t time, const char *fractions_s, char **str)
{
struct tm tm;
- char zoneshift[8];
- int32_t zonediff_h, zonediff_m;
+ char zoneshift[12];
+ int zonediff_s, zonediff_h, zonediff_m;
LY_CHECK_ARG_RET(NULL, str, LY_EINVAL);
- /* initialize the local timezone */
+ /* init timezone */
tzset();
-#ifdef HAVE_TM_GMTOFF
/* convert */
if (!localtime_r(&time, &tm)) {
return LY_ESYS;
}
- /* get timezone offset */
- if (tm.tm_gmtoff == 0) {
- /* time is Zulu (UTC) */
- zonediff_h = 0;
- zonediff_m = 0;
- } else {
- /* timezone offset */
- zonediff_h = tm.tm_gmtoff / 60 / 60;
- zonediff_m = tm.tm_gmtoff / 60 % 60;
- }
+ /* get timezone offset (do not use tm_gmtoff to avoid portability problems) */
+ zonediff_s = ly_time_tz_offset_at(time);
+ zonediff_h = zonediff_s / 60 / 60;
+ zonediff_m = zonediff_s / 60 % 60;
sprintf(zoneshift, "%+03d:%02d", zonediff_h, zonediff_m);
-#else
- /* convert */
- if (!gmtime_r(&time, &tm)) {
- return LY_ESYS;
- }
-
- (void)zonediff_h;
- (void)zonediff_m;
- sprintf(zoneshift, "-00:00");
-#endif
/* print */
if (asprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d%s%s%s",
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index bf17a91..0281ae5 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -166,7 +166,7 @@ lyd_free_subtree(struct lyd_node *node, ly_bool top)
ly_free_prefix_data(opaq->format, opaq->val_prefix_data);
} else if (node->schema->nodetype & LYD_NODE_INNER) {
/* remove children hash table in case of inner data node */
- lyht_free(((struct lyd_node_inner *)node)->children_ht);
+ lyht_free(((struct lyd_node_inner *)node)->children_ht, NULL);
((struct lyd_node_inner *)node)->children_ht = NULL;
/* free the children */
@@ -189,7 +189,7 @@ lyd_free_subtree(struct lyd_node *node, ly_bool top)
/* unlink only the nodes from the first level, nodes in subtree are freed all, so no unlink is needed */
if (top) {
- lyd_unlink_tree(node);
+ lyd_unlink(node);
}
free(node);
@@ -202,6 +202,11 @@ lyd_free_tree(struct lyd_node *node)
return;
}
+ if (lysc_is_key(node->schema) && node->parent) {
+ LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot free a list key \"%s\", free the list instance instead.", LYD_NAME(node));
+ return;
+ }
+
lyd_free_subtree(node, 1);
}
@@ -223,6 +228,11 @@ lyd_free_(struct lyd_node *node, ly_bool top)
}
LY_LIST_FOR_SAFE(node, next, iter) {
+ if (lysc_is_key(iter->schema) && iter->parent) {
+ LOGERR(LYD_CTX(iter), LY_EINVAL, "Cannot free a list key \"%s\", free the list instance instead.", LYD_NAME(iter));
+ return;
+ }
+
/* in case of the top-level nodes (node->parent is NULL), no unlinking needed */
lyd_free_subtree(iter, iter->parent ? 1 : 0);
}
diff --git a/src/tree_data_hash.c b/src/tree_data_hash.c
index 52f99a8..7235c27 100644
--- a/src/tree_data_hash.c
+++ b/src/tree_data_hash.c
@@ -39,15 +39,15 @@ lyd_hash(struct lyd_node *node)
}
/* hash always starts with the module and schema name */
- node->hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
- node->hash = dict_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
+ node->hash = lyht_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+ node->hash = lyht_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
if (node->schema->nodetype == LYS_LIST) {
if (node->schema->flags & LYS_KEYLESS) {
/* key-less list simply calls hash function again with empty key,
* just so that it differs from the first-instance hash
*/
- node->hash = dict_hash_multi(node->hash, NULL, 0);
+ node->hash = lyht_hash_multi(node->hash, NULL, 0);
} else {
struct lyd_node_inner *list = (struct lyd_node_inner *)node;
@@ -56,7 +56,7 @@ lyd_hash(struct lyd_node *node)
struct lyd_node_term *key = (struct lyd_node_term *)iter;
hash_key = key->value.realtype->plugin->print(NULL, &key->value, LY_VALUE_LYB, NULL, &dyn, &key_len);
- node->hash = dict_hash_multi(node->hash, hash_key, key_len);
+ node->hash = lyht_hash_multi(node->hash, hash_key, key_len);
if (dyn) {
free((void *)hash_key);
}
@@ -67,14 +67,14 @@ lyd_hash(struct lyd_node *node)
struct lyd_node_term *llist = (struct lyd_node_term *)node;
hash_key = llist->value.realtype->plugin->print(NULL, &llist->value, LY_VALUE_LYB, NULL, &dyn, &key_len);
- node->hash = dict_hash_multi(node->hash, hash_key, key_len);
+ node->hash = lyht_hash_multi(node->hash, hash_key, key_len);
if (dyn) {
free((void *)hash_key);
}
}
/* finish the hash */
- node->hash = dict_hash_multi(node->hash, NULL, 0);
+ node->hash = lyht_hash_multi(node->hash, NULL, 0);
return LY_SUCCESS;
}
@@ -121,14 +121,14 @@ lyd_hash_table_val_equal(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(c
* @return LY_ERR value.
*/
static LY_ERR
-lyd_insert_hash_add(struct hash_table *ht, struct lyd_node *node, ly_bool empty_ht)
+lyd_insert_hash_add(struct ly_ht *ht, struct lyd_node *node, ly_bool empty_ht)
{
uint32_t hash;
assert(ht && node && node->schema);
/* add node itself */
- if (lyht_insert(ht, &node, node->hash, NULL)) {
+ if (lyht_insert_no_check(ht, &node, node->hash, NULL)) {
LOGINT_RET(LYD_CTX(node));
}
@@ -136,9 +136,9 @@ lyd_insert_hash_add(struct hash_table *ht, struct lyd_node *node, ly_bool empty_
if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) &&
(!node->prev->next || (node->prev->schema != node->schema))) {
/* get the simple hash */
- hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
- hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+ hash = lyht_hash_multi(hash, node->schema->name, strlen(node->schema->name));
+ hash = lyht_hash_multi(hash, NULL, 0);
/* remove any previous stored instance, only if we did not start with an empty HT */
if (!empty_ht && node->next && (node->next->schema == node->schema)) {
@@ -182,8 +182,7 @@ lyd_insert_hash(struct lyd_node *node)
}
}
if (u >= LYD_HT_MIN_ITEMS) {
- /* create hash table, insert all the children */
- node->parent->children_ht = lyht_new(1, sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1);
+ node->parent->children_ht = lyht_new(lyht_get_fixed_size(u), sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1);
LY_LIST_FOR(node->parent->child, iter) {
if (iter->schema) {
LY_CHECK_RET(lyd_insert_hash_add(node->parent->children_ht, iter, 1));
@@ -216,9 +215,9 @@ lyd_unlink_hash(struct lyd_node *node)
/* first instance of the (leaf-)list, needs to be removed from HT */
if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (!node->prev->next || (node->prev->schema != node->schema))) {
/* get the simple hash */
- hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
- hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+ hash = lyht_hash_multi(hash, node->schema->name, strlen(node->schema->name));
+ hash = lyht_hash_multi(hash, NULL, 0);
/* remove the instance */
if (lyht_remove(node->parent->children_ht, &node, hash)) {
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index fd0792d..cfb93f1 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief internal functions for YANG schema trees.
*
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 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.
@@ -34,31 +34,30 @@ struct lysc_module;
#define LY_LYB_SUFFIX_LEN 4
/**
- * @brief Internal structure for remembering "used" instances of lists with duplicate instances allowed.
+ * @brief Internal item structure for remembering "used" instances of duplicate node instances.
*/
struct lyd_dup_inst {
- struct ly_set *inst_set;
+ struct ly_set *set;
uint32_t used;
};
/**
- * @brief Update a found inst using a duplicate instance cache. Needs to be called for every "used"
+ * @brief Update a found inst using a duplicate instance cache hash table. Needs to be called for every "used"
* (that should not be considered next time) instance.
*
* @param[in,out] inst Found instance, is updated so that the same instance is not returned twice.
* @param[in] siblings Siblings where @p inst was found.
- * @param[in,out] dup_inst_cache Duplicate instance cache.
+ * @param[in] dup_inst_ht Duplicate instance cache hash table.
* @return LY_ERR value.
*/
-LY_ERR lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings,
- struct lyd_dup_inst **dup_inst_cache);
+LY_ERR lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struct ly_ht **dup_inst_ht);
/**
* @brief Free duplicate instance cache.
*
- * @param[in] dup_inst Duplicate instance cache to free.
+ * @param[in] dup_inst Duplicate instance cache hash table to free.
*/
-void lyd_dup_inst_free(struct lyd_dup_inst *dup_inst);
+void lyd_dup_inst_free(struct ly_ht *dup_inst_ht);
/**
* @brief Just like ::lys_getnext() but iterates over all data instances of the schema nodes.
@@ -118,14 +117,6 @@ const struct lys_module *lyd_mod_next_module(struct lyd_node *tree, const struct
const struct lys_module *lyd_data_next_module(struct lyd_node **next, struct lyd_node **first);
/**
- * @brief Get schema node of a data node. Useful especially for opaque nodes.
- *
- * @param[in] node Data node to use.
- * @return Schema node represented by data @p node, NULL if there is none.
- */
-const struct lysc_node *lyd_node_schema(const struct lyd_node *node);
-
-/**
* @brief Set dflt flag for a NP container if applicable, recursively for parents.
*
* @param[in] node Node whose criteria for the dflt flag has changed.
@@ -233,6 +224,7 @@ const char *ly_format2str(LY_VALUE_FORMAT format);
* @param[in] schema Schema node of the new data node.
* @param[in] value String value to be parsed.
* @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in] is_utf8 Whether @p value is a valid UTF-8 string, if applicable.
* @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
* @param[in] format Input format of @p value.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
@@ -243,8 +235,9 @@ const char *ly_format2str(LY_VALUE_FORMAT format);
* @return LY_EINCOMPLETE in case data tree is needed to finish the validation.
* @return LY_ERR value if an error occurred.
*/
-LY_ERR lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool *dynamic,
- LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, ly_bool *incomplete, struct lyd_node **node);
+LY_ERR lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool is_utf8,
+ ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, ly_bool *incomplete,
+ struct lyd_node **node);
/**
* @brief Create a term (leaf/leaf-list) node from a parsed value by duplicating it.
@@ -280,11 +273,13 @@ LY_ERR lyd_create_inner(const struct lysc_node *schema, struct lyd_node **node);
*
* @param[in] schema Schema node of the new data node.
* @param[in] predicates Compiled key list predicates.
+ * @param[in] vars Array of defined variables to use in predicates, may be NULL.
* @param[out] node Created node.
* @return LY_SUCCESS on success.
* @return LY_ERR value if an error occurred.
*/
-LY_ERR lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates, struct lyd_node **node);
+LY_ERR lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates,
+ const struct lyxp_var *vars, struct lyd_node **node);
/**
* @brief Create a list with all its keys (cannot be used for key-less list).
@@ -403,6 +398,13 @@ void lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node);
void lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling, struct lyd_node *node, ly_bool last);
/**
+ * @brief Unlink the specified data subtree.
+ *
+ * @param[in] node Data tree node to be unlinked (together with all the children).
+ */
+void lyd_unlink(struct lyd_node *node);
+
+/**
* @brief Insert a metadata (last) into a parent
*
* @param[in] parent Parent of the metadata.
@@ -421,6 +423,7 @@ void lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool cle
* @param[in] name_len Length of @p name, must be set correctly.
* @param[in] value String value to be parsed.
* @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in] is_utf8 Whether @p value is a valid UTF-8 string, if applicable.
* @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
* @param[in] format Input format of @p value.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
@@ -433,7 +436,7 @@ void lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool cle
* @return LY_ERR value if an error occurred.
*/
LY_ERR lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
- size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
+ size_t name_len, const char *value, size_t value_len, ly_bool is_utf8, ly_bool *dynamic, LY_VALUE_FORMAT format,
void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, ly_bool clear_dflt, ly_bool *incomplete);
/**
@@ -477,6 +480,7 @@ LY_ERR lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const st
* @param[in] type Type of the value.
* @param[in] value Value to be parsed, must not be NULL.
* @param[in] value_len Length of the give @p value, must be set correctly.
+ * @param[in] is_utf8 Whether @p value is a valid UTF-8 string, if applicable.
* @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
* @param[in] format Input format of @p value.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
@@ -487,7 +491,7 @@ LY_ERR lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const st
* @return LY_ERR value on error.
*/
LY_ERR lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct lysc_type *type, const void *value,
- size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ size_t value_len, ly_bool is_utf8, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
const struct lysc_node *ctx_node, ly_bool *incomplete);
/**
@@ -505,8 +509,7 @@ LY_ERR lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc
const struct lyd_node *ctx_node, const struct lyd_node *tree);
/**
- * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value coming
- * from a schema.
+ * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
*
* This function check just the type's restriction, if you want to check also the data tree context (e.g. in case of
* require-instance restriction), use ::lyd_value_validate().
@@ -517,11 +520,12 @@ LY_ERR lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc
* @param[in] value_len Length of the given @p value (mandatory).
* @param[in] format Value prefix format.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in] hints Value encoding hints.
* @return LY_SUCCESS on success
* @return LY_ERR value if an error occurred.
*/
-LY_ERR lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
- LY_VALUE_FORMAT format, void *prefix_data);
+LY_ERR ly_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
+ LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints);
/**
* @defgroup datahash Data nodes hash manipulation
diff --git a/src/tree_data_new.c b/src/tree_data_new.c
index 752b181..5d9f429 100644
--- a/src/tree_data_new.c
+++ b/src/tree_data_new.c
@@ -51,7 +51,7 @@
#include "xpath.h"
LY_ERR
-lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool *dynamic,
+lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool is_utf8, ly_bool *dynamic,
LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, ly_bool *incomplete, struct lyd_node **node)
{
LY_ERR ret;
@@ -68,7 +68,7 @@ lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_
LOG_LOCSET(schema, NULL, NULL, NULL);
ret = lyd_value_store(schema->module->ctx, &term->value, ((struct lysc_node_leaf *)term->schema)->type, value,
- value_len, dynamic, format, prefix_data, hints, schema, incomplete);
+ value_len, is_utf8, dynamic, format, prefix_data, hints, schema, incomplete);
LOG_LOCBACK(1, 0, 0, 0);
LY_CHECK_ERR_RET(ret, free(term), ret);
lyd_hash(&term->node);
@@ -134,10 +134,14 @@ lyd_create_inner(const struct lysc_node *schema, struct lyd_node **node)
}
LY_ERR
-lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates, struct lyd_node **node)
+lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates, const struct lyxp_var *vars,
+ struct lyd_node **node)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *list = NULL, *key;
+ const struct lyd_value *value;
+ struct lyd_value val = {0};
+ struct lyxp_var *var;
LY_ARRAY_COUNT_TYPE u;
assert((schema->nodetype == LYS_LIST) && !(schema->flags & LYS_KEYLESS));
@@ -149,7 +153,31 @@ lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *
/* create and insert all the keys */
LY_ARRAY_FOR(predicates, u) {
- LY_CHECK_GOTO(ret = lyd_create_term2(predicates[u].key, &predicates[u].value, &key), cleanup);
+ if (predicates[u].type == LY_PATH_PREDTYPE_LIST_VAR) {
+ /* find the var */
+ if ((ret = lyxp_vars_find(schema->module->ctx, vars, predicates[u].variable, 0, &var))) {
+ goto cleanup;
+ }
+
+ /* store the value */
+ LOG_LOCSET(predicates[u].key, NULL, NULL, NULL);
+ ret = lyd_value_store(schema->module->ctx, &val, ((struct lysc_node_leaf *)predicates[u].key)->type,
+ var->value, strlen(var->value), 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, predicates[u].key, NULL);
+ LOG_LOCBACK(1, 0, 0, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ value = &val;
+ } else {
+ assert(predicates[u].type == LY_PATH_PREDTYPE_LIST);
+ value = &predicates[u].value;
+ }
+
+ ret = lyd_create_term2(predicates[u].key, value, &key);
+ if (val.realtype) {
+ val.realtype->plugin->free(schema->module->ctx, &val);
+ memset(&val, 0, sizeof val);
+ }
+ LY_CHECK_GOTO(ret, cleanup);
lyd_insert_node(list, NULL, key, 0);
}
@@ -172,7 +200,6 @@ lyd_create_list2(const struct lysc_node *schema, const char *keys, size_t keys_l
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *expr = NULL;
uint32_t exp_idx = 0;
- enum ly_path_pred_type pred_type = 0;
struct ly_path_predicate *predicates = NULL;
LOG_LOCSET(schema, NULL, NULL, NULL);
@@ -183,100 +210,111 @@ lyd_create_list2(const struct lysc_node *schema, const char *keys, size_t keys_l
/* compile them */
LY_CHECK_GOTO(ret = ly_path_compile_predicate(schema->module->ctx, NULL, NULL, schema, expr, &exp_idx, LY_VALUE_JSON,
- NULL, &predicates, &pred_type), cleanup);
+ NULL, &predicates), cleanup);
/* create the list node */
- LY_CHECK_GOTO(ret = lyd_create_list(schema, predicates, node), cleanup);
+ LY_CHECK_GOTO(ret = lyd_create_list(schema, predicates, NULL, node), cleanup);
cleanup:
LOG_LOCBACK(1, 0, 0, 0);
lyxp_expr_free(schema->module->ctx, expr);
- ly_path_predicates_free(schema->module->ctx, pred_type, predicates);
+ ly_path_predicates_free(schema->module->ctx, predicates);
return ret;
}
/**
- * @brief Convert an anydata value into a datatree.
+ * @brief Learn actual any value type in case it is currently ::LYD_ANYDATA_STRING.
+ *
+ * @param[in] value Any value.
+ * @param[out] value_type Detected value type.
+ */
+static void
+lyd_create_any_string_valtype(const void *value, LYD_ANYDATA_VALUETYPE *value_type)
+{
+ /* detect format */
+ if (!value) {
+ /* interpret it as an empty data tree */
+ *value_type = LYD_ANYDATA_DATATREE;
+ } else if (((char *)value)[0] == '<') {
+ *value_type = LYD_ANYDATA_XML;
+ } else if (((char *)value)[0] == '{') {
+ *value_type = LYD_ANYDATA_JSON;
+ } else if (!strncmp(value, "lyb", 3)) {
+ *value_type = LYD_ANYDATA_LYB;
+ } else {
+ /* really just some string */
+ *value_type = LYD_ANYDATA_STRING;
+ }
+}
+
+/**
+ * @brief Convert an any value into a datatree.
*
* @param[in] ctx libyang context.
- * @param[in] value Anydata value.
- * @param[in] value_type Anydata @p value type.
+ * @param[in] value_in Any value as an input.
+ * @param[in] value_type Any @p value type.
+ * @param[in] log Whether parsing errors should be logged.
* @param[out] tree Parsed data tree.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_create_anydata_datatree(const struct ly_ctx *ctx, const void *value, LYD_ANYDATA_VALUETYPE value_type,
+lyd_create_any_datatree(const struct ly_ctx *ctx, struct ly_in *value_in, LYD_ANYDATA_VALUETYPE value_type, ly_bool log,
struct lyd_node **tree)
{
- LY_ERR r;
- struct ly_in *in = NULL;
+ LY_ERR rc = LY_SUCCESS;
struct lyd_ctx *lydctx = NULL;
- uint32_t parse_opts, int_opts;
+ uint32_t parse_opts, int_opts, log_opts = 0;
*tree = NULL;
- if (!value) {
- /* empty data tree no matter the value type */
- return LY_SUCCESS;
- }
-
- if (value_type == LYD_ANYDATA_STRING) {
- /* detect format */
- if (((char *)value)[0] == '<') {
- value_type = LYD_ANYDATA_XML;
- } else if (((char *)value)[0] == '{') {
- value_type = LYD_ANYDATA_JSON;
- } else if (!strncmp(value, "lyb", 3)) {
- value_type = LYD_ANYDATA_LYB;
- } else {
- LOGERR(ctx, LY_EINVAL, "Invalid string value of an anydata node.");
- return LY_EINVAL;
- }
- }
-
- /* create input */
- LY_CHECK_RET(ly_in_new_memory(value, &in));
-
/* set options */
parse_opts = LYD_PARSE_ONLY | LYD_PARSE_OPAQ;
int_opts = LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
+ if (!log) {
+ /* no logging */
+ ly_temp_log_options(&log_opts);
+ }
+
switch (value_type) {
case LYD_ANYDATA_DATATREE:
case LYD_ANYDATA_STRING:
/* unreachable */
- ly_in_free(in, 0);
LOGINT_RET(ctx);
case LYD_ANYDATA_XML:
- r = lyd_parse_xml(ctx, NULL, NULL, tree, in, parse_opts, 0, int_opts, NULL, NULL, &lydctx);
+ rc = lyd_parse_xml(ctx, NULL, NULL, tree, value_in, parse_opts, 0, int_opts, NULL, NULL, &lydctx);
break;
case LYD_ANYDATA_JSON:
- r = lyd_parse_json(ctx, NULL, NULL, tree, in, parse_opts, 0, int_opts, NULL, NULL, &lydctx);
+ rc = lyd_parse_json(ctx, NULL, NULL, tree, value_in, parse_opts, 0, int_opts, NULL, NULL, &lydctx);
break;
case LYD_ANYDATA_LYB:
- r = lyd_parse_lyb(ctx, NULL, NULL, tree, in, parse_opts | LYD_PARSE_STRICT, 0, int_opts, NULL, NULL, &lydctx);
+ rc = lyd_parse_lyb(ctx, NULL, NULL, tree, value_in, parse_opts | LYD_PARSE_STRICT, 0, int_opts, NULL, NULL, &lydctx);
break;
}
if (lydctx) {
lydctx->free(lydctx);
}
- ly_in_free(in, 0);
- if (r) {
- LOGERR(ctx, LY_EINVAL, "Failed to parse anydata content into a data tree.");
- return LY_EINVAL;
+ if (!log) {
+ /* restore logging */
+ ly_temp_log_options(NULL);
}
- return LY_SUCCESS;
+ if (rc && *tree) {
+ lyd_free_siblings(*tree);
+ *tree = NULL;
+ }
+ return rc;
}
LY_ERR
lyd_create_any(const struct lysc_node *schema, const void *value, LYD_ANYDATA_VALUETYPE value_type, ly_bool use_value,
struct lyd_node **node)
{
- LY_ERR ret;
+ LY_ERR rc = LY_SUCCESS, r;
struct lyd_node *tree;
struct lyd_node_any *any = NULL;
union lyd_any_value any_val;
+ struct ly_in *in = NULL;
assert(schema->nodetype & LYD_NODE_ANY);
@@ -287,15 +325,70 @@ lyd_create_any(const struct lysc_node *schema, const void *value, LYD_ANYDATA_VA
any->prev = &any->node;
any->flags = LYD_NEW;
- if ((schema->nodetype == LYS_ANYDATA) && (value_type != LYD_ANYDATA_DATATREE)) {
- /* only a data tree can be stored */
- LY_CHECK_GOTO(ret = lyd_create_anydata_datatree(schema->module->ctx, value, value_type, &tree), error);
- if (use_value) {
- free((void *)value);
+ if (schema->nodetype == LYS_ANYDATA) {
+ /* anydata */
+ if (value_type == LYD_ANYDATA_STRING) {
+ /* detect value type */
+ lyd_create_any_string_valtype(value, &value_type);
+ }
+
+ if (value_type != LYD_ANYDATA_DATATREE) {
+ /* create input */
+ assert(value);
+ LY_CHECK_GOTO(rc = ly_in_new_memory(value, &in), cleanup);
+
+ /* parse as a data tree */
+ if ((r = lyd_create_any_datatree(schema->module->ctx, in, value_type, 1, &tree))) {
+ LOGERR(schema->module->ctx, rc, "Failed to parse any content into a data tree.");
+ rc = r;
+ goto cleanup;
+ }
+
+ /* use the parsed data tree */
+ if (use_value) {
+ free((void *)value);
+ }
+ use_value = 1;
+ value = tree;
+ value_type = LYD_ANYDATA_DATATREE;
+ }
+ } else {
+ /* anyxml */
+ switch (value_type) {
+ case LYD_ANYDATA_DATATREE:
+ /* fine, just use the value */
+ break;
+ case LYD_ANYDATA_STRING:
+ /* detect value type */
+ lyd_create_any_string_valtype(value, &value_type);
+ if ((value_type == LYD_ANYDATA_DATATREE) || (value_type == LYD_ANYDATA_STRING)) {
+ break;
+ }
+ /* fallthrough */
+ case LYD_ANYDATA_XML:
+ case LYD_ANYDATA_JSON:
+ case LYD_ANYDATA_LYB:
+ if (!value) {
+ /* nothing to parse */
+ break;
+ }
+
+ /* create input */
+ LY_CHECK_GOTO(rc = ly_in_new_memory(value, &in), cleanup);
+
+ /* try to parse as a data tree */
+ r = lyd_create_any_datatree(schema->module->ctx, in, value_type, 0, &tree);
+ if (!r) {
+ /* use the parsed data tree */
+ if (use_value) {
+ free((void *)value);
+ }
+ use_value = 1;
+ value = tree;
+ value_type = LYD_ANYDATA_DATATREE;
+ }
+ break;
}
- use_value = 1;
- value = tree;
- value_type = LYD_ANYDATA_DATATREE;
}
if (use_value) {
@@ -306,7 +399,7 @@ lyd_create_any(const struct lysc_node *schema, const void *value, LYD_ANYDATA_VA
case LYD_ANYDATA_STRING:
case LYD_ANYDATA_XML:
case LYD_ANYDATA_JSON:
- LY_CHECK_GOTO(ret = lydict_insert_zc(schema->module->ctx, (void *)value, &any->value.str), error);
+ LY_CHECK_GOTO(rc = lydict_insert_zc(schema->module->ctx, (void *)value, &any->value.str), cleanup);
break;
case LYD_ANYDATA_LYB:
any->value.mem = (void *)value;
@@ -315,16 +408,18 @@ lyd_create_any(const struct lysc_node *schema, const void *value, LYD_ANYDATA_VA
any->value_type = value_type;
} else {
any_val.str = value;
- LY_CHECK_GOTO(ret = lyd_any_copy_value(&any->node, &any_val, value_type), error);
+ LY_CHECK_GOTO(rc = lyd_any_copy_value(&any->node, &any_val, value_type), cleanup);
}
lyd_hash(&any->node);
- *node = &any->node;
- return LY_SUCCESS;
-
-error:
- free(any);
- return ret;
+cleanup:
+ if (rc) {
+ lyd_free_tree(&any->node);
+ } else {
+ *node = &any->node;
+ }
+ ly_in_free(in, 0);
+ return rc;
}
LY_ERR
@@ -444,33 +539,25 @@ lyd_new_ext_inner(const struct lysc_ext_instance *ext, const char *name, struct
}
/**
- * @brief Create a new list node in the data tree.
+ * @brief Create a new lits node instance without its keys.
*
+ * @param[in] ctx Context to use for logging.
* @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
* @param[in] module Module of the node being created. If NULL, @p parent module will be used.
* @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
- * @param[in] format Format of key values.
* @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
* taken into consideration. Otherwise, the output's data node is going to be created.
- * @param[out] node Optional created node.
- * @param[in] ap Ordered key values of the new list instance, all must be set. For ::LY_VALUE_LYB, every value must
- * be followed by the value length.
+ * @param[out] node Created node.
* @return LY_ERR value.
*/
static LY_ERR
-_lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const char *name, LY_VALUE_FORMAT format,
- ly_bool output, struct lyd_node **node, va_list ap)
+_lyd_new_list_node(const struct ly_ctx *ctx, const struct lyd_node *parent, const struct lys_module *module,
+ const char *name, ly_bool output, struct lyd_node **node)
{
- struct lyd_node *ret = NULL, *key;
- const struct lysc_node *schema, *key_s;
+ struct lyd_node *ret = NULL;
+ const struct lysc_node *schema;
struct lysc_ext_instance *ext = NULL;
- const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
- const void *key_val;
- uint32_t key_len;
- LY_ERR r, rc = LY_SUCCESS;
-
- LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
- LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
+ LY_ERR r;
if (!module) {
module = parent->schema->module;
@@ -487,8 +574,47 @@ _lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const ch
/* create list inner node */
LY_CHECK_RET(lyd_create_inner(schema, &ret));
+ if (ext) {
+ ret->flags |= LYD_EXT;
+ }
+
+ *node = ret;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @param[in] ap Ordered key values of the new list instance, all must be set. For ::LY_VALUE_LYB, every value must
+ * be followed by the value length.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+_lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const char *name, LY_VALUE_FORMAT format,
+ ly_bool output, struct lyd_node **node, va_list ap)
+{
+ struct lyd_node *ret = NULL, *key;
+ const struct lysc_node *key_s;
+ const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
+ const void *key_val;
+ uint32_t key_len;
+ LY_ERR rc = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
+
+ /* create the list node */
+ LY_CHECK_RET(_lyd_new_list_node(ctx, parent, module, name, output, &ret));
+
/* create and insert all the keys */
- for (key_s = lysc_node_child(schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
+ for (key_s = lysc_node_child(ret->schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
if (format == LY_VALUE_LYB) {
key_val = va_arg(ap, const void *);
key_len = va_arg(ap, uint32_t);
@@ -497,14 +623,11 @@ _lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const ch
key_len = key_val ? strlen((char *)key_val) : 0;
}
- rc = lyd_create_term(key_s, key_val, key_len, NULL, format, NULL, LYD_HINT_DATA, NULL, &key);
+ rc = lyd_create_term(key_s, key_val, key_len, 0, NULL, format, NULL, LYD_HINT_DATA, NULL, &key);
LY_CHECK_GOTO(rc, cleanup);
lyd_insert_node(ret, NULL, key, 1);
}
- if (ext) {
- ret->flags |= LYD_EXT;
- }
if (parent) {
lyd_insert_node(parent, NULL, ret, 0);
}
@@ -595,7 +718,7 @@ lyd_new_ext_list(const struct lysc_ext_instance *ext, const char *name, struct l
for (key_s = lysc_node_child(schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
key_val = va_arg(ap, const char *);
- rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA,
+ rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA,
NULL, &key);
LY_CHECK_GOTO(rc, cleanup);
lyd_insert_node(ret, NULL, key, 1);
@@ -661,6 +784,84 @@ lyd_new_list2(struct lyd_node *parent, const struct lys_module *module, const ch
}
/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] key_values Ordered key values of the new list instance ended with NULL, all must be set.
+ * @param[in] value_lengths Lengths of @p key_values, required for ::LY_VALUE_LYB, optional otherwise.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+_lyd_new_list3(struct lyd_node *parent, const struct lys_module *module, const char *name, LY_VALUE_FORMAT format,
+ const void **key_values, uint32_t *value_lengths, ly_bool output, struct lyd_node **node)
+{
+ struct lyd_node *ret = NULL, *key;
+ const struct lysc_node *key_s;
+ const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
+ const void *key_val;
+ uint32_t key_len, i;
+ LY_ERR rc = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, key_values, (format != LY_VALUE_LYB) || value_lengths,
+ LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
+
+ /* create the list node */
+ LY_CHECK_RET(_lyd_new_list_node(ctx, parent, module, name, output, &ret));
+
+ /* create and insert all the keys */
+ i = 0;
+ for (key_s = lysc_node_child(ret->schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
+ key_val = key_values[i] ? key_values[i] : "";
+ key_len = value_lengths ? value_lengths[i] : strlen(key_val);
+
+ rc = lyd_create_term(key_s, key_val, key_len, 0, NULL, format, NULL, LYD_HINT_DATA, NULL, &key);
+ LY_CHECK_GOTO(rc, cleanup);
+ lyd_insert_node(ret, NULL, key, 1);
+ }
+
+ if (parent) {
+ lyd_insert_node(parent, NULL, ret, 0);
+ }
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(ret);
+ ret = NULL;
+ } else if (node) {
+ *node = ret;
+ }
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_list3(struct lyd_node *parent, const struct lys_module *module, const char *name, const char **key_values,
+ uint32_t *value_lengths, ly_bool output, struct lyd_node **node)
+{
+ return _lyd_new_list3(parent, module, name, LY_VALUE_JSON, (const void **)key_values, value_lengths, output, node);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_list3_bin(struct lyd_node *parent, const struct lys_module *module, const char *name, const void **key_values,
+ uint32_t *value_lengths, ly_bool output, struct lyd_node **node)
+{
+ return _lyd_new_list3(parent, module, name, LY_VALUE_LYB, key_values, value_lengths, output, node);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_list3_canon(struct lyd_node *parent, const struct lys_module *module, const char *name, const char **key_values,
+ uint32_t *value_lengths, ly_bool output, struct lyd_node **node)
+{
+ return _lyd_new_list3(parent, module, name, LY_VALUE_CANON, (const void **)key_values, value_lengths, output, node);
+}
+
+/**
* @brief Create a new term node in the data tree.
*
* @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
@@ -699,7 +900,7 @@ _lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const ch
}
LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Term node \"%s\" not found.", name), LY_ENOTFOUND);
- LY_CHECK_RET(lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA, NULL, &ret));
+ LY_CHECK_RET(lyd_create_term(schema, value, value_len, 0, NULL, format, NULL, LYD_HINT_DATA, NULL, &ret));
if (ext) {
ret->flags |= LYD_EXT;
}
@@ -754,7 +955,8 @@ lyd_new_ext_term(const struct lysc_ext_instance *ext, const char *name, const ch
}
return LY_ENOTFOUND;
}
- rc = lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &ret);
+ rc = lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA,
+ NULL, &ret);
LY_CHECK_RET(rc);
*node = ret;
@@ -772,7 +974,8 @@ lyd_new_any(struct lyd_node *parent, const struct lys_module *module, const char
struct lysc_ext_instance *ext = NULL;
const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
- LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
+ LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name,
+ (value_type == LYD_ANYDATA_DATATREE) || (value_type == LYD_ANYDATA_STRING) || value, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
if (!module) {
@@ -867,7 +1070,7 @@ lyd_new_meta(const struct ly_ctx *ctx, struct lyd_node *parent, const struct lys
val_str = "";
}
- return lyd_create_meta(parent, meta, module, name, name_len, val_str, strlen(val_str), NULL, LY_VALUE_JSON,
+ return lyd_create_meta(parent, meta, module, name, name_len, val_str, strlen(val_str), 0, NULL, LY_VALUE_JSON,
NULL, LYD_HINT_DATA, parent ? parent->schema : NULL, clear_dflt, NULL);
}
@@ -881,7 +1084,8 @@ lyd_new_meta2(const struct ly_ctx *ctx, struct lyd_node *parent, ly_bool clear_d
LY_CHECK_CTX_EQUAL_RET(ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL);
if (parent && !parent->schema) {
- LOGERR(ctx, LY_EINVAL, "Cannot add metadata to an opaque node \"%s\".", ((struct lyd_node_opaq *)parent)->name);
+ LOGERR(ctx, LY_EINVAL, "Cannot add metadata to an opaque node \"%s\".",
+ ((struct lyd_node_opaq *)parent)->name.name);
return LY_EINVAL;
}
if (meta) {
@@ -908,7 +1112,7 @@ lyd_new_meta2(const struct ly_ctx *ctx, struct lyd_node *parent, ly_bool clear_d
}
return lyd_create_meta(parent, meta, mod, attr->name.name, strlen(attr->name.name), attr->value, strlen(attr->value),
- NULL, attr->format, attr->val_prefix_data, attr->hints, parent ? parent->schema : NULL, clear_dflt, NULL);
+ 0, NULL, attr->format, attr->val_prefix_data, attr->hints, parent ? parent->schema : NULL, clear_dflt, NULL);
}
LIBYANG_API_DEF LY_ERR
@@ -1094,7 +1298,8 @@ _lyd_change_term(struct lyd_node *term, const void *value, size_t value_len, LY_
/* parse the new value */
LOG_LOCSET(term->schema, term, NULL, NULL);
- ret = lyd_value_store(LYD_CTX(term), &val, type, value, value_len, NULL, format, NULL, LYD_HINT_DATA, term->schema, NULL);
+ ret = lyd_value_store(LYD_CTX(term), &val, type, value, value_len, 0, NULL, format, NULL, LYD_HINT_DATA,
+ term->schema, NULL);
LOG_LOCBACK(term->schema ? 1 : 0, 1, 0, 0);
LY_CHECK_GOTO(ret, cleanup);
@@ -1195,7 +1400,7 @@ lyd_change_meta(struct lyd_meta *meta, const char *val_str)
/* parse the new value into a new meta structure */
ret = lyd_create_meta(NULL, &m2, meta->annotation->module, meta->name, strlen(meta->name), val_str, strlen(val_str),
- NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, meta->parent ? meta->parent->schema : NULL, 0, NULL);
+ 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, meta->parent ? meta->parent->schema : NULL, 0, NULL);
LY_CHECK_GOTO(ret, cleanup);
/* compare original and new value */
@@ -1320,18 +1525,20 @@ lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const
schema = path[u].node;
if (lysc_is_dup_inst_list(schema)) {
- if (path[u].pred_type == LY_PATH_PREDTYPE_NONE) {
+ if (!path[u].predicates ||
+ ((schema->nodetype == LYS_LEAFLIST) && (path[u].predicates[0].type == LY_PATH_PREDTYPE_LEAFLIST))) {
/* creating a new key-less list or state leaf-list instance */
create = 1;
new_count = u;
- } else if (path[u].pred_type != LY_PATH_PREDTYPE_POSITION) {
+ } else if (path[u].predicates[0].type != LY_PATH_PREDTYPE_POSITION) {
LOG_LOCSET(schema, NULL, NULL, NULL);
- LOGVAL(schema->module->ctx, LYVE_XPATH, "Invalid predicate for %s \"%s\" in path \"%s\".",
+ LOGVAL(schema->module->ctx, LYVE_XPATH, "Invalid predicate for state %s \"%s\" in path \"%s\".",
lys_nodetype2str(schema->nodetype), schema->name, str_path);
LOG_LOCBACK(1, 0, 0, 0);
return LY_EINVAL;
}
- } else if ((schema->nodetype == LYS_LIST) && (path[u].pred_type != LY_PATH_PREDTYPE_LIST)) {
+ } else if ((schema->nodetype == LYS_LIST) &&
+ (!path[u].predicates || (path[u].predicates[0].type != LY_PATH_PREDTYPE_LIST))) {
if ((u < LY_ARRAY_COUNT(path) - 1) || !(options & LYD_NEW_PATH_OPAQ)) {
LOG_LOCSET(schema, NULL, NULL, NULL);
LOGVAL(schema->module->ctx, LYVE_XPATH, "Predicate missing for %s \"%s\" in path \"%s\".",
@@ -1339,7 +1546,8 @@ lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const
LOG_LOCBACK(1, 0, 0, 0);
return LY_EINVAL;
} /* else creating an opaque list */
- } else if ((schema->nodetype == LYS_LEAFLIST) && (path[u].pred_type != LY_PATH_PREDTYPE_LEAFLIST)) {
+ } else if ((schema->nodetype == LYS_LEAFLIST) &&
+ (!path[u].predicates || (path[u].predicates[0].type != LY_PATH_PREDTYPE_LEAFLIST))) {
r = LY_SUCCESS;
if (options & LYD_NEW_PATH_OPAQ) {
r = lyd_value_validate(NULL, schema, value, value_len, NULL, NULL, NULL);
@@ -1347,12 +1555,12 @@ lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const
if (!r) {
/* try to store the value */
LY_CHECK_RET(lyd_value_store(schema->module->ctx, &val, ((struct lysc_node_leaflist *)schema)->type,
- value, value_len, NULL, format, NULL, LYD_HINT_DATA, schema, NULL));
+ value, value_len, 0, NULL, format, NULL, LYD_HINT_DATA, schema, NULL));
++((struct lysc_type *)val.realtype)->refcount;
/* store the new predicate so that it is used when searching for this instance */
- path[u].pred_type = LY_PATH_PREDTYPE_LEAFLIST;
LY_ARRAY_NEW_RET(schema->module->ctx, path[u].predicates, pred, LY_EMEM);
+ pred->type = LY_PATH_PREDTYPE_LEAFLIST;
pred->value = val;
} /* else we have opaq flag and the value is not valid, leave no predicate and then create an opaque node */
}
@@ -1429,7 +1637,7 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
}
/* parse path */
- LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_OPTIONAL,
+ LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST,
LY_PATH_PRED_SIMPLE, &exp), cleanup);
/* compile path */
@@ -1442,32 +1650,36 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
/* try to find any existing nodes in the path */
if (parent) {
- ret = ly_path_eval_partial(p, parent, &path_idx, &node);
- if (ret == LY_SUCCESS) {
+ r = ly_path_eval_partial(p, parent, NULL, options & LYD_NEW_PATH_WITH_OPAQ, &path_idx, &node);
+ if (r == LY_SUCCESS) {
if (orig_count == LY_ARRAY_COUNT(p)) {
/* the node exists, are we supposed to update it or is it just a default? */
if (!(options & LYD_NEW_PATH_UPDATE) && !(node->flags & LYD_DEFAULT)) {
LOG_LOCSET(NULL, node, NULL, NULL);
- LOGVAL(ctx, LYVE_REFERENCE, "Path \"%s\" already exists", path);
+ LOGVAL(ctx, LYVE_REFERENCE, "Path \"%s\" already exists.", path);
LOG_LOCBACK(0, 1, 0, 0);
ret = LY_EEXIST;
goto cleanup;
+ } else if ((options & LYD_NEW_PATH_UPDATE) && lysc_is_key(node->schema)) {
+ /* fine, the key value must not be changed and has to be in the predicate to be found */
+ goto cleanup;
}
/* update the existing node */
ret = lyd_new_path_update(node, value, value_len, value_type, format, &nparent, &nnode);
goto cleanup;
} /* else we were not searching for the whole path */
- } else if (ret == LY_EINCOMPLETE) {
+ } else if (r == LY_EINCOMPLETE) {
/* some nodes were found, adjust the iterator to the next segment */
++path_idx;
- } else if (ret == LY_ENOTFOUND) {
+ } else if (r == LY_ENOTFOUND) {
/* we will create the nodes from top-level, default behavior (absolute path), or from the parent (relative path) */
if (lysc_data_parent(p[0].node)) {
node = parent;
}
} else {
/* error */
+ ret = r;
goto cleanup;
}
}
@@ -1487,15 +1699,14 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
if (lysc_is_dup_inst_list(schema)) {
/* create key-less list instance */
LY_CHECK_GOTO(ret = lyd_create_inner(schema, &node), cleanup);
- } else if ((options & LYD_NEW_PATH_OPAQ) && (p[path_idx].pred_type == LY_PATH_PREDTYPE_NONE)) {
+ } else if ((options & LYD_NEW_PATH_OPAQ) && !p[path_idx].predicates) {
/* creating opaque list without keys */
LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0,
schema->module->name, strlen(schema->module->name), NULL, 0, NULL, LY_VALUE_JSON, NULL,
LYD_NODEHINT_LIST, &node), cleanup);
} else {
/* create standard list instance */
- assert(p[path_idx].pred_type == LY_PATH_PREDTYPE_LIST);
- LY_CHECK_GOTO(ret = lyd_create_list(schema, p[path_idx].predicates, &node), cleanup);
+ LY_CHECK_GOTO(ret = lyd_create_list(schema, p[path_idx].predicates, NULL, &node), cleanup);
}
break;
case LYS_CONTAINER:
@@ -1505,7 +1716,8 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
LY_CHECK_GOTO(ret = lyd_create_inner(schema, &node), cleanup);
break;
case LYS_LEAFLIST:
- if ((options & LYD_NEW_PATH_OPAQ) && (p[path_idx].pred_type != LY_PATH_PREDTYPE_LEAFLIST)) {
+ if ((options & LYD_NEW_PATH_OPAQ) &&
+ (!p[path_idx].predicates || (p[path_idx].predicates[0].type != LY_PATH_PREDTYPE_LEAFLIST))) {
/* we have not checked this only for dup-inst lists, otherwise it must be opaque */
r = LY_EVALID;
if (lysc_is_dup_inst_list(schema)) {
@@ -1522,15 +1734,15 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
}
/* get value to set */
- if (p[path_idx].pred_type == LY_PATH_PREDTYPE_LEAFLIST) {
+ if (p[path_idx].predicates && (p[path_idx].predicates[0].type == LY_PATH_PREDTYPE_LEAFLIST)) {
val = &p[path_idx].predicates[0].value;
}
/* create a leaf-list instance */
if (val) {
- LY_CHECK_GOTO(ret = lyd_create_term2(schema, &p[path_idx].predicates[0].value, &node), cleanup);
+ LY_CHECK_GOTO(ret = lyd_create_term2(schema, val, &node), cleanup);
} else {
- LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA,
+ LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, 0, NULL, format, NULL, LYD_HINT_DATA,
NULL, &node), cleanup);
}
break;
@@ -1560,7 +1772,7 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
}
/* create a leaf instance */
- LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA, NULL,
+ LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, 0, NULL, format, NULL, LYD_HINT_DATA, NULL,
&node), cleanup);
break;
case LYS_ANYDATA:
@@ -1804,9 +2016,7 @@ lyd_new_implicit_tree(struct lyd_node *tree, uint32_t implicit_options, struct l
}
LYD_TREE_DFS_BEGIN(tree, node) {
- /* skip added default nodes */
- if (((node->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) &&
- (node->schema->nodetype & LYD_NODE_INNER)) {
+ if (node->schema->nodetype & LYD_NODE_INNER) {
LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &node_when, NULL,
NULL, implicit_options, diff), cleanup);
}
@@ -1891,16 +2101,12 @@ lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module,
/* process nested nodes */
LY_LIST_FOR(*tree, root) {
- /* skip added default nodes */
- if ((root->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) {
- LY_CHECK_GOTO(ret = lyd_new_implicit_tree(root, implicit_options, diff ? &d : NULL), cleanup);
-
- if (d) {
- /* merge into one diff */
- lyd_insert_sibling(*diff, d, diff);
+ LY_CHECK_GOTO(ret = lyd_new_implicit_tree(root, implicit_options, diff ? &d : NULL), cleanup);
- d = NULL;
- }
+ if (d) {
+ /* merge into one diff */
+ lyd_insert_sibling(*diff, d, diff);
+ d = NULL;
}
}
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 5c897bf..baf2c46 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -37,6 +37,7 @@
#include "parser_internal.h"
#include "parser_schema.h"
#include "path.h"
+#include "plugins_exts.h"
#include "plugins_internal.h"
#include "schema_compile.h"
#include "schema_compile_amend.h"
@@ -164,7 +165,11 @@ lys_getnext_(const struct lysc_node *last, const struct lysc_node *parent, const
const struct lysc_ext_instance *ext, uint32_t options)
{
const struct lysc_node *next = NULL;
- ly_bool action_flag = 0, notif_flag = 0;
+ ly_bool action_flag = 0, notif_flag = 0, sm_flag = options & LYS_GETNEXT_WITHSCHEMAMOUNT ? 0 : 1;
+ LY_ARRAY_COUNT_TYPE u;
+ struct ly_ctx *sm_ctx = NULL;
+ const struct lys_module *mod;
+ uint32_t idx;
LY_CHECK_ARG_RET(NULL, parent || module || ext, NULL);
@@ -172,7 +177,7 @@ next:
if (!last) {
/* first call */
- /* get know where to start */
+ /* learn where to start */
if (parent) {
/* schema subtree */
next = last = lysc_node_child(parent);
@@ -204,8 +209,29 @@ next:
repeat:
if (!next) {
- /* possibly go back to parent */
- if (last && (last->parent != parent)) {
+ if (last && !sm_flag && parent && (last->module->ctx != parent->module->ctx)) {
+ sm_flag = 1;
+
+ /* find the module of last */
+ sm_ctx = last->module->ctx;
+ idx = 0;
+ while ((mod = ly_ctx_get_module_iter(sm_ctx, &idx))) {
+ if (mod == last->module) {
+ break;
+ }
+ }
+ assert(mod);
+
+ /* get node from the next mounted module */
+ while (!next && (mod = ly_ctx_get_module_iter(sm_ctx, &idx))) {
+ if (!mod->implemented) {
+ continue;
+ }
+
+ next = lys_getnext(NULL, NULL, mod->compiled, options & ~LYS_GETNEXT_WITHSCHEMAMOUNT);
+ }
+ } else if (last && (last->parent != parent)) {
+ /* go back to parent */
last = last->parent;
goto next;
} else if (!action_flag) {
@@ -226,6 +252,35 @@ repeat:
} else {
next = (struct lysc_node *)module->notifs;
}
+ } else if (!sm_flag) {
+ sm_flag = 1;
+ if (parent) {
+ LY_ARRAY_FOR(parent->exts, u) {
+ if (!strcmp(parent->exts[u].def->name, "mount-point") &&
+ !strcmp(parent->exts[u].def->module->name, "ietf-yang-schema-mount")) {
+ lyplg_ext_schema_mount_create_context(&parent->exts[u], &sm_ctx);
+ if (sm_ctx) {
+ /* some usable context created */
+ break;
+ }
+ }
+ }
+ if (sm_ctx) {
+ /* get the first node from the first usable module */
+ idx = 0;
+ while (!next && (mod = ly_ctx_get_module_iter(sm_ctx, &idx))) {
+ if (!mod->implemented) {
+ continue;
+ }
+
+ next = lys_getnext(NULL, NULL, mod->compiled, options & ~LYS_GETNEXT_WITHSCHEMAMOUNT);
+ }
+ if (!next) {
+ /* no nodes found */
+ ly_ctx_destroy(sm_ctx);
+ }
+ }
+ }
} else {
return NULL;
}
@@ -387,7 +442,7 @@ lys_find_xpath_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
struct ly_set **set)
{
LY_ERR ret = LY_SUCCESS;
- struct lyxp_set xp_set;
+ struct lyxp_set xp_set = {0};
struct lyxp_expr *exp = NULL;
uint32_t i;
@@ -400,7 +455,9 @@ lys_find_xpath_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
ctx = ctx_node->module->ctx;
}
- memset(&xp_set, 0, sizeof xp_set);
+ /* allocate return set */
+ ret = ly_set_new(set);
+ LY_CHECK_GOTO(ret, cleanup);
/* compile expression */
ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp);
@@ -410,10 +467,6 @@ lys_find_xpath_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
ret = lyxp_atomize(ctx, exp, NULL, LY_VALUE_JSON, NULL, ctx_node, ctx_node, &xp_set, options);
LY_CHECK_GOTO(ret, cleanup);
- /* allocate return set */
- ret = ly_set_new(set);
- LY_CHECK_GOTO(ret, cleanup);
-
/* transform into ly_set */
(*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(ctx); ret = LY_EMEM, cleanup);
@@ -446,15 +499,15 @@ lys_find_expr_atoms(const struct lysc_node *ctx_node, const struct lys_module *c
options = LYXP_SCNODE;
}
+ /* allocate return set */
+ ret = ly_set_new(set);
+ LY_CHECK_GOTO(ret, cleanup);
+
/* atomize expression */
ret = lyxp_atomize(cur_mod->ctx, expr, cur_mod, LY_VALUE_SCHEMA_RESOLVED, (void *)prefixes, ctx_node, ctx_node,
&xp_set, options);
LY_CHECK_GOTO(ret, cleanup);
- /* allocate return set */
- ret = ly_set_new(set);
- LY_CHECK_GOTO(ret, cleanup);
-
/* transform into ly_set */
(*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(cur_mod->ctx); ret = LY_EMEM, cleanup);
@@ -497,6 +550,10 @@ lys_find_xpath(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
ctx = ctx_node->module->ctx;
}
+ /* allocate return set */
+ ret = ly_set_new(set);
+ LY_CHECK_GOTO(ret, cleanup);
+
/* compile expression */
ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp);
LY_CHECK_GOTO(ret, cleanup);
@@ -505,10 +562,6 @@ lys_find_xpath(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
ret = lyxp_atomize(ctx, exp, NULL, LY_VALUE_JSON, NULL, ctx_node, ctx_node, &xp_set, options);
LY_CHECK_GOTO(ret, cleanup);
- /* allocate return set */
- ret = ly_set_new(set);
- LY_CHECK_GOTO(ret, cleanup);
-
/* transform into ly_set */
(*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(ctx); ret = LY_EMEM, cleanup);
@@ -545,8 +598,8 @@ lys_find_lypath_atoms(const struct ly_path *path, struct ly_set **set)
LY_ARRAY_FOR(path, u) {
/* add nodes from the path */
LY_CHECK_GOTO(ret = ly_set_add(*set, (void *)path[u].node, 0, NULL), cleanup);
- if (path[u].pred_type == LY_PATH_PREDTYPE_LIST) {
- LY_ARRAY_FOR(path[u].predicates, v) {
+ LY_ARRAY_FOR(path[u].predicates, v) {
+ if ((path[u].predicates[v].type == LY_PATH_PREDTYPE_LIST) || (path[u].predicates[v].type == LY_PATH_PREDTYPE_LIST_VAR)) {
/* add all the keys in a predicate */
LY_CHECK_GOTO(ret = ly_set_add(*set, (void *)path[u].predicates[v].key, 0, NULL), cleanup);
}
@@ -578,7 +631,8 @@ lys_find_path_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
}
/* parse */
- ret = lyxp_expr_parse(ctx, path, strlen(path), 0, &expr);
+ ret = ly_path_parse(ctx, ctx_node, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST,
+ LY_PATH_PRED_SIMPLE, &expr);
LY_CHECK_GOTO(ret, cleanup);
/* compile */
@@ -599,7 +653,7 @@ LIBYANG_API_DEF const struct lysc_node *
lys_find_path(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *path, ly_bool output)
{
const struct lysc_node *snode = NULL;
- struct lyxp_expr *exp = NULL;
+ struct lyxp_expr *expr = NULL;
struct ly_path *p = NULL;
LY_ERR ret;
uint8_t oper;
@@ -612,12 +666,13 @@ lys_find_path(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
}
/* parse */
- ret = lyxp_expr_parse(ctx, path, strlen(path), 0, &exp);
+ ret = ly_path_parse(ctx, ctx_node, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST,
+ LY_PATH_PRED_SIMPLE, &expr);
LY_CHECK_GOTO(ret, cleanup);
/* compile */
oper = output ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
- ret = ly_path_compile(ctx, NULL, ctx_node, NULL, exp, oper, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p);
+ ret = ly_path_compile(ctx, NULL, ctx_node, NULL, expr, oper, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p);
LY_CHECK_GOTO(ret, cleanup);
/* get last node */
@@ -625,7 +680,7 @@ lys_find_path(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
cleanup:
ly_path_free(ctx, p);
- lyxp_expr_free(ctx, exp);
+ lyxp_expr_free(ctx, expr);
return snode;
}
@@ -1457,9 +1512,10 @@ error:
static LY_ERR
lysp_add_internal_ietf_netconf(struct lysp_ctx *pctx, struct lysp_module *mod)
{
- struct lysp_ext_instance *extp;
+ struct lysp_ext_instance *extp, *prev_exts = mod->exts;
struct lysp_stmt *stmt;
struct lysp_import *imp;
+ uint32_t idx;
/*
* 1) edit-config's operation
@@ -1599,10 +1655,16 @@ lysp_add_internal_ietf_netconf(struct lysp_ctx *pctx, struct lysp_module *mod)
stmt->prefix_data = mod;
stmt->kw = LY_STMT_TYPE;
- if (LY_ARRAY_COUNT(mod->exts) == 3) {
+ if (!prev_exts) {
/* first extension instances */
assert(pctx->main_ctx == pctx);
LY_CHECK_RET(ly_set_add(&pctx->ext_inst, mod->exts, 1, NULL));
+ } else {
+ /* replace previously stored extension instances */
+ if (!ly_set_contains(&pctx->ext_inst, prev_exts, &idx)) {
+ LOGINT_RET(mod->mod->ctx);
+ }
+ pctx->ext_inst.objs[idx] = mod->exts;
}
/* create new imports for the used prefixes */
@@ -1630,9 +1692,10 @@ lysp_add_internal_ietf_netconf(struct lysp_ctx *pctx, struct lysp_module *mod)
static LY_ERR
lysp_add_internal_ietf_netconf_with_defaults(struct lysp_ctx *pctx, struct lysp_module *mod)
{
- struct lysp_ext_instance *extp;
+ struct lysp_ext_instance *extp, *prev_exts = mod->exts;
struct lysp_stmt *stmt;
struct lysp_import *imp;
+ uint32_t idx;
/* add new extension instance */
LY_ARRAY_NEW_RET(mod->mod->ctx, mod->exts, extp, LY_EMEM);
@@ -1654,10 +1717,16 @@ lysp_add_internal_ietf_netconf_with_defaults(struct lysp_ctx *pctx, struct lysp_
stmt->prefix_data = mod;
stmt->kw = LY_STMT_TYPE;
- if (LY_ARRAY_COUNT(mod->exts) == 1) {
- /* first extension instance */
+ if (!prev_exts) {
+ /* first extension instances */
assert(pctx->main_ctx == pctx);
LY_CHECK_RET(ly_set_add(&pctx->ext_inst, mod->exts, 1, NULL));
+ } else {
+ /* replace previously stored extension instances */
+ if (!ly_set_contains(&pctx->ext_inst, prev_exts, &idx)) {
+ LOGINT_RET(mod->mod->ctx);
+ }
+ pctx->ext_inst.objs[idx] = mod->exts;
}
/* create new import for the used prefix */
@@ -1680,8 +1749,6 @@ lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format,
struct lysp_yin_ctx *yinctx = NULL;
struct lysp_ctx *pctx = NULL;
struct lysf_ctx fctx = {.ctx = ctx};
- char *filename, *rev, *dot;
- size_t len;
ly_bool module_created = 0;
assert(ctx && in && new_mods);
@@ -1762,30 +1829,7 @@ lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format,
switch (in->type) {
case LY_IN_FILEPATH:
- /* check that name and revision match filename */
- filename = strrchr(in->method.fpath.filepath, '/');
- if (!filename) {
- filename = in->method.fpath.filepath;
- } else {
- filename++;
- }
- rev = strchr(filename, '@');
- dot = strrchr(filename, '.');
-
- /* name */
- len = strlen(mod->name);
- 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);
- }
- if (rev) {
- len = dot - ++rev;
- if (!mod->parsed->revs || (len != LY_REV_SIZE - 1) || 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].date : "none");
- }
- }
-
+ ly_check_module_filename(ctx, mod->name, mod->parsed->revs ? mod->parsed->revs[0].date : NULL, in->method.fpath.filepath);
break;
case LY_IN_FD:
case LY_IN_FILE:
diff --git a/src/tree_schema.h b/src/tree_schema.h
index c57a0fc..e712239 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1579,9 +1579,7 @@ struct lysc_node_choice {
};
};
- 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 *cases; /**< list of all the cases (linked list) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
struct lysc_node_case *dflt; /**< default case of the choice, only a pointer into the cases array. */
};
@@ -1887,6 +1885,14 @@ LIBYANG_API_DECL struct lysc_must *lysc_node_musts(const struct lysc_node *node)
LIBYANG_API_DECL struct lysc_when **lysc_node_when(const struct lysc_node *node);
/**
+ * @brief Get the target node of a leafref node.
+ *
+ * @param[in] node Leafref node.
+ * @return Leafref target, NULL on any error.
+ */
+LIBYANG_API_DECL const struct lysc_node *lysc_node_lref_target(const struct lysc_node *node);
+
+/**
* @brief Callback to be called for every schema node in a DFS traversal.
*
* @param[in] node Current node.
@@ -1971,6 +1977,8 @@ LIBYANG_API_DECL struct lysp_feature *lysp_feature_next(const struct lysp_featur
#define LYS_FIND_XP_OUTPUT 0x10 /**< Search RPC/action output nodes instead of input ones. */
#define LYS_FIND_NO_MATCH_ERROR 0x40 /**< Return error if a path segment matches no nodes, otherwise only warning
is printed. */
+#define LYS_FIND_SCHEMAMOUNT 0x0200 /**< Traverse also nodes from mounted modules. If any such nodes are returned,
+ the caller **must free** their context! */
/** @} findxpathoptions */
/**
@@ -2189,6 +2197,9 @@ LIBYANG_API_DECL const struct lysc_node *lys_getnext_ext(const struct lysc_node
#define LYS_GETNEXT_INTONPCONT 0x08 /**< ::lys_getnext() option to look into non-presence container, instead of returning container itself */
#define LYS_GETNEXT_OUTPUT 0x10 /**< ::lys_getnext() option to provide RPC's/action's output schema nodes instead of input schema nodes
provided by default */
+#define LYS_GETNEXT_WITHSCHEMAMOUNT 0x20 /**< ::lys_getnext() option to also traverse top-level nodes of all the mounted modules
+ on the parent mount point but note that if any such nodes are returned,
+ the caller **must free** their context */
/** @} sgetnextflags */
/**
diff --git a/src/tree_schema_common.c b/src/tree_schema_common.c
index bbdd676..6d3b710 100644
--- a/src/tree_schema_common.c
+++ b/src/tree_schema_common.c
@@ -32,6 +32,7 @@
#include "in_internal.h"
#include "log.h"
#include "parser_schema.h"
+#include "path.h"
#include "schema_compile.h"
#include "schema_features.h"
#include "set.h"
@@ -104,7 +105,7 @@ lysp_check_date(struct lysp_ctx *ctx, const char *date, size_t date_len, const c
error:
if (stmt) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, date_len, date, stmt);
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)date_len, date, stmt);
}
return LY_EINVAL;
}
@@ -140,10 +141,10 @@ lysp_check_enum_name(struct lysp_ctx *ctx, const char *name, size_t name_len)
(int)name_len, name);
return LY_EVALID;
} else {
- for (size_t u = 0; u < name_len; ++u) {
+ for (uint32_t u = 0; u < name_len; ++u) {
if (iscntrl(name[u])) {
- LOGWRN(PARSER_CTX(ctx), "Control characters in enum name should be avoided (\"%.*s\", character number %d).",
- (int)name_len, name, u + 1);
+ LOGWRN(PARSER_CTX(ctx), "Control characters in enum name should be avoided "
+ "(\"%.*s\", character number %" PRIu32 ").", (int)name_len, name, u + 1);
break;
}
}
@@ -350,19 +351,18 @@ lysp_type_find(const char *id, struct lysp_node *start_node, const struct lysp_m
* @param[in,out] ctx Context to log the error.
* @param[in,out] ht Hash table with top-level names.
* @param[in] name Inserted top-level identifier.
- * @param[in] statement The name of the statement type from which
- * @p name originated (eg typedef, feature, ...).
+ * @param[in] statement The name of the statement type from which @p name originated (eg typedef, feature, ...).
* @param[in] err_detail Optional error specification.
* @return LY_ERR, but LY_EEXIST is mapped to LY_EVALID.
*/
static LY_ERR
-lysp_check_dup_ht_insert(struct lysp_ctx *ctx, struct hash_table *ht,
- const char *name, const char *statement, const char *err_detail)
+lysp_check_dup_ht_insert(struct lysp_ctx *ctx, struct ly_ht *ht, const char *name, const char *statement,
+ const char *err_detail)
{
LY_ERR ret;
uint32_t hash;
- hash = dict_hash(name, strlen(name));
+ hash = lyht_hash(name, strlen(name));
ret = lyht_insert(ht, &name, hash, NULL);
if (ret == LY_EEXIST) {
if (err_detail) {
@@ -388,7 +388,7 @@ lysp_check_dup_ht_insert(struct lysp_ctx *ctx, struct hash_table *ht,
*/
static LY_ERR
lysp_check_dup_typedef(struct lysp_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
- struct hash_table *tpdfs_global)
+ struct ly_ht *tpdfs_global)
{
struct lysp_node *parent;
uint32_t hash;
@@ -434,7 +434,7 @@ lysp_check_dup_typedef(struct lysp_ctx *ctx, struct lysp_node *node, const struc
/* check collision with the top-level typedefs */
if (node) {
- hash = dict_hash(name, name_len);
+ hash = lyht_hash(name, name_len);
if (!lyht_find(tpdfs_global, &name, hash, NULL)) {
LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
"Duplicate identifier \"%s\" of typedef statement - scoped type collide with a top-level type.", name);
@@ -469,7 +469,7 @@ lysp_id_cmp(void *val1, void *val2, ly_bool UNUSED(mod), void *UNUSED(cb_data))
LY_ERR
lysp_check_dup_typedefs(struct lysp_ctx *ctx, struct lysp_module *mod)
{
- struct hash_table *ids_global;
+ struct ly_ht *ids_global;
const struct lysp_tpdf *typedefs;
LY_ARRAY_COUNT_TYPE u, v;
uint32_t i;
@@ -496,7 +496,7 @@ lysp_check_dup_typedefs(struct lysp_ctx *ctx, struct lysp_module *mod)
}
cleanup:
- lyht_free(ids_global);
+ lyht_free(ids_global, NULL);
return ret;
}
@@ -528,7 +528,7 @@ lysp_grouping_match(const char *name, struct lysp_node *node)
*/
static LY_ERR
lysp_check_dup_grouping(struct lysp_ctx *ctx, struct lysp_node *node, const struct lysp_node_grp *grp,
- struct hash_table *grps_global)
+ struct ly_ht *grps_global)
{
struct lysp_node *parent;
uint32_t hash;
@@ -567,7 +567,7 @@ lysp_check_dup_grouping(struct lysp_ctx *ctx, struct lysp_node *node, const stru
/* check collision with the top-level groupings */
if (node) {
- hash = dict_hash(name, name_len);
+ hash = lyht_hash(name, name_len);
if (!lyht_find(grps_global, &name, hash, NULL)) {
LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
"Duplicate identifier \"%s\" of grouping statement - scoped grouping collide with a top-level grouping.", name);
@@ -584,7 +584,7 @@ lysp_check_dup_grouping(struct lysp_ctx *ctx, struct lysp_node *node, const stru
LY_ERR
lysp_check_dup_groupings(struct lysp_ctx *ctx, struct lysp_module *mod)
{
- struct hash_table *ids_global;
+ struct ly_ht *ids_global;
const struct lysp_node_grp *groupings, *grp_iter;
LY_ARRAY_COUNT_TYPE u;
uint32_t i;
@@ -610,7 +610,7 @@ lysp_check_dup_groupings(struct lysp_ctx *ctx, struct lysp_module *mod)
}
cleanup:
- lyht_free(ids_global);
+ lyht_free(ids_global, NULL);
return ret;
}
@@ -626,7 +626,7 @@ LY_ERR
lysp_check_dup_features(struct lysp_ctx *ctx, struct lysp_module *mod)
{
LY_ARRAY_COUNT_TYPE u;
- struct hash_table *ht;
+ struct ly_ht *ht;
struct lysp_feature *f;
LY_ERR ret = LY_SUCCESS;
@@ -650,7 +650,7 @@ lysp_check_dup_features(struct lysp_ctx *ctx, struct lysp_module *mod)
}
cleanup:
- lyht_free(ht);
+ lyht_free(ht, NULL);
return ret;
}
@@ -658,7 +658,7 @@ LY_ERR
lysp_check_dup_identities(struct lysp_ctx *ctx, struct lysp_module *mod)
{
LY_ARRAY_COUNT_TYPE u;
- struct hash_table *ht;
+ struct ly_ht *ht;
struct lysp_ident *i;
LY_ERR ret = LY_SUCCESS;
@@ -682,7 +682,7 @@ lysp_check_dup_identities(struct lysp_ctx *ctx, struct lysp_module *mod)
}
cleanup:
- lyht_free(ht);
+ lyht_free(ht, NULL);
return ret;
}
@@ -697,9 +697,8 @@ static LY_ERR
lysp_load_module_check(const struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *data)
{
struct lysp_load_module_check_data *info = data;
- const char *filename, *dot, *rev, *name;
+ const char *name;
uint8_t latest_revision;
- size_t len;
struct lysp_revision *revs;
name = mod ? mod->mod->name : submod->name;
@@ -740,29 +739,7 @@ lysp_load_module_check(const struct ly_ctx *ctx, struct lysp_module *mod, struct
}
}
if (info->path) {
- /* check that name and revision match filename */
- filename = strrchr(info->path, '/');
- if (!filename) {
- filename = info->path;
- } else {
- filename++;
- }
- /* name */
- len = strlen(name);
- rev = strchr(filename, '@');
- dot = strrchr(info->path, '.');
- if (strncmp(filename, name, len) ||
- ((rev && (rev != &filename[len])) || (!rev && (dot != &filename[len])))) {
- LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, name);
- }
- /* revision */
- if (rev) {
- len = dot - ++rev;
- if (!revs || (len != LY_REV_SIZE - 1) || strncmp(revs[0].date, rev, len)) {
- LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
- revs ? revs[0].date : "none");
- }
- }
+ ly_check_module_filename(ctx, name, revs ? revs[0].date : NULL, info->path);
}
return LY_SUCCESS;
}
@@ -921,7 +898,7 @@ lys_get_module_without_revision(struct ly_ctx *ctx, const char *name)
struct lys_module *mod, *mod_impl;
uint32_t index;
- /* Try to find module with LYS_MOD_IMPORTED_REV flag. */
+ /* try to find module with LYS_MOD_IMPORTED_REV flag */
index = 0;
while ((mod = ly_ctx_get_module_iter(ctx, &index))) {
if (!strcmp(mod->name, name) && (mod->latest_revision & LYS_MOD_IMPORTED_REV)) {
@@ -929,17 +906,19 @@ lys_get_module_without_revision(struct ly_ctx *ctx, const char *name)
}
}
- /* Try to find the implemented module. */
+ /* try to find the implemented module */
mod_impl = ly_ctx_get_module_implemented(ctx, name);
- if (mod && mod_impl) {
- LOGVRB("Implemented module \"%s@%s\" is not used for import, revision \"%s\" is imported instead.",
- mod_impl->name, mod_impl->revision, mod->revision);
+ if (mod) {
+ if (mod_impl && (mod != mod_impl)) {
+ LOGVRB("Implemented module \"%s@%s\" is not used for import, revision \"%s\" is imported instead.",
+ mod_impl->name, mod_impl->revision, mod->revision);
+ }
return mod;
} else if (mod_impl) {
return mod_impl;
}
- /* Try to find the latest module in the current context. */
+ /* try to find the latest module in the current context */
mod = ly_ctx_get_module_latest(ctx, name);
return mod;
@@ -949,10 +928,8 @@ lys_get_module_without_revision(struct ly_ctx *ctx, const char *name)
* @brief Check if a circular dependency exists between modules.
*
* @param[in] ctx libyang context for log an error.
- * @param[in,out] mod Examined module which is set to NULL
- * if the circular dependency is detected.
- * @return LY_SUCCESS if no circular dependecy is detected,
- * otherwise LY_EVALID.
+ * @param[in,out] mod Examined module which is set to NULL if the circular dependency is detected.
+ * @return LY_SUCCESS if no circular dependecy is detected, otherwise LY_EVALID.
*/
static LY_ERR
lys_check_circular_dependency(struct ly_ctx *ctx, struct lys_module **mod)
@@ -1184,7 +1161,6 @@ lysp_inject_submodule(struct lysp_ctx *pctx, struct lysp_include *inc)
DUP_STRING_RET(PARSER_CTX(pctx), inc->name, inc_new->name);
DUP_STRING_RET(PARSER_CTX(pctx), inc->dsc, inc_new->dsc);
DUP_STRING_RET(PARSER_CTX(pctx), inc->ref, inc_new->ref);
- /* TODO duplicate extensions */
memcpy(inc_new->rev, inc->rev, LY_REV_SIZE);
inc_new->injected = 1;
}
@@ -1826,6 +1802,36 @@ lysc_node_when(const struct lysc_node *node)
}
}
+LIBYANG_API_DEF const struct lysc_node *
+lysc_node_lref_target(const struct lysc_node *node)
+{
+ struct lysc_type_leafref *lref;
+ struct ly_path *p;
+ const struct lysc_node *target;
+
+ if (!node || !(node->nodetype & LYD_NODE_TERM)) {
+ return NULL;
+ }
+
+ lref = (struct lysc_type_leafref *)((struct lysc_node_leaf *)node)->type;
+ if (lref->basetype != LY_TYPE_LEAFREF) {
+ return NULL;
+ }
+
+ /* compile the path */
+ if (ly_path_compile_leafref(node->module->ctx, node, NULL, lref->path,
+ (node->flags & LYS_IS_OUTPUT) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY,
+ LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, &p)) {
+ return NULL;
+ }
+
+ /* get the target node */
+ target = p[LY_ARRAY_COUNT(p) - 1].node;
+ ly_path_free(node->module->ctx, p);
+
+ return target;
+}
+
enum ly_stmt
lysp_match_kw(struct ly_in *in, uint64_t *indent)
{
@@ -2615,3 +2621,41 @@ lys_stmt_flags(enum ly_stmt stmt)
return 0;
}
+
+void
+ly_check_module_filename(const struct ly_ctx *ctx, const char *name, const char *revision, const char *filename)
+{
+ const char *basename, *rev, *dot;
+ size_t len;
+
+ /* check that name and revision match filename */
+ basename = strrchr(filename, '/');
+#ifdef _WIN32
+ const char *backslash = strrchr(filename, '\\');
+
+ if (!basename || (basename && backslash && (backslash > basename))) {
+ basename = backslash;
+ }
+#endif
+ if (!basename) {
+ basename = filename;
+ } else {
+ basename++; /* leading slash */
+ }
+ rev = strchr(basename, '@');
+ dot = strrchr(basename, '.');
+
+ /* name */
+ len = strlen(name);
+ if (strncmp(basename, name, len) ||
+ ((rev && (rev != &basename[len])) || (!rev && (dot != &basename[len])))) {
+ LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", basename, name);
+ }
+ if (rev) {
+ len = dot - ++rev;
+ if (!revision || (len != LY_REV_SIZE - 1) || strncmp(revision, rev, len)) {
+ LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", basename,
+ revision ? revision : "none");
+ }
+ }
+}
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 95dee0b..0a2ad3f 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -732,4 +732,14 @@ uint8_t lys_stmt_flags(enum ly_stmt stmt);
*/
LY_ERR lyplg_ext_get_storage_p(const struct lysc_ext_instance *ext, int stmt, const void ***storage_p);
+/**
+ * @brief Warning if the filename does not match the expected module name and version
+ *
+ * @param[in] ctx Context for logging
+ * @param[in] name Expected module name
+ * @param[in] revision Expected module revision, or NULL if not to be checked
+ * @param[in] filename File path to be checked
+ */
+void ly_check_module_filename(const struct ly_ctx *ctx, const char *name, const char *revision, const char *filename);
+
#endif /* LY_TREE_SCHEMA_INTERNAL_H_ */
diff --git a/src/validation.c b/src/validation.c
index 6db020a..35136ad 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Validation
*
- * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2019 - 2023 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.
@@ -40,6 +40,22 @@
#include "tree_schema_internal.h"
#include "xpath.h"
+/**
+ * @brief Check validation error taking into account multi-error validation.
+ *
+ * @param[in] r Local return value.
+ * @param[in] err_cmd Command to perform on any error.
+ * @param[in] val_opts Validation options.
+ * @param[in] label Label to go to on fatal error.
+ */
+#define LY_VAL_ERR_GOTO(r, err_cmd, val_opts, label) \
+ if (r) { \
+ err_cmd; \
+ if ((r != LY_EVALID) || !(val_opts & LYD_VALIDATE_MULTI_ERROR)) { \
+ goto label; \
+ } \
+ }
+
LY_ERR
lyd_val_diff_add(const struct lyd_node *node, enum lyd_diff_op op, struct lyd_node **diff)
{
@@ -126,7 +142,7 @@ static LY_ERR
lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node, const struct lysc_node *schema,
uint32_t xpath_options, const struct lysc_when **disabled)
{
- LY_ERR ret;
+ LY_ERR r;
const struct lyd_node *ctx_node;
struct lyxp_set xp_set;
LY_ARRAY_COUNT_TYPE u;
@@ -152,12 +168,12 @@ lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node,
/* evaluate when */
memset(&xp_set, 0, sizeof xp_set);
- ret = lyxp_eval(LYD_CTX(node), when->cond, schema->module, LY_VALUE_SCHEMA_RESOLVED, when->prefixes,
+ r = lyxp_eval(LYD_CTX(node), when->cond, schema->module, LY_VALUE_SCHEMA_RESOLVED, when->prefixes,
ctx_node, ctx_node, tree, NULL, &xp_set, LYXP_SCHEMA | xpath_options);
lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
/* return error or LY_EINCOMPLETE for dependant unresolved when */
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(r);
if (!xp_set.val.bln) {
/* false when */
@@ -173,6 +189,62 @@ lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node,
}
/**
+ * @brief Properly delete a node as part of auto-delete validation tasks.
+ *
+ * @param[in,out] first First sibling, is updated if needed.
+ * @param[in] del Node instance to delete.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in] np_cont_diff Whether to put NP container into diff or only its children.
+ * @param[in,out] node Optional current iteration node, update it if it is deleted.
+ * @param[in,out] node_when Optional set with nodes with "when" conditions, may be removed from.
+ * @param[in,out] diff Validation diff.
+ * @return 1 if @p node auto-deleted and updated to its next sibling.
+ * @return 0 if @p node was not auto-deleted.
+ */
+static ly_bool
+lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *del, const struct lys_module *mod,
+ int np_cont_diff, struct lyd_node **node, struct ly_set *node_types, struct lyd_node **diff)
+{
+ struct lyd_node *iter;
+ ly_bool node_autodel = 0;
+ uint32_t idx;
+
+ /* update pointers */
+ lyd_del_move_root(first, del, mod);
+ if (node && (del == *node)) {
+ *node = (*node)->next;
+ node_autodel = 1;
+ }
+
+ if (diff) {
+ /* add into diff */
+ if (!np_cont_diff && (del->schema->nodetype == LYS_CONTAINER) && !(del->schema->flags & LYS_PRESENCE)) {
+ /* we do not want to track NP container changes, but remember any removed children */
+ LY_LIST_FOR(lyd_child(del), iter) {
+ lyd_val_diff_add(iter, LYD_DIFF_OP_DELETE, diff);
+ }
+ } else {
+ lyd_val_diff_add(del, LYD_DIFF_OP_DELETE, diff);
+ }
+ }
+
+ if (node_types && node_types->count) {
+ /* remove from node_types set */
+ LYD_TREE_DFS_BEGIN(del, iter) {
+ if (ly_set_contains(node_types, iter, &idx)) {
+ ly_set_rm_index(node_types, idx, NULL);
+ }
+ LYD_TREE_DFS_END(del, iter);
+ }
+ }
+
+ /* free */
+ lyd_free_tree(del);
+
+ return node_autodel;
+}
+
+/**
* @brief Evaluate when conditions of collected unres nodes.
*
* @param[in,out] tree Data tree, is updated if some nodes are autodeleted.
@@ -180,6 +252,7 @@ lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node,
* If set, it is expected @p tree should point to the first node of @p mod. Otherwise it will simply be
* the first top-level sibling.
* @param[in] node_when Set with nodes with "when" conditions.
+ * @param[in] val_opts Validation options.
* @param[in] xpath_options Additional XPath options to use.
* @param[in,out] node_types Set with nodes with unresolved types, remove any with false "when" parents.
* @param[in,out] diff Validation diff.
@@ -187,13 +260,13 @@ lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node,
* @return LY_ERR value on error.
*/
static LY_ERR
-lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when,
+lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when, uint32_t val_opts,
uint32_t xpath_options, struct ly_set *node_types, struct lyd_node **diff)
{
- LY_ERR rc, r;
- uint32_t i, idx;
+ LY_ERR rc = LY_SUCCESS, r;
+ uint32_t i;
const struct lysc_when *disabled;
- struct lyd_node *node = NULL, *elem;
+ struct lyd_node *node = NULL;
if (!node_when->count) {
return LY_SUCCESS;
@@ -212,32 +285,15 @@ lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, st
/* when false */
if (node->flags & LYD_WHEN_TRUE) {
/* autodelete */
- lyd_del_move_root(tree, node, mod);
- if (diff) {
- /* add into diff */
- LY_CHECK_GOTO(rc = lyd_val_diff_add(node, LYD_DIFF_OP_DELETE, diff), error);
- }
-
- /* remove from node types set, if present */
- if (node_types && node_types->count) {
- LYD_TREE_DFS_BEGIN(node, elem) {
- /* only term nodes with a validation callback can be in node_types */
- if ((elem->schema->nodetype & LYD_NODE_TERM) &&
- ((struct lysc_node_leaf *)elem->schema)->type->plugin->validate &&
- ly_set_contains(node_types, elem, &idx)) {
- LY_CHECK_GOTO(rc = ly_set_rm_index(node_types, idx, NULL), error);
- }
- LYD_TREE_DFS_END(node, elem);
- }
- }
-
- /* free */
- lyd_free_tree(node);
+ lyd_validate_autodel_node_del(tree, node, mod, 1, NULL, node_types, diff);
+ } else if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ LOGWRN(LYD_CTX(node), "When condition \"%s\" not satisfied.", disabled->cond->expr);
} else {
/* invalid data */
LOGVAL(LYD_CTX(node), LY_VCODE_NOWHEN, disabled->cond->expr);
- rc = LY_EVALID;
- goto error;
+ r = LY_EVALID;
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, error);
}
} else {
/* when true */
@@ -248,14 +304,13 @@ lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, st
ly_set_rm_index_ordered(node_when, i, NULL);
} else if (r != LY_EINCOMPLETE) {
/* error */
- rc = r;
- goto error;
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, error);
}
LOG_LOCBACK(1, 1, 0, 0);
} while (i);
- return LY_SUCCESS;
+ return rc;
error:
LOG_LOCBACK(1, 1, 0, 0);
@@ -267,7 +322,7 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
uint32_t when_xp_opts, struct ly_set *node_types, struct ly_set *meta_types, struct ly_set *ext_node,
struct ly_set *ext_val, uint32_t val_opts, struct lyd_node **diff)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
uint32_t i;
if (ext_val && ext_val->count) {
@@ -279,8 +334,8 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
struct lyd_ctx_ext_val *ext_v = ext_val->objs[i];
/* validate extension data */
- ret = ext_v->ext->def->plugin->validate(ext_v->ext, ext_v->sibling, *tree, data_type, val_opts, diff);
- LY_CHECK_RET(ret);
+ r = ext_v->ext->def->plugin->validate(ext_v->ext, ext_v->sibling, *tree, data_type, val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* remove this item from the set */
ly_set_rm_index(ext_val, i, free);
@@ -296,8 +351,8 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
struct lyd_ctx_ext_node *ext_n = ext_node->objs[i];
/* validate the node */
- ret = ext_n->ext->def->plugin->node(ext_n->ext, ext_n->node, val_opts);
- LY_CHECK_RET(ret);
+ r = ext_n->ext->def->plugin->node(ext_n->ext, ext_n->node, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* remove this item from the set */
ly_set_rm_index(ext_node, i, free);
@@ -310,12 +365,14 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
do {
prev_count = node_when->count;
- LY_CHECK_RET(lyd_validate_unres_when(tree, mod, node_when, when_xp_opts, node_types, diff));
+ r = lyd_validate_unres_when(tree, mod, node_when, val_opts, when_xp_opts, node_types, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
+
/* there must have been some when conditions resolved */
} while (prev_count > node_when->count);
/* there could have been no cyclic when dependencies, checked during compilation */
- assert(!node_when->count);
+ assert(!node_when->count || ((rc == LY_EVALID) && (val_opts & LYD_VALIDATE_MULTI_ERROR)));
}
if (node_types && node_types->count) {
@@ -329,9 +386,9 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
/* resolve the value of the node */
LOG_LOCSET(NULL, &node->node, NULL, NULL);
- ret = lyd_value_validate_incomplete(LYD_CTX(node), type, &node->value, &node->node, *tree);
+ r = lyd_value_validate_incomplete(LYD_CTX(node), type, &node->value, &node->node, *tree);
LOG_LOCBACK(0, 1, 0, 0);
- LY_CHECK_RET(ret);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* remove this node from the set */
ly_set_rm_index(node_types, i, NULL);
@@ -349,15 +406,16 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
/* validate and store the value of the metadata */
lyplg_ext_get_storage(meta->annotation, LY_STMT_TYPE, sizeof type, (const void **)&type);
- ret = lyd_value_validate_incomplete(LYD_CTX(meta->parent), type, &meta->value, meta->parent, *tree);
- LY_CHECK_RET(ret);
+ r = lyd_value_validate_incomplete(LYD_CTX(meta->parent), type, &meta->value, meta->parent, *tree);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* remove this attr from the set */
ly_set_rm_index(meta_types, i, NULL);
} while (i);
}
- return ret;
+cleanup:
+ return rc;
}
/**
@@ -365,12 +423,13 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
*
* @param[in] first First sibling to search in.
* @param[in] node Data node instance to check.
+ * @param[in] val_opts Validation options.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *node)
+lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *node, uint32_t val_opts)
{
- struct lyd_node **match_p;
+ struct lyd_node **match_p, *match;
ly_bool fail = 0;
assert(node->flags & LYD_NEW);
@@ -383,7 +442,12 @@ lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *nod
/* find exactly the same next instance using hashes if possible */
if (node->parent && node->parent->children_ht) {
- if (!lyht_find_next(node->parent->children_ht, &node, node->hash, (void **)&match_p)) {
+ lyd_find_sibling_first(first, node, &match);
+ assert(match);
+
+ if (match != node) {
+ fail = 1;
+ } else if (!lyht_find_next(node->parent->children_ht, &node, node->hash, (void **)&match_p)) {
fail = 1;
}
} else {
@@ -405,8 +469,17 @@ lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *nod
}
if (fail) {
- LOGVAL(node->schema->module->ctx, LY_VCODE_DUP, node->schema->name);
- return LY_EVALID;
+ if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (val_opts & LYD_VALIDATE_OPERATIONAL)) {
+ /* only a warning */
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ LOGWRN(node->schema->module->ctx, "Duplicate instance of \"%s\".", node->schema->name);
+ LOG_LOCBACK(0, 1, 0, 0);
+ } else {
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ LOGVAL(node->schema->module->ctx, LY_VCODE_DUP, node->schema->name);
+ LOG_LOCBACK(0, 1, 0, 0);
+ return LY_EVALID;
+ }
}
return LY_SUCCESS;
}
@@ -530,45 +603,6 @@ lyd_val_has_default(const struct lysc_node *schema)
}
/**
- * @brief Properly delete a node as part of auto-delete validation tasks.
- *
- * @param[in,out] first First sibling, is updated if needed.
- * @param[in] del Node instance to delete.
- * @param[in] mod Module of the siblings, NULL for nested siblings.
- * @param[in,out] node Current iteration node, update it if it is deleted.
- * @param[in,out] diff Validation diff.
- * @return 1 if @p node auto-deleted and updated to its next sibling.
- * @return 0 if @p node was not auto-deleted.
- */
-static ly_bool
-lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *del, const struct lys_module *mod,
- struct lyd_node **node, struct lyd_node **diff)
-{
- struct lyd_node *iter;
- ly_bool node_autodel = 0;
-
- lyd_del_move_root(first, del, mod);
- if (del == *node) {
- *node = (*node)->next;
- node_autodel = 1;
- }
- if (diff) {
- /* add into diff */
- if ((del->schema->nodetype == LYS_CONTAINER) && !(del->schema->flags & LYS_PRESENCE)) {
- /* we do not want to track NP container changes, but remember any removed children */
- LY_LIST_FOR(lyd_child(del), iter) {
- lyd_val_diff_add(iter, LYD_DIFF_OP_DELETE, diff);
- }
- } else {
- lyd_val_diff_add(del, LYD_DIFF_OP_DELETE, diff);
- }
- }
- lyd_free_tree(del);
-
- return node_autodel;
-}
-
-/**
* @brief Auto-delete leaf-list default instances to prevent validation errors.
*
* @param[in,out] first First sibling to search in, is updated if needed.
@@ -606,7 +640,7 @@ lyd_validate_autodel_leaflist_dflt(struct lyd_node **first, struct lyd_node **no
LYD_LIST_FOR_INST_SAFE(*first, schema, next, iter) {
if (iter->flags & LYD_DEFAULT) {
/* default instance found, remove it */
- if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+ if (lyd_validate_autodel_node_del(first, iter, mod, 0, node, NULL, diff)) {
node_autodel = 1;
}
}
@@ -651,7 +685,7 @@ lyd_validate_autodel_cont_leaf_dflt(struct lyd_node **first, struct lyd_node **n
LYD_LIST_FOR_INST_SAFE(*first, schema, next, iter) {
if (iter->flags & LYD_DEFAULT) {
/* default instance, remove it */
- if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+ if (lyd_validate_autodel_node_del(first, iter, mod, 0, node, NULL, diff)) {
node_autodel = 1;
}
}
@@ -661,7 +695,7 @@ lyd_validate_autodel_cont_leaf_dflt(struct lyd_node **first, struct lyd_node **n
LYD_LIST_FOR_INST(*first, schema, iter) {
if ((iter->flags & LYD_DEFAULT) && !(iter->flags & LYD_NEW)) {
/* old default instance, remove it */
- if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+ if (lyd_validate_autodel_node_del(first, iter, mod, 0, node, NULL, diff)) {
node_autodel = 1;
}
break;
@@ -719,7 +753,7 @@ lyd_validate_autodel_case_dflt(struct lyd_node **first, struct lyd_node **node,
if (!iter) {
/* there are only default nodes of the case meaning it does not exist and neither should any default nodes
* of the case, remove this one default node */
- if (lyd_validate_autodel_node_del(first, *node, mod, node, diff)) {
+ if (lyd_validate_autodel_node_del(first, *node, mod, 0, node, NULL, diff)) {
node_autodel = 1;
}
}
@@ -733,40 +767,46 @@ lyd_validate_autodel_case_dflt(struct lyd_node **first, struct lyd_node **node,
* @param[in,out] first First sibling.
* @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
* @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in] val_opts Validation options.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_choice_r(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
- struct lyd_node **diff)
+ uint32_t val_opts, struct lyd_node **diff)
{
+ LY_ERR r, rc = LY_SUCCESS;
const struct lysc_node *snode = NULL;
while (*first && (snode = lys_getnext(snode, sparent, mod ? mod->compiled : NULL, LYS_GETNEXT_WITHCHOICE))) {
/* check case duplicites */
if (snode->nodetype == LYS_CHOICE) {
- LY_CHECK_RET(lyd_validate_cases(first, mod, (struct lysc_node_choice *)snode, diff));
+ r = lyd_validate_cases(first, mod, (struct lysc_node_choice *)snode, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* check for nested choice */
- LY_CHECK_RET(lyd_validate_choice_r(first, snode, mod, diff));
+ r = lyd_validate_choice_r(first, snode, mod, val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
}
- return LY_SUCCESS;
+cleanup:
+ return rc;
}
LY_ERR
lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
- struct lyd_node **diff)
+ uint32_t val_opts, struct lyd_node **diff)
{
- LY_ERR r;
+ LY_ERR r, rc = LY_SUCCESS;
struct lyd_node *node;
const struct lysc_node *last_dflt_schema = NULL;
assert(first && (sparent || mod));
/* validate choices */
- LY_CHECK_RET(lyd_validate_choice_r(first, sparent, mod, diff));
+ r = lyd_validate_choice_r(first, sparent, mod, val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
node = *first;
while (node) {
@@ -797,10 +837,8 @@ lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const
if (node->flags & LYD_NEW) {
/* then check new node instance duplicities */
- LOG_LOCSET(NULL, node, NULL, NULL);
- r = lyd_validate_duplicates(*first, node);
- LOG_LOCBACK(0, 1, 0, 0);
- LY_CHECK_RET(r);
+ r = lyd_validate_duplicates(*first, node, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* this node is valid */
node->flags &= ~LYD_NEW;
@@ -817,7 +855,8 @@ lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const
node = node->next;
}
- return LY_SUCCESS;
+cleanup:
+ return rc;
}
/**
@@ -833,7 +872,7 @@ static LY_ERR
lyd_validate_dummy_when(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
const struct lysc_when **disabled)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR rc = LY_SUCCESS;
struct lyd_node *tree, *dummy = NULL;
uint32_t xp_opts;
@@ -850,8 +889,8 @@ lyd_validate_dummy_when(const struct lyd_node *first, const struct lyd_node *par
}
/* create dummy opaque node */
- ret = lyd_new_opaq((struct lyd_node *)parent, snode->module->ctx, snode->name, NULL, NULL, snode->module->name, &dummy);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lyd_new_opaq((struct lyd_node *)parent, snode->module->ctx, snode->name, NULL, NULL, snode->module->name, &dummy);
+ LY_CHECK_GOTO(rc, cleanup);
/* connect it if needed */
if (!parent) {
@@ -871,20 +910,20 @@ lyd_validate_dummy_when(const struct lyd_node *first, const struct lyd_node *par
}
/* evaluate all when */
- ret = lyd_validate_node_when(tree, dummy, snode, xp_opts, disabled);
- if (ret == LY_EINCOMPLETE) {
+ rc = lyd_validate_node_when(tree, dummy, snode, xp_opts, disabled);
+ if (rc == LY_EINCOMPLETE) {
/* all other when must be resolved by now */
LOGINT(snode->module->ctx);
- ret = LY_EINT;
+ rc = LY_EINT;
goto cleanup;
- } else if (ret) {
+ } else if (rc) {
/* error */
goto cleanup;
}
cleanup:
lyd_free_tree(dummy);
- return ret;
+ return rc;
}
/**
@@ -893,10 +932,12 @@ cleanup:
* @param[in] first First sibling to search in.
* @param[in] parent Data parent.
* @param[in] snode Schema node to validate.
+ * @param[in] val_opts Validation options.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode)
+lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
+ uint32_t val_opts)
{
const struct lysc_when *disabled;
@@ -921,13 +962,26 @@ lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *pare
}
if (!disabled) {
- /* node instance not found */
- if (snode->nodetype == LYS_CHOICE) {
- LOGVAL_APPTAG(snode->module->ctx, "missing-choice", LY_VCODE_NOMAND_CHOIC, snode->name);
+ if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ LOG_LOCSET(parent ? NULL : snode, parent, NULL, NULL);
+ if (snode->nodetype == LYS_CHOICE) {
+ LOGWRN(snode->module->ctx, "Mandatory choice \"%s\" data do not exist.", snode->name);
+ } else {
+ LOGWRN(snode->module->ctx, "Mandatory node \"%s\" instance does not exist.", snode->name);
+ }
+ LOG_LOCBACK(parent ? 0 : 1, parent ? 1 : 0, 0, 0);
} else {
- LOGVAL(snode->module->ctx, LY_VCODE_NOMAND, snode->name);
+ /* node instance not found */
+ LOG_LOCSET(parent ? NULL : snode, parent, NULL, NULL);
+ if (snode->nodetype == LYS_CHOICE) {
+ LOGVAL_APPTAG(snode->module->ctx, "missing-choice", LY_VCODE_NOMAND_CHOIC, snode->name);
+ } else {
+ LOGVAL(snode->module->ctx, LY_VCODE_NOMAND, snode->name);
+ }
+ LOG_LOCBACK(parent ? 0 : 1, parent ? 1 : 0, 0, 0);
+ return LY_EVALID;
}
- return LY_EVALID;
}
return LY_SUCCESS;
@@ -941,16 +995,16 @@ lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *pare
* @param[in] snode Schema node to validate.
* @param[in] min Minimum number of elements, 0 for no restriction.
* @param[in] max Max number of elements, 0 for no restriction.
+ * @param[in] val_opts Validation options.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
- uint32_t min, uint32_t max)
+ uint32_t min, uint32_t max, uint32_t val_opts)
{
uint32_t count = 0;
struct lyd_node *iter;
const struct lysc_when *disabled;
- ly_bool invalid_instance = 0;
assert(min || max);
@@ -967,8 +1021,6 @@ lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent,
}
if (max && (count > max)) {
/* not satisifed */
- LOG_LOCSET(NULL, iter, NULL, NULL);
- invalid_instance = 1;
break;
}
}
@@ -982,20 +1034,42 @@ lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent,
LY_CHECK_RET(lyd_validate_dummy_when(first, parent, snode, &disabled));
}
- if (!disabled) {
- LOGVAL_APPTAG(snode->module->ctx, "too-few-elements", LY_VCODE_NOMIN, snode->name);
- goto failure;
+ if (disabled) {
+ /* satisfied */
+ min = 0;
}
- } else if (max && (count > max)) {
- LOGVAL_APPTAG(snode->module->ctx, "too-many-elements", LY_VCODE_NOMAX, snode->name);
- goto failure;
+ }
+ if (max && (count <= max)) {
+ /* satisfied */
+ max = 0;
}
+ if (min) {
+ if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ LOG_LOCSET(snode, NULL, NULL, NULL);
+ LOGWRN(snode->module->ctx, "Too few \"%s\" instances.", snode->name);
+ LOG_LOCBACK(1, 0, 0, 0);
+ } else {
+ LOG_LOCSET(snode, NULL, NULL, NULL);
+ LOGVAL_APPTAG(snode->module->ctx, "too-few-elements", LY_VCODE_NOMIN, snode->name);
+ LOG_LOCBACK(1, 0, 0, 0);
+ return LY_EVALID;
+ }
+ } else if (max) {
+ if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ LOG_LOCSET(NULL, iter, NULL, NULL);
+ LOGWRN(snode->module->ctx, "Too many \"%s\" instances.", snode->name);
+ LOG_LOCBACK(0, 1, 0, 0);
+ } else {
+ LOG_LOCSET(NULL, iter, NULL, NULL);
+ LOGVAL_APPTAG(snode->module->ctx, "too-many-elements", LY_VCODE_NOMAX, snode->name);
+ LOG_LOCBACK(0, 1, 0, 0);
+ return LY_EVALID;
+ }
+ }
return LY_SUCCESS;
-
-failure:
- LOG_LOCBACK(0, invalid_instance, 0, 0);
- return LY_EVALID;
}
/**
@@ -1035,11 +1109,17 @@ lyd_val_uniq_find_leaf(const struct lysc_node_leaf *uniq_leaf, const struct lyd_
}
/**
+ * @brief Unique list validation callback argument.
+ */
+struct lyd_val_uniq_arg {
+ LY_ARRAY_COUNT_TYPE action; /**< Action to perform - 0 to compare all uniques, n to compare only n-th unique. */
+ uint32_t val_opts; /**< Validation options. */
+};
+
+/**
* @brief Callback for comparing 2 list unique leaf values.
*
* Implementation of ::lyht_value_equal_cb.
- *
- * @param[in] cb_data 0 to compare all uniques, n to compare only n-th unique.
*/
static ly_bool
lyd_val_uniq_list_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
@@ -1049,13 +1129,14 @@ lyd_val_uniq_list_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *c
struct lyd_node *diter, *first, *second;
struct lyd_value *val1, *val2;
char *path1, *path2, *uniq_str, *ptr;
- LY_ARRAY_COUNT_TYPE u, v, action;
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lyd_val_uniq_arg *arg = cb_data;
+ const uint32_t uniq_err_msg_size = 1024;
assert(val1_p && val2_p);
first = *((struct lyd_node **)val1_p);
second = *((struct lyd_node **)val2_p);
- action = (uintptr_t)cb_data;
assert(first && (first->schema->nodetype == LYS_LIST));
assert(second && (second->schema == first->schema));
@@ -1065,8 +1146,8 @@ lyd_val_uniq_list_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *c
slist = (struct lysc_node_list *)first->schema;
/* compare unique leaves */
- if (action > 0) {
- u = action - 1;
+ if (arg->action > 0) {
+ u = arg->action - 1;
if (u < LY_ARRAY_COUNT(slist->uniques)) {
goto uniquecheck;
}
@@ -1098,13 +1179,12 @@ uniquecheck:
}
}
if (v && (v == LY_ARRAY_COUNT(slist->uniques[u]))) {
- /* all unique leafs are the same in this set, create this nice error */
+ /* all unique leaves are the same in this set, create this nice error */
path1 = lyd_path(first, LYD_PATH_STD, NULL, 0);
path2 = lyd_path(second, LYD_PATH_STD, NULL, 0);
/* use buffer to rebuild the unique string */
-#define UNIQ_BUF_SIZE 1024
- uniq_str = malloc(UNIQ_BUF_SIZE);
+ uniq_str = malloc(uniq_err_msg_size);
uniq_str[0] = '\0';
ptr = uniq_str;
LY_ARRAY_FOR(slist->uniques[u], v) {
@@ -1113,7 +1193,7 @@ uniquecheck:
++ptr;
}
ptr = lysc_path_until((struct lysc_node *)slist->uniques[u][v], &slist->node, LYSC_PATH_LOG,
- ptr, UNIQ_BUF_SIZE - (ptr - uniq_str));
+ ptr, uniq_err_msg_size - (ptr - uniq_str));
if (!ptr) {
/* path will be incomplete, whatever */
break;
@@ -1122,18 +1202,24 @@ uniquecheck:
ptr += strlen(ptr);
}
LOG_LOCSET(NULL, second, NULL, NULL);
- LOGVAL_APPTAG(ctx, "data-not-unique", LY_VCODE_NOUNIQ, uniq_str, path1, path2);
+ if (arg->val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ LOGWRN(ctx, "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\".", uniq_str, path1, path2);
+ } else {
+ LOGVAL_APPTAG(ctx, "data-not-unique", LY_VCODE_NOUNIQ, uniq_str, path1, path2);
+ }
LOG_LOCBACK(0, 1, 0, 0);
free(path1);
free(path2);
free(uniq_str);
-#undef UNIQ_BUF_SIZE
- return 1;
+ if (!(arg->val_opts & LYD_VALIDATE_OPERATIONAL)) {
+ return 1;
+ }
}
- if (action > 0) {
+ if (arg->action > 0) {
/* done */
return 0;
}
@@ -1148,10 +1234,12 @@ uniquecheck:
* @param[in] first First sibling to search in.
* @param[in] snode Schema node to validate.
* @param[in] uniques List unique arrays to validate.
+ * @param[in] val_opts Validation options.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode, const struct lysc_node_leaf ***uniques)
+lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode, const struct lysc_node_leaf ***uniques,
+ uint32_t val_opts)
{
const struct lyd_node *diter;
struct ly_set *set;
@@ -1161,8 +1249,8 @@ lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode,
size_t key_len;
ly_bool dyn;
const void *hash_key;
- void *cb_data;
- struct hash_table **uniqtables = NULL;
+ struct lyd_val_uniq_arg arg, *args = NULL;
+ struct ly_ht **uniqtables = NULL;
struct lyd_value *val;
struct ly_ctx *ctx = snode->module->ctx;
@@ -1179,7 +1267,9 @@ lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode,
if (set->count == 2) {
/* simple comparison */
- if (lyd_val_uniq_list_equal(&set->objs[0], &set->objs[1], 0, (void *)0)) {
+ arg.action = 0;
+ arg.val_opts = val_opts;
+ if (lyd_val_uniq_list_equal(&set->objs[0], &set->objs[1], 0, &arg)) {
/* instance duplication */
ret = LY_EVALID;
goto cleanup;
@@ -1187,12 +1277,14 @@ lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode,
} else if (set->count > 2) {
/* use hashes for comparison */
uniqtables = malloc(LY_ARRAY_COUNT(uniques) * sizeof *uniqtables);
- LY_CHECK_ERR_GOTO(!uniqtables, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ args = malloc(LY_ARRAY_COUNT(uniques) * sizeof *args);
+ LY_CHECK_ERR_GOTO(!uniqtables || !args, LOGMEM(ctx); ret = LY_EMEM, cleanup);
x = LY_ARRAY_COUNT(uniques);
for (v = 0; v < x; v++) {
- cb_data = (void *)(uintptr_t)(v + 1L);
+ args[v].action = v + 1;
+ args[v].val_opts = val_opts;
uniqtables[v] = lyht_new(lyht_get_fixed_size(set->count), sizeof(struct lyd_node *),
- lyd_val_uniq_list_equal, cb_data, 0);
+ lyd_val_uniq_list_equal, &args[v], 0);
LY_CHECK_ERR_GOTO(!uniqtables[v], LOGMEM(ctx); ret = LY_EMEM, cleanup);
}
@@ -1215,7 +1307,7 @@ lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode,
/* get hash key */
hash_key = val->realtype->plugin->print(NULL, val, LY_VALUE_LYB, NULL, &dyn, &key_len);
- hash = dict_hash_multi(hash, hash_key, key_len);
+ hash = lyht_hash_multi(hash, hash_key, key_len);
if (dyn) {
free((void *)hash_key);
}
@@ -1226,7 +1318,7 @@ lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode,
}
/* finish the hash value */
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(hash, NULL, 0);
/* insert into the hashtable */
ret = lyht_insert(uniqtables[u], &set->objs[i], hash, NULL);
@@ -1246,9 +1338,10 @@ cleanup:
/* failed when allocating uniquetables[j], following j are not allocated */
break;
}
- lyht_free(uniqtables[v]);
+ lyht_free(uniqtables[v], NULL);
}
free(uniqtables);
+ free(args);
return ret;
}
@@ -1268,7 +1361,7 @@ static LY_ERR
lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_node *parent,
const struct lysc_node *sparent, const struct lysc_module *mod, uint32_t val_opts, uint32_t int_opts)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
const struct lysc_node *snode = NULL, *scase;
struct lysc_node_list *slist;
struct lysc_node_leaflist *sllist;
@@ -1282,34 +1375,32 @@ lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_no
continue;
}
- LOG_LOCSET(snode, NULL, NULL, NULL);
-
/* check min-elements and max-elements */
if (snode->nodetype == LYS_LIST) {
slist = (struct lysc_node_list *)snode;
if (slist->min || slist->max) {
- ret = lyd_validate_minmax(first, parent, snode, slist->min, slist->max);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_validate_minmax(first, parent, snode, slist->min, slist->max, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
} else if (snode->nodetype == LYS_LEAFLIST) {
sllist = (struct lysc_node_leaflist *)snode;
if (sllist->min || sllist->max) {
- ret = lyd_validate_minmax(first, parent, snode, sllist->min, sllist->max);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_validate_minmax(first, parent, snode, sllist->min, sllist->max, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
} else if (snode->flags & LYS_MAND_TRUE) {
/* check generic mandatory existence */
- ret = lyd_validate_mandatory(first, parent, snode);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_validate_mandatory(first, parent, snode, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
/* check unique */
if (snode->nodetype == LYS_LIST) {
slist = (struct lysc_node_list *)snode;
if (slist->uniques) {
- ret = lyd_validate_unique(first, snode, (const struct lysc_node_leaf ***)slist->uniques);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_validate_unique(first, snode, (const struct lysc_node_leaf ***)slist->uniques, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
}
@@ -1318,21 +1409,16 @@ lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_no
LY_LIST_FOR(lysc_node_child(snode), scase) {
if (lys_getnext_data(NULL, first, NULL, scase, NULL)) {
/* validate only this case */
- ret = lyd_validate_siblings_schema_r(first, parent, scase, mod, val_opts, int_opts);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_validate_siblings_schema_r(first, parent, scase, mod, val_opts, int_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
break;
}
}
}
-
- LOG_LOCBACK(1, 0, 0, 0);
}
- return LY_SUCCESS;
-
-error:
- LOG_LOCBACK(1, 0, 0, 0);
- return ret;
+cleanup:
+ return rc;
}
/**
@@ -1348,7 +1434,9 @@ lyd_validate_obsolete(const struct lyd_node *node)
snode = node->schema;
do {
if (snode->flags & LYS_STATUS_OBSLT) {
+ LOG_LOCSET(NULL, node, NULL, NULL);
LOGWRN(snode->module->ctx, "Obsolete schema node \"%s\" instantiated in data.", snode->name);
+ LOG_LOCBACK(0, 1, 0, 0);
break;
}
@@ -1360,14 +1448,15 @@ lyd_validate_obsolete(const struct lyd_node *node)
* @brief Validate must conditions of a data node.
*
* @param[in] node Node to validate.
+ * @param[in] val_opts Validation options.
* @param[in] int_opts Internal parser options.
* @param[in] xpath_options Additional XPath options to use.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_must(const struct lyd_node *node, uint32_t int_opts, uint32_t xpath_options)
+lyd_validate_must(const struct lyd_node *node, uint32_t val_opts, uint32_t int_opts, uint32_t xpath_options)
{
- LY_ERR ret;
+ LY_ERR r, rc = LY_SUCCESS;
struct lyxp_set xp_set;
struct lysc_must *musts;
const struct lyd_node *tree;
@@ -1403,30 +1492,46 @@ lyd_validate_must(const struct lyd_node *node, uint32_t int_opts, uint32_t xpath
memset(&xp_set, 0, sizeof xp_set);
/* evaluate must */
- ret = lyxp_eval(LYD_CTX(node), musts[u].cond, node->schema->module, LY_VALUE_SCHEMA_RESOLVED,
+ r = lyxp_eval(LYD_CTX(node), musts[u].cond, node->schema->module, LY_VALUE_SCHEMA_RESOLVED,
musts[u].prefixes, node, node, tree, NULL, &xp_set, LYXP_SCHEMA | xpath_options);
- if (ret == LY_EINCOMPLETE) {
- LOGINT_RET(LYD_CTX(node));
- } else if (ret) {
- return ret;
+ if (r == LY_EINCOMPLETE) {
+ LOGERR(LYD_CTX(node), LY_EINCOMPLETE,
+ "Must \"%s\" depends on a node with a when condition, which has not been evaluated.", musts[u].cond->expr);
}
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
/* check the result */
lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
if (!xp_set.val.bln) {
- /* use specific error information */
- emsg = musts[u].emsg;
- eapptag = musts[u].eapptag ? musts[u].eapptag : "must-violation";
- if (emsg) {
- LOGVAL_APPTAG(LYD_CTX(node), eapptag, LYVE_DATA, "%s", emsg);
+ if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ emsg = musts[u].emsg;
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ if (emsg) {
+ LOGWRN(LYD_CTX(node), "%s", emsg);
+ } else {
+ LOGWRN(LYD_CTX(node), "Must condition \"%s\" not satisfied.", musts[u].cond->expr);
+ }
+ LOG_LOCBACK(0, 1, 0, 0);
} else {
- LOGVAL_APPTAG(LYD_CTX(node), eapptag, LY_VCODE_NOMUST, musts[u].cond->expr);
+ /* use specific error information */
+ emsg = musts[u].emsg;
+ eapptag = musts[u].eapptag ? musts[u].eapptag : "must-violation";
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ if (emsg) {
+ LOGVAL_APPTAG(LYD_CTX(node), eapptag, LYVE_DATA, "%s", emsg);
+ } else {
+ LOGVAL_APPTAG(LYD_CTX(node), eapptag, LY_VCODE_NOMUST, musts[u].cond->expr);
+ }
+ LOG_LOCBACK(0, 1, 0, 0);
+ r = LY_EVALID;
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
- return LY_EVALID;
}
}
- return LY_SUCCESS;
+cleanup:
+ return rc;
}
/**
@@ -1445,29 +1550,25 @@ static LY_ERR
lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *sparent,
const struct lys_module *mod, uint32_t val_opts, uint32_t int_opts, uint32_t must_xp_opts)
{
- LY_ERR r;
+ LY_ERR r, rc = LY_SUCCESS;
const char *innode;
- struct lyd_node *next = NULL, *node;
+ struct lyd_node *node;
/* validate all restrictions of nodes themselves */
- LY_LIST_FOR_SAFE(first, next, node) {
+ LY_LIST_FOR(first, node) {
if (node->flags & LYD_EXT) {
/* ext instance data should have already been validated */
continue;
}
- LOG_LOCSET(node->schema, node, NULL, NULL);
-
/* opaque data */
if (!node->schema) {
r = lyd_parse_opaq_error(node);
- LOG_LOCBACK(0, 1, 0, 0);
- return r;
+ goto next_iter;
}
if (!node->parent && mod && (lyd_owner_module(node) != mod)) {
/* all top-level data from this module checked */
- LOG_LOCBACK(1, 1, 0, 0);
break;
}
@@ -1487,43 +1588,47 @@ lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, cons
innode = "notification";
}
if (innode) {
+ LOG_LOCSET(NULL, node, NULL, NULL);
LOGVAL(LYD_CTX(node), LY_VCODE_UNEXPNODE, innode, node->schema->name);
- LOG_LOCBACK(1, 1, 0, 0);
- return LY_EVALID;
+ LOG_LOCBACK(0, 1, 0, 0);
+ r = LY_EVALID;
+ goto next_iter;
}
/* obsolete data */
lyd_validate_obsolete(node);
/* node's musts */
- if ((r = lyd_validate_must(node, int_opts, must_xp_opts))) {
- LOG_LOCBACK(1, 1, 0, 0);
- return r;
+ if ((r = lyd_validate_must(node, val_opts, int_opts, must_xp_opts))) {
+ goto next_iter;
}
/* node value was checked by plugins */
- /* next iter */
- LOG_LOCBACK(1, 1, 0, 0);
+next_iter:
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
/* validate schema-based restrictions */
- LY_CHECK_RET(lyd_validate_siblings_schema_r(first, parent, sparent, mod ? mod->compiled : NULL, val_opts, int_opts));
+ r = lyd_validate_siblings_schema_r(first, parent, sparent, mod ? mod->compiled : NULL, val_opts, int_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
LY_LIST_FOR(first, node) {
- if (!node->parent && mod && (lyd_owner_module(node) != mod)) {
- /* all top-level data from this module checked */
+ if (!node->schema || (!node->parent && mod && (lyd_owner_module(node) != mod))) {
+ /* only opaque data following or all top-level data from this module checked */
break;
}
/* validate all children recursively */
- LY_CHECK_RET(lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, val_opts, int_opts, must_xp_opts));
+ r = lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, val_opts, int_opts, must_xp_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* set default for containers */
lyd_cont_set_dflt(node);
}
- return LY_SUCCESS;
+cleanup:
+ return rc;
}
/**
@@ -1609,18 +1714,20 @@ lyd_validate_node_ext(struct lyd_node *node, struct ly_set *ext_node)
* @param[in,out] meta_types Set for unres metadata types.
* @param[in,out] ext_node Set with nodes with extensions to validate.
* @param[in,out] ext_val Set for parsed extension data to validate.
- * @param[in] impl_opts Implicit options, see @ref implicitoptions.
+ * @param[in] val_opts Validation options.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_set *node_types,
- struct ly_set *meta_types, struct ly_set *ext_node, struct ly_set *ext_val, uint32_t impl_opts,
+ struct ly_set *meta_types, struct ly_set *ext_node, struct ly_set *ext_val, uint32_t val_opts,
struct lyd_node **diff)
{
+ LY_ERR r, rc = LY_SUCCESS;
const struct lyd_meta *meta;
const struct lysc_type *type;
struct lyd_node *node;
+ uint32_t impl_opts;
LYD_TREE_DFS_BEGIN(root, node) {
if (node->flags & LYD_EXT) {
@@ -1637,34 +1744,48 @@ lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_
lyplg_ext_get_storage(meta->annotation, LY_STMT_TYPE, sizeof type, (const void **)&type);
if (type->plugin->validate) {
/* metadata type resolution */
- LY_CHECK_RET(ly_set_add(meta_types, (void *)meta, 1, NULL));
+ r = ly_set_add(meta_types, (void *)meta, 1, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
}
if ((node->schema->nodetype & LYD_NODE_TERM) && ((struct lysc_node_leaf *)node->schema)->type->plugin->validate) {
/* node type resolution */
- LY_CHECK_RET(ly_set_add(node_types, (void *)node, 1, NULL));
+ r = ly_set_add(node_types, (void *)node, 1, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
} else if (node->schema->nodetype & LYD_NODE_INNER) {
/* new node validation, autodelete */
- LY_CHECK_RET(lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, diff));
+ r = lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* add nested defaults */
- LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL, NULL, NULL, impl_opts, diff));
+ impl_opts = 0;
+ if (val_opts & LYD_VALIDATE_NO_STATE) {
+ impl_opts |= LYD_IMPLICIT_NO_STATE;
+ }
+ if (val_opts & LYD_VALIDATE_NO_DEFAULTS) {
+ impl_opts |= LYD_IMPLICIT_NO_DEFAULTS;
+ }
+ r = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL, NULL, NULL, impl_opts, diff);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
if (lysc_has_when(node->schema)) {
/* when evaluation */
- LY_CHECK_RET(ly_set_add(node_when, (void *)node, 1, NULL));
+ r = ly_set_add(node_when, (void *)node, 1, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
/* store for ext instance node validation, if needed */
- LY_CHECK_RET(lyd_validate_node_ext(node, ext_node));
+ r = lyd_validate_node_ext(node, ext_node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
next_node:
LYD_TREE_DFS_END(root, node);
}
- return LY_SUCCESS;
+cleanup:
+ return rc;
}
LY_ERR
@@ -1672,11 +1793,11 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
struct lyd_node *first, *next, **first2, *iter;
const struct lys_module *mod;
struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_node = {0}, ext_val = {0};
- uint32_t i = 0;
+ uint32_t i = 0, impl_opts;
assert(tree && ctx);
assert((node_when_p && node_types_p && meta_types_p && ext_node_p && ext_val_p) ||
@@ -1708,15 +1829,21 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
}
/* validate new top-level nodes of this module, autodelete */
- ret = lyd_validate_new(first2, NULL, mod, diff);
- LY_CHECK_GOTO(ret, cleanup);
+ r = lyd_validate_new(first2, *first2 ? lysc_data_parent((*first2)->schema) : NULL, mod, val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* add all top-level defaults for this module, if going to validate subtree, do not add into unres sets
* (lyd_validate_subtree() adds all the nodes in that case) */
- ret = lyd_new_implicit_r(NULL, first2, NULL, mod, validate_subtree ? NULL : node_when_p,
- validate_subtree ? NULL : node_types_p, validate_subtree ? NULL : ext_node_p,
- (val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, diff);
- LY_CHECK_GOTO(ret, cleanup);
+ impl_opts = 0;
+ if (val_opts & LYD_VALIDATE_NO_STATE) {
+ impl_opts |= LYD_IMPLICIT_NO_STATE;
+ }
+ if (val_opts & LYD_VALIDATE_NO_DEFAULTS) {
+ impl_opts |= LYD_IMPLICIT_NO_DEFAULTS;
+ }
+ r = lyd_new_implicit_r(lyd_parent(*first2), first2, NULL, mod, validate_subtree ? NULL : node_when_p,
+ validate_subtree ? NULL : node_types_p, validate_subtree ? NULL : ext_node_p, impl_opts, diff);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
/* our first module node pointer may no longer be the first */
first = *first2;
@@ -1734,20 +1861,22 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
break;
}
- ret = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p,
- (val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, diff);
- LY_CHECK_GOTO(ret, cleanup);
+ r = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p,
+ val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
}
/* finish incompletely validated terminal values/attributes and when conditions */
- ret = lyd_validate_unres(first2, mod, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p,
+ r = lyd_validate_unres(first2, mod, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p,
ext_node_p, ext_val_p, val_opts, diff);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
- /* perform final validation that assumes the data tree is final */
- ret = lyd_validate_final_r(*first2, NULL, NULL, mod, val_opts, 0, 0);
- LY_CHECK_GOTO(ret, cleanup);
+ if (!(val_opts & LYD_VALIDATE_NOT_FINAL)) {
+ /* perform final validation that assumes the data tree is final */
+ r = lyd_validate_final_r(*first2, NULL, NULL, mod, val_opts, 0, 0);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
+ }
}
cleanup:
@@ -1756,7 +1885,7 @@ cleanup:
ly_set_erase(&meta_types, NULL);
ly_set_erase(&ext_node, free);
ly_set_erase(&ext_val, free);
- return ret;
+ return rc;
}
LIBYANG_API_DEF LY_ERR
@@ -1777,14 +1906,36 @@ lyd_validate_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t val_
LIBYANG_API_DEF LY_ERR
lyd_validate_module(struct lyd_node **tree, const struct lys_module *module, uint32_t val_opts, struct lyd_node **diff)
{
- LY_CHECK_ARG_RET(NULL, tree, *tree || module, LY_EINVAL);
- LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, module ? module->ctx : NULL, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, tree, module, !(val_opts & LYD_VALIDATE_PRESENT), LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, module->ctx, LY_EINVAL);
if (diff) {
*diff = NULL;
}
- return lyd_validate(tree, module, (*tree) ? LYD_CTX(*tree) : module->ctx, val_opts, 1, NULL, NULL, NULL, NULL, NULL,
- diff);
+ return lyd_validate(tree, module, module->ctx, val_opts, 1, NULL, NULL, NULL, NULL, NULL, diff);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_validate_module_final(struct lyd_node *tree, const struct lys_module *module, uint32_t val_opts)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ struct lyd_node *first;
+ const struct lys_module *mod;
+ uint32_t i = 0;
+
+ LY_CHECK_ARG_RET(NULL, module, !(val_opts & (LYD_VALIDATE_PRESENT | LYD_VALIDATE_NOT_FINAL)), LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(LYD_CTX(tree), module->ctx, LY_EINVAL);
+
+ /* module is unchanged but we need to get the first module data node */
+ mod = lyd_mod_next_module(tree, module, module->ctx, &i, &first);
+ assert(mod);
+
+ /* perform final validation that assumes the data tree is final */
+ r = lyd_validate_final_r(first, NULL, NULL, mod, val_opts, 0, 0);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
+
+cleanup:
+ return rc;
}
/**
@@ -1895,14 +2046,12 @@ _lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struc
op_sibling_after = op_subtree->next;
op_parent = lyd_parent(op_subtree);
- lyd_unlink_tree(op_subtree);
+ lyd_unlink(op_subtree);
lyd_insert_node(tree_parent, &tree_sibling, op_subtree, 0);
if (!dep_tree) {
dep_tree = tree_sibling;
}
- LOG_LOCSET(NULL, op_node, NULL, NULL);
-
if (int_opts & LYD_INTOPT_REPLY) {
/* add output children defaults */
rc = lyd_new_implicit_r(op_node, lyd_node_child_p(op_node), NULL, NULL, node_when_p, node_types_p,
@@ -1931,21 +2080,21 @@ _lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struc
/* perform final validation of the operation/notification */
lyd_validate_obsolete(op_node);
- LY_CHECK_GOTO(rc = lyd_validate_must(op_node, int_opts, LYXP_IGNORE_WHEN), cleanup);
+ LY_CHECK_GOTO(rc = lyd_validate_must(op_node, 0, int_opts, LYXP_IGNORE_WHEN), cleanup);
/* final validation of all the descendants */
rc = lyd_validate_final_r(lyd_child(op_node), op_node, op_node->schema, NULL, 0, int_opts, LYXP_IGNORE_WHEN);
LY_CHECK_GOTO(rc, cleanup);
cleanup:
- LOG_LOCBACK(0, 1, 0, 0);
-
/* restore operation tree */
- lyd_unlink_tree(op_subtree);
+ lyd_unlink(op_subtree);
if (op_sibling_before) {
lyd_insert_after_node(op_sibling_before, op_subtree);
+ lyd_insert_hash(op_subtree);
} else if (op_sibling_after) {
lyd_insert_before_node(op_sibling_after, op_subtree);
+ lyd_insert_hash(op_subtree);
} else if (op_parent) {
lyd_insert_node(op_parent, NULL, op_subtree, 0);
}
diff --git a/src/validation.h b/src/validation.h
index c9f5da0..db24ef6 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -69,11 +69,12 @@ LY_ERR lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod,
* @param[in,out] first First sibling.
* @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
* @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in] val_opts Validation options.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
LY_ERR lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
- struct lyd_node **diff);
+ uint32_t val_opts, struct lyd_node **diff);
/**
* @brief Validate data node with an extension instance, if any, by storing it in its unres set.
diff --git a/src/xml.c b/src/xml.c
index 97e6abb..2bdbdf3 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -222,17 +222,29 @@ cleanup:
void
lyxml_ns_rm(struct lyxml_ctx *xmlctx)
{
- for (uint32_t u = xmlctx->ns.count - 1; u + 1 > 0; --u) {
- if (((struct lyxml_ns *)xmlctx->ns.objs[u])->depth != xmlctx->elements.count + 1) {
+ struct lyxml_ns *ns;
+ uint32_t u;
+
+ if (!xmlctx->ns.count) {
+ return;
+ }
+
+ u = xmlctx->ns.count;
+ do {
+ --u;
+ ns = (struct lyxml_ns *)xmlctx->ns.objs[u];
+
+ if (ns->depth != xmlctx->elements.count + 1) {
/* we are done, the namespaces from a single element are supposed to be together */
break;
}
+
/* remove the ns structure */
- free(((struct lyxml_ns *)xmlctx->ns.objs[u])->prefix);
- free(((struct lyxml_ns *)xmlctx->ns.objs[u])->uri);
- free(xmlctx->ns.objs[u]);
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
--xmlctx->ns.count;
- }
+ } while (u);
if (!xmlctx->ns.count) {
/* cleanup the xmlctx's namespaces storage */
@@ -244,9 +256,17 @@ const struct lyxml_ns *
lyxml_ns_get(const struct ly_set *ns_set, const char *prefix, size_t prefix_len)
{
struct lyxml_ns *ns;
+ uint32_t u;
- for (uint32_t u = ns_set->count - 1; u + 1 > 0; --u) {
+ if (!ns_set->count) {
+ return NULL;
+ }
+
+ u = ns_set->count;
+ do {
+ --u;
ns = (struct lyxml_ns *)ns_set->objs[u];
+
if (prefix && prefix_len) {
if (ns->prefix && !ly_strncmp(ns->prefix, prefix, prefix_len)) {
return ns;
@@ -255,7 +275,7 @@ lyxml_ns_get(const struct ly_set *ns_set, const char *prefix, size_t prefix_len)
/* default namespace */
return ns;
}
- }
+ } while (u);
return NULL;
}
@@ -415,12 +435,11 @@ static LY_ERR
lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *length, ly_bool *ws_only, ly_bool *dynamic)
{
const struct ly_ctx *ctx = xmlctx->ctx; /* shortcut */
- const char *in = xmlctx->in->current, *start, *in_aux;
+ const char *in = xmlctx->in->current, *start, *in_aux, *p;
char *buf = NULL;
size_t offset; /* read offset in input buffer */
size_t len; /* length of the output string (write offset in output buffer) */
size_t size = 0; /* size of the output buffer */
- void *p;
uint32_t n;
size_t u;
ly_bool ws = 1;
@@ -467,7 +486,7 @@ lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *
}
offset = 0;
} else {
- p = (void *)&in[offset - 1];
+ p = &in[offset - 1];
/* character reference */
++offset;
if (isdigit(in[offset])) {
@@ -486,7 +505,7 @@ lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *
n = (LY_BASE_HEX * n) + u;
}
} else {
- LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\".", 12, p);
+ LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.12s\".", p);
goto error;
}
@@ -497,7 +516,7 @@ lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *
}
++offset;
if (ly_pututf8(&buf[len], n, &u)) {
- LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).", 12, p, n);
+ LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.12s\" (0x%08x).", p, n);
goto error;
}
len += u;
diff --git a/src/xpath.c b/src/xpath.c
index ab7921e..bc5f695 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -43,8 +43,6 @@
#include "tree_schema_internal.h"
#include "xml.h"
-static LY_ERR set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
- enum lyxp_axis axis, uint32_t *index_p);
static LY_ERR reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth);
static LY_ERR eval_expr_select(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_expr_type etype,
struct lyxp_set *set, uint32_t options);
@@ -123,6 +121,8 @@ lyxp_token2str(enum lyxp_token tok)
return "@";
case LYXP_TOKEN_COMMA:
return ",";
+ case LYXP_TOKEN_DCOLON:
+ return "::";
case LYXP_TOKEN_NAMETEST:
return "NameTest";
case LYXP_TOKEN_NODETYPE:
@@ -147,6 +147,8 @@ lyxp_token2str(enum lyxp_token tok)
return "Operator(Path)";
case LYXP_TOKEN_OPER_RPATH:
return "Operator(Recursive Path)";
+ case LYXP_TOKEN_AXISNAME:
+ return "AxisName";
case LYXP_TOKEN_LITERAL:
return "Literal";
case LYXP_TOKEN_NUMBER:
@@ -217,16 +219,53 @@ str2axis(const char *str, uint32_t str_len)
}
/**
- * @brief Print the whole expression \p exp to debug output.
+ * @brief Append a string to a dynamic string variable.
+ *
+ * @param[in,out] str String to use.
+ * @param[in,out] size String size.
+ * @param[in,out] used String used size excluding terminating zero.
+ * @param[in] format Message format.
+ * @param[in] ... Message format arguments.
+ */
+static void
+print_expr_str(char **str, size_t *size, size_t *used, const char *format, ...)
+{
+ int p;
+ va_list ap;
+
+ va_start(ap, format);
+
+ /* try to append the string */
+ p = vsnprintf(*str ? *str + *used : NULL, *size - *used, format, ap);
+
+ if ((unsigned)p >= *size - *used) {
+ /* realloc */
+ *str = ly_realloc(*str, *size + p + 1);
+ *size += p + 1;
+
+ /* restart ap */
+ va_end(ap);
+ va_start(ap, format);
+
+ /* print */
+ p = vsnprintf(*str + *used, *size - *used, format, ap);
+ }
+
+ *used += p;
+ va_end(ap);
+}
+
+/**
+ * @brief Print the whole expression @p exp to debug output.
*
* @param[in] exp Expression to use.
*/
static void
print_expr_struct_debug(const struct lyxp_expr *exp)
{
-#define MSG_BUFFER_SIZE 128
- char tmp[MSG_BUFFER_SIZE];
+ char *buf = NULL;
uint32_t i, j;
+ size_t size = 0, used = 0;
if (!exp || (ly_ll < LY_LLDBG)) {
return;
@@ -234,18 +273,21 @@ print_expr_struct_debug(const struct lyxp_expr *exp)
LOGDBG(LY_LDGXPATH, "expression \"%s\":", exp->expr);
for (i = 0; i < exp->used; ++i) {
- sprintf(tmp, "\ttoken %s, in expression \"%.*s\"", lyxp_token2str(exp->tokens[i]), exp->tok_len[i],
- &exp->expr[exp->tok_pos[i]]);
+ print_expr_str(&buf, &size, &used, "\ttoken %s, in expression \"%.*s\"",
+ lyxp_token2str(exp->tokens[i]), exp->tok_len[i], &exp->expr[exp->tok_pos[i]]);
+
if (exp->repeat && exp->repeat[i]) {
- sprintf(tmp + strlen(tmp), " (repeat %d", exp->repeat[i][0]);
+ print_expr_str(&buf, &size, &used, " (repeat %d", exp->repeat[i][0]);
for (j = 1; exp->repeat[i][j]; ++j) {
- sprintf(tmp + strlen(tmp), ", %d", exp->repeat[i][j]);
+ print_expr_str(&buf, &size, &used, ", %d", exp->repeat[i][j]);
}
- strcat(tmp, ")");
+ print_expr_str(&buf, &size, &used, ")");
}
- LOGDBG(LY_LDGXPATH, tmp);
+ LOGDBG(LY_LDGXPATH, buf);
+ used = 0;
}
-#undef MSG_BUFFER_SIZE
+
+ free(buf);
}
#ifndef NDEBUG
@@ -284,18 +326,19 @@ print_set_debug(struct lyxp_set *set)
LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ROOT CONFIG", i + 1, item->pos);
break;
case LYXP_NODE_ELEM:
- if ((item->node->schema->nodetype == LYS_LIST) && (lyd_child(item->node)->schema->nodetype == LYS_LEAF)) {
+ if (item->node->schema && (item->node->schema->nodetype == LYS_LIST) &&
+ (lyd_child(item->node)->schema->nodetype == LYS_LEAF)) {
LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s (1st child val: %s)", i + 1, item->pos,
item->node->schema->name, lyd_get_value(lyd_child(item->node)));
- } else if (item->node->schema->nodetype == LYS_LEAFLIST) {
+ } else if ((!item->node->schema && !lyd_child(item->node)) || (item->node->schema->nodetype == LYS_LEAFLIST)) {
LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s (val: %s)", i + 1, item->pos,
- item->node->schema->name, lyd_get_value(item->node));
+ LYD_NAME(item->node), lyd_get_value(item->node));
} else {
- LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s", i + 1, item->pos, item->node->schema->name);
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s", i + 1, item->pos, LYD_NAME(item->node));
}
break;
case LYXP_NODE_TEXT:
- if (item->node->schema->nodetype & LYS_ANYDATA) {
+ if (item->node->schema && (item->node->schema->nodetype & LYS_ANYDATA)) {
LOGDBG(LY_LDGXPATH, "\t%d (pos %u): TEXT <%s>", i + 1, item->pos,
item->node->schema->nodetype == LYS_ANYXML ? "anyxml" : "anydata");
} else {
@@ -416,13 +459,14 @@ cast_string_recursive(const struct lyd_node *node, struct lyxp_set *set, uint32_
{
char *buf, *line, *ptr = NULL;
const char *value_str;
+ uint16_t nodetype;
const struct lyd_node *child;
enum lyxp_node_type child_type;
struct lyd_node *tree;
struct lyd_node_any *any;
LY_ERR rc;
- if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && node && (node->schema->flags & LYS_CONFIG_R)) {
+ if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && node && node->schema && (node->schema->flags & LYS_CONFIG_R)) {
return LY_SUCCESS;
}
@@ -448,7 +492,15 @@ cast_string_recursive(const struct lyd_node *node, struct lyxp_set *set, uint32_
--indent;
} else {
- switch (node->schema->nodetype) {
+ if (node->schema) {
+ nodetype = node->schema->nodetype;
+ } else if (lyd_child(node)) {
+ nodetype = LYS_CONTAINER;
+ } else {
+ nodetype = LYS_LEAF;
+ }
+
+ switch (nodetype) {
case LYS_CONTAINER:
case LYS_LIST:
case LYS_RPC:
@@ -691,29 +743,29 @@ set_insert_node_hash(struct lyxp_set *set, struct lyd_node *node, enum lyxp_node
hnode.node = set->val.nodes[i].node;
hnode.type = set->val.nodes[i].type;
- hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
- hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
r = lyht_insert(set->ht, &hnode, hash, NULL);
assert(!r);
(void)r;
- if (hnode.node == node) {
+ if ((hnode.node == node) && (hnode.type == type)) {
/* it was just added, do not add it twice */
- node = NULL;
+ return;
}
}
}
- if (set->ht && node) {
+ if (set->ht) {
/* add the new node into hash table */
hnode.node = node;
hnode.type = type;
- hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
- hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
r = lyht_insert(set->ht, &hnode, hash, NULL);
assert(!r);
@@ -739,16 +791,16 @@ set_remove_node_hash(struct lyxp_set *set, struct lyd_node *node, enum lyxp_node
hnode.node = node;
hnode.type = type;
- hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
- hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
r = lyht_remove(set->ht, &hnode, hash);
assert(!r);
(void)r;
if (!set->ht->used) {
- lyht_free(set->ht);
+ lyht_free(set->ht, NULL);
set->ht = NULL;
}
}
@@ -772,9 +824,9 @@ set_dup_node_hash_check(const struct lyxp_set *set, struct lyd_node *node, enum
hnode.node = node;
hnode.type = type;
- hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
- hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
if (!lyht_find(set->ht, &hnode, hash, (void **)&match_p)) {
if ((skip_idx > -1) && (set->val.nodes[skip_idx].node == match_p->node) && (set->val.nodes[skip_idx].type == match_p->type)) {
@@ -802,10 +854,10 @@ lyxp_set_free_content(struct lyxp_set *set)
if (set->type == LYXP_SET_NODE_SET) {
free(set->val.nodes);
- lyht_free(set->ht);
+ lyht_free(set->ht, NULL);
} else if (set->type == LYXP_SET_SCNODE_SET) {
free(set->val.scnodes);
- lyht_free(set->ht);
+ lyht_free(set->ht, NULL);
} else {
if (set->type == LYXP_SET_STRING) {
free(set->val.str);
@@ -847,18 +899,21 @@ static void
set_init(struct lyxp_set *new, const struct lyxp_set *set)
{
memset(new, 0, sizeof *new);
- if (set) {
- new->non_child_axis = set->non_child_axis;
- new->ctx = set->ctx;
- new->cur_node = set->cur_node;
- new->root_type = set->root_type;
- new->context_op = set->context_op;
- new->tree = set->tree;
- new->cur_mod = set->cur_mod;
- new->format = set->format;
- new->prefix_data = set->prefix_data;
- new->vars = set->vars;
+ if (!set) {
+ return;
}
+
+ new->non_child_axis = set->non_child_axis;
+ new->not_found = set->not_found;
+ new->ctx = set->ctx;
+ new->cur_node = set->cur_node;
+ new->root_type = set->root_type;
+ new->context_op = set->context_op;
+ new->tree = set->tree;
+ new->cur_mod = set->cur_mod;
+ new->format = set->format;
+ new->prefix_data = set->prefix_data;
+ new->vars = set->vars;
}
/**
@@ -889,7 +944,7 @@ set_copy(struct lyxp_set *set)
(set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_START)) {
uint32_t idx;
- LY_CHECK_ERR_RET(set_scnode_insert_node(ret, set->val.scnodes[i].scnode, set->val.scnodes[i].type,
+ LY_CHECK_ERR_RET(lyxp_set_scnode_insert_node(ret, set->val.scnodes[i].scnode, set->val.scnodes[i].type,
set->val.scnodes[i].axis, &idx), lyxp_set_free(ret), NULL);
/* coverity seems to think scnodes can be NULL */
if (!ret->val.scnodes) {
@@ -989,11 +1044,7 @@ set_fill_set(struct lyxp_set *trg, const struct lyxp_set *src)
return;
}
- if (trg->type == LYXP_SET_NODE_SET) {
- free(trg->val.nodes);
- } else if (trg->type == LYXP_SET_STRING) {
- free(trg->val.str);
- }
+ lyxp_set_free_content(trg);
set_init(trg, src);
if (src->type == LYXP_SET_SCNODE_SET) {
@@ -1314,19 +1365,8 @@ set_insert_node(struct lyxp_set *set, const struct lyd_node *node, uint32_t pos,
set_insert_node_hash(set, (struct lyd_node *)node, node_type);
}
-/**
- * @brief Insert schema node into set.
- *
- * @param[in] set Set to insert into.
- * @param[in] node Node to insert.
- * @param[in] node_type Node type of @p node.
- * @param[in] axis Axis that @p node was reached on.
- * @param[out] index_p Optional pointer to store index if the inserted @p node.
- * @return LY_SUCCESS on success.
- * @return LY_EMEM on memory allocation failure.
- */
-static LY_ERR
-set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
+LY_ERR
+lyxp_set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
enum lyxp_axis axis, uint32_t *index_p)
{
uint32_t index;
@@ -1346,7 +1386,7 @@ set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum
}
if (lyxp_set_scnode_contains(set, node, node_type, -1, &index)) {
- /* BUG if axes differs, this new one is thrown away */
+ /* BUG if axes differ, this new one is thrown away */
set->val.scnodes[index].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
} else {
if (set->used == set->size) {
@@ -1445,7 +1485,7 @@ dfs_search:
LYD_TREE_DFS_continue = 0;
}
- if (!elem->schema || ((root_type == LYXP_NODE_ROOT_CONFIG) && (elem->schema->flags & LYS_CONFIG_R))) {
+ if ((root_type == LYXP_NODE_ROOT_CONFIG) && elem->schema && (elem->schema->flags & LYS_CONFIG_R)) {
/* skip */
LYD_TREE_DFS_continue = 1;
} else {
@@ -1662,7 +1702,7 @@ set_comp_canonize(struct lyxp_set *set, const struct lyxp_set_node *xp_node)
/* is there anything to canonize even? */
if (set->type == LYXP_SET_STRING) {
/* do we have a type to use for canonization? */
- if ((xp_node->type == LYXP_NODE_ELEM) && (xp_node->node->schema->nodetype & LYD_NODE_TERM)) {
+ if ((xp_node->type == LYXP_NODE_ELEM) && xp_node->node->schema && (xp_node->node->schema->nodetype & LYD_NODE_TERM)) {
type = ((struct lyd_node_term *)xp_node->node)->value.realtype;
} else if (xp_node->type == LYXP_NODE_META) {
type = ((struct lyd_meta *)xp_node->node)->value.realtype;
@@ -1785,9 +1825,9 @@ set_sort(struct lyxp_set *set)
hnode.node = set->val.nodes[i].node;
hnode.type = set->val.nodes[i].type;
- hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
- hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
assert(!lyht_find(set->ht, &hnode, hash, NULL));
}
@@ -1993,11 +2033,11 @@ lyxp_next_token2(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t
* @brief Stack operation push on the repeat array.
*
* @param[in] exp Expression to use.
- * @param[in] tok_idx Position in the expresion \p exp.
- * @param[in] repeat_op_idx Index from \p exp of the operator token. This value is pushed.
+ * @param[in] tok_idx Position in the expresion @p exp.
+ * @param[in] repeat_expr_type Repeated expression type, this value is pushed.
*/
static void
-exp_repeat_push(struct lyxp_expr *exp, uint32_t tok_idx, uint32_t repeat_op_idx)
+exp_repeat_push(struct lyxp_expr *exp, uint32_t tok_idx, enum lyxp_expr_type repeat_expr_type)
{
uint32_t i;
@@ -2005,12 +2045,12 @@ exp_repeat_push(struct lyxp_expr *exp, uint32_t tok_idx, uint32_t repeat_op_idx)
for (i = 0; exp->repeat[tok_idx][i]; ++i) {}
exp->repeat[tok_idx] = realloc(exp->repeat[tok_idx], (i + 2) * sizeof *exp->repeat[tok_idx]);
LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
- exp->repeat[tok_idx][i] = repeat_op_idx;
+ exp->repeat[tok_idx][i] = repeat_expr_type;
exp->repeat[tok_idx][i + 1] = 0;
} else {
exp->repeat[tok_idx] = calloc(2, sizeof *exp->repeat[tok_idx]);
LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
- exp->repeat[tok_idx][0] = repeat_op_idx;
+ exp->repeat[tok_idx][0] = repeat_expr_type;
}
}
@@ -2994,7 +3034,7 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
parsed++;
ncname_len = parse_ncname(&expr_str[parsed]);
LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
- parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ (uint32_t)(parsed - ncname_len + 1), expr_str); ret = LY_EVALID, error);
tok_len = ncname_len;
LY_CHECK_ERR_GOTO(expr_str[parsed + tok_len] == ':',
LOGVAL(ctx, LYVE_XPATH, "Variable with prefix is not supported."); ret = LY_EVALID,
@@ -3084,7 +3124,7 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
ret = LY_EVALID;
goto error;
} else {
- LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], parsed + 1, expr_str);
+ LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], (uint32_t)(parsed + 1), expr_str);
ret = LY_EVALID;
goto error;
}
@@ -3096,7 +3136,7 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
} else {
ncname_len = parse_ncname(&expr_str[parsed]);
LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
- parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ (uint32_t)(parsed - ncname_len + 1), expr_str); ret = LY_EVALID, error);
}
tok_len = ncname_len;
@@ -3104,7 +3144,8 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
if (!strncmp(&expr_str[parsed + tok_len], "::", 2)) {
/* axis */
LY_CHECK_ERR_GOTO(expr_parse_axis(&expr_str[parsed], ncname_len),
- LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], parsed + 1, expr_str); ret = LY_EVALID, error);
+ LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], (uint32_t)(parsed + 1), expr_str); ret = LY_EVALID,
+ error);
tok_type = LYXP_TOKEN_AXISNAME;
LY_CHECK_GOTO(ret = exp_add_token(ctx, expr, tok_type, parsed, tok_len), error);
@@ -3122,7 +3163,7 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
} else {
ncname_len = parse_ncname(&expr_str[parsed]);
LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
- parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ (uint32_t)(parsed - ncname_len + 1), expr_str); ret = LY_EVALID, error);
}
tok_len = ncname_len;
@@ -3136,7 +3177,7 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
} else {
ncname_len = parse_ncname(&expr_str[parsed + tok_len]);
LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
- parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ (uint32_t)(parsed - ncname_len + 1), expr_str); ret = LY_EVALID, error);
tok_len += ncname_len;
}
/* remove old flags to prevent ambiguities */
@@ -3934,10 +3975,10 @@ xpath_current(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set,
set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
if (set->cur_scnode) {
- LY_CHECK_RET(set_scnode_insert_node(set, set->cur_scnode, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, set->cur_scnode, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
} else {
/* root node */
- LY_CHECK_RET(set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
}
} else {
lyxp_set_free_content(set);
@@ -4003,7 +4044,7 @@ xpath_deref(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set
target = p[LY_ARRAY_COUNT(p) - 1].node;
ly_path_free(set->ctx, p);
- LY_CHECK_RET(set_scnode_insert_node(set, target, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, target, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
} /* else the target was found before but is disabled so it was removed */
}
@@ -4030,7 +4071,7 @@ xpath_deref(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set
}
} else {
assert(sleaf->type->basetype == LY_TYPE_INST);
- if (ly_path_eval(leaf->value.target, set->tree, &node)) {
+ if (ly_path_eval(leaf->value.target, set->tree, NULL, &node)) {
LOGERR(set->ctx, LY_EVALID, "Invalid instance-identifier \"%s\" value - required instance not found.",
lyd_get_value(&leaf->node));
return LY_EVALID;
@@ -4274,9 +4315,25 @@ xpath_false(struct lyxp_set **UNUSED(args), uint32_t UNUSED(arg_count), struct l
* @return LY_ERR
*/
static LY_ERR
-xpath_floor(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t UNUSED(options))
+xpath_floor(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (args[0]->type != LYXP_SET_SCNODE_SET) {
+ LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
+ } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_DEC64)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"decimal64\".", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
LY_CHECK_RET(rc);
@@ -4474,7 +4531,7 @@ xpath_local_name(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *se
set_fill_string(set, "", 0);
break;
case LYXP_NODE_ELEM:
- set_fill_string(set, item->node->schema->name, strlen(item->node->schema->name));
+ set_fill_string(set, LYD_NAME(item->node), strlen(LYD_NAME(item->node)));
break;
case LYXP_NODE_META:
set_fill_string(set, ((struct lyd_meta *)item->node)->name, strlen(((struct lyd_meta *)item->node)->name));
@@ -4498,7 +4555,7 @@ static LY_ERR
xpath_name(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
{
struct lyxp_set_node *item;
- struct lys_module *mod = NULL;
+ const struct lys_module *mod = NULL;
char *str;
const char *name = NULL;
@@ -4544,8 +4601,8 @@ xpath_name(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uin
/* keep NULL */
break;
case LYXP_NODE_ELEM:
- mod = item->node->schema->module;
- name = item->node->schema->name;
+ mod = lyd_node_module(item->node);
+ name = LYD_NAME(item->node);
break;
case LYXP_NODE_META:
mod = ((struct lyd_meta *)item->node)->annotation->module;
@@ -4580,7 +4637,7 @@ static LY_ERR
xpath_namespace_uri(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
{
struct lyxp_set_node *item;
- struct lys_module *mod;
+ const struct lys_module *mod;
/* suppress unused variable warning */
(void)options;
@@ -4630,7 +4687,7 @@ xpath_namespace_uri(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set
case LYXP_NODE_ELEM:
case LYXP_NODE_META:
if (item->type == LYXP_NODE_ELEM) {
- mod = item->node->schema->module;
+ mod = lyd_node_module(item->node);
} else { /* LYXP_NODE_META */
/* annotations */
mod = ((struct lyd_meta *)item->node)->annotation->module;
@@ -5541,7 +5598,7 @@ xpath_pi_text(struct lyxp_set *set, enum lyxp_axis axis, uint32_t options)
case LYXP_NODE_NONE:
LOGINT_RET(set->ctx);
case LYXP_NODE_ELEM:
- if (set->val.nodes[i].node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+ if (!set->val.nodes[i].node->schema || (set->val.nodes[i].node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
set->val.nodes[i].type = LYXP_NODE_TEXT;
break;
}
@@ -5588,7 +5645,7 @@ moveto_resolve_model(const char **qname, uint32_t *qname_len, const struct lyxp_
/* check for errors and non-implemented modules, as they are not valid */
if (!mod || !mod->implemented) {
- LOGVAL(set->ctx, LY_VCODE_XP_INMOD, pref_len, *qname);
+ LOGVAL(set->ctx, LY_VCODE_XP_INMOD, (int)pref_len, *qname);
return LY_EVALID;
}
@@ -5641,10 +5698,9 @@ moveto_root(struct lyxp_set *set, uint32_t options)
if (options & LYXP_SCNODE_ALL) {
set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
- LY_CHECK_RET(set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
} else {
- set->type = LYXP_SET_NODE_SET;
- set->used = 0;
+ lyxp_set_free_content(set);
set_insert_node(set, NULL, 0, set->root_type, 0);
set->non_child_axis = 0;
}
@@ -5668,6 +5724,8 @@ static LY_ERR
moveto_node_check(const struct lyd_node *node, enum lyxp_node_type node_type, const struct lyxp_set *set,
const char *node_name, const struct lys_module *moveto_mod, uint32_t options)
{
+ const struct lysc_node *schema;
+
if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT)) {
assert(node_type == set->root_type);
@@ -5681,39 +5739,40 @@ moveto_node_check(const struct lyd_node *node, enum lyxp_node_type node_type, co
return LY_ENOT;
}
- if (!node->schema) {
- /* opaque node never matches */
+ /* get schema node even of an opaque node */
+ schema = lyd_node_schema(node);
+ if (!schema) {
+ /* unknown opaque node never matches */
return LY_ENOT;
}
/* module check */
if (moveto_mod) {
- if ((set->ctx == LYD_CTX(node)) && (node->schema->module != moveto_mod)) {
+ if ((set->ctx == LYD_CTX(node)) && (schema->module != moveto_mod)) {
return LY_ENOT;
- } else if ((set->ctx != LYD_CTX(node)) && strcmp(node->schema->module->name, moveto_mod->name)) {
+ } else if ((set->ctx != LYD_CTX(node)) && strcmp(schema->module->name, moveto_mod->name)) {
return LY_ENOT;
}
}
/* context check */
- if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (node->schema->flags & LYS_CONFIG_R)) {
+ if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (schema->flags & LYS_CONFIG_R)) {
return LY_EINVAL;
- } else if (set->context_op && (node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) &&
- (node->schema != set->context_op)) {
+ } else if (set->context_op && (schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && (schema != set->context_op)) {
return LY_EINVAL;
}
/* name check */
if (node_name) {
- if ((set->ctx == LYD_CTX(node)) && (node->schema->name != node_name)) {
+ if ((set->ctx == LYD_CTX(node)) && (schema->name != node_name)) {
return LY_ENOT;
- } else if ((set->ctx != LYD_CTX(node)) && strcmp(node->schema->name, node_name)) {
+ } else if ((set->ctx != LYD_CTX(node)) && strcmp(schema->name, node_name)) {
return LY_ENOT;
}
}
/* when check, accept the context node because it should only be the path ".", we have checked the when is valid before */
- if (!(options & LYXP_IGNORE_WHEN) && lysc_has_when(node->schema) && !(node->flags & LYD_WHEN_TRUE) &&
+ if (!(options & LYXP_IGNORE_WHEN) && lysc_has_when(schema) && !(node->flags & LYD_WHEN_TRUE) &&
(node != set->cur_node)) {
return LY_EINCOMPLETE;
}
@@ -6144,7 +6203,7 @@ moveto_node_hash_child(struct lyxp_set *set, const struct lysc_node *scnode, con
/* create specific data instance if needed */
if (scnode->nodetype == LYS_LIST) {
- LY_CHECK_GOTO(ret = lyd_create_list(scnode, predicates, &inst), cleanup);
+ LY_CHECK_GOTO(ret = lyd_create_list(scnode, predicates, NULL, &inst), cleanup);
} else if (scnode->nodetype == LYS_LEAFLIST) {
LY_CHECK_GOTO(ret = lyd_create_term2(scnode, &predicates[0].value, &inst), cleanup);
}
@@ -6168,6 +6227,10 @@ moveto_node_hash_child(struct lyxp_set *set, const struct lysc_node *scnode, con
} else {
r = lyd_find_sibling_val(siblings, scnode, NULL, 0, &sub);
}
+ if (r == LY_ENOTFOUND) {
+ /* may still be an opaque node */
+ r = lyd_find_sibling_opaq_next(siblings, scnode->name, &sub);
+ }
LY_CHECK_ERR_GOTO(r && (r != LY_ENOTFOUND), ret = r, cleanup);
/* when check */
@@ -6655,7 +6718,7 @@ moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const c
{
ly_bool temp_ctx = 0;
uint32_t getnext_opts, orig_used, i, mod_idx, idx;
- const struct lys_module *mod;
+ const struct lys_module *mod = NULL;
const struct lysc_node *iter;
enum lyxp_node_type iter_type;
@@ -6673,6 +6736,9 @@ moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const c
if (options & LYXP_SCNODE_OUTPUT) {
getnext_opts |= LYS_GETNEXT_OUTPUT;
}
+ if (options & LYXP_SCNODE_SCHEMAMOUNT) {
+ getnext_opts |= LYS_GETNEXT_WITHSCHEMAMOUNT;
+ }
orig_used = set->used;
for (i = 0; i < orig_used; ++i) {
@@ -6691,7 +6757,7 @@ moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const c
}
/* insert */
- LY_CHECK_RET(set_scnode_insert_node(set, iter, iter_type, axis, &idx));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, iter, iter_type, axis, &idx));
/* we need to prevent these nodes from being considered in this moveto */
if ((idx < orig_used) && (idx > i)) {
@@ -6704,7 +6770,7 @@ moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const c
(set->val.scnodes[i].type == LYXP_NODE_ELEM) && !ly_nested_ext_schema(NULL, set->val.scnodes[i].scnode,
moveto_mod->name, strlen(moveto_mod->name), LY_VALUE_JSON, NULL, ncname, strlen(ncname), &iter, NULL)) {
/* there is a matching node from an extension, use it */
- LY_CHECK_RET(set_scnode_insert_node(set, iter, LYXP_NODE_ELEM, axis, &idx));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM, axis, &idx));
if ((idx < orig_used) && (idx > i)) {
set->val.scnodes[idx].in_ctx = LYXP_SET_SCNODE_ATOM_NEW_CTX;
temp_ctx = 1;
@@ -6848,7 +6914,7 @@ moveto_scnode_dfs(struct lyxp_set *set, const struct lysc_node *start, uint32_t
goto skip_children;
}
} else {
- LY_CHECK_RET(set_scnode_insert_node(set, elem, LYXP_NODE_ELEM, LYXP_AXIS_DESCENDANT, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, elem, LYXP_NODE_ELEM, LYXP_AXIS_DESCENDANT, NULL));
}
} else if (rc == LY_EINVAL) {
goto skip_children;
@@ -7459,7 +7525,11 @@ only_parse:
*tok_idx = orig_exp;
rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
- if (rc != LY_SUCCESS) {
+ if (!rc && set2.not_found) {
+ set->not_found = 1;
+ break;
+ }
+ if (rc) {
lyxp_set_free_content(&set2);
return rc;
}
@@ -7508,6 +7578,9 @@ only_parse:
*tok_idx = orig_exp;
rc = eval_expr_select(exp, tok_idx, 0, set, options);
+ if (!rc && set->not_found) {
+ break;
+ }
LY_CHECK_RET(rc);
set->val.scnodes[i].in_ctx = pred_in_ctx;
@@ -7527,7 +7600,7 @@ only_parse:
set_fill_set(&set2, set);
rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
- if (rc != LY_SUCCESS) {
+ if (rc) {
lyxp_set_free_content(&set2);
return rc;
}
@@ -7702,14 +7775,13 @@ cleanup:
* @param[in] ctx_scnode Found schema node as the context for the predicate.
* @param[in] set Context set.
* @param[out] predicates Parsed predicates.
- * @param[out] pred_type Type of @p predicates.
* @return LY_SUCCESS on success,
* @return LY_ENOT if a predicate could not be compiled.
* @return LY_ERR on any error.
*/
static LY_ERR
eval_name_test_try_compile_predicates(const struct lyxp_expr *exp, uint32_t *tok_idx, const struct lysc_node *ctx_scnode,
- const struct lyxp_set *set, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
+ const struct lyxp_set *set, struct ly_path_predicate **predicates)
{
LY_ERR rc = LY_SUCCESS;
uint32_t e_idx, val_start_idx, pred_idx = 0, temp_lo = 0, pred_len = 0, nested_pred;
@@ -7848,7 +7920,7 @@ eval_name_test_try_compile_predicates(const struct lyxp_expr *exp, uint32_t *tok
/* compile */
rc = ly_path_compile_predicate(set->ctx, set->cur_node ? set->cur_node->schema : NULL, set->cur_mod, ctx_scnode, exp2,
- &pred_idx, LY_VALUE_JSON, NULL, predicates, pred_type);
+ &pred_idx, LY_VALUE_JSON, NULL, predicates);
LY_CHECK_GOTO(rc, cleanup);
/* success, the predicate must include all the needed information for hash-based search */
@@ -7881,7 +7953,7 @@ eval_name_test_with_predicate_get_scnode(const struct ly_ctx *ctx, const struct
uint32_t name_len, const struct lys_module *moveto_mod, enum lyxp_node_type root_type, LY_VALUE_FORMAT format,
const struct lysc_node **found)
{
- const struct lysc_node *scnode;
+ const struct lysc_node *scnode, *scnode2;
const struct lys_module *mod;
uint32_t idx = 0;
@@ -7919,7 +7991,19 @@ continue_search:
}
/* search in children, do not repeat the same search */
- scnode = lys_find_child(node->schema, moveto_mod, name, name_len, 0, 0);
+ if (node->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
+ /* make sure the node is unique, whether in input or output */
+ scnode = lys_find_child(node->schema, moveto_mod, name, name_len, 0, 0);
+ scnode2 = lys_find_child(node->schema, moveto_mod, name, name_len, 0, LYS_GETNEXT_OUTPUT);
+ if (scnode && scnode2) {
+ /* conflict, do not use hashes */
+ scnode = NULL;
+ } else if (scnode2) {
+ scnode = scnode2;
+ }
+ } else {
+ scnode = lys_find_child(node->schema, moveto_mod, name, name_len, 0, 0);
+ }
} /* else skip redundant search */
/* additional context check */
@@ -7977,14 +8061,14 @@ eval_name_test_scnode_no_match_msg(struct lyxp_set *set, const struct lyxp_set_s
if (ppath) {
format = "Schema node \"%.*s\" for parent \"%s\" not found; in expr \"%.*s\" with context node \"%s\".";
if (options & LYXP_SCNODE_ERROR) {
- LOGERR(set->ctx, LY_EVALID, format, ncname_len, ncname, ppath, (ncname - expr) + ncname_len, expr, path);
+ LOGERR(set->ctx, LY_ENOTFOUND, format, ncname_len, ncname, ppath, (ncname - expr) + ncname_len, expr, path);
} else {
LOGWRN(set->ctx, format, ncname_len, ncname, ppath, (ncname - expr) + ncname_len, expr, path);
}
} else {
format = "Schema node \"%.*s\" not found; in expr \"%.*s\" with context node \"%s\".";
if (options & LYXP_SCNODE_ERROR) {
- LOGERR(set->ctx, LY_EVALID, format, ncname_len, ncname, (ncname - expr) + ncname_len, expr, path);
+ LOGERR(set->ctx, LY_ENOTFOUND, format, ncname_len, ncname, (ncname - expr) + ncname_len, expr, path);
} else {
LOGWRN(set->ctx, format, ncname_len, ncname, (ncname - expr) + ncname_len, expr, path);
}
@@ -8018,7 +8102,6 @@ eval_name_test_with_predicate(const struct lyxp_expr *exp, uint32_t *tok_idx, en
const struct lys_module *moveto_mod = NULL;
const struct lysc_node *scnode = NULL;
struct ly_path_predicate *predicates = NULL;
- enum ly_path_pred_type pred_type = 0;
int scnode_skip_pred = 0;
LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
@@ -8060,7 +8143,7 @@ eval_name_test_with_predicate(const struct lyxp_expr *exp, uint32_t *tok_idx, en
if (scnode && (scnode->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
/* try to create the predicates */
- if (eval_name_test_try_compile_predicates(exp, tok_idx, scnode, set, &predicates, &pred_type)) {
+ if (eval_name_test_try_compile_predicates(exp, tok_idx, scnode, set, &predicates)) {
/* hashes cannot be used */
scnode = NULL;
}
@@ -8132,8 +8215,7 @@ moveto:
if (options & LYXP_SCNODE_ERROR) {
/* error */
- rc = LY_EVALID;
- goto cleanup;
+ set->not_found = 1;
}
/* skip the predicates and the rest of this path to not generate invalid warnings */
@@ -8175,10 +8257,8 @@ cleanup:
/* restore options */
options &= ~LYXP_SKIP_EXPR;
}
- if (!(options & LYXP_SKIP_EXPR)) {
- lydict_remove(set->ctx, ncname_dict);
- ly_path_predicates_free(set->ctx, pred_type, predicates);
- }
+ lydict_remove(set->ctx, ncname_dict);
+ ly_path_predicates_free(set->ctx, predicates);
return rc;
}
@@ -8350,8 +8430,9 @@ step:
rc = eval_name_test_with_predicate(exp, tok_idx, axis, all_desc, set, options);
if (rc == LY_ENOT) {
assert(options & LYXP_SCNODE_ALL);
- /* skip the rest of this path */
rc = LY_SUCCESS;
+
+ /* skip the rest of this path */
scnode_skip_path = 1;
options |= LYXP_SKIP_EXPR;
}
@@ -8600,8 +8681,9 @@ eval_function_call(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_s
rc = eval_expr_select(exp, tok_idx, 0, args[0], options);
LY_CHECK_GOTO(rc, cleanup);
+ set->not_found = args[0]->not_found;
} else {
- rc = eval_expr_select(exp, tok_idx, 0, set, options | LYXP_SKIP_EXPR);
+ rc = eval_expr_select(exp, tok_idx, 0, set, options);
LY_CHECK_GOTO(rc, cleanup);
}
}
@@ -8623,8 +8705,11 @@ eval_function_call(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_s
rc = eval_expr_select(exp, tok_idx, 0, args[arg_count - 1], options);
LY_CHECK_GOTO(rc, cleanup);
+ if (args[arg_count - 1]->not_found) {
+ set->not_found = 1;
+ }
} else {
- rc = eval_expr_select(exp, tok_idx, 0, set, options | LYXP_SKIP_EXPR);
+ rc = eval_expr_select(exp, tok_idx, 0, set, options);
LY_CHECK_GOTO(rc, cleanup);
}
}
@@ -8698,27 +8783,30 @@ eval_number(struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t *tok_idx,
}
LY_ERR
-lyxp_vars_find(struct lyxp_var *vars, const char *name, size_t name_len, struct lyxp_var **var)
+lyxp_vars_find(const struct ly_ctx *ctx, const struct lyxp_var *vars, const char *name, size_t name_len,
+ struct lyxp_var **var)
{
- LY_ERR ret = LY_ENOTFOUND;
LY_ARRAY_COUNT_TYPE u;
- assert(vars && name);
+ assert(name);
- name_len = name_len ? name_len : strlen(name);
+ if (!name_len) {
+ name_len = strlen(name);
+ }
LY_ARRAY_FOR(vars, u) {
if (!strncmp(vars[u].name, name, name_len)) {
- ret = LY_SUCCESS;
- break;
+ if (var) {
+ *var = (struct lyxp_var *)&vars[u];
+ }
+ return LY_SUCCESS;
}
}
- if (var && !ret) {
- *var = &vars[u];
+ if (ctx) {
+ LOGERR(ctx, LY_ENOTFOUND, "Variable \"%.*s\" not defined.", (int)name_len, name);
}
-
- return ret;
+ return LY_ENOTFOUND;
}
/**
@@ -8737,17 +8825,14 @@ eval_variable_reference(const struct lyxp_expr *exp, uint32_t *tok_idx, struct l
LY_ERR ret;
const char *name;
struct lyxp_var *var;
- const struct lyxp_var *vars;
struct lyxp_expr *tokens = NULL;
uint32_t token_index, name_len;
- vars = set->vars;
-
/* find out the name and value of the variable */
name = &exp->expr[exp->tok_pos[*tok_idx]];
name_len = exp->tok_len[*tok_idx];
- ret = lyxp_vars_find((struct lyxp_var *)vars, name, name_len, &var);
- LY_CHECK_ERR_RET(ret, LOGERR(set->ctx, ret, "XPath variable \"%.*s\" not defined.", (int)name_len, name), ret);
+ ret = lyxp_vars_find(set->ctx, set->vars, name, name_len, &var);
+ LY_CHECK_RET(ret);
/* parse value */
ret = lyxp_expr_parse(set->ctx, var->value, 0, 1, &tokens);
@@ -8917,8 +9002,9 @@ static LY_ERR
eval_union_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
LY_ERR rc = LY_SUCCESS;
- struct lyxp_set orig_set, set2;
uint32_t i;
+ struct lyxp_set orig_set, set2;
+ ly_bool found = 0;
assert(repeat);
@@ -8929,6 +9015,11 @@ eval_union_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat,
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, set, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set->not_found) {
+ set->not_found = 0;
+ } else {
+ found = 1;
+ }
/* ('|' PathExpr)* */
for (i = 0; i < repeat; ++i) {
@@ -8946,6 +9037,9 @@ eval_union_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat,
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (!set2.not_found) {
+ found = 1;
+ }
/* eval */
if (options & LYXP_SCNODE_ALL) {
@@ -8959,6 +9053,9 @@ eval_union_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat,
cleanup:
lyxp_set_free_content(&orig_set);
lyxp_set_free_content(&set2);
+ if (!found) {
+ set->not_found = 1;
+ }
return rc;
}
@@ -9026,7 +9123,7 @@ static LY_ERR
eval_multiplicative_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set,
uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
uint32_t i, this_op;
struct lyxp_set orig_set, set2;
@@ -9058,6 +9155,9 @@ eval_multiplicative_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval */
if (options & LYXP_SCNODE_ALL) {
@@ -9093,7 +9193,7 @@ cleanup:
static LY_ERR
eval_additive_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
uint32_t i, this_op;
struct lyxp_set orig_set, set2;
@@ -9125,6 +9225,9 @@ eval_additive_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repe
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval */
if (options & LYXP_SCNODE_ALL) {
@@ -9162,7 +9265,7 @@ cleanup:
static LY_ERR
eval_relational_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
uint32_t i, this_op;
struct lyxp_set orig_set, set2;
@@ -9194,6 +9297,9 @@ eval_relational_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t re
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval */
if (options & LYXP_SCNODE_ALL) {
@@ -9231,7 +9337,7 @@ cleanup:
static LY_ERR
eval_equality_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
uint32_t i, this_op;
struct lyxp_set orig_set, set2;
@@ -9263,6 +9369,9 @@ eval_equality_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repe
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval */
if (options & LYXP_SCNODE_ALL) {
@@ -9301,7 +9410,7 @@ cleanup:
static LY_ERR
eval_and_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
struct lyxp_set orig_set, set2;
uint32_t i;
@@ -9315,11 +9424,13 @@ eval_and_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, s
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, set, options);
LY_CHECK_GOTO(rc, cleanup);
- /* cast to boolean, we know that will be the final result */
- if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
- set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
- } else {
- lyxp_set_cast(set, LYXP_SET_BOOLEAN);
+ if (!(options & LYXP_SKIP_EXPR)) {
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ } else {
+ /* cast to boolean, we know that will be the final result */
+ lyxp_set_cast(set, LYXP_SET_BOOLEAN);
+ }
}
/* ('and' EqualityExpr)* */
@@ -9339,6 +9450,9 @@ eval_and_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, s
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval - just get boolean value actually */
if (set->type == LYXP_SET_SCNODE_SET) {
@@ -9371,7 +9485,7 @@ cleanup:
static LY_ERR
eval_or_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
struct lyxp_set orig_set, set2;
uint32_t i;
@@ -9385,11 +9499,13 @@ eval_or_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, st
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, set, options);
LY_CHECK_GOTO(rc, cleanup);
- /* cast to boolean, we know that will be the final result */
- if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
- set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
- } else {
- lyxp_set_cast(set, LYXP_SET_BOOLEAN);
+ if (!(options & LYXP_SKIP_EXPR)) {
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ } else {
+ /* cast to boolean, we know that will be the final result */
+ lyxp_set_cast(set, LYXP_SET_BOOLEAN);
+ }
}
/* ('or' AndExpr)* */
@@ -9411,6 +9527,9 @@ eval_or_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, st
* but it does not matter */
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval - just get boolean value actually */
if (set->type == LYXP_SET_SCNODE_SET) {
@@ -9523,7 +9642,7 @@ lyxp_get_root_type(const struct lyd_node *ctx_node, const struct lysc_node *ctx_
/* schema */
for (op = ctx_scnode; op && !(op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = op->parent) {}
- if (op || (options & LYXP_SCNODE)) {
+ if (op || !(options & LYXP_SCNODE_SCHEMA)) {
/* general root that can access everything */
return LYXP_NODE_ROOT;
} else if (!ctx_scnode || (ctx_scnode->flags & LYS_CONFIG_W)) {
@@ -9597,7 +9716,10 @@ lyxp_eval(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct ly
/* evaluate */
rc = eval_expr_select(exp, &tok_idx, 0, set, options);
- if (rc != LY_SUCCESS) {
+ if (!rc && set->not_found) {
+ rc = LY_ENOTFOUND;
+ }
+ if (rc) {
lyxp_set_free_content(set);
}
@@ -9838,7 +9960,7 @@ lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct
LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *cur_scnode,
const struct lysc_node *ctx_scnode, struct lyxp_set *set, uint32_t options)
{
- LY_ERR ret;
+ LY_ERR rc;
uint32_t tok_idx = 0;
LY_CHECK_ARG_RET(ctx, ctx, exp, set, LY_EINVAL);
@@ -9851,7 +9973,7 @@ lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct
memset(set, 0, sizeof *set);
set->type = LYXP_SET_SCNODE_SET;
set->root_type = lyxp_get_root_type(NULL, ctx_scnode, options);
- LY_CHECK_RET(set_scnode_insert_node(set, ctx_scnode, ctx_scnode ? LYXP_NODE_ELEM : set->root_type, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, ctx_scnode, ctx_scnode ? LYXP_NODE_ELEM : set->root_type, LYXP_AXIS_SELF, NULL));
set->val.scnodes[0].in_ctx = LYXP_SET_SCNODE_START;
set->ctx = (struct ly_ctx *)ctx;
@@ -9866,10 +9988,13 @@ lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct
LOG_LOCSET(set->cur_scnode, NULL, NULL, NULL);
/* evaluate */
- ret = eval_expr_select(exp, &tok_idx, 0, set, options);
+ rc = eval_expr_select(exp, &tok_idx, 0, set, options);
+ if (!rc && set->not_found) {
+ rc = LY_ENOTFOUND;
+ }
LOG_LOCBACK(set->cur_scnode ? 1 : 0, 0, 0, 0);
- return ret;
+ return rc;
}
LIBYANG_API_DEF const char *
diff --git a/src/xpath.h b/src/xpath.h
index 3e61bb0..17bda6c 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -291,13 +291,15 @@ struct lyxp_set {
/* this is valid only for type LYXP_SET_NODE_SET and LYXP_SET_SCNODE_SET */
uint32_t used; /**< Number of nodes in the set. */
uint32_t size; /**< Allocated size for the set. */
- struct hash_table *ht; /**< Hash table for quick determination of whether a node is in the set. */
+ struct ly_ht *ht; /**< Hash table for quick determination of whether a node is in the set. */
/* XPath context information, this is valid only for type LYXP_SET_NODE_SET */
uint32_t ctx_pos; /**< Position of the current examined node in the set. */
uint32_t ctx_size; /**< Position of the last node at the time the node was examined. */
ly_bool non_child_axis; /**< Whether any node change was performed on a non-child axis. */
+ ly_bool not_found; /**< Set if a node is not found and it is considered an error. */
+
/* general context */
struct ly_ctx *ctx; /**< General context for logging. */
@@ -327,6 +329,8 @@ const char *lyxp_token2str(enum lyxp_token tok);
* @brief Evaluate an XPath expression on data. Be careful when using this function, the result can often
* be confusing without thorough understanding of XPath evaluation rules defined in RFC 7950.
*
+ * Traverses (valid) opaque nodes in the evaluation.
+ *
* @param[in] ctx libyang context to use.
* @param[in] exp Parsed XPath expression to be evaluated.
* @param[in] cur_mod Current module for the expression (where it was "instantiated").
@@ -381,6 +385,7 @@ LY_ERR lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const
warning is printed. */
#define LYXP_ACCESS_TREE_ALL 0x80 /**< Explicit accessible tree of all the nodes. */
#define LYXP_ACCESS_TREE_CONFIG 0x0100 /**< Explicit accessible tree of only configuration data. */
+#define LYXP_SCNODE_SCHEMAMOUNT LYS_FIND_SCHEMAMOUNT /**< Nodes from mounted modules are also accessible. */
/**
* @brief Cast XPath set to another type.
@@ -421,6 +426,20 @@ ly_bool lyxp_set_scnode_contains(struct lyxp_set *set, const struct lysc_node *n
void lyxp_set_scnode_merge(struct lyxp_set *set1, struct lyxp_set *set2);
/**
+ * @brief Insert schema node into set.
+ *
+ * @param[in] set Set to insert into.
+ * @param[in] node Node to insert.
+ * @param[in] node_type Node type of @p node.
+ * @param[in] axis Axis that @p node was reached on.
+ * @param[out] index_p Optional pointer to store the index of the inserted @p node.
+ * @return LY_SUCCESS on success.
+ * @return LY_EMEM on memory allocation failure.
+ */
+LY_ERR lyxp_set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
+ enum lyxp_axis axis, uint32_t *index_p);
+
+/**
* @brief Parse an XPath expression into a structure of tokens.
* Logs directly.
*
@@ -496,15 +515,17 @@ LY_ERR lyxp_next_token2(const struct ly_ctx *ctx, const struct lyxp_expr *exp, u
enum lyxp_token want_tok1, enum lyxp_token want_tok2);
/**
- * @brief Find variable named @name in @p vars.
+ * @brief Find variable named @p name in @p vars.
*
+ * @param[in] ctx Context for logging, not logged if NULL.
* @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
* @param[in] name Name of the variable being searched.
* @param[in] name_len Name length can be set to 0 if @p name is terminated by null byte.
* @param[out] var Variable that was found. The parameter is optional.
* @return LY_SUCCESS if the variable was found, otherwise LY_ENOTFOUND.
*/
-LY_ERR lyxp_vars_find(struct lyxp_var *vars, const char *name, size_t name_len, struct lyxp_var **var);
+LY_ERR lyxp_vars_find(const struct ly_ctx *ctx, const struct lyxp_var *vars, const char *name, size_t name_len,
+ struct lyxp_var **var);
/**
* @brief Frees a parsed XPath expression. @p expr should not be used afterwards.