diff options
Diffstat (limited to '')
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; +} @@ -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_ */ @@ -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) { @@ -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 @@ -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. */ @@ -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 @@ -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); @@ -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; }; @@ -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, ¤t)); - - 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, ¤t)); - } + } 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, ¤t)); + } 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, ¤t)); + 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, ¤t)); + 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); } @@ -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); } @@ -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; } @@ -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]); } @@ -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. @@ -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. |