From d0e4956794a6c0dad3ea250db8381c44012698a4 Mon Sep 17 00:00:00 2001 From: MeherRushi Date: Sat, 19 Jul 2025 13:03:16 +0530 Subject: [PATCH 1/2] ADD CBOR support --- CMakeLists.txt | 23 +- CMakeModules/FindCBOR.cmake | 37 + src/lcbor.c | 117 ++ src/lcbor.h | 79 ++ src/log.h | 1 + src/parser_cbor.c | 1882 +++++++++++++++++++++++++++ src/parser_data.h | 23 + src/parser_internal.h | 50 + src/plugins_types.c | 2 + src/plugins_types/instanceid.c | 1 + src/plugins_types/instanceid_keys.c | 1 + src/plugins_types/node_instanceid.c | 2 + src/plugins_types/xpath1.0.c | 1 + src/printer_cbor.c | 1308 +++++++++++++++++++ src/printer_data.c | 10 + src/printer_internal.h | 10 + src/printer_json.c | 2 + src/printer_lyb.c | 1 + src/printer_xml.c | 1 + src/schema_compile_node.c | 1 + src/tree.h | 1 + src/tree_data.c | 39 + src/tree_data.h | 19 +- src/tree_data_common.c | 8 + src/tree_data_internal.h | 2 + src/tree_data_new.c | 1 + src/tree_schema_internal.h | 2 + src/xpath.c | 2 + 28 files changed, 3624 insertions(+), 2 deletions(-) create mode 100644 CMakeModules/FindCBOR.cmake create mode 100644 src/lcbor.c create mode 100644 src/lcbor.h create mode 100644 src/parser_cbor.c create mode 100644 src/printer_cbor.c diff --git a/CMakeLists.txt b/CMakeLists.txt index da1491429..69cb64d8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -238,6 +238,7 @@ set(format_sources src/*.h src/plugins_exts/* src/plugins_types/*) + # # options # @@ -323,6 +324,15 @@ if(ENABLE_COVERAGE) gen_coverage_enable(${ENABLE_TESTS}) endif() +find_package(CBOR) +if(CBOR_FOUND) + list(APPEND libsrc src/parser_cbor.c src/lcbor.c src/printer_cbor.c) + list(APPEND headers src/lcbor.h) + list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c src/printer_cbor.c) +else() + message(STATUS "libcbor not found, CBOR support disabled") +endif() + if ("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG") # enable before adding tests to let them detect that format checking is available - one of the tests is format checking source_format_enable(0.77) @@ -427,6 +437,17 @@ find_package(PCRE2 10.21 REQUIRED) include_directories(${PCRE2_INCLUDE_DIRS}) target_link_libraries(yang ${PCRE2_LIBRARIES}) +# link libcbor if found +if(CBOR_FOUND) + if(TARGET yangobj) + target_compile_definitions(yangobj PRIVATE ENABLE_CBOR_SUPPORT) + target_include_directories(yangobj PRIVATE ${CBOR_INCLUDE_DIR}) + endif() + target_compile_definitions(yang PRIVATE ENABLE_CBOR_SUPPORT) + target_include_directories(yang PRIVATE ${CBOR_INCLUDE_DIR}) + target_link_libraries(yang ${CBOR_LIBRARY}) +endif() + # XXHash include and library find_package(XXHash) if(XXHASH_FOUND) @@ -521,4 +542,4 @@ add_custom_target(cclean COMMAND make clean COMMAND find . -iname '*cmake*' -not -name CMakeLists.txt -not -path './CMakeModules*' -exec rm -rf {} + COMMAND rm -rf Makefile Doxyfile - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/CMakeModules/FindCBOR.cmake b/CMakeModules/FindCBOR.cmake new file mode 100644 index 000000000..14478f3ac --- /dev/null +++ b/CMakeModules/FindCBOR.cmake @@ -0,0 +1,37 @@ +# Try to find libcbor +# Once done this will define +# +# Read-Only variables: +# CBOR_FOUND - system has libcbor +# CBOR_INCLUDE_DIR - the libcbor include directory +# CBOR_LIBRARY - Link these to use libcbor + +find_path(CBOR_INCLUDE_DIR + NAMES + cbor.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include +) + +find_library(CBOR_LIBRARY + NAMES + cbor + libcbor + PATHS + /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CBOR FOUND_VAR CBOR_FOUND REQUIRED_VARS CBOR_INCLUDE_DIR CBOR_LIBRARY) diff --git a/src/lcbor.c b/src/lcbor.c new file mode 100644 index 000000000..d0431fa8a --- /dev/null +++ b/src/lcbor.c @@ -0,0 +1,117 @@ +/** + * @file lcbor.c + * @author MeherRushi + * @brief CBOR data parser for libyang (abstraction over libcbor) + * + * Copyright (c) 2026 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 +#include +#include + +#include "in_internal.h" +#include "lcbor.h" +#include "log.h" +#include "ly_common.h" + +const char * +lycbor_token2str(enum cbor_type cbortype) +{ + switch (cbortype) { + case CBOR_TYPE_UINT: + return "unsigned integer"; + case CBOR_TYPE_NEGINT: + return "negative integer"; + case CBOR_TYPE_BYTESTRING: + return "byte string"; + case CBOR_TYPE_STRING: + return "string"; + case CBOR_TYPE_ARRAY: + return "array"; + case CBOR_TYPE_MAP: + return "map"; + case CBOR_TYPE_TAG: + return "tag"; + case CBOR_TYPE_FLOAT_CTRL: + return "decimals and special values (true, false, nil, ...)"; + } + + return ""; +} + +void +lycbor_ctx_free(struct lycbor_ctx *cborctx) +{ + if (cborctx) { + if (cborctx->cbor_data) { + cbor_decref(&cborctx->cbor_data); + } + free(cborctx); + } +} + +/** + * @brief Detect CBOR format variant from input data. + * + * @param[in] in Input structure to analyze. + * @param[out] format Detected format. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_detect_format(struct ly_in *in, enum lyd_cbor_format *format) +{ + /* Simple heuristic: try to parse as CBOR and examine structure */ + /* For now, default to named format */ + (void)in; + *format = LYD_CBOR_NAMED; + return LY_SUCCESS; +} + +LY_ERR +lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx_p) +{ + /* TODO : Need to restructure error handling here */ + LY_ERR ret = LY_SUCCESS; + struct lycbor_ctx *cborctx; + struct cbor_load_result result = {0}; + enum lyd_cbor_format format; + + assert(ctx && in && cborctx_p); + + /* TODO : error handling after the detect_format function call */ + ret = lydcbor_detect_format(in, &format); + + /* Allocate and initialize CBOR context */ + cborctx = calloc(1, sizeof *cborctx); + LY_CHECK_ERR_RET(!cborctx, LOGMEM(ctx), LY_EMEM); + cborctx->ctx = ctx; + cborctx->in = in; + cborctx->format = format; + + /* input line logging */ + ly_log_location(NULL, NULL, in); + + /* load and parse CBOR data */ + cborctx->cbor_data = cbor_load((cbor_data)in->current, in->length, &result); + if (!cborctx->cbor_data) { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Failed to parse CBOR data."); + free(cborctx); + return LY_EVALID; + } + if (result.error.code != CBOR_ERR_NONE) { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "CBOR parsing error (code %d).", result.error.code); + cbor_decref(&cborctx->cbor_data); + free(cborctx); + return LY_EVALID; + } + + *cborctx_p = cborctx; + return ret; +} diff --git a/src/lcbor.h b/src/lcbor.h new file mode 100644 index 000000000..4f7a9d8d4 --- /dev/null +++ b/src/lcbor.h @@ -0,0 +1,79 @@ +/** + * @file lcbor.h + * @author MeherRushi + * @brief CBOR data parser routines for libyang (abstraction over libcbor) + * + * Copyright (c) 2026 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_CBOR_H_ +#define LY_CBOR_H_ + +#include +#include +/* using libcbor as the low-level parser */ +#include + + +#include "log.h" +#include "set.h" + +struct ly_ctx; +struct ly_in; + +/** + * @brief CBOR format variants for different encoding schemes + */ +enum lyd_cbor_format +{ + LYD_CBOR_NAMED, /**< CBOR with named identifiers (JSON-like) */ + LYD_CBOR_SID /**< CBOR with Schema Item identifiers (future implementation) */ +}; + +struct lycbor_ctx { + const struct ly_ctx *ctx; + struct ly_in *in; /** input structure */ + + cbor_item_t *cbor_data; /**< parsed CBOR data */ + + enum lyd_cbor_format format; /**< CBOR format variant */ + + struct { + cbor_item_t *cbor_data; /**< parsed CBOR data */ + enum lyd_cbor_format format; /**< CBOR format variant */ + const char *input; + } backup; +}; + +/** + * @brief Get a human-readable name for a CBOR type. + * + * @param[in] cbortype CBOR type. + * @return String representation of the CBOR type. + */ +const char *lycbor_token2str(enum cbor_type cbortype); + +/** + * @brief Create new CBOR context for parsing. + * + * @param[in] ctx libyang context. + * @param[in] in CBOR string data to parse. + * @param[out] cborctx New CBOR parser context containing parsed CBOR data. + * @return LY_ERR value. + */ +LY_ERR lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx); + +/** + * @brief Free CBOR context. + * + * @param[in] cborctx CBOR context to free. + */ +void lycbor_ctx_free(struct lycbor_ctx *cborctx); + +#endif /* LY_CBOR_H_ */ diff --git a/src/log.h b/src/log.h index 711b06211..9b0692f75 100644 --- a/src/log.h +++ b/src/log.h @@ -286,6 +286,7 @@ typedef enum { LYVE_SEMANTICS, /**< generic semantic error */ LYVE_SYNTAX_XML, /**< XML-related syntax error */ LYVE_SYNTAX_JSON, /**< JSON-related syntax error */ + LYVE_SYNTAX_CBOR, /**< CBOR-related syntax error */ LYVE_DATA, /**< YANG data does not reflect some of the module restrictions */ LYVE_OTHER /**< Unknown error */ diff --git a/src/parser_cbor.c b/src/parser_cbor.c new file mode 100644 index 000000000..a816d8179 --- /dev/null +++ b/src/parser_cbor.c @@ -0,0 +1,1882 @@ +/** + * @file parser_cbor.c + * @author Meher Rushi + * @brief CBOR data parser for libyang + * + * Copyright (c) 2026 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 +#include +#include +#include +#include + +#include "compat.h" +#include "context.h" +#include "dict.h" +#include "in_internal.h" +#include "lcbor.h" +#include "log.h" +#include "ly_common.h" +#include "parser_data.h" +#include "parser_internal.h" +#include "plugins_exts.h" +#include "set.h" +#include "tree.h" +#include "tree_data.h" +#include "tree_data_internal.h" +#include "tree_schema.h" +#include "tree_schema_internal.h" +#include "validation.h" + +/** + * @brief Free the CBOR data parser context. + * + * CBOR implementation of lyd_ctx_free_clb(). + */ +static void +lyd_cbor_ctx_free(struct lyd_ctx *lydctx) +{ + struct lyd_cbor_ctx *ctx = (struct lyd_cbor_ctx *)lydctx; + + if (lydctx) { + lyd_ctx_free(lydctx); + ly_set_erase(&ctx->ext_node, NULL); + lycbor_ctx_free(ctx->cborctx); + free(ctx); + } +} + +/** + * @brief Parse CBOR member-name as [\@][prefix:][name] + * + * \@ - metadata flag, maps to 1 in @p is_meta_p + * prefix - name of the module of the data node + * name - name of the data node + * + * All the output parameters are mandatory. Function only parses the member-name. + * + * @param[in] value String to parse + * @param[in] value_len Length of the @p value. + * @param[out] name_p Pointer to the beginning of the parsed name. + * @param[out] name_len_p Pointer to the length of the parsed name. + * @param[out] prefix_p Pointer to the beginning of the parsed prefix. If the member-name does not contain prefix, result is NULL. + * @param[out] prefix_len_p Pointer to the length of the parsed prefix. If the member-name does not contain prefix, result is 0. + * @param[out] is_meta_p Pointer to the metadata flag, set to 1 if the member-name contains \@, 0 otherwise. + */ +static void +lydcbor_parse_name(const char *value, size_t value_len, const char **name_p, size_t *name_len_p, const char **prefix_p, + size_t *prefix_len_p, ly_bool *is_meta_p) +{ + const char *name, *prefix = NULL; + size_t name_len, prefix_len = 0; + ly_bool is_meta = 0; + + name = memchr(value, ':', value_len); + if (name != NULL) + { + prefix = value; + if (*prefix == '@') + { + is_meta = 1; + prefix++; + } + prefix_len = name - prefix; + name++; + name_len = value_len - (prefix_len + 1) - is_meta; + } + else + { + name = value; + if (name[0] == '@') + { + is_meta = 1; + name++; + } + name_len = value_len - is_meta; + } + + *name_p = name; + *name_len_p = name_len; + *prefix_p = prefix; + *prefix_len_p = prefix_len; + *is_meta_p = is_meta; +} + +/** + * @brief Get correct prefix (module_name) inside the @p node. + * + * @param[in] node Data node to get inherited prefix. + * @param[in] local_prefix Local prefix to replace the inherited prefix. + * @param[in] local_prefix_len Length of the @p local_prefix string. In case of 0, the inherited prefix is taken. + * @param[out] prefix_p Pointer to the resulting prefix string. + * @param[out] prefix_len_p Pointer to the length of the resulting @p prefix_p string. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_get_node_prefix(struct lyd_node *node, const char *local_prefix, size_t local_prefix_len, const char **prefix_p, + size_t *prefix_len_p) +{ + struct lyd_node_opaq *onode; + const char *module_name = NULL; + + assert(prefix_p && prefix_len_p); + + if (local_prefix_len) + { + *prefix_p = local_prefix; + *prefix_len_p = local_prefix_len; + return LY_SUCCESS; + } + + while (node) + { + if (node->schema) + { + module_name = node->schema->module->name; + break; + } + onode = (struct lyd_node_opaq *)node; + if (onode->name.module_name) + { + module_name = onode->name.module_name; + break; + } + else if (onode->name.prefix) + { + module_name = onode->name.prefix; + break; + } + node = lyd_parent(node); + } + + *prefix_p = module_name; + *prefix_len_p = ly_strlen(module_name); + return LY_SUCCESS; +} + +/** + * @brief Skip the current CBOR item based on its type. + * + * @param[in] cborctx CBOR context. + * @param[in] item CBOR item to skip. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_data_skip(struct lycbor_ctx *cborctx) +{ + (void)cborctx; + /* In CBOR, items are already parsed, so skipping is implicit */ + return LY_SUCCESS; +} + +/** + * @brief Get schema node corresponding to the input parameters. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] is_attr Flag if the reference to the node is an attribute. + * @param[in] prefix Requested node's prefix (module name). + * @param[in] prefix_len Length of the @p prefix. + * @param[in] name Requested node's name. + * @param[in] name_len Length of the @p name. + * @param[in] parent Parent of the node being processed. + * @param[out] snode Found schema node corresponding to the input parameters. + * @param[out] ext Extension instance that provided @p snode, if any. + * @return LY_SUCCESS on success. + * @return LY_ENOT if the whole object was parsed (skipped or as an extension). + * @return LY_ERR on error. + */ +static LY_ERR +lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, ly_bool is_attr, const char *prefix, size_t prefix_len, const char *name, + size_t name_len, struct lyd_node *parent, const struct lysc_node **snode, struct lysc_ext_instance **ext) +{ + LY_ERR r; + struct lys_module *mod = NULL; + const struct lysc_node *sparent; + uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0; + + *snode = NULL; + if (ext) { + *ext = NULL; + } + + /* try to find parent schema node */ + if (parent && parent->schema && !(parent->schema->nodetype & LYD_NODE_ANY)) { + sparent = parent->schema; + } else { + sparent = NULL; + } + if (!prefix_len) { + prefix = NULL; + } + r = lys_find_child_node(parent ? LYD_CTX(parent) : lydctx->cborctx->ctx, sparent, NULL, prefix, prefix_len, + LY_VALUE_CBOR, NULL, name, name_len, getnext_opts, snode, ext); + LY_CHECK_RET(r && (r != LY_ENOT), r); + + if (!r) { + /* check that schema node is valid and can be used */ + LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode)); + return LY_SUCCESS; + } + + /* generate error, find the module */ + if (prefix_len) { + mod = ly_ctx_get_module_implemented2(parent ? LYD_CTX(parent) : lydctx->cborctx->ctx, prefix, prefix_len); + } else if (parent) { + if (parent->schema) { + mod = parent->schema->module; + } + } else if (!(lydctx->int_opts & LYD_INTOPT_ANY)) { + LOGVAL(lydctx->cborctx->ctx, parent, LYVE_SYNTAX, + "Top-level CBOR object member \"%.*s\" must be namespace-qualified.", + (int)(is_attr ? name_len + 1 : name_len), is_attr ? name - 1 : name); + return LY_EVALID; + } + if (!(lydctx->parse_opts & LYD_PARSE_STRICT)) { + return LY_SUCCESS; + } + + if (!mod) { + LOGVAL(lydctx->cborctx->ctx, parent, LYVE_REFERENCE, + "No module named \"%.*s\" in the context.", (int)prefix_len, prefix); + return LY_EVALID; + } + + if (sparent) { + LOGVAL(lydctx->cborctx->ctx, parent, LYVE_REFERENCE, + "Node \"%.*s\" not found as a child of \"%s\" node.", + (int)name_len, name, sparent->name); + } else { + LOGVAL(lydctx->cborctx->ctx, parent, LYVE_REFERENCE, + "Node \"%.*s\" not found in the \"%s\" module.", + (int)name_len, name, mod->name); + } + return LY_EVALID; +} + +/** + * @brief Check if CBOR item is null/undefined. + * + * @param[in] item CBOR item to check. + * @return 1 if null/undefined, 0 otherwise. + */ +static ly_bool +lydcbor_is_null(const cbor_item_t *item) +{ + if (!item) + { + return 1; + } + + if (cbor_isa_float_ctrl(item)) + { + if (cbor_float_get_width(item) == CBOR_FLOAT_0) + { + uint8_t ctrl = cbor_ctrl_value(item); + if (ctrl == 22 || ctrl == 23) + { /* null or undefined */ + return 1; + } + } + } + + return 0; +} + +/** + * @brief Get the hint for the data type parsers according to the current CBOR type. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] item CBOR item. + * @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 CBOR type. + */ +static LY_ERR +lydcbor_value_type_hint(struct lyd_cbor_ctx *lydctx, const cbor_item_t *item, uint32_t *type_hint_p) +{ + enum cbor_type type; + + *type_hint_p = 0; + + if (!item) + { + return LY_EINVAL; + } + + type = cbor_typeof(item); + + if (type == CBOR_TYPE_ARRAY) + { + /* check for [null] */ + if (cbor_array_size(item) == 1) + { + cbor_item_t **handle = cbor_array_handle(item); + if (handle && lydcbor_is_null(handle[0])) + { + *type_hint_p = LYD_VALHINT_EMPTY; + return LY_SUCCESS; + } + } + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Expected CBOR value or [null], but array found."); + return LY_EINVAL; + } + else if (type == CBOR_TYPE_STRING) + { + *type_hint_p = LYD_VALHINT_STRING | LYD_VALHINT_NUM64; + } + else if (type == CBOR_TYPE_UINT || type == CBOR_TYPE_NEGINT) + { + *type_hint_p = LYD_VALHINT_DECNUM; + } + else if (type == CBOR_TYPE_FLOAT_CTRL) + { + if (cbor_float_ctrl_is_ctrl(item)) + { + uint8_t ctrl = cbor_ctrl_value(item); + if (ctrl == CBOR_CTRL_TRUE || ctrl == CBOR_CTRL_FALSE) + { + *type_hint_p = LYD_VALHINT_BOOLEAN; + } + else if (ctrl == CBOR_CTRL_NULL) + { + *type_hint_p = 0; + } + else + { + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Unexpected CBOR control value."); + return LY_EINVAL; + } + } + else + { + *type_hint_p = LYD_VALHINT_DECNUM; + } + } + else + { + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Unexpected CBOR data type."); + return LY_EINVAL; + } + + return LY_SUCCESS; +} + +/** + * @brief Convert a CBOR item to a string representation. + * + * @param[in] item CBOR item to convert. + * @param[out] str_val String value (allocated, caller must free). + * @param[out] str_len String length. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) +{ + LY_ERR ret = LY_SUCCESS; + + assert(item && str_val && str_len); + *str_val = NULL; + *str_len = 0; + + switch (cbor_typeof(item)) { + case CBOR_TYPE_UINT: { + uint64_t val = cbor_get_int(item); + int len = snprintf(NULL, 0, "%" PRIu64, val); + if (len < 0) { + return LY_ESYS; + } + *str_val = malloc(len + 1); + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + sprintf(*str_val, "%" PRIu64, val); + *str_len = len; + break; + } + case CBOR_TYPE_NEGINT: { + int64_t val = -1 - (int64_t)cbor_get_int(item); + int len = snprintf(NULL, 0, "%" PRId64, val); + if (len < 0) { + return LY_ESYS; + } + *str_val = malloc(len + 1); + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + sprintf(*str_val, "%" PRId64, val); + *str_len = len; + break; + } + case CBOR_TYPE_BYTESTRING: + *str_len = cbor_bytestring_length(item); + *str_val = malloc(*str_len + 1); + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + memcpy(*str_val, cbor_bytestring_handle(item), *str_len); + (*str_val)[*str_len] = '\0'; + break; + case CBOR_TYPE_STRING: + *str_len = cbor_string_length(item); + *str_val = malloc(*str_len + 1); + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + memcpy(*str_val, cbor_string_handle(item), *str_len); + (*str_val)[*str_len] = '\0'; + break; + case CBOR_TYPE_FLOAT_CTRL: + if (cbor_float_ctrl_is_ctrl(item)) { + switch (cbor_ctrl_value(item)) { + case CBOR_CTRL_TRUE: + *str_val = strdup("true"); + *str_len = 4; + break; + case CBOR_CTRL_FALSE: + *str_val = strdup("false"); + *str_len = 5; + break; + case CBOR_CTRL_NULL: + *str_val = strdup(""); + *str_len = 0; + break; + default: + LOGVAL(NULL, NULL, LYVE_SYNTAX, "Unsupported CBOR control value"); + ret = LY_EVALID; + break; + } + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + } else { + double val = cbor_float_get_float(item); + int len = snprintf(NULL, 0, "%g", val); + if (len < 0) { + return LY_ESYS; + } + *str_val = malloc(len + 1); + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + sprintf(*str_val, "%g", val); + *str_len = len; + } + break; + default: + LOGVAL(NULL, NULL, LYVE_SYNTAX, "Unsupported CBOR data type %d", cbor_typeof(item)); + ret = LY_EVALID; + break; + } + + return ret; +} + +/** + * @brief Check in advance if the input data are parsable according to the provided @p snode. + * + * Note that the checks are done only in case the LYD_PARSE_OPAQ is allowed. Otherwise the same checking + * is naturally done when the data are really parsed. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] snode Schema node corresponding to the member currently being processed. + * @param[in] cbor_value CBOR value to check. + * @param[out] type_hint_p Pointer to store detected value type hint. + * @return LY_SUCCESS if data are parsable. + * @return LY_ENOT if input data are not sufficient. + * @return LY_EINVAL in case of invalid encoding. + */ +static LY_ERR +lydcbor_data_check_opaq(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, const cbor_item_t *cbor_value, + uint32_t *type_hint_p) +{ + LY_ERR ret = LY_SUCCESS; + uint32_t *prev_lo, temp_lo = 0; + char *str_val = NULL; + size_t str_len = 0; + + assert(snode); + + if (!(snode->nodetype & (LYD_NODE_TERM | LYS_LIST))) + { + return LY_SUCCESS; + } + + if (lydctx->parse_opts & LYD_PARSE_OPAQ) + { + switch (snode->nodetype) + { + case LYS_LEAFLIST: + case LYS_LEAF: + if ((ret = lydcbor_value_type_hint(lydctx, cbor_value, type_hint_p))) + { + break; + } + + prev_lo = ly_temp_log_options(&temp_lo); + ret = lydcbor_item_to_string(cbor_value, &str_val, &str_len); + if (ret == LY_SUCCESS) + { + if (ly_value_validate(NULL, snode, str_val, str_len, LY_VALUE_CBOR, NULL, *type_hint_p)) + { + ret = LY_ENOT; + } + } + ly_temp_log_options(prev_lo); + free(str_val); + break; + case LYS_LIST: + /* Lists may not have all keys - handled elsewhere */ + break; + } + } + else if (snode->nodetype & LYD_NODE_TERM) + { + ret = lydcbor_value_type_hint(lydctx, cbor_value, type_hint_p); + } + + return ret; +} + +/** + * @brief Join the forward-referencing metadata with their target data nodes. + * + * @param[in] lydctx CBOR data parser context. + * @param[in,out] first_p Pointer to the first sibling node. + * @return LY_SUCCESS on success. + * @return LY_EVALID if there are unresolved metadata. + */ +static LY_ERR +lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_node *node, *attr, *next, *meta_iter; + struct lysc_ext_instance *ext; + uint64_t instance = 0; + const char *prev = NULL; + uint32_t log_location_items = 0; + + /* finish linking metadata */ + LY_LIST_FOR_SAFE(*first_p, next, attr) + { + struct lyd_node_opaq *meta_container = (struct lyd_node_opaq *)attr; + uint64_t match = 0; + ly_bool is_attr; + const char *name, *prefix; + size_t name_len, prefix_len; + const struct lysc_node *snode; + + if (attr->schema || (meta_container->name.name[0] != '@')) + { + continue; + } + + /* dnode-only location no longer supported by LOG_LOCSET */ + log_location_items++; + + if (prev != meta_container->name.name) + { + lydict_remove(lydctx->cborctx->ctx, prev); + LY_CHECK_GOTO(ret = lydict_insert(lydctx->cborctx->ctx, meta_container->name.name, 0, &prev), cleanup); + instance = 1; + } + else + { + instance++; + } + + /* find the corresponding data node */ + LY_LIST_FOR(*first_p, node) + { + if (!node->schema) + { + /* opaq node */ + if (strcmp(&meta_container->name.name[1], ((struct lyd_node_opaq *)node)->name.name)) + { + continue; + } + + if (((struct lyd_node_opaq *)node)->hints & LYD_NODEHINT_LIST) + { + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Metadata container references a sibling list node %s.", + ((struct lyd_node_opaq *)node)->name.name); + ret = LY_EVALID; + goto cleanup; + } + + match++; + if (match != instance) + { + continue; + } + + LY_LIST_FOR(meta_container->child, meta_iter) + { + struct lyd_node_opaq *meta = (struct lyd_node_opaq *)meta_iter; + + ret = lyd_create_attr(node, NULL, lydctx->cborctx->ctx, meta->name.name, strlen(meta->name.name), + meta->name.prefix, ly_strlen(meta->name.prefix), meta->name.module_name, + ly_strlen(meta->name.module_name), meta->value, ly_strlen(meta->value), NULL, LY_VALUE_CBOR, + NULL, meta->hints); + LY_CHECK_GOTO(ret, cleanup); + } + break; + } + else + { + lydcbor_parse_name(meta_container->name.name, strlen(meta_container->name.name), &name, &name_len, + &prefix, &prefix_len, &is_attr); + assert(is_attr); + lydcbor_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, lyd_parent(*first_p), &snode, &ext); + + if (snode != node->schema) + { + continue; + } + + match++; + if (match != instance) + { + continue; + } + + LY_LIST_FOR(meta_container->child, meta_iter) + { + struct lyd_node_opaq *meta = (struct lyd_node_opaq *)meta_iter; + struct lys_module *mod = NULL; + + mod = ly_ctx_get_module_implemented(lydctx->cborctx->ctx, meta->name.prefix); + if (mod) + { + ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, + meta->name.name, strlen(meta->name.name), meta->value, ly_strlen(meta->value) * 8, + NULL, LY_VALUE_CBOR, NULL, meta->hints, node->schema, node); + LY_CHECK_GOTO(ret, cleanup); + } + else if (lydctx->parse_opts & LYD_PARSE_STRICT) + { + if (meta->name.prefix) + { + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, + "Unknown (or not implemented) YANG module \"%s\" of metadata \"%s%s%s\".", + meta->name.prefix, meta->name.prefix, ly_strlen(meta->name.prefix) ? ":" : "", + meta->name.name); + } + else + { + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, "Missing YANG module of metadata \"%s\".", + meta->name.name); + } + ret = LY_EVALID; + goto cleanup; + } + } + + ret = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, ext); + LY_CHECK_GOTO(ret, cleanup); + break; + } + } + + if (match != instance) + { + if (instance > 1) + { + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, + "Missing CBOR data instance #%" PRIu64 " to be coupled with %s metadata.", + instance, meta_container->name.name); + } + else + { + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, "Missing CBOR data instance to be coupled with %s metadata.", + meta_container->name.name); + } + ret = LY_EVALID; + } + else + { + if (attr == (*first_p)) + { + *first_p = attr->next; + } + lyd_free_tree(attr); + } + + LOG_LOCBACK(0); + log_location_items = 0; + } + +cleanup: + lydict_remove(lydctx->cborctx->ctx, prev); + LOG_LOCBACK(0); + return ret; +} + +/** + * @brief Parse a metadata member/attribute from CBOR. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] snode Schema node of the metadata parent. + * @param[in] node Parent node. + * @param[in] cbor_meta CBOR metadata item. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, struct lyd_node *node, + const cbor_item_t *cbor_meta) +{ + LY_ERR rc = LY_SUCCESS, r; + enum cbor_type type; + const char *expected; + ly_bool in_parent = 0; + const char *name, *prefix = NULL; + size_t name_len, prefix_len = 0; + struct lys_module *mod; + const struct ly_ctx *ctx = lydctx->cborctx->ctx; + ly_bool is_attr = 0; + struct lyd_node *prev = node; + uint32_t instance = 0, val_hints; + uint16_t nodetype; + struct cbor_pair *pairs; + size_t map_size; + + assert(snode || node); + + nodetype = snode ? snode->nodetype : LYS_CONTAINER; + if (snode) + { + LOG_LOCSET(snode); + } + + type = cbor_typeof(cbor_meta); + + /* check attribute encoding */ + switch (nodetype) + { + case LYS_LEAFLIST: + expected = "@name/array of objects/nulls"; + LY_CHECK_GOTO(type != CBOR_TYPE_ARRAY, representation_error); + + next_entry: + instance++; + if (!node || (node->schema != prev->schema)) + { + LOGVAL(ctx, NULL, LYVE_REFERENCE, "Missing CBOR data instance #%" PRIu32 " of %s:%s to be coupled with metadata.", instance, prev->schema->module->name, prev->schema->name); + rc = LY_EVALID; + goto cleanup; + } + + /* Process array item */ + if (cbor_array_size(cbor_meta) > instance - 1) + { + cbor_item_t **handle = cbor_array_handle(cbor_meta); + const cbor_item_t *item = handle[instance - 1]; + + if (lydcbor_is_null(item)) + { + prev = node; + node = node->next; + if (instance < cbor_array_size(cbor_meta)) + { + goto next_entry; + } + goto cleanup; + } + } + else + { + goto cleanup; + } + break; + case LYS_LEAF: + case LYS_ANYXML: + expected = "@name/object"; + LY_CHECK_GOTO(type != CBOR_TYPE_MAP, representation_error); + break; + case LYS_CONTAINER: + case LYS_LIST: + case LYS_ANYDATA: + case LYS_NOTIF: + case LYS_ACTION: + case LYS_RPC: + in_parent = 1; + expected = "@/object"; + LY_CHECK_GOTO(type != CBOR_TYPE_MAP, representation_error); + break; + default: + LOGINT(ctx); + rc = LY_EINT; + goto cleanup; + } + + /* process all members inside metadata object */ + assert(type == CBOR_TYPE_MAP); + map_size = cbor_map_size(cbor_meta); + pairs = cbor_map_handle(cbor_meta); + + for (size_t i = 0; i < map_size; ++i) + { + const cbor_item_t *key_item = pairs[i].key; + const cbor_item_t *value_item = pairs[i].value; + char *key_str = NULL; + size_t key_len = 0; + + if (!cbor_isa_string(key_item)) + { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Metadata key must be a string."); + rc = LY_EVALID; + goto cleanup; + } + + LY_CHECK_GOTO(rc = lydcbor_item_to_string(key_item, &key_str, &key_len), cleanup); + + lydcbor_parse_name(key_str, key_len, &name, &name_len, &prefix, &prefix_len, &is_attr); + + if (!name_len) + { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Metadata in CBOR found with an empty name."); + free(key_str); + rc = LY_EVALID; + goto cleanup; + } + else if (!prefix_len) + { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Metadata in CBOR must be namespace-qualified, missing prefix for \"%.*s\".", + (int)key_len, key_str); + free(key_str); + rc = LY_EVALID; + goto cleanup; + } + else if (is_attr) + { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Invalid format of Metadata identifier in CBOR, unexpected '@' in \"%.*s\"", + (int)key_len, key_str); + free(key_str); + rc = LY_EVALID; + goto cleanup; + } + + /* get the element module */ + mod = ly_ctx_get_module_implemented2(ctx, prefix, prefix_len); + if (!mod) + { + if (lydctx->parse_opts & LYD_PARSE_STRICT) + { + LOGVAL(ctx, NULL, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.", + (int)prefix_len, prefix, (int)name_len, name); + free(key_str); + rc = LY_EVALID; + goto cleanup; + } + if (node->schema) + { + free(key_str); + continue; + } + assert(lydctx->parse_opts & LYD_PARSE_OPAQ); + } + + /* get value hints */ + LY_CHECK_ERR_GOTO(rc = lydcbor_value_type_hint(lydctx, value_item, &val_hints), free(key_str), cleanup); + + if (node->schema) + { + char *str_val = NULL; + size_t str_len = 0; + + LY_CHECK_ERR_GOTO(rc = lydcbor_item_to_string(value_item, &str_val, &str_len), free(key_str), cleanup); + + /* create metadata */ + rc = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, str_val, + str_len * 8, NULL, LY_VALUE_CBOR, NULL, val_hints, node->schema, node); + free(str_val); + LY_CHECK_ERR_GOTO(rc, free(key_str), cleanup); + + /* add/correct flags */ + rc = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL); + LY_CHECK_ERR_GOTO(rc, free(key_str), cleanup); + } + else + { + /* create attribute */ + const char *module_name; + size_t module_name_len; + char *str_val = NULL; + size_t str_len = 0; + + lydcbor_get_node_prefix(node, prefix, prefix_len, &module_name, &module_name_len); + + LY_CHECK_ERR_GOTO(rc = lydcbor_item_to_string(value_item, &str_val, &str_len), free(key_str), cleanup); + + rc = lyd_create_attr(node, NULL, ctx, name, name_len, prefix, prefix_len, module_name, + module_name_len, str_val, str_len, NULL, LY_VALUE_CBOR, NULL, val_hints); + free(str_val); + LY_CHECK_ERR_GOTO(rc, free(key_str), cleanup); + } + + free(key_str); + } + + if (nodetype == LYS_LEAFLIST && instance < cbor_array_size(cbor_meta)) + { + prev = node; + node = node->next; + goto next_entry; + } + + goto cleanup; + +representation_error: + LOGVAL(ctx, NULL, LYVE_SYNTAX, + "The attribute(s) of %s \"%s\" is expected to be represented as CBOR %s, but input data contains different type.", + lys_nodetype2str(nodetype), node ? LYD_NAME(node) : LYD_NAME(prev), expected); + rc = LY_EVALID; + +cleanup: + if ((rc == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) + { + if ((r = lydcbor_data_skip(lydctx->cborctx))) + { + rc = r; + } + } + LOG_LOCBACK(snode ? 1 : 0); + return rc; +} + +/** + * @brief Maintain children - insert node and update first pointer. + * + * @param[in] parent Parent node to insert to. + * @param[in,out] first_p Pointer to the first sibling. + * @param[in,out] node_p Pointer to the node to insert. + * @param[in] last If set, insert at the end. + * @param[in] ext Extension instance. + */ +static void +lydcbor_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) + { + return; + } + + 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; + } + } + } + *node_p = NULL; +} + +/** + * @brief Create an opaq node from CBOR. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] name Name of the opaq node. + * @param[in] name_len Length of @p name. + * @param[in] prefix Prefix of the opaq node. + * @param[in] prefix_len Length of @p prefix. + * @param[in] parent Data parent. + * @param[in] cbor_value CBOR value item. + * @param[out] node_p Pointer to the created opaq node. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_create_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len, + struct lyd_node *parent, const cbor_item_t *cbor_value, struct lyd_node **node_p) +{ + LY_ERR ret = LY_SUCCESS; + const char *value = NULL, *module_name; + size_t value_len = 0, module_name_len = 0; + ly_bool dynamic = 0; + uint32_t type_hint = 0; + char *str_val = NULL; + + if (cbor_typeof(cbor_value) != CBOR_TYPE_MAP) + { + /* prepare for creating opaq node with a value */ + LY_CHECK_RET(lydcbor_item_to_string(cbor_value, &str_val, &value_len)); + value = str_val; + dynamic = 1; + + LY_CHECK_GOTO(ret = lydcbor_value_type_hint(lydctx, cbor_value, &type_hint), cleanup); + } + + /* get the module name */ + lydcbor_get_node_prefix(parent, prefix, prefix_len, &module_name, &module_name_len); + if (!module_name && !parent && lydctx->any_schema) + { + module_name = lydctx->any_schema->module->name; + module_name_len = strlen(module_name); + } + + /* create node */ + ret = lyd_create_opaq(lydctx->cborctx->ctx, name, name_len, prefix, prefix_len, module_name, module_name_len, value, + value_len, &dynamic, LY_VALUE_CBOR, NULL, type_hint, node_p); + +cleanup: + if (dynamic) + { + free((char *)value); + } + return ret; +} + +static LY_ERR lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj); + +/** + * @brief Parse opaq node from CBOR. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] name Name of the opaq node. + * @param[in] name_len Length of @p name. + * @param[in] prefix Prefix of the opaq node. + * @param[in] prefix_len Length of @p prefix. + * @param[in] parent Data parent. + * @param[in] cbor_value CBOR value item. + * @param[in,out] first_p First top-level/parent sibling. + * @param[out] node_p Pointer to the created opaq node. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len, + struct lyd_node *parent, const cbor_item_t *cbor_value, struct lyd_node **first_p, struct lyd_node **node_p) +{ + LY_ERR ret = LY_SUCCESS; + enum cbor_type type = cbor_typeof(cbor_value); + + LY_CHECK_GOTO(ret = lydcbor_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, cbor_value, node_p), cleanup); + + assert(*node_p); + /* dnode-only location no longer supported by LOG_LOCSET */ + + if ((type == CBOR_TYPE_ARRAY) && (cbor_array_size(cbor_value) == 1)) + { + cbor_item_t **handle = cbor_array_handle(cbor_value); + if (lydcbor_is_null(handle[0])) + { + /* special array null value */ + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_VALHINT_EMPTY; + goto finish; + } + } + + if (type == CBOR_TYPE_ARRAY) + { + /* process array */ + size_t array_size = cbor_array_size(cbor_value); + cbor_item_t **array_handle = cbor_array_handle(cbor_value); + + for (size_t i = 0; i < array_size; ++i) + { + const cbor_item_t *item = array_handle[i]; + + if (cbor_typeof(item) == CBOR_TYPE_MAP) + { + /* array with objects, list */ + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST; + + /* process children */ + LY_CHECK_GOTO(ret = lydcbor_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL, item), cleanup); + } + else + { + /* array with values, leaf-list */ + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LEAFLIST; + } + + if (i < array_size - 1) + { + /* continue with next instance */ + assert(*node_p); + lydcbor_maintain_children(parent, first_p, node_p, + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL); + + LOG_LOCBACK(0); + + LY_CHECK_GOTO(ret = lydcbor_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, item, node_p), cleanup); + + assert(*node_p); + /* dnode-only location no longer supported by LOG_LOCSET */ + } + } + } + else if (type == CBOR_TYPE_MAP) + { + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_CONTAINER; + /* process children */ + LY_CHECK_GOTO(ret = lydcbor_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL, cbor_value), cleanup); + } + +finish: + /* finish linking metadata */ + ret = lydcbor_metadata_finish(lydctx, lyd_node_child_p(*node_p)); + +cleanup: + if (*node_p) + { + LOG_LOCBACK(0); + } + return ret; +} + +/** + * @brief Process the attribute container (starting by @). + * + * @param[in] lydctx CBOR data parser context. + * @param[in] attr_node The data node referenced by the attribute container. + * @param[in] snode The schema node of the data node. + * @param[in] name Name of the node. + * @param[in] name_len Length of @p name. + * @param[in] prefix Prefix of the node. + * @param[in] prefix_len Length of @p prefix. + * @param[in] parent Data parent. + * @param[in] cbor_value CBOR value item. + * @param[in,out] first_p First top-level/parent sibling. + * @param[out] node_p Pointer to the created node. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_parse_attribute(struct lyd_cbor_ctx *lydctx, struct lyd_node *attr_node, const struct lysc_node *snode, + const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct lyd_node **node_p) +{ + LY_ERR r; + const char *opaq_name, *mod_name, *attr_mod; + size_t opaq_name_len, attr_mod_len; + + if (!attr_node) + { + /* learn the attribute module name */ + if (!snode) + { + if (!prefix) + { + lydcbor_get_node_prefix(parent, NULL, 0, &attr_mod, &attr_mod_len); + } + else + { + attr_mod = prefix; + attr_mod_len = prefix_len; + } + } + + /* try to find the instance */ + LY_LIST_FOR(parent ? lyd_child(parent) : *first_p, attr_node) + { + if (snode) + { + if (attr_node->schema) + { + if (attr_node->schema == snode) + { + break; + } + } + else + { + mod_name = ((struct lyd_node_opaq *)attr_node)->name.module_name; + if (!strcmp(LYD_NAME(attr_node), snode->name) && mod_name && !strcmp(mod_name, snode->module->name)) + { + break; + } + } + } + else + { + if (attr_node->schema) + { + mod_name = attr_node->schema->module->name; + } + else + { + mod_name = ((struct lyd_node_opaq *)attr_node)->name.module_name; + } + + if (!ly_strncmp(LYD_NAME(attr_node), name, name_len) && mod_name && attr_mod && + !ly_strncmp(mod_name, attr_mod, attr_mod_len)) + { + break; + } + } + } + } + + if (!attr_node) + { + /* parse as an opaq node with @ prefix */ + uint32_t prev_opts; + + prev_opts = lydctx->parse_opts; + lydctx->parse_opts &= ~LYD_PARSE_STRICT; + lydctx->parse_opts |= LYD_PARSE_OPAQ; + + opaq_name = prefix ? prefix - 1 : name - 1; + opaq_name_len = prefix ? prefix_len + name_len + 2 : name_len + 1; + r = lydcbor_parse_opaq(lydctx, opaq_name, opaq_name_len, NULL, 0, parent, cbor_value, first_p, node_p); + + lydctx->parse_opts = prev_opts; + LY_CHECK_RET(r); + } + else + { + LY_CHECK_RET(lydcbor_meta_attr(lydctx, snode, attr_node, cbor_value)); + } + + return LY_SUCCESS; +} + +/** + * @brief Parse a single anydata/anyxml node from CBOR. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] snode Schema node corresponding to the member. + * @param[in] ext Extension instance of @p snode, if any. + * @param[in] cbor_value CBOR value item. + * @param[out] node Parsed data (or opaque) node. + * @return LY_SUCCESS if a node was successfully parsed. + * @return LY_ENOT in case of invalid CBOR encoding. + * @return LY_ERR on other errors. + */ +static LY_ERR +lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext, + const cbor_item_t *cbor_value, struct lyd_node **node) +{ + LY_ERR r, rc = LY_SUCCESS; + uint32_t prev_parse_opts = lydctx->parse_opts, prev_int_opts = lydctx->int_opts; + struct lyd_node *child = NULL; + ly_bool log_node = 0; + enum cbor_type type = cbor_typeof(cbor_value); + + assert(snode->nodetype & LYD_NODE_ANY); + + *node = NULL; + + /* status check according to allowed CBOR types */ + if (snode->nodetype == LYS_ANYXML) + { + LY_CHECK_RET((type != CBOR_TYPE_MAP) && (type != CBOR_TYPE_ARRAY) && (type != CBOR_TYPE_UINT) && + (type != CBOR_TYPE_NEGINT) && (type != CBOR_TYPE_STRING) && (type != CBOR_TYPE_FLOAT_CTRL), + LY_ENOT); + } + else + { + LY_CHECK_RET(type != CBOR_TYPE_MAP, LY_ENOT); + } + + /* create any node */ + if (type == CBOR_TYPE_MAP) + { + /* create node */ + r = lyd_create_any(snode, NULL, NULL, 0, 1, 0, node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + assert(*node); + /* dnode-only location no longer supported by LOG_LOCSET */ + log_node = 1; + + /* parse data tree with correct options */ + 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; + lydctx->any_schema = snode; + + /* process the anydata content */ + r = lydcbor_subtree_r(lydctx, NULL, &child, NULL, cbor_value); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + /* finish linking metadata */ + r = lydcbor_metadata_finish(lydctx, &child); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + /* assign the data tree */ + ((struct lyd_node_any *)*node)->child = child; + child = NULL; + } + else + { + /* convert CBOR scalar value to string and store */ + char *str_val = NULL; + size_t str_len = 0; + + r = lydcbor_item_to_string(cbor_value, &str_val, &str_len); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + r = lyd_create_any(snode, NULL, str_val, LYD_VALHINT_STRING, 1, 0, node); + LY_CHECK_ERR_GOTO(r, free(str_val); rc = r, cleanup); + } + +cleanup: + if (log_node) + { + LOG_LOCBACK(0); + } + lydctx->parse_opts = prev_parse_opts; + lydctx->int_opts = prev_int_opts; + lydctx->any_schema = NULL; + lyd_free_tree(child); + return rc; +} + +/** + * @brief Parse a single instance of an inner node from CBOR. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] snode Schema node corresponding to the member. + * @param[in] ext Extension instance of @p snode, if any. + * @param[in] cbor_value CBOR value item. + * @param[out] node Parsed data (or opaque) node. + * @return LY_SUCCESS if a node was successfully parsed. + * @return LY_ENOT in case of invalid CBOR encoding. + * @return LY_ERR on other errors. + */ +static LY_ERR +lydcbor_parse_instance_inner(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext, + const cbor_item_t *cbor_value, struct lyd_node **node) +{ + LY_ERR r, rc = LY_SUCCESS; + uint32_t prev_parse_opts = lydctx->parse_opts; + + LY_CHECK_RET(cbor_typeof(cbor_value) != CBOR_TYPE_MAP, LY_ENOT); + + /* create inner node */ + LY_CHECK_RET(lyd_create_inner(snode, node)); + + /* use it for logging */ + /* dnode-only location no longer supported by LOG_LOCSET */ + + if (ext) + { + /* only parse these extension data and validate afterwards */ + lydctx->parse_opts |= LYD_PARSE_ONLY; + } + + /* process children */ + r = lydcbor_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL, cbor_value); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + /* finish linking metadata */ + r = lydcbor_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 */ + r = lyd_parser_check_keys(*node); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + + if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) + { + /* new node validation */ + r = lyd_parser_validate_new_implicit((struct lyd_ctx *)lydctx, *node); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + +cleanup: + lydctx->parse_opts = prev_parse_opts; + LOG_LOCBACK(0); + if (!(*node)->hash) + { + /* list without keys is unusable */ + lyd_free_tree(*node); + *node = NULL; + } + return rc; +} + +/** + * @brief Parse a single instance of a node from CBOR. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] parent Data parent of the subtree. + * @param[in,out] first_p Pointer to the variable holding the first top-level sibling. + * @param[in] snode Schema node corresponding to the member. + * @param[in] ext Extension instance of @p snode, if any. + * @param[in] name Parsed CBOR node name. + * @param[in] name_len Length of @p name. + * @param[in] prefix Parsed CBOR node prefix. + * @param[in] prefix_len Length of @p prefix. + * @param[in] cbor_value CBOR value item. + * @param[out] node Parsed data (or opaque) node. + * @return LY_SUCCESS if a node was successfully parsed. + * @return LY_ENOT in case of invalid CBOR encoding. + * @return LY_ERR on other errors. + */ +static LY_ERR +lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, + const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len, + const char *prefix, size_t prefix_len, const cbor_item_t *cbor_value, struct lyd_node **node) +{ + LY_ERR r, rc = LY_SUCCESS; + uint32_t type_hints = 0; + char *str_val = NULL; + size_t str_len = 0; + + LOG_LOCSET(snode); + + r = lydcbor_data_check_opaq(lydctx, snode, cbor_value, &type_hints); + if (r == LY_SUCCESS) + { + assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY)); + if (lydcbor_is_null(cbor_value)) + { + /* do not do anything if value is CBOR null */ + goto cleanup; + } + else if (snode->nodetype & LYD_NODE_TERM) + { + enum cbor_type type = cbor_typeof(cbor_value); + + if ((type == CBOR_TYPE_ARRAY) && (cbor_array_size(cbor_value) == 1)) + { + cbor_item_t **handle = cbor_array_handle(cbor_value); + if (lydcbor_is_null(handle[0])) + { + /* [null] case */ + goto cleanup; + } + } + + if ((type != CBOR_TYPE_ARRAY) && (type != CBOR_TYPE_UINT) && (type != CBOR_TYPE_NEGINT) && + (type != CBOR_TYPE_STRING) && (type != CBOR_TYPE_FLOAT_CTRL)) + { + rc = LY_ENOT; + goto cleanup; + } + + /* create terminal node */ + r = lydcbor_item_to_string(cbor_value, &str_val, &str_len); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, parent, str_val, str_len * 8, + NULL, LY_VALUE_CBOR, NULL, type_hints, node); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + else if (snode->nodetype & LYD_NODE_INNER) + { + /* create inner node */ + r = lydcbor_parse_instance_inner(lydctx, snode, ext, cbor_value, node); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + else + { + /* create any node */ + r = lydcbor_parse_any(lydctx, snode, ext, cbor_value, node); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + LY_CHECK_GOTO(!*node, cleanup); + + /* add/correct flags */ + r = lyd_parser_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 */ + r = lyd_validate_node_ext(*node, &lydctx->ext_node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + } + } + else if (r == LY_ENOT) + { + /* parse it again as an opaq node */ + r = lydcbor_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, cbor_value, 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; + } + else if (snode->nodetype == LYS_LEAFLIST) + { + ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LEAFLIST; + } + } + else + { + /* error */ + rc = r; + goto cleanup; + } + +cleanup: + free(str_val); + LOG_LOCBACK(1); + return rc; +} + +/** + * @brief Parse CBOR subtree. All leaf-list and list instances of a node are considered one subtree. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] parent Data parent of the subtree, must be set if @p first_p is not. + * @param[in,out] first_p Pointer to the variable holding the first top-level sibling. + * @param[in,out] parsed Optional set to add all the parsed siblings into. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, + struct ly_set *parsed, const cbor_item_t *cbor_obj) +{ + LY_ERR r, rc = LY_SUCCESS; + const char *name, *prefix = NULL, *expected = NULL; + size_t name_len, prefix_len = 0; + ly_bool is_meta = 0; + const struct lysc_node *snode = NULL; + struct lysc_ext_instance *ext = NULL; + struct lyd_node *node = NULL, *attr_node = NULL; + const struct ly_ctx *ctx = lydctx->cborctx->ctx; + struct cbor_pair *pairs; + size_t map_size; + + assert(parent || first_p); + assert(cbor_typeof(cbor_obj) == CBOR_TYPE_MAP); + + map_size = cbor_map_size(cbor_obj); + pairs = cbor_map_handle(cbor_obj); + + if (!pairs && map_size > 0) + { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Invalid CBOR map structure"); + return LY_EVALID; + } + + /* process all members */ + for (size_t i = 0; i < map_size; ++i) + { + const cbor_item_t *key_item = pairs[i].key; + const cbor_item_t *value_item = pairs[i].value; + char *key_str = NULL; + size_t key_len = 0; + + if (!key_item || !value_item) + { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Null key or value in CBOR map"); + rc = LY_EVALID; + goto cleanup; + } + + /* Skip null values */ + if (lydcbor_is_null(value_item)) + { + continue; + } + + /* Get key string */ + if (!cbor_isa_string(key_item)) + { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "CBOR map key must be string for named identifier format"); + rc = LY_EVALID; + goto cleanup; + } + + LY_CHECK_ERR_GOTO(rc = lydcbor_item_to_string(key_item, &key_str, &key_len), free(key_str), cleanup); + + /* process the node name */ + lydcbor_parse_name(key_str, key_len, &name, &name_len, &prefix, &prefix_len, &is_meta); + + if (!is_meta || name_len || prefix_len) + { + /* get the schema node */ + r = lydcbor_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode, &ext); + if (r == LY_ENOT) + { + free(key_str); + continue; + } + else if ((r == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) + { + rc = r; + free(key_str); + continue; + } + else if (r) + { + rc = r; + free(key_str); + goto cleanup; + } + } + + if (is_meta) + { + /* parse as metadata */ + if (!name_len && !prefix_len && !parent) + { + LOGVAL(ctx, NULL, LYVE_SYNTAX, + "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries."); + r = LY_EVALID; + free(key_str); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + else if (!name_len && !prefix_len) + { + /* parent's metadata without a name */ + attr_node = parent; + snode = attr_node->schema; + } + r = lydcbor_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, + value_item, first_p, &node); + free(key_str); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + } + else if (!snode) + { + if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) + { + /* skip element */ + free(key_str); + continue; + } + else + { + /* parse as an opaq node */ + if (name_len == 0) + { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "CBOR object member name cannot be a zero-length string."); + r = LY_EVALID; + free(key_str); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + + /* parse opaq */ + r = lydcbor_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, value_item, first_p, &node); + free(key_str); + 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 */ + + /* set expected representation */ + switch (snode->nodetype) + { + case LYS_LEAFLIST: + expected = "name/array of values"; + break; + case LYS_LIST: + expected = "name/array of objects"; + break; + case LYS_LEAF: + expected = "name/value"; + break; + case LYS_CONTAINER: + case LYS_NOTIF: + case LYS_ACTION: + case LYS_RPC: + case LYS_ANYDATA: + expected = "name/object"; + break; + case LYS_ANYXML: + expected = "name/value"; + break; + } + + /* check the representation and process */ + enum cbor_type value_type = cbor_typeof(value_item); + + switch (snode->nodetype) + { + case LYS_LEAFLIST: + case LYS_LIST: + if (value_type != CBOR_TYPE_ARRAY) + { + goto representation_error; + } + + /* process all values/objects in array */ + size_t array_size = cbor_array_size(value_item); + cbor_item_t **array_handle = cbor_array_handle(value_item); + + for (size_t j = 0; j < array_size; ++j) + { + const cbor_item_t *item = array_handle[j]; + + r = lydcbor_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len, + item, &node); + if (r == LY_ENOT) + { + free(key_str); + goto representation_error; + } + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + lydcbor_maintain_children(parent, first_p, &node, + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); + } + break; + case LYS_LEAF: + case LYS_CONTAINER: + case LYS_NOTIF: + case LYS_ACTION: + case LYS_RPC: + case LYS_ANYDATA: + case LYS_ANYXML: + /* process the value/object */ + r = lydcbor_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len, + value_item, &node); + if (r == LY_ENOT) + { + free(key_str); + goto representation_error; + } + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) + { + /* remember the RPC/action/notification */ + lydctx->op_node = node; + } + break; + } + } + + free(key_str); + + /* remember a successfully parsed node */ + if (parsed && node) + { + ly_set_add(parsed, node, 1, NULL); + } + + /* finally connect the parsed node */ + lydcbor_maintain_children(parent, first_p, &node, + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); + } + + /* success */ + goto cleanup; + +representation_error: + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Expecting CBOR %s but %s \"%s\" is represented in input data differently.", + expected, lys_nodetype2str(snode->nodetype), snode->name); + rc = LY_EVALID; + if (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) + { + /* try to skip the invalid data */ + if ((r = lydcbor_data_skip(lydctx->cborctx))) + { + rc = r; + } + } + +cleanup: + lyd_free_tree(node); + return rc; +} + +/** + * @brief Common start of CBOR parser processing different types of input data. + * + * @param[in] ctx libyang context. + * @param[in] in Input structure. + * @param[in] parse_opts Options for parser. + * @param[in] val_opts Options for validation phase. + * @param[out] lydctx_p Data parser context to finish validation. + * @return LY_ERR value. + */ +static LY_ERR +lyd_parse_cbor_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, + struct lyd_cbor_ctx **lydctx_p) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_cbor_ctx *lydctx; + enum cbor_type cbortype; + + assert(lydctx_p); + + /* init context */ + lydctx = calloc(1, sizeof *lydctx); + LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM); + lydctx->parse_opts = parse_opts; + lydctx->val_opts = val_opts; + lydctx->free = lyd_cbor_ctx_free; + + /* Create low-level CBOR context */ + LY_CHECK_ERR_RET(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), free(lydctx), ret); + cbortype = cbor_typeof(lydctx->cborctx->cbor_data); + + if (!cbor_isa_map(lydctx->cborctx->cbor_data)) + { + /* expecting top-level map */ + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Expected top-level CBOR map, but %d found.", cbortype); + *lydctx_p = NULL; + lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); + return LY_EVALID; + } + + *lydctx_p = lydctx; + return ret; +} + +LY_ERR +lyd_parse_cbor(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, uint32_t int_opts, + struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p) +{ + LY_ERR r, rc = LY_SUCCESS; + struct lyd_cbor_ctx *lydctx = NULL; + + rc = lyd_parse_cbor_init(ctx, in, parse_opts, val_opts, &lydctx); + LY_CHECK_GOTO(rc, cleanup); + + lydctx->int_opts = int_opts; + lydctx->ext = ext; + + + /* 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) */ + r = lydcbor_subtree_r(lydctx, parent, first_p, parsed, lydctx->cborctx->cbor_data); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + if ((int_opts & LYD_INTOPT_NO_SIBLINGS)) + { + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Unexpected sibling node."); + 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, NULL, LYVE_DATA, "Missing the operation node."); + r = LY_EVALID; + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + + /* finish linking metadata */ + r = lydcbor_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + /* parse_opts & LYD_PARSE_SUBTREE not implemented */ + (void)subtree_sibling; + +cleanup: + /* there should be no unresolved types stored */ + 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_cbor_ctx_free((struct lyd_ctx *)lydctx); + lydctx = NULL; + } + else + { + *lydctx_p = (struct lyd_ctx *)lydctx; + lycbor_ctx_free(lydctx->cborctx); + lydctx->cborctx = NULL; + } + return rc; +} diff --git a/src/parser_data.h b/src/parser_data.h index 3941354c5..a423c2892 100644 --- a/src/parser_data.h +++ b/src/parser_data.h @@ -40,6 +40,12 @@ struct ly_in; * can be found in [RFC 7951](http://tools.ietf.org/html/rfc7951). The specification does not cover RPCs, actions and * Notifications, so the representation of these data trees is proprietary and corresponds to the representation of these * trees in XML. + * + * - CBOR + * + * The reference documentation would be `Encoding of Data Modeled with YANG in the Concise Binary Object + * Representation (CBOR)` : [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254). < $TODO$ Look at the edge cases of + * RPCs, actions and Notifications and maybe like json only - where we make a proprietary representation> * * While the parsers themselves process the input data only syntactically, all the parser functions actually incorporate * the [common validator](@ref howtoDataValidation) checking the input data semantically. Therefore, the parser functions @@ -261,6 +267,23 @@ LIBYANG_API_DECL LY_ERR lyd_parse_data_mem(const struct ly_ctx *ctx, const char uint32_t validate_options, struct lyd_node **tree); /** + * @brief Parse data from a memory buffer with a specified length. + * + * This function parses the provided data buffer of a given length and returns the resulting data tree. + * + * @param[in] ctx libyang context for parsing. + * @param[in] data Pointer to the memory buffer containing the data to parse. + * @param[in] data_len Length of the memory buffer. + * @param[in] format Data format (e.g., XML, JSON, LYD_LYB). + * @param[in] options Parsing options, see @ref dataparseroptions. + * @param[in] ctx_node Optional context node for parsing (can be NULL). + * @param[out] tree Pointer to the resulting data tree (set on success). + * @return LY_ERR value indicating success or error reason. + */ +LIBYANG_API_DECL LY_ERR lyd_parse_data_mem_len(const struct ly_ctx *ctx, const char *data, size_t data_len, LYD_FORMAT format, + uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree); + + /** * @brief Parse (and validate) input data as a YANG data tree. * * Wrapper around ::lyd_parse_data() hiding work with the input handler and some obscure options. diff --git a/src/parser_internal.h b/src/parser_internal.h index ece6f9fe5..520ab957f 100644 --- a/src/parser_internal.h +++ b/src/parser_internal.h @@ -22,6 +22,7 @@ struct lyd_ctx; struct ly_in; +struct lycbor_ctx; struct lysp_ext_substmt; struct lysp_stmt; struct lysp_yang_ctx; @@ -175,6 +176,32 @@ struct lyd_lyb_ctx { }; }; +/** + * @brief Internal context for CBOR data parser. + */ +struct lyd_cbor_ctx { + uint32_t parse_opts; /**< various @ref dataparseroptions. */ + uint32_t val_opts; /**< various @ref datavalidationoptions. */ + uint32_t int_opts; /**< internal parser options */ + uint32_t path_len; /**< used bytes in the path buffer */ + char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */ + struct ly_set node_when; /**< set of nodes with "when" conditions */ + struct ly_set node_types; /**< set of nodes with unresolved types */ + struct ly_set meta_types; /**< set of metadata with unresolved types */ + struct ly_set ext_val; /**< set of nested extension data to validate */ + struct lyd_node *op_node; /**< if an operation is being parsed, its node */ + const struct lys_module *val_getnext_ht_mod; + struct ly_ht *val_getnext_ht; + + /* callbacks */ + lyd_ctx_free_clb free; /**< destructor */ + + const struct lysc_ext_instance *ext; /**< extension instance possibly changing document root context, NULL if none */ + struct ly_set ext_node; /**< set of nodes with extension instances to validate */ + struct lycbor_ctx *cborctx; /**< CBOR context for low-level operations */ + const struct lysc_node *any_schema; /**< parent anyxml/anydata schema node if parsing nested data tree */ +}; + /** * @brief Parsed extension instance data to validate. */ @@ -325,6 +352,29 @@ LY_ERR lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent LY_ERR lyd_parse_lyb(const struct ly_ctx *ctx, struct lyd_node *parent, 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, struct lyd_ctx **lydctx_p); +/** + * @brief Parse CBOR data into libyang data tree. + * + * This function mirrors the signature and behavior of lyd_parse_json() but handles + * CBOR input instead. It supports both named identifier and SID formats. + * + * @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 to read from. + * @param[in] parse_opts Options for parser, see @ref dataparseroptions. + * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions. + * @param[in] int_opts Internal data parser options. + * @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_cbor(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, uint32_t int_opts, + struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p); + /** * @brief Validate eventTime date-and-time value. * diff --git a/src/plugins_types.c b/src/plugins_types.c index 2f7a95bd7..7e1d4e30f 100644 --- a/src/plugins_types.c +++ b/src/plugins_types.c @@ -151,6 +151,7 @@ ly_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix prefix = ly_xml_get_prefix(mod, prefix_data); break; case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: prefix = ly_json_get_prefix(mod, prefix_data); @@ -759,6 +760,7 @@ lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, uint32_t valu break; case LY_VALUE_CANON: case LY_VALUE_LYB: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_STR_NS: prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT; diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c index 97b166a4e..512a7b643 100644 --- a/src/plugins_types/instanceid.c +++ b/src/plugins_types/instanceid.c @@ -71,6 +71,7 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr inherit_prefix = 0; break; case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: diff --git a/src/plugins_types/instanceid_keys.c b/src/plugins_types/instanceid_keys.c index 6a129f93c..f21f81c75 100644 --- a/src/plugins_types/instanceid_keys.c +++ b/src/plugins_types/instanceid_keys.c @@ -195,6 +195,7 @@ lyplg_type_store_instanceid_keys(const struct ly_ctx *ctx, const struct lysc_typ switch (format) { case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: diff --git a/src/plugins_types/node_instanceid.c b/src/plugins_types/node_instanceid.c index 85ae923dc..ff5341f86 100644 --- a/src/plugins_types/node_instanceid.c +++ b/src/plugins_types/node_instanceid.c @@ -75,6 +75,7 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi inherit_prefix = 0; break; case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: @@ -197,6 +198,7 @@ lyplg_type_store_node_instanceid(const struct ly_ctx *ctx, const struct lysc_typ break; case LY_VALUE_CANON: case LY_VALUE_LYB: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_STR_NS: prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT; diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c index 18171e12f..a4a47393d 100644 --- a/src/plugins_types/xpath1.0.c +++ b/src/plugins_types/xpath1.0.c @@ -296,6 +296,7 @@ lyplg_type_store_xpath10(const struct ly_ctx *ctx, const struct lysc_type *type, switch (format) { case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: diff --git a/src/printer_cbor.c b/src/printer_cbor.c new file mode 100644 index 000000000..cde213083 --- /dev/null +++ b/src/printer_cbor.c @@ -0,0 +1,1308 @@ +/** + * @file printer_cbor.c + * @author Meher Rushi + * @brief CBOR printer for libyang data structure + * + * Copyright (c) 2026 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 +#include +#include +#include + +#include "context.h" +#include "log.h" +#include "ly_common.h" +#include "out.h" +#include "out_internal.h" +#include "parser_data.h" +#include "plugins_exts/metadata.h" +#include "plugins_internal.h" +#include "plugins_types.h" +#include "printer_data.h" +#include "printer_internal.h" +#include "set.h" +#include "tree.h" +#include "tree_data.h" +#include "tree_schema.h" +#include "lcbor.h" + +/** + * @brief CBOR printer context. + */ +struct cborpr_ctx { + struct ly_out *out; /**< output specification */ + const struct lyd_node *root; /**< root node of the subtree being printed */ + const struct lyd_node *parent; /**< parent of the node being printed */ + uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */ + const struct ly_ctx *ctx; /**< libyang context */ + + struct ly_set open; /**< currently open array(s) */ + const struct lyd_node *first_leaflist; /**< first printed leaf-list instance, used when printing its metadata/attributes */ + + cbor_item_t *root_map; /**< root CBOR map */ + cbor_item_t *array; /**< currently open CBOR array for leaf-list/list instances */ +}; + +static LY_ERR cbor_print_node(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map); + +/** + * @brief Compare 2 nodes, despite it is regular data node or an opaq node, and + * decide if they corresponds to the same schema node. + * + * @return 1 - matching nodes, 0 - non-matching nodes + */ +static int +matching_node(const struct lyd_node *node1, const struct lyd_node *node2) +{ + assert(node1 || node2); + + if (!node1 || !node2) { + return 0; + } else if (node1->schema != node2->schema) { + return 0; + } + if (!node1->schema) { + /* compare node names */ + struct lyd_node_opaq *onode1 = (struct lyd_node_opaq *)node1; + struct lyd_node_opaq *onode2 = (struct lyd_node_opaq *)node2; + + if ((onode1->name.name != onode2->name.name) || (onode1->name.prefix != onode2->name.prefix)) { + return 0; + } + } + + return 1; +} + +/** + * @brief Open a CBOR array for the specified @p node + * + * @param[in] pctx CBOR printer context. + * @param[in] node First node of the array. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_array_open(struct cborpr_ctx *pctx, const struct lyd_node *node) +{ + LY_CHECK_RET(ly_set_add(&pctx->open, (void *)node, 0, NULL)); + return LY_SUCCESS; +} + +/** + * @brief Get know if the array for the provided @p node is currently open. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to check. + * @return 1 in case the printer is currently in the array belonging to the provided @p node. + * @return 0 in case the provided @p node is not connected with the currently open array (or there is no open array). + */ +static int +is_open_array(struct cborpr_ctx *pctx, const struct lyd_node *node) +{ + if (pctx->open.count && matching_node(node, pctx->open.dnodes[pctx->open.count - 1])) { + return 1; + } else { + return 0; + } +} + +/** + * @brief Close the most inner CBOR array. + * + * @param[in] pctx CBOR printer context. + */ +static void +cbor_print_array_close(struct cborpr_ctx *pctx) +{ + ly_set_rm_index(&pctx->open, pctx->open.count - 1, NULL); +} + +/** + * @brief Get the node's module name to use as the @p node prefix in CBOR. + * + * @param[in] node Node to process. + * @return The name of the module where the @p node belongs, it can be NULL in case the module name + * cannot be determined (source format is XML and the refered namespace is unknown/not implemented in the current context). + */ +static const char * +node_prefix(const struct lyd_node *node) +{ + if (node->schema) { + return node->schema->module->name; + } else { + struct lyd_node_opaq *onode = (struct lyd_node_opaq *)node; + const struct lys_module *mod; + + switch (onode->format) { + case LY_VALUE_CBOR: + case LY_VALUE_JSON: + return onode->name.module_name; + case LY_VALUE_XML: + mod = ly_ctx_get_module_implemented_ns(onode->ctx, onode->name.module_ns); + if (!mod) { + return NULL; + } + return mod->name; + default: + /* cannot be created */ + LOGINT(LYD_CTX(node)); + } + } + + return NULL; +} + +/** + * @brief Compare 2 nodes if the belongs to the same module (if they come from the same namespace) + * + * Accepts both regulard a well as opaq nodes. + * + * @param[in] node1 The first node to compare. + * @param[in] node2 The second node to compare. + * @return 0 in case the nodes' modules are the same + * @return 1 in case the nodes belongs to different modules + */ +static int +cbor_nscmp(const struct lyd_node *node1, const struct lyd_node *node2) +{ + assert(node1 || node2); + + if (!node1 || !node2) { + return 1; + } else if (node1->schema && node2->schema) { + if (node1->schema->module == node2->schema->module) { + /* belongs to the same module */ + return 0; + } else { + /* different modules */ + return 1; + } + } else { + const char *pref1 = node_prefix(node1); + const char *pref2 = node_prefix(node2); + + if ((pref1 && pref2) && (pref1 == pref2)) { + return 0; + } else { + return 1; + } + } +} + +/** + * @brief Create CBOR member name as [prefix:]name + * + * @param[in] pctx CBOR printer context. + * @param[in] node The data node being printed. + * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier. + * @return Newly allocated string with the member name, NULL on error. + */ +static char * +cbor_print_member_name(struct cborpr_ctx *pctx, const struct lyd_node *node, ly_bool is_attr) +{ + char *name = NULL; + const char *prefix_str = node_prefix(node); + const char *node_name = node->schema->name; + + if (cbor_nscmp(node, pctx->parent)) { + /* print "namespace" */ + if (is_attr) { + if (asprintf(&name, "@%s:%s", prefix_str, node_name) == -1) { + return NULL; + } + } else { + if (asprintf(&name, "%s:%s", prefix_str, node_name) == -1) { + return NULL; + } + } + } else { + if (is_attr) { + if (asprintf(&name, "@%s", node_name) == -1) { + return NULL; + } + } else { + name = strdup(node_name); + } + } + + return name; +} + +/** + * @brief More generic alternative to cbor_print_member_name() to print some special cases of the member names. + * + * @param[in] pctx CBOR printer context. + * @param[in] parent Parent node to compare modules deciding if the prefix is printed. + * @param[in] format Format to decide how to process the @p prefix. + * @param[in] name Name structure to provide name and prefix to print. If NULL, only "" name is printed. + * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier. + * @return Newly allocated string with the member name, NULL on error. + */ +static char * +cbor_print_member_name2(struct cborpr_ctx *pctx, const struct lyd_node *parent, LY_VALUE_FORMAT format, + const struct ly_opaq_name *name, ly_bool is_attr) +{ + const char *module_name = NULL, *name_str; + char *result = NULL; + + /* determine prefix string */ + if (name) { + switch (format) { + case LY_VALUE_CBOR: + case LY_VALUE_JSON: + module_name = name->module_name; + break; + 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(pctx->ctx); + return NULL; + } + + name_str = name->name; + } else { + name_str = ""; + } + + /* create the member name */ + if (module_name && (!parent || (node_prefix(parent) != module_name))) { + if (is_attr) { + if (asprintf(&result, "@%s:%s", module_name, name_str) == -1) { + return NULL; + } + } else { + if (asprintf(&result, "%s:%s", module_name, name_str) == -1) { + return NULL; + } + } + } else { + if (is_attr) { + if (asprintf(&result, "@%s", name_str) == -1) { + return NULL; + } + } else { + result = strdup(name_str); + } + } + + return result; +} + +/** + * @brief Print data value to CBOR item. + * + * @param[in] pctx CBOR 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. + * @param[out] item_p Pointer to store the created CBOR item. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_value(struct cborpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val, + const struct lys_module *local_mod, cbor_item_t **item_p) +{ + ly_bool dynamic; + LY_DATA_TYPE basetype; + const char *value; + cbor_item_t *item = NULL; + + value = LYSC_GET_TYPE_PLG(val->realtype->plugin_ref)->print(ctx, val, LY_VALUE_CBOR, (void *)local_mod, &dynamic, NULL); + LY_CHECK_RET(!value, LY_EINVAL); + basetype = val->realtype->basetype; + +print_val: + /* leafref is not supported */ + switch (basetype) { + case LY_TYPE_UNION: + /* use the resolved type */ + val = &val->subvalue->value; + basetype = val->realtype->basetype; + goto print_val; + + case LY_TYPE_BINARY: + case LY_TYPE_STRING: + case LY_TYPE_BITS: + case LY_TYPE_ENUM: + case LY_TYPE_INST: + case LY_TYPE_IDENT: + /* string types */ + item = cbor_build_string(value); + break; + + case LY_TYPE_INT64: + case LY_TYPE_UINT64: + case LY_TYPE_DEC64: { + /* numeric types stored as strings in CBOR */ + item = cbor_build_string(value); + break; + } + + case LY_TYPE_INT8: + case LY_TYPE_INT16: + case LY_TYPE_INT32: { + /* signed integer types */ + int64_t num = strtoll(value, NULL, 10); + if (num >= 0) { + item = cbor_build_uint64(num); + } else { + item = cbor_build_negint64(-num - 1); + } + break; + } + + case LY_TYPE_UINT8: + case LY_TYPE_UINT16: + case LY_TYPE_UINT32: { + /* unsigned integer types */ + uint64_t num = strtoull(value, NULL, 10); + item = cbor_build_uint64(num); + break; + } + + case LY_TYPE_BOOL: + /* boolean */ + if (strcmp(value, "true") == 0) { + item = cbor_build_bool(true); + } else { + item = cbor_build_bool(false); + } + break; + + case LY_TYPE_EMPTY: + /* empty type is represented as [null] */ + item = cbor_new_definite_array(1); + if (item) { + cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + if (null_item) { + cbor_array_push(item, null_item); + cbor_decref(&null_item); + } + } + break; + + default: + /* error */ + LOGINT_RET(pctx->ctx); + } + + if (dynamic) { + free((char *)value); + } + + *item_p = item; + return item ? LY_SUCCESS : LY_EMEM; +} + +/** + * @brief Print all the attributes of the opaq node to CBOR map. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Opaq node where the attributes are placed. + * @param[in] attr_map CBOR map to add attributes to. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_attribute(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, cbor_item_t *attr_map) +{ + struct lyd_attr *attr; + cbor_item_t *value_item = NULL; + + for (attr = node->attr; attr; attr = attr->next) { + char *key = cbor_print_member_name2(pctx, &node->node, attr->format, &attr->name, 0); + LY_CHECK_RET(!key, LY_EMEM); + + if (attr->hints & (LYD_VALHINT_STRING | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM | LYD_VALHINT_NUM64)) { + value_item = cbor_build_string(attr->value); + } else if (attr->hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) { + if (strcmp(attr->value, "true") == 0) { + value_item = cbor_build_bool(true); + } else if (strcmp(attr->value, "false") == 0) { + value_item = cbor_build_bool(false); + } else { + /* numeric value as string */ + value_item = cbor_build_string(attr->value); + } + } else if (attr->hints & LYD_VALHINT_EMPTY) { + value_item = cbor_new_definite_array(1); + if (value_item) { + cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + if (null_item) { + cbor_array_push(value_item, null_item); + cbor_decref(&null_item); + } + } + } else { + /* unknown value format with no hints, use universal string */ + value_item = cbor_build_string(attr->value); + } + + if (!value_item) { + free(key); + return LY_EMEM; + } + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(value_item) + }; + free(key); + + if (!cbor_map_add(attr_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } + + return LY_SUCCESS; +} + +/** + * @brief Print all the metadata of the node to CBOR map. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Node where the metadata are placed. + * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed. + * @param[in] meta_map CBOR map to add metadata to. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_metadata(struct cborpr_ctx *pctx, const struct lyd_node *node, const struct lys_module *wdmod, + cbor_item_t *meta_map) +{ + struct lyd_meta *meta; + cbor_item_t *value_item = NULL; + char *key = NULL; + + if (wdmod) { + if (asprintf(&key, "%s:default", wdmod->name) == -1) { + return LY_EMEM; + } + value_item = cbor_build_bool(true); + if (!value_item) { + free(key); + return LY_EMEM; + } + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(value_item) + }; + free(key); + + if (!cbor_map_add(meta_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } + + for (meta = node->meta; meta; meta = meta->next) { + if (!lyd_metadata_should_print(meta)) { + continue; + } + + if (asprintf(&key, "%s:%s", meta->annotation->module->name, meta->name) == -1) { + return LY_EMEM; + } + + LY_CHECK_RET(cbor_print_value(pctx, LYD_CTX(node), &meta->value, NULL, &value_item)); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(value_item) + }; + free(key); + + if (!cbor_map_add(meta_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } + + return LY_SUCCESS; +} + +/** + * @brief Check if a value can be printed for at least one metadata. + * + * @param[in] node Node to check. + * @return 1 if node has printable meta otherwise 0. + */ +static ly_bool +node_has_printable_meta(const struct lyd_node *node) +{ + struct lyd_meta *iter; + + if (!node->meta) { + return 0; + } + + LY_LIST_FOR(node->meta, iter) { + if (lyd_metadata_should_print(iter)) { + return 1; + } + } + + return 0; +} + +/** + * @brief Print attributes/metadata of the given @p node to CBOR map. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node where the attributes/metadata are placed. + * @param[in] parent_map CBOR map to add the metadata to. + * @param[in] inner Flag if the @p node is an inner node in the tree. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_attributes(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map, ly_bool inner) +{ + const struct lys_module *wdmod = NULL; + cbor_item_t *meta_map = NULL; + char *key = NULL; + + if (node->schema && (node->schema->nodetype != LYS_CONTAINER) && (((node->flags & LYD_DEFAULT) && + (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) || + ((pctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node)))) { + /* 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->schema && (wdmod || node_has_printable_meta(node))) { + meta_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!meta_map, LY_EMEM); + + LY_CHECK_RET(cbor_print_metadata(pctx, node, wdmod, meta_map)); + + if (inner) { + key = cbor_print_member_name2(pctx, lyd_parent(node), LY_VALUE_JSON, NULL, 1); + } else { + key = cbor_print_member_name(pctx, node, 1); + } + LY_CHECK_RET(!key, LY_EMEM); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(meta_map) + }; + free(key); + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } else if (!node->schema && ((struct lyd_node_opaq *)node)->attr) { + meta_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!meta_map, LY_EMEM); + + LY_CHECK_RET(cbor_print_attribute(pctx, (struct lyd_node_opaq *)node, meta_map)); + + if (inner) { + key = cbor_print_member_name2(pctx, lyd_parent(node), LY_VALUE_JSON, NULL, 1); + } else { + key = cbor_print_member_name2(pctx, lyd_parent(node), ((struct lyd_node_opaq *)node)->format, + &((struct lyd_node_opaq *)node)->name, 1); + } + LY_CHECK_RET(!key, LY_EMEM); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(meta_map) + }; + free(key); + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } + + return LY_SUCCESS; +} + +/** + * @brief Print leaf data node including its metadata. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[in] parent_map CBOR map to add the leaf to. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_leaf(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map) +{ + char *key = NULL; + cbor_item_t *value_item = NULL; + + key = cbor_print_member_name(pctx, node, 0); + LY_CHECK_RET(!key, LY_EMEM); + + LY_CHECK_ERR_RET(cbor_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, + node->schema->module, &value_item), free(key), LY_EINVAL); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(value_item) + }; + + if (!cbor_map_add(parent_map, pair)) { + free(key); + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + free(key); + + /* print attributes as sibling */ + cbor_print_attributes(pctx, node, parent_map, 0); + + return LY_SUCCESS; +} + +/** + * @brief Print anydata/anyxml content to CBOR item. + * + * @param[in] pctx CBOR printer context. + * @param[in] any Anydata node to print. + * @param[out] item_p Pointer to store the created CBOR item. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_any_content(struct cborpr_ctx *pctx, struct lyd_node_any *any, cbor_item_t **item_p) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_node *iter; + const struct lyd_node *prev_parent; + uint32_t prev_opts; + cbor_item_t *content_map = NULL; + + assert(any->schema->nodetype & LYD_NODE_ANY); + + if ((any->schema->nodetype == LYS_ANYDATA) && any->value) { + LOGINT_RET(pctx->ctx); + } + + if (any->child) { + /* create a map for the content */ + content_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!content_map, LY_EMEM); + + /* print data tree */ + prev_parent = pctx->parent; + prev_opts = pctx->options; + pctx->parent = &any->node; + pctx->options &= ~LYD_PRINT_SIBLINGS; + LY_LIST_FOR(any->child, iter) { + ret = cbor_print_node(pctx, iter, content_map); + LY_CHECK_ERR_RET(ret, cbor_decref(&content_map), ret); + } + pctx->parent = prev_parent; + pctx->options = prev_opts; + + *item_p = content_map; + } else if (any->value) { + /* print as a string */ + *item_p = cbor_build_string(any->value); + } else { + /* no content */ + if (any->schema->nodetype == LYS_ANYXML) { + *item_p = cbor_build_ctrl(CBOR_CTRL_NULL); + } else { + *item_p = cbor_new_indefinite_map(); + } + } + + return LY_SUCCESS; +} + +/** + * @brief Print content of a single container/list data node including its metadata. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[out] item_p Pointer to store the created CBOR map. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_inner(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t **item_p) +{ + struct lyd_node *child; + const struct lyd_node *prev_parent; + cbor_item_t *inner_map = NULL; + + /* create map for inner node */ + inner_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!inner_map, LY_EMEM); + + /* print attributes first */ + cbor_print_attributes(pctx, node, inner_map, 1); + + /* print children */ + prev_parent = pctx->parent; + pctx->parent = node; + LY_LIST_FOR(lyd_child(node), child) { + LY_CHECK_ERR_RET(cbor_print_node(pctx, child, inner_map), cbor_decref(&inner_map), LY_EINVAL); + } + pctx->parent = prev_parent; + + *item_p = inner_map; + return LY_SUCCESS; +} + +/** + * @brief Print container data node including its metadata. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[in] parent_map CBOR map to add the container to. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_container(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map) +{ + char *key = NULL; + cbor_item_t *inner_map = NULL; + + key = cbor_print_member_name(pctx, node, 0); + LY_CHECK_RET(!key, LY_EMEM); + + LY_CHECK_ERR_RET(cbor_print_inner(pctx, node, &inner_map), free(key), LY_EINVAL); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(inner_map) + }; + + if (!cbor_map_add(parent_map, pair)) { + free(key); + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + free(key); + + return LY_SUCCESS; +} + +/** + * @brief Print anydata/anyxml data node including its metadata. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[in] parent_map CBOR map to add the anydata to. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_any(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map) +{ + char *key = NULL; + cbor_item_t *any_item = NULL; + + key = cbor_print_member_name(pctx, node, 0); + LY_CHECK_RET(!key, LY_EMEM); + + LY_CHECK_ERR_RET(cbor_print_any_content(pctx, (struct lyd_node_any *)node, &any_item), free(key), LY_EINVAL); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(any_item) + }; + + if (!cbor_map_add(parent_map, pair)) { + free(key); + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + free(key); + + /* print attributes as sibling */ + cbor_print_attributes(pctx, node, parent_map, 0); + + return LY_SUCCESS; +} + +/** + * @brief Check whether a node is the last printed instance of a (leaf-)list. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Last printed node. + * @return Whether it is the last printed instance or not. + */ +static ly_bool +cbor_print_array_is_last_inst(struct cborpr_ctx *pctx, const struct lyd_node *node) +{ + if (!is_open_array(pctx, node)) { + /* no array open */ + return 0; + } + + if ((pctx->root == node) && !(pctx->options & LYD_PRINT_SIBLINGS)) { + /* the only printed instance */ + return 1; + } + + if (!node->next || (node->next->schema != node->schema)) { + /* last instance */ + return 1; + } + + return 0; +} + +/** + * @brief Print single leaf-list or list instance. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[in] parent_map CBOR map to add the node to. + * @param[in,out] array_p Pointer to the array being built. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_leaf_list(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map, cbor_item_t **array_p) +{ + const struct lys_module *wdmod = NULL; + cbor_item_t *value_item = NULL; + cbor_item_t *inner_map = NULL; + char *key = NULL; + + if (!is_open_array(pctx, node)) { + /* start new array */ + *array_p = cbor_new_indefinite_array(); + LY_CHECK_RET(!*array_p, LY_EMEM); + + key = cbor_print_member_name(pctx, node, 0); + LY_CHECK_ERR_RET(!key, cbor_decref(array_p), LY_EMEM); + + LY_CHECK_RET(cbor_print_array_open(pctx, node)); + } + + if (node->schema->nodetype == LYS_LIST) { + /* print list's content */ + LY_CHECK_RET(cbor_print_inner(pctx, node, &inner_map)); + cbor_array_push(*array_p, inner_map); + cbor_decref(&inner_map); + } else { + assert(node->schema->nodetype == LYS_LEAFLIST); + + LY_CHECK_RET(cbor_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, + node->schema->module, &value_item)); + cbor_array_push(*array_p, value_item); + cbor_decref(&value_item); + + if (!pctx->first_leaflist) { + if (((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) || + ((pctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) { + /* 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 (wdmod || node_has_printable_meta(node)) { + /* we will be printing metadata for these siblings */ + pctx->first_leaflist = node; + } + } + } + + if (cbor_print_array_is_last_inst(pctx, node)) { + /* add completed array to parent map */ + if (key) { + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(*array_p) + }; + free(key); + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } + cbor_print_array_close(pctx); + *array_p = NULL; + } + + return LY_SUCCESS; +} + +/** + * @brief Print leaf-list's metadata or opaque nodes attributes. + * + * @param[in] pctx CBOR printer context. + * @param[in] parent_map CBOR map to add metadata to. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_meta_attr_leaflist(struct cborpr_ctx *pctx, cbor_item_t *parent_map) +{ + const struct lyd_node *prev, *node, *iter; + const struct lys_module *wdmod = NULL, *iter_wdmod; + const struct lyd_node_opaq *opaq = NULL; + cbor_item_t *meta_array = NULL; + cbor_item_t *meta_map = NULL; + char *key = NULL; + + assert(pctx->first_leaflist); + + if (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG)) { + /* get with-defaults module */ + wdmod = ly_ctx_get_module_implemented(pctx->ctx, "ietf-netconf-with-defaults"); + } + + /* node is the first instance of the leaf-list */ + for (node = pctx->first_leaflist, prev = pctx->first_leaflist->prev; + prev->next && matching_node(node, prev); + node = prev, prev = node->prev) {} + + /* create metadata array */ + meta_array = cbor_new_indefinite_array(); + LY_CHECK_RET(!meta_array, LY_EMEM); + + if (node->schema) { + key = cbor_print_member_name(pctx, node, 1); + } else { + opaq = (struct lyd_node_opaq *)node; + key = cbor_print_member_name2(pctx, lyd_parent(node), opaq->format, &opaq->name, 1); + } + LY_CHECK_ERR_RET(!key, cbor_decref(&meta_array), LY_EMEM); + + LY_LIST_FOR(node, iter) { + if (iter->schema && ((iter->flags & LYD_DEFAULT) || ((pctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(iter)))) { + iter_wdmod = wdmod; + } else { + iter_wdmod = NULL; + } + + if ((iter->schema && (node_has_printable_meta(iter) || iter_wdmod)) || (opaq && opaq->attr)) { + meta_map = cbor_new_indefinite_map(); + if (!meta_map) { + free(key); + cbor_decref(&meta_array); + return LY_EMEM; + } + + if (iter->schema) { + LY_CHECK_ERR_RET(cbor_print_metadata(pctx, iter, iter_wdmod, meta_map), + free(key); cbor_decref(&meta_array); cbor_decref(&meta_map), LY_EINVAL); + } else { + LY_CHECK_ERR_RET(cbor_print_attribute(pctx, (struct lyd_node_opaq *)iter, meta_map), + free(key); cbor_decref(&meta_array); cbor_decref(&meta_map), LY_EINVAL); + } + + cbor_array_push(meta_array, meta_map); + cbor_decref(&meta_map); + } else { + cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + cbor_array_push(meta_array, null_item); + cbor_decref(&null_item); + } + + if (!matching_node(iter, iter->next)) { + break; + } + } + + /* add metadata array to parent map */ + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(meta_array) + }; + free(key); + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + + return LY_SUCCESS; +} + +/** + * @brief Print opaq data node including its attributes. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Opaq node to print. + * @param[in] parent_map CBOR map to add the node to. + * @param[in,out] array_p Pointer to the array being built (for leaf-lists/lists). + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_opaq(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, cbor_item_t *parent_map, cbor_item_t **array_p) +{ + ly_bool first = 1, last = 1; + uint32_t hints; + char *key = NULL; + cbor_item_t *value_item = NULL; + + if (node->hints == LYD_HINT_DATA) { + /* useless and confusing hints */ + hints = 0; + } else { + hints = node->hints; + } + + if (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) { + if (node->prev->next && matching_node(node->prev, &node->node)) { + first = 0; + } + if (node->next && matching_node(&node->node, node->next)) { + last = 0; + } + } + + if (first) { + key = cbor_print_member_name2(pctx, pctx->parent, node->format, &node->name, 0); + LY_CHECK_RET(!key, LY_EMEM); + + if (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) { + *array_p = cbor_new_indefinite_array(); + LY_CHECK_ERR_RET(!*array_p, free(key), LY_EMEM); + LY_CHECK_ERR_RET(cbor_print_array_open(pctx, &node->node), free(key), LY_EINVAL); + } + } + + if (node->child || (hints & LYD_NODEHINT_LIST) || (hints & LYD_NODEHINT_CONTAINER)) { + cbor_item_t *inner_map = NULL; + LY_CHECK_ERR_RET(cbor_print_inner(pctx, &node->node, &inner_map), free(key), LY_EINVAL); + + if (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) { + cbor_array_push(*array_p, inner_map); + cbor_decref(&inner_map); + } else { + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(inner_map) + }; + free(key); + key = NULL; + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } + } else { + if (hints & LYD_VALHINT_EMPTY) { + value_item = cbor_new_definite_array(1); + if (value_item) { + cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + if (null_item) { + cbor_array_push(value_item, null_item); + cbor_decref(&null_item); + } + } + } else if ((hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) && !(hints & LYD_VALHINT_NUM64)) { + if (strcmp(node->value, "true") == 0) { + value_item = cbor_build_bool(true); + } else if (strcmp(node->value, "false") == 0) { + value_item = cbor_build_bool(false); + } else { + value_item = cbor_build_string(node->value); + } + } else { + /* string or a large number */ + value_item = cbor_build_string(node->value); + } + + LY_CHECK_ERR_RET(!value_item, free(key), LY_EMEM); + + if (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) { + cbor_array_push(*array_p, value_item); + cbor_decref(&value_item); + } else { + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(value_item) + }; + free(key); + key = NULL; + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } + + if (!(hints & LYD_NODEHINT_LEAFLIST)) { + /* attributes */ + cbor_print_attributes(pctx, (const struct lyd_node *)node, parent_map, 0); + } else if (!pctx->first_leaflist && node->attr) { + /* attributes printed later */ + pctx->first_leaflist = &node->node; + } + } + + if (last && (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) { + if (key) { + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(*array_p) + }; + free(key); + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } + cbor_print_array_close(pctx); + *array_p = NULL; + } + + if (key) { + free(key); + } + + return LY_SUCCESS; +} + +/** + * @brief Print all the types of data node including its metadata. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[in] parent_map CBOR map to add the node to. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_node(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map) +{ + + if (!lyd_node_should_print(node, pctx->options)) { + if (cbor_print_array_is_last_inst(pctx, node)) { + cbor_print_array_close(pctx); + } + return LY_SUCCESS; + } + + if (!node->schema) { + LY_CHECK_RET(cbor_print_opaq(pctx, (const struct lyd_node_opaq *)node, parent_map, &pctx->array)); + } else { + switch (node->schema->nodetype) { + case LYS_RPC: + case LYS_ACTION: + case LYS_NOTIF: + case LYS_CONTAINER: + LY_CHECK_RET(cbor_print_container(pctx, node, parent_map)); + break; + case LYS_LEAF: + LY_CHECK_RET(cbor_print_leaf(pctx, node, parent_map)); + break; + case LYS_LEAFLIST: + case LYS_LIST: + LY_CHECK_RET(cbor_print_leaf_list(pctx, node, parent_map, &pctx->array)); + break; + case LYS_ANYDATA: + case LYS_ANYXML: + LY_CHECK_RET(cbor_print_any(pctx, node, parent_map)); + break; + default: + LOGINT(pctx->ctx); + return EXIT_FAILURE; + } + } + + if (pctx->first_leaflist && !matching_node(node->next, pctx->first_leaflist)) { + cbor_print_meta_attr_leaflist(pctx, parent_map); + pctx->first_leaflist = NULL; + } + + return LY_SUCCESS; +} + +LY_ERR +cbor_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options) +{ + const struct lyd_node *node; + struct cborpr_ctx pctx = {0}; + unsigned char *buffer = NULL; + size_t buffer_size = 0; + + if (!root) { + /* empty data - print empty map */ + cbor_item_t *empty_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!empty_map, LY_EMEM); + + buffer_size = cbor_serialize_alloc(empty_map, &buffer, &buffer_size); + cbor_decref(&empty_map); + + if (buffer_size == 0) { + return LY_EMEM; + } + + ly_write_(out, (const char *)buffer, buffer_size); + free(buffer); + ly_print_flush(out); + return LY_SUCCESS; + } + + pctx.out = out; + pctx.parent = NULL; + pctx.options = options; + pctx.ctx = LYD_CTX(root); + + /* create root map */ + pctx.root_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!pctx.root_map, LY_EMEM); + + /* print content */ + LY_LIST_FOR(root, node) { + pctx.root = node; + LY_CHECK_ERR_RET(cbor_print_node(&pctx, node, pctx.root_map), + cbor_decref(&pctx.root_map); ly_set_erase(&pctx.open, NULL), LY_EINVAL); + if (!(options & LYD_PRINT_SIBLINGS)) { + break; + } + } + + /* serialize CBOR to buffer */ + buffer_size = cbor_serialize_alloc(pctx.root_map, &buffer, &buffer_size); + cbor_decref(&pctx.root_map); + + if (buffer_size == 0) { + ly_set_erase(&pctx.open, NULL); + return LY_EMEM; + } + + /* write to output */ + ly_write_(out, (const char *)buffer, buffer_size); + free(buffer); + + assert(!pctx.open.count); + ly_set_erase(&pctx.open, NULL); + + ly_print_flush(out); + return LY_SUCCESS; +} diff --git a/src/printer_data.c b/src/printer_data.c index 174131b07..0fd1775bb 100644 --- a/src/printer_data.c +++ b/src/printer_data.c @@ -38,6 +38,16 @@ lyd_print_(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, u case LYD_LYB: ret = lyb_print_data(out, root, options); break; +#ifdef ENABLE_CBOR_SUPPORT + case LYD_CBOR: + ret = cbor_print_data(out, root, options); + break; +#else + case LYD_CBOR: + LOGERR(root ? LYD_CTX(root) : NULL, LY_EINVAL, "CBOR format not supported, libcbor not found."); + ret = LY_EINVAL; + break; +#endif /* ENABLE_CBOR_SUPPORT */ case LYD_UNKNOWN: LOGERR(root ? LYD_CTX(root) : NULL, LY_EINVAL, "Unknown data output format."); ret = LY_EINVAL; diff --git a/src/printer_internal.h b/src/printer_internal.h index 5cf205e77..0f1d8b827 100644 --- a/src/printer_internal.h +++ b/src/printer_internal.h @@ -185,4 +185,14 @@ LY_ERR json_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t */ LY_ERR lyb_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options); +/** + * @brief CBOR printer of YANG data. + * + * @param[in] out Output structure. + * @param[in] root The root element of the (sub)tree to print. + * @param[in] options [Data printer flags](@ref dataprinterflags). + * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed. + */ +LY_ERR cbor_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options); + #endif /* LY_PRINTER_INTERNAL_H_ */ diff --git a/src/printer_json.c b/src/printer_json.c index 25f00c51f..f590a81d4 100644 --- a/src/printer_json.c +++ b/src/printer_json.c @@ -170,6 +170,7 @@ node_prefix(const struct lyd_node *node, const struct lysc_node *snode, const ch onode = (struct lyd_node_opaq *)node; switch (onode->format) { + case LY_VALUE_CBOR: case LY_VALUE_JSON: *mod_name = onode->name.module_name; if (data_dict) { @@ -342,6 +343,7 @@ json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VA /* determine prefix string */ if (name) { switch (format) { + case LY_VALUE_CBOR: case LY_VALUE_JSON: module_name = name->module_name; break; diff --git a/src/printer_lyb.c b/src/printer_lyb.c index cb6d1621a..2a160268e 100644 --- a/src/printer_lyb.c +++ b/src/printer_lyb.c @@ -753,6 +753,7 @@ lyb_print_prefix_data(LY_VALUE_FORMAT format, const void *prefix_data, struct ly LY_CHECK_RET(lyb_write_string(ns->uri, 0, lybctx)); } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: /* nothing to print */ diff --git a/src/printer_xml.c b/src/printer_xml.c index e04a2aaac..07d7dde78 100644 --- a/src/printer_xml.c +++ b/src/printer_xml.c @@ -129,6 +129,7 @@ xml_print_ns_opaq(struct xmlpr_ctx *pctx, LY_VALUE_FORMAT format, const struct l return xml_print_ns(pctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts); } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: if (name->module_name) { mod = ly_ctx_get_module_latest(pctx->ctx, name->module_name); diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c index 202f04f34..78d0f4c27 100644 --- a/src/schema_compile_node.c +++ b/src/schema_compile_node.c @@ -3028,6 +3028,7 @@ lysc_resolve_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t node /* use the current module */ mod = ctx->cur_mod; break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: if (!ctx_node) { diff --git a/src/tree.h b/src/tree.h index 283cea768..309c6cb1e 100644 --- a/src/tree.h +++ b/src/tree.h @@ -237,6 +237,7 @@ typedef enum { LY_VALUE_SCHEMA_RESOLVED, /**< resolved YANG schema value, prefixes map to module structures directly */ LY_VALUE_XML, /**< XML data value, prefixes map to XML namespace prefixes */ LY_VALUE_JSON, /**< JSON data value, prefixes map to module names */ + LY_VALUE_CBOR, /**< CBOR data value, prefixes map to module names (same as JSON) */ LY_VALUE_LYB, /**< LYB data binary value, prefix mapping is type-specific (but usually like JSON) */ LY_VALUE_STR_NS /**< any data format value, prefixes map to XML namespace prefixes */ } LY_VALUE_FORMAT; diff --git a/src/tree_data.c b/src/tree_data.c index 96b987fcc..14ef2e90b 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -78,6 +78,9 @@ lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format) } else if ((len >= LY_LYB_SUFFIX_LEN + 1) && !strncmp(&path[len - LY_LYB_SUFFIX_LEN], LY_LYB_SUFFIX, LY_LYB_SUFFIX_LEN)) { format = LYD_LYB; + } else if ((len >= LY_CBOR_SUFFIX_LEN + 1) && + !strncmp(&path[len - LY_CBOR_SUFFIX_LEN], LY_CBOR_SUFFIX, LY_CBOR_SUFFIX_LEN)) { + format = LYD_CBOR; } /* else still unknown */ } @@ -131,6 +134,16 @@ lyd_parse(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **f case LYD_LYB: r = lyd_parse_lyb(ctx, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, &lydctx); break; +#ifdef ENABLE_CBOR_SUPPORT + case LYD_CBOR: + r = lyd_parse_cbor(ctx, NULL, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx); + break; +#else + case LYD_CBOR: + LOGARG(ctx, format); + r = LY_EINVAL; + break; +#endif /* ENABLE_CBOR_SUPPORT */ case LYD_UNKNOWN: LOGARG(ctx, format); r = LY_EINVAL; @@ -206,6 +219,22 @@ lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in * return lyd_parse(ctx, parent, tree, in, format, parse_options, validate_options, NULL); } +LIBYANG_API_DEF LY_ERR +lyd_parse_data_mem_len(const struct ly_ctx *ctx, const char *data, size_t data_len, LYD_FORMAT format, + uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree) +{ + LY_ERR ret; + struct ly_in *in; + + LY_CHECK_RET(ly_in_new_memory(data, &in)); + in->length = data_len; // Set the length for the input + + ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree); + + ly_in_free(in, 0); + return ret; +} + LIBYANG_API_DEF LY_ERR lyd_parse_data_mem(const struct ly_ctx *ctx, const char *data, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree) @@ -460,6 +489,16 @@ lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in case LYD_LYB: rc = lyd_parse_lyb(ctx, parent, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); break; +#ifdef ENABLE_CBOR_SUPPORT + case LYD_CBOR: + rc = lyd_parse_cbor(ctx, NULL, parent, &first, in, parse_options, val_opts, int_opts, &parsed, NULL, &lydctx); + break; +#else + case LYD_CBOR: + LOGARG(ctx, format); + rc = LY_EINVAL; + break; +#endif /* ENABLE_CBOR_SUPPORT */ case LYD_UNKNOWN: LOGARG(ctx, format); rc = LY_EINVAL; diff --git a/src/tree_data.h b/src/tree_data.h index badbd77e8..7f5316418 100644 --- a/src/tree_data.h +++ b/src/tree_data.h @@ -536,9 +536,26 @@ typedef enum { LYD_UNKNOWN = 0, /**< unknown data format, invalid value */ LYD_XML, /**< XML instance data format */ LYD_JSON, /**< JSON instance data format */ - LYD_LYB /**< LYB instance data format */ + LYD_LYB, /**< LYB instance data format */ + LYD_CBOR /**< CBOR instance data format */ } LYD_FORMAT; +/** + * @brief List of possible value types stored in ::lyd_node_any. + */ +typedef enum { + LYD_ANYDATA_DATATREE, /**< Value is a pointer to ::lyd_node structure (first sibling). When provided as input + parameter, the pointer is directly connected into the anydata node without duplication, + caller is supposed to not manipulate with the data after a successful call (including + calling ::lyd_free_all() on the provided data) */ + LYD_ANYDATA_STRING, /**< Value is a generic string without any knowledge about its format (e.g. anyxml value in + JSON encoded as string). XML sensitive characters (such as & or \>) are automatically + escaped when the anydata is printed in XML format. */ + LYD_ANYDATA_XML, /**< Value is a string containing the serialized XML data. */ + LYD_ANYDATA_JSON, /**< Value is a string containing the data modeled by YANG and encoded as I-JSON. */ + LYD_ANYDATA_LYB /**< Value is a memory chunk with the serialized data tree in LYB format. */ +} LYD_ANYDATA_VALUETYPE; + /** @} */ /** diff --git a/src/tree_data_common.c b/src/tree_data_common.c index 29ac333e1..3f22f22bd 100644 --- a/src/tree_data_common.c +++ b/src/tree_data_common.c @@ -317,6 +317,7 @@ lyd_owner_module(const struct lyd_node *node) return ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns); } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: if (opaq->name.module_name) { return ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name); @@ -351,6 +352,7 @@ lyd_node_module(const struct lyd_node *node) return ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns); } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: if (opaq->name.module_name) { return ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name); @@ -895,6 +897,7 @@ lyd_parse_opaq_error(const struct lyd_node *node) mod = sparent->module; } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: if (!sparent || strcmp(opaq->name.module_name, sparent->module->name)) { @@ -1243,6 +1246,7 @@ ly_free_prefix_data(LY_VALUE_FORMAT format, void *prefix_data) break; case LY_VALUE_CANON: case LY_VALUE_SCHEMA: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: break; @@ -1302,6 +1306,7 @@ ly_dup_prefix_data(const struct ly_ctx *ctx, LY_VALUE_FORMAT format, const void } break; case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: assert(!prefix_data); @@ -1427,6 +1432,7 @@ ly_store_prefix_data(const struct ly_ctx *ctx, const void *value, uint32_t value break; case LY_VALUE_CANON: case LY_VALUE_SCHEMA_RESOLVED: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: if (!*prefix_data_p) { @@ -1457,6 +1463,8 @@ ly_format2str(LY_VALUE_FORMAT format) return "schema stored mapping"; case LY_VALUE_XML: return "XML prefixes"; + case LY_VALUE_CBOR: + return "CBOR module names"; case LY_VALUE_JSON: return "JSON module names"; case LY_VALUE_LYB: diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h index da46df22f..63d688289 100644 --- a/src/tree_data_internal.h +++ b/src/tree_data_internal.h @@ -33,6 +33,8 @@ struct lysc_module; #define LY_JSON_SUFFIX_LEN 5 #define LY_LYB_SUFFIX ".lyb" #define LY_LYB_SUFFIX_LEN 4 +#define LY_CBOR_SUFFIX ".cbor" +#define LY_CBOR_SUFFIX_LEN 5 /** * @brief Internal item structure for remembering "used" instances of duplicate node instances. diff --git a/src/tree_data_new.c b/src/tree_data_new.c index 2acb65d50..93612adf0 100644 --- a/src/tree_data_new.c +++ b/src/tree_data_new.c @@ -946,6 +946,7 @@ lyd_new_meta2(const struct ly_ctx *ctx, struct lyd_node *parent, uint32_t option return LY_ENOTFOUND; } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: mod = ly_ctx_get_module_implemented(ctx, attr->name.module_name); if (!mod) { diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h index 6216a8516..75f4303e8 100644 --- a/src/tree_schema_internal.h +++ b/src/tree_schema_internal.h @@ -693,6 +693,7 @@ char *lysc_path_until(const struct lysc_node *node, const struct lysc_node *pare * LY_VALUE_SCHEMA - const struct ::lysp_module* (module used for resolving imports to prefixes) * LY_VALUE_SCHEMA_RESOLVED - struct ::lysc_prefix* (sized array of pairs: prefix - module) * LY_VALUE_XML - struct ::ly_set* (set of all returned modules as struct ::lys_module) + * LY_VALUE_CBOR - NULL * LY_VALUE_JSON - NULL * LY_VALUE_LYB - NULL * @return Module prefix to print. @@ -712,6 +713,7 @@ const char *ly_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, * LY_VALUE_SCHEMA - const struct lysp_module * (module used for resolving prefixes from imports) * LY_VALUE_SCHEMA_RESOLVED - struct lyd_value_prefix * (sized array of pairs: prefix - module) * LY_VALUE_XML - const struct ly_set * (set with defined namespaces stored as ::lyxml_ns) + * LY_VALUE_CBOR - NULL * LY_VALUE_JSON - NULL * LY_VALUE_LYB - NULL * @return Resolved prefix module, diff --git a/src/xpath.c b/src/xpath.c index b18fa3893..b7e0124e2 100644 --- a/src/xpath.c +++ b/src/xpath.c @@ -5712,6 +5712,7 @@ moveto_resolve_module(const char **qname, uint32_t *qname_len, const struct lyxp mod = set->cur_mod; break; case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: @@ -6386,6 +6387,7 @@ moveto_scnode_check(const struct lysc_node *node, const struct lysc_node *ctx_sc /* use current module */ moveto_mod = set->cur_mod; break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: From c2fbda2c1f80d937de67a19dabcdcede9f9f39c8 Mon Sep 17 00:00:00 2001 From: Juraj Budai Date: Mon, 29 Jun 2026 22:28:50 +0200 Subject: [PATCH 2/2] CBOR REFACTOR address PR review comments --- CMakeLists.txt | 2 +- src/lcbor.c | 6 +- src/lcbor.h | 4 +- src/parser_cbor.c | 630 ++++++++++++--------------------- src/parser_data.h | 11 +- src/parser_internal.h | 4 +- src/plugins_types/instanceid.c | 2 +- src/printer_cbor.c | 43 ++- src/printer_data.c | 4 +- src/tree_data.c | 2 +- src/tree_data.h | 16 - 11 files changed, 264 insertions(+), 460 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 69cb64d8f..53750e79d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -542,4 +542,4 @@ add_custom_target(cclean COMMAND make clean COMMAND find . -iname '*cmake*' -not -name CMakeLists.txt -not -path './CMakeModules*' -exec rm -rf {} + COMMAND rm -rf Makefile Doxyfile - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/lcbor.c b/src/lcbor.c index d0431fa8a..e8e432b6f 100644 --- a/src/lcbor.c +++ b/src/lcbor.c @@ -77,7 +77,6 @@ lydcbor_detect_format(struct ly_in *in, enum lyd_cbor_format *format) LY_ERR lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx_p) { - /* TODO : Need to restructure error handling here */ LY_ERR ret = LY_SUCCESS; struct lycbor_ctx *cborctx; struct cbor_load_result result = {0}; @@ -85,8 +84,7 @@ lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **c assert(ctx && in && cborctx_p); - /* TODO : error handling after the detect_format function call */ - ret = lydcbor_detect_format(in, &format); + LY_CHECK_RET(lydcbor_detect_format(in, &format)); /* Allocate and initialize CBOR context */ cborctx = calloc(1, sizeof *cborctx); @@ -95,7 +93,7 @@ lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **c cborctx->in = in; cborctx->format = format; - /* input line logging */ + /* input line logging */ ly_log_location(NULL, NULL, in); /* load and parse CBOR data */ diff --git a/src/lcbor.h b/src/lcbor.h index 4f7a9d8d4..f31465a8b 100644 --- a/src/lcbor.h +++ b/src/lcbor.h @@ -20,7 +20,6 @@ /* using libcbor as the low-level parser */ #include - #include "log.h" #include "set.h" @@ -30,8 +29,7 @@ struct ly_in; /** * @brief CBOR format variants for different encoding schemes */ -enum lyd_cbor_format -{ +enum lyd_cbor_format { LYD_CBOR_NAMED, /**< CBOR with named identifiers (JSON-like) */ LYD_CBOR_SID /**< CBOR with Schema Item identifiers (future implementation) */ }; diff --git a/src/parser_cbor.c b/src/parser_cbor.c index a816d8179..8beeb75f0 100644 --- a/src/parser_cbor.c +++ b/src/parser_cbor.c @@ -75,30 +75,25 @@ lyd_cbor_ctx_free(struct lyd_ctx *lydctx) */ static void lydcbor_parse_name(const char *value, size_t value_len, const char **name_p, size_t *name_len_p, const char **prefix_p, - size_t *prefix_len_p, ly_bool *is_meta_p) + size_t *prefix_len_p, ly_bool *is_meta_p) { const char *name, *prefix = NULL; size_t name_len, prefix_len = 0; ly_bool is_meta = 0; name = memchr(value, ':', value_len); - if (name != NULL) - { + if (name != NULL) { prefix = value; - if (*prefix == '@') - { + if (*prefix == '@') { is_meta = 1; prefix++; } prefix_len = name - prefix; name++; name_len = value_len - (prefix_len + 1) - is_meta; - } - else - { + } else { name = value; - if (name[0] == '@') - { + if (name[0] == '@') { is_meta = 1; name++; } @@ -124,35 +119,29 @@ lydcbor_parse_name(const char *value, size_t value_len, const char **name_p, siz */ static LY_ERR lydcbor_get_node_prefix(struct lyd_node *node, const char *local_prefix, size_t local_prefix_len, const char **prefix_p, - size_t *prefix_len_p) + size_t *prefix_len_p) { struct lyd_node_opaq *onode; const char *module_name = NULL; assert(prefix_p && prefix_len_p); - if (local_prefix_len) - { + if (local_prefix_len) { *prefix_p = local_prefix; *prefix_len_p = local_prefix_len; return LY_SUCCESS; } - while (node) - { - if (node->schema) - { + while (node) { + if (node->schema) { module_name = node->schema->module->name; break; } onode = (struct lyd_node_opaq *)node; - if (onode->name.module_name) - { + if (onode->name.module_name) { module_name = onode->name.module_name; break; - } - else if (onode->name.prefix) - { + } else if (onode->name.prefix) { module_name = onode->name.prefix; break; } @@ -197,7 +186,7 @@ lydcbor_data_skip(struct lycbor_ctx *cborctx) */ static LY_ERR lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, ly_bool is_attr, const char *prefix, size_t prefix_len, const char *name, - size_t name_len, struct lyd_node *parent, const struct lysc_node **snode, struct lysc_ext_instance **ext) + size_t name_len, struct lyd_node *parent, const struct lysc_node **snode, struct lysc_ext_instance **ext) { LY_ERR r; struct lys_module *mod = NULL; @@ -272,18 +261,15 @@ lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, ly_bool is_attr, const char *pref static ly_bool lydcbor_is_null(const cbor_item_t *item) { - if (!item) - { + if (!item) { return 1; } - if (cbor_isa_float_ctrl(item)) - { - if (cbor_float_get_width(item) == CBOR_FLOAT_0) - { + if (cbor_isa_float_ctrl(item)) { + if (cbor_float_get_width(item) == CBOR_FLOAT_0) { uint8_t ctrl = cbor_ctrl_value(item); - if (ctrl == 22 || ctrl == 23) - { /* null or undefined */ + + if ((ctrl == 22) || (ctrl == 23)) { /* null or undefined */ return 1; } } @@ -308,62 +294,44 @@ lydcbor_value_type_hint(struct lyd_cbor_ctx *lydctx, const cbor_item_t *item, ui *type_hint_p = 0; - if (!item) - { + if (!item) { return LY_EINVAL; } type = cbor_typeof(item); - if (type == CBOR_TYPE_ARRAY) - { + if (type == CBOR_TYPE_ARRAY) { /* check for [null] */ - if (cbor_array_size(item) == 1) - { + if (cbor_array_size(item) == 1) { cbor_item_t **handle = cbor_array_handle(item); - if (handle && lydcbor_is_null(handle[0])) - { + + if (handle && lydcbor_is_null(handle[0])) { *type_hint_p = LYD_VALHINT_EMPTY; return LY_SUCCESS; } } LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Expected CBOR value or [null], but array found."); return LY_EINVAL; - } - else if (type == CBOR_TYPE_STRING) - { + } else if (type == CBOR_TYPE_STRING) { *type_hint_p = LYD_VALHINT_STRING | LYD_VALHINT_NUM64; - } - else if (type == CBOR_TYPE_UINT || type == CBOR_TYPE_NEGINT) - { + } else if ((type == CBOR_TYPE_UINT) || (type == CBOR_TYPE_NEGINT)) { *type_hint_p = LYD_VALHINT_DECNUM; - } - else if (type == CBOR_TYPE_FLOAT_CTRL) - { - if (cbor_float_ctrl_is_ctrl(item)) - { + } else if (type == CBOR_TYPE_FLOAT_CTRL) { + if (cbor_float_ctrl_is_ctrl(item)) { uint8_t ctrl = cbor_ctrl_value(item); - if (ctrl == CBOR_CTRL_TRUE || ctrl == CBOR_CTRL_FALSE) - { + + if ((ctrl == CBOR_CTRL_TRUE) || (ctrl == CBOR_CTRL_FALSE)) { *type_hint_p = LYD_VALHINT_BOOLEAN; - } - else if (ctrl == CBOR_CTRL_NULL) - { + } else if (ctrl == CBOR_CTRL_NULL) { *type_hint_p = 0; - } - else - { + } else { LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Unexpected CBOR control value."); return LY_EINVAL; } - } - else - { + } else { *type_hint_p = LYD_VALHINT_DECNUM; } - } - else - { + } else { LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Unexpected CBOR data type."); return LY_EINVAL; } @@ -392,6 +360,7 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) case CBOR_TYPE_UINT: { uint64_t val = cbor_get_int(item); int len = snprintf(NULL, 0, "%" PRIu64, val); + if (len < 0) { return LY_ESYS; } @@ -404,6 +373,7 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) case CBOR_TYPE_NEGINT: { int64_t val = -1 - (int64_t)cbor_get_int(item); int len = snprintf(NULL, 0, "%" PRId64, val); + if (len < 0) { return LY_ESYS; } @@ -451,6 +421,7 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) } else { double val = cbor_float_get_float(item); int len = snprintf(NULL, 0, "%g", val); + if (len < 0) { return LY_ESYS; } @@ -485,7 +456,7 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) */ static LY_ERR lydcbor_data_check_opaq(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, const cbor_item_t *cbor_value, - uint32_t *type_hint_p) + uint32_t *type_hint_p) { LY_ERR ret = LY_SUCCESS; uint32_t *prev_lo, temp_lo = 0; @@ -494,28 +465,22 @@ lydcbor_data_check_opaq(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sno assert(snode); - if (!(snode->nodetype & (LYD_NODE_TERM | LYS_LIST))) - { + if (!(snode->nodetype & (LYD_NODE_TERM | LYS_LIST))) { return LY_SUCCESS; } - if (lydctx->parse_opts & LYD_PARSE_OPAQ) - { - switch (snode->nodetype) - { + if (lydctx->parse_opts & LYD_PARSE_OPAQ) { + switch (snode->nodetype) { case LYS_LEAFLIST: case LYS_LEAF: - if ((ret = lydcbor_value_type_hint(lydctx, cbor_value, type_hint_p))) - { + if ((ret = lydcbor_value_type_hint(lydctx, cbor_value, type_hint_p))) { break; } prev_lo = ly_temp_log_options(&temp_lo); ret = lydcbor_item_to_string(cbor_value, &str_val, &str_len); - if (ret == LY_SUCCESS) - { - if (ly_value_validate(NULL, snode, str_val, str_len, LY_VALUE_CBOR, NULL, *type_hint_p)) - { + if (ret == LY_SUCCESS) { + if (ly_value_validate(NULL, snode, str_val, str_len, LY_VALUE_CBOR, NULL, *type_hint_p)) { ret = LY_ENOT; } } @@ -526,9 +491,7 @@ lydcbor_data_check_opaq(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sno /* Lists may not have all keys - handled elsewhere */ break; } - } - else if (snode->nodetype & LYD_NODE_TERM) - { + } else if (snode->nodetype & LYD_NODE_TERM) { ret = lydcbor_value_type_hint(lydctx, cbor_value, type_hint_p); } @@ -563,47 +526,39 @@ lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) size_t name_len, prefix_len; const struct lysc_node *snode; - if (attr->schema || (meta_container->name.name[0] != '@')) - { + if (attr->schema || (meta_container->name.name[0] != '@')) { continue; } /* dnode-only location no longer supported by LOG_LOCSET */ log_location_items++; - if (prev != meta_container->name.name) - { + if (prev != meta_container->name.name) { lydict_remove(lydctx->cborctx->ctx, prev); LY_CHECK_GOTO(ret = lydict_insert(lydctx->cborctx->ctx, meta_container->name.name, 0, &prev), cleanup); instance = 1; - } - else - { + } else { instance++; } /* find the corresponding data node */ LY_LIST_FOR(*first_p, node) { - if (!node->schema) - { + if (!node->schema) { /* opaq node */ - if (strcmp(&meta_container->name.name[1], ((struct lyd_node_opaq *)node)->name.name)) - { + if (strcmp(&meta_container->name.name[1], ((struct lyd_node_opaq *)node)->name.name)) { continue; } - if (((struct lyd_node_opaq *)node)->hints & LYD_NODEHINT_LIST) - { + if (((struct lyd_node_opaq *)node)->hints & LYD_NODEHINT_LIST) { LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Metadata container references a sibling list node %s.", - ((struct lyd_node_opaq *)node)->name.name); + ((struct lyd_node_opaq *)node)->name.name); ret = LY_EVALID; goto cleanup; } match++; - if (match != instance) - { + if (match != instance) { continue; } @@ -612,28 +567,24 @@ lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) struct lyd_node_opaq *meta = (struct lyd_node_opaq *)meta_iter; ret = lyd_create_attr(node, NULL, lydctx->cborctx->ctx, meta->name.name, strlen(meta->name.name), - meta->name.prefix, ly_strlen(meta->name.prefix), meta->name.module_name, - ly_strlen(meta->name.module_name), meta->value, ly_strlen(meta->value), NULL, LY_VALUE_CBOR, - NULL, meta->hints); + meta->name.prefix, ly_strlen(meta->name.prefix), meta->name.module_name, + ly_strlen(meta->name.module_name), meta->value, ly_strlen(meta->value), NULL, LY_VALUE_CBOR, + NULL, meta->hints); LY_CHECK_GOTO(ret, cleanup); } break; - } - else - { + } else { lydcbor_parse_name(meta_container->name.name, strlen(meta_container->name.name), &name, &name_len, - &prefix, &prefix_len, &is_attr); + &prefix, &prefix_len, &is_attr); assert(is_attr); lydcbor_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, lyd_parent(*first_p), &snode, &ext); - if (snode != node->schema) - { + if (snode != node->schema) { continue; } match++; - if (match != instance) - { + if (match != instance) { continue; } @@ -643,26 +594,20 @@ lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) struct lys_module *mod = NULL; mod = ly_ctx_get_module_implemented(lydctx->cborctx->ctx, meta->name.prefix); - if (mod) - { + if (mod) { ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, - meta->name.name, strlen(meta->name.name), meta->value, ly_strlen(meta->value) * 8, - NULL, LY_VALUE_CBOR, NULL, meta->hints, node->schema, node); + meta->name.name, strlen(meta->name.name), meta->value, ly_strlen(meta->value) * 8, + NULL, LY_VALUE_CBOR, NULL, meta->hints, node->schema, node); LY_CHECK_GOTO(ret, cleanup); - } - else if (lydctx->parse_opts & LYD_PARSE_STRICT) - { - if (meta->name.prefix) - { + } else if (lydctx->parse_opts & LYD_PARSE_STRICT) { + if (meta->name.prefix) { LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, - "Unknown (or not implemented) YANG module \"%s\" of metadata \"%s%s%s\".", - meta->name.prefix, meta->name.prefix, ly_strlen(meta->name.prefix) ? ":" : "", - meta->name.name); - } - else - { + "Unknown (or not implemented) YANG module \"%s\" of metadata \"%s%s%s\".", + meta->name.prefix, meta->name.prefix, ly_strlen(meta->name.prefix) ? ":" : "", + meta->name.name); + } else { LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, "Missing YANG module of metadata \"%s\".", - meta->name.name); + meta->name.name); } ret = LY_EVALID; goto cleanup; @@ -675,25 +620,18 @@ lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) } } - if (match != instance) - { - if (instance > 1) - { + if (match != instance) { + if (instance > 1) { LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, - "Missing CBOR data instance #%" PRIu64 " to be coupled with %s metadata.", - instance, meta_container->name.name); - } - else - { + "Missing CBOR data instance #%" PRIu64 " to be coupled with %s metadata.", + instance, meta_container->name.name); + } else { LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, "Missing CBOR data instance to be coupled with %s metadata.", - meta_container->name.name); + meta_container->name.name); } ret = LY_EVALID; - } - else - { - if (attr == (*first_p)) - { + } else { + if (attr == (*first_p)) { *first_p = attr->next; } lyd_free_tree(attr); @@ -720,7 +658,7 @@ lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) */ static LY_ERR lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, struct lyd_node *node, - const cbor_item_t *cbor_meta) + const cbor_item_t *cbor_meta) { LY_ERR rc = LY_SUCCESS, r; enum cbor_type type; @@ -740,48 +678,40 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st assert(snode || node); nodetype = snode ? snode->nodetype : LYS_CONTAINER; - if (snode) - { + if (snode) { LOG_LOCSET(snode); } type = cbor_typeof(cbor_meta); /* check attribute encoding */ - switch (nodetype) - { + switch (nodetype) { case LYS_LEAFLIST: expected = "@name/array of objects/nulls"; LY_CHECK_GOTO(type != CBOR_TYPE_ARRAY, representation_error); next_entry: instance++; - if (!node || (node->schema != prev->schema)) - { + if (!node || (node->schema != prev->schema)) { LOGVAL(ctx, NULL, LYVE_REFERENCE, "Missing CBOR data instance #%" PRIu32 " of %s:%s to be coupled with metadata.", instance, prev->schema->module->name, prev->schema->name); rc = LY_EVALID; goto cleanup; } /* Process array item */ - if (cbor_array_size(cbor_meta) > instance - 1) - { + if (cbor_array_size(cbor_meta) > instance - 1) { cbor_item_t **handle = cbor_array_handle(cbor_meta); const cbor_item_t *item = handle[instance - 1]; - if (lydcbor_is_null(item)) - { + if (lydcbor_is_null(item)) { prev = node; node = node->next; - if (instance < cbor_array_size(cbor_meta)) - { + if (instance < cbor_array_size(cbor_meta)) { goto next_entry; } goto cleanup; } - } - else - { + } else { goto cleanup; } break; @@ -811,15 +741,13 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st map_size = cbor_map_size(cbor_meta); pairs = cbor_map_handle(cbor_meta); - for (size_t i = 0; i < map_size; ++i) - { + for (size_t i = 0; i < map_size; ++i) { const cbor_item_t *key_item = pairs[i].key; const cbor_item_t *value_item = pairs[i].value; char *key_str = NULL; size_t key_len = 0; - if (!cbor_isa_string(key_item)) - { + if (!cbor_isa_string(key_item)) { LOGVAL(ctx, NULL, LYVE_SYNTAX, "Metadata key must be a string."); rc = LY_EVALID; goto cleanup; @@ -829,25 +757,20 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st lydcbor_parse_name(key_str, key_len, &name, &name_len, &prefix, &prefix_len, &is_attr); - if (!name_len) - { + if (!name_len) { LOGVAL(ctx, NULL, LYVE_SYNTAX, "Metadata in CBOR found with an empty name."); free(key_str); rc = LY_EVALID; goto cleanup; - } - else if (!prefix_len) - { + } else if (!prefix_len) { LOGVAL(ctx, NULL, LYVE_SYNTAX, "Metadata in CBOR must be namespace-qualified, missing prefix for \"%.*s\".", - (int)key_len, key_str); + (int)key_len, key_str); free(key_str); rc = LY_EVALID; goto cleanup; - } - else if (is_attr) - { + } else if (is_attr) { LOGVAL(ctx, NULL, LYVE_SYNTAX, "Invalid format of Metadata identifier in CBOR, unexpected '@' in \"%.*s\"", - (int)key_len, key_str); + (int)key_len, key_str); free(key_str); rc = LY_EVALID; goto cleanup; @@ -855,18 +778,15 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st /* get the element module */ mod = ly_ctx_get_module_implemented2(ctx, prefix, prefix_len); - if (!mod) - { - if (lydctx->parse_opts & LYD_PARSE_STRICT) - { + if (!mod) { + if (lydctx->parse_opts & LYD_PARSE_STRICT) { LOGVAL(ctx, NULL, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.", - (int)prefix_len, prefix, (int)name_len, name); + (int)prefix_len, prefix, (int)name_len, name); free(key_str); rc = LY_EVALID; goto cleanup; } - if (node->schema) - { + if (node->schema) { free(key_str); continue; } @@ -876,8 +796,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st /* get value hints */ LY_CHECK_ERR_GOTO(rc = lydcbor_value_type_hint(lydctx, value_item, &val_hints), free(key_str), cleanup); - if (node->schema) - { + if (node->schema) { char *str_val = NULL; size_t str_len = 0; @@ -885,16 +804,14 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st /* create metadata */ rc = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, str_val, - str_len * 8, NULL, LY_VALUE_CBOR, NULL, val_hints, node->schema, node); + str_len * 8, NULL, LY_VALUE_CBOR, NULL, val_hints, node->schema, node); free(str_val); LY_CHECK_ERR_GOTO(rc, free(key_str), cleanup); /* add/correct flags */ rc = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL); LY_CHECK_ERR_GOTO(rc, free(key_str), cleanup); - } - else - { + } else { /* create attribute */ const char *module_name; size_t module_name_len; @@ -906,7 +823,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st LY_CHECK_ERR_GOTO(rc = lydcbor_item_to_string(value_item, &str_val, &str_len), free(key_str), cleanup); rc = lyd_create_attr(node, NULL, ctx, name, name_len, prefix, prefix_len, module_name, - module_name_len, str_val, str_len, NULL, LY_VALUE_CBOR, NULL, val_hints); + module_name_len, str_val, str_len, NULL, LY_VALUE_CBOR, NULL, val_hints); free(str_val); LY_CHECK_ERR_GOTO(rc, free(key_str), cleanup); } @@ -914,8 +831,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st free(key_str); } - if (nodetype == LYS_LEAFLIST && instance < cbor_array_size(cbor_meta)) - { + if ((nodetype == LYS_LEAFLIST) && (instance < cbor_array_size(cbor_meta))) { prev = node; node = node->next; goto next_entry; @@ -925,15 +841,13 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st representation_error: LOGVAL(ctx, NULL, LYVE_SYNTAX, - "The attribute(s) of %s \"%s\" is expected to be represented as CBOR %s, but input data contains different type.", - lys_nodetype2str(nodetype), node ? LYD_NAME(node) : LYD_NAME(prev), expected); + "The attribute(s) of %s \"%s\" is expected to be represented as CBOR %s, but input data contains different type.", + lys_nodetype2str(nodetype), node ? LYD_NAME(node) : LYD_NAME(prev), expected); rc = LY_EVALID; cleanup: - if ((rc == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) - { - if ((r = lydcbor_data_skip(lydctx->cborctx))) - { + if ((rc == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) { + if ((r = lydcbor_data_skip(lydctx->cborctx))) { rc = r; } } @@ -952,24 +866,18 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st */ static void lydcbor_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last, - struct lysc_ext_instance *ext) + struct lysc_ext_instance *ext) { - if (!*node_p) - { + if (!*node_p) { return; } lyd_insert_node(parent, first_p, *node_p, last); - if (first_p) - { - if (parent) - { + if (first_p) { + if (parent) { *first_p = lyd_child(parent); - } - else - { - while ((*first_p)->prev->next) - { + } else { + while ((*first_p)->prev->next) { *first_p = (*first_p)->prev; } } @@ -992,7 +900,7 @@ lydcbor_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, st */ static LY_ERR lydcbor_create_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len, - struct lyd_node *parent, const cbor_item_t *cbor_value, struct lyd_node **node_p) + struct lyd_node *parent, const cbor_item_t *cbor_value, struct lyd_node **node_p) { LY_ERR ret = LY_SUCCESS; const char *value = NULL, *module_name; @@ -1001,8 +909,7 @@ lydcbor_create_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_l uint32_t type_hint = 0; char *str_val = NULL; - if (cbor_typeof(cbor_value) != CBOR_TYPE_MAP) - { + if (cbor_typeof(cbor_value) != CBOR_TYPE_MAP) { /* prepare for creating opaq node with a value */ LY_CHECK_RET(lydcbor_item_to_string(cbor_value, &str_val, &value_len)); value = str_val; @@ -1013,25 +920,23 @@ lydcbor_create_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_l /* get the module name */ lydcbor_get_node_prefix(parent, prefix, prefix_len, &module_name, &module_name_len); - if (!module_name && !parent && lydctx->any_schema) - { + if (!module_name && !parent && lydctx->any_schema) { module_name = lydctx->any_schema->module->name; module_name_len = strlen(module_name); } /* create node */ ret = lyd_create_opaq(lydctx->cborctx->ctx, name, name_len, prefix, prefix_len, module_name, module_name_len, value, - value_len, &dynamic, LY_VALUE_CBOR, NULL, type_hint, node_p); + value_len, &dynamic, LY_VALUE_CBOR, NULL, type_hint, node_p); cleanup: - if (dynamic) - { + if (dynamic) { free((char *)value); } return ret; } -static LY_ERR lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, +static LY_ERR lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj); /** @@ -1050,7 +955,7 @@ static LY_ERR lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *pa */ static LY_ERR lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len, - struct lyd_node *parent, const cbor_item_t *cbor_value, struct lyd_node **first_p, struct lyd_node **node_p) + struct lyd_node *parent, const cbor_item_t *cbor_value, struct lyd_node **first_p, struct lyd_node **node_p) { LY_ERR ret = LY_SUCCESS; enum cbor_type type = cbor_typeof(cbor_value); @@ -1060,47 +965,40 @@ lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_le assert(*node_p); /* dnode-only location no longer supported by LOG_LOCSET */ - if ((type == CBOR_TYPE_ARRAY) && (cbor_array_size(cbor_value) == 1)) - { + if ((type == CBOR_TYPE_ARRAY) && (cbor_array_size(cbor_value) == 1)) { cbor_item_t **handle = cbor_array_handle(cbor_value); - if (lydcbor_is_null(handle[0])) - { + + if (lydcbor_is_null(handle[0])) { /* special array null value */ ((struct lyd_node_opaq *)*node_p)->hints |= LYD_VALHINT_EMPTY; goto finish; } } - if (type == CBOR_TYPE_ARRAY) - { + if (type == CBOR_TYPE_ARRAY) { /* process array */ size_t array_size = cbor_array_size(cbor_value); cbor_item_t **array_handle = cbor_array_handle(cbor_value); - for (size_t i = 0; i < array_size; ++i) - { + for (size_t i = 0; i < array_size; ++i) { const cbor_item_t *item = array_handle[i]; - if (cbor_typeof(item) == CBOR_TYPE_MAP) - { + if (cbor_typeof(item) == CBOR_TYPE_MAP) { /* array with objects, list */ ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST; /* process children */ LY_CHECK_GOTO(ret = lydcbor_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL, item), cleanup); - } - else - { + } else { /* array with values, leaf-list */ ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LEAFLIST; } - if (i < array_size - 1) - { + if (i < array_size - 1) { /* continue with next instance */ assert(*node_p); lydcbor_maintain_children(parent, first_p, node_p, - lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL); + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL); LOG_LOCBACK(0); @@ -1110,9 +1008,7 @@ lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_le /* dnode-only location no longer supported by LOG_LOCSET */ } } - } - else if (type == CBOR_TYPE_MAP) - { + } else if (type == CBOR_TYPE_MAP) { ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_CONTAINER; /* process children */ LY_CHECK_GOTO(ret = lydcbor_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL, cbor_value), cleanup); @@ -1123,8 +1019,7 @@ lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_le ret = lydcbor_metadata_finish(lydctx, lyd_node_child_p(*node_p)); cleanup: - if (*node_p) - { + if (*node_p) { LOG_LOCBACK(0); } return ret; @@ -1148,24 +1043,19 @@ lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_le */ static LY_ERR lydcbor_parse_attribute(struct lyd_cbor_ctx *lydctx, struct lyd_node *attr_node, const struct lysc_node *snode, - const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct lyd_node **node_p) + const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct lyd_node **node_p) { LY_ERR r; const char *opaq_name, *mod_name, *attr_mod; size_t opaq_name_len, attr_mod_len; - if (!attr_node) - { + if (!attr_node) { /* learn the attribute module name */ - if (!snode) - { - if (!prefix) - { + if (!snode) { + if (!prefix) { lydcbor_get_node_prefix(parent, NULL, 0, &attr_mod, &attr_mod_len); - } - else - { + } else { attr_mod = prefix; attr_mod_len = prefix_len; } @@ -1174,46 +1064,33 @@ lydcbor_parse_attribute(struct lyd_cbor_ctx *lydctx, struct lyd_node *attr_node, /* try to find the instance */ LY_LIST_FOR(parent ? lyd_child(parent) : *first_p, attr_node) { - if (snode) - { - if (attr_node->schema) - { - if (attr_node->schema == snode) - { + if (snode) { + if (attr_node->schema) { + if (attr_node->schema == snode) { break; } - } - else - { + } else { mod_name = ((struct lyd_node_opaq *)attr_node)->name.module_name; - if (!strcmp(LYD_NAME(attr_node), snode->name) && mod_name && !strcmp(mod_name, snode->module->name)) - { + if (!strcmp(LYD_NAME(attr_node), snode->name) && mod_name && !strcmp(mod_name, snode->module->name)) { break; } } - } - else - { - if (attr_node->schema) - { + } else { + if (attr_node->schema) { mod_name = attr_node->schema->module->name; - } - else - { + } else { mod_name = ((struct lyd_node_opaq *)attr_node)->name.module_name; } if (!ly_strncmp(LYD_NAME(attr_node), name, name_len) && mod_name && attr_mod && - !ly_strncmp(mod_name, attr_mod, attr_mod_len)) - { + !ly_strncmp(mod_name, attr_mod, attr_mod_len)) { break; } } } } - if (!attr_node) - { + if (!attr_node) { /* parse as an opaq node with @ prefix */ uint32_t prev_opts; @@ -1227,9 +1104,7 @@ lydcbor_parse_attribute(struct lyd_cbor_ctx *lydctx, struct lyd_node *attr_node, lydctx->parse_opts = prev_opts; LY_CHECK_RET(r); - } - else - { + } else { LY_CHECK_RET(lydcbor_meta_attr(lydctx, snode, attr_node, cbor_value)); } @@ -1250,7 +1125,7 @@ lydcbor_parse_attribute(struct lyd_cbor_ctx *lydctx, struct lyd_node *attr_node, */ static LY_ERR lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext, - const cbor_item_t *cbor_value, struct lyd_node **node) + const cbor_item_t *cbor_value, struct lyd_node **node) { LY_ERR r, rc = LY_SUCCESS; uint32_t prev_parse_opts = lydctx->parse_opts, prev_int_opts = lydctx->int_opts; @@ -1263,20 +1138,16 @@ lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st *node = NULL; /* status check according to allowed CBOR types */ - if (snode->nodetype == LYS_ANYXML) - { + if (snode->nodetype == LYS_ANYXML) { LY_CHECK_RET((type != CBOR_TYPE_MAP) && (type != CBOR_TYPE_ARRAY) && (type != CBOR_TYPE_UINT) && - (type != CBOR_TYPE_NEGINT) && (type != CBOR_TYPE_STRING) && (type != CBOR_TYPE_FLOAT_CTRL), - LY_ENOT); - } - else - { + (type != CBOR_TYPE_NEGINT) && (type != CBOR_TYPE_STRING) && (type != CBOR_TYPE_FLOAT_CTRL), + LY_ENOT); + } else { LY_CHECK_RET(type != CBOR_TYPE_MAP, LY_ENOT); } /* create any node */ - if (type == CBOR_TYPE_MAP) - { + if (type == CBOR_TYPE_MAP) { /* create node */ r = lyd_create_any(snode, NULL, NULL, 0, 1, 0, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); @@ -1302,9 +1173,7 @@ lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st /* assign the data tree */ ((struct lyd_node_any *)*node)->child = child; child = NULL; - } - else - { + } else { /* convert CBOR scalar value to string and store */ char *str_val = NULL; size_t str_len = 0; @@ -1317,8 +1186,7 @@ lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st } cleanup: - if (log_node) - { + if (log_node) { LOG_LOCBACK(0); } lydctx->parse_opts = prev_parse_opts; @@ -1342,7 +1210,7 @@ lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st */ static LY_ERR lydcbor_parse_instance_inner(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext, - const cbor_item_t *cbor_value, struct lyd_node **node) + const cbor_item_t *cbor_value, struct lyd_node **node) { LY_ERR r, rc = LY_SUCCESS; uint32_t prev_parse_opts = lydctx->parse_opts; @@ -1355,8 +1223,7 @@ lydcbor_parse_instance_inner(struct lyd_cbor_ctx *lydctx, const struct lysc_node /* use it for logging */ /* dnode-only location no longer supported by LOG_LOCSET */ - if (ext) - { + if (ext) { /* only parse these extension data and validate afterwards */ lydctx->parse_opts |= LYD_PARSE_ONLY; } @@ -1369,15 +1236,13 @@ lydcbor_parse_instance_inner(struct lyd_cbor_ctx *lydctx, const struct lysc_node r = lydcbor_metadata_finish(lydctx, lyd_node_child_p(*node)); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - if (snode->nodetype == LYS_LIST) - { + if (snode->nodetype == LYS_LIST) { /* check all keys exist */ r = lyd_parser_check_keys(*node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } - if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) - { + if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) { /* new node validation */ r = lyd_parser_validate_new_implicit((struct lyd_ctx *)lydctx, *node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); @@ -1386,8 +1251,7 @@ lydcbor_parse_instance_inner(struct lyd_cbor_ctx *lydctx, const struct lysc_node cleanup: lydctx->parse_opts = prev_parse_opts; LOG_LOCBACK(0); - if (!(*node)->hash) - { + if (!(*node)->hash) { /* list without keys is unusable */ lyd_free_tree(*node); *node = NULL; @@ -1415,8 +1279,8 @@ lydcbor_parse_instance_inner(struct lyd_cbor_ctx *lydctx, const struct lysc_node */ static LY_ERR lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, - const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len, - const char *prefix, size_t prefix_len, const cbor_item_t *cbor_value, struct lyd_node **node) + const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len, + const char *prefix, size_t prefix_len, const cbor_item_t *cbor_value, struct lyd_node **node) { LY_ERR r, rc = LY_SUCCESS; uint32_t type_hints = 0; @@ -1426,31 +1290,25 @@ lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, str LOG_LOCSET(snode); r = lydcbor_data_check_opaq(lydctx, snode, cbor_value, &type_hints); - if (r == LY_SUCCESS) - { + if (r == LY_SUCCESS) { assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY)); - if (lydcbor_is_null(cbor_value)) - { + if (lydcbor_is_null(cbor_value)) { /* do not do anything if value is CBOR null */ goto cleanup; - } - else if (snode->nodetype & LYD_NODE_TERM) - { + } else if (snode->nodetype & LYD_NODE_TERM) { enum cbor_type type = cbor_typeof(cbor_value); - if ((type == CBOR_TYPE_ARRAY) && (cbor_array_size(cbor_value) == 1)) - { + if ((type == CBOR_TYPE_ARRAY) && (cbor_array_size(cbor_value) == 1)) { cbor_item_t **handle = cbor_array_handle(cbor_value); - if (lydcbor_is_null(handle[0])) - { + + if (lydcbor_is_null(handle[0])) { /* [null] case */ goto cleanup; } } if ((type != CBOR_TYPE_ARRAY) && (type != CBOR_TYPE_UINT) && (type != CBOR_TYPE_NEGINT) && - (type != CBOR_TYPE_STRING) && (type != CBOR_TYPE_FLOAT_CTRL)) - { + (type != CBOR_TYPE_STRING) && (type != CBOR_TYPE_FLOAT_CTRL)) { rc = LY_ENOT; goto cleanup; } @@ -1460,17 +1318,13 @@ lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, str LY_CHECK_ERR_GOTO(r, rc = r, cleanup); r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, parent, str_val, str_len * 8, - NULL, LY_VALUE_CBOR, NULL, type_hints, node); + NULL, LY_VALUE_CBOR, NULL, type_hints, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - } - else if (snode->nodetype & LYD_NODE_INNER) - { + } else if (snode->nodetype & LYD_NODE_INNER) { /* create inner node */ r = lydcbor_parse_instance_inner(lydctx, snode, ext, cbor_value, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - } - else - { + } else { /* create any node */ r = lydcbor_parse_any(lydctx, snode, ext, cbor_value, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); @@ -1481,30 +1335,22 @@ lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, str r = lyd_parser_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)) - { + if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) { /* store for ext instance node validation, if needed */ r = lyd_validate_node_ext(*node, &lydctx->ext_node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); } - } - else if (r == LY_ENOT) - { + } else if (r == LY_ENOT) { /* parse it again as an opaq node */ r = lydcbor_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, cbor_value, first_p, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - if (snode->nodetype == LYS_LIST) - { + if (snode->nodetype == LYS_LIST) { ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LIST; - } - else if (snode->nodetype == LYS_LEAFLIST) - { + } else if (snode->nodetype == LYS_LEAFLIST) { ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LEAFLIST; } - } - else - { + } else { /* error */ rc = r; goto cleanup; @@ -1527,7 +1373,7 @@ lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, str */ static LY_ERR lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, - struct ly_set *parsed, const cbor_item_t *cbor_obj) + struct ly_set *parsed, const cbor_item_t *cbor_obj) { LY_ERR r, rc = LY_SUCCESS; const char *name, *prefix = NULL, *expected = NULL; @@ -1546,36 +1392,31 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l map_size = cbor_map_size(cbor_obj); pairs = cbor_map_handle(cbor_obj); - if (!pairs && map_size > 0) - { + if (!pairs && (map_size > 0)) { LOGVAL(ctx, NULL, LYVE_SYNTAX, "Invalid CBOR map structure"); return LY_EVALID; } /* process all members */ - for (size_t i = 0; i < map_size; ++i) - { + for (size_t i = 0; i < map_size; ++i) { const cbor_item_t *key_item = pairs[i].key; const cbor_item_t *value_item = pairs[i].value; char *key_str = NULL; size_t key_len = 0; - if (!key_item || !value_item) - { + if (!key_item || !value_item) { LOGVAL(ctx, NULL, LYVE_SYNTAX, "Null key or value in CBOR map"); rc = LY_EVALID; goto cleanup; } /* Skip null values */ - if (lydcbor_is_null(value_item)) - { + if (lydcbor_is_null(value_item)) { continue; } /* Get key string */ - if (!cbor_isa_string(key_item)) - { + if (!cbor_isa_string(key_item)) { LOGVAL(ctx, NULL, LYVE_SYNTAX, "CBOR map key must be string for named identifier format"); rc = LY_EVALID; goto cleanup; @@ -1586,64 +1427,48 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l /* process the node name */ lydcbor_parse_name(key_str, key_len, &name, &name_len, &prefix, &prefix_len, &is_meta); - if (!is_meta || name_len || prefix_len) - { + if (!is_meta || name_len || prefix_len) { /* get the schema node */ r = lydcbor_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode, &ext); - if (r == LY_ENOT) - { + if (r == LY_ENOT) { free(key_str); continue; - } - else if ((r == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) - { + } else if ((r == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) { rc = r; free(key_str); continue; - } - else if (r) - { + } else if (r) { rc = r; free(key_str); goto cleanup; } } - if (is_meta) - { + if (is_meta) { /* parse as metadata */ - if (!name_len && !prefix_len && !parent) - { + if (!name_len && !prefix_len && !parent) { LOGVAL(ctx, NULL, LYVE_SYNTAX, - "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries."); + "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries."); r = LY_EVALID; free(key_str); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - } - else if (!name_len && !prefix_len) - { + } else if (!name_len && !prefix_len) { /* parent's metadata without a name */ attr_node = parent; snode = attr_node->schema; } r = lydcbor_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, - value_item, first_p, &node); + value_item, first_p, &node); free(key_str); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - } - else if (!snode) - { - if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) - { + } else if (!snode) { + if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) { /* skip element */ free(key_str); continue; - } - else - { + } else { /* parse as an opaq node */ - if (name_len == 0) - { + if (name_len == 0) { LOGVAL(ctx, NULL, LYVE_SYNTAX, "CBOR object member name cannot be a zero-length string."); r = LY_EVALID; free(key_str); @@ -1655,14 +1480,11 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l free(key_str); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); } - } - else - { + } else { /* parse as a standard lyd_node but it can still turn out to be an opaque node */ /* set expected representation */ - switch (snode->nodetype) - { + switch (snode->nodetype) { case LYS_LEAFLIST: expected = "name/array of values"; break; @@ -1687,12 +1509,10 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l /* check the representation and process */ enum cbor_type value_type = cbor_typeof(value_item); - switch (snode->nodetype) - { + switch (snode->nodetype) { case LYS_LEAFLIST: case LYS_LIST: - if (value_type != CBOR_TYPE_ARRAY) - { + if (value_type != CBOR_TYPE_ARRAY) { goto representation_error; } @@ -1700,21 +1520,19 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l size_t array_size = cbor_array_size(value_item); cbor_item_t **array_handle = cbor_array_handle(value_item); - for (size_t j = 0; j < array_size; ++j) - { + for (size_t j = 0; j < array_size; ++j) { const cbor_item_t *item = array_handle[j]; r = lydcbor_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len, - item, &node); - if (r == LY_ENOT) - { + item, &node); + if (r == LY_ENOT) { free(key_str); goto representation_error; } LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); lydcbor_maintain_children(parent, first_p, &node, - lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); } break; case LYS_LEAF: @@ -1726,16 +1544,14 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l case LYS_ANYXML: /* process the value/object */ r = lydcbor_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len, - value_item, &node); - if (r == LY_ENOT) - { + value_item, &node); + if (r == LY_ENOT) { free(key_str); goto representation_error; } LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) - { + if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) { /* remember the RPC/action/notification */ lydctx->op_node = node; } @@ -1746,14 +1562,13 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l free(key_str); /* remember a successfully parsed node */ - if (parsed && node) - { + if (parsed && node) { ly_set_add(parsed, node, 1, NULL); } /* finally connect the parsed node */ lydcbor_maintain_children(parent, first_p, &node, - lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); } /* success */ @@ -1761,13 +1576,11 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l representation_error: LOGVAL(ctx, NULL, LYVE_SYNTAX, "Expecting CBOR %s but %s \"%s\" is represented in input data differently.", - expected, lys_nodetype2str(snode->nodetype), snode->name); + expected, lys_nodetype2str(snode->nodetype), snode->name); rc = LY_EVALID; - if (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) - { + if (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) { /* try to skip the invalid data */ - if ((r = lydcbor_data_skip(lydctx->cborctx))) - { + if ((r = lydcbor_data_skip(lydctx->cborctx))) { rc = r; } } @@ -1789,7 +1602,7 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l */ static LY_ERR lyd_parse_cbor_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, - struct lyd_cbor_ctx **lydctx_p) + struct lyd_cbor_ctx **lydctx_p) { LY_ERR ret = LY_SUCCESS; struct lyd_cbor_ctx *lydctx; @@ -1808,8 +1621,7 @@ lyd_parse_cbor_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_o LY_CHECK_ERR_RET(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), free(lydctx), ret); cbortype = cbor_typeof(lydctx->cborctx->cbor_data); - if (!cbor_isa_map(lydctx->cborctx->cbor_data)) - { + if (!cbor_isa_map(lydctx->cborctx->cbor_data)) { /* expecting top-level map */ LOGVAL(ctx, NULL, LYVE_SYNTAX, "Expected top-level CBOR map, but %d found.", cbortype); *lydctx_p = NULL; @@ -1823,8 +1635,8 @@ lyd_parse_cbor_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_o LY_ERR lyd_parse_cbor(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, uint32_t int_opts, - struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p) + 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 r, rc = LY_SUCCESS; struct lyd_cbor_ctx *lydctx = NULL; @@ -1835,7 +1647,6 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st lydctx->int_opts = int_opts; lydctx->ext = ext; - /* find the operation node if it exists already */ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup); @@ -1843,14 +1654,12 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st r = lydcbor_subtree_r(lydctx, parent, first_p, parsed, lydctx->cborctx->cbor_data); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - if ((int_opts & LYD_INTOPT_NO_SIBLINGS)) - { + if ((int_opts & LYD_INTOPT_NO_SIBLINGS)) { LOGVAL(ctx, NULL, LYVE_SYNTAX, "Unexpected sibling node."); 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) - { + if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) { LOGVAL(ctx, NULL, LYVE_DATA, "Missing the operation node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); @@ -1867,13 +1676,10 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st /* there should be no unresolved types stored */ 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))) - { + if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); lydctx = NULL; - } - else - { + } else { *lydctx_p = (struct lyd_ctx *)lydctx; lycbor_ctx_free(lydctx->cborctx); lydctx->cborctx = NULL; diff --git a/src/parser_data.h b/src/parser_data.h index a423c2892..7c00a7ad7 100644 --- a/src/parser_data.h +++ b/src/parser_data.h @@ -40,12 +40,11 @@ struct ly_in; * can be found in [RFC 7951](http://tools.ietf.org/html/rfc7951). The specification does not cover RPCs, actions and * Notifications, so the representation of these data trees is proprietary and corresponds to the representation of these * trees in XML. - * + * * - CBOR * - * The reference documentation would be `Encoding of Data Modeled with YANG in the Concise Binary Object - * Representation (CBOR)` : [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254). < $TODO$ Look at the edge cases of - * RPCs, actions and Notifications and maybe like json only - where we make a proprietary representation> + * The reference documentation would be `Encoding of Data Modeled with YANG in the Concise Binary Object + * Representation (CBOR)` : [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254). * * While the parsers themselves process the input data only syntactically, all the parser functions actually incorporate * the [common validator](@ref howtoDataValidation) checking the input data semantically. Therefore, the parser functions @@ -279,11 +278,11 @@ LIBYANG_API_DECL LY_ERR lyd_parse_data_mem(const struct ly_ctx *ctx, const char * @param[in] ctx_node Optional context node for parsing (can be NULL). * @param[out] tree Pointer to the resulting data tree (set on success). * @return LY_ERR value indicating success or error reason. - */ + */ LIBYANG_API_DECL LY_ERR lyd_parse_data_mem_len(const struct ly_ctx *ctx, const char *data, size_t data_len, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree); - /** +/** * @brief Parse (and validate) input data as a YANG data tree. * * Wrapper around ::lyd_parse_data() hiding work with the input handler and some obscure options. diff --git a/src/parser_internal.h b/src/parser_internal.h index 520ab957f..204b3a2c3 100644 --- a/src/parser_internal.h +++ b/src/parser_internal.h @@ -372,8 +372,8 @@ LY_ERR lyd_parse_lyb(const struct ly_ctx *ctx, struct lyd_node *parent, struct l * @return LY_ERR value. */ LY_ERR lyd_parse_cbor(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, uint32_t int_opts, - struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p); + 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); /** * @brief Validate eventTime date-and-time value. diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c index 512a7b643..a61144520 100644 --- a/src/plugins_types/instanceid.c +++ b/src/plugins_types/instanceid.c @@ -71,7 +71,7 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr inherit_prefix = 0; break; case LY_VALUE_CANON: - case LY_VALUE_CBOR: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: diff --git a/src/printer_cbor.c b/src/printer_cbor.c index cde213083..1fd9a33a0 100644 --- a/src/printer_cbor.c +++ b/src/printer_cbor.c @@ -19,7 +19,9 @@ #include #include +#include "compat.h" #include "context.h" +#include "lcbor.h" #include "log.h" #include "ly_common.h" #include "out.h" @@ -34,7 +36,6 @@ #include "tree.h" #include "tree_data.h" #include "tree_schema.h" -#include "lcbor.h" /** * @brief CBOR printer context. @@ -213,7 +214,7 @@ cbor_print_member_name(struct cborpr_ctx *pctx, const struct lyd_node *node, ly_ char *name = NULL; const char *prefix_str = node_prefix(node); const char *node_name = node->schema->name; - + if (cbor_nscmp(node, pctx->parent)) { /* print "namespace" */ if (is_attr) { @@ -234,7 +235,7 @@ cbor_print_member_name(struct cborpr_ctx *pctx, const struct lyd_node *node, ly_ name = strdup(node_name); } } - + return name; } @@ -352,17 +353,17 @@ cbor_print_value(struct cborpr_ctx *pctx, const struct ly_ctx *ctx, const struct case LY_TYPE_INT64: case LY_TYPE_UINT64: - case LY_TYPE_DEC64: { + case LY_TYPE_DEC64: /* numeric types stored as strings in CBOR */ item = cbor_build_string(value); break; - } case LY_TYPE_INT8: case LY_TYPE_INT16: case LY_TYPE_INT32: { /* signed integer types */ int64_t num = strtoll(value, NULL, 10); + if (num >= 0) { item = cbor_build_uint64(num); } else { @@ -376,6 +377,7 @@ cbor_print_value(struct cborpr_ctx *pctx, const struct ly_ctx *ctx, const struct case LY_TYPE_UINT32: { /* unsigned integer types */ uint64_t num = strtoull(value, NULL, 10); + item = cbor_build_uint64(num); break; } @@ -394,6 +396,7 @@ cbor_print_value(struct cborpr_ctx *pctx, const struct ly_ctx *ctx, const struct item = cbor_new_definite_array(1); if (item) { cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + if (null_item) { cbor_array_push(item, null_item); cbor_decref(&null_item); @@ -430,6 +433,7 @@ cbor_print_attribute(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, for (attr = node->attr; attr; attr = attr->next) { char *key = cbor_print_member_name2(pctx, &node->node, attr->format, &attr->name, 0); + LY_CHECK_RET(!key, LY_EMEM); if (attr->hints & (LYD_VALHINT_STRING | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM | LYD_VALHINT_NUM64)) { @@ -447,6 +451,7 @@ cbor_print_attribute(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, value_item = cbor_new_definite_array(1); if (value_item) { cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + if (null_item) { cbor_array_push(value_item, null_item); cbor_decref(&null_item); @@ -466,6 +471,7 @@ cbor_print_attribute(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, .key = cbor_move(cbor_build_string(key)), .value = cbor_move(value_item) }; + free(key); if (!cbor_map_add(attr_map, pair)) { @@ -509,6 +515,7 @@ cbor_print_metadata(struct cborpr_ctx *pctx, const struct lyd_node *node, const .key = cbor_move(cbor_build_string(key)), .value = cbor_move(value_item) }; + free(key); if (!cbor_map_add(meta_map, pair)) { @@ -533,6 +540,7 @@ cbor_print_metadata(struct cborpr_ctx *pctx, const struct lyd_node *node, const .key = cbor_move(cbor_build_string(key)), .value = cbor_move(value_item) }; + free(key); if (!cbor_map_add(meta_map, pair)) { @@ -610,6 +618,7 @@ cbor_print_attributes(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor .key = cbor_move(cbor_build_string(key)), .value = cbor_move(meta_map) }; + free(key); if (!cbor_map_add(parent_map, pair)) { @@ -635,6 +644,7 @@ cbor_print_attributes(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor .key = cbor_move(cbor_build_string(key)), .value = cbor_move(meta_map) }; + free(key); if (!cbor_map_add(parent_map, pair)) { @@ -671,7 +681,7 @@ cbor_print_leaf(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_ .key = cbor_move(cbor_build_string(key)), .value = cbor_move(value_item) }; - + if (!cbor_map_add(parent_map, pair)) { free(key); cbor_decref(&pair.key); @@ -799,7 +809,7 @@ cbor_print_container(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_ .key = cbor_move(cbor_build_string(key)), .value = cbor_move(inner_map) }; - + if (!cbor_map_add(parent_map, pair)) { free(key); cbor_decref(&pair.key); @@ -834,7 +844,7 @@ cbor_print_any(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t .key = cbor_move(cbor_build_string(key)), .value = cbor_move(any_item) }; - + if (!cbor_map_add(parent_map, pair)) { free(key); cbor_decref(&pair.key); @@ -938,6 +948,7 @@ cbor_print_leaf_list(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_ .key = cbor_move(cbor_build_string(key)), .value = cbor_move(*array_p) }; + free(key); if (!cbor_map_add(parent_map, pair)) { @@ -1021,6 +1032,7 @@ cbor_print_meta_attr_leaflist(struct cborpr_ctx *pctx, cbor_item_t *parent_map) cbor_decref(&meta_map); } else { cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + cbor_array_push(meta_array, null_item); cbor_decref(&null_item); } @@ -1035,6 +1047,7 @@ cbor_print_meta_attr_leaflist(struct cborpr_ctx *pctx, cbor_item_t *parent_map) .key = cbor_move(cbor_build_string(key)), .value = cbor_move(meta_array) }; + free(key); if (!cbor_map_add(parent_map, pair)) { @@ -1092,6 +1105,7 @@ cbor_print_opaq(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, cbor_ if (node->child || (hints & LYD_NODEHINT_LIST) || (hints & LYD_NODEHINT_CONTAINER)) { cbor_item_t *inner_map = NULL; + LY_CHECK_ERR_RET(cbor_print_inner(pctx, &node->node, &inner_map), free(key), LY_EINVAL); if (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) { @@ -1102,6 +1116,7 @@ cbor_print_opaq(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, cbor_ .key = cbor_move(cbor_build_string(key)), .value = cbor_move(inner_map) }; + free(key); key = NULL; @@ -1116,6 +1131,7 @@ cbor_print_opaq(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, cbor_ value_item = cbor_new_definite_array(1); if (value_item) { cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + if (null_item) { cbor_array_push(value_item, null_item); cbor_decref(&null_item); @@ -1144,6 +1160,7 @@ cbor_print_opaq(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, cbor_ .key = cbor_move(cbor_build_string(key)), .value = cbor_move(value_item) }; + free(key); key = NULL; @@ -1169,6 +1186,7 @@ cbor_print_opaq(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, cbor_ .key = cbor_move(cbor_build_string(key)), .value = cbor_move(*array_p) }; + free(key); if (!cbor_map_add(parent_map, pair)) { @@ -1253,15 +1271,16 @@ cbor_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t option if (!root) { /* empty data - print empty map */ cbor_item_t *empty_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!empty_map, LY_EMEM); - + buffer_size = cbor_serialize_alloc(empty_map, &buffer, &buffer_size); cbor_decref(&empty_map); - + if (buffer_size == 0) { return LY_EMEM; } - + ly_write_(out, (const char *)buffer, buffer_size); free(buffer); ly_print_flush(out); @@ -1280,7 +1299,7 @@ cbor_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t option /* print content */ LY_LIST_FOR(root, node) { pctx.root = node; - LY_CHECK_ERR_RET(cbor_print_node(&pctx, node, pctx.root_map), + LY_CHECK_ERR_RET(cbor_print_node(&pctx, node, pctx.root_map), cbor_decref(&pctx.root_map); ly_set_erase(&pctx.open, NULL), LY_EINVAL); if (!(options & LYD_PRINT_SIBLINGS)) { break; diff --git a/src/printer_data.c b/src/printer_data.c index 0fd1775bb..ebd162e7e 100644 --- a/src/printer_data.c +++ b/src/printer_data.c @@ -44,8 +44,8 @@ lyd_print_(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, u break; #else case LYD_CBOR: - LOGERR(root ? LYD_CTX(root) : NULL, LY_EINVAL, "CBOR format not supported, libcbor not found."); - ret = LY_EINVAL; + LOGERR(root ? LYD_CTX(root) : NULL, LY_ENOT, "CBOR format not supported, libcbor not found."); + ret = LY_ENOT; break; #endif /* ENABLE_CBOR_SUPPORT */ case LYD_UNKNOWN: diff --git a/src/tree_data.c b/src/tree_data.c index 14ef2e90b..0567c4728 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -78,7 +78,7 @@ lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format) } else if ((len >= LY_LYB_SUFFIX_LEN + 1) && !strncmp(&path[len - LY_LYB_SUFFIX_LEN], LY_LYB_SUFFIX, LY_LYB_SUFFIX_LEN)) { format = LYD_LYB; - } else if ((len >= LY_CBOR_SUFFIX_LEN + 1) && + } else if ((len >= LY_CBOR_SUFFIX_LEN + 1) && !strncmp(&path[len - LY_CBOR_SUFFIX_LEN], LY_CBOR_SUFFIX, LY_CBOR_SUFFIX_LEN)) { format = LYD_CBOR; } /* else still unknown */ diff --git a/src/tree_data.h b/src/tree_data.h index 7f5316418..946c0afb8 100644 --- a/src/tree_data.h +++ b/src/tree_data.h @@ -540,22 +540,6 @@ typedef enum { LYD_CBOR /**< CBOR instance data format */ } LYD_FORMAT; -/** - * @brief List of possible value types stored in ::lyd_node_any. - */ -typedef enum { - LYD_ANYDATA_DATATREE, /**< Value is a pointer to ::lyd_node structure (first sibling). When provided as input - parameter, the pointer is directly connected into the anydata node without duplication, - caller is supposed to not manipulate with the data after a successful call (including - calling ::lyd_free_all() on the provided data) */ - LYD_ANYDATA_STRING, /**< Value is a generic string without any knowledge about its format (e.g. anyxml value in - JSON encoded as string). XML sensitive characters (such as & or \>) are automatically - escaped when the anydata is printed in XML format. */ - LYD_ANYDATA_XML, /**< Value is a string containing the serialized XML data. */ - LYD_ANYDATA_JSON, /**< Value is a string containing the data modeled by YANG and encoded as I-JSON. */ - LYD_ANYDATA_LYB /**< Value is a memory chunk with the serialized data tree in LYB format. */ -} LYD_ANYDATA_VALUETYPE; - /** @} */ /**