summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 04:23:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 04:23:18 +0000
commitb90161ccd3b318f3314a23cb10c387651ad35831 (patch)
treea47dc087160299ce02d728cbf031d84af6281537
parentAdding upstream version 2.1.30. (diff)
downloadlibyang2-upstream.tar.xz
libyang2-upstream.zip
Adding upstream version 2.1.148.upstream/2.1.148upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--.github/workflows/codeql.yml2
-rw-r--r--CMakeLists.txt32
-rw-r--r--CMakeModules/FindPCRE2.cmake30
-rw-r--r--CMakeModules/UseCompat.cmake7
-rw-r--r--README.md3
-rw-r--r--compat/compat.c60
-rw-r--r--compat/compat.h.in18
-rw-r--r--compat/posix-shims/strings.h8
-rw-r--r--distro/README.md4
-rw-r--r--distro/pkg/deb/libyang2.install2
-rw-r--r--distro/pkg/rpm/libyang.spec43
-rw-r--r--models/yang@2022-06-16.h920
-rw-r--r--models/yang@2022-06-16.yang1
-rw-r--r--src/common.c183
-rw-r--r--src/common.h113
-rw-r--r--src/context.c69
-rw-r--r--src/context.h20
-rw-r--r--src/dict.c271
-rw-r--r--src/diff.c488
-rw-r--r--src/hash_table.c770
-rw-r--r--src/hash_table.h174
-rw-r--r--src/hash_table_internal.h146
-rw-r--r--src/json.c669
-rw-r--r--src/json.h89
-rw-r--r--src/libyang.h1
-rw-r--r--src/log.c483
-rw-r--r--src/log.h57
-rw-r--r--src/lyb.c8
-rw-r--r--src/lyb.h2
-rw-r--r--src/out.c14
-rw-r--r--src/parser_common.c176
-rw-r--r--src/parser_data.h108
-rw-r--r--src/parser_internal.h62
-rw-r--r--src/parser_json.c1056
-rw-r--r--src/parser_lyb.c49
-rw-r--r--src/parser_xml.c1018
-rw-r--r--src/parser_yang.c63
-rw-r--r--src/parser_yin.c11
-rw-r--r--src/path.c512
-rw-r--r--src/path.h58
-rw-r--r--src/plugins.c2
-rw-r--r--src/plugins_exts.c11
-rw-r--r--src/plugins_exts.h18
-rw-r--r--src/plugins_exts/nacm.c2
-rw-r--r--src/plugins_exts/schema_mount.c2
-rw-r--r--src/plugins_types.c71
-rw-r--r--src/plugins_types.h11
-rw-r--r--src/plugins_types/binary.c4
-rw-r--r--src/plugins_types/bits.c4
-rw-r--r--src/plugins_types/boolean.c4
-rw-r--r--src/plugins_types/date_and_time.c57
-rw-r--r--src/plugins_types/decimal64.c4
-rw-r--r--src/plugins_types/hex_string.c171
-rw-r--r--src/plugins_types/identityref.c26
-rw-r--r--src/plugins_types/instanceid.c90
-rw-r--r--src/plugins_types/instanceid_keys.c44
-rw-r--r--src/plugins_types/integer.c4
-rw-r--r--src/plugins_types/ipv4_address.c4
-rw-r--r--src/plugins_types/ipv4_address_no_zone.c4
-rw-r--r--src/plugins_types/ipv4_prefix.c4
-rw-r--r--src/plugins_types/ipv6_address.c4
-rw-r--r--src/plugins_types/ipv6_address_no_zone.c4
-rw-r--r--src/plugins_types/ipv6_prefix.c4
-rw-r--r--src/plugins_types/node_instanceid.c30
-rw-r--r--src/plugins_types/string.c32
-rw-r--r--src/plugins_types/union.c89
-rw-r--r--src/plugins_types/xpath1.0.c94
-rw-r--r--src/printer_json.c134
-rw-r--r--src/printer_lyb.c53
-rw-r--r--src/printer_schema.c15
-rw-r--r--src/printer_tree.c50
-rw-r--r--src/printer_xml.c47
-rw-r--r--src/printer_yang.c12
-rw-r--r--src/schema_compile.c361
-rw-r--r--src/schema_compile_amend.c9
-rw-r--r--src/schema_compile_node.c540
-rw-r--r--src/schema_features.c16
-rw-r--r--src/set.c4
-rw-r--r--src/set.h33
-rw-r--r--src/tree_data.c914
-rw-r--r--src/tree_data.h222
-rw-r--r--src/tree_data_common.c458
-rw-r--r--src/tree_data_free.c14
-rw-r--r--src/tree_data_hash.c31
-rw-r--r--src/tree_data_internal.h56
-rw-r--r--src/tree_data_new.c458
-rw-r--r--src/tree_schema.c156
-rw-r--r--src/tree_schema.h17
-rw-r--r--src/tree_schema_common.c158
-rw-r--r--src/tree_schema_internal.h10
-rw-r--r--src/validation.c649
-rw-r--r--src/validation.h3
-rw-r--r--src/xml.c45
-rw-r--r--src/xpath.c473
-rw-r--r--src/xpath.h27
-rw-r--r--tests/CMakeLists.txt2
-rw-r--r--tests/tool_i.tcl156
-rw-r--r--tests/tool_ni.tcl141
-rw-r--r--tests/utests/basic/test_common.c2
-rw-r--r--tests/utests/basic/test_context.c32
-rw-r--r--tests/utests/basic/test_hash_table.c132
-rw-r--r--tests/utests/basic/test_inout.c6
-rw-r--r--tests/utests/basic/test_json.c443
-rw-r--r--tests/utests/basic/test_plugins.c2
-rw-r--r--tests/utests/basic/test_set.c2
-rw-r--r--tests/utests/basic/test_xml.c2
-rw-r--r--tests/utests/basic/test_xpath.c204
-rw-r--r--tests/utests/data/test_diff.c258
-rw-r--r--tests/utests/data/test_new.c63
-rw-r--r--tests/utests/data/test_parser_json.c172
-rw-r--r--tests/utests/data/test_parser_xml.c226
-rw-r--r--tests/utests/data/test_printer_xml.c3
-rw-r--r--tests/utests/data/test_tree_data.c35
-rw-r--r--tests/utests/data/test_validation.c93
-rw-r--r--tests/utests/extensions/test_metadata.c24
-rw-r--r--tests/utests/extensions/test_nacm.c8
-rw-r--r--tests/utests/extensions/test_schema_mount.c121
-rw-r--r--tests/utests/extensions/test_structure.c14
-rw-r--r--tests/utests/extensions/test_yangdata.c20
-rw-r--r--tests/utests/node/list.c53
-rw-r--r--tests/utests/schema/test_printer_tree.c119
-rw-r--r--tests/utests/schema/test_schema.c273
-rw-r--r--tests/utests/schema/test_tree_schema_compile.c890
-rw-r--r--tests/utests/schema/test_yang.c12
-rw-r--r--tests/utests/schema/test_yin.c8
-rw-r--r--tests/utests/types/binary.c42
-rw-r--r--tests/utests/types/bits.c97
-rw-r--r--tests/utests/types/identityref.c50
-rw-r--r--tests/utests/types/instanceid.c47
-rw-r--r--tests/utests/types/int8.c37
-rw-r--r--tests/utests/types/leafref.c66
-rw-r--r--tests/utests/types/string.c60
-rw-r--r--tests/utests/types/union.c73
-rw-r--r--tests/utests/types/yang_types.c90
-rw-r--r--tests/utests/utests.h131
-rw-r--r--tests/yanglint/CMakeLists.txt36
-rw-r--r--tests/yanglint/README.md107
-rw-r--r--tests/yanglint/common.tcl114
-rw-r--r--tests/yanglint/data/modaction.xml8
-rw-r--r--tests/yanglint/data/modaction_ds.xml5
-rw-r--r--tests/yanglint/data/modaction_nc.xml13
-rw-r--r--tests/yanglint/data/modaction_reply.xml8
-rw-r--r--tests/yanglint/data/modaction_reply_nc.xml4
-rw-r--r--tests/yanglint/data/modconfig.xml4
-rw-r--r--tests/yanglint/data/modconfig2.xml3
-rw-r--r--tests/yanglint/data/modconfig_ctx.xml13
-rw-r--r--tests/yanglint/data/moddatanodes.xml17
-rw-r--r--tests/yanglint/data/moddefault.xml4
-rw-r--r--tests/yanglint/data/modimp_type_ctx.xml13
-rw-r--r--tests/yanglint/data/modleaf.djson3
-rw-r--r--tests/yanglint/data/modleaf.dxml1
-rw-r--r--tests/yanglint/data/modleaf.xml1
-rw-r--r--tests/yanglint/data/modleafref.xml2
-rw-r--r--tests/yanglint/data/modleafref2.xml2
-rw-r--r--tests/yanglint/data/modmandatory.xml3
-rw-r--r--tests/yanglint/data/modmandatory_invalid.xml3
-rw-r--r--tests/yanglint/data/modmerge.xml4
-rw-r--r--tests/yanglint/data/modmerge2.xml3
-rw-r--r--tests/yanglint/data/modmerge3.xml3
-rw-r--r--tests/yanglint/data/modnotif.xml5
-rw-r--r--tests/yanglint/data/modnotif2.xml3
-rw-r--r--tests/yanglint/data/modnotif2_nc.xml6
-rw-r--r--tests/yanglint/data/modnotif_ds.xml1
-rw-r--r--tests/yanglint/data/modnotif_nc.xml8
-rw-r--r--tests/yanglint/data/modoper_leafref_action.xml8
-rw-r--r--tests/yanglint/data/modoper_leafref_action_reply.xml8
-rw-r--r--tests/yanglint/data/modoper_leafref_ds.xml9
-rw-r--r--tests/yanglint/data/modoper_leafref_notif.xml3
-rw-r--r--tests/yanglint/data/modoper_leafref_notif2.xml8
-rw-r--r--tests/yanglint/data/modoper_leafref_notif_err.xml7
-rw-r--r--tests/yanglint/data/modoper_leafref_rpc.xml3
-rw-r--r--tests/yanglint/data/modoper_leafref_rpc_reply.xml5
-rw-r--r--tests/yanglint/data/modrpc.xml3
-rw-r--r--tests/yanglint/data/modrpc_nc.xml6
-rw-r--r--tests/yanglint/data/modrpc_reply.xml5
-rw-r--r--tests/yanglint/data/modrpc_reply_nc.xml6
-rw-r--r--tests/yanglint/data/modsm.xml3
-rw-r--r--tests/yanglint/data/modsm2.xml4
-rw-r--r--tests/yanglint/data/modsm_ctx_ext.xml20
-rw-r--r--tests/yanglint/data/modsm_ctx_main.xml17
-rw-r--r--tests/yanglint/interactive/add.test59
-rw-r--r--tests/yanglint/interactive/all.tcl15
-rw-r--r--tests/yanglint/interactive/clear.test53
-rw-r--r--tests/yanglint/interactive/completion.test69
-rw-r--r--tests/yanglint/interactive/data_default.test41
-rw-r--r--tests/yanglint/interactive/data_format.test23
-rw-r--r--tests/yanglint/interactive/data_in_format.test21
-rw-r--r--tests/yanglint/interactive/data_merge.test33
-rw-r--r--tests/yanglint/interactive/data_not_strict.test25
-rw-r--r--tests/yanglint/interactive/data_operational.test86
-rw-r--r--tests/yanglint/interactive/data_present.test25
-rw-r--r--tests/yanglint/interactive/data_type.test140
-rw-r--r--tests/yanglint/interactive/data_xpath.test57
-rw-r--r--tests/yanglint/interactive/debug.test33
-rw-r--r--tests/yanglint/interactive/extdata.test63
-rw-r--r--tests/yanglint/interactive/feature.test37
-rw-r--r--tests/yanglint/interactive/list.test34
-rw-r--r--tests/yanglint/interactive/load.test45
-rw-r--r--tests/yanglint/interactive/ly.tcl81
-rw-r--r--tests/yanglint/interactive/modcwd.yang4
-rw-r--r--tests/yanglint/interactive/print.test77
-rw-r--r--tests/yanglint/interactive/searchpath.test24
-rw-r--r--tests/yanglint/modules/ietf-interfaces.yang725
-rw-r--r--tests/yanglint/modules/ietf-ip.yang758
-rw-r--r--tests/yanglint/modules/ietf-netconf-acm.yang411
-rw-r--r--tests/yanglint/modules/ietf-netconf-with-defaults@2011-06-01.yang140
-rw-r--r--tests/yanglint/modules/ietf-netconf@2011-06-01.yang934
-rw-r--r--tests/yanglint/modules/modaction.yang26
-rw-r--r--tests/yanglint/modules/modconfig-augment.yang15
-rw-r--r--tests/yanglint/modules/modconfig.yang17
-rw-r--r--tests/yanglint/modules/moddatanodes.yang31
-rw-r--r--tests/yanglint/modules/moddefault.yang19
-rw-r--r--tests/yanglint/modules/modextleafref.yang24
-rw-r--r--tests/yanglint/modules/modfeature.yang7
-rw-r--r--tests/yanglint/modules/modimp-cwd.yang8
-rw-r--r--tests/yanglint/modules/modimp-path.yang8
-rw-r--r--tests/yanglint/modules/modimp-type.yang12
-rw-r--r--tests/yanglint/modules/modinclude.yang9
-rw-r--r--tests/yanglint/modules/modleaf.yang8
-rw-r--r--tests/yanglint/modules/modleafref.yang14
-rw-r--r--tests/yanglint/modules/modmandatory.yang14
-rw-r--r--tests/yanglint/modules/modmerge.yang21
-rw-r--r--tests/yanglint/modules/modmust.yang13
-rw-r--r--tests/yanglint/modules/modnotif.yang19
-rw-r--r--tests/yanglint/modules/modoper-leafref.yang68
-rw-r--r--tests/yanglint/modules/modpath.yang4
-rw-r--r--tests/yanglint/modules/modrpc.yang19
-rw-r--r--tests/yanglint/modules/modsm-augment.yang15
-rw-r--r--tests/yanglint/modules/modsm.yang13
-rw-r--r--tests/yanglint/modules/modsub.yang8
-rw-r--r--tests/yanglint/modules/modtypedef.yang8
-rw-r--r--tests/yanglint/non-interactive/all.tcl15
-rw-r--r--tests/yanglint/non-interactive/data_default.test31
-rw-r--r--tests/yanglint/non-interactive/data_in_format.test18
-rw-r--r--tests/yanglint/non-interactive/data_merge.test28
-rw-r--r--tests/yanglint/non-interactive/data_not_strict.test20
-rw-r--r--tests/yanglint/non-interactive/data_operational.test62
-rw-r--r--tests/yanglint/non-interactive/data_present.test25
-rw-r--r--tests/yanglint/non-interactive/data_type.test107
-rw-r--r--tests/yanglint/non-interactive/data_xpath.test42
-rw-r--r--tests/yanglint/non-interactive/debug.test25
-rw-r--r--tests/yanglint/non-interactive/disabled_searchdir.test18
-rw-r--r--tests/yanglint/non-interactive/ext_data.test29
-rw-r--r--tests/yanglint/non-interactive/extended_leafref.test13
-rw-r--r--tests/yanglint/non-interactive/format.test72
-rw-r--r--tests/yanglint/non-interactive/list.test26
-rw-r--r--tests/yanglint/non-interactive/ly.tcl8
-rw-r--r--tests/yanglint/non-interactive/make_implemented.test17
-rw-r--r--tests/yanglint/non-interactive/modcwd.yang4
-rw-r--r--tests/yanglint/non-interactive/path.test9
-rw-r--r--tests/yanglint/non-interactive/yang_library_file.test18
-rw-r--r--tests/yangre/CMakeLists.txt8
-rw-r--r--tests/yangre/all.tcl15
-rw-r--r--tests/yangre/arg.test19
-rw-r--r--tests/yangre/file.test37
-rw-r--r--tests/yangre/files/empty.txt0
-rw-r--r--tests/yangre/files/empty_str.txt2
-rw-r--r--tests/yangre/files/empty_str_err.txt2
-rw-r--r--tests/yangre/files/one_pattern.txt3
-rw-r--r--tests/yangre/files/two_patterns.txt4
-rw-r--r--tests/yangre/files/two_patterns_err.txt4
-rw-r--r--tests/yangre/files/two_patterns_invert_match.txt4
-rw-r--r--tests/yangre/files/two_patterns_invert_match_err.txt4
-rw-r--r--tests/yangre/invert_match.test28
-rw-r--r--tests/yangre/ly.tcl17
-rw-r--r--tests/yangre/pattern.test19
-rw-r--r--tools/lint/CMakeLists.txt48
-rw-r--r--tools/lint/cmd.c275
-rw-r--r--tools/lint/cmd.h353
-rw-r--r--tools/lint/cmd_add.c186
-rw-r--r--tools/lint/cmd_clear.c139
-rw-r--r--tools/lint/cmd_data.c694
-rw-r--r--tools/lint/cmd_debug.c134
-rw-r--r--tools/lint/cmd_extdata.c115
-rw-r--r--tools/lint/cmd_feature.c122
-rw-r--r--tools/lint/cmd_help.c107
-rw-r--r--tools/lint/cmd_list.c145
-rw-r--r--tools/lint/cmd_load.c129
-rw-r--r--tools/lint/cmd_print.c339
-rw-r--r--tools/lint/cmd_searchpath.c64
-rw-r--r--tools/lint/cmd_verb.c114
-rw-r--r--tools/lint/common.c775
-rw-r--r--tools/lint/common.h199
-rw-r--r--tools/lint/completion.c208
-rw-r--r--tools/lint/configuration.c20
-rw-r--r--tools/lint/examples/README.md75
-rw-r--r--tools/lint/main.c52
-rw-r--r--tools/lint/main_ni.c790
-rw-r--r--tools/lint/tests/expect/common.exp68
-rwxr-xr-xtools/lint/tests/expect/completion.exp60
-rwxr-xr-xtools/lint/tests/expect/feature.exp20
-rwxr-xr-xtools/lint/tests/expect/list.exp20
-rwxr-xr-xtools/lint/tests/shunit2/feature.sh19
-rwxr-xr-xtools/lint/tests/shunit2/list.sh28
-rw-r--r--tools/lint/yl_opt.c344
-rw-r--r--tools/lint/yl_opt.h237
-rw-r--r--tools/lint/yl_schema_features.c212
-rw-r--r--tools/lint/yl_schema_features.h84
-rw-r--r--tools/re/main.c363
-rw-r--r--uncrustify.cfg57
301 files changed, 21326 insertions, 8717 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 210b5be..daa55d9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -122,7 +122,7 @@ jobs:
shell: bash
working-directory: ${{ github.workspace }}
run: |
- git clone --branch uncrustify-0.75.1 https://github.com/uncrustify/uncrustify
+ git clone --branch uncrustify-0.77.1 https://github.com/uncrustify/uncrustify
cd uncrustify
mkdir build
cd build
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 264faa8..023d159 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -5,8 +5,6 @@ on:
branches: [ "master", "devel" ]
pull_request:
branches: [ "devel" ]
- schedule:
- - cron: "38 17 * * 4"
jobs:
analyze:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6470ce3..ee7d21f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -61,12 +61,12 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
# set version of the project
set(LIBYANG_MAJOR_VERSION 2)
set(LIBYANG_MINOR_VERSION 1)
-set(LIBYANG_MICRO_VERSION 30)
+set(LIBYANG_MICRO_VERSION 148)
set(LIBYANG_VERSION ${LIBYANG_MAJOR_VERSION}.${LIBYANG_MINOR_VERSION}.${LIBYANG_MICRO_VERSION})
# set version of the library
set(LIBYANG_MAJOR_SOVERSION 2)
-set(LIBYANG_MINOR_SOVERSION 29)
-set(LIBYANG_MICRO_SOVERSION 2)
+set(LIBYANG_MINOR_SOVERSION 46)
+set(LIBYANG_MICRO_SOVERSION 3)
set(LIBYANG_SOVERSION_FULL ${LIBYANG_MAJOR_SOVERSION}.${LIBYANG_MINOR_SOVERSION}.${LIBYANG_MICRO_SOVERSION})
set(LIBYANG_SOVERSION ${LIBYANG_MAJOR_SOVERSION})
@@ -103,6 +103,7 @@ set(type_plugins
src/plugins_types/ipv4_prefix.c
src/plugins_types/ipv6_prefix.c
src/plugins_types/date_and_time.c
+ src/plugins_types/hex_string.c
src/plugins_types/xpath1.0.c
src/plugins_types/node_instanceid.c)
@@ -110,6 +111,7 @@ set(libsrc
src/common.c
src/log.c
src/hash_table.c
+ src/dict.c
src/set.c
src/path.c
src/diff.c
@@ -159,6 +161,7 @@ set(libsrc
set(headers
src/context.h
+ src/hash_table.h
src/dict.h
src/in.h
src/libyang.h
@@ -181,7 +184,7 @@ set(headers
set(internal_headers
src/common.h
src/diff.h
- src/hash_table.h
+ src/hash_table_internal.h
src/in_internal.h
src/json.h
src/lyb.h
@@ -314,7 +317,7 @@ 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.76)
+ source_format_enable(0.77)
endif()
# generate files
@@ -337,9 +340,14 @@ use_compat()
# create static libyang library
if(ENABLE_STATIC)
add_definitions(-DSTATIC)
- set(CMAKE_EXE_LINKER_FLAGS -static)
- set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
+
+ # allow binaries compilation linking both static and dynamic libraries never linking static glibc
+ #set(CMAKE_EXE_LINKER_FLAGS -static)
+
+ # prefer static libraries
+ set(CMAKE_FIND_LIBRARY_SUFFIXES .a;.so)
set(CMAKE_LINK_SEARCH_START_STATIC TRUE)
+
set(CMAKE_EXE_LINK_DYNAMIC_C_FLAGS) # remove -Wl,-Bdynamic
set(CMAKE_EXE_LINK_DYNAMIC_CXX_FLAGS)
add_library(yang STATIC ${libsrc} ${compatsrc})
@@ -382,19 +390,15 @@ endif()
set_target_properties(yang PROPERTIES VERSION ${LIBYANG_SOVERSION_FULL} SOVERSION ${LIBYANG_SOVERSION})
+# link math
if(NOT WIN32)
- # link math
target_link_libraries(yang m)
endif()
# find pthreads
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_package(Threads REQUIRED)
-if(ENABLE_STATIC)
- target_link_libraries(yang -Wl,--whole-archive ${CMAKE_THREAD_LIBS_INIT} -Wl,--no-whole-archive)
-else()
- target_link_libraries(yang ${CMAKE_THREAD_LIBS_INIT})
-endif()
+target_link_libraries(yang ${CMAKE_THREAD_LIBS_INIT})
# find PCRE2 library
unset(PCRE2_LIBRARY CACHE)
@@ -459,7 +463,7 @@ gen_doc("${doxy_files}" ${LIBYANG_VERSION} ${LIBYANG_DESCRIPTION} ${project_logo
# generate API/ABI report
if ("${BUILD_TYPE_UPPER}" STREQUAL "ABICHECK")
- lib_abi_check(yang "${headers}" ${LIBYANG_SOVERSION_FULL} 003fa46e190930912e4d3f7b178c671c0662f671)
+ lib_abi_check(yang "${headers}" ${LIBYANG_SOVERSION_FULL} 8b787ba8edf21556c8845722587ac8036400150a)
endif()
# source code format target for Makefile
diff --git a/CMakeModules/FindPCRE2.cmake b/CMakeModules/FindPCRE2.cmake
index 19af7b7..a05d998 100644
--- a/CMakeModules/FindPCRE2.cmake
+++ b/CMakeModules/FindPCRE2.cmake
@@ -22,19 +22,23 @@ else()
${CMAKE_INSTALL_PREFIX}/include)
# Look for the library.
- find_library(PCRE2_LIBRARY
- NAMES
- libpcre2.a
- pcre2-8
- PATHS
- /usr/lib
- /usr/lib64
- /usr/local/lib
- /usr/local/lib64
- /opt/local/lib
- /sw/lib
- ${CMAKE_LIBRARY_PATH}
- ${CMAKE_INSTALL_PREFIX}/lib)
+ if (WIN32 AND "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
+ # For the Debug build, the pcre2 library is called pcre2-8d. The Release build should be pcre2-8.
+ find_library(PCRE2_LIBRARY pcre2-8d)
+ else()
+ find_library(PCRE2_LIBRARY
+ NAMES
+ pcre2-8
+ PATHS
+ /usr/lib
+ /usr/lib64
+ /usr/local/lib
+ /usr/local/lib64
+ /opt/local/lib
+ /sw/lib
+ ${CMAKE_LIBRARY_PATH}
+ ${CMAKE_INSTALL_PREFIX}/lib)
+ endif()
if(PCRE2_INCLUDE_DIR AND PCRE2_LIBRARY)
# learn pcre2 version
diff --git a/CMakeModules/UseCompat.cmake b/CMakeModules/UseCompat.cmake
index c1befd7..ef3df89 100644
--- a/CMakeModules/UseCompat.cmake
+++ b/CMakeModules/UseCompat.cmake
@@ -27,6 +27,7 @@ macro(USE_COMPAT)
set(CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=200809L)
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__BSD_VISIBLE=1)
+ list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_DEFAULT_SOURCE)
set(CMAKE_REQUIRED_LIBRARIES pthread)
check_symbol_exists(vdprintf "stdio.h;stdarg.h" HAVE_VDPRINTF)
@@ -47,16 +48,12 @@ macro(USE_COMPAT)
check_include_file("stdatomic.h" HAVE_STDATOMIC)
- include(CheckStructHasMember)
- check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
- check_symbol_exists(timezone time.h HAVE_TIME_H_TIMEZONE)
-
check_symbol_exists(realpath "stdlib.h" HAVE_REALPATH)
check_symbol_exists(localtime_r "time.h" HAVE_LOCALTIME_R)
check_symbol_exists(gmtime_r "time.h" HAVE_GMTIME_R)
+ check_function_exists(timegm HAVE_TIMEGM)
check_symbol_exists(strptime "time.h" HAVE_STRPTIME)
check_symbol_exists(mmap "sys/mman.h" HAVE_MMAP)
- check_symbol_exists(dirname "libgen.h" HAVE_DIRNAME)
check_symbol_exists(setenv "stdlib.h" HAVE_SETENV)
unset(CMAKE_REQUIRED_DEFINITIONS)
diff --git a/README.md b/README.md
index 05e55d2..7dfb8c0 100644
--- a/README.md
+++ b/README.md
@@ -12,9 +12,6 @@ libyang is a YANG data modelling language parser and toolkit written (and
providing API) in C. The library is used e.g. in [libnetconf2](https://github.com/CESNET/libnetconf2),
[Netopeer2](https://github.com/CESNET/Netopeer2) or [sysrepo](https://github.com/sysrepo/sysrepo) projects.
-If you are interested in future plans announcements, please subscribe to the
-[Future Plans issue](https://github.com/CESNET/libyang/issues/880).
-
## Branches
The project uses 2 main branches `master` and `devel`. Other branches should not be cloned. In `master` there are files of the
diff --git a/compat/compat.c b/compat/compat.c
index 5fb2be8..24f235c 100644
--- a/compat/compat.c
+++ b/compat/compat.c
@@ -86,9 +86,9 @@ vasprintf(char **strp, const char *fmt, va_list ap)
ssize_t
getline(char **lineptr, size_t *n, FILE *stream)
{
- static char line[256];
+ static char chunk[256];
char *ptr;
- ssize_t len;
+ ssize_t len, written;
if (!lineptr || !n) {
errno = EINVAL;
@@ -99,28 +99,31 @@ getline(char **lineptr, size_t *n, FILE *stream)
return -1;
}
- if (!fgets(line, 256, stream)) {
- return -1;
- }
-
- ptr = strchr(line, '\n');
- if (ptr) {
- *ptr = '\0';
- }
-
- len = strlen(line);
-
- if (len + 1 < 256) {
- ptr = realloc(*lineptr, 256);
- if (!ptr) {
- return -1;
+ *n = *lineptr ? *n : 0;
+ written = 0;
+ while (fgets(chunk, sizeof(chunk), stream)) {
+ len = strlen(chunk);
+ if (written + len > *n) {
+ ptr = realloc(*lineptr, *n + sizeof(chunk));
+ if (!ptr) {
+ return -1;
+ }
+ *lineptr = ptr;
+ *n = *n + sizeof(chunk);
}
- *lineptr = ptr;
- *n = 256;
+ memcpy(*lineptr + written, &chunk, len);
+ written += len;
+ if ((*lineptr)[written - 1] == '\n') {
+ break;
+ }
+ }
+ if (written) {
+ (*lineptr)[written] = '\0';
+ } else {
+ written = -1;
}
- strcpy(*lineptr, line);
- return len;
+ return written;
}
#endif
@@ -322,21 +325,6 @@ gmtime_r(const time_t *timep, struct tm *result)
#endif
#endif
-#ifndef HAVE_DIRNAME
-#ifdef _WIN32
-#include <shlwapi.h>
-char *
-dirname(char *path)
-{
- PathRemoveFileSpecA(path);
- return path;
-}
-
-#else
-#error No dirname() implementation for this platform is available.
-#endif
-#endif
-
#ifndef HAVE_SETENV
#ifdef _WIN32
int
diff --git a/compat/compat.h.in b/compat/compat.h.in
index c697d6c..3baa489 100644
--- a/compat/compat.h.in
+++ b/compat/compat.h.in
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief compatibility functions header
*
- * Copyright (c) 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2021 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -65,14 +65,12 @@
#cmakedefine HAVE_STRCHRNUL
#cmakedefine HAVE_GET_CURRENT_DIR_NAME
#cmakedefine HAVE_PTHREAD_MUTEX_TIMEDLOCK
-#cmakedefine HAVE_TM_GMTOFF
-#cmakedefine HAVE_TIME_H_TIMEZONE
#cmakedefine HAVE_REALPATH
#cmakedefine HAVE_LOCALTIME_R
#cmakedefine HAVE_GMTIME_R
+#cmakedefine HAVE_TIMEGM
#cmakedefine HAVE_STRPTIME
#cmakedefine HAVE_MMAP
-#cmakedefine HAVE_DIRNAME
#cmakedefine HAVE_STRCASECMP
#cmakedefine HAVE_SETENV
@@ -188,6 +186,18 @@ char *realpath(const char *path, char *resolved_path);
struct tm *localtime_r(const time_t *timep, struct tm *result);
#endif
+#ifndef HAVE_GMTIME_R
+struct tm *gmtime_r(const time_t *timep, struct tm *result);
+#endif
+
+#ifndef HAVE_TIMEGM
+# if defined (_WIN32)
+# define timegm _mkgmtime
+# else
+# error No timegm() implementation for this platform is available.
+# endif
+#endif
+
#ifndef HAVE_STRPTIME
char *strptime(const char *s, const char *format, struct tm *tm);
#endif
diff --git a/compat/posix-shims/strings.h b/compat/posix-shims/strings.h
index c917a66..1ac0519 100644
--- a/compat/posix-shims/strings.h
+++ b/compat/posix-shims/strings.h
@@ -7,3 +7,11 @@
#error No strcasecmp() implementation for this platform is available.
#endif
#endif
+
+#ifndef HAVE_STRNCASECMP
+#ifdef _MSC_VER
+#define strncasecmp _strnicmp
+#else
+#error No strncasecmp() implementation for this platform is available.
+#endif
+#endif
diff --git a/distro/README.md b/distro/README.md
index 995cb53..dae5d1c 100644
--- a/distro/README.md
+++ b/distro/README.md
@@ -1,9 +1,9 @@
# upstream packaging
## RPM-based system (Fedora, CentOS, SUSE, ...) quickstart
-
+```
sudo dnf install -y git rpm-build python3-pip
pip3 install apkg
apkg build -i
-
+```
See apkg docs: https://pkg.labs.nic.cz/pages/apkg/
diff --git a/distro/pkg/deb/libyang2.install b/distro/pkg/deb/libyang2.install
index e5f2c1c..9d72c7e 100644
--- a/distro/pkg/deb/libyang2.install
+++ b/distro/pkg/deb/libyang2.install
@@ -1,2 +1,2 @@
usr/lib/*/*.so.*
-usr/share/yang
+usr/share/yang/modules/libyang
diff --git a/distro/pkg/rpm/libyang.spec b/distro/pkg/rpm/libyang.spec
index 374fc4e..f57ef91 100644
--- a/distro/pkg/rpm/libyang.spec
+++ b/distro/pkg/rpm/libyang.spec
@@ -1,3 +1,7 @@
+%if 0%{?rhel} == 8
+%undefine __cmake_in_source_build
+%endif
+
Name: libyang
Version: {{ version }}
Release: {{ release }}%{?dist}
@@ -43,27 +47,46 @@ written (and providing API) in C.
%prep
%autosetup -p1
+%if 0%{?rhel} && 0%{?rhel} < 8
+ mkdir build
+%endif
%build
-%cmake -DCMAKE_BUILD_TYPE=RELWITHDEBINFO
-%cmake_build
-
-%if "x%{?suse_version}" == "x"
-cd redhat-linux-build
+%if 0%{?rhel} && 0%{?rhel} < 8
+ cd build
+ cmake \
+ -DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \
+ -DCMAKE_BUILD_TYPE:String="Release" \
+ -DCMAKE_C_FLAGS="${RPM_OPT_FLAGS}" \
+ -DCMAKE_CXX_FLAGS="${RPM_OPT_FLAGS}" \
+ ..
+ make
+%else
+ %cmake -DCMAKE_BUILD_TYPE=RELWITHDEBINFO
+ %cmake_build
+ %if "x%{?suse_version}" == "x"
+ cd %{__cmake_builddir}
+ %endif
%endif
make doc
%check
-%if "x%{?suse_version}" == "x"
-cd redhat-linux-build
+%if ( 0%{?rhel} == 0 ) || 0%{?rhel} > 7
+ %if "x%{?suse_version}" == "x"
+ cd %{__cmake_builddir}
+ %endif
%endif
ctest --output-on-failure -V %{?_smp_mflags}
%install
-%cmake_install
-
mkdir -m0755 -p %{buildroot}/%{_docdir}/libyang
-cp -a doc/html %{buildroot}/%{_docdir}/libyang/html
+%if 0%{?rhel} && 0%{?rhel} < 8
+ cd build
+ make DESTDIR=%{buildroot} install
+%else
+ %cmake_install
+ cp -a doc/html %{buildroot}/%{_docdir}/libyang/html
+%endif
%files
%license LICENSE
diff --git a/models/yang@2022-06-16.h b/models/yang@2022-06-16.h
index 563398b..2ca2ef0 100644
--- a/models/yang@2022-06-16.h
+++ b/models/yang@2022-06-16.h
@@ -1,483 +1,485 @@
-unsigned char yang_2022_06_16_yang[] = {
+char yang_2022_06_16_yang[] = {
0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x20,
0x7b, 0x0a, 0x20, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63,
0x65, 0x20, 0x22, 0x75, 0x72, 0x6e, 0x3a, 0x69, 0x65, 0x74, 0x66, 0x3a,
0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3a, 0x78, 0x6d, 0x6c, 0x3a, 0x6e,
0x73, 0x3a, 0x79, 0x61, 0x6e, 0x67, 0x3a, 0x31, 0x22, 0x3b, 0x0a, 0x20,
0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x79, 0x61, 0x6e, 0x67,
- 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20,
- 0x69, 0x65, 0x74, 0x66, 0x2d, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6d, 0x65,
- 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x20, 0x6d, 0x64, 0x3b, 0x0a,
- 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
- 0x2d, 0x64, 0x61, 0x74, 0x65, 0x20, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30,
- 0x38, 0x2d, 0x30, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20,
- 0x20, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6c, 0x69, 0x62, 0x79, 0x61,
- 0x6e, 0x67, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74,
- 0x61, 0x63, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x57, 0x65, 0x62,
- 0x3a, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
- 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
- 0x2f, 0x43, 0x45, 0x53, 0x4e, 0x45, 0x54, 0x2f, 0x6c, 0x69, 0x62, 0x79,
- 0x61, 0x6e, 0x67, 0x2f, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41,
- 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x52, 0x61, 0x64, 0x65, 0x6b,
- 0x20, 0x4b, 0x72, 0x65, 0x6a, 0x63, 0x69, 0x20, 0x3c, 0x72, 0x6b, 0x72,
- 0x65, 0x6a, 0x63, 0x69, 0x40, 0x63, 0x65, 0x73, 0x6e, 0x65, 0x74, 0x2e,
- 0x63, 0x7a, 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x75, 0x74,
- 0x68, 0x6f, 0x72, 0x3a, 0x20, 0x4d, 0x69, 0x63, 0x68, 0x61, 0x6c, 0x20,
- 0x56, 0x61, 0x73, 0x6b, 0x6f, 0x20, 0x3c, 0x6d, 0x76, 0x61, 0x73, 0x6b,
- 0x6f, 0x40, 0x63, 0x65, 0x73, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x7a, 0x3e,
- 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
- 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x54,
- 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x64, 0x75, 0x6d,
- 0x6d, 0x79, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x77, 0x69,
- 0x74, 0x68, 0x20, 0x6e, 0x6f, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66,
- 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x73, 0x75, 0x70, 0x70,
- 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65,
- 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75,
- 0x73, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x64,
- 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x52, 0x46,
- 0x43, 0x20, 0x36, 0x30, 0x32, 0x30, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x52,
- 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x30, 0x2e, 0x20, 0x54, 0x68, 0x65,
- 0x72, 0x65, 0x20, 0x61, 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x6d,
- 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x75, 0x73, 0x65, 0x64,
- 0x20, 0x69, 0x6e, 0x20, 0x6c, 0x69, 0x62, 0x79, 0x61, 0x6e, 0x67, 0x20,
- 0x64, 0x69, 0x66, 0x66, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f,
- 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x72,
- 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x32, 0x32,
- 0x2d, 0x30, 0x36, 0x2d, 0x31, 0x36, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x41, 0x64, 0x64, 0x65,
- 0x64, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x66, 0x6f,
- 0x72, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
- 0x74, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
- 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
- 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x32, 0x31, 0x2d, 0x30, 0x34, 0x2d, 0x30,
- 0x37, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63,
- 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x22, 0x41, 0x64, 0x64, 0x65, 0x64, 0x20, 0x6d, 0x65, 0x74,
- 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x65,
- 0x79, 0x2d, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20,
- 0x61, 0x6e, 0x64, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x6c, 0x65,
- 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x64, 0x69, 0x66, 0x66,
- 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x72,
- 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x32, 0x30,
- 0x2d, 0x30, 0x36, 0x2d, 0x31, 0x37, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x41, 0x64, 0x64, 0x65,
- 0x64, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66,
- 0x6f, 0x72, 0x20, 0x64, 0x69, 0x66, 0x66, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
- 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
- 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x31, 0x37, 0x2d, 0x30, 0x32, 0x2d, 0x32,
- 0x30, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63,
- 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x22, 0x41, 0x64, 0x64, 0x65, 0x64, 0x20, 0x6d, 0x65, 0x74,
- 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4e, 0x45,
- 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x27, 0x73, 0x20, 0x65, 0x64, 0x69, 0x74,
- 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x6d, 0x61, 0x6e, 0x69,
- 0x70, 0x75, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x69, 0x74,
- 0x68, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61,
- 0x6e, 0x64, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74,
- 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66,
- 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x22, 0x52, 0x46, 0x43, 0x20, 0x37, 0x39, 0x35, 0x30, 0x3a, 0x20,
- 0x54, 0x68, 0x65, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x31, 0x2e, 0x31,
- 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x69,
- 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x22,
- 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x76,
- 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30,
- 0x32, 0x2d, 0x31, 0x31, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64,
- 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61,
- 0x6c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3b,
+ 0x3b, 0x0a, 0x20, 0x20, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x76, 0x65, 0x72,
+ 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x2e, 0x31, 0x3b, 0x0a, 0x0a, 0x20,
+ 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x69, 0x65, 0x74, 0x66,
+ 0x2d, 0x79, 0x61, 0x6e, 0x67, 0x2d, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
+ 0x74, 0x61, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x72, 0x65,
+ 0x66, 0x69, 0x78, 0x20, 0x6d, 0x64, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2d, 0x64, 0x61, 0x74,
+ 0x65, 0x20, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x38, 0x2d, 0x30, 0x35,
+ 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6f, 0x72, 0x67,
+ 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x22, 0x6c, 0x69, 0x62, 0x79, 0x61, 0x6e, 0x67, 0x22, 0x3b,
+ 0x0a, 0x0a, 0x20, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x22, 0x57, 0x65, 0x62, 0x3a, 0x20, 0x20, 0x20,
+ 0x20, 0x3c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69,
+ 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x45, 0x53,
+ 0x4e, 0x45, 0x54, 0x2f, 0x6c, 0x69, 0x62, 0x79, 0x61, 0x6e, 0x67, 0x2f,
+ 0x3e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x3a, 0x20, 0x52, 0x61, 0x64, 0x65, 0x6b, 0x20, 0x4b, 0x72, 0x65,
+ 0x6a, 0x63, 0x69, 0x20, 0x3c, 0x72, 0x6b, 0x72, 0x65, 0x6a, 0x63, 0x69,
+ 0x40, 0x63, 0x65, 0x73, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x7a, 0x3e, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x3a,
+ 0x20, 0x4d, 0x69, 0x63, 0x68, 0x61, 0x6c, 0x20, 0x56, 0x61, 0x73, 0x6b,
+ 0x6f, 0x20, 0x3c, 0x6d, 0x76, 0x61, 0x73, 0x6b, 0x6f, 0x40, 0x63, 0x65,
+ 0x73, 0x6e, 0x65, 0x74, 0x2e, 0x63, 0x7a, 0x3e, 0x22, 0x3b, 0x0a, 0x0a,
+ 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x61, 0x20, 0x64, 0x75, 0x6d, 0x6d, 0x79, 0x20, 0x6d,
+ 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6e,
+ 0x6f, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61,
+ 0x6c, 0x6c, 0x79, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x69, 0x6e,
+ 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6f,
+ 0x66, 0x20, 0x76, 0x61, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x6d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e,
+ 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x52, 0x46, 0x43, 0x20, 0x36, 0x30,
+ 0x32, 0x30, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x52, 0x46, 0x43, 0x20, 0x37,
+ 0x39, 0x35, 0x30, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x61,
+ 0x72, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61, 0x64, 0x64, 0x69,
+ 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64,
+ 0x61, 0x74, 0x61, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20,
+ 0x6c, 0x69, 0x62, 0x79, 0x61, 0x6e, 0x67, 0x20, 0x64, 0x69, 0x66, 0x66,
+ 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
+ 0x2e, 0x22, 0x3b, 0x0a, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x32, 0x32, 0x2d, 0x30, 0x36, 0x2d,
+ 0x31, 0x36, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x22, 0x41, 0x64, 0x64, 0x65, 0x64, 0x20, 0x74, 0x79,
+ 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x65,
+ 0x79, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
+ 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32,
+ 0x30, 0x32, 0x31, 0x2d, 0x30, 0x34, 0x2d, 0x30, 0x37, 0x20, 0x7b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x41,
+ 0x64, 0x64, 0x65, 0x64, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+ 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x65, 0x79, 0x2d, 0x6c, 0x65,
+ 0x73, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20,
+ 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c,
+ 0x69, 0x73, 0x74, 0x20, 0x64, 0x69, 0x66, 0x66, 0x2e, 0x22, 0x3b, 0x0a,
+ 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x30, 0x32, 0x30, 0x2d, 0x30, 0x36, 0x2d,
+ 0x31, 0x37, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x22, 0x41, 0x64, 0x64, 0x65, 0x64, 0x20, 0x6d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x64,
+ 0x69, 0x66, 0x66, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
+ 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32,
+ 0x30, 0x31, 0x37, 0x2d, 0x30, 0x32, 0x2d, 0x32, 0x30, 0x20, 0x7b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x41,
+ 0x64, 0x64, 0x65, 0x64, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+ 0x61, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4e, 0x45, 0x54, 0x43, 0x4f, 0x4e,
+ 0x46, 0x27, 0x73, 0x20, 0x65, 0x64, 0x69, 0x74, 0x2d, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x20, 0x6d, 0x61, 0x6e, 0x69, 0x70, 0x75, 0x6c, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x72,
+ 0x64, 0x65, 0x72, 0x65, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6c,
+ 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x2e, 0x22, 0x3b,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
0x63, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x52, 0x46,
- 0x43, 0x20, 0x36, 0x30, 0x32, 0x30, 0x3a, 0x20, 0x59, 0x41, 0x4e, 0x47,
- 0x20, 0x2d, 0x20, 0x41, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x4d, 0x6f,
- 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x75,
- 0x61, 0x67, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f,
- 0x72, 0x6b, 0x20, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
- 0x6c, 0x20, 0x28, 0x4e, 0x45, 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x29, 0x22,
- 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x74, 0x79, 0x70,
- 0x65, 0x64, 0x65, 0x66, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
- 0x65, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72,
- 0x2d, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
- 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
- 0x63, 0x65, 0x20, 0x22, 0x52, 0x46, 0x43, 0x37, 0x39, 0x35, 0x30, 0x20,
- 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x37, 0x2e, 0x38, 0x2e,
- 0x36, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73,
- 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20,
- 0x70, 0x72, 0x65, 0x64, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x6f,
- 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x69,
- 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x69, 0x64, 0x65, 0x6e,
- 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x74,
- 0x2d, 0x69, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x22, 0x3b, 0x0a,
- 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e,
- 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x73,
- 0x65, 0x72, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79,
- 0x70, 0x65, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x69,
- 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65,
- 0x6e, 0x75, 0x6d, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x3b, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x6c, 0x61,
- 0x73, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e,
- 0x75, 0x6d, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x3b, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x61, 0x66,
- 0x74, 0x65, 0x72, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
- 0x20, 0x22, 0x52, 0x46, 0x43, 0x37, 0x39, 0x35, 0x30, 0x20, 0x73, 0x65,
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x37, 0x2e, 0x38, 0x2e, 0x36, 0x2e,
- 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
- 0x20, 0x37, 0x2e, 0x37, 0x2e, 0x39, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
- 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e, 0x20,
- 0x75, 0x73, 0x65, 0x72, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64,
- 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20,
- 0x74, 0x68, 0x69, 0x73, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
- 0x74, 0x65, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73,
- 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
- 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x65,
- 0x72, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65,
- 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20,
- 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x73,
- 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x63, 0x61,
- 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x64, 0x75,
- 0x72, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x74, 0x68, 0x65, 0x20, 0x4e, 0x45, 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x20,
- 0x3c, 0x65, 0x64, 0x69, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
- 0x3e, 0x20, 0x5c, 0x22, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5c, 0x22,
- 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
- 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20,
- 0x6e, 0x65, 0x77, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x0a,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d,
- 0x6c, 0x69, 0x73, 0x74, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2c, 0x20,
- 0x6f, 0x72, 0x20, 0x64, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x5c, 0x22,
- 0x6d, 0x65, 0x72, 0x67, 0x65, 0x5c, 0x22, 0x20, 0x6f, 0x72, 0x20, 0x5c,
- 0x22, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5c, 0x22, 0x20, 0x6f,
- 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f,
- 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6c, 0x69, 0x73,
- 0x74, 0x20, 0x6f, 0x72, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69,
- 0x73, 0x74, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x72, 0x20,
- 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x73,
- 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x65, 0x2e, 0x0a, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69, 0x73, 0x20, 0x5c, 0x22,
- 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5c, 0x22, 0x20, 0x6f, 0x72, 0x20,
- 0x5c, 0x22, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5c, 0x22, 0x2c, 0x20, 0x74,
- 0x68, 0x65, 0x20, 0x5c, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5c, 0x22,
- 0x2f, 0x5c, 0x22, 0x6b, 0x65, 0x79, 0x5c, 0x22, 0x20, 0x61, 0x74, 0x74,
- 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20,
- 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73,
- 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78,
- 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79,
- 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x73, 0x74,
- 0x20, 0x6f, 0x72, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73,
- 0x74, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x49,
- 0x66, 0x20, 0x6e, 0x6f, 0x20, 0x5c, 0x22, 0x69, 0x6e, 0x73, 0x65, 0x72,
- 0x74, 0x5c, 0x22, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
- 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
- 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x5c, 0x22, 0x63, 0x72,
- 0x65, 0x61, 0x74, 0x65, 0x5c, 0x22, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x69, 0x74, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73,
- 0x20, 0x74, 0x6f, 0x20, 0x5c, 0x22, 0x6c, 0x61, 0x73, 0x74, 0x5c, 0x22,
- 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d,
- 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
- 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
- 0x6e, 0x63, 0x65, 0x20, 0x22, 0x52, 0x46, 0x43, 0x37, 0x39, 0x35, 0x30,
+ 0x43, 0x20, 0x37, 0x39, 0x35, 0x30, 0x3a, 0x20, 0x54, 0x68, 0x65, 0x20,
+ 0x59, 0x41, 0x4e, 0x47, 0x20, 0x31, 0x2e, 0x31, 0x20, 0x44, 0x61, 0x74,
+ 0x61, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x4c,
+ 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x32, 0x30, 0x31, 0x36, 0x2d, 0x30, 0x32, 0x2d, 0x31, 0x31,
+ 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x22, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x72, 0x65,
+ 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x52, 0x46, 0x43, 0x20, 0x36, 0x30,
+ 0x32, 0x30, 0x3a, 0x20, 0x59, 0x41, 0x4e, 0x47, 0x20, 0x2d, 0x20, 0x41,
+ 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x69,
+ 0x6e, 0x67, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x20,
+ 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x28, 0x4e,
+ 0x45, 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x29, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x64, 0x65, 0x66,
+ 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x69, 0x64,
+ 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2d, 0x6b, 0x65, 0x79,
+ 0x73, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x22,
+ 0x52, 0x46, 0x43, 0x37, 0x39, 0x35, 0x30, 0x20, 0x73, 0x65, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x37, 0x2e, 0x38, 0x2e, 0x36, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
+ 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x70, 0x72, 0x65, 0x64,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x66, 0x75, 0x6c, 0x6c, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61,
+ 0x6e, 0x63, 0x65, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x72, 0x20, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x2d, 0x69, 0x6e, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a,
+ 0x0a, 0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20,
+ 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65,
+ 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20,
+ 0x66, 0x69, 0x72, 0x73, 0x74, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x3b, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x62,
+ 0x65, 0x66, 0x6f, 0x72, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x61, 0x66, 0x74, 0x65, 0x72, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72,
+ 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x22, 0x52, 0x46,
+ 0x43, 0x37, 0x39, 0x35, 0x30, 0x20, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x37, 0x2e, 0x38, 0x2e, 0x36, 0x2e, 0x20, 0x61, 0x6e, 0x64,
0x20, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x37, 0x2e, 0x37,
0x2e, 0x39, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x72,
0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x20, 0x6c, 0x65, 0x61,
0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73,
- 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x6d,
- 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20,
- 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69,
- 0x62, 0x75, 0x74, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73,
- 0x65, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69,
- 0x66, 0x69, 0x65, 0x73, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x2f,
- 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20,
- 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x73,
- 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x69, 0x6e, 0x73,
- 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
- 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64,
- 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d,
- 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x20, 0x6b, 0x65, 0x79, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74,
- 0x79, 0x70, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65,
- 0x6d, 0x70, 0x74, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
- 0x65, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72,
- 0x2d, 0x6b, 0x65, 0x79, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
- 0x63, 0x65, 0x20, 0x22, 0x52, 0x46, 0x43, 0x37, 0x39, 0x35, 0x30, 0x20,
- 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x37, 0x2e, 0x38, 0x2e,
- 0x36, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73,
- 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20,
- 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x20, 0x6c, 0x69, 0x73, 0x74,
- 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69,
- 0x62, 0x75, 0x74, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65,
- 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74,
- 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x61, 0x6e, 0x64,
- 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x73, 0x20, 0x62,
- 0x65, 0x66, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20,
- 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69,
- 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20,
- 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e,
- 0x65, 0x77, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20,
- 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e,
- 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
- 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f,
- 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74,
- 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79,
- 0x70, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
- 0x65, 0x6d, 0x70, 0x74, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74,
- 0x33, 0x32, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x31,
- 0x2e, 0x2e, 0x6d, 0x61, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
- 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e,
- 0x20, 0x6b, 0x65, 0x79, 0x2d, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x6c, 0x69,
- 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20,
- 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x74,
- 0x68, 0x69, 0x73, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
- 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73,
- 0x65, 0x64, 0x20, 0x69, 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
- 0x74, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x69, 0x73,
- 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x70,
- 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
- 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x70, 0x6f, 0x73,
- 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x63,
+ 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74,
+ 0x6f, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69,
+ 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c,
+ 0x69, 0x73, 0x74, 0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x74, 0x72,
+ 0x79, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65,
+ 0x64, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65,
+ 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x64, 0x75, 0x72, 0x69, 0x6e, 0x67,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x4e, 0x45, 0x54, 0x43, 0x4f, 0x4e, 0x46, 0x20, 0x3c, 0x65, 0x64, 0x69,
+ 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3e, 0x20, 0x5c, 0x22,
+ 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5c, 0x22, 0x20, 0x6f, 0x70, 0x65,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69,
+ 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20,
+ 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74,
+ 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x64,
+ 0x75, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x5c, 0x22, 0x6d, 0x65, 0x72, 0x67,
+ 0x65, 0x5c, 0x22, 0x20, 0x6f, 0x72, 0x20, 0x5c, 0x22, 0x72, 0x65, 0x70,
+ 0x6c, 0x61, 0x63, 0x65, 0x5c, 0x22, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x73,
+ 0x65, 0x72, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x61,
+ 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x72,
+ 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x65,
+ 0x6e, 0x74, 0x72, 0x79, 0x20, 0x6f, 0x72, 0x20, 0x6d, 0x6f, 0x76, 0x65,
+ 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67,
+ 0x20, 0x6f, 0x6e, 0x65, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x49, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x20, 0x69, 0x73, 0x20, 0x5c, 0x22, 0x62, 0x65, 0x66, 0x6f,
+ 0x72, 0x65, 0x5c, 0x22, 0x20, 0x6f, 0x72, 0x20, 0x5c, 0x22, 0x61, 0x66,
+ 0x74, 0x65, 0x72, 0x5c, 0x22, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x5c,
+ 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5c, 0x22, 0x2f, 0x5c, 0x22, 0x6b,
+ 0x65, 0x79, 0x5c, 0x22, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4d, 0x55,
+ 0x53, 0x54, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x75,
+ 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69,
+ 0x66, 0x79, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69,
+ 0x6e, 0x67, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x69, 0x6e, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20,
+ 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x0a, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
+ 0x20, 0x5c, 0x22, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x5c, 0x22, 0x20,
+ 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69, 0x73,
+ 0x20, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x5c, 0x22, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
+ 0x5c, 0x22, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x2c, 0x20, 0x69, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x74, 0x6f, 0x20,
+ 0x5c, 0x22, 0x6c, 0x61, 0x73, 0x74, 0x5c, 0x22, 0x2e, 0x22, 0x3b, 0x0a,
+ 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e,
+ 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70,
+ 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20,
+ 0x22, 0x52, 0x46, 0x43, 0x37, 0x39, 0x35, 0x30, 0x20, 0x73, 0x65, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x37, 0x2e, 0x37, 0x2e, 0x39, 0x2e, 0x22,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x22, 0x49, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x6f, 0x72, 0x64,
+ 0x65, 0x72, 0x65, 0x64, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69,
+ 0x73, 0x74, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x61, 0x74, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20,
+ 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x73, 0x65,
+ 0x72, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x61,
+ 0x6e, 0x64, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x73,
0x20, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x66, 0x74, 0x65,
- 0x72, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20,
- 0x6e, 0x65, 0x77, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
- 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
- 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
- 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e,
- 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x70, 0x65, 0x72,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
- 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x20, 0x7b,
+ 0x72, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x65, 0x78, 0x69, 0x73,
+ 0x74, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x6e, 0x65, 0x77, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20,
+ 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a,
+ 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e,
+ 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6b, 0x65, 0x79,
+ 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20,
+ 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x69, 0x64,
+ 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2d, 0x6b, 0x65, 0x79,
+ 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x22,
+ 0x52, 0x46, 0x43, 0x37, 0x39, 0x35, 0x30, 0x20, 0x73, 0x65, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x37, 0x2e, 0x38, 0x2e, 0x36, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
+ 0x49, 0x6e, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x6f, 0x72, 0x64, 0x65,
+ 0x72, 0x65, 0x64, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
+ 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x64, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x74, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x69, 0x73, 0x20,
+ 0x75, 0x73, 0x65, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x70, 0x65,
+ 0x63, 0x69, 0x66, 0x69, 0x65, 0x73, 0x20, 0x62, 0x65, 0x66, 0x6f, 0x72,
+ 0x65, 0x2f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x77, 0x68, 0x69, 0x63,
+ 0x68, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x69,
+ 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x74, 0x68, 0x65, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x69,
+ 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x73, 0x68, 0x6f, 0x75,
+ 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74,
+ 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20,
+ 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75,
+ 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74,
+ 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74,
+ 0x79, 0x70, 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x20, 0x31, 0x2e, 0x2e, 0x6d, 0x61,
+ 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e, 0x20, 0x6b, 0x65, 0x79,
+ 0x2d, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f,
+ 0x72, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20, 0x6c, 0x65, 0x61, 0x66,
+ 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20,
+ 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x6d, 0x75,
+ 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x69,
+ 0x66, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
+ 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65,
+ 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66,
+ 0x69, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74,
+ 0x61, 0x6e, 0x63, 0x65, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x65, 0x66,
+ 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x20, 0x77, 0x68,
+ 0x69, 0x63, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20,
+ 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x73, 0x68, 0x6f,
+ 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72,
+ 0x74, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
+ 0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75,
+ 0x6d, 0x20, 0x6e, 0x6f, 0x6e, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65,
+ 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20,
+ 0x62, 0x6f, 0x74, 0x68, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x72,
+ 0x65, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65, 0x72,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65,
+ 0x64, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61,
+ 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x49, 0x6e, 0x20, 0x63, 0x61, 0x73,
+ 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2c,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x69, 0x74, 0x73, 0x20, 0x64, 0x65,
+ 0x66, 0x61, 0x75, 0x6c, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x20, 0x63, 0x68,
+ 0x61, 0x6e, 0x67, 0x65, 0x64, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65,
+ 0x6e, 0x75, 0x6d, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x7b,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73,
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20,
- 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x65, 0x64,
- 0x20, 0x69, 0x6e, 0x20, 0x62, 0x6f, 0x74, 0x68, 0x20, 0x64, 0x61, 0x74,
- 0x61, 0x20, 0x74, 0x72, 0x65, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, 0x20,
- 0x74, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6e,
- 0x65, 0x73, 0x74, 0x65, 0x64, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69,
- 0x74, 0x68, 0x20, 0x61, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x6f,
- 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x20, 0x49, 0x6e,
- 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x6c,
- 0x65, 0x61, 0x66, 0x2c, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x69, 0x74,
- 0x73, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x61,
- 0x67, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x2e, 0x22, 0x3b,
+ 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x64, 0x69, 0x64, 0x20, 0x6e, 0x6f, 0x74,
+ 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x74, 0x72, 0x65, 0x65,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x63, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x0a,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x73,
+ 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x22,
+ 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x64, 0x65, 0x6c,
+ 0x65, 0x74, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x22, 0x54, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x65, 0x78,
+ 0x69, 0x73, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x74, 0x72, 0x65, 0x65, 0x20,
+ 0x61, 0x6e, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x64, 0x65, 0x6c, 0x65,
+ 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x22, 0x3b,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x63, 0x72, 0x65, 0x61,
- 0x74, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
- 0x54, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x64, 0x69, 0x64,
- 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x20, 0x69,
- 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20,
- 0x74, 0x72, 0x65, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x77, 0x61, 0x73,
- 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20,
- 0x74, 0x68, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x74, 0x72,
- 0x65, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d,
- 0x20, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
- 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64,
- 0x65, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e,
- 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, 0x74,
- 0x72, 0x65, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20,
- 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74,
- 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x72, 0x65,
- 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20,
- 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
- 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x22, 0x54, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64,
- 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20,
- 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x74,
- 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20,
- 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61,
- 0x76, 0x65, 0x73, 0x2f, 0x61, 0x6e, 0x79, 0x78, 0x6d, 0x6c, 0x2f, 0x61,
- 0x6e, 0x79, 0x64, 0x61, 0x74, 0x61, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75,
- 0x73, 0x65, 0x72, 0x2d, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x20,
- 0x6c, 0x69, 0x73, 0x74, 0x73, 0x2f, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c,
- 0x69, 0x73, 0x74, 0x73, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63, 0x74,
- 0x69, 0x76, 0x65, 0x6c, 0x79, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
- 0x20, 0x22, 0x52, 0x46, 0x43, 0x36, 0x32, 0x34, 0x31, 0x20, 0x73, 0x65,
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x37, 0x2e, 0x32, 0x2e, 0x22, 0x3b,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
- 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
- 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66,
- 0x20, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x61,
- 0x20, 0x64, 0x69, 0x66, 0x66, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x61, 0x20,
- 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x68, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x20,
- 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20,
- 0x69, 0x6e, 0x68, 0x65, 0x72, 0x69, 0x74, 0x65, 0x64, 0x20, 0x66, 0x72,
- 0x6f, 0x6d, 0x20, 0x69, 0x74, 0x73, 0x20, 0x6e, 0x65, 0x61, 0x72, 0x65,
- 0x73, 0x74, 0x20, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x77, 0x69,
- 0x74, 0x68, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
- 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x54, 0x6f, 0x70, 0x2d, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x6e, 0x6f,
- 0x64, 0x65, 0x73, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x61, 0x6c, 0x77,
- 0x61, 0x79, 0x73, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x20,
- 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x20, 0x20, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x20, 0x72, 0x65, 0x70, 0x6c,
+ 0x61, 0x63, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x22, 0x54, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20, 0x63, 0x68, 0x61, 0x6e,
+ 0x67, 0x65, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e,
+ 0x6f, 0x64, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6d, 0x6f, 0x76, 0x65,
+ 0x64, 0x20, 0x66, 0x6f, 0x72, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x2f,
+ 0x61, 0x6e, 0x79, 0x78, 0x6d, 0x6c, 0x2f, 0x61, 0x6e, 0x79, 0x64, 0x61,
+ 0x74, 0x61, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2d,
0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x64, 0x20, 0x6c, 0x69, 0x73, 0x74,
0x73, 0x2f, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x73,
- 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
- 0x69, 0x6f, 0x6e, 0x20, 0x27, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x27,
- 0x20, 0x61, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x27, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x27, 0x20, 0x4d, 0x55,
- 0x53, 0x54, 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x68, 0x61, 0x76, 0x65,
- 0x20, 0x74, 0x68, 0x65, 0x20, 0x27, 0x6b, 0x65, 0x79, 0x27, 0x2c, 0x20,
- 0x27, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x27, 0x2c, 0x20, 0x6f, 0x72, 0x20,
- 0x27, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
- 0x74, 0x61, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x20,
- 0x49, 0x74, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x73,
- 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x63, 0x65, 0x64, 0x69,
- 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2e,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x49, 0x6e, 0x20, 0x63,
- 0x61, 0x73, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75,
- 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x65,
- 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x69, 0x73, 0x20, 0x65, 0x6d,
- 0x70, 0x74, 0x79, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64,
- 0x65, 0x20, 0x77, 0x61, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x6d, 0x6f, 0x76,
- 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69,
- 0x72, 0x73, 0x74, 0x20, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e,
- 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x6c,
- 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
- 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6b, 0x65, 0x65, 0x70, 0x20, 0x74, 0x68,
- 0x65, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66,
- 0x20, 0x65, 0x64, 0x69, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
- 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20,
- 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x20, 0x6e, 0x61, 0x6d, 0x65,
- 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x61,
- 0x72, 0x65, 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72, 0x20, 0x72,
- 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x6d,
- 0x65, 0x61, 0x6e, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x61, 0x72, 0x65, 0x20, 0x75,
- 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
- 0x20, 0x61, 0x20, 0x73, 0x75, 0x62, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66,
- 0x20, 0x75, 0x73, 0x65, 0x2d, 0x63, 0x61, 0x73, 0x65, 0x73, 0x2e, 0x22,
- 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x64, 0x3a,
- 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f,
- 0x72, 0x69, 0x67, 0x2d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20,
- 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x62,
- 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
- 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x6e, 0x66, 0x6f, 0x72,
- 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74,
- 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75,
- 0x73, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x73, 0x74,
- 0x61, 0x74, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e,
- 0x6f, 0x64, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
+ 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x6c,
+ 0x79, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72,
+ 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x22, 0x52, 0x46,
+ 0x43, 0x36, 0x32, 0x34, 0x31, 0x20, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x37, 0x2e, 0x32, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x4f, 0x70, 0x65, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x6e,
+ 0x6f, 0x64, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x64, 0x69, 0x66,
+ 0x66, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x61, 0x20, 0x6e, 0x6f, 0x64, 0x65,
+ 0x20, 0x68, 0x61, 0x73, 0x20, 0x6e, 0x6f, 0x20, 0x6f, 0x70, 0x65, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x68, 0x65,
+ 0x72, 0x69, 0x74, 0x65, 0x64, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x69,
+ 0x74, 0x73, 0x20, 0x6e, 0x65, 0x61, 0x72, 0x65, 0x73, 0x74, 0x20, 0x70,
+ 0x61, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61,
+ 0x6e, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x54, 0x6f, 0x70, 0x2d,
+ 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20,
+ 0x6d, 0x75, 0x73, 0x74, 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20,
+ 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x6f, 0x70, 0x65, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x6f, 0x72, 0x64, 0x65,
+ 0x72, 0x65, 0x64, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x2f, 0x6c, 0x65,
+ 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74,
+ 0x68, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x27, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x27, 0x20, 0x61, 0x6e, 0x64,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x27, 0x72, 0x65, 0x70,
+ 0x6c, 0x61, 0x63, 0x65, 0x27, 0x20, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x61,
+ 0x6c, 0x73, 0x6f, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x27, 0x6b, 0x65, 0x79, 0x27, 0x2c, 0x20, 0x27, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x27, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x27, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x64,
+ 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x73,
+ 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x70, 0x72, 0x65, 0x63, 0x65, 0x64, 0x69, 0x6e, 0x67, 0x20, 0x69,
+ 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x49, 0x6e, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
+ 0x74, 0x61, 0x20, 0x69, 0x73, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x20, 0x77, 0x61,
+ 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x64, 0x2f, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x6f,
+ 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x0a, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x41, 0x6c, 0x6c, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x6b, 0x65, 0x65, 0x70, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6d, 0x65,
+ 0x61, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x6f, 0x66, 0x20, 0x65, 0x64, 0x69,
+ 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x6f, 0x70, 0x65,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x73, 0x69, 0x6d, 0x69,
+ 0x6c, 0x61, 0x72, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, 0x62, 0x75,
+ 0x74, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x61, 0x72, 0x65, 0x20, 0x66,
+ 0x75, 0x72, 0x74, 0x68, 0x65, 0x72, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x69,
+ 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68,
+ 0x65, 0x79, 0x20, 0x61, 0x72, 0x65, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x61, 0x20, 0x73,
+ 0x75, 0x62, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65,
+ 0x2d, 0x63, 0x61, 0x73, 0x65, 0x73, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f,
+ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x2d,
+ 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x7b, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x65,
+ 0x61, 0x6e, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x22, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x64, 0x65,
+ 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x20,
+ 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x2e,
+ 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x64,
+ 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x6f, 0x72, 0x69, 0x67, 0x2d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7b,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x73, 0x74,
+ 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65,
+ 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x22, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75,
+ 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x61,
+ 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x20, 0x6c, 0x65, 0x61,
+ 0x66, 0x2e, 0x20, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69,
+ 0x76, 0x65, 0x6c, 0x79, 0x2c, 0x20, 0x69, 0x74, 0x73, 0x20, 0x6d, 0x65,
+ 0x61, 0x6e, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65,
+ 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x5c, 0x22, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x5c, 0x22, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62,
+ 0x75, 0x74, 0x65, 0x20, 0x62, 0x75, 0x74, 0x20, 0x69, 0x64, 0x65, 0x6e,
+ 0x74, 0x69, 0x66, 0x69, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f,
+ 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d, 0x6c, 0x69, 0x73, 0x74,
+ 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x72, 0x61,
+ 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6f, 0x6e, 0x65, 0x2e, 0x22, 0x3b,
+ 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61,
+ 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72,
+ 0x69, 0x67, 0x2d, 0x6b, 0x65, 0x79, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20,
+ 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65,
+ 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61,
+ 0x6e, 0x63, 0x65, 0x2d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
+ 0x65, 0x72, 0x2d, 0x6b, 0x65, 0x79, 0x73, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x22, 0x49, 0x74, 0x73, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x69, 0x6e,
+ 0x67, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d,
+ 0x65, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x5c, 0x22, 0x6b,
+ 0x65, 0x79, 0x5c, 0x22, 0x20, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x62, 0x75, 0x74, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74,
+ 0x69, 0x66, 0x69, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61,
+ 0x6c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61,
+ 0x6e, 0x63, 0x65, 0x20, 0x72, 0x61, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74,
+ 0x68, 0x61, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20,
+ 0x6f, 0x6e, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a,
0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74,
- 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x2d, 0x76, 0x61, 0x6c,
- 0x75, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70,
- 0x65, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3b, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
- 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x50, 0x72, 0x65,
- 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20,
- 0x6f, 0x66, 0x20, 0x61, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64,
- 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2e, 0x20, 0x41, 0x6c, 0x74, 0x65, 0x72,
- 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x6c, 0x79, 0x2c, 0x20, 0x69, 0x74,
- 0x73, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x69, 0x6e, 0x67, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
- 0x73, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
- 0x5c, 0x22, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5c, 0x22, 0x20, 0x61, 0x74,
- 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x62, 0x75, 0x74, 0x20,
- 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x73, 0x20, 0x74,
- 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x0a,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x65, 0x61, 0x66, 0x2d,
- 0x6c, 0x69, 0x73, 0x74, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
- 0x65, 0x20, 0x72, 0x61, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61,
- 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6f, 0x6e,
- 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20,
- 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x2d, 0x6b, 0x65, 0x79, 0x20, 0x7b,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x6e,
- 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x3b, 0x0a,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x69,
- 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x69, 0x64, 0x65, 0x6e,
- 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x2d, 0x6b, 0x65, 0x79, 0x73, 0x3b,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64,
- 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x49, 0x74, 0x73, 0x20, 0x6d, 0x65,
- 0x61, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x5c, 0x22, 0x6b, 0x65, 0x79, 0x5c, 0x22, 0x20, 0x61, 0x74, 0x74,
- 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x62, 0x75, 0x74, 0x20, 0x69,
- 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x73, 0x0a, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69,
- 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x69,
- 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x72, 0x61, 0x74, 0x68,
- 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
- 0x6e, 0x65, 0x77, 0x20, 0x6f, 0x6e, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20,
- 0x20, 0x7d, 0x0a, 0x0a, 0x20, 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e,
- 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x69, 0x67,
- 0x2d, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a,
- 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x6e, 0x69,
- 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x3b,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x72, 0x69, 0x67, 0x2d, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x7b,
0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70,
- 0x65, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32, 0x20, 0x7b, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72,
- 0x61, 0x6e, 0x67, 0x65, 0x20, 0x31, 0x2e, 0x2e, 0x6d, 0x61, 0x78, 0x3b,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20,
- 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73,
- 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x22, 0x49, 0x74, 0x73, 0x20, 0x6d, 0x65, 0x61, 0x6e,
- 0x69, 0x6e, 0x67, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
- 0x61, 0x6d, 0x65, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x5c,
- 0x22, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x22, 0x20,
- 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x62, 0x75,
- 0x74, 0x20, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x73,
- 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20,
- 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x6c, 0x69, 0x73,
- 0x74, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x72,
- 0x61, 0x74, 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x74,
- 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6f, 0x6e, 0x65, 0x2e, 0x22,
- 0x3b, 0x0a, 0x20, 0x20, 0x7d, 0x0a, 0x7d, 0x0a, 0x00
+ 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x75, 0x69,
+ 0x6e, 0x74, 0x33, 0x32, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x61, 0x6e, 0x67, 0x65,
+ 0x20, 0x31, 0x2e, 0x2e, 0x6d, 0x61, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
+ 0x49, 0x74, 0x73, 0x20, 0x6d, 0x65, 0x61, 0x6e, 0x69, 0x6e, 0x67, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x20,
+ 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x5c, 0x22, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x22, 0x20, 0x61, 0x74, 0x74, 0x72,
+ 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x62, 0x75, 0x74, 0x20, 0x69, 0x64,
+ 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x73, 0x0a, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69, 0x67,
+ 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x69, 0x6e,
+ 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x72, 0x61, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e,
+ 0x65, 0x77, 0x20, 0x6f, 0x6e, 0x65, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20,
+ 0x7d, 0x0a, 0x7d, 0x0a, 0x00
};
diff --git a/models/yang@2022-06-16.yang b/models/yang@2022-06-16.yang
index 0d41360..e506127 100644
--- a/models/yang@2022-06-16.yang
+++ b/models/yang@2022-06-16.yang
@@ -1,6 +1,7 @@
module yang {
namespace "urn:ietf:params:xml:ns:yang:1";
prefix yang;
+ yang-version 1.1;
import ietf-yang-metadata {
prefix md;
diff --git a/src/common.c b/src/common.c
index 38f51ea..03dd81c 100644
--- a/src/common.c
+++ b/src/common.c
@@ -176,19 +176,14 @@ ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
uint32_t c, aux;
size_t len;
- if (bytes_read) {
- (*bytes_read) = 0;
- }
-
c = (*input)[0];
- LY_CHECK_RET(!c, LY_EINVAL);
if (!(c & 0x80)) {
/* one byte character */
len = 1;
if ((c < 0x20) && (c != 0x9) && (c != 0xa) && (c != 0xd)) {
- return LY_EINVAL;
+ goto error;
}
} else if ((c & 0xe0) == 0xc0) {
/* two bytes character */
@@ -196,12 +191,12 @@ ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
aux = (*input)[1];
if ((aux & 0xc0) != 0x80) {
- return LY_EINVAL;
+ goto error;
}
c = ((c & 0x1f) << 6) | (aux & 0x3f);
if (c < 0x80) {
- return LY_EINVAL;
+ goto error;
}
} else if ((c & 0xf0) == 0xe0) {
/* three bytes character */
@@ -211,14 +206,14 @@ ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
for (uint64_t i = 1; i <= 2; i++) {
aux = (*input)[i];
if ((aux & 0xc0) != 0x80) {
- return LY_EINVAL;
+ goto error;
}
c = (c << 6) | (aux & 0x3f);
}
if ((c < 0x800) || ((c > 0xd7ff) && (c < 0xe000)) || (c > 0xfffd)) {
- return LY_EINVAL;
+ goto error;
}
} else if ((c & 0xf8) == 0xf0) {
/* four bytes character */
@@ -228,17 +223,17 @@ ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
for (uint64_t i = 1; i <= 3; i++) {
aux = (*input)[i];
if ((aux & 0xc0) != 0x80) {
- return LY_EINVAL;
+ goto error;
}
c = (c << 6) | (aux & 0x3f);
}
if ((c < 0x1000) || (c > 0x10ffff)) {
- return LY_EINVAL;
+ goto error;
}
} else {
- return LY_EINVAL;
+ goto error;
}
(*utf8_char) = c;
@@ -247,6 +242,163 @@ ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read)
(*bytes_read) = len;
}
return LY_SUCCESS;
+
+error:
+ if (bytes_read) {
+ (*bytes_read) = 0;
+ }
+ return LY_EINVAL;
+}
+
+/**
+ * @brief Check whether an UTF-8 string is equal to a hex string after a bitwise and.
+ *
+ * (input & 0x[arg1][arg3][arg5]...) == 0x[arg2][arg4][arg6]...
+ *
+ * @param[in] input UTF-8 string.
+ * @param[in] bytes Number of bytes to compare.
+ * @param[in] ... 2x @p bytes number of bytes to perform bitwise and and equality operations.
+ * @return Result of the operation.
+ */
+static int
+ly_utf8_and_equal(const char *input, uint8_t bytes, ...)
+{
+ va_list ap;
+ int i, and, byte;
+
+ va_start(ap, bytes);
+ for (i = 0; i < bytes; ++i) {
+ and = va_arg(ap, int);
+ byte = va_arg(ap, int);
+
+ /* compare each byte */
+ if (((uint8_t)input[i] & and) != (uint8_t)byte) {
+ return 0;
+ }
+ }
+ va_end(ap);
+
+ return 1;
+}
+
+/**
+ * @brief Check whether an UTF-8 string is smaller than a hex string.
+ *
+ * input < 0x[arg1][arg2]...
+ *
+ * @param[in] input UTF-8 string.
+ * @param[in] bytes Number of bytes to compare.
+ * @param[in] ... @p bytes number of bytes to compare with.
+ * @return Result of the operation.
+ */
+static int
+ly_utf8_less(const char *input, uint8_t bytes, ...)
+{
+ va_list ap;
+ int i, byte;
+
+ va_start(ap, bytes);
+ for (i = 0; i < bytes; ++i) {
+ byte = va_arg(ap, int);
+
+ /* compare until bytes differ */
+ if ((uint8_t)input[i] > (uint8_t)byte) {
+ return 0;
+ } else if ((uint8_t)input[i] < (uint8_t)byte) {
+ return 1;
+ }
+ }
+ va_end(ap);
+
+ /* equals */
+ return 0;
+}
+
+/**
+ * @brief Check whether an UTF-8 string is greater than a hex string.
+ *
+ * input > 0x[arg1][arg2]...
+ *
+ * @param[in] input UTF-8 string.
+ * @param[in] bytes Number of bytes to compare.
+ * @param[in] ... @p bytes number of bytes to compare with.
+ * @return Result of the operation.
+ */
+static int
+ly_utf8_greater(const char *input, uint8_t bytes, ...)
+{
+ va_list ap;
+ int i, byte;
+
+ va_start(ap, bytes);
+ for (i = 0; i < bytes; ++i) {
+ byte = va_arg(ap, int);
+
+ /* compare until bytes differ */
+ if ((uint8_t)input[i] > (uint8_t)byte) {
+ return 1;
+ } else if ((uint8_t)input[i] < (uint8_t)byte) {
+ return 0;
+ }
+ }
+ va_end(ap);
+
+ /* equals */
+ return 0;
+}
+
+LY_ERR
+ly_checkutf8(const char *input, size_t in_len, size_t *utf8_len)
+{
+ size_t len;
+
+ if (!(input[0] & 0x80)) {
+ /* one byte character */
+ len = 1;
+
+ if (ly_utf8_less(input, 1, 0x20) && (input[0] != 0x9) && (input[0] != 0xa) && (input[0] != 0xd)) {
+ /* invalid control characters */
+ return LY_EINVAL;
+ }
+ } else if (((input[0] & 0xe0) == 0xc0) && (in_len > 1)) {
+ /* two bytes character */
+ len = 2;
+
+ /* (input < 0xC280) || (input > 0xDFBF) || ((input & 0xE0C0) != 0xC080) */
+ if (ly_utf8_less(input, 2, 0xC2, 0x80) || ly_utf8_greater(input, 2, 0xDF, 0xBF) ||
+ !ly_utf8_and_equal(input, 2, 0xE0, 0xC0, 0xC0, 0x80)) {
+ return LY_EINVAL;
+ }
+ } else if (((input[0] & 0xf0) == 0xe0) && (in_len > 2)) {
+ /* three bytes character */
+ len = 3;
+
+ /* (input >= 0xEDA080) && (input <= 0xEDBFBF) */
+ if (!ly_utf8_less(input, 3, 0xED, 0xA0, 0x80) && !ly_utf8_greater(input, 3, 0xED, 0xBF, 0xBF)) {
+ /* reject UTF-16 surrogates */
+ return LY_EINVAL;
+ }
+
+ /* (input < 0xE0A080) || (input > 0xEFBFBF) || ((input & 0xF0C0C0) != 0xE08080) */
+ if (ly_utf8_less(input, 3, 0xE0, 0xA0, 0x80) || ly_utf8_greater(input, 3, 0xEF, 0xBF, 0xBF) ||
+ !ly_utf8_and_equal(input, 3, 0xF0, 0xE0, 0xC0, 0x80, 0xC0, 0x80)) {
+ return LY_EINVAL;
+ }
+ } else if (((input[0] & 0xf8) == 0xf0) && (in_len > 3)) {
+ /* four bytes character */
+ len = 4;
+
+ /* (input < 0xF0908080) || (input > 0xF48FBFBF) || ((input & 0xF8C0C0C0) != 0xF0808080) */
+ if (ly_utf8_less(input, 4, 0xF0, 0x90, 0x80, 0x80) || ly_utf8_greater(input, 4, 0xF4, 0x8F, 0xBF, 0xBF) ||
+ !ly_utf8_and_equal(input, 4, 0xF8, 0xF0, 0xC0, 0x80, 0xC0, 0x80, 0xC0, 0x80)) {
+ return LY_EINVAL;
+ }
+ } else {
+ return LY_EINVAL;
+ }
+
+ *utf8_len = len;
+ return LY_SUCCESS;
}
LY_ERR
@@ -258,6 +410,7 @@ ly_pututf8(char *dst, uint32_t value, size_t *bytes_written)
(value != 0x09) &&
(value != 0x0a) &&
(value != 0x0d)) {
+ /* valid UTF8 but not YANG string character */
return LY_EINVAL;
}
@@ -337,10 +490,10 @@ ly_utf8len(const char *str, size_t bytes)
return len;
}
-size_t
+int
LY_VCODE_INSTREXP_len(const char *str)
{
- size_t len = 0;
+ int len = 0;
if (!str) {
return len;
diff --git a/src/common.h b/src/common.h
index 264fe81..0fedeae 100644
--- a/src/common.h
+++ b/src/common.h
@@ -1,9 +1,10 @@
/**
* @file common.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief common internal definitions for libyang
*
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -23,7 +24,7 @@
#include "compat.h"
#include "config.h"
#include "context.h"
-#include "hash_table.h"
+#include "hash_table_internal.h"
#include "log.h"
#include "schema_compile.h"
#include "set.h"
@@ -45,17 +46,28 @@ struct lysc_node;
# error "Cannot define THREAD_LOCAL"
#endif
+/** platform-specific environment variable path separator */
+#ifndef _WIN32
+# define PATH_SEPARATOR ":"
+#else
+# define PATH_SEPARATOR ";"
+#endif
+
#define GETMACRO1(_1, NAME, ...) NAME
#define GETMACRO2(_1, _2, NAME, ...) NAME
#define GETMACRO3(_1, _2, _3, NAME, ...) NAME
#define GETMACRO4(_1, _2, _3, _4, NAME, ...) NAME
#define GETMACRO5(_1, _2, _3, _4, _5, NAME, ...) NAME
#define GETMACRO6(_1, _2, _3, _4, _5, _6, NAME, ...) NAME
+#define GETMACRO7(_1, _2, _3, _4, _5, _6, _7, NAME, ...) NAME
/******************************************************************************
* Logger
*****************************************************************************/
+/** size of the last message buffer */
+#define LY_LAST_MSG_SIZE 512
+
extern ATOMIC_T ly_ll;
extern ATOMIC_T ly_log_opts;
@@ -75,7 +87,16 @@ struct ly_log_location_s {
* @param[in] no Error type code.
* @param[in] format Format string to print.
*/
-void ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...);
+void ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...) _FORMAT_PRINTF(4, 5);
+
+/**
+ * @brief Generate data path based on the data and schema nodes stored in the log location.
+ *
+ * @param[in] ctx Context for logging.
+ * @param[out] path Generated data path.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_vlog_build_data_path(const struct ly_ctx *ctx, char **path);
/**
* @brief Print Validation error and store it into the context (if provided).
@@ -85,7 +106,15 @@ void ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char
* @param[in] code Validation error code.
* @param[in] format Format string to print.
*/
-void ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char *format, ...);
+void ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char *format, ...) _FORMAT_PRINTF(4, 5);
+
+/**
+ * @brief Move error items from source to target context replacing any previous ones.
+ *
+ * @param[in] src_ctx Source context to read errors from.
+ * @param[in] trg_ctx Target context to set the errors for.
+ */
+void ly_err_move(struct ly_ctx *src_ctx, struct ly_ctx *trg_ctx);
/**
* @brief Logger's location data setter.
@@ -110,6 +139,21 @@ void ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnod
void ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t path_steps, uint32_t in_steps);
/**
+ * @brief Get the stored data node for logging at the index.
+ *
+ * @param[in] idx Index of the data node.
+ * @return Logged data node, NULL if out of range.
+ */
+const struct lyd_node *ly_log_location_dnode(uint32_t idx);
+
+/**
+ * @brief Get the count of stored data nodes for logging.
+ *
+ * @return Count of the data nodes.
+ */
+uint32_t ly_log_location_dnode_count(void);
+
+/**
* @brief Update location data for logger, not provided arguments (NULLs) are kept (does not override).
*
* @param[in] SCNODE Compiled schema node.
@@ -201,8 +245,10 @@ void ly_log_dbg(uint32_t group, const char *format, ...);
LY_CHECK_ARG_RET1(CTX, ARG4, RETVAL)
#define LY_CHECK_ARG_RET5(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL) LY_CHECK_ARG_RET4(CTX, ARG1, ARG2, ARG3, ARG4, RETVAL);\
LY_CHECK_ARG_RET1(CTX, ARG5, RETVAL)
-#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO6(__VA_ARGS__, LY_CHECK_ARG_RET5, LY_CHECK_ARG_RET4, LY_CHECK_ARG_RET3, \
- LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1, DUMMY) (CTX, __VA_ARGS__)
+#define LY_CHECK_ARG_RET6(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, RETVAL) LY_CHECK_ARG_RET5(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL);\
+ LY_CHECK_ARG_RET1(CTX, ARG6, RETVAL)
+#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO7(__VA_ARGS__, LY_CHECK_ARG_RET6, LY_CHECK_ARG_RET5, LY_CHECK_ARG_RET4, \
+ LY_CHECK_ARG_RET3, LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1, DUMMY) (CTX, __VA_ARGS__)
#define LY_CHECK_CTX_EQUAL_RET2(CTX1, CTX2, RETVAL) if ((CTX1) && (CTX2) && ((CTX1) != (CTX2))) \
{LOGERR(CTX1, LY_EINVAL, "Different contexts mixed in a single function call."); return RETVAL;}
@@ -212,11 +258,12 @@ void ly_log_dbg(uint32_t group, const char *format, ...);
DUMMY) (CTX, __VA_ARGS__)
/* count sequence size for LY_VCODE_INCHILDSTMT validation error code */
-size_t LY_VCODE_INSTREXP_len(const char *str);
+int LY_VCODE_INSTREXP_len(const char *str);
+
/* default maximum characters to print in LY_VCODE_INCHILDSTMT */
#define LY_VCODE_INSTREXP_MAXLEN 20
-#define LY_VCODE_INCHAR LYVE_SYNTAX, "Invalid character 0x%x."
+#define LY_VCODE_INCHAR LYVE_SYNTAX, "Invalid character 0x%hhx."
#define LY_VCODE_INSTREXP LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected %s."
#define LY_VCODE_EOF LYVE_SYNTAX, "Unexpected end-of-input."
#define LY_VCODE_NTERM LYVE_SYNTAX, "%s not terminated."
@@ -300,10 +347,18 @@ size_t LY_VCODE_INSTREXP_len(const char *str);
*****************************************************************************/
/**
+ * @brief Context error hash table record.
+ */
+struct ly_ctx_err_rec {
+ struct ly_err_item *err; /** pointer to the error items, if any */
+ pthread_t tid; /** pthread thread ID */
+};
+
+/**
* @brief Context of the YANG schemas
*/
struct ly_ctx {
- struct dict_table dict; /**< dictionary to effectively store strings used in the context related structures */
+ struct ly_dict dict; /**< dictionary to effectively store strings used in the context related structures */
struct ly_set search_paths; /**< set of directories where to search for schema's imports/includes */
struct ly_set list; /**< set of loaded YANG schemas */
ly_module_imp_clb imp_clb; /**< optional callback for retrieving missing included or imported models */
@@ -316,7 +371,7 @@ struct ly_ctx {
ly_ext_data_clb ext_clb; /**< optional callback for providing extension-specific run-time data for extensions */
void *ext_clb_data; /**< optional private data for ::ly_ctx.ext_clb */
- pthread_key_t errlist_key; /**< key for the thread-specific list of errors related to the context */
+ struct ly_ht *err_ht; /**< hash table of thread-specific list of errors related to the context */
pthread_mutex_t lyb_hash_lock; /**< lock for storing LYB schema hashes in schema nodes */
};
@@ -359,7 +414,6 @@ struct lys_module *ly_ctx_get_module_implemented2(const struct ly_ctx *ctx, cons
*
* @param[in] ptr Memory to reallocate.
* @param[in] size New size of the memory block.
- *
* @return Pointer to the new memory, NULL on error.
*/
void *ly_realloc(void *ptr, size_t size);
@@ -428,7 +482,8 @@ LY_ERR ly_strntou8(const char *nptr, size_t len, uint8_t *ret);
* If no string remains, it is set to NULL.
* @return LY_ERR value.
*/
-LY_ERR ly_value_prefix_next(const char *str_begin, const char *str_end, uint32_t *len, ly_bool *is_prefix, const char **str_next);
+LY_ERR ly_value_prefix_next(const char *str_begin, const char *str_end, uint32_t *len, ly_bool *is_prefix,
+ const char **str_next);
/**
* @brief Wrapper around strlen() to handle NULL strings.
@@ -485,7 +540,18 @@ LY_ERR ly_value_prefix_next(const char *str_begin, const char *str_end, uint32_t
LY_ERR ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read);
/**
- * Store UTF-8 character specified as 4byte integer into the dst buffer.
+ * @brief Check an UTF-8 character is valid.
+ *
+ * @param[in] input Input string to process.
+ * @param[in] in_len Bytes left to read in @p input.
+ * @param[out] utf8_len Length of a valid UTF-8 character.
+ * @return LY_SUCCESS on success
+ * @return LY_EINVAL in case of invalid UTF-8 character.
+ */
+LY_ERR ly_checkutf8(const char *input, size_t in_len, size_t *utf8_len);
+
+/**
+ * @brief Store UTF-8 character specified as 4byte integer into the dst buffer.
*
* UTF-8 mapping:
* 00000000 -- 0000007F: 0xxxxxxx
@@ -495,7 +561,7 @@ LY_ERR ly_getutf8(const char **input, uint32_t *utf8_char, size_t *bytes_read);
*
* Includes checking for valid characters (following RFC 7950, sec 9.4)
*
- * @param[in, out] dst Destination buffer to store the UTF-8 character, must provide enough space (up to 4 bytes) for storing the UTF-8 character.
+ * @param[in,out] dst Destination buffer to store the UTF-8 character, must provide enough space (up to 4 bytes) for storing the UTF-8 character.
* @param[in] value 32b value of the UTF-8 character to store.
* @param[out] bytes_written Number of bytes written into @p dst (size of the written UTF-8 character).
* @return LY_SUCCESS on success
@@ -505,6 +571,7 @@ LY_ERR ly_pututf8(char *dst, uint32_t value, size_t *bytes_written);
/**
* @brief Get number of characters in the @p str, taking multibyte characters into account.
+ *
* @param[in] str String to examine.
* @param[in] bytes Number of valid bytes that are supposed to be taken into account in @p str.
* This parameter is useful mainly for non NULL-terminated strings. In case of NULL-terminated
@@ -515,6 +582,7 @@ size_t ly_utf8len(const char *str, size_t bytes);
/**
* @brief Parse signed integer with possible limitation.
+ *
* @param[in] val_str String value containing signed integer, note that
* nothing else than whitespaces are expected after the value itself.
* @param[in] val_len Length of the @p val_str string.
@@ -533,6 +601,7 @@ LY_ERR ly_parse_int(const char *val_str, size_t val_len, int64_t min, int64_t ma
/**
* @brief Parse unsigned integer with possible limitation.
+ *
* @param[in] val_str String value containing unsigned integer, note that
* nothing else than whitespaces are expected after the value itself.
* @param[in] val_len Length of the @p val_str string.
@@ -553,7 +622,7 @@ LY_ERR ly_parse_uint(const char *val_str, size_t val_len, uint64_t max, int base
*
* node-identifier = [prefix ":"] identifier
*
- * @param[in, out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
* @param[out] prefix Node's prefix, NULL if there is not any.
* @param[out] prefix_len Length of the node's prefix, 0 if there is not any.
* @param[out] name Node's name.
@@ -565,7 +634,7 @@ LY_ERR ly_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len,
/**
* @brief parse instance-identifier's predicate, supports key-predicate, leaf-list-predicate and pos rules from YANG ABNF Grammar.
*
- * @param[in, out] pred Predicate string (including the leading '[') to parse. The string is updated according to what was parsed
+ * @param[in,out] pred Predicate string (including the leading '[') to parse. The string is updated according to what was parsed
* (even for error case, so it can be used to determine which substring caused failure).
* @param[in] limit Limiting length of the @p pred. Function expects NULL terminated string which is not overread.
* The limit value is not checked with each character, so it can be overread and the failure is detected later.
@@ -610,17 +679,11 @@ LY_ERR ly_munmap(void *addr, size_t length);
/**
* @brief Concatenate formating string to the @p dest.
*
- * @param[in, out] dest String to be concatenated by @p format.
- * Note that the input string can be reallocated during concatenation.
+ * @param[in,out] dest String to be concatenated by @p format.
+ * Note that the input string can be reallocated during concatenation.
* @param[in] format Formating string (as for printf) which is supposed to be added after @p dest.
* @return LY_SUCCESS or LY_EMEM.
*/
-LY_ERR ly_strcat(char **dest, const char *format, ...);
-
-#ifndef _WIN32
-# define PATH_SEPARATOR ":"
-#else
-# define PATH_SEPARATOR ";"
-#endif
+LY_ERR ly_strcat(char **dest, const char *format, ...) _FORMAT_PRINTF(2, 3);
#endif /* LY_COMMON_H_ */
diff --git a/src/context.c b/src/context.c
index 47e63d4..7a14203 100644
--- a/src/context.c
+++ b/src/context.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Context implementations
*
- * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -228,6 +228,17 @@ cleanup:
return mod;
}
+/**
+ * @brief Hash table value-equal callback for comparing context error hash table record.
+ */
+static ly_bool
+ly_ctx_ht_err_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+ struct ly_ctx_err_rec *err1 = val1_p, *err2 = val2_p;
+
+ return !memcmp(&err1->tid, &err2->tid, sizeof err1->tid);
+}
+
LIBYANG_API_DEF LY_ERR
ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx)
{
@@ -251,8 +262,9 @@ ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx)
/* plugins */
LY_CHECK_ERR_GOTO(lyplg_init(), LOGINT(NULL); rc = LY_EINT, cleanup);
- /* initialize thread-specific keys */
- while ((pthread_key_create(&ctx->errlist_key, ly_err_free)) == EAGAIN) {}
+ /* initialize thread-specific error hash table */
+ ctx->err_ht = lyht_new(1, sizeof(struct ly_ctx_err_rec), ly_ctx_ht_err_equal_cb, NULL, 1);
+ LY_CHECK_ERR_GOTO(!ctx->err_ht, rc = LY_EMEM, cleanup);
/* init LYB hash lock */
pthread_mutex_init(&ctx->lyb_hash_lock, NULL);
@@ -657,6 +669,39 @@ ly_ctx_get_change_count(const struct ly_ctx *ctx)
return ctx->change_count;
}
+LIBYANG_API_DEF uint32_t
+ly_ctx_get_modules_hash(const struct ly_ctx *ctx)
+{
+ const struct lys_module *mod;
+ uint32_t i = ly_ctx_internal_modules_count(ctx), hash = 0, fi = 0;
+ struct lysp_feature *f = NULL;
+
+ LY_CHECK_ARG_RET(ctx, ctx, 0);
+
+ while ((mod = ly_ctx_get_module_iter(ctx, &i))) {
+ /* name */
+ hash = lyht_hash_multi(hash, mod->name, strlen(mod->name));
+
+ /* revision */
+ if (mod->revision) {
+ hash = lyht_hash_multi(hash, mod->revision, strlen(mod->revision));
+ }
+
+ /* enabled features */
+ while ((f = lysp_feature_next(f, mod->parsed, &fi))) {
+ if (f->flags & LYS_FENABLED) {
+ hash = lyht_hash_multi(hash, f->name, strlen(f->name));
+ }
+ }
+
+ /* imported/implemented */
+ hash = lyht_hash_multi(hash, (char *)&mod->implemented, sizeof mod->implemented);
+ }
+
+ hash = lyht_hash_multi(hash, NULL, 0);
+ return hash;
+}
+
LIBYANG_API_DEF ly_module_imp_clb
ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data)
{
@@ -1228,6 +1273,19 @@ error:
return ret;
}
+/**
+ * @brief Callback for freeing context error hash table values.
+ *
+ * @param[in] val_p Pointer to a pointer to an error item to free with all the siblings.
+ */
+static void
+ly_ctx_ht_err_rec_free(void *val_p)
+{
+ struct ly_ctx_err_rec *err = val_p;
+
+ ly_err_free(err->err);
+}
+
LIBYANG_API_DEF void
ly_ctx_destroy(struct ly_ctx *ctx)
{
@@ -1260,9 +1318,8 @@ ly_ctx_destroy(struct ly_ctx *ctx)
/* leftover unres */
lys_unres_glob_erase(&ctx->unres);
- /* clean the error list */
- ly_err_clean(ctx, 0);
- pthread_key_delete(ctx->errlist_key);
+ /* clean the error hash table */
+ lyht_free(ctx->err_ht, ly_ctx_ht_err_rec_free);
/* dictionary */
lydict_clean(&ctx->dict);
diff --git a/src/context.h b/src/context.h
index 331a89f..a6367f5 100644
--- a/src/context.h
+++ b/src/context.h
@@ -1,9 +1,10 @@
/**
* @file context.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief internal context structures and functions
*
- * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -196,6 +197,8 @@ struct ly_ctx;
#define LY_CTX_ENABLE_IMP_FEATURES 0x0100 /**< By default, all features of newly implemented imported modules of
a module that is being loaded are disabled. With this flag they all become
enabled. */
+#define LY_CTX_LEAFREF_EXTENDED 0x0200 /**< By default, path attribute of leafref accepts only path as defined in RFC 7950.
+ By using this option, the path attribute will also allow using XPath functions as deref() */
/** @} contextoptions */
@@ -370,6 +373,18 @@ LIBYANG_API_DECL LY_ERR ly_ctx_unset_options(struct ly_ctx *ctx, uint16_t option
LIBYANG_API_DECL uint16_t ly_ctx_get_change_count(const struct ly_ctx *ctx);
/**
+ * @brief Get the hash of all the modules in the context. Since order of the modules is significant,
+ * even when 2 contexts have the same modules but loaded in a different order, the hash will differ.
+ *
+ * Hash consists of all module names (1), their revisions (2), all enabled features (3), and their
+ * imported/implemented state (4).
+ *
+ * @param[in] ctx Context to be examined.
+ * @return Context modules hash.
+ */
+LIBYANG_API_DECL uint32_t ly_ctx_get_modules_hash(const struct ly_ctx *ctx);
+
+/**
* @brief Callback for freeing returned module data in #ly_module_imp_clb.
*
* @param[in] module_data Data to free.
@@ -630,7 +645,8 @@ LIBYANG_API_DECL struct lys_module *ly_ctx_load_module(struct ly_ctx *ctx, const
* If the data identifier can be limited to the existence and changes of this context, the following
* last 2 parameters can be used:
*
- * "%u" as @p content_id_format and ::ly_ctx_get_change_count() as its parameter.
+ * "%u" as @p content_id_format and ::ly_ctx_get_change_count() as its parameter;
+ * "%u" as @p content_id_format and ::ly_ctx_get_modules_hash() as its parameter.
*
* @param[in] ctx Context with the modules.
* @param[out] root Generated yang-library data.
diff --git a/src/dict.c b/src/dict.c
new file mode 100644
index 0000000..e1426ca
--- /dev/null
+++ b/src/dict.c
@@ -0,0 +1,271 @@
+/**
+ * @file dict.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang dictionary for storing strings
+ *
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "dict.h"
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "log.h"
+
+/* starting size of the dictionary */
+#define LYDICT_MIN_SIZE 1024
+
+/**
+ * @brief Comparison callback for dictionary's hash table
+ *
+ * Implementation of ::lyht_value_equal_cb.
+ */
+static ly_bool
+lydict_val_eq(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
+{
+ const char *str1, *str2;
+ size_t *len1;
+
+ LY_CHECK_ARG_RET(NULL, val1_p, val2_p, cb_data, 0);
+
+ str1 = ((struct ly_dict_rec *)val1_p)->value;
+ str2 = ((struct ly_dict_rec *)val2_p)->value;
+ len1 = cb_data;
+
+ LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
+ LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
+
+ if (!strncmp(str1, str2, *len1) && !str2[*len1]) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+lydict_init(struct ly_dict *dict)
+{
+ LY_CHECK_ARG_RET(NULL, dict, );
+
+ dict->hash_tab = lyht_new(LYDICT_MIN_SIZE, sizeof(struct ly_dict_rec), lydict_val_eq, NULL, 1);
+ LY_CHECK_ERR_RET(!dict->hash_tab, LOGINT(NULL), );
+ pthread_mutex_init(&dict->lock, NULL);
+}
+
+void
+lydict_clean(struct ly_dict *dict)
+{
+ struct ly_dict_rec *dict_rec = NULL;
+ struct ly_ht_rec *rec = NULL;
+ uint32_t hlist_idx;
+ uint32_t rec_idx;
+
+ LY_CHECK_ARG_RET(NULL, dict, );
+
+ LYHT_ITER_ALL_RECS(dict->hash_tab, hlist_idx, rec_idx, rec) {
+ /*
+ * this should not happen, all records inserted into
+ * dictionary are supposed to be removed using lydict_remove()
+ * before calling lydict_clean()
+ */
+ dict_rec = (struct ly_dict_rec *)rec->val;
+ LOGWRN(NULL, "String \"%s\" not freed from the dictionary, refcount %d", dict_rec->value, dict_rec->refcount);
+ /* if record wasn't removed before free string allocated for that record */
+#ifdef NDEBUG
+ free(dict_rec->value);
+#endif
+ }
+
+ /* free table and destroy mutex */
+ lyht_free(dict->hash_tab, NULL);
+ pthread_mutex_destroy(&dict->lock);
+}
+
+static ly_bool
+lydict_resize_val_eq(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(cb_data))
+{
+ const char *str1, *str2;
+
+ LY_CHECK_ARG_RET(NULL, val1_p, val2_p, 0);
+
+ str1 = ((struct ly_dict_rec *)val1_p)->value;
+ str2 = ((struct ly_dict_rec *)val2_p)->value;
+
+ LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
+ LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
+
+ if (mod) {
+ /* used when inserting new values */
+ if (strcmp(str1, str2) == 0) {
+ return 1;
+ }
+ } else {
+ /* used when finding the original value again in the resized table */
+ if (str1 == str2) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_remove(const struct ly_ctx *ctx, const char *value)
+{
+ LY_ERR ret = LY_SUCCESS;
+ size_t len;
+ uint32_t hash;
+ struct ly_dict_rec rec, *match = NULL;
+ char *val_p;
+
+ if (!ctx || !value) {
+ return LY_SUCCESS;
+ }
+
+ LOGDBG(LY_LDGDICT, "removing \"%s\"", value);
+
+ len = strlen(value);
+ hash = lyht_hash(value, len);
+
+ /* create record for lyht_find call */
+ rec.value = (char *)value;
+ rec.refcount = 0;
+
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+ /* set len as data for compare callback */
+ lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
+ /* check if value is already inserted */
+ ret = lyht_find(ctx->dict.hash_tab, &rec, hash, (void **)&match);
+
+ if (ret == LY_SUCCESS) {
+ LY_CHECK_ERR_GOTO(!match, LOGINT(ctx), finish);
+
+ /* if value is already in dictionary, decrement reference counter */
+ match->refcount--;
+ if (match->refcount == 0) {
+ /*
+ * remove record
+ * save pointer to stored string before lyht_remove to
+ * free it after it is removed from hash table
+ */
+ val_p = match->value;
+ ret = lyht_remove_with_resize_cb(ctx->dict.hash_tab, &rec, hash, lydict_resize_val_eq);
+ free(val_p);
+ LY_CHECK_ERR_GOTO(ret, LOGINT(ctx), finish);
+ }
+ } else if (ret == LY_ENOTFOUND) {
+ LOGERR(ctx, LY_ENOTFOUND, "Value \"%s\" was not found in the dictionary.", value);
+ } else {
+ LOGINT(ctx);
+ }
+
+finish:
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+ return ret;
+}
+
+LY_ERR
+dict_insert(const struct ly_ctx *ctx, char *value, size_t len, ly_bool zerocopy, const char **str_p)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_dict_rec *match = NULL, rec;
+ uint32_t hash;
+
+ LOGDBG(LY_LDGDICT, "inserting \"%.*s\"", (int)len, value);
+
+ hash = lyht_hash(value, len);
+ /* set len as data for compare callback */
+ lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
+ /* create record for lyht_insert */
+ rec.value = value;
+ rec.refcount = 1;
+
+ ret = lyht_insert_with_resize_cb(ctx->dict.hash_tab, (void *)&rec, hash, lydict_resize_val_eq, (void **)&match);
+ if (ret == LY_EEXIST) {
+ match->refcount++;
+ if (zerocopy) {
+ free(value);
+ }
+ ret = LY_SUCCESS;
+ } else if (ret == LY_SUCCESS) {
+ if (!zerocopy) {
+ /*
+ * allocate string for new record
+ * record is already inserted in hash table
+ */
+ match->value = malloc(sizeof *match->value * (len + 1));
+ LY_CHECK_ERR_RET(!match->value, LOGMEM(ctx), LY_EMEM);
+ if (len) {
+ memcpy(match->value, value, len);
+ }
+ match->value[len] = '\0';
+ }
+ } else {
+ /* lyht_insert returned error */
+ if (zerocopy) {
+ free(value);
+ }
+ return ret;
+ }
+
+ if (str_p) {
+ *str_p = match->value;
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
+{
+ LY_ERR result;
+
+ LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
+
+ if (!value) {
+ *str_p = NULL;
+ return LY_SUCCESS;
+ }
+
+ if (!len) {
+ len = strlen(value);
+ }
+
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+ result = dict_insert(ctx, (char *)value, len, 0, str_p);
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+
+ return result;
+}
+
+LIBYANG_API_DEF LY_ERR
+lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
+{
+ LY_ERR result;
+
+ LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
+
+ if (!value) {
+ *str_p = NULL;
+ return LY_SUCCESS;
+ }
+
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
+ result = dict_insert(ctx, value, strlen(value), 1, str_p);
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
+
+ return result;
+}
diff --git a/src/diff.c b/src/diff.c
index edfcb34..f30d44a 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -161,6 +161,127 @@ cleanup:
return rc;
}
+/**
+ * @brief Find metadata/an attribute of a node.
+ *
+ * @param[in] node Node to search.
+ * @param[in] name Metadata/attribute name.
+ * @param[out] meta Metadata found, NULL if not found.
+ * @param[out] attr Attribute found, NULL if not found.
+ */
+static void
+lyd_diff_find_meta(const struct lyd_node *node, const char *name, struct lyd_meta **meta, struct lyd_attr **attr)
+{
+ struct lyd_meta *m;
+ struct lyd_attr *a;
+
+ if (meta) {
+ *meta = NULL;
+ }
+ if (attr) {
+ *attr = NULL;
+ }
+
+ if (node->schema) {
+ assert(meta);
+
+ LY_LIST_FOR(node->meta, m) {
+ if (!strcmp(m->name, name) && !strcmp(m->annotation->module->name, "yang")) {
+ *meta = m;
+ break;
+ }
+ }
+ } else {
+ assert(attr);
+
+ LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, a) {
+ /* name */
+ if (strcmp(a->name.name, name)) {
+ continue;
+ }
+
+ /* module */
+ switch (a->format) {
+ case LY_VALUE_JSON:
+ if (strcmp(a->name.module_name, "yang")) {
+ continue;
+ }
+ break;
+ case LY_VALUE_XML:
+ if (strcmp(a->name.module_ns, "urn:ietf:params:xml:ns:yang:1")) {
+ continue;
+ }
+ break;
+ default:
+ LOGINT(LYD_CTX(node));
+ return;
+ }
+
+ *attr = a;
+ break;
+ }
+ }
+}
+
+/**
+ * @brief Learn operation of a diff node.
+ *
+ * @param[in] diff_node Diff node.
+ * @param[out] op Operation.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
+{
+ struct lyd_meta *meta = NULL;
+ struct lyd_attr *attr = NULL;
+ const struct lyd_node *diff_parent;
+ const char *str;
+ char *path;
+
+ for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
+ lyd_diff_find_meta(diff_parent, "operation", &meta, &attr);
+ if (!meta && !attr) {
+ continue;
+ }
+
+ str = meta ? lyd_get_meta_value(meta) : attr->value;
+ if ((str[0] == 'r') && (diff_parent != diff_node)) {
+ /* we do not care about this operation if it's in our parent */
+ continue;
+ }
+ *op = lyd_diff_str2op(str);
+ return LY_SUCCESS;
+ }
+
+ /* operation not found */
+ path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
+ LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
+ free(path);
+ return LY_EINT;
+}
+
+/**
+ * @brief Remove metadata/an attribute from a node.
+ *
+ * @param[in] node Node to update.
+ * @param[in] name Metadata/attribute name.
+ */
+static void
+lyd_diff_del_meta(struct lyd_node *node, const char *name)
+{
+ struct lyd_meta *meta;
+ struct lyd_attr *attr;
+
+ lyd_diff_find_meta(node, name, &meta, &attr);
+
+ if (meta) {
+ lyd_free_meta_single(meta);
+ } else if (attr) {
+ lyd_free_attr_single(LYD_CTX(node), attr);
+ }
+}
+
LY_ERR
lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_default, const char *orig_value,
const char *key, const char *value, const char *position, const char *orig_key, const char *orig_position,
@@ -168,6 +289,9 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_
{
struct lyd_node *dup, *siblings, *match = NULL, *diff_parent = NULL, *elem;
const struct lyd_node *parent = NULL;
+ enum lyd_diff_op cur_op;
+ struct lyd_meta *meta;
+ uint32_t diff_opts;
assert(diff);
@@ -189,14 +313,16 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_
/* find the first existing parent */
siblings = *diff;
- while (1) {
+ do {
/* find next node parent */
parent = node;
while (parent->parent && (!diff_parent || (parent->parent->schema != diff_parent->schema))) {
parent = lyd_parent(parent);
}
- if (parent == node) {
- /* no more parents to find */
+
+ if (lysc_is_dup_inst_list(parent->schema)) {
+ /* assume it never exists, we are not able to distinguish whether it does or not */
+ match = NULL;
break;
}
@@ -210,36 +336,75 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_
/* move down in the diff */
siblings = lyd_child_no_keys(match);
- }
+ } while (parent != node);
+
+ if (match && (parent == node)) {
+ /* special case when there is already an operation on our descendant */
+ assert(!lyd_diff_get_op(diff_parent, &cur_op) && (cur_op == LYD_DIFF_OP_NONE));
+ (void)cur_op;
+
+ /* move it to the end where it is expected (matters for user-ordered lists) */
+ if (lysc_is_userordered(diff_parent->schema)) {
+ for (elem = diff_parent; elem->next && (elem->next->schema == elem->schema); elem = elem->next) {}
+ if (elem != diff_parent) {
+ LY_CHECK_RET(lyd_insert_after(elem, diff_parent));
+ }
+ }
- /* duplicate the subtree (and connect to the diff if possible) */
- LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent,
- LYD_DUP_RECURSIVE | LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS, &dup));
+ /* will be replaced by the new operation but keep the current op for descendants */
+ lyd_diff_del_meta(diff_parent, "operation");
+ LY_LIST_FOR(lyd_child_no_keys(diff_parent), elem) {
+ lyd_diff_find_meta(elem, "operation", &meta, NULL);
+ if (meta) {
+ /* explicit operation, fine */
+ continue;
+ }
- /* find the first duplicated parent */
- if (!diff_parent) {
- diff_parent = lyd_parent(dup);
- while (diff_parent && diff_parent->parent) {
- diff_parent = lyd_parent(diff_parent);
+ /* set the none operation */
+ LY_CHECK_RET(lyd_new_meta(NULL, elem, NULL, "yang:operation", "none", 0, NULL));
}
+
+ dup = diff_parent;
} else {
- diff_parent = dup;
- while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
- diff_parent = lyd_parent(diff_parent);
+ diff_opts = LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_WITH_FLAGS;
+ if ((op != LYD_DIFF_OP_REPLACE) || !lysc_is_userordered(node->schema) || (node->schema->flags & LYS_CONFIG_R)) {
+ /* move applies only to the user-ordered list, no descendants */
+ diff_opts |= LYD_DUP_RECURSIVE;
}
- }
- /* no parent existed, must be manually connected */
- if (!diff_parent) {
- /* there actually was no parent to duplicate */
- lyd_insert_sibling(*diff, dup, diff);
- } else if (!diff_parent->parent) {
- lyd_insert_sibling(*diff, diff_parent, diff);
- }
+ /* duplicate the subtree (and connect to the diff if possible) */
+ if (diff_parent) {
+ LY_CHECK_RET(lyd_dup_single_to_ctx(node, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent,
+ diff_opts, &dup));
+ } else {
+ LY_CHECK_RET(lyd_dup_single(node, NULL, diff_opts, &dup));
+ }
+
+ /* find the first duplicated parent */
+ if (!diff_parent) {
+ diff_parent = lyd_parent(dup);
+ while (diff_parent && diff_parent->parent) {
+ diff_parent = lyd_parent(diff_parent);
+ }
+ } else {
+ diff_parent = dup;
+ while (diff_parent->parent && (diff_parent->parent->schema == parent->schema)) {
+ diff_parent = lyd_parent(diff_parent);
+ }
+ }
+
+ /* no parent existed, must be manually connected */
+ if (!diff_parent) {
+ /* there actually was no parent to duplicate */
+ lyd_insert_sibling(*diff, dup, diff);
+ } else if (!diff_parent->parent) {
+ lyd_insert_sibling(*diff, diff_parent, diff);
+ }
- /* add parent operation, if any */
- if (diff_parent && (diff_parent != dup)) {
- LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
+ /* add parent operation, if any */
+ if (diff_parent && (diff_parent != dup)) {
+ LY_CHECK_RET(lyd_new_meta(NULL, diff_parent, NULL, "yang:operation", "none", 0, NULL));
+ }
}
/* add subtree operation */
@@ -361,7 +526,7 @@ lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *seco
LY_ERR rc = LY_SUCCESS;
const struct lysc_node *schema;
size_t buflen, bufused;
- uint32_t first_pos, second_pos;
+ uint32_t first_pos, second_pos, comp_opts;
assert(first || second);
@@ -397,7 +562,8 @@ lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *seco
} else if (!first) {
*op = LYD_DIFF_OP_CREATE;
} else {
- if (lyd_compare_single(second, userord_item->inst[second_pos], 0)) {
+ comp_opts = lysc_is_dup_inst_list(second->schema) ? LYD_COMPARE_FULL_RECURSION : 0;
+ if (lyd_compare_single(second, userord_item->inst[second_pos], comp_opts)) {
/* in first, there is a different instance on the second position, we are going to move 'first' node */
*op = LYD_DIFF_OP_REPLACE;
} else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) {
@@ -648,17 +814,20 @@ lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, uint
* @param[in] siblings Siblings to search in.
* @param[in] target Target node to search for.
* @param[in] defaults Whether to consider (or ignore) default values.
- * @param[in,out] dup_inst_cache Duplicate instance cache.
+ * @param[in,out] dup_inst_ht Duplicate instance cache.
* @param[out] match Found match, NULL if no matching node found.
* @return LY_ERR value.
*/
static LY_ERR
lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *target, ly_bool defaults,
- struct lyd_dup_inst **dup_inst_cache, struct lyd_node **match)
+ struct ly_ht **dup_inst_ht, struct lyd_node **match)
{
LY_ERR r;
- if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+ if (!target->schema) {
+ /* try to find the same opaque node */
+ r = lyd_find_sibling_opaq_next(siblings, LYD_NAME(target), match);
+ } else if (target->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
/* try to find the exact instance */
r = lyd_find_sibling_first(siblings, target, match);
} else {
@@ -670,7 +839,7 @@ lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *targ
}
/* update match as needed */
- LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_cache));
+ LY_CHECK_RET(lyd_dup_inst_next(match, siblings, dup_inst_ht));
if (*match && ((*match)->flags & LYD_DEFAULT) && !defaults) {
/* ignore default nodes */
@@ -728,7 +897,7 @@ lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second,
const struct lyd_node *iter_first, *iter_second;
struct lyd_node *match_second, *match_first;
struct lyd_diff_userord *userord = NULL, *userord_item;
- struct lyd_dup_inst *dup_inst_first = NULL, *dup_inst_second = NULL;
+ struct ly_ht *dup_inst_first = NULL, *dup_inst_second = NULL;
LY_ARRAY_COUNT_TYPE u;
enum lyd_diff_op op;
const char *orig_default;
@@ -920,48 +1089,6 @@ lyd_diff_siblings(const struct lyd_node *first, const struct lyd_node *second, u
}
/**
- * @brief Learn operation of a diff node.
- *
- * @param[in] diff_node Diff node.
- * @param[out] op Operation.
- * @return LY_ERR value.
- */
-static LY_ERR
-lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op)
-{
- struct lyd_meta *meta = NULL;
- const struct lyd_node *diff_parent;
- const char *str;
- char *path;
-
- for (diff_parent = diff_node; diff_parent; diff_parent = lyd_parent(diff_parent)) {
- LY_LIST_FOR(diff_parent->meta, meta) {
- if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
- str = lyd_get_meta_value(meta);
- if ((str[0] == 'r') && (diff_parent != diff_node)) {
- /* we do not care about this operation if it's in our parent */
- continue;
- }
- *op = lyd_diff_str2op(str);
- break;
- }
- }
- if (meta) {
- break;
- }
- }
-
- if (!meta) {
- path = lyd_path(diff_node, LYD_PATH_STD, NULL, 0);
- LOGERR(LYD_CTX(diff_node), LY_EINVAL, "Node \"%s\" without an operation.", path);
- free(path);
- return LY_EINT;
- }
-
- return LY_SUCCESS;
-}
-
-/**
* @brief Insert a diff node into a data tree.
*
* @param[in,out] first_node First sibling of the data tree.
@@ -1079,14 +1206,14 @@ lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, stru
*/
static LY_ERR
lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, const struct lyd_node *diff_node,
- lyd_diff_cb diff_cb, void *cb_data, struct lyd_dup_inst **dup_inst)
+ lyd_diff_cb diff_cb, void *cb_data, struct ly_ht **dup_inst)
{
LY_ERR ret;
struct lyd_node *match, *diff_child;
const char *str_val, *meta_str;
enum lyd_diff_op op;
struct lyd_meta *meta;
- struct lyd_dup_inst *child_dup_inst = NULL;
+ struct ly_ht *child_dup_inst = NULL;
const struct ly_ctx *ctx = LYD_CTX(diff_node);
/* read all the valid attributes */
@@ -1242,7 +1369,7 @@ lyd_diff_apply_module(struct lyd_node **data, const struct lyd_node *diff, const
lyd_diff_cb diff_cb, void *cb_data)
{
const struct lyd_node *root;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
LY_ERR ret = LY_SUCCESS;
LY_LIST_FOR(diff, root) {
@@ -1299,27 +1426,6 @@ lyd_diff_merge_none(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const
}
/**
- * @brief Remove an attribute from a node.
- *
- * @param[in] node Node with the metadata.
- * @param[in] name Metadata name.
- */
-static void
-lyd_diff_del_meta(struct lyd_node *node, const char *name)
-{
- struct lyd_meta *meta;
-
- LY_LIST_FOR(node->meta, meta) {
- if (!strcmp(meta->name, name) && !strcmp(meta->annotation->module->name, "yang")) {
- lyd_free_meta_single(meta);
- return;
- }
- }
-
- assert(0);
-}
-
-/**
* @brief Set a specific operation of a node. Delete the previous operation, if any.
* Does not change the default flag.
*
@@ -1330,16 +1436,13 @@ lyd_diff_del_meta(struct lyd_node *node, const char *name)
static LY_ERR
lyd_diff_change_op(struct lyd_node *node, enum lyd_diff_op op)
{
- struct lyd_meta *meta;
+ lyd_diff_del_meta(node, "operation");
- LY_LIST_FOR(node->meta, meta) {
- if (!strcmp(meta->name, "operation") && !strcmp(meta->annotation->module->name, "yang")) {
- lyd_free_meta_single(meta);
- break;
- }
+ if (node->schema) {
+ return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
+ } else {
+ return lyd_new_attr(node, "yang", "operation", lyd_diff_op2str(op), NULL);
}
-
- return lyd_new_meta(LYD_CTX(node), node, NULL, "yang:operation", lyd_diff_op2str(op), 0, NULL);
}
/**
@@ -1431,28 +1534,43 @@ lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, con
}
break;
case LYD_DIFF_OP_NONE:
- /* it is moved now */
- assert(lysc_is_userordered(diff_match->schema) && (diff_match->schema->nodetype == LYS_LIST));
+ switch (diff_match->schema->nodetype) {
+ case LYS_LIST:
+ /* it is moved now */
+ assert(lysc_is_userordered(diff_match->schema));
- /* change the operation */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+ /* change the operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
- /* set orig-meta and meta */
- if (lysc_is_dup_inst_list(diff_match->schema)) {
- meta_name = "position";
- orig_meta_name = "orig-position";
- } else {
- meta_name = "key";
- orig_meta_name = "orig-key";
- }
+ /* set orig-meta and meta */
+ if (lysc_is_dup_inst_list(diff_match->schema)) {
+ meta_name = "position";
+ orig_meta_name = "orig-position";
+ } else {
+ meta_name = "key";
+ orig_meta_name = "orig-key";
+ }
+
+ meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
+ LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
- meta = lyd_find_meta(src_diff->meta, mod, orig_meta_name);
- LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, orig_meta_name, src_diff), LY_EINVAL);
- LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ meta = lyd_find_meta(src_diff->meta, mod, meta_name);
+ LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
+ LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ break;
+ case LYS_LEAF:
+ /* only dflt flag changed, now value changed as well, update the operation */
+ LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
- meta = lyd_find_meta(src_diff->meta, mod, meta_name);
- LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
- LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ /* modify the node value */
+ if (lyd_change_term(diff_match, lyd_get_value(src_diff))) {
+ LOGINT_RET(LYD_CTX(src_diff));
+ }
+ break;
+ default:
+ LOGINT_RET(LYD_CTX(src_diff));
+ }
break;
default:
/* delete operation is not valid */
@@ -1466,33 +1584,41 @@ lyd_diff_merge_replace(struct lyd_node *diff_match, enum lyd_diff_op cur_op, con
/**
* @brief Update operations in a diff node when the new operation is CREATE.
*
- * @param[in] diff_match Node from the diff.
+ * @param[in,out] diff_match Node from the diff, may be replaced.
+ * @param[in,out] diff Diff root node, may be updated.
* @param[in] cur_op Current operation of @p diff_match.
* @param[in] src_diff Current source diff node.
* @param[in] options Diff merge options.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, const struct lyd_node *src_diff, uint16_t options)
+lyd_diff_merge_create(struct lyd_node **diff_match, struct lyd_node **diff, enum lyd_diff_op cur_op,
+ const struct lyd_node *src_diff, uint16_t options)
{
- struct lyd_node *child;
+ struct lyd_node *child, *src_dup, *to_free = NULL;
const struct lysc_node_leaf *sleaf = NULL;
uint32_t trg_flags;
const char *meta_name, *orig_meta_name;
struct lyd_meta *meta, *orig_meta;
- const struct ly_ctx *ctx = LYD_CTX(diff_match);
+ const struct ly_ctx *ctx = LYD_CTX(*diff_match);
+ LY_ERR r;
+
+ /* create operation is valid only for data nodes */
+ LY_CHECK_ERR_RET(!src_diff->schema, LOGINT(ctx), LY_EINT);
switch (cur_op) {
case LYD_DIFF_OP_DELETE:
/* remember current flags */
- trg_flags = diff_match->flags;
+ trg_flags = (*diff_match)->flags;
+
+ if (lysc_is_userordered(src_diff->schema)) {
+ assert((*diff_match)->schema);
- if (lysc_is_userordered(diff_match->schema)) {
/* get anchor metadata */
- if (lysc_is_dup_inst_list(diff_match->schema)) {
+ if (lysc_is_dup_inst_list((*diff_match)->schema)) {
meta_name = "yang:position";
orig_meta_name = "yang:orig-position";
- } else if (diff_match->schema->nodetype == LYS_LIST) {
+ } else if ((*diff_match)->schema->nodetype == LYS_LIST) {
meta_name = "yang:key";
orig_meta_name = "yang:orig-key";
} else {
@@ -1501,71 +1627,86 @@ lyd_diff_merge_create(struct lyd_node *diff_match, enum lyd_diff_op cur_op, cons
}
meta = lyd_find_meta(src_diff->meta, NULL, meta_name);
LY_CHECK_ERR_RET(!meta, LOGERR_META(ctx, meta_name, src_diff), LY_EINVAL);
- orig_meta = lyd_find_meta(diff_match->meta, NULL, orig_meta_name);
- LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, diff_match), LY_EINVAL);
+ orig_meta = lyd_find_meta((*diff_match)->meta, NULL, orig_meta_name);
+ LY_CHECK_ERR_RET(!orig_meta, LOGERR_META(ctx, orig_meta_name, *diff_match), LY_EINVAL);
/* the (incorrect) assumption made here is that there are no previous diff nodes that would affect
* the anchors stored in the metadata */
if (strcmp(lyd_get_meta_value(meta), lyd_get_meta_value(orig_meta))) {
/* deleted + created at another position -> operation REPLACE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE));
/* add anchor metadata */
- LY_CHECK_RET(lyd_dup_meta_single(meta, diff_match, NULL));
+ LY_CHECK_RET(lyd_dup_meta_single(meta, *diff_match, NULL));
} else {
/* deleted + created at the same position -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
/* delete anchor metadata */
lyd_free_meta_single(orig_meta);
}
- } else if (diff_match->schema->nodetype == LYS_LEAF) {
+ } else if (src_diff->schema->nodetype == LYS_LEAF) {
if (options & LYD_DIFF_MERGE_DEFAULTS) {
/* we are dealing with a leaf and are handling default values specially (as explicit nodes) */
- sleaf = (struct lysc_node_leaf *)diff_match->schema;
+ sleaf = (struct lysc_node_leaf *)src_diff->schema;
}
if (sleaf && sleaf->dflt && !sleaf->dflt->realtype->plugin->compare(sleaf->dflt,
&((struct lyd_node_term *)src_diff)->value)) {
/* we deleted it, so a default value was in-use, and it matches the created value -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
- } else if (!lyd_compare_single(diff_match, src_diff, 0)) {
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
+ } else if (!lyd_compare_single(*diff_match, src_diff, 0)) {
/* deleted + created -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
- } else {
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
+ } else if ((*diff_match)->schema) {
/* we deleted it, but it was created with a different value -> operation REPLACE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_REPLACE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_REPLACE));
/* current value is the previous one (meta) */
- LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-value",
- lyd_get_value(diff_match), 0, NULL));
+ LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-value",
+ lyd_get_value(*diff_match), 0, NULL));
/* update the value itself */
- LY_CHECK_RET(lyd_change_term(diff_match, lyd_get_value(src_diff)));
+ LY_CHECK_RET(lyd_change_term(*diff_match, lyd_get_value(src_diff)));
+ } else {
+ /* also operation REPLACE but we need to change an opaque node into a data node */
+ LY_CHECK_RET(lyd_dup_single(src_diff, (*diff_match)->parent, LYD_DUP_NO_META | LYD_DUP_WITH_FLAGS, &src_dup));
+ if (!(*diff_match)->parent) {
+ /* will always be inserted before diff_match, which is opaque */
+ LY_CHECK_RET(lyd_insert_sibling(*diff_match, src_dup, diff));
+ }
+ to_free = *diff_match;
+ *diff_match = src_dup;
+
+ r = lyd_new_meta(ctx, src_dup, NULL, "yang:orig-value", lyd_get_value(to_free), 0, NULL);
+ lyd_free_tree(to_free);
+ LY_CHECK_RET(r);
+ LY_CHECK_RET(lyd_new_meta(ctx, src_dup, NULL, "yang:operation", lyd_diff_op2str(LYD_DIFF_OP_REPLACE), 0, NULL));
}
} else {
/* deleted + created -> operation NONE */
- LY_CHECK_RET(lyd_diff_change_op(diff_match, LYD_DIFF_OP_NONE));
+ LY_CHECK_RET(lyd_diff_change_op(*diff_match, LYD_DIFF_OP_NONE));
}
- if (diff_match->schema->nodetype & LYD_NODE_TERM) {
+ assert((*diff_match)->schema);
+ if ((*diff_match)->schema->nodetype & LYD_NODE_TERM) {
/* add orig-dflt metadata */
- LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
+ LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), *diff_match, NULL, "yang:orig-default",
trg_flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
/* update dflt flag itself */
- diff_match->flags &= ~LYD_DEFAULT;
- diff_match->flags |= src_diff->flags & LYD_DEFAULT;
+ (*diff_match)->flags &= ~LYD_DEFAULT;
+ (*diff_match)->flags |= src_diff->flags & LYD_DEFAULT;
}
/* but the operation of its children should remain DELETE */
- LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
+ LY_LIST_FOR(lyd_child_no_keys(*diff_match), child) {
LY_CHECK_RET(lyd_diff_change_op(child, LYD_DIFF_OP_DELETE));
}
break;
default:
/* create and replace operations are not valid */
- LOGERR_MERGEOP(LYD_CTX(src_diff), diff_match, cur_op, LYD_DIFF_OP_CREATE);
+ LOGERR_MERGEOP(LYD_CTX(src_diff), *diff_match, cur_op, LYD_DIFF_OP_CREATE);
return LY_EINVAL;
}
@@ -1599,7 +1740,7 @@ lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, cons
if (diff_match->schema->nodetype & LYD_NODE_TERM) {
/* add orig-default meta because it is expected */
LY_CHECK_RET(lyd_new_meta(LYD_CTX(src_diff), diff_match, NULL, "yang:orig-default",
- diff_match->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
+ src_diff->flags & LYD_DEFAULT ? "true" : "false", 0, NULL));
} else if (!lysc_is_dup_inst_list(diff_match->schema)) {
/* keep operation for all descendants (for now) */
LY_LIST_FOR(lyd_child_no_keys(diff_match), child) {
@@ -1723,17 +1864,24 @@ lyd_diff_is_redundant(struct lyd_node *diff)
*/
return 1;
}
- } else if ((op == LYD_DIFF_OP_NONE) && (diff->schema->nodetype & LYD_NODE_TERM)) {
- /* check whether at least the default flags are different */
- meta = lyd_find_meta(diff->meta, mod, "orig-default");
- assert(meta);
- str = lyd_get_meta_value(meta);
-
- /* if previous and current dflt flags are the same, this node is redundant */
- if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
+ } else if (op == LYD_DIFF_OP_NONE) {
+ if (!diff->schema) {
+ /* opaque node with none must be redundant */
return 1;
}
- return 0;
+
+ if (diff->schema->nodetype & LYD_NODE_TERM) {
+ /* check whether at least the default flags are different */
+ meta = lyd_find_meta(diff->meta, mod, "orig-default");
+ assert(meta);
+ str = lyd_get_meta_value(meta);
+
+ /* if previous and current dflt flags are the same, this node is redundant */
+ if ((!strcmp(str, "true") && (diff->flags & LYD_DEFAULT)) || (!strcmp(str, "false") && !(diff->flags & LYD_DEFAULT))) {
+ return 1;
+ }
+ return 0;
+ }
}
if (!child && (op == LYD_DIFF_OP_NONE)) {
@@ -1757,12 +1905,12 @@ lyd_diff_is_redundant(struct lyd_node *diff)
*/
static LY_ERR
lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, lyd_diff_cb diff_cb, void *cb_data,
- struct lyd_dup_inst **dup_inst, uint16_t options, struct lyd_node **diff)
+ struct ly_ht **dup_inst, uint16_t options, struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *child, *diff_node = NULL;
enum lyd_diff_op src_op, cur_op;
- struct lyd_dup_inst *child_dup_inst = NULL;
+ struct ly_ht *child_dup_inst = NULL;
/* get source node operation */
LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op));
@@ -1785,7 +1933,7 @@ lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent,
goto add_diff;
}
- ret = lyd_diff_merge_create(diff_node, cur_op, src_diff, options);
+ ret = lyd_diff_merge_create(&diff_node, diff, cur_op, src_diff, options);
break;
case LYD_DIFF_OP_DELETE:
ret = lyd_diff_merge_delete(diff_node, cur_op, src_diff);
@@ -1868,7 +2016,7 @@ lyd_diff_merge_module(struct lyd_node **diff, const struct lyd_node *src_diff, c
lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
{
const struct lyd_node *src_root;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
LY_ERR ret = LY_SUCCESS;
LY_LIST_FOR(src_diff, src_root) {
@@ -1891,7 +2039,7 @@ lyd_diff_merge_tree(struct lyd_node **diff_first, struct lyd_node *diff_parent,
lyd_diff_cb diff_cb, void *cb_data, uint16_t options)
{
LY_ERR ret;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
if (!src_sibling) {
return LY_SUCCESS;
diff --git a/src/hash_table.c b/src/hash_table.c
index 4f9dec3..9655bd6 100644
--- a/src/hash_table.c
+++ b/src/hash_table.c
@@ -1,9 +1,10 @@
/**
* @file hash_table.c
* @author Radek Krejci <rkrejci@cesnet.cz>
- * @brief libyang dictionary for storing strings and generic hash table
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang generic hash table implementation
*
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -25,80 +26,8 @@
#include "dict.h"
#include "log.h"
-#define LYDICT_MIN_SIZE 1024
-
-/**
- * @brief Comparison callback for dictionary's hash table
- *
- * Implementation of ::lyht_value_equal_cb.
- */
-static ly_bool
-lydict_val_eq(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
-{
- LY_CHECK_ARG_RET(NULL, val1_p, val2_p, cb_data, 0);
-
- const char *str1 = ((struct dict_rec *)val1_p)->value;
- const char *str2 = ((struct dict_rec *)val2_p)->value;
-
- LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
- LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
-
- if (strncmp(str1, str2, *(size_t *)cb_data) == 0) {
- return 1;
- }
-
- return 0;
-}
-
-void
-lydict_init(struct dict_table *dict)
-{
- LY_CHECK_ARG_RET(NULL, dict, );
-
- dict->hash_tab = lyht_new(LYDICT_MIN_SIZE, sizeof(struct dict_rec), lydict_val_eq, NULL, 1);
- LY_CHECK_ERR_RET(!dict->hash_tab, LOGINT(NULL), );
- pthread_mutex_init(&dict->lock, NULL);
-}
-
-void
-lydict_clean(struct dict_table *dict)
-{
- struct dict_rec *dict_rec = NULL;
- struct ht_rec *rec = NULL;
-
- LY_CHECK_ARG_RET(NULL, dict, );
-
- for (uint32_t i = 0; i < dict->hash_tab->size; i++) {
- /* get ith record */
- rec = (struct ht_rec *)&dict->hash_tab->recs[i * dict->hash_tab->rec_size];
- if (rec->hits == 1) {
- /*
- * this should not happen, all records inserted into
- * dictionary are supposed to be removed using lydict_remove()
- * before calling lydict_clean()
- */
- dict_rec = (struct dict_rec *)rec->val;
- LOGWRN(NULL, "String \"%s\" not freed from the dictionary, refcount %d", dict_rec->value, dict_rec->refcount);
- /* if record wasn't removed before free string allocated for that record */
-#ifdef NDEBUG
- free(dict_rec->value);
-#endif
- }
- }
-
- /* free table and destroy mutex */
- lyht_free(dict->hash_tab);
- pthread_mutex_destroy(&dict->lock);
-}
-
-/*
- * Usage:
- * - init hash to 0
- * - repeatedly call dict_hash_multi(), provide hash from the last call
- * - call dict_hash_multi() with key_part = NULL to finish the hash
- */
-uint32_t
-dict_hash_multi(uint32_t hash, const char *key_part, size_t len)
+LIBYANG_API_DEF uint32_t
+lyht_hash_multi(uint32_t hash, const char *key_part, size_t len)
{
uint32_t i;
@@ -117,203 +46,47 @@ dict_hash_multi(uint32_t hash, const char *key_part, size_t len)
return hash;
}
-/*
- * Bob Jenkin's one-at-a-time hash
- * http://www.burtleburtle.net/bob/hash/doobs.html
- *
- * Spooky hash is faster, but it works only for little endian architectures.
- */
-uint32_t
-dict_hash(const char *key, size_t len)
+LIBYANG_API_DEF uint32_t
+lyht_hash(const char *key, size_t len)
{
uint32_t hash;
- hash = dict_hash_multi(0, key, len);
- return dict_hash_multi(hash, NULL, len);
+ hash = lyht_hash_multi(0, key, len);
+ return lyht_hash_multi(hash, NULL, len);
}
-static ly_bool
-lydict_resize_val_eq(void *val1_p, void *val2_p, ly_bool mod, void *cb_data)
-{
- LY_CHECK_ARG_RET(NULL, val1_p, val2_p, 0);
-
- const char *str1 = ((struct dict_rec *)val1_p)->value;
- const char *str2 = ((struct dict_rec *)val2_p)->value;
-
- LY_CHECK_ERR_RET(!str1, LOGARG(NULL, val1_p), 0);
- LY_CHECK_ERR_RET(!str2, LOGARG(NULL, val2_p), 0);
-
- if (mod) {
- /* used when inserting new values */
- if (strcmp(str1, str2) == 0) {
- return 1;
- }
- } else {
- /* used when finding the original value again in the resized table */
- return lydict_val_eq(val1_p, val2_p, mod, cb_data);
- }
-
- return 0;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_remove(const struct ly_ctx *ctx, const char *value)
-{
- LY_ERR ret = LY_SUCCESS;
- size_t len;
- uint32_t hash;
- struct dict_rec rec, *match = NULL;
- char *val_p;
-
- if (!ctx || !value) {
- return LY_SUCCESS;
- }
-
- LOGDBG(LY_LDGDICT, "removing \"%s\"", value);
-
- len = strlen(value);
- hash = dict_hash(value, len);
-
- /* create record for lyht_find call */
- rec.value = (char *)value;
- rec.refcount = 0;
-
- pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
- /* set len as data for compare callback */
- lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
- /* check if value is already inserted */
- ret = lyht_find(ctx->dict.hash_tab, &rec, hash, (void **)&match);
-
- if (ret == LY_SUCCESS) {
- LY_CHECK_ERR_GOTO(!match, LOGINT(ctx), finish);
-
- /* if value is already in dictionary, decrement reference counter */
- match->refcount--;
- if (match->refcount == 0) {
- /*
- * remove record
- * save pointer to stored string before lyht_remove to
- * free it after it is removed from hash table
- */
- val_p = match->value;
- ret = lyht_remove_with_resize_cb(ctx->dict.hash_tab, &rec, hash, lydict_resize_val_eq);
- free(val_p);
- LY_CHECK_ERR_GOTO(ret, LOGINT(ctx), finish);
- }
- } else if (ret == LY_ENOTFOUND) {
- LOGERR(ctx, LY_ENOTFOUND, "Value \"%s\" was not found in the dictionary.", value);
- } else {
- LOGINT(ctx);
- }
-
-finish:
- pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
- return ret;
-}
-
-LY_ERR
-dict_insert(const struct ly_ctx *ctx, char *value, size_t len, ly_bool zerocopy, const char **str_p)
+static LY_ERR
+lyht_init_hlists_and_records(struct ly_ht *ht)
{
- LY_ERR ret = LY_SUCCESS;
- struct dict_rec *match = NULL, rec;
- uint32_t hash;
-
- LOGDBG(LY_LDGDICT, "inserting \"%.*s\"", (int)len, value);
-
- hash = dict_hash(value, len);
- /* set len as data for compare callback */
- lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
- /* create record for lyht_insert */
- rec.value = value;
- rec.refcount = 1;
+ struct ly_ht_rec *rec;
+ uint32_t i;
- ret = lyht_insert_with_resize_cb(ctx->dict.hash_tab, (void *)&rec, hash, lydict_resize_val_eq, (void **)&match);
- if (ret == LY_EEXIST) {
- match->refcount++;
- if (zerocopy) {
- free(value);
- }
- ret = LY_SUCCESS;
- } else if (ret == LY_SUCCESS) {
- if (!zerocopy) {
- /*
- * allocate string for new record
- * record is already inserted in hash table
- */
- match->value = malloc(sizeof *match->value * (len + 1));
- LY_CHECK_ERR_RET(!match->value, LOGMEM(ctx), LY_EMEM);
- if (len) {
- memcpy(match->value, value, len);
- }
- match->value[len] = '\0';
- }
- } else {
- /* lyht_insert returned error */
- if (zerocopy) {
- free(value);
+ ht->recs = calloc(ht->size, ht->rec_size);
+ LY_CHECK_ERR_RET(!ht->recs, LOGMEM(NULL), LY_EMEM);
+ for (i = 0; i < ht->size; i++) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, i);
+ if (i != ht->size) {
+ rec->next = i + 1;
+ } else {
+ rec->next = LYHT_NO_RECORD;
}
- return ret;
- }
-
- if (str_p) {
- *str_p = match->value;
- }
-
- return ret;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_insert(const struct ly_ctx *ctx, const char *value, size_t len, const char **str_p)
-{
- LY_ERR result;
-
- LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
-
- if (!value) {
- *str_p = NULL;
- return LY_SUCCESS;
- }
-
- if (!len) {
- len = strlen(value);
}
- pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
- result = dict_insert(ctx, (char *)value, len, 0, str_p);
- pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
-
- return result;
-}
-
-LIBYANG_API_DEF LY_ERR
-lydict_insert_zc(const struct ly_ctx *ctx, char *value, const char **str_p)
-{
- LY_ERR result;
-
- LY_CHECK_ARG_RET(ctx, ctx, str_p, LY_EINVAL);
-
- if (!value) {
- *str_p = NULL;
- return LY_SUCCESS;
+ ht->hlists = malloc(sizeof(ht->hlists[0]) * ht->size);
+ LY_CHECK_ERR_RET(!ht->hlists, free(ht->recs); LOGMEM(NULL), LY_EMEM);
+ for (i = 0; i < ht->size; i++) {
+ ht->hlists[i].first = LYHT_NO_RECORD;
+ ht->hlists[i].last = LYHT_NO_RECORD;
}
+ ht->first_free_rec = 0;
- pthread_mutex_lock((pthread_mutex_t *)&ctx->dict.lock);
- result = dict_insert(ctx, value, strlen(value), 1, str_p);
- pthread_mutex_unlock((pthread_mutex_t *)&ctx->dict.lock);
-
- return result;
-}
-
-struct ht_rec *
-lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx)
-{
- return (struct ht_rec *)&recs[idx * rec_size];
+ return LY_SUCCESS;
}
-struct hash_table *
+LIBYANG_API_DEF struct ly_ht *
lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data, uint16_t resize)
{
- struct hash_table *ht;
+ struct ly_ht *ht;
/* check that 2^x == size (power of 2) */
assert(size && !(size & (size - 1)));
@@ -329,21 +102,21 @@ lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *
ht->used = 0;
ht->size = size;
- ht->invalid = 0;
ht->val_equal = val_equal;
ht->cb_data = cb_data;
ht->resize = resize;
- ht->rec_size = (sizeof(struct ht_rec) - 1) + val_size;
- /* allocate the records correctly */
- ht->recs = calloc(size, ht->rec_size);
- LY_CHECK_ERR_RET(!ht->recs, free(ht); LOGMEM(NULL), NULL);
+ ht->rec_size = SIZEOF_LY_HT_REC + val_size;
+ if (lyht_init_hlists_and_records(ht) != LY_SUCCESS) {
+ free(ht);
+ return NULL;
+ }
return ht;
}
-lyht_value_equal_cb
-lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_val_equal)
+LIBYANG_API_DEF lyht_value_equal_cb
+lyht_set_cb(struct ly_ht *ht, lyht_value_equal_cb new_val_equal)
{
lyht_value_equal_cb prev;
@@ -352,8 +125,8 @@ lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_val_equal)
return prev;
}
-void *
-lyht_set_cb_data(struct hash_table *ht, void *new_cb_data)
+LIBYANG_API_DEF void *
+lyht_set_cb_data(struct ly_ht *ht, void *new_cb_data)
{
void *prev;
@@ -362,31 +135,43 @@ lyht_set_cb_data(struct hash_table *ht, void *new_cb_data)
return prev;
}
-struct hash_table *
-lyht_dup(const struct hash_table *orig)
+LIBYANG_API_DEF struct ly_ht *
+lyht_dup(const struct ly_ht *orig)
{
- struct hash_table *ht;
+ struct ly_ht *ht;
LY_CHECK_ARG_RET(NULL, orig, NULL);
- ht = lyht_new(orig->size, orig->rec_size - (sizeof(struct ht_rec) - 1), orig->val_equal, orig->cb_data, orig->resize ? 1 : 0);
+ ht = lyht_new(orig->size, orig->rec_size - SIZEOF_LY_HT_REC, orig->val_equal, orig->cb_data, orig->resize ? 1 : 0);
if (!ht) {
return NULL;
}
- memcpy(ht->recs, orig->recs, (size_t)orig->used * (size_t)orig->rec_size);
+ memcpy(ht->hlists, orig->hlists, sizeof(ht->hlists[0]) * orig->size);
+ memcpy(ht->recs, orig->recs, (size_t)orig->size * orig->rec_size);
ht->used = orig->used;
- ht->invalid = orig->invalid;
return ht;
}
-void
-lyht_free(struct hash_table *ht)
+LIBYANG_API_DEF void
+lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p))
{
- if (ht) {
- free(ht->recs);
- free(ht);
+ struct ly_ht_rec *rec;
+ uint32_t hlist_idx;
+ uint32_t rec_idx;
+
+ if (!ht) {
+ return;
+ }
+
+ if (val_free) {
+ LYHT_ITER_ALL_RECS(ht, hlist_idx, rec_idx, rec) {
+ val_free(&rec->val);
+ }
}
+ free(ht->hlists);
+ free(ht->recs);
+ free(ht);
}
/**
@@ -397,14 +182,19 @@ lyht_free(struct hash_table *ht)
* @return LY_ERR value.
*/
static LY_ERR
-lyht_resize(struct hash_table *ht, int operation)
+lyht_resize(struct ly_ht *ht, int operation, int check)
{
- struct ht_rec *rec;
+ struct ly_ht_rec *rec;
+ struct ly_ht_hlist *old_hlists;
unsigned char *old_recs;
+ uint32_t old_first_free_rec;
uint32_t i, old_size;
+ uint32_t rec_idx;
+ old_hlists = ht->hlists;
old_recs = ht->recs;
old_size = ht->size;
+ old_first_free_rec = ht->first_free_rec;
if (operation > 0) {
/* double the size */
@@ -414,18 +204,29 @@ lyht_resize(struct hash_table *ht, int operation)
ht->size >>= 1;
}
- ht->recs = calloc(ht->size, ht->rec_size);
- LY_CHECK_ERR_RET(!ht->recs, LOGMEM(NULL); ht->recs = old_recs; ht->size = old_size, LY_EMEM);
+ if (lyht_init_hlists_and_records(ht) != LY_SUCCESS) {
+ ht->hlists = old_hlists;
+ ht->recs = old_recs;
+ ht->size = old_size;
+ ht->first_free_rec = old_first_free_rec;
+ return LY_EMEM;
+ }
- /* reset used and invalid, it will increase again */
+ /* reset used, it will increase again */
ht->used = 0;
- ht->invalid = 0;
/* add all the old records into the new records array */
- for (i = 0; i < old_size; ++i) {
- rec = lyht_get_rec(old_recs, ht->rec_size, i);
- if (rec->hits > 0) {
- LY_ERR ret = lyht_insert(ht, rec->val, rec->hash, NULL);
+ for (i = 0; i < old_size; i++) {
+ for (rec_idx = old_hlists[i].first, rec = lyht_get_rec(old_recs, ht->rec_size, rec_idx);
+ rec_idx != LYHT_NO_RECORD;
+ rec_idx = rec->next, rec = lyht_get_rec(old_recs, ht->rec_size, rec_idx)) {
+ LY_ERR ret;
+
+ if (check) {
+ ret = lyht_insert(ht, rec->val, rec->hash, NULL);
+ } else {
+ ret = lyht_insert_no_check(ht, rec->val, rec->hash, NULL);
+ }
assert(!ret);
(void)ret;
@@ -434,117 +235,18 @@ lyht_resize(struct hash_table *ht, int operation)
/* final touches */
free(old_recs);
+ free(old_hlists);
return LY_SUCCESS;
}
/**
- * @brief Search for the first match.
- *
- * @param[in] ht Hash table to search in.
- * @param[in] hash Hash to find.
- * @param[out] rec_p Optional found record.
- * @return LY_SUCCESS hash found, returned its record,
- * @return LY_ENOTFOUND hash not found, returned the record where it would be inserted.
- */
-static LY_ERR
-lyht_find_first(struct hash_table *ht, uint32_t hash, struct ht_rec **rec_p)
-{
- struct ht_rec *rec;
- uint32_t i, idx;
-
- if (rec_p) {
- *rec_p = NULL;
- }
-
- idx = i = hash & (ht->size - 1);
- rec = lyht_get_rec(ht->recs, ht->rec_size, idx);
-
- /* skip through overflow and deleted records */
- while ((rec->hits != 0) && ((rec->hits == -1) || ((rec->hash & (ht->size - 1)) != idx))) {
- if ((rec->hits == -1) && rec_p && !(*rec_p)) {
- /* remember this record for return */
- *rec_p = rec;
- }
- i = (i + 1) % ht->size;
- if (i == idx) {
- /* we went through all the records (very unlikely, but possible when many records are invalid),
- * just return not found */
- assert(!rec_p || *rec_p);
- return LY_ENOTFOUND;
- }
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- }
- if (rec->hits == 0) {
- /* we could not find the value */
- if (rec_p && !*rec_p) {
- *rec_p = rec;
- }
- return LY_ENOTFOUND;
- }
-
- /* we have found a record with equal (shortened) hash */
- if (rec_p) {
- *rec_p = rec;
- }
- return LY_SUCCESS;
-}
-
-/**
- * @brief Search for the next collision.
- *
- * @param[in] ht Hash table to search in.
- * @param[in,out] last Last returned collision record.
- * @param[in] first First collision record (hits > 1).
- * @return LY_SUCCESS when hash collision found, \p last points to this next collision,
- * @return LY_ENOTFOUND when hash collision not found, \p last points to the record where it would be inserted.
- */
-static LY_ERR
-lyht_find_collision(struct hash_table *ht, struct ht_rec **last, struct ht_rec *first)
-{
- struct ht_rec *empty = NULL;
- uint32_t i, idx;
-
- assert(last && *last);
-
- idx = (*last)->hash & (ht->size - 1);
- i = (((unsigned char *)*last) - ht->recs) / ht->rec_size;
-
- do {
- i = (i + 1) % ht->size;
- *last = lyht_get_rec(ht->recs, ht->rec_size, i);
- if (*last == first) {
- /* we went through all the records (very unlikely, but possible when many records are invalid),
- * just return an invalid record */
- assert(empty);
- *last = empty;
- return LY_ENOTFOUND;
- }
-
- if (((*last)->hits == -1) && !empty) {
- empty = *last;
- }
- } while (((*last)->hits != 0) && (((*last)->hits == -1) || (((*last)->hash & (ht->size - 1)) != idx)));
-
- if ((*last)->hits > 0) {
- /* we found a collision */
- assert((*last)->hits == 1);
- return LY_SUCCESS;
- }
-
- /* no next collision found, return the record where it would be inserted */
- if (empty) {
- *last = empty;
- } /* else (*last)->hits == 0, it is already correct */
- return LY_ENOTFOUND;
-}
-
-/**
* @brief Search for a record with specific value and hash.
*
* @param[in] ht Hash table to search in.
* @param[in] val_p Pointer to the value to find.
* @param[in] hash Hash to find.
* @param[in] mod Whether the operation modifies the hash table (insert or remove) or not (find).
+ * @param[in] val_equal Callback for checking value equivalence.
* @param[out] crec_p Optional found first record.
* @param[out] col Optional collision number of @p rec_p, 0 for no collision.
* @param[out] rec_p Found exact matching record, may be a collision of @p crec_p.
@@ -552,12 +254,12 @@ lyht_find_collision(struct hash_table *ht, struct ht_rec **last, struct ht_rec *
* @return LY_SUCCESS if record was found.
*/
static LY_ERR
-lyht_find_rec(struct hash_table *ht, void *val_p, uint32_t hash, ly_bool mod, struct ht_rec **crec_p, uint32_t *col,
- struct ht_rec **rec_p)
+lyht_find_rec(const struct ly_ht *ht, void *val_p, uint32_t hash, ly_bool mod, lyht_value_equal_cb val_equal,
+ struct ly_ht_rec **crec_p, uint32_t *col, struct ly_ht_rec **rec_p)
{
- struct ht_rec *rec, *crec;
- uint32_t i, c;
- LY_ERR r;
+ uint32_t hlist_idx = hash & (ht->size - 1);
+ struct ly_ht_rec *rec;
+ uint32_t rec_idx;
if (crec_p) {
*crec_p = NULL;
@@ -567,53 +269,43 @@ lyht_find_rec(struct hash_table *ht, void *val_p, uint32_t hash, ly_bool mod, st
}
*rec_p = NULL;
- if (lyht_find_first(ht, hash, &rec)) {
- /* not found */
- return LY_ENOTFOUND;
- }
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, mod, ht->cb_data)) {
- /* even the value matches */
- if (crec_p) {
- *crec_p = rec;
- }
- if (col) {
- *col = 0;
- }
- *rec_p = rec;
- return LY_SUCCESS;
- }
-
- /* some collisions, we need to go through them, too */
- crec = rec;
- c = crec->hits;
- for (i = 1; i < c; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
- (void)r;
-
- /* compare values */
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, mod, ht->cb_data)) {
+ LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
+ if ((rec->hash == hash) && val_equal(val_p, &rec->val, mod, ht->cb_data)) {
if (crec_p) {
- *crec_p = crec;
- }
- if (col) {
- *col = i;
+ *crec_p = rec;
}
*rec_p = rec;
return LY_SUCCESS;
}
+
+ if (col) {
+ *col = *col + 1;
+ }
}
/* not found even in collisions */
return LY_ENOTFOUND;
}
-LY_ERR
-lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+LIBYANG_API_DEF LY_ERR
+lyht_find(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
+{
+ struct ly_ht_rec *rec;
+
+ lyht_find_rec(ht, val_p, hash, 0, ht->val_equal, NULL, NULL, &rec);
+
+ if (rec && match_p) {
+ *match_p = rec->val;
+ }
+ return rec ? LY_SUCCESS : LY_ENOTFOUND;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyht_find_with_val_cb(const struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb val_equal, void **match_p)
{
- struct ht_rec *rec;
+ struct ly_ht_rec *rec;
- lyht_find_rec(ht, val_p, hash, 0, NULL, NULL, &rec);
+ lyht_find_rec(ht, val_p, hash, 0, val_equal ? val_equal : ht->val_equal, NULL, NULL, &rec);
if (rec && match_p) {
*match_p = rec->val;
@@ -621,26 +313,23 @@ lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
return rec ? LY_SUCCESS : LY_ENOTFOUND;
}
-LY_ERR
-lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint32_t hash,
+LIBYANG_API_DEF LY_ERR
+lyht_find_next_with_collision_cb(const struct ly_ht *ht, void *val_p, uint32_t hash,
lyht_value_equal_cb collision_val_equal, void **match_p)
{
- struct ht_rec *rec, *crec;
- uint32_t i, c;
- LY_ERR r;
+ struct ly_ht_rec *rec, *crec;
+ uint32_t rec_idx;
+ uint32_t i;
/* find the record of the previously found value */
- if (lyht_find_rec(ht, val_p, hash, 1, &crec, &i, &rec)) {
+ if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, &crec, &i, &rec)) {
/* not found, cannot happen */
LOGINT_RET(NULL);
}
- /* go through collisions and find the next one after the previous one */
- c = crec->hits;
- for (++i; i < c; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
- (void)r;
+ for (rec_idx = rec->next, rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
+ rec_idx != LYHT_NO_RECORD;
+ rec_idx = rec->next, rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx)) {
if (rec->hash != hash) {
continue;
@@ -667,71 +356,51 @@ lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint32_t ha
return LY_ENOTFOUND;
}
-LY_ERR
-lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+LIBYANG_API_DEF LY_ERR
+lyht_find_next(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
{
return lyht_find_next_with_collision_cb(ht, val_p, hash, NULL, match_p);
}
-LY_ERR
-lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
- void **match_p)
+static LY_ERR
+_lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
+ void **match_p, int check)
{
+ uint32_t hlist_idx = hash & (ht->size - 1);
LY_ERR r, ret = LY_SUCCESS;
- struct ht_rec *rec, *crec = NULL;
- int32_t i;
+ struct ly_ht_rec *rec, *prev_rec;
lyht_value_equal_cb old_val_equal = NULL;
+ uint32_t rec_idx;
- if (!lyht_find_first(ht, hash, &rec)) {
- /* we found matching shortened hash */
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
- /* even the value matches */
- if (match_p) {
- *match_p = (void *)&rec->val;
+ if (check) {
+ if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, NULL, NULL, &rec) == LY_SUCCESS) {
+ if (rec && match_p) {
+ *match_p = rec->val;
}
return LY_EEXIST;
}
+ }
- /* some collisions, we need to go through them, too */
- crec = rec;
- for (i = 1; i < crec->hits; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
-
- /* compare values */
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
- if (match_p) {
- *match_p = (void *)&rec->val;
- }
- return LY_EEXIST;
- }
- }
+ rec_idx = ht->first_free_rec;
+ assert(rec_idx < ht->size);
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
+ ht->first_free_rec = rec->next;
- /* value not found, get the record where it will be inserted */
- r = lyht_find_collision(ht, &rec, crec);
- assert(r);
+ if (ht->hlists[hlist_idx].first == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].first = rec_idx;
+ } else {
+ prev_rec = lyht_get_rec(ht->recs, ht->rec_size, ht->hlists[hlist_idx].last);
+ prev_rec->next = rec_idx;
}
+ rec->next = LYHT_NO_RECORD;
+ ht->hlists[hlist_idx].last = rec_idx;
- /* insert it into the returned record */
- assert(rec->hits < 1);
- if (rec->hits < 0) {
- --ht->invalid;
- }
rec->hash = hash;
- rec->hits = 1;
- memcpy(&rec->val, val_p, ht->rec_size - (sizeof(struct ht_rec) - 1));
+ memcpy(&rec->val, val_p, ht->rec_size - SIZEOF_LY_HT_REC);
if (match_p) {
*match_p = (void *)&rec->val;
}
- if (crec) {
- /* there was a collision, increase hits */
- if (crec->hits == INT32_MAX) {
- LOGINT(NULL);
- }
- ++crec->hits;
- }
-
/* check size & enlarge if needed */
++ht->used;
if (ht->resize) {
@@ -746,10 +415,11 @@ lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
}
/* enlarge */
- ret = lyht_resize(ht, 1);
+ ret = lyht_resize(ht, 1, check);
/* if hash_table was resized, we need to find new matching value */
if ((ret == LY_SUCCESS) && match_p) {
- lyht_find(ht, val_p, hash, match_p);
+ ret = lyht_find(ht, val_p, hash, match_p);
+ assert(!ret);
}
if (resize_val_equal) {
@@ -760,61 +430,66 @@ lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
return ret;
}
-LY_ERR
-lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
+LIBYANG_API_DEF LY_ERR
+lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
+ void **match_p)
{
- return lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p);
+ return _lyht_insert_with_resize_cb(ht, val_p, hash, resize_val_equal, match_p, 1);
}
-LY_ERR
-lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal)
+LIBYANG_API_DEF LY_ERR
+lyht_insert(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
{
- struct ht_rec *rec, *crec;
- int32_t i;
- ly_bool first_matched = 0;
- LY_ERR r, ret = LY_SUCCESS;
- lyht_value_equal_cb old_val_equal;
+ return _lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p, 1);
+}
- LY_CHECK_ERR_RET(lyht_find_first(ht, hash, &rec), LOGARG(NULL, hash), LY_ENOTFOUND); /* hash not found */
+LIBYANG_API_DEF LY_ERR
+lyht_insert_no_check(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
+{
+ return _lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p, 0);
+}
- if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
- /* even the value matches */
- first_matched = 1;
- }
+LIBYANG_API_DEF LY_ERR
+lyht_remove_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal)
+{
+ struct ly_ht_rec *found_rec, *prev_rec, *rec;
+ uint32_t hlist_idx = hash & (ht->size - 1);
+ LY_ERR r, ret = LY_SUCCESS;
+ lyht_value_equal_cb old_val_equal = NULL;
+ uint32_t prev_rec_idx;
+ uint32_t rec_idx;
- /* we always need to go through collisions */
- crec = rec;
- for (i = 1; i < crec->hits; ++i) {
- r = lyht_find_collision(ht, &rec, crec);
- assert(!r);
+ if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, NULL, NULL, &found_rec)) {
+ LOGARG(NULL, hash);
+ return LY_ENOTFOUND;
+ }
- /* compare values */
- if (!first_matched && (rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
+ prev_rec_idx = LYHT_NO_RECORD;
+ LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
+ if (rec == found_rec) {
break;
}
+ prev_rec_idx = rec_idx;
}
- if (i < crec->hits) {
- /* one of collisions matched, reduce collision count, remove the record */
- assert(!first_matched);
- --crec->hits;
- rec->hits = -1;
- } else if (first_matched) {
- /* the first record matches */
- if (crec != rec) {
- /* ... so put the last collision in its place */
- rec->hits = crec->hits - 1;
- memcpy(crec, rec, ht->rec_size);
+ if (prev_rec_idx == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].first = rec->next;
+ if (rec->next == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].last = LYHT_NO_RECORD;
}
- rec->hits = -1;
} else {
- /* value not found even in collisions */
- return LY_ENOTFOUND;
+ prev_rec = lyht_get_rec(ht->recs, ht->rec_size, prev_rec_idx);
+ prev_rec->next = rec->next;
+ if (rec->next == LYHT_NO_RECORD) {
+ ht->hlists[hlist_idx].last = prev_rec_idx;
+ }
}
+ rec->next = ht->first_free_rec;
+ ht->first_free_rec = rec_idx;
+
/* check size & shrink if needed */
--ht->used;
- ++ht->invalid;
if (ht->resize == 2) {
r = (ht->used * LYHT_HUNDRED_PERCENTAGE) / ht->size;
if ((r < LYHT_SHRINK_PERCENTAGE) && (ht->size > LYHT_MIN_SIZE)) {
@@ -823,7 +498,7 @@ lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
}
/* shrink */
- ret = lyht_resize(ht, -1);
+ ret = lyht_resize(ht, -1, 1);
if (resize_val_equal) {
lyht_set_cb(ht, old_val_equal);
@@ -831,50 +506,29 @@ lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, ly
}
}
- /* rehash all records if needed */
- r = ((ht->size - ht->used - ht->invalid) * 100) / ht->size;
- if (r < LYHT_REHASH_PERCENTAGE) {
- if (resize_val_equal) {
- old_val_equal = lyht_set_cb(ht, resize_val_equal);
- }
-
- /* rehash */
- ret = lyht_resize(ht, 0);
-
- if (resize_val_equal) {
- lyht_set_cb(ht, old_val_equal);
- }
- }
-
return ret;
}
-LY_ERR
-lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash)
+LIBYANG_API_DEF LY_ERR
+lyht_remove(struct ly_ht *ht, void *val_p, uint32_t hash)
{
return lyht_remove_with_resize_cb(ht, val_p, hash, NULL);
}
-uint32_t
+LIBYANG_API_DEF uint32_t
lyht_get_fixed_size(uint32_t item_count)
{
- uint32_t i, size = 0;
-
- /* detect number of upper zero bits in the items' counter value ... */
- for (i = (sizeof item_count * CHAR_BIT) - 1; i > 0; i--) {
- size = item_count << i;
- size = size >> i;
- if (size == item_count) {
- break;
- }
+ if (item_count == 0) {
+ return 1;
}
- assert(i);
-
- /* ... and then we convert it to the position of the highest non-zero bit ... */
- i = (sizeof item_count * CHAR_BIT) - i;
- /* ... and by using it to shift 1 to the left we get the closest sufficient hash table size */
- size = 1 << i;
+ /* return next power of 2 (greater or equal) */
+ item_count--;
+ item_count |= item_count >> 1;
+ item_count |= item_count >> 2;
+ item_count |= item_count >> 4;
+ item_count |= item_count >> 8;
+ item_count |= item_count >> 16;
- return size;
+ return item_count + 1;
}
diff --git a/src/hash_table.h b/src/hash_table.h
index 91ae63d..5780f1e 100644
--- a/src/hash_table.h
+++ b/src/hash_table.h
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libyang hash table
*
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -16,27 +16,49 @@
#ifndef LY_HASH_TABLE_H_
#define LY_HASH_TABLE_H_
-#include <pthread.h>
#include <stddef.h>
#include <stdint.h>
-#include "compat.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include "log.h"
/**
+ * @struct ly_ht
+ * @brief libyang hash table.
+ */
+struct ly_ht;
+
+/**
* @brief Compute hash from (several) string(s).
*
* Usage:
* - init hash to 0
- * - repeatedly call ::dict_hash_multi(), provide hash from the last call
- * - call ::dict_hash_multi() with key_part = NULL to finish the hash
+ * - repeatedly call ::lyht_hash_multi(), provide hash from the last call
+ * - call ::lyht_hash_multi() with key_part = NULL to finish the hash
+ *
+ * @param[in] hash Previous hash.
+ * @param[in] key_part Next key to hash,
+ * @param[in] len Length of @p key_part.
+ * @return Hash with the next key.
*/
-uint32_t dict_hash_multi(uint32_t hash, const char *key_part, size_t len);
+LIBYANG_API_DECL uint32_t lyht_hash_multi(uint32_t hash, const char *key_part, size_t len);
/**
* @brief Compute hash from a string.
+ *
+ * Bob Jenkin's one-at-a-time hash
+ * http://www.burtleburtle.net/bob/hash/doobs.html
+ *
+ * Spooky hash is faster, but it works only for little endian architectures.
+ *
+ * @param[in] key Key to hash.
+ * @param[in] len Length of @p key.
+ * @return Hash of the key.
*/
-uint32_t dict_hash(const char *key, size_t len);
+LIBYANG_API_DECL uint32_t lyht_hash(const char *key, size_t len);
/**
* @brief Callback for checking hash table values equivalence.
@@ -49,82 +71,6 @@ uint32_t dict_hash(const char *key, size_t len);
*/
typedef ly_bool (*lyht_value_equal_cb)(void *val1_p, void *val2_p, ly_bool mod, void *cb_data);
-/** reference value for 100% */
-#define LYHT_HUNDRED_PERCENTAGE 100
-
-/** when the table is at least this much percent full, it is enlarged (double the size) */
-#define LYHT_ENLARGE_PERCENTAGE 75
-
-/** only once the table is this much percent full, enable shrinking */
-#define LYHT_FIRST_SHRINK_PERCENTAGE 50
-
-/** when the table is less than this much percent full, it is shrunk (half the size) */
-#define LYHT_SHRINK_PERCENTAGE 25
-
-/** when the table has less than this much percent empty records, it is rehashed to get rid of all the invalid records */
-#define LYHT_REHASH_PERCENTAGE 2
-
-/** never shrink beyond this size */
-#define LYHT_MIN_SIZE 8
-
-/**
- * @brief Generic hash table record.
- */
-struct ht_rec {
- uint32_t hash; /* hash of the value */
- int32_t hits; /* collision/overflow value count - 1 (a filled entry has 1 hit,
- * special value -1 means a deleted record) */
- unsigned char val[1]; /* arbitrary-size value */
-} _PACKED;
-
-/**
- * @brief (Very) generic hash table.
- *
- * Hash table with open addressing collision resolution and
- * linear probing of interval 1 (next free record is used).
- * Removal is lazy (removed records are only marked), but
- * if possible, they are fully emptied.
- */
-struct hash_table {
- uint32_t used; /* number of values stored in the hash table (filled records) */
- uint32_t size; /* always holds 2^x == size (is power of 2), actually number of records allocated */
- uint32_t invalid; /* number of invalid records (deleted) */
- lyht_value_equal_cb val_equal; /* callback for testing value equivalence */
- void *cb_data; /* user data callback arbitrary value */
- uint16_t resize; /* 0 - resizing is disabled, *
- * 1 - enlarging is enabled, *
- * 2 - both shrinking and enlarging is enabled */
- uint16_t rec_size; /* real size (in bytes) of one record for accessing recs array */
- unsigned char *recs; /* pointer to the hash table itself (array of struct ht_rec) */
-};
-
-struct dict_rec {
- char *value;
- uint32_t refcount;
-};
-
-/**
- * dictionary to store repeating strings
- */
-struct dict_table {
- struct hash_table *hash_tab;
- pthread_mutex_t lock;
-};
-
-/**
- * @brief Initiate content (non-zero values) of the dictionary
- *
- * @param[in] dict Dictionary table to initiate
- */
-void lydict_init(struct dict_table *dict);
-
-/**
- * @brief Cleanup the dictionary content
- *
- * @param[in] dict Dictionary table to cleanup
- */
-void lydict_clean(struct dict_table *dict);
-
/**
* @brief Create new hash table.
*
@@ -135,7 +81,8 @@ void lydict_clean(struct dict_table *dict);
* @param[in] resize Whether to resize the table on too few/too many records taken.
* @return Empty hash table, NULL on error.
*/
-struct hash_table *lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data, uint16_t resize);
+LIBYANG_API_DECL struct ly_ht *lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_cb val_equal, void *cb_data,
+ uint16_t resize);
/**
* @brief Set hash table value equal callback.
@@ -144,7 +91,7 @@ struct hash_table *lyht_new(uint32_t size, uint16_t val_size, lyht_value_equal_c
* @param[in] new_val_equal New callback for checking value equivalence.
* @return Previous callback for checking value equivalence.
*/
-lyht_value_equal_cb lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_val_equal);
+LIBYANG_API_DECL lyht_value_equal_cb lyht_set_cb(struct ly_ht *ht, lyht_value_equal_cb new_val_equal);
/**
* @brief Set hash table value equal callback user data.
@@ -153,7 +100,7 @@ lyht_value_equal_cb lyht_set_cb(struct hash_table *ht, lyht_value_equal_cb new_v
* @param[in] new_cb_data New data for values callback.
* @return Previous data for values callback.
*/
-void *lyht_set_cb_data(struct hash_table *ht, void *new_cb_data);
+LIBYANG_API_DECL void *lyht_set_cb_data(struct ly_ht *ht, void *new_cb_data);
/**
* @brief Make a duplicate of an existing hash table.
@@ -161,14 +108,15 @@ void *lyht_set_cb_data(struct hash_table *ht, void *new_cb_data);
* @param[in] orig Original hash table to duplicate.
* @return Duplicated hash table @p orig, NULL on error.
*/
-struct hash_table *lyht_dup(const struct hash_table *orig);
+LIBYANG_API_DECL struct ly_ht *lyht_dup(const struct ly_ht *orig);
/**
* @brief Free a hash table.
*
* @param[in] ht Hash table to be freed.
+ * @param[in] val_free Optional callback for freeing all the stored values, @p val_p is a pointer to a stored value.
*/
-void lyht_free(struct hash_table *ht);
+LIBYANG_API_DECL void lyht_free(struct ly_ht *ht, void (*val_free)(void *val_p));
/**
* @brief Find a value in a hash table.
@@ -180,7 +128,21 @@ void lyht_free(struct hash_table *ht);
* @return LY_SUCCESS if value was found,
* @return LY_ENOTFOUND if not found.
*/
-LY_ERR lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_find(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
+
+/**
+ * @brief Find a value in a hash table but use a custom val_equal callback.
+ *
+ * @param[in] ht Hash table to search in.
+ * @param[in] val_p Pointer to the value to find.
+ * @param[in] hash Hash of the stored value.
+ * @param[in] val_equal Callback for checking value equivalence.
+ * @param[out] match_p Pointer to the matching value, optional.
+ * @return LY_SUCCESS if value was found,
+ * @return LY_ENOTFOUND if not found.
+ */
+LIBYANG_API_DECL LY_ERR lyht_find_with_val_cb(const struct ly_ht *ht, void *val_p, uint32_t hash,
+ lyht_value_equal_cb val_equal, void **match_p);
/**
* @brief Find another equal value in the hash table.
@@ -192,7 +154,7 @@ LY_ERR lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match
* @return LY_SUCCESS if value was found,
* @return LY_ENOTFOUND if not found.
*/
-LY_ERR lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_find_next(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
/**
* @brief Find another equal value in the hash table. Same functionality as ::lyht_find_next()
@@ -206,7 +168,7 @@ LY_ERR lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **
* @return LY_SUCCESS if value was found,
* @return LY_ENOTFOUND if not found.
*/
-LY_ERR lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint32_t hash,
+LIBYANG_API_DECL LY_ERR lyht_find_next_with_collision_cb(const struct ly_ht *ht, void *val_p, uint32_t hash,
lyht_value_equal_cb collision_val_equal, void **match_p);
/**
@@ -221,7 +183,20 @@ LY_ERR lyht_find_next_with_collision_cb(struct hash_table *ht, void *val_p, uint
* @return LY_EEXIST in case the value is already present.
* @return LY_EMEM in case of memory allocation failure.
*/
-LY_ERR lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_insert(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
+
+/**
+ * @brief Insert a value into a hash table, without checking whether the value has already been inserted.
+ *
+ * @param[in] ht Hash table to insert into.
+ * @param[in] val_p Pointer to the value to insert. Be careful, if the values stored in the hash table
+ * are pointers, @p val_p must be a pointer to a pointer.
+ * @param[in] hash Hash of the stored value.
+ * @param[out] match_p Pointer to the stored value, optional
+ * @return LY_SUCCESS on success,
+ * @return LY_EMEM in case of memory allocation failure.
+ */
+LIBYANG_API_DECL LY_ERR lyht_insert_no_check(struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p);
/**
* @brief Insert a value into hash table. Same functionality as ::lyht_insert()
@@ -238,8 +213,8 @@ LY_ERR lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **mat
* @return LY_EEXIST in case the value is already present.
* @return LY_EMEM in case of memory allocation failure.
*/
-LY_ERR lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal,
- void **match_p);
+LIBYANG_API_DECL LY_ERR lyht_insert_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash,
+ lyht_value_equal_cb resize_val_equal, void **match_p);
/**
* @brief Remove a value from a hash table.
@@ -251,7 +226,7 @@ LY_ERR lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t h
* @return LY_SUCCESS on success,
* @return LY_ENOTFOUND if value was not found.
*/
-LY_ERR lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash);
+LIBYANG_API_DECL LY_ERR lyht_remove(struct ly_ht *ht, void *val_p, uint32_t hash);
/**
* @brief Remove a value from a hash table. Same functionality as ::lyht_remove()
@@ -266,7 +241,8 @@ LY_ERR lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash);
* @return LY_SUCCESS on success,
* @return LY_ENOTFOUND if value was not found.
*/
-LY_ERR lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash, lyht_value_equal_cb resize_val_equal);
+LIBYANG_API_DECL LY_ERR lyht_remove_with_resize_cb(struct ly_ht *ht, void *val_p, uint32_t hash,
+ lyht_value_equal_cb resize_val_equal);
/**
* @brief Get suitable size of a hash table for a fixed number of items.
@@ -274,6 +250,10 @@ LY_ERR lyht_remove_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t h
* @param[in] item_count Number of stored items.
* @return Hash table size.
*/
-uint32_t lyht_get_fixed_size(uint32_t item_count);
+LIBYANG_API_DECL uint32_t lyht_get_fixed_size(uint32_t item_count);
+
+#ifdef __cplusplus
+}
+#endif
#endif /* LY_HASH_TABLE_H_ */
diff --git a/src/hash_table_internal.h b/src/hash_table_internal.h
new file mode 100644
index 0000000..22b6cf4
--- /dev/null
+++ b/src/hash_table_internal.h
@@ -0,0 +1,146 @@
+/**
+ * @file hash_table_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief libyang hash table internal header
+ *
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_HASH_TABLE_INTERNAL_H_
+#define LY_HASH_TABLE_INTERNAL_H_
+
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "compat.h"
+#include "hash_table.h"
+
+/** reference value for 100% */
+#define LYHT_HUNDRED_PERCENTAGE 100
+
+/** when the table is at least this much percent full, it is enlarged (double the size) */
+#define LYHT_ENLARGE_PERCENTAGE 75
+
+/** only once the table is this much percent full, enable shrinking */
+#define LYHT_FIRST_SHRINK_PERCENTAGE 50
+
+/** when the table is less than this much percent full, it is shrunk (half the size) */
+#define LYHT_SHRINK_PERCENTAGE 25
+
+/** never shrink beyond this size */
+#define LYHT_MIN_SIZE 8
+
+/**
+ * @brief Generic hash table record.
+ */
+struct ly_ht_rec {
+ uint32_t hash; /* hash of the value */
+ uint32_t next; /* index of next collision */
+ unsigned char val[1]; /* arbitrary-size value */
+} _PACKED;
+
+/* real size, without taking the val[1] in account */
+#define SIZEOF_LY_HT_REC (sizeof(struct ly_ht_rec) - 1)
+
+struct ly_ht_hlist {
+ uint32_t first;
+ uint32_t last;
+};
+
+/**
+ * @brief (Very) generic hash table.
+ *
+ * This structure implements a hash table, providing fast accesses to stored
+ * values from their hash.
+ *
+ * The hash table structure contains 2 pointers to tables that are allocated
+ * at initialization:
+ * - a table of records: each record is composed of a struct ly_ht_rec header,
+ * followed by the user value. The header contains the hash value and a
+ * next index that can be used to chain records.
+ * - a table of list heads: each list head entry contains the index of the
+ * first record in the records table whose hash (modulo hash table size)
+ * is equal to the index of the list head entry. The other matching records
+ * are chained together.
+ *
+ * The unused records are chained in first_free_rec, which contains the index
+ * of the first unused record entry in the records table.
+ *
+ * The LYHT_NO_RECORD magic value is used when an index points to nothing.
+ */
+struct ly_ht {
+ uint32_t used; /* number of values stored in the hash table (filled records) */
+ uint32_t size; /* always holds 2^x == size (is power of 2), actually number of records allocated */
+ lyht_value_equal_cb val_equal; /* callback for testing value equivalence */
+ void *cb_data; /* user data callback arbitrary value */
+ uint16_t resize; /* 0 - resizing is disabled, *
+ * 1 - enlarging is enabled, *
+ * 2 - both shrinking and enlarging is enabled */
+ uint16_t rec_size; /* real size (in bytes) of one record for accessing recs array */
+ uint32_t first_free_rec; /* index of the first free record */
+ struct ly_ht_hlist *hlists; /* pointer to the hlists table */
+ unsigned char *recs; /* pointer to the hash table itself (array of struct ht_rec) */
+};
+
+/* index that points to nothing */
+#define LYHT_NO_RECORD UINT32_MAX
+
+/* get the record associated to */
+static inline struct ly_ht_rec *
+lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx)
+{
+ return (struct ly_ht_rec *)&recs[idx * rec_size];
+}
+
+/* Iterate all records in a hlist */
+#define LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) \
+ for (rec_idx = ht->hlists[hlist_idx].first, \
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx); \
+ rec_idx != LYHT_NO_RECORD; \
+ rec_idx = rec->next, \
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx))
+
+/* Iterate all records in the hash table */
+#define LYHT_ITER_ALL_RECS(ht, hlist_idx, rec_idx, rec) \
+ for (hlist_idx = 0; hlist_idx < ht->size; hlist_idx++) \
+ LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec)
+
+/**
+ * @brief Dictionary hash table record.
+ */
+struct ly_dict_rec {
+ char *value; /**< stored string */
+ uint32_t refcount; /**< reference count of the string */
+};
+
+/**
+ * @brief Dictionary for storing repeated strings.
+ */
+struct ly_dict {
+ struct ly_ht *hash_tab;
+ pthread_mutex_t lock;
+};
+
+/**
+ * @brief Initiate content (non-zero values) of the dictionary
+ *
+ * @param[in] dict Dictionary table to initiate
+ */
+void lydict_init(struct ly_dict *dict);
+
+/**
+ * @brief Cleanup the dictionary content
+ *
+ * @param[in] dict Dictionary table to cleanup
+ */
+void lydict_clean(struct ly_dict *dict);
+
+#endif /* LY_HASH_TABLE_INTERNAL_H_ */
diff --git a/src/json.c b/src/json.c
index 5c45c8c..53f41dc 100644
--- a/src/json.c
+++ b/src/json.c
@@ -1,9 +1,10 @@
/**
* @file json.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Generic JSON format parser for libyang
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -30,30 +31,30 @@ lyjson_token2str(enum LYJSON_PARSER_STATUS status)
switch (status) {
case LYJSON_ERROR:
return "error";
- case LYJSON_ROOT:
- return "document root";
- case LYJSON_FALSE:
- return "false";
- case LYJSON_TRUE:
- return "true";
- case LYJSON_NULL:
- return "null";
case LYJSON_OBJECT:
return "object";
+ case LYJSON_OBJECT_NEXT:
+ return "object next";
case LYJSON_OBJECT_CLOSED:
return "object closed";
- case LYJSON_OBJECT_EMPTY:
- return "empty object";
case LYJSON_ARRAY:
return "array";
+ case LYJSON_ARRAY_NEXT:
+ return "array next";
case LYJSON_ARRAY_CLOSED:
return "array closed";
- case LYJSON_ARRAY_EMPTY:
- return "empty array";
+ case LYJSON_OBJECT_NAME:
+ return "object name";
case LYJSON_NUMBER:
return "number";
case LYJSON_STRING:
return "string";
+ case LYJSON_TRUE:
+ return "true";
+ case LYJSON_FALSE:
+ return "false";
+ case LYJSON_NULL:
+ return "null";
case LYJSON_END:
return "end of input";
}
@@ -61,25 +62,48 @@ lyjson_token2str(enum LYJSON_PARSER_STATUS status)
return "";
}
-static LY_ERR
-skip_ws(struct lyjson_ctx *jsonctx)
+enum LYJSON_PARSER_STATUS
+lyjson_ctx_status(struct lyjson_ctx *jsonctx)
{
- /* skip leading whitespaces */
- while (*jsonctx->in->current != '\0' && is_jsonws(*jsonctx->in->current)) {
+ assert(jsonctx);
+
+ if (!jsonctx->status.count) {
+ return LYJSON_END;
+ }
+
+ return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - 1];
+}
+
+uint32_t
+lyjson_ctx_depth(struct lyjson_ctx *jsonctx)
+{
+ return jsonctx->status.count;
+}
+
+/**
+ * @brief Skip WS in the JSON context.
+ *
+ * @param[in] jsonctx JSON parser context.
+ */
+static void
+lyjson_skip_ws(struct lyjson_ctx *jsonctx)
+{
+ /* skip whitespaces */
+ while (is_jsonws(*jsonctx->in->current)) {
if (*jsonctx->in->current == '\n') {
LY_IN_NEW_LINE(jsonctx->in);
}
ly_in_skip(jsonctx->in, 1);
}
- if (*jsonctx->in->current == '\0') {
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_END);
- }
-
- return LY_SUCCESS;
}
-/*
- * @brief Set value corresponding to the current context's status
+/**
+ * @brief Set value in the JSON context.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @param[in] value Value to set.
+ * @param[in] value_len Length of @p value.
+ * @param[in] dynamic Whether @p value is dynamically-allocated.
*/
static void
lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic)
@@ -94,48 +118,24 @@ lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value
jsonctx->dynamic = dynamic;
}
-static LY_ERR
-lyjson_check_next(struct lyjson_ctx *jsonctx)
-{
- if (jsonctx->status.count == 1) {
- /* top level value (JSON-text), ws expected */
- if ((*jsonctx->in->current == '\0') || is_jsonws(*jsonctx->in->current)) {
- return LY_SUCCESS;
- }
- } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_OBJECT) {
- LY_CHECK_RET(skip_ws(jsonctx));
- if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == '}')) {
- return LY_SUCCESS;
- }
- } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_ARRAY) {
- LY_CHECK_RET(skip_ws(jsonctx));
- if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == ']')) {
- return LY_SUCCESS;
- }
- }
-
- LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Unexpected character \"%c\" after JSON %s.",
- *jsonctx->in->current, lyjson_token2str(lyjson_ctx_status(jsonctx, 0)));
- return LY_EVALID;
-}
-
/**
- * Input is expected to start after the opening quotation-mark.
- * When succeeds, input is moved after the closing quotation-mark.
+ * @brief Parse a JSON string (starting after double quotes) and store it in the context.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @return LY_ERR value.
*/
static LY_ERR
-lyjson_string_(struct lyjson_ctx *jsonctx)
+lyjson_string(struct lyjson_ctx *jsonctx)
{
-#define BUFSIZE 24
-#define BUFSIZE_STEP 128
-
- const char *in = jsonctx->in->current, *start;
+ const char *in = jsonctx->in->current, *start, *c;
char *buf = NULL;
size_t offset; /* read offset in input buffer */
size_t len; /* length of the output string (write offset in output buffer) */
size_t size = 0; /* size of the output buffer */
size_t u;
uint64_t start_line;
+ uint32_t value;
+ uint8_t i;
assert(jsonctx);
@@ -146,17 +146,15 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
/* parse */
while (in[offset]) {
- if (in[offset] == '\\') {
+ switch (in[offset]) {
+ case '\\':
/* escape sequence */
- const char *slash = &in[offset];
- uint32_t value;
- uint8_t i = 1;
-
+ c = &in[offset];
if (!buf) {
/* prepare output buffer */
- buf = malloc(BUFSIZE);
+ buf = malloc(LYJSON_STRING_BUF_START);
LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
- size = BUFSIZE;
+ size = LYJSON_STRING_BUF_START;
}
/* allocate enough for the offset and next character,
@@ -165,10 +163,10 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
if (len + offset + 4 >= size) {
size_t increment;
- for (increment = BUFSIZE_STEP; len + offset + 4 >= size + increment; increment += BUFSIZE_STEP) {}
+ for (increment = LYJSON_STRING_BUF_STEP; len + offset + 4 >= size + increment; increment += LYJSON_STRING_BUF_STEP) {}
buf = ly_realloc(buf, size + increment);
LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM);
- size += BUFSIZE_STEP;
+ size += LYJSON_STRING_BUF_STEP;
}
if (offset) {
@@ -179,6 +177,7 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
offset = 0;
}
+ i = 1;
switch (in[++offset]) {
case '"':
/* quotation mark */
@@ -217,7 +216,7 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
offset++;
for (value = i = 0; i < 4; i++) {
if (!in[offset + i]) {
- LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", slash);
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", c);
goto error;
} else if (isdigit(in[offset + i])) {
u = (in[offset + i] - '0');
@@ -239,13 +238,14 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
offset += i; /* add read escaped characters */
LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u),
LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).",
- (int)(&in[offset] - slash), slash, value),
+ (int)(&in[offset] - c), c, value),
error);
len += u; /* update number of bytes in buffer */
in += offset; /* move the input by the processed bytes stored in the buffer ... */
offset = 0; /* ... and reset the offset index for future moving data into buffer */
+ break;
- } else if (in[offset] == '"') {
+ case '"':
/* end of string */
if (buf) {
/* realloc exact size string */
@@ -263,22 +263,21 @@ lyjson_string_(struct lyjson_ctx *jsonctx)
++offset;
in += offset;
goto success;
- } else {
- /* get it as UTF-8 character for check */
- const char *c = &in[offset];
- uint32_t code = 0;
- size_t code_len = 0;
- LY_CHECK_ERR_GOTO(ly_getutf8(&c, &code, &code_len),
+ default:
+ /* get it as UTF-8 character for check */
+ c = &in[offset];
+ LY_CHECK_ERR_GOTO(ly_getutf8(&c, &value, &u),
LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error);
- LY_CHECK_ERR_GOTO(!is_jsonstrchar(code),
+ LY_CHECK_ERR_GOTO(!is_jsonstrchar(value),
LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08x).",
- (int)(&in[offset] - start + code_len), start, code),
+ (int)(&in[offset] - start + u), start, value),
error);
/* character is ok, continue */
- offset += code_len;
+ offset += u;
+ break;
}
}
@@ -299,24 +298,6 @@ success:
}
return LY_SUCCESS;
-
-#undef BUFSIZE
-#undef BUFSIZE_STEP
-}
-
-/*
- *
- * Wrapper around lyjson_string_() adding LYJSON_STRING status into context to allow using lyjson_string_() for parsing object's name.
- */
-static LY_ERR
-lyjson_string(struct lyjson_ctx *jsonctx)
-{
- LY_CHECK_RET(lyjson_string_(jsonctx));
-
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_STRING);
- LY_CHECK_RET(lyjson_check_next(jsonctx));
-
- return LY_SUCCESS;
}
/**
@@ -414,8 +395,7 @@ lyjson_get_buffer_for_number(const struct ly_ctx *ctx, uint64_t num_len, char **
* @return Number of characters written to the @p dst.
*/
static uint32_t
-lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len,
- char *dec_point, int32_t dp_position, char *dst)
+lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len, char *dec_point, int32_t dp_position, char *dst)
{
int32_t dec_point_idx;
int32_t n, d;
@@ -456,8 +436,8 @@ lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len,
* @return LY_ERR value.
*/
static LY_ERR
-lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent,
- uint64_t total_len, char **res, size_t *res_len)
+lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent, uint64_t total_len, char **res,
+ size_t *res_len)
{
#define MAYBE_WRITE_MINUS(ARRAY, INDEX, FLAG) \
@@ -642,6 +622,12 @@ lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent
return LY_SUCCESS;
}
+/**
+ * @brief Parse a JSON number and store it in the context.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @return LY_ERR value.
+ */
static LY_ERR
lyjson_number(struct lyjson_ctx *jsonctx)
{
@@ -713,140 +699,270 @@ invalid_character:
}
ly_in_skip(jsonctx->in, offset);
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NUMBER);
- LY_CHECK_RET(lyjson_check_next(jsonctx));
-
return LY_SUCCESS;
}
-static LY_ERR
-lyjson_object_name(struct lyjson_ctx *jsonctx)
+LY_ERR
+lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
{
- if (*jsonctx->in->current != '"') {
- LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
- jsonctx->in->current, "a JSON object's member");
- return LY_EVALID;
- }
- ly_in_skip(jsonctx->in, 1);
+ LY_ERR ret = LY_SUCCESS;
+ struct lyjson_ctx *jsonctx;
- LY_CHECK_RET(lyjson_string_(jsonctx));
- LY_CHECK_RET(skip_ws(jsonctx));
- if (*jsonctx->in->current != ':') {
- LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
- "a JSON object's name-separator ':'");
- return LY_EVALID;
+ assert(ctx && in && jsonctx_p);
+
+ /* new context */
+ jsonctx = calloc(1, sizeof *jsonctx);
+ LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
+ jsonctx->ctx = ctx;
+ jsonctx->in = in;
+
+ LOG_LOCSET(NULL, NULL, NULL, in);
+
+ /* WS are always expected to be skipped */
+ lyjson_skip_ws(jsonctx);
+
+ if (jsonctx->in->current[0] == '\0') {
+ /* empty file, invalid */
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON file.");
+ ret = LY_EVALID;
+ goto cleanup;
}
- ly_in_skip(jsonctx->in, 1);
- LY_CHECK_RET(skip_ws(jsonctx));
- return LY_SUCCESS;
+ /* start JSON parsing */
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(jsonctx, NULL), cleanup);
+
+cleanup:
+ if (ret) {
+ lyjson_ctx_free(jsonctx);
+ } else {
+ *jsonctx_p = jsonctx;
+ }
+ return ret;
}
+/**
+ * @brief Parse next JSON token, object-name is expected.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @return LY_ERR value.
+ */
static LY_ERR
-lyjson_object(struct lyjson_ctx *jsonctx)
+lyjson_next_object_name(struct lyjson_ctx *jsonctx)
{
- LY_CHECK_RET(skip_ws(jsonctx));
+ switch (*jsonctx->in->current) {
+ case '\0':
+ /* EOF */
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
- if (*jsonctx->in->current == '}') {
- assert(jsonctx->depth);
- jsonctx->depth--;
- /* empty object */
+ case '"':
+ /* object name */
ly_in_skip(jsonctx->in, 1);
- lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_EMPTY);
- return LY_SUCCESS;
- }
+ LY_CHECK_RET(lyjson_string(jsonctx));
+ lyjson_skip_ws(jsonctx);
- LY_CHECK_RET(lyjson_object_name(jsonctx));
+ if (*jsonctx->in->current != ':') {
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
+ "a JSON value name-separator ':'");
+ return LY_EVALID;
+ }
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NAME);
+ break;
- /* output data are set by lyjson_string_() */
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT);
+ case '}':
+ /* object end */
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
+ break;
+
+ default:
+ /* unexpected value */
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
+ jsonctx->in->current, "a JSON object name");
+ return LY_EVALID;
+ }
return LY_SUCCESS;
}
/**
- * @brief Process JSON array envelope
+ * @brief Parse next JSON token, value is expected.
*
- * @param[in] jsonctx JSON parser context
- * @return LY_SUCCESS or LY_EMEM
+ * @param[in] jsonctx JSON parser context.
+ * @param[in] array_end Whether array-end is accepted or not.
+ * @return LY_ERR value.
*/
static LY_ERR
-lyjson_array(struct lyjson_ctx *jsonctx)
+lyjson_next_value(struct lyjson_ctx *jsonctx, ly_bool array_end)
{
- LY_CHECK_RET(skip_ws(jsonctx));
+ switch (*jsonctx->in->current) {
+ case '\0':
+ /* EOF */
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
- if (*jsonctx->in->current == ']') {
- /* empty array */
+ case '"':
+ /* string */
+ ly_in_skip(jsonctx->in, 1);
+ LY_CHECK_RET(lyjson_string(jsonctx));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_STRING);
+ break;
+
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ /* number */
+ LY_CHECK_RET(lyjson_number(jsonctx));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NUMBER);
+ break;
+
+ case '{':
+ /* object */
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT);
+ break;
+
+ case '[':
+ /* array */
ly_in_skip(jsonctx->in, 1);
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_EMPTY);
- } else {
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY);
- }
+ break;
- /* erase previous values, array has no value on its own */
- lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
+ case 't':
+ if (strncmp(jsonctx->in->current + 1, "rue", ly_strlen_const("rue"))) {
+ goto unexpected_value;
+ }
- return LY_SUCCESS;
-}
+ /* true */
+ lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
+ ly_in_skip(jsonctx->in, ly_strlen_const("true"));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_TRUE);
+ break;
-static LY_ERR
-lyjson_value(struct lyjson_ctx *jsonctx)
-{
- if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
- return LY_SUCCESS;
- }
+ case 'f':
+ if (strncmp(jsonctx->in->current + 1, "alse", ly_strlen_const("alse"))) {
+ goto unexpected_value;
+ }
- if ((*jsonctx->in->current == 'f') && !strncmp(jsonctx->in->current, "false", ly_strlen_const("false"))) {
/* false */
lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0);
ly_in_skip(jsonctx->in, ly_strlen_const("false"));
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_FALSE);
- LY_CHECK_RET(lyjson_check_next(jsonctx));
+ break;
- } else if ((*jsonctx->in->current == 't') && !strncmp(jsonctx->in->current, "true", ly_strlen_const("true"))) {
- /* true */
- lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0);
- ly_in_skip(jsonctx->in, ly_strlen_const("true"));
- LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_TRUE);
- LY_CHECK_RET(lyjson_check_next(jsonctx));
+ case 'n':
+ if (strncmp(jsonctx->in->current + 1, "ull", ly_strlen_const("ull"))) {
+ goto unexpected_value;
+ }
- } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) {
- /* none */
+ /* null */
lyjson_ctx_set_value(jsonctx, "", 0, 0);
ly_in_skip(jsonctx->in, ly_strlen_const("null"));
LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NULL);
- LY_CHECK_RET(lyjson_check_next(jsonctx));
+ break;
- } else if (*jsonctx->in->current == '"') {
- /* string */
+ case ']':
+ if (!array_end) {
+ goto unexpected_value;
+ }
+
+ /* array end */
ly_in_skip(jsonctx->in, 1);
- LY_CHECK_RET(lyjson_string(jsonctx));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
+ break;
- } else if (*jsonctx->in->current == '[') {
- /* array */
+ default:
+unexpected_value:
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
+ jsonctx->in->current, "a JSON value");
+ return LY_EVALID;
+ }
+
+ if (jsonctx->status.count > LY_MAX_BLOCK_DEPTH * 10) {
+ LOGERR(jsonctx->ctx, LY_EINVAL, "Maximum number %d of nestings has been exceeded.", LY_MAX_BLOCK_DEPTH * 10);
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse next JSON token, object-next-item is expected.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyjson_next_object_item(struct lyjson_ctx *jsonctx)
+{
+ switch (*jsonctx->in->current) {
+ case '\0':
+ /* EOF */
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
+
+ case '}':
+ /* object end */
ly_in_skip(jsonctx->in, 1);
- LY_CHECK_RET(lyjson_array(jsonctx));
-
- } else if (*jsonctx->in->current == '{') {
- jsonctx->depth++;
- if (jsonctx->depth > LY_MAX_BLOCK_DEPTH) {
- LOGERR(jsonctx->ctx, LY_EINVAL,
- "The maximum number of block nestings has been exceeded.");
- return LY_EINVAL;
- }
- /* object */
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
+ break;
+
+ case ',':
+ /* next object item */
ly_in_skip(jsonctx->in, 1);
- LY_CHECK_RET(lyjson_object(jsonctx));
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NEXT);
+ break;
- } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) {
- /* number */
- LY_CHECK_RET(lyjson_number(jsonctx));
+ default:
+ /* unexpected value */
+ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
+ jsonctx->in->current, "a JSON object-end or next item");
+ return LY_EVALID;
+ }
- } else {
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse next JSON token, array-next-item is expected.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyjson_next_array_item(struct lyjson_ctx *jsonctx)
+{
+ switch (*jsonctx->in->current) {
+ case '\0':
+ /* EOF */
+ LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+ return LY_EVALID;
+
+ case ']':
+ /* array end */
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
+ break;
+
+ case ',':
+ /* next array item */
+ ly_in_skip(jsonctx->in, 1);
+ LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_NEXT);
+ break;
+
+ default:
/* unexpected value */
LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current),
- jsonctx->in->current, "a JSON value");
+ jsonctx->in->current, "a JSON array-end or next item");
return LY_EVALID;
}
@@ -854,46 +970,72 @@ lyjson_value(struct lyjson_ctx *jsonctx)
}
LY_ERR
-lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, ly_bool subtree, struct lyjson_ctx **jsonctx_p)
+lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
{
LY_ERR ret = LY_SUCCESS;
- struct lyjson_ctx *jsonctx;
+ enum LYJSON_PARSER_STATUS cur;
- assert(ctx);
- assert(in);
- assert(jsonctx_p);
-
- /* new context */
- jsonctx = calloc(1, sizeof *jsonctx);
- LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM);
- jsonctx->ctx = ctx;
- jsonctx->in = in;
+ assert(jsonctx);
- LOG_LOCSET(NULL, NULL, NULL, in);
+ cur = lyjson_ctx_status(jsonctx);
+ switch (cur) {
+ case LYJSON_OBJECT:
+ LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup);
+ break;
+ case LYJSON_ARRAY:
+ LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 1), cleanup);
+ break;
+ case LYJSON_OBJECT_NEXT:
+ LYJSON_STATUS_POP(jsonctx);
+ LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup);
+ break;
+ case LYJSON_ARRAY_NEXT:
+ LYJSON_STATUS_POP(jsonctx);
+ LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
+ break;
+ case LYJSON_OBJECT_NAME:
+ lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
+ LYJSON_STATUS_POP(jsonctx);
+ LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
+ break;
+ case LYJSON_OBJECT_CLOSED:
+ case LYJSON_ARRAY_CLOSED:
+ LYJSON_STATUS_POP(jsonctx);
+ /* fallthrough */
+ case LYJSON_NUMBER:
+ case LYJSON_STRING:
+ case LYJSON_TRUE:
+ case LYJSON_FALSE:
+ case LYJSON_NULL:
+ lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
+ LYJSON_STATUS_POP(jsonctx);
+ cur = lyjson_ctx_status(jsonctx);
+
+ if (cur == LYJSON_OBJECT) {
+ LY_CHECK_GOTO(ret = lyjson_next_object_item(jsonctx), cleanup);
+ break;
+ } else if (cur == LYJSON_ARRAY) {
+ LY_CHECK_GOTO(ret = lyjson_next_array_item(jsonctx), cleanup);
+ break;
+ }
- /* parse JSON value, if any */
- LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup);
- if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) {
- /* empty data input */
+ assert(cur == LYJSON_END);
+ goto cleanup;
+ case LYJSON_END:
+ LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup);
+ break;
+ case LYJSON_ERROR:
+ LOGINT(jsonctx->ctx);
+ ret = LY_EINT;
goto cleanup;
}
- if (subtree) {
- ret = lyjson_object(jsonctx);
- jsonctx->depth++;
- } else {
- ret = lyjson_value(jsonctx);
- }
- if ((jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
- LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
- ret = LY_EVALID;
- }
+ /* skip WS */
+ lyjson_skip_ws(jsonctx);
cleanup:
- if (ret) {
- lyjson_ctx_free(jsonctx);
- } else {
- *jsonctx_p = jsonctx;
+ if (!ret && status) {
+ *status = lyjson_ctx_status(jsonctx);
}
return ret;
}
@@ -904,13 +1046,12 @@ lyjson_ctx_backup(struct lyjson_ctx *jsonctx)
if (jsonctx->backup.dynamic) {
free((char *)jsonctx->backup.value);
}
- jsonctx->backup.status = lyjson_ctx_status(jsonctx, 0);
+ jsonctx->backup.status = lyjson_ctx_status(jsonctx);
jsonctx->backup.status_count = jsonctx->status.count;
jsonctx->backup.value = jsonctx->value;
jsonctx->backup.value_len = jsonctx->value_len;
jsonctx->backup.input = jsonctx->in->current;
jsonctx->backup.dynamic = jsonctx->dynamic;
- jsonctx->backup.depth = jsonctx->depth;
jsonctx->dynamic = 0;
}
@@ -926,105 +1067,9 @@ lyjson_ctx_restore(struct lyjson_ctx *jsonctx)
jsonctx->value_len = jsonctx->backup.value_len;
jsonctx->in->current = jsonctx->backup.input;
jsonctx->dynamic = jsonctx->backup.dynamic;
- jsonctx->depth = jsonctx->backup.depth;
jsonctx->backup.dynamic = 0;
}
-LY_ERR
-lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
-{
- LY_ERR ret = LY_SUCCESS;
- ly_bool toplevel = 0;
- enum LYJSON_PARSER_STATUS prev;
-
- assert(jsonctx);
-
- prev = lyjson_ctx_status(jsonctx, 0);
-
- if ((prev == LYJSON_OBJECT) || (prev == LYJSON_ARRAY)) {
- /* get value for the object's member OR the first value in the array */
- ret = lyjson_value(jsonctx);
- goto result;
- } else {
- /* the previous token is closed and should be completely processed */
- LYJSON_STATUS_POP_RET(jsonctx);
- prev = lyjson_ctx_status(jsonctx, 0);
- }
-
- if (!jsonctx->status.count) {
- /* we are done with the top level value */
- toplevel = 1;
- }
- LY_CHECK_RET(skip_ws(jsonctx));
- if (toplevel && !jsonctx->status.count) {
- /* EOF expected, but there are some data after the top level token */
- LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Expecting end-of-input, but some data follows the top level JSON value.");
- return LY_EVALID;
- }
-
- if (toplevel) {
- /* we are done */
- goto result;
- }
-
- /* continue with the next token */
- assert(prev == LYJSON_OBJECT || prev == LYJSON_ARRAY);
-
- if (*jsonctx->in->current == ',') {
- /* sibling item in the ... */
- ly_in_skip(jsonctx->in, 1);
- LY_CHECK_RET(skip_ws(jsonctx));
-
- if (prev == LYJSON_OBJECT) {
- /* ... object - get another object's member */
- ret = lyjson_object_name(jsonctx);
- } else { /* LYJSON_ARRAY */
- /* ... array - get another complete value */
- ret = lyjson_value(jsonctx);
- }
- } else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) ||
- ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) {
- if (*jsonctx->in->current == '}') {
- assert(jsonctx->depth);
- jsonctx->depth--;
- }
- ly_in_skip(jsonctx->in, 1);
- LYJSON_STATUS_POP_RET(jsonctx);
- LYJSON_STATUS_PUSH_RET(jsonctx, prev + 1);
- } else {
- /* unexpected value */
- LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current,
- prev == LYJSON_ARRAY ? "another JSON value in array" : "another JSON object's member");
- return LY_EVALID;
- }
-
-result:
- if ((ret == LY_SUCCESS) && (jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
- LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
- ret = LY_EVALID;
- }
-
- if ((ret == LY_SUCCESS) && status) {
- *status = lyjson_ctx_status(jsonctx, 0);
- }
-
- return ret;
-}
-
-enum LYJSON_PARSER_STATUS
-lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index)
-{
- assert(jsonctx);
-
- if (jsonctx->status.count < index) {
- return LYJSON_ERROR;
- } else if (jsonctx->status.count == index) {
- return LYJSON_ROOT;
- } else {
- return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - (index + 1)];
- }
-}
-
void
lyjson_ctx_free(struct lyjson_ctx *jsonctx)
{
diff --git a/src/json.h b/src/json.h
index 53efe2a..61c561a 100644
--- a/src/json.h
+++ b/src/json.h
@@ -1,9 +1,10 @@
/**
* @file json.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Generic JSON format parser routines.
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -24,6 +25,9 @@
struct ly_ctx;
struct ly_in;
+#define LYJSON_STRING_BUF_START 24
+#define LYJSON_STRING_BUF_STEP 128
+
/* Macro to test if character is whitespace */
#define is_jsonws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
@@ -35,27 +39,27 @@ struct ly_in;
LY_CHECK_RET(ly_set_add(&CTX->status, (void *)(uintptr_t)(STATUS), 1, NULL))
/* Macro to pop JSON parser status */
-#define LYJSON_STATUS_POP_RET(CTX) \
+#define LYJSON_STATUS_POP(CTX) \
assert(CTX->status.count); CTX->status.count--;
/**
* @brief Status of the parser providing information what is expected next (which function is supposed to be called).
*/
enum LYJSON_PARSER_STATUS {
- LYJSON_ERROR, /* JSON parser error - value is used as an error return code */
- LYJSON_ROOT, /* JSON document root, used internally */
- LYJSON_OBJECT, /* JSON object */
- LYJSON_OBJECT_CLOSED, /* JSON object closed */
- LYJSON_OBJECT_EMPTY, /* empty JSON object { } */
- LYJSON_ARRAY, /* JSON array */
- LYJSON_ARRAY_CLOSED, /* JSON array closed */
- LYJSON_ARRAY_EMPTY, /* empty JSON array */
- LYJSON_NUMBER, /* JSON number value */
- LYJSON_STRING, /* JSON string value */
- LYJSON_FALSE, /* JSON false value */
- LYJSON_TRUE, /* JSON true value */
- LYJSON_NULL, /* JSON null value */
- LYJSON_END /* end of input data */
+ LYJSON_ERROR = 0, /* JSON parser error - value is used as an error return code */
+ LYJSON_OBJECT, /* JSON object */
+ LYJSON_OBJECT_NEXT, /* JSON object next item */
+ LYJSON_OBJECT_CLOSED, /* JSON object closed */
+ LYJSON_ARRAY, /* JSON array */
+ LYJSON_ARRAY_NEXT, /* JSON array next item */
+ LYJSON_ARRAY_CLOSED, /* JSON array closed */
+ LYJSON_OBJECT_NAME, /* JSON object name */
+ LYJSON_NUMBER, /* JSON number value */
+ LYJSON_STRING, /* JSON string value */
+ LYJSON_TRUE, /* JSON true value */
+ LYJSON_FALSE, /* JSON false value */
+ LYJSON_NULL, /* JSON null value */
+ LYJSON_END /* end of input data */
};
struct lyjson_ctx {
@@ -64,10 +68,9 @@ struct lyjson_ctx {
struct ly_set status; /* stack of ::LYJSON_PARSER_STATUS values corresponding to the JSON items being processed */
- const char *value; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
- size_t value_len; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
- ly_bool dynamic; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT */
- uint32_t depth; /* current number of nested blocks, see ::LY_MAX_BLOCK_DEPTH */
+ const char *value; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT_NAME */
+ size_t value_len; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT_NAME */
+ ly_bool dynamic; /* ::LYJSON_STRING, ::LYJSON_NUMBER, ::LYJSON_OBJECT_NAME */
struct {
enum LYJSON_PARSER_STATUS status;
@@ -75,38 +78,44 @@ struct lyjson_ctx {
const char *value;
size_t value_len;
ly_bool dynamic;
- uint32_t depth;
const char *input;
} backup;
};
/**
- * @brief Create a new JSON parser context and start parsing.
+ * @brief Get string representation of the JSON context status (token).
*
- * @param[in] ctx libyang context.
- * @param[in] in JSON string data to parse.
- * @param[in] subtree Whether this is a special case of parsing a subtree (starting with object name).
- * @param[out] jsonctx New JSON context with status referring the parsed value.
- * @return LY_ERR value.
+ * @param[in] status Context status (aka JSON token)
+ * @return String representation of the @p status.
*/
-LY_ERR lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, ly_bool subtree, struct lyjson_ctx **jsonctx);
+const char *lyjson_token2str(enum LYJSON_PARSER_STATUS status);
/**
- * @brief Get status of the parser as the last/previous parsed token
+ * @brief Get current status of the parser.
*
- * @param[in] jsonctx JSON context to check.
- * @param[in] index Index of the token, starting by 0 for the last token
- * @return ::LYJSON_ERROR in case of invalid index, other ::LYJSON_PARSER_STATUS corresponding to the token.
+ * @param[in] jsonctx JSON parser context to check.
+ * @return ::LYJSON_PARSER_STATUS according to the last parsed token.
*/
-enum LYJSON_PARSER_STATUS lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index);
+enum LYJSON_PARSER_STATUS lyjson_ctx_status(struct lyjson_ctx *jsonctx);
/**
- * @brief Get string representation of the JSON context status (token).
+ * @brief Get current nesting (object/array) depth.
*
- * @param[in] status Context status (aka JSON token)
- * @return String representation of the @p status.
+ * @param[in] jsonctx JSON parser context to check.
+ * @return Current nesting depth.
*/
-const char *lyjson_token2str(enum LYJSON_PARSER_STATUS status);
+uint32_t lyjson_ctx_depth(struct lyjson_ctx *jsonctx);
+
+/**
+ * @brief Create a new JSON parser context and start parsing.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] in JSON string data to parse.
+ * @param[in] subtree Whether this is a special case of parsing a subtree (starting with object name).
+ * @param[out] jsonctx New JSON parser context with status referring the parsed value.
+ * @return LY_ERR value.
+ */
+LY_ERR lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx);
/**
* @brief Move to the next JSON artifact and update parser status.
@@ -119,12 +128,14 @@ LY_ERR lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *st
/**
* @brief Backup the JSON parser context's state To restore the backup, use ::lyjson_ctx_restore().
+ *
* @param[in] jsonctx JSON parser context to backup.
*/
void lyjson_ctx_backup(struct lyjson_ctx *jsonctx);
/**
- * @brief REstore the JSON parser context's state from the backup created by ::lyjson_ctx_backup().
+ * @brief Restore the JSON parser context's state from the backup created by ::lyjson_ctx_backup().
+ *
* @param[in] jsonctx JSON parser context to restore.
*/
void lyjson_ctx_restore(struct lyjson_ctx *jsonctx);
@@ -132,7 +143,7 @@ void lyjson_ctx_restore(struct lyjson_ctx *jsonctx);
/**
* @brief Remove the allocated working memory of the context.
*
- * @param[in] jsonctx JSON context to clear.
+ * @param[in] jsonctx JSON parser context to clear.
*/
void lyjson_ctx_free(struct lyjson_ctx *jsonctx);
diff --git a/src/libyang.h b/src/libyang.h
index 2bfc6be..f992a78 100644
--- a/src/libyang.h
+++ b/src/libyang.h
@@ -39,6 +39,7 @@ extern "C" {
/*
* The following headers are supposed to be included explicitly:
+ * - hash_table.h
* - metadata.h
* - plugins_types.h
* - plugins_exts.h
diff --git a/src/log.c b/src/log.c
index 2a1f862..41c8e90 100644
--- a/src/log.c
+++ b/src/log.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Logger routines implementations
*
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -41,15 +41,13 @@ ATOMIC_T ly_log_opts = (uint_fast32_t)(LY_LOLOG | LY_LOSTORE_LAST);
THREAD_LOCAL uint32_t *temp_ly_log_opts;
static ly_log_clb log_clb;
static ATOMIC_T path_flag = 1;
+THREAD_LOCAL char last_msg[LY_LAST_MSG_SIZE];
#ifndef NDEBUG
ATOMIC_T ly_ldbg_groups = 0;
#endif
THREAD_LOCAL struct ly_log_location_s log_location = {0};
-/* how many bytes add when enlarging buffers */
-#define LY_BUF_STEP 128
-
LIBYANG_API_DEF LY_ERR
ly_errcode(const struct ly_ctx *ctx)
{
@@ -63,6 +61,47 @@ ly_errcode(const struct ly_ctx *ctx)
return LY_SUCCESS;
}
+LIBYANG_API_DEF const char *
+ly_strerrcode(LY_ERR err)
+{
+ /* ignore plugin flag */
+ err &= ~LY_EPLUGIN;
+
+ switch (err) {
+ case LY_SUCCESS:
+ return "Success";
+ case LY_EMEM:
+ return "Out of memory";
+ case LY_ESYS:
+ return "System call failed";
+ case LY_EINVAL:
+ return "Invalid value";
+ case LY_EEXIST:
+ return "Already exists";
+ case LY_ENOTFOUND:
+ return "Not found";
+ case LY_EINT:
+ return "Internal error";
+ case LY_EVALID:
+ return "Validation failed";
+ case LY_EDENIED:
+ return "Operation denied";
+ case LY_EINCOMPLETE:
+ return "Operation incomplete";
+ case LY_ERECOMPILE:
+ return "Recompilation required";
+ case LY_ENOT:
+ return "Negative result";
+ case LY_EOTHER:
+ return "Another failure reason";
+ case LY_EPLUGIN:
+ break;
+ }
+
+ /* unreachable */
+ return "Unknown";
+}
+
LIBYANG_API_DEF LY_VECODE
ly_vecode(const struct ly_ctx *ctx)
{
@@ -77,6 +116,38 @@ ly_vecode(const struct ly_ctx *ctx)
}
LIBYANG_API_DEF const char *
+ly_strvecode(LY_VECODE vecode)
+{
+ switch (vecode) {
+ case LYVE_SUCCESS:
+ return "Success";
+ case LYVE_SYNTAX:
+ return "General syntax error";
+ case LYVE_SYNTAX_YANG:
+ return "YANG syntax error";
+ case LYVE_SYNTAX_YIN:
+ return "YIN syntax error";
+ case LYVE_REFERENCE:
+ return "Reference error";
+ case LYVE_XPATH:
+ return "XPath error";
+ case LYVE_SEMANTICS:
+ return "Semantic error";
+ case LYVE_SYNTAX_XML:
+ return "XML syntax error";
+ case LYVE_SYNTAX_JSON:
+ return "JSON syntax error";
+ case LYVE_DATA:
+ return "YANG data error";
+ case LYVE_OTHER:
+ return "Another error";
+ }
+
+ /* unreachable */
+ return "Unknown";
+}
+
+LIBYANG_API_DEF const char *
ly_errmsg(const struct ly_ctx *ctx)
{
struct ly_err_item *i;
@@ -92,6 +163,12 @@ ly_errmsg(const struct ly_ctx *ctx)
}
LIBYANG_API_DEF const char *
+ly_last_errmsg(void)
+{
+ return last_msg;
+}
+
+LIBYANG_API_DEF const char *
ly_errpath(const struct ly_ctx *ctx)
{
struct ly_err_item *i;
@@ -169,61 +246,148 @@ ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path,
return e->no;
}
+/**
+ * @brief Get error record from error hash table of a context for the current thread.
+ *
+ * @param[in] ctx Context to use.
+ * @return Thread error record, if any.
+ */
+static struct ly_ctx_err_rec *
+ly_err_get_rec(const struct ly_ctx *ctx)
+{
+ struct ly_ctx_err_rec rec, *match;
+
+ /* prepare record */
+ rec.tid = pthread_self();
+
+ /* get the pointer to the matching record */
+ if (lyht_find(ctx->err_ht, &rec, lyht_hash((void *)&rec.tid, sizeof rec.tid), (void **)&match)) {
+ return NULL;
+ }
+
+ return match;
+}
+
+/**
+ * @brief Insert new error record to error hash table of a context for the current thread.
+ *
+ * @param[in] ctx Context to use.
+ * @return Thread error record.
+ */
+static struct ly_ctx_err_rec *
+ly_err_new_rec(const struct ly_ctx *ctx)
+{
+ struct ly_ctx_err_rec new, *rec;
+ LY_ERR r;
+
+ /* insert a new record */
+ new.err = NULL;
+ new.tid = pthread_self();
+
+ /* reuse lock */
+ /* LOCK */
+ pthread_mutex_lock((pthread_mutex_t *)&ctx->lyb_hash_lock);
+
+ r = lyht_insert(ctx->err_ht, &new, lyht_hash((void *)&new.tid, sizeof new.tid), (void **)&rec);
+
+ /* UNLOCK */
+ pthread_mutex_unlock((pthread_mutex_t *)&ctx->lyb_hash_lock);
+
+ return r ? NULL : rec;
+}
+
LIBYANG_API_DEF struct ly_err_item *
ly_err_first(const struct ly_ctx *ctx)
{
+ struct ly_ctx_err_rec *rec;
+
LY_CHECK_ARG_RET(NULL, ctx, NULL);
- return pthread_getspecific(ctx->errlist_key);
+ /* get the pointer to the matching record */
+ rec = ly_err_get_rec(ctx);
+
+ return rec ? rec->err : NULL;
}
LIBYANG_API_DEF struct ly_err_item *
ly_err_last(const struct ly_ctx *ctx)
{
- const struct ly_err_item *e;
+ struct ly_ctx_err_rec *rec;
LY_CHECK_ARG_RET(NULL, ctx, NULL);
- e = pthread_getspecific(ctx->errlist_key);
- return e ? e->prev : NULL;
+ /* get the pointer to the matching record */
+ if (!(rec = ly_err_get_rec(ctx))) {
+ return NULL;
+ }
+
+ return rec->err ? rec->err->prev : NULL;
+}
+
+void
+ly_err_move(struct ly_ctx *src_ctx, struct ly_ctx *trg_ctx)
+{
+ struct ly_ctx_err_rec *rec;
+ struct ly_err_item *err = NULL;
+
+ /* get and remove the errors from src */
+ rec = ly_err_get_rec(src_ctx);
+ if (rec) {
+ err = rec->err;
+ rec->err = NULL;
+ }
+
+ /* set them for trg */
+ if (!(rec = ly_err_get_rec(trg_ctx))) {
+ if (!(rec = ly_err_new_rec(trg_ctx))) {
+ LOGINT(NULL);
+ ly_err_free(err);
+ return;
+ }
+ }
+ ly_err_free(rec->err);
+ rec->err = err;
}
LIBYANG_API_DEF void
ly_err_free(void *ptr)
{
- struct ly_err_item *i, *next;
+ struct ly_err_item *e, *next;
/* clean the error list */
- for (i = (struct ly_err_item *)ptr; i; i = next) {
- next = i->next;
- free(i->msg);
- free(i->path);
- free(i->apptag);
- free(i);
+ LY_LIST_FOR_SAFE(ptr, next, e) {
+ free(e->msg);
+ free(e->path);
+ free(e->apptag);
+ free(e);
}
}
LIBYANG_API_DEF void
ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
{
- struct ly_err_item *i, *first;
+ struct ly_ctx_err_rec *rec;
+ struct ly_err_item *e;
- first = ly_err_first(ctx);
- if (first == eitem) {
+ if (!(rec = ly_err_get_rec(ctx))) {
+ return;
+ }
+ if (rec->err == eitem) {
eitem = NULL;
}
- if (eitem) {
+
+ if (!eitem) {
+ /* free all err */
+ ly_err_free(rec->err);
+ rec->err = NULL;
+ } else {
/* disconnect the error */
- for (i = first; i && (i->next != eitem); i = i->next) {}
- assert(i);
- i->next = NULL;
- first->prev = i;
+ for (e = rec->err; e && (e->next != eitem); e = e->next) {}
+ assert(e);
+ e->next = NULL;
+ rec->err->prev = e;
/* free this err and newer */
ly_err_free(eitem);
- } else {
- /* free all err */
- ly_err_free(first);
- pthread_setspecific(ctx->errlist_key, NULL);
}
}
@@ -336,64 +500,99 @@ ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t pat
}
}
+const struct lyd_node *
+ly_log_location_dnode(uint32_t idx)
+{
+ if (idx < log_location.dnodes.count) {
+ return log_location.dnodes.dnodes[idx];
+ }
+
+ return NULL;
+}
+
+uint32_t
+ly_log_location_dnode_count(void)
+{
+ return log_location.dnodes.count;
+}
+
+/**
+ * @brief Store generated error in a context.
+ *
+ * @param[in] ctx Context to use.
+ * @param[in] level Message log level.
+ * @param[in] no Error number.
+ * @param[in] vecode Error validation error code.
+ * @param[in] msg Error message, always spent.
+ * @param[in] path Error path, always spent.
+ * @param[in] apptag Error app tag, always spent.
+ * @return LY_ERR value.
+ */
static LY_ERR
log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
{
- struct ly_err_item *eitem, *last;
+ struct ly_ctx_err_rec *rec;
+ struct ly_err_item *e, *last;
assert(ctx && (level < LY_LLVRB));
- eitem = pthread_getspecific(ctx->errlist_key);
- if (!eitem) {
+ if (!(rec = ly_err_get_rec(ctx))) {
+ if (!(rec = ly_err_new_rec(ctx))) {
+ goto mem_fail;
+ }
+ }
+
+ e = rec->err;
+ if (!e) {
/* if we are only to fill in path, there must have been an error stored */
assert(msg);
- eitem = malloc(sizeof *eitem);
- LY_CHECK_GOTO(!eitem, mem_fail);
- eitem->prev = eitem;
- eitem->next = NULL;
+ e = malloc(sizeof *e);
+ LY_CHECK_GOTO(!e, mem_fail);
+ e->prev = e;
+ e->next = NULL;
- pthread_setspecific(ctx->errlist_key, eitem);
+ rec->err = e;
} else if (!msg) {
/* only filling the path */
assert(path);
/* find last error */
- eitem = eitem->prev;
+ e = e->prev;
do {
- if (eitem->level == LY_LLERR) {
+ if (e->level == LY_LLERR) {
/* fill the path */
- free(eitem->path);
- eitem->path = path;
+ free(e->path);
+ e->path = path;
return LY_SUCCESS;
}
- eitem = eitem->prev;
- } while (eitem->prev->next);
+ e = e->prev;
+ } while (e->prev->next);
/* last error was not found */
assert(0);
} else if ((temp_ly_log_opts && ((*temp_ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) ||
(!temp_ly_log_opts && ((ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE_LAST) == LY_LOSTORE_LAST))) {
/* overwrite last message */
- free(eitem->msg);
- free(eitem->path);
- free(eitem->apptag);
+ free(e->msg);
+ free(e->path);
+ free(e->apptag);
} else {
/* store new message */
- last = eitem->prev;
- eitem->prev = malloc(sizeof *eitem);
- LY_CHECK_GOTO(!eitem->prev, mem_fail);
- eitem = eitem->prev;
- eitem->prev = last;
- eitem->next = NULL;
- last->next = eitem;
+ last = e->prev;
+ e->prev = malloc(sizeof *e);
+ LY_CHECK_GOTO(!e->prev, mem_fail);
+ e = e->prev;
+ e->prev = last;
+ e->next = NULL;
+ last->next = e;
}
/* fill in the information */
- eitem->level = level;
- eitem->no = no;
- eitem->vecode = vecode;
- eitem->msg = msg;
- eitem->path = path;
- eitem->apptag = apptag;
+ e->level = level;
+ e->no = no;
+ e->vecode = vecode;
+ e->msg = msg;
+ e->path = path;
+ e->apptag = apptag;
return LY_SUCCESS;
mem_fail:
@@ -445,14 +644,19 @@ log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE v
return;
}
- /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
+ /* print into a single message */
+ if (vasprintf(&msg, format, args) == -1) {
+ LOGMEM(ctx);
+ free(path);
+ return;
+ }
+
+ /* store as the last message */
+ strncpy(last_msg, msg, LY_LAST_MSG_SIZE - 1);
+
+ /* store the error/warning in the context (if we need to store errors internally, it does not matter what are
+ * the user log options) */
if ((level < LY_LLVRB) && ctx && lostore) {
- assert(format);
- if (vasprintf(&msg, format, args) == -1) {
- LOGMEM(ctx);
- free(path);
- return;
- }
if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
/* assume we are inheriting the error, so inherit vecode as well */
vecode = ly_vecode(ctx);
@@ -462,11 +666,6 @@ log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE v
}
free_strs = 0;
} else {
- if (vasprintf(&msg, format, args) == -1) {
- LOGMEM(ctx);
- free(path);
- return;
- }
free_strs = 1;
}
@@ -524,6 +723,8 @@ ly_log_dbg(uint32_t group, const char *format, ...)
va_start(ap, format);
log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, NULL, dbg_format, ap);
va_end(ap);
+
+ free(dbg_format);
}
#endif
@@ -556,20 +757,20 @@ ly_vlog_build_path_append(char **str, const struct lysc_node *snode, const struc
if (snode->nodetype & (LYS_CHOICE | LYS_CASE)) {
/* schema-only node */
return LY_SUCCESS;
- } else if (lysc_data_parent(snode) != parent->schema) {
+ } else if (lysc_data_parent(snode) != lyd_node_schema(parent)) {
/* not a direct descendant node */
return LY_SUCCESS;
}
/* get module to print, if any */
mod = snode->module;
- prev_mod = (parent->schema) ? parent->schema->module : lyd_owner_module(parent);
+ prev_mod = lyd_node_module(parent);
if (prev_mod == mod) {
mod = NULL;
}
/* realloc string */
- len = strlen(*str);
+ len = *str ? strlen(*str) : 0;
new_len = len + 1 + (mod ? strlen(mod->name) + 1 : 0) + strlen(snode->name);
mem = realloc(*str, new_len + 1);
LY_CHECK_ERR_RET(!mem, LOGMEM(LYD_CTX(parent)), LY_EMEM);
@@ -580,6 +781,41 @@ ly_vlog_build_path_append(char **str, const struct lysc_node *snode, const struc
return LY_SUCCESS;
}
+LY_ERR
+ly_vlog_build_data_path(const struct ly_ctx *ctx, char **path)
+{
+ LY_ERR rc = LY_SUCCESS;
+ const struct lyd_node *dnode = NULL;
+
+ *path = NULL;
+
+ if (log_location.dnodes.count) {
+ dnode = log_location.dnodes.objs[log_location.dnodes.count - 1];
+ if (dnode->parent || !lysc_data_parent(dnode->schema)) {
+ /* data node with all of its parents */
+ *path = lyd_path(log_location.dnodes.objs[log_location.dnodes.count - 1], LYD_PATH_STD, NULL, 0);
+ LY_CHECK_ERR_GOTO(!*path, LOGMEM(ctx); rc = LY_EMEM, cleanup);
+ } else {
+ /* data parsers put all the parent nodes in the set, but they are not connected */
+ *path = lyd_path_set(&log_location.dnodes, LYD_PATH_STD);
+ LY_CHECK_ERR_GOTO(!*path, LOGMEM(ctx); rc = LY_EMEM, cleanup);
+ }
+ }
+
+ /* sometimes the last node is not created yet and we only have the schema node */
+ if (log_location.scnodes.count) {
+ rc = ly_vlog_build_path_append(path, log_location.scnodes.objs[log_location.scnodes.count - 1], dnode);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+
+cleanup:
+ if (rc) {
+ free(*path);
+ *path = NULL;
+ }
+ return rc;
+}
+
/**
* @brief Build log path from the stored log location information.
*
@@ -592,32 +828,17 @@ ly_vlog_build_path(const struct ly_ctx *ctx, char **path)
{
int r;
char *str = NULL, *prev = NULL;
- const struct lyd_node *dnode;
*path = NULL;
if (log_location.paths.count && ((const char *)(log_location.paths.objs[log_location.paths.count - 1]))[0]) {
/* simply get what is in the provided path string */
- *path = strdup((const char *)log_location.paths.objs[log_location.paths.count - 1]);
- LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
+ r = asprintf(path, "Path \"%s\"", (const char *)log_location.paths.objs[log_location.paths.count - 1]);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
} else {
/* data/schema node */
if (log_location.dnodes.count) {
- dnode = log_location.dnodes.objs[log_location.dnodes.count - 1];
- if (dnode->parent || !lysc_data_parent(dnode->schema)) {
- /* data node with all of its parents */
- str = lyd_path(log_location.dnodes.objs[log_location.dnodes.count - 1], LYD_PATH_STD, NULL, 0);
- LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
- } else {
- /* data parsers put all the parent nodes in the set, but they are not connected */
- str = lyd_path_set(&log_location.dnodes, LYD_PATH_STD);
- LY_CHECK_ERR_RET(!str, LOGMEM(ctx), LY_EMEM);
- }
-
- /* sometimes the last node is not created yet and we only have the schema node */
- if (log_location.scnodes.count) {
- ly_vlog_build_path_append(&str, log_location.scnodes.objs[log_location.scnodes.count - 1], dnode);
- }
+ LY_CHECK_RET(ly_vlog_build_data_path(ctx, &str));
r = asprintf(path, "Data location \"%s\"", str);
free(str);
@@ -630,28 +851,28 @@ ly_vlog_build_path(const struct ly_ctx *ctx, char **path)
free(str);
LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
}
+ }
- /* line */
- prev = *path;
- if (log_location.line) {
- r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", log_location.line);
- free(prev);
- LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
+ /* line */
+ prev = *path;
+ if (log_location.line) {
+ r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L", log_location.line);
+ free(prev);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
- log_location.line = 0;
- } else if (log_location.inputs.count) {
- r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L",
- ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line);
- free(prev);
- LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
- }
+ log_location.line = 0;
+ } else if (log_location.inputs.count) {
+ r = asprintf(path, "%s%sine number %" PRIu64, prev ? prev : "", prev ? ", l" : "L",
+ ((struct ly_in *)log_location.inputs.objs[log_location.inputs.count - 1])->line);
+ free(prev);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
+ }
- if (*path) {
- prev = *path;
- r = asprintf(path, "%s.", prev);
- free(prev);
- LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
- }
+ if (*path) {
+ prev = *path;
+ r = asprintf(path, "%s.", prev);
+ free(prev);
+ LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
}
return LY_SUCCESS;
@@ -680,12 +901,12 @@ ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char
* @param[in] plugin_name Name of the plugin generating the message.
* @param[in] level Log message level (error, warning, etc.)
* @param[in] err_no Error type code.
- * @param[in] path Optional path of the error.
+ * @param[in] path Optional path of the error, used if set.
* @param[in] format Format string to print.
* @param[in] ap Var arg list for @p format.
*/
static void
-ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level, LY_ERR err_no, const char *path,
+ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level, LY_ERR err_no, char *path,
const char *format, va_list ap)
{
char *plugin_msg;
@@ -698,8 +919,7 @@ ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level
return;
}
- log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path ? strdup(path) : NULL, NULL,
- plugin_msg, ap);
+ log_vprintf(ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path, NULL, plugin_msg, ap);
free(plugin_msg);
}
@@ -717,8 +937,6 @@ lyplg_ext_parse_log(const struct lysp_ctx *pctx, const struct lysp_ext_instance
va_start(ap, format);
ly_ext_log(PARSER_CTX(pctx), ext->record->plugin.id, level, err_no, path, format, ap);
va_end(ap);
-
- free(path);
}
LIBYANG_API_DEF void
@@ -726,9 +944,15 @@ lyplg_ext_compile_log(const struct lysc_ctx *cctx, const struct lysc_ext_instanc
const char *format, ...)
{
va_list ap;
+ char *path = NULL;
+
+ if (cctx && (asprintf(&path, "Path \"%s\".", cctx->path) == -1)) {
+ LOGMEM(cctx->ctx);
+ return;
+ }
va_start(ap, format);
- ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, cctx ? cctx->path : NULL, format, ap);
+ ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, path, format, ap);
va_end(ap);
}
@@ -737,12 +961,37 @@ lyplg_ext_compile_log_path(const char *path, const struct lysc_ext_instance *ext
const char *format, ...)
{
va_list ap;
+ char *log_path = NULL;
+
+ if (path && (asprintf(&log_path, "Path \"%s\".", path) == -1)) {
+ LOGMEM(ext->module->ctx);
+ return;
+ }
va_start(ap, format);
- ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, path, format, ap);
+ ly_ext_log(ext->module->ctx, ext->def->plugin->id, level, err_no, log_path, format, ap);
+ va_end(ap);
+}
+
+/**
+ * @brief Serves only for creating ap.
+ */
+static void
+_lyplg_ext_compile_log_err(const struct ly_err_item *err, const struct lysc_ext_instance *ext, ...)
+{
+ va_list ap;
+
+ va_start(ap, ext);
+ ly_ext_log(ext->module->ctx, ext->def->plugin->id, err->level, err->no, err->path ? strdup(err->path) : NULL, "%s", ap);
va_end(ap);
}
+LIBYANG_API_DEF void
+lyplg_ext_compile_log_err(const struct ly_err_item *err, const struct lysc_ext_instance *ext)
+{
+ _lyplg_ext_compile_log_err(err, ext, err->msg);
+}
+
/**
* @brief Exact same functionality as ::ly_err_print() but has variable arguments so log_vprintf() can be called.
*/
diff --git a/src/log.h b/src/log.h
index 2144668..c6c1851 100644
--- a/src/log.h
+++ b/src/log.h
@@ -141,8 +141,6 @@ LIBYANG_API_DECL uint32_t ly_log_options(uint32_t opts);
*/
LIBYANG_API_DECL void ly_temp_log_options(uint32_t *opts);
-#ifndef NDEBUG
-
/**
* @ingroup log
* @defgroup dbggroup Debug messages groups
@@ -166,13 +164,12 @@ LIBYANG_API_DECL void ly_temp_log_options(uint32_t *opts);
* @brief Enable specific debugging messages (independent of log level).
*
* To get the current value, the function must be called twice resetting the level by the received value.
+ * Note: does not have any effect on non-debug (Release) builds
*
* @param[in] dbg_groups Bitfield of enabled debug message groups (see @ref dbggroup).
* @return Previous options bitfield.
*/
-uint32_t ly_log_dbg_groups(uint32_t dbg_groups);
-
-#endif
+LIBYANG_API_DECL uint32_t ly_log_dbg_groups(uint32_t dbg_groups);
/**
* @brief Logger callback.
@@ -294,17 +291,33 @@ typedef enum {
* @brief Libyang full error structure.
*/
struct ly_err_item {
- LY_LOG_LEVEL level;
- LY_ERR no;
- LY_VECODE vecode;
- char *msg;
- char *path;
- char *apptag;
- struct ly_err_item *next;
- struct ly_err_item *prev; /* first item's prev points to the last item */
+ LY_LOG_LEVEL level; /**< error (message) log level */
+ LY_ERR no; /**< error code */
+ LY_VECODE vecode; /**< validation error code, if any */
+ char *msg; /**< error message */
+ char *path; /**< error path that caused the error, if any */
+ char *apptag; /**< error-app-tag, if any */
+ struct ly_err_item *next; /**< next error item */
+ struct ly_err_item *prev; /**< previous error item, points to the last item for the ifrst item */
};
/**
+ * @brief Get the last (thread, context-specific) error code.
+ *
+ * @param[in] ctx Relative context.
+ * @return LY_ERR value of the last error code.
+ */
+LIBYANG_API_DECL LY_ERR ly_errcode(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get human-readable error message for an error code.
+ *
+ * @param[in] err Error code.
+ * @return String error message.
+ */
+LIBYANG_API_DECL const char *ly_strerrcode(LY_ERR err);
+
+/**
* @brief Get the last (thread, context-specific) validation error code.
*
* This value is set only if ly_errno is #LY_EVALID.
@@ -315,12 +328,12 @@ struct ly_err_item {
LIBYANG_API_DECL LY_VECODE ly_vecode(const struct ly_ctx *ctx);
/**
- * @brief Get the last (thread, context-specific) error code.
+ * @brief Get human-readable error message for a validation error code.
*
- * @param[in] ctx Relative context.
- * @return LY_ERR value of the last error code.
+ * @param[in] vecode Validation error code.
+ * @return String error message.
*/
-LIBYANG_API_DECL LY_ERR ly_errcode(const struct ly_ctx *ctx);
+LIBYANG_API_DECL const char *ly_strvecode(LY_VECODE vecode);
/**
* @brief Get the last (thread, context-specific) error message. If the coresponding module defined
@@ -335,6 +348,16 @@ LIBYANG_API_DECL LY_ERR ly_errcode(const struct ly_ctx *ctx);
LIBYANG_API_DECL const char *ly_errmsg(const struct ly_ctx *ctx);
/**
+ * @brief Get the last (thread-specific) error message.
+ *
+ * ::ly_errmsg() should be used instead of this function but this one is useful for getting
+ * errors from functions that do not have any context accessible. Or as a simple unified logging API.
+ *
+ * @return Last generated error message.
+ */
+LIBYANG_API_DECL const char *ly_last_errmsg(void);
+
+/**
* @brief Get the last (thread, context-specific) path of the element where was an error.
*
* The path always corresponds to the error message available via ::ly_errmsg(), so
diff --git a/src/lyb.c b/src/lyb.c
index 87806c8..59d379c 100644
--- a/src/lyb.c
+++ b/src/lyb.c
@@ -39,8 +39,8 @@ lyb_generate_hash(const struct lysc_node *node, uint8_t collision_id)
LYB_HASH hash;
/* generate full hash */
- full_hash = dict_hash_multi(0, mod->name, strlen(mod->name));
- full_hash = dict_hash_multi(full_hash, node->name, strlen(node->name));
+ full_hash = lyht_hash_multi(0, mod->name, strlen(mod->name));
+ full_hash = lyht_hash_multi(full_hash, node->name, strlen(node->name));
if (collision_id) {
size_t ext_len;
@@ -51,9 +51,9 @@ lyb_generate_hash(const struct lysc_node *node, uint8_t collision_id)
/* use one more byte from the module name than before */
ext_len = collision_id;
}
- full_hash = dict_hash_multi(full_hash, mod->name, ext_len);
+ full_hash = lyht_hash_multi(full_hash, mod->name, ext_len);
}
- full_hash = dict_hash_multi(full_hash, NULL, 0);
+ full_hash = lyht_hash_multi(full_hash, NULL, 0);
/* use the shortened hash */
hash = full_hash & (LYB_HASH_MASK >> collision_id);
diff --git a/src/lyb.h b/src/lyb.h
index b123ee5..70de5b7 100644
--- a/src/lyb.h
+++ b/src/lyb.h
@@ -95,7 +95,7 @@ struct lylyb_ctx {
/* LYB printer only */
struct lyd_lyb_sib_ht {
struct lysc_node *first_sibling;
- struct hash_table *ht;
+ struct ly_ht *ht;
} *sib_hts;
};
diff --git a/src/out.c b/src/out.c
index 5567387..1e66ed9 100644
--- a/src/out.c
+++ b/src/out.c
@@ -417,7 +417,7 @@ ly_vprint_(struct ly_out *out, const char *format, va_list ap)
{
LY_ERR ret;
int written = 0;
- char *msg = NULL, *aux;
+ char *msg = NULL;
switch (out->type) {
case LY_OUT_FD:
@@ -433,15 +433,13 @@ ly_vprint_(struct ly_out *out, const char *format, va_list ap)
break;
}
if (out->method.mem.len + written + 1 > out->method.mem.size) {
- aux = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
- if (!aux) {
- out->method.mem.buf = NULL;
+ *out->method.mem.buf = ly_realloc(*out->method.mem.buf, out->method.mem.len + written + 1);
+ if (!*out->method.mem.buf) {
out->method.mem.len = 0;
out->method.mem.size = 0;
LOGMEM(NULL);
return LY_EMEM;
}
- *out->method.mem.buf = aux;
out->method.mem.size = out->method.mem.len + written + 1;
}
if (written) {
@@ -630,9 +628,9 @@ repeat:
}
LOGERR(NULL, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
written = 0;
- } else if ((size_t)written != len) {
- LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__,
- len - (size_t)written, len);
+ } else if (written != len) {
+ LOGERR(NULL, LY_ESYS, "%s: writing data failed (unable to write %" PRIu32 " from %" PRIu32 " data).", __func__,
+ (uint32_t)(len - written), (uint32_t)len);
ret = LY_ESYS;
} else {
if (out->type == LY_OUT_FDSTREAM) {
diff --git a/src/parser_common.c b/src/parser_common.c
index 6fe068b..3215275 100644
--- a/src/parser_common.c
+++ b/src/parser_common.c
@@ -46,6 +46,7 @@
#include "parser_data.h"
#include "path.h"
#include "plugins_exts/metadata.h"
+#include "schema_compile_node.h"
#include "schema_features.h"
#include "set.h"
#include "tree.h"
@@ -65,6 +66,50 @@ lyd_ctx_free(struct lyd_ctx *lydctx)
}
LY_ERR
+lyd_parser_notif_eventtime_validate(const struct lyd_node *node)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct ly_ctx *ctx = (struct ly_ctx *)LYD_CTX(node);
+ struct lysc_ctx cctx;
+ const struct lys_module *mod;
+ LY_ARRAY_COUNT_TYPE u;
+ struct ly_err_item *err = NULL;
+ struct lysp_type *type_p = NULL;
+ struct lysc_pattern **patterns = NULL;
+ const char *value;
+
+ LYSC_CTX_INIT_CTX(cctx, ctx);
+
+ /* get date-and-time parsed type */
+ mod = ly_ctx_get_module_latest(ctx, "ietf-yang-types");
+ assert(mod);
+ LY_ARRAY_FOR(mod->parsed->typedefs, u) {
+ if (!strcmp(mod->parsed->typedefs[u].name, "date-and-time")) {
+ type_p = &mod->parsed->typedefs[u].type;
+ break;
+ }
+ }
+ assert(type_p);
+
+ /* compile patterns */
+ assert(type_p->patterns);
+ LY_CHECK_GOTO(rc = lys_compile_type_patterns(&cctx, type_p->patterns, NULL, &patterns), cleanup);
+
+ /* validate */
+ value = lyd_get_value(node);
+ rc = lyplg_type_validate_patterns(patterns, value, strlen(value), &err);
+
+cleanup:
+ FREE_ARRAY(&cctx.free_ctx, patterns, lysc_pattern_free);
+ if (rc && err) {
+ LOGVAL_ERRITEM(ctx, err);
+ ly_err_free(err);
+ LOGVAL(ctx, LYVE_DATA, "Invalid \"eventTime\" in the notification.");
+ }
+ return rc;
+}
+
+LY_ERR
lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op)
{
const struct lyd_node *iter;
@@ -112,6 +157,63 @@ lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, stru
return LY_SUCCESS;
}
+const struct lysc_node *
+lyd_parser_node_schema(const struct lyd_node *node)
+{
+ uint32_t i;
+ const struct lyd_node *iter;
+ const struct lysc_node *schema = NULL;
+ const struct lys_module *mod;
+
+ if (!node) {
+ return NULL;
+ } else if (node->schema) {
+ /* simplest case */
+ return node->schema;
+ }
+
+ /* find the first schema node in the parsed nodes */
+ i = ly_log_location_dnode_count();
+ if (i) {
+ do {
+ --i;
+ if (ly_log_location_dnode(i)->schema) {
+ /* this node is processed */
+ schema = ly_log_location_dnode(i)->schema;
+ ++i;
+ break;
+ }
+ } while (i);
+ }
+
+ /* get schema node of an opaque node */
+ do {
+ /* get next data node */
+ if (i == ly_log_location_dnode_count()) {
+ iter = node;
+ } else {
+ iter = ly_log_location_dnode(i);
+ }
+ assert(!iter->schema);
+
+ /* get module */
+ mod = lyd_node_module(iter);
+ if (!mod) {
+ /* unknown module, no schema node */
+ schema = NULL;
+ break;
+ }
+
+ /* get schema node */
+ schema = lys_find_child(schema, mod, LYD_NAME(iter), 0, 0, 0);
+
+ /* move to the descendant */
+ ++i;
+ } while (schema && (iter != node));
+
+ return schema;
+}
+
LY_ERR
lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode)
{
@@ -180,9 +282,16 @@ LY_ERR
lyd_parser_create_term(struct lyd_ctx *lydctx, const struct lysc_node *schema, const void *value, size_t value_len,
ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, struct lyd_node **node)
{
+ LY_ERR r;
ly_bool incomplete;
- LY_CHECK_RET(lyd_create_term(schema, value, value_len, dynamic, format, prefix_data, hints, &incomplete, node));
+ if ((r = lyd_create_term(schema, value, value_len, 1, dynamic, format, prefix_data, hints, &incomplete, node))) {
+ if (lydctx->data_ctx->ctx != schema->module->ctx) {
+ /* move errors to the main context */
+ ly_err_move(schema->module->ctx, (struct ly_ctx *)lydctx->data_ctx->ctx);
+ }
+ return r;
+ }
if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
LY_CHECK_RET(ly_set_add(&lydctx->node_types, *node, 1, NULL));
@@ -195,6 +304,8 @@ lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct l
const char *name, size_t name_len, const void *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node)
{
+ LY_ERR rc = LY_SUCCESS;
+ char *dpath = NULL, *path = NULL;
ly_bool incomplete;
struct lyd_meta *first = NULL;
@@ -203,11 +314,20 @@ lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct l
first = *meta;
}
- LY_CHECK_RET(lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, format, prefix_data,
- hints, ctx_node, 0, &incomplete));
+ /* generate path to the metadata */
+ LY_CHECK_RET(ly_vlog_build_data_path(lydctx->data_ctx->ctx, &dpath));
+ if (asprintf(&path, "%s/@%s:%.*s", dpath, mod->name, (int)name_len, name) == -1) {
+ LOGMEM(lydctx->data_ctx->ctx);
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ LOG_LOCSET(NULL, NULL, path, NULL);
+
+ LY_CHECK_GOTO(rc = lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, 1, dynamic, format,
+ prefix_data, hints, ctx_node, 0, &incomplete), cleanup);
if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
- LY_CHECK_RET(ly_set_add(&lydctx->meta_types, *meta, 1, NULL));
+ LY_CHECK_GOTO(rc = ly_set_add(&lydctx->meta_types, *meta, 1, NULL), cleanup);
}
if (first) {
@@ -215,7 +335,11 @@ lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct l
*meta = first;
}
- return LY_SUCCESS;
+cleanup:
+ LOG_LOCBACK(0, 0, 1, 0);
+ free(dpath);
+ free(path);
+ return rc;
}
LY_ERR
@@ -387,6 +511,16 @@ lysp_stmt_validate_value(struct lysp_ctx *ctx, enum yang_arg val_type, const cha
uint32_t c;
size_t utf8_char_len;
+ if (!val) {
+ if (val_type == Y_MAYBE_STR_ARG) {
+ /* fine */
+ return LY_SUCCESS;
+ }
+
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Missing an expected string.");
+ return LY_EVALID;
+ }
+
while (*val) {
LY_CHECK_ERR_RET(ly_getutf8(&val, &c, &utf8_char_len),
LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, (val)[-utf8_char_len]), LY_EVALID);
@@ -605,7 +739,7 @@ lysp_stmt_text_fields(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, const
static LY_ERR
lysp_stmt_status(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
if (*flags & LYS_STATUS_MASK) {
LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "status");
@@ -698,7 +832,7 @@ lysp_stmt_when(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, struct lysp_w
static LY_ERR
lysp_stmt_config(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
if (*flags & LYS_CONFIG_MASK) {
LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "config");
@@ -744,7 +878,7 @@ static LY_ERR
lysp_stmt_mandatory(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
if (*flags & LYS_MAND_MASK) {
LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "mandatory");
@@ -914,7 +1048,7 @@ static LY_ERR
lysp_stmt_type_enum_value_pos(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, int64_t *value, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
char *ptr = NULL;
long long num = 0;
unsigned long long unum = 0;
@@ -949,7 +1083,7 @@ lysp_stmt_type_enum_value_pos(struct lysp_ctx *ctx, const struct lysp_stmt *stmt
}
}
/* we have not parsed the whole argument */
- if ((size_t)(ptr - stmt->arg) != arg_len) {
+ if (ptr - stmt->arg != arg_len) {
LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, lyplg_ext_stmt2str(stmt->kw));
goto error;
}
@@ -1056,7 +1190,7 @@ lysp_stmt_type_fracdigits(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, ui
struct lysp_ext_instance **exts)
{
char *ptr;
- size_t arg_len;
+ int arg_len;
unsigned long long num;
if (*fracdig) {
@@ -1074,7 +1208,7 @@ lysp_stmt_type_fracdigits(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, ui
errno = 0;
num = strtoull(stmt->arg, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
- if ((size_t)(ptr - stmt->arg) != arg_len) {
+ if (ptr - stmt->arg != arg_len) {
LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "fraction-digits");
return LY_EVALID;
}
@@ -1112,7 +1246,7 @@ static LY_ERR
lysp_stmt_type_reqinstance(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint8_t *reqinst, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
if (*flags & LYS_SET_REQINST) {
LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "require-instance");
@@ -1155,7 +1289,7 @@ static LY_ERR
lysp_stmt_type_pattern_modifier(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, const char **pat,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
char *buf;
if ((*pat)[0] == LYSP_RESTR_PATTERN_NACK) {
@@ -1338,7 +1472,7 @@ lysp_stmt_yangver(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint8_t *v
} else if (!strcmp(stmt->arg, "1.1")) {
*version = LYS_VERSION_1_1;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, strlen(stmt->arg), stmt->arg, "yang-version");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)strlen(stmt->arg), stmt->arg, "yang-version");
return LY_EVALID;
}
@@ -1418,7 +1552,7 @@ lysp_stmt_yinelem(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *
} else if (!strcmp(stmt->arg, "false")) {
*flags |= LYS_YINELEM_FALSE;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, strlen(stmt->arg), stmt->arg, "yin-element");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)strlen(stmt->arg), stmt->arg, "yin-element");
return LY_EVALID;
}
@@ -1946,7 +2080,7 @@ static LY_ERR
lysp_stmt_maxelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32_t *max, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
char *ptr;
unsigned long long num;
@@ -1969,7 +2103,7 @@ lysp_stmt_maxelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32
errno = 0;
num = strtoull(stmt->arg, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
- if ((size_t)(ptr - stmt->arg) != arg_len) {
+ if (ptr - stmt->arg != arg_len) {
LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "max-elements");
return LY_EVALID;
}
@@ -2012,7 +2146,7 @@ static LY_ERR
lysp_stmt_minelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32_t *min, uint16_t *flags,
struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
char *ptr;
unsigned long long num;
@@ -2034,7 +2168,7 @@ lysp_stmt_minelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32
errno = 0;
num = strtoull(stmt->arg, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
- if ((size_t)(ptr - stmt->arg) != arg_len) {
+ if (ptr - stmt->arg != arg_len) {
LOGVAL_PARSER(ctx, LY_VCODE_INVAL, arg_len, stmt->arg, "min-elements");
return LY_EVALID;
}
@@ -2070,7 +2204,7 @@ lysp_stmt_minelements(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint32
static LY_ERR
lysp_stmt_orderedby(struct lysp_ctx *ctx, const struct lysp_stmt *stmt, uint16_t *flags, struct lysp_ext_instance **exts)
{
- size_t arg_len;
+ int arg_len;
if (*flags & LYS_ORDBY_MASK) {
LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "ordered-by");
diff --git a/src/parser_data.h b/src/parser_data.h
index 050ced3..feedb14 100644
--- a/src/parser_data.h
+++ b/src/parser_data.h
@@ -1,9 +1,10 @@
/**
* @file parser_data.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Data parsers for libyang
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -156,9 +157,14 @@ struct ly_in;
modified manually. If this flag is used incorrectly (for unordered data),
the behavior is undefined and most functions executed with these
data will not work correctly. */
-#define LYD_PARSE_SUBTREE 0x400000 /**< Parse only the current data subtree with any descendants, no siblings.
+#define LYD_PARSE_SUBTREE 0x400000 /**< Parse only the first child item along with any descendants, but no
+ siblings. This flag is not required when parsing data which do not
+ start at the schema root; for that purpose, use lyd_parse_data's parent
+ argument.
Also, a new return value ::LY_ENOT is returned if there is a sibling
- subtree following in the input data. */
+ subtree following in the input data. Note that if validation is requested,
+ only the newly parsed subtree is validated. This might result in
+ an invalid datastore content. */
#define LYD_PARSE_WHEN_TRUE 0x800000 /**< Mark all the parsed nodes dependend on a when condition with the flag
that means the condition was satisifed before. This allows for
auto-deletion of these nodes during validation. */
@@ -196,6 +202,15 @@ struct ly_in;
#define LYD_VALIDATE_NO_STATE 0x0001 /**< Consider state data not allowed and raise an error if they are found.
Also, no implicit state data are added. */
#define LYD_VALIDATE_PRESENT 0x0002 /**< Validate only modules whose data actually exist. */
+#define LYD_VALIDATE_MULTI_ERROR 0x0004 /**< Do not stop validation on the first error but generate all the detected errors. */
+#define LYD_VALIDATE_OPERATIONAL 0x0008 /**< Semantic constraint violations are reported only as warnings instead of
+ errors (see [RFC 8342 sec. 5.3](https://datatracker.ietf.org/doc/html/rfc8342#section-5.3)). */
+#define LYD_VALIDATE_NO_DEFAULTS 0x0010 /**< Do not add any default nodes during validation, other implicit nodes
+ (such as NP containers) are still added. Validation will fail if a
+ default node is required for it to pass. */
+#define LYD_VALIDATE_NOT_FINAL 0x0020 /**< Skip final validation tasks that require for all the data nodes to
+ either exist or not, based on the YANG constraints. Once the data
+ satisfy this requirement, the final validation should be performed. */
#define LYD_VALIDATE_OPTS_MASK 0x0000FFFF /**< Mask for all the LYD_VALIDATE_* options. */
@@ -205,7 +220,8 @@ struct ly_in;
* @brief Parse (and validate) data from the input handler as a YANG data tree.
*
* @param[in] ctx Context to connect with the tree being built here.
- * @param[in] parent Optional parent to connect the parsed nodes to.
+ * @param[in] parent Optional parent to connect the parsed nodes to. If provided, the data are expected to describe
+ * a subtree of the YANG model instead of starting at the schema root.
* @param[in] in The input handle to provide the dumped data in the specified @p format to parse (and validate).
* @param[in] format Format of the input data to be parsed. Can be 0 to try to detect format from the input handler.
* @param[in] parse_options Options for parser, see @ref dataparseroptions.
@@ -213,6 +229,11 @@ struct ly_in;
* @param[out] tree Full parsed data tree, note that NULL can be a valid tree. If @p parent is set, set to NULL.
* @return LY_SUCCESS in case of successful parsing (and validation).
* @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
+ *
+ * When parsing subtrees (i.e., when @p parent is non-NULL), validation is only performed on the newly parsed data.
+ * This might result in allowing invalid datastore content when the schema contains cross-branch constraints,
+ * complicated `must` statements, etc. When a full-datastore validation is desirable, parse all subtrees
+ * first, and then request validation of the complete datastore content.
*/
LIBYANG_API_DECL LY_ERR lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree);
@@ -303,26 +324,35 @@ LIBYANG_API_DECL LY_ERR lyd_parse_ext_data(const struct lysc_ext_instance *ext,
* @{
*/
enum lyd_type {
- LYD_TYPE_DATA_YANG = 0, /* generic YANG instance data */
- LYD_TYPE_RPC_YANG, /* instance of a YANG RPC/action request with only "input" data children,
- including all parents in case of an action */
- LYD_TYPE_NOTIF_YANG, /* instance of a YANG notification, including all parents in case of a nested one */
- LYD_TYPE_REPLY_YANG, /* instance of a YANG RPC/action reply with only "output" data children,
- including all parents in case of an action */
+ LYD_TYPE_DATA_YANG = 0, /* generic YANG instance data */
+ LYD_TYPE_RPC_YANG, /* instance of a YANG RPC/action request with only "input" data children,
+ including all parents and optional top-level "action" element in case of an action */
+ LYD_TYPE_NOTIF_YANG, /* instance of a YANG notification, including all parents in case of a nested one */
+ LYD_TYPE_REPLY_YANG, /* instance of a YANG RPC/action reply with only "output" data children,
+ including all parents in case of an action */
+
+ LYD_TYPE_RPC_NETCONF, /* complete NETCONF RPC invocation as defined for
+ [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+ [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+ LYD_TYPE_NOTIF_NETCONF, /* complete NETCONF notification message as defined for
+ [notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
+ LYD_TYPE_REPLY_NETCONF, /* complete NETCONF RPC reply as defined for
+ [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+ [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
- LYD_TYPE_RPC_NETCONF, /* complete NETCONF RPC invocation as defined for
- [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
- [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
- LYD_TYPE_NOTIF_NETCONF, /* complete NETCONF notification message as defined for
- [notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
- LYD_TYPE_REPLY_NETCONF /* complete NETCONF RPC reply as defined for
- [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
- [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+ LYD_TYPE_RPC_RESTCONF, /* message-body of a RESTCONF operation input parameters
+ ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.1)) */
+ LYD_TYPE_NOTIF_RESTCONF, /* RESTCONF JSON notification data
+ ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-6.4)), to parse
+ a notification in XML, use ::LYD_TYPE_NOTIF_NETCONF */
+ LYD_TYPE_REPLY_RESTCONF /* message-body of a RESTCONF operation output parameters
+ ([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.2)) */
};
/** @} datatype */
/**
- * @brief Parse YANG data into an operation data tree.
+ * @brief Parse YANG data into an operation data tree. Specific parsing flags ::LYD_PARSE_ONLY, ::LYD_PARSE_STRICT and
+ * no validation flags are used.
*
* At least one of @p parent, @p tree, or @p op must always be set.
*
@@ -349,6 +379,28 @@ enum lyd_type {
* - @p op - must be NULL, the reply is appended to the RPC;
* Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
*
+ * - ::LYD_TYPE_RPC_RESTCONF:
+ * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ * - @p format - can be both ::LYD_JSON and ::LYD_XML;
+ * - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be NULL, @p parent points to the operation;
+ *
+ * - ::LYD_TYPE_NOTIF_RESTCONF:
+ * - @p parent - must be NULL, the whole notification is expected;
+ * - @p format - must be ::LYD_JSON, XML-formatted notifications are parsed using ::LYD_TYPE_NOTIF_NETCONF;
+ * - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_REPLY_RESTCONF:
+ * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ * - @p format - can be both ::LYD_JSON and ::LYD_XML;
+ * - @p tree - must be provided, all the RESTCONF-specific JSON objects will be returned here as
+ * a separate opaque data tree, even if the function fails, this may be returned;
+ * - @p op - must be NULL, @p parent points to the operation;
+ * Note that error reply should be parsed as 'yang-data' extension data.
+ *
* @param[in] ctx libyang context.
* @param[in] parent Optional parent to connect the parsed nodes to.
* @param[in] in Input handle to read the input from.
@@ -427,6 +479,10 @@ LIBYANG_API_DECL LY_ERR lyd_validate_all(struct lyd_node **tree, const struct ly
* The data tree is modified in-place. As a result of the validation, some data might be removed
* from the tree. In that case, the removed items are freed, not just unlinked.
*
+ * If several modules need to be validated, the flag ::LYD_VALIDATE_NOT_FINAL should be used first for validation
+ * of each module and then ::lyd_validate_module_final() should be called also for each module. Otherwise,
+ * false-positive validation errors for foreign dependencies may occur.
+ *
* @param[in,out] tree Data tree to recursively validate. May be changed by validation, might become NULL.
* @param[in] module Module whose data (and schema restrictions) to validate.
* @param[in] val_opts Validation options (@ref datavalidationoptions).
@@ -438,6 +494,20 @@ LIBYANG_API_DECL LY_ERR lyd_validate_module(struct lyd_node **tree, const struct
struct lyd_node **diff);
/**
+ * @brief Finish validation of a module data that have previously been validated with ::LYD_VALIDATE_NOT_FINAL flag.
+ *
+ * This final validation will not add or remove any nodes.
+ *
+ * @param[in] tree Data tree to recursively validate.
+ * @param[in] module Module whose data (and schema restrictions) to validate.
+ * @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_validate_module_final(struct lyd_node *tree, const struct lys_module *module,
+ uint32_t val_opts);
+
+/**
* @brief Validate an RPC/action request, reply, or notification. Only the operation data tree (input/output/notif)
* is validate, any parents are ignored.
*
diff --git a/src/parser_internal.h b/src/parser_internal.h
index 458d297..92412e2 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -1,9 +1,10 @@
/**
* @file parser_internal.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Internal structures and functions for libyang parsers
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -27,6 +28,23 @@ struct lysp_yin_ctx;
struct lysp_ctx;
/**
+ * @brief Check data parser error taking into account multi-error validation.
+ *
+ * @param[in] r Local return value.
+ * @param[in] err_cmd Command to perform on any error.
+ * @param[in] lydctx Data parser context.
+ * @param[in] label Label to go to on fatal error.
+ */
+#define LY_DPARSER_ERR_GOTO(r, err_cmd, lydctx, label) \
+ if (r) { \
+ err_cmd; \
+ if ((r != LY_EVALID) || !lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || \
+ (ly_vecode(((struct lyd_ctx *)lydctx)->data_ctx->ctx) == LYVE_SYNTAX)) { \
+ goto label; \
+ } \
+ }
+
+/**
* @brief Callback for ::lyd_ctx to free the structure
*
* @param[in] ctx Data parser context to free.
@@ -43,6 +61,7 @@ typedef void (*lyd_ctx_free_clb)(struct lyd_ctx *ctx);
#define LYD_INTOPT_ANY 0x10 /**< Anydata/anyxml content is being parsed, there can be anything. */
#define LYD_INTOPT_WITH_SIBLINGS 0x20 /**< Parse the whole input with any siblings. */
#define LYD_INTOPT_NO_SIBLINGS 0x40 /**< If there are any siblings, return an error. */
+#define LYD_INTOPT_EVENTTIME 0x80 /**< Parse notification eventTime node. */
/**
* @brief Internal (common) context for YANG data parsers.
@@ -118,7 +137,8 @@ struct lyd_json_ctx {
/* callbacks */
lyd_ctx_free_clb free;
- struct lyjson_ctx *jsonctx; /**< JSON context */
+ struct lyjson_ctx *jsonctx; /**< JSON context */
+ const struct lysc_node *any_schema; /**< parent anyxml/anydata schema node if parsing nested data tree */
};
/**
@@ -279,6 +299,27 @@ LY_ERR lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
/**
+ * @brief Parse JSON string as a RESTCONF message.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input structure.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] data_type Expected RESTCONF data type of the data.
+ * @param[out] envp Individual parsed envelopes tree, may be returned possibly even on an error.
+ * @param[out] parsed Set to add all the parsed siblings into.
+ * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in.
+ * @param[out] lydctx_p Data parser context to finish validation.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type,
+ struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p);
+
+/**
* @brief Parse binary LYB data as a YANG data tree.
*
* @param[in] ctx libyang context.
@@ -299,6 +340,15 @@ LY_ERR lyd_parse_lyb(const struct ly_ctx *ctx, const struct lysc_ext_instance *e
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p);
/**
+ * @brief Validate eventTime date-and-time value.
+ *
+ * @param[in] node Opaque eventTime node.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LY_ERR lyd_parser_notif_eventtime_validate(const struct lyd_node *node);
+
+/**
* @brief Search all the parents for an operation node, check validity based on internal parser flags.
*
* @param[in] parent Parent to connect the parsed nodes to.
@@ -309,6 +359,14 @@ LY_ERR lyd_parse_lyb(const struct ly_ctx *ctx, const struct lysc_ext_instance *e
LY_ERR lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op);
/**
+ * @brief Get schema node of a node being parsed, use nodes stored for logging.
+ *
+ * @param[in] node Node whose schema node to get.
+ * @return Schema node even for an opaque node, NULL if none found.
+ */
+const struct lysc_node *lyd_parser_node_schema(const struct lyd_node *node);
+
+/**
* @brief Check that a data node representing the @p snode is suitable based on options.
*
* @param[in] lydctx Common data parsers context.
diff --git a/src/parser_json.c b/src/parser_json.c
index 6219c6e..3655d4c 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief JSON data parser for libyang
*
- * Copyright (c) 2020 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -161,29 +161,32 @@ lydjson_get_node_prefix(struct lyd_node *node, const char *local_prefix, size_t
return LY_SUCCESS;
}
- *prefix_p = NULL;
while (node) {
if (node->schema) {
- *prefix_p = node->schema->module->name;
+ module_name = node->schema->module->name;
break;
}
onode = (struct lyd_node_opaq *)node;
if (onode->name.module_name) {
- *prefix_p = onode->name.module_name;
+ module_name = onode->name.module_name;
break;
} else if (onode->name.prefix) {
- *prefix_p = onode->name.prefix;
+ module_name = onode->name.prefix;
break;
}
node = lyd_parent(node);
}
- *prefix_len_p = ly_strlen(module_name);
+ *prefix_p = module_name;
+ *prefix_len_p = ly_strlen(module_name);
return LY_SUCCESS;
}
/**
- * @brief Skip the current JSON object/array.
+ * @brief Skip the current JSON item (based on status).
+ *
+ * The JSON context is moved to the last "status" of the JSON item so to completely
+ * finish the skip, one more JSON context move is required.
*
* @param[in] jsonctx JSON context with the input data to skip.
* @return LY_ERR value.
@@ -192,34 +195,39 @@ static LY_ERR
lydjson_data_skip(struct lyjson_ctx *jsonctx)
{
enum LYJSON_PARSER_STATUS status, current;
- uint32_t orig_depth;
+ uint32_t depth;
- status = lyjson_ctx_status(jsonctx, 0);
- assert((status == LYJSON_OBJECT) || (status == LYJSON_ARRAY));
- orig_depth = jsonctx->depth;
+ status = lyjson_ctx_status(jsonctx);
+ depth = lyjson_ctx_depth(jsonctx);
- /* next */
- LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
-
- if ((status == LYJSON_OBJECT) && (current != LYJSON_OBJECT) && (current != LYJSON_ARRAY)) {
- /* no nested objects */
- LY_CHECK_RET(lyjson_ctx_next(jsonctx, NULL));
- return LY_SUCCESS;
- }
+ switch (status) {
+ case LYJSON_OBJECT:
+ ++depth;
- /* skip after the content */
- while ((jsonctx->depth > orig_depth) || (current != status + 1)) {
- if (current == LYJSON_ARRAY) {
- /* skip the array separately */
- LY_CHECK_RET(lydjson_data_skip(jsonctx));
- current = lyjson_ctx_status(jsonctx, 0);
- } else {
+ /* skip until object closes */
+ do {
LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
- }
+ } while ((current != LYJSON_OBJECT_CLOSED) || (depth != lyjson_ctx_depth(jsonctx)));
+ break;
+ case LYJSON_ARRAY:
+ ++depth;
- if (current == LYJSON_END) {
- break;
+ /* skip until array closes */
+ do {
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
+ } while ((current != LYJSON_ARRAY_CLOSED) || (depth != lyjson_ctx_depth(jsonctx)));
+ break;
+ case LYJSON_OBJECT_NAME:
+ /* just get to the value */
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
+ if ((current == LYJSON_OBJECT) || (current == LYJSON_ARRAY)) {
+ LY_CHECK_RET(lydjson_data_skip(jsonctx));
}
+ break;
+ default:
+ /* no other status really expected, just go to next */
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, &current));
+ break;
}
return LY_SUCCESS;
@@ -280,14 +288,6 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
ret = LY_EVALID;
goto cleanup;
}
- if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
- /* skip element with children */
- ret = lydjson_data_skip(lydctx->jsonctx);
- LY_CHECK_GOTO(ret, cleanup);
-
- ret = LY_ENOT;
- goto cleanup;
- }
}
/* get the schema node */
@@ -295,7 +295,7 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
if (!parent && lydctx->ext) {
*snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
} else {
- *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
+ *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts);
}
if (!*snode) {
/* check for extension data */
@@ -326,13 +326,6 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref
}
ret = LY_EVALID;
goto cleanup;
- } else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
- /* skip element with children */
- ret = lydjson_data_skip(lydctx->jsonctx);
- LY_CHECK_GOTO(ret, cleanup);
-
- ret = LY_ENOT;
- goto cleanup;
}
} else {
/* check that schema node is valid and can be used */
@@ -345,6 +338,55 @@ cleanup:
}
/**
+ * @brief Get the hint for the data type parsers according to the current JSON parser context.
+ *
+ * @param[in] jsonctx JSON parser context. The context is supposed to be on a value.
+ * @param[in,out] status Pointer to the current context status,
+ * in some circumstances the function manipulates with the context so the status is updated.
+ * @param[out] type_hint_p Pointer to the variable to store the result.
+ * @return LY_SUCCESS in case of success.
+ * @return LY_EINVAL in case of invalid context status not referring to a value.
+ */
+static LY_ERR
+lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
+{
+ *type_hint_p = 0;
+
+ if (*status_p == LYJSON_ARRAY) {
+ /* only [null] */
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, status_p));
+ if (*status_p != LYJSON_NULL) {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON,
+ "Expected JSON name/value or special name/[null], but input data contains name/[%s].",
+ lyjson_token2str(*status_p));
+ return LY_EINVAL;
+ }
+
+ LY_CHECK_RET(lyjson_ctx_next(jsonctx, NULL));
+ if (lyjson_ctx_status(jsonctx) != LYJSON_ARRAY_CLOSED) {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Expected array end, but input data contains %s.",
+ lyjson_token2str(*status_p));
+ return LY_EINVAL;
+ }
+
+ *type_hint_p = LYD_VALHINT_EMPTY;
+ } else if (*status_p == LYJSON_STRING) {
+ *type_hint_p = LYD_VALHINT_STRING | LYD_VALHINT_NUM64;
+ } else if (*status_p == LYJSON_NUMBER) {
+ *type_hint_p = LYD_VALHINT_DECNUM;
+ } else if ((*status_p == LYJSON_FALSE) || (*status_p == LYJSON_TRUE)) {
+ *type_hint_p = LYD_VALHINT_BOOLEAN;
+ } else if (*status_p == LYJSON_NULL) {
+ *type_hint_p = 0;
+ } else {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Unexpected input data %s.", lyjson_token2str(*status_p));
+ return LY_EINVAL;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
* @brief Check that the input data are parseable as the @p list.
*
* Checks for all the list's keys. Function does not revert the context state.
@@ -357,128 +399,85 @@ cleanup:
static LY_ERR
lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list)
{
- LY_ERR ret = LY_SUCCESS;
- enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx, 0);
+ LY_ERR rc = LY_SUCCESS;
+ enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
struct ly_set key_set = {0};
const struct lysc_node *snode;
- uint32_t i, status_count;
+ uint32_t i, hints;
assert(list && (list->nodetype == LYS_LIST));
/* get all keys into a set (keys do not have if-features or anything) */
snode = NULL;
while ((snode = lys_getnext(snode, list, NULL, 0)) && (snode->flags & LYS_KEY)) {
- ret = ly_set_add(&key_set, (void *)snode, 1, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = ly_set_add(&key_set, (void *)snode, 1, NULL);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+ if (!key_set.count) {
+ /* no keys */
+ goto cleanup;
}
if (status == LYJSON_OBJECT) {
- status_count = jsonctx->status.count;
-
- while (key_set.count && (status != LYJSON_OBJECT_CLOSED)) {
+ do {
const char *name, *prefix;
size_t name_len, prefix_len;
ly_bool is_attr;
/* match the key */
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(jsonctx, &status), cleanup);
+ if (status != LYJSON_OBJECT_NAME) {
+ break;
+ }
snode = NULL;
lydjson_parse_name(jsonctx->value, jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
if (!is_attr && !prefix) {
for (i = 0; i < key_set.count; ++i) {
- snode = (const struct lysc_node *)key_set.objs[i];
- if (!ly_strncmp(snode->name, name, name_len)) {
+ if (!ly_strncmp(key_set.snodes[i]->name, name, name_len)) {
+ snode = key_set.snodes[i];
break;
}
}
- /* go into the item to a) process it as a key or b) start skipping it as another list child */
- ret = lyjson_ctx_next(jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the value */
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(jsonctx, &status), cleanup);
if (snode) {
/* we have the key, validate the value */
- if (status < LYJSON_NUMBER) {
+ if ((status < LYJSON_NUMBER) || (status > LYJSON_NULL)) {
/* not a terminal */
- ret = LY_ENOT;
+ rc = LY_ENOT;
goto cleanup;
}
- ret = lys_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lydjson_value_type_hint(jsonctx, &status, &hints);
+ LY_CHECK_GOTO(rc, cleanup);
+ rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, hints);
+ LY_CHECK_GOTO(rc, cleanup);
/* key with a valid value, remove from the set */
ly_set_rm_index(&key_set, i, NULL);
}
+
+ /* next object */
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(jsonctx, &status), cleanup);
} else {
- /* start skipping the member we are not interested in */
- ret = lyjson_ctx_next(jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
- }
- /* move to the next child */
- while (status_count < jsonctx->status.count) {
- ret = lyjson_ctx_next(jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
+ /* skip the uninteresting object */
+ LY_CHECK_GOTO(rc = lydjson_data_skip(jsonctx), cleanup);
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(jsonctx, &status), cleanup);
}
- }
+ } while (key_set.count && (status == LYJSON_OBJECT_NEXT));
}
if (key_set.count) {
/* some keys are missing/did not validate */
- ret = LY_ENOT;
+ rc = LY_ENOT;
}
cleanup:
ly_set_erase(&key_set, NULL);
- return ret;
-}
-
-/**
- * @brief Get the hint for the data type parsers according to the current JSON parser context.
- *
- * @param[in] lydctx JSON data parser context. The context is supposed to be on a value.
- * @param[in,out] status Pointer to the current context status,
- * in some circumstances the function manipulates with the context so the status is updated.
- * @param[out] type_hint_p Pointer to the variable to store the result.
- * @return LY_SUCCESS in case of success.
- * @return LY_EINVAL in case of invalid context status not referring to a value.
- */
-static LY_ERR
-lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p)
-{
- *type_hint_p = 0;
-
- if (*status_p == LYJSON_ARRAY) {
- /* only [null] */
- LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_p));
- if (*status_p != LYJSON_NULL) {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON,
- "Expected JSON name/value or special name/[null], but input data contains name/[%s].",
- lyjson_token2str(*status_p));
- return LY_EINVAL;
- }
-
- LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, NULL));
- if (lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_ARRAY_CLOSED) {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Expected array end, but input data contains %s.",
- lyjson_token2str(*status_p));
- return LY_EINVAL;
- }
-
- *type_hint_p = LYD_VALHINT_EMPTY;
- } else if (*status_p == LYJSON_STRING) {
- *type_hint_p = LYD_VALHINT_STRING | LYD_VALHINT_NUM64;
- } else if (*status_p == LYJSON_NUMBER) {
- *type_hint_p = LYD_VALHINT_DECNUM;
- } else if ((*status_p == LYJSON_FALSE) || (*status_p == LYJSON_TRUE)) {
- *type_hint_p = LYD_VALHINT_BOOLEAN;
- } else if (*status_p == LYJSON_NULL) {
- *type_hint_p = 0;
- } else {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Unexpected input data %s.", lyjson_token2str(*status_p));
- return LY_EINVAL;
- }
-
- return LY_SUCCESS;
+ return rc;
}
/**
@@ -512,7 +511,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
/* backup parser */
lyjson_ctx_backup(jsonctx);
- status = lyjson_ctx_status(jsonctx, 0);
+ status = lyjson_ctx_status(jsonctx);
if (lydctx->parse_opts & LYD_PARSE_OPAQ) {
/* check if the node is parseable. if not, NULL the snode to announce that it is supposed to be parsed
@@ -521,12 +520,10 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
case LYS_LEAFLIST:
case LYS_LEAF:
/* value may not be valid in which case we parse it as an opaque node */
- ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
- if (ret) {
+ if ((ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p))) {
break;
}
-
- if (lys_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL)) {
+ if (ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, *type_hint_p)) {
ret = LY_ENOT;
}
break;
@@ -539,8 +536,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
break;
}
} else if (snode->nodetype & LYD_NODE_TERM) {
- status = lyjson_ctx_status(jsonctx, 0);
- ret = lydjson_value_type_hint(lydctx, &status, type_hint_p);
+ ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p);
}
/* restore parser */
@@ -725,7 +721,7 @@ cleanup:
static LY_ERR
lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lyd_node *node)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR rc = LY_SUCCESS, r;
enum LYJSON_PARSER_STATUS status;
const char *expected;
ly_bool in_parent = 0;
@@ -745,33 +741,29 @@ lydjson_metadata(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, str
LOG_LOCSET(snode, NULL, NULL, NULL);
/* move to the second item in the name/X pair */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
/* check attribute encoding */
switch (nodetype) {
case LYS_LEAFLIST:
expected = "@name/array of objects/nulls";
-
LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
next_entry:
- instance++;
-
- /* move into array/next entry */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
-
if (status == LYJSON_ARRAY_CLOSED) {
/* no more metadata */
goto cleanup;
}
+
+ /* move into the array/next item */
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ instance++;
LY_CHECK_GOTO((status != LYJSON_OBJECT) && (status != LYJSON_NULL), representation_error);
if (!node || (node->schema != prev->schema)) {
LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance #%u of %s:%s to be coupled with metadata.",
instance, prev->schema->module->name, prev->schema->name);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
@@ -779,6 +771,8 @@ next_entry:
/* continue with the next entry in the leaf-list array */
prev = node;
node = node->next;
+
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
goto next_entry;
}
break;
@@ -800,31 +794,32 @@ next_entry:
break;
default:
LOGINT(ctx);
- ret = LY_EINT;
+ rc = LY_EINT;
goto cleanup;
}
/* process all the members inside a single metadata object */
assert(status == LYJSON_OBJECT);
- while (status != LYJSON_OBJECT_CLOSED) {
- LY_CHECK_GOTO(status != LYJSON_OBJECT, representation_error);
+ do {
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ LY_CHECK_GOTO(status != LYJSON_OBJECT_NAME, representation_error);
lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &dynamic_prefname);
if (!name_len) {
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON found with an empty name, followed by: %.10s", name);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
} else if (!prefix_len) {
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON must be namespace-qualified, missing prefix for \"%.*s\".",
(int)lydctx->jsonctx->value_len, lydctx->jsonctx->value);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
} else if (is_attr) {
LOGVAL(ctx, LYVE_SYNTAX_JSON, "Invalid format of the Metadata identifier in JSON, unexpected '@' in \"%.*s\"",
(int)lydctx->jsonctx->value_len, lydctx->jsonctx->value);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
@@ -834,14 +829,13 @@ next_entry:
if (lydctx->parse_opts & LYD_PARSE_STRICT) {
LOGVAL(ctx, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.",
(int)prefix_len, prefix, (int)name_len, name);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
if (node->schema) {
/* skip element with children */
- ret = lydjson_data_skip(lydctx->jsonctx);
- LY_CHECK_GOTO(ret, cleanup);
- status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ LY_CHECK_GOTO(rc = lydjson_data_skip(lydctx->jsonctx), cleanup);
+ status = lyjson_ctx_status(lydctx->jsonctx);
/* end of the item */
continue;
}
@@ -849,22 +843,20 @@ next_entry:
}
/* get the value */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
/* get value hints */
- ret = lydjson_value_type_hint(lydctx, &status, &val_hints);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx->jsonctx, &status, &val_hints), cleanup);
if (node->schema) {
/* create metadata */
- ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, lydctx->jsonctx->value,
+ rc = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, lydctx->jsonctx->value,
lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, val_hints, node->schema);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(rc, cleanup);
/* add/correct flags */
- ret = lyd_parse_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lyd_parse_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL);
+ LY_CHECK_GOTO(rc, cleanup);
} else {
/* create attribute */
const char *module_name;
@@ -873,22 +865,24 @@ next_entry:
lydjson_get_node_prefix(node, prefix, prefix_len, &module_name, &module_name_len);
/* attr2 is always changed to the created attribute */
- ret = lyd_create_attr(node, NULL, lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, module_name,
+ rc = lyd_create_attr(node, NULL, lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, module_name,
module_name_len, lydctx->jsonctx->value, lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic,
LY_VALUE_JSON, NULL, val_hints);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_GOTO(rc, cleanup);
}
+
/* next member */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
- LY_CHECK_GOTO((status != LYJSON_OBJECT) && (status != LYJSON_OBJECT_CLOSED), representation_error);
- }
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ } while (status == LYJSON_OBJECT_NEXT);
+ LY_CHECK_GOTO(status != LYJSON_OBJECT_CLOSED, representation_error);
if (nodetype == LYS_LEAFLIST) {
/* continue by processing another metadata object for the following
* leaf-list instance since they are always instantiated in JSON array */
prev = node;
node = node->next;
+
+ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
goto next_entry;
}
@@ -900,12 +894,18 @@ representation_error:
"The attribute(s) of %s \"%s\" is expected to be represented as JSON %s, but input data contains @%s/%s.",
lys_nodetype2str(nodetype), node ? LYD_NAME(node) : LYD_NAME(prev), expected, lyjson_token2str(status),
in_parent ? "" : "name");
- ret = LY_EVALID;
+ rc = LY_EVALID;
cleanup:
+ if ((rc == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) {
+ /* try to skip the invalid data */
+ if ((r = lydjson_data_skip(lydctx->jsonctx))) {
+ rc = r;
+ }
+ }
free(dynamic_prefname);
LOG_LOCBACK(1, 0, 0, 0);
- return ret;
+ return rc;
}
/**
@@ -922,24 +922,26 @@ static void
lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last,
struct lysc_ext_instance *ext)
{
- if (*node_p) {
- /* insert, keep first pointer correct */
- if (ext) {
- lyplg_ext_insert(parent, *node_p);
+ if (!*node_p) {
+ return;
+ }
+
+ /* insert, keep first pointer correct */
+ if (ext) {
+ lyplg_ext_insert(parent, *node_p);
+ } else {
+ lyd_insert_node(parent, first_p, *node_p, last);
+ }
+ if (first_p) {
+ if (parent) {
+ *first_p = lyd_child(parent);
} else {
- lyd_insert_node(parent, first_p, *node_p, last);
- }
- if (first_p) {
- if (parent) {
- *first_p = lyd_child(parent);
- } else {
- while ((*first_p)->prev->next) {
- *first_p = (*first_p)->prev;
- }
+ while ((*first_p)->prev->next) {
+ *first_p = (*first_p)->prev;
}
}
- *node_p = NULL;
}
+ *node_p = NULL;
}
/**
@@ -950,8 +952,7 @@ lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, st
* @param[in] name_len Length of the @p name string.
* @param[in] prefix Prefix of the opaq node to create.
* @param[in] prefix_len Length of the @p prefx string.
- * @param[in] parent Data parent of the opaq node to create, can be NULL in case of top level,
- * but must be set if @p first is not.
+ * @param[in] parent Data parent of the opaq node, to inherit module name from.
* @param[in,out] status_inner_p In case of processing JSON array, this parameter points to a standalone
* context status of the array content. Otherwise, it is supposed to be the same as @p status_p.
* @param[out] node_p Pointer to the created opaq node.
@@ -967,18 +968,25 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l
ly_bool dynamic = 0;
uint32_t type_hint = 0;
- if ((*status_inner_p != LYJSON_OBJECT) && (*status_inner_p != LYJSON_OBJECT_EMPTY)) {
+ if (*status_inner_p != LYJSON_OBJECT) {
/* prepare for creating opaq node with a value */
value = lydctx->jsonctx->value;
value_len = lydctx->jsonctx->value_len;
dynamic = lydctx->jsonctx->dynamic;
lydctx->jsonctx->dynamic = 0;
- LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint));
+ LY_CHECK_RET(lydjson_value_type_hint(lydctx->jsonctx, status_inner_p, &type_hint));
}
- /* create node */
+ /* get the module name */
lydjson_get_node_prefix(parent, prefix, prefix_len, &module_name, &module_name_len);
+ if (!module_name && !parent && lydctx->any_schema) {
+ /* in an anyxml/anydata tree, parsing first node, use the previous any schema node */
+ module_name = lydctx->any_schema->module->name;
+ module_name_len = strlen(module_name);
+ }
+
+ /* create node */
ret = lyd_create_opaq(lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, module_name, module_name_len, value,
value_len, &dynamic, LY_VALUE_JSON, NULL, type_hint, node_p);
if (dynamic) {
@@ -1016,61 +1024,80 @@ lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_le
struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p, enum LYJSON_PARSER_STATUS *status_inner_p,
struct lyd_node **first_p, struct lyd_node **node_p)
{
- LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
+ LY_ERR ret = LY_SUCCESS;
+
+ LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p), cleanup);
+
+ assert(*node_p);
+ LOG_LOCSET(NULL, *node_p, NULL, NULL);
if ((*status_p == LYJSON_ARRAY) && (*status_inner_p == LYJSON_NULL)) {
/* special array null value */
((struct lyd_node_opaq *)*node_p)->hints |= LYD_VALHINT_EMPTY;
/* must be the only item */
- LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup);
if (*status_inner_p != LYJSON_ARRAY_CLOSED) {
LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX, "Array \"null\" member with another member.");
- return LY_EVALID;
+ ret = LY_EVALID;
+ goto cleanup;
}
goto finish;
}
- while ((*status_p == LYJSON_ARRAY) || (*status_p == LYJSON_ARRAY_EMPTY)) {
+ while (*status_p == LYJSON_ARRAY) {
/* process another instance of the same node */
-
- if ((*status_inner_p == LYJSON_OBJECT) || (*status_inner_p == LYJSON_OBJECT_EMPTY)) {
+ if (*status_inner_p == LYJSON_OBJECT) {
/* array with objects, list */
((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST;
/* but first process children of the object in the array */
- while ((*status_inner_p != LYJSON_OBJECT_CLOSED) && (*status_inner_p != LYJSON_OBJECT_EMPTY)) {
- LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
- *status_inner_p = lyjson_ctx_status(lydctx->jsonctx, 0);
- }
+ do {
+ LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL), cleanup);
+ *status_inner_p = lyjson_ctx_status(lydctx->jsonctx);
+ } while (*status_inner_p == LYJSON_OBJECT_NEXT);
} else {
/* array with values, leaf-list */
((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LEAFLIST;
}
- LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup);
if (*status_inner_p == LYJSON_ARRAY_CLOSED) {
goto finish;
}
+ assert(*status_inner_p == LYJSON_ARRAY_NEXT);
/* continue with the next instance */
- assert(node_p);
+ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup);
+ assert(*node_p);
lydjson_maintain_children(parent, first_p, node_p, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, NULL);
- LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
+
+ LOG_LOCBACK(0, 1, 0, 0);
+
+ LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p), cleanup);
+
+ assert(*node_p);
+ LOG_LOCSET(NULL, *node_p, NULL, NULL);
}
- if ((*status_p == LYJSON_OBJECT) || (*status_p == LYJSON_OBJECT_EMPTY)) {
+ if (*status_p == LYJSON_OBJECT) {
/* process children */
- while (*status_p != LYJSON_OBJECT_CLOSED && *status_p != LYJSON_OBJECT_EMPTY) {
- LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
- *status_p = lyjson_ctx_status(lydctx->jsonctx, 0);
- }
+ do {
+ LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL), cleanup);
+ *status_p = lyjson_ctx_status(lydctx->jsonctx);
+ } while (*status_p == LYJSON_OBJECT_NEXT);
}
finish:
/* finish linking metadata */
- return lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p));
+ ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p));
+
+cleanup:
+ if (*node_p) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ return ret;
}
/**
@@ -1094,8 +1121,8 @@ finish:
* @return LY_ERR value.
*/
static LY_ERR
-lydjson_ctx_next_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len,
- const char *prefix, size_t prefix_len, struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p,
+lydjson_ctx_next_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len, const char *prefix,
+ size_t prefix_len, struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p,
struct lyd_node **first_p, struct lyd_node **node_p)
{
enum LYJSON_PARSER_STATUS status_inner = 0;
@@ -1232,79 +1259,94 @@ static LY_ERR
lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext,
enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
{
- LY_ERR rc = LY_SUCCESS;
- uint32_t prev_parse_opts, prev_int_opts;
+ LY_ERR r, rc = LY_SUCCESS;
+ uint32_t prev_parse_opts = lydctx->parse_opts, prev_int_opts = lydctx->int_opts;
struct ly_in in_start;
char *val = NULL;
- struct lyd_node *tree = NULL;
+ const char *end;
+ struct lyd_node *child = NULL;
+ ly_bool log_node = 0;
assert(snode->nodetype & LYD_NODE_ANY);
+ *node = NULL;
+
/* status check according to allowed JSON types */
if (snode->nodetype == LYS_ANYXML) {
- LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY) && (*status != LYJSON_ARRAY) &&
- (*status != LYJSON_ARRAY_EMPTY) && (*status != LYJSON_NUMBER) && (*status != LYJSON_STRING) &&
- (*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) && (*status != LYJSON_NULL), LY_ENOT);
+ LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_ARRAY) && (*status != LYJSON_NUMBER) &&
+ (*status != LYJSON_STRING) && (*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) &&
+ (*status != LYJSON_NULL), LY_ENOT);
} else {
- LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_ENOT);
+ LY_CHECK_RET(*status != LYJSON_OBJECT, LY_ENOT);
}
/* create any node */
switch (*status) {
case LYJSON_OBJECT:
+ /* create node */
+ r = lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ assert(*node);
+ LOG_LOCSET(NULL, *node, NULL, NULL);
+ log_node = 1;
+
/* parse any data tree with correct options, first backup the current options and then make the parser
* process data as opaq nodes */
- prev_parse_opts = lydctx->parse_opts;
lydctx->parse_opts &= ~LYD_PARSE_STRICT;
lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
- prev_int_opts = lydctx->int_opts;
lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
+ lydctx->any_schema = snode;
/* process the anydata content */
- while (*status != LYJSON_OBJECT_CLOSED) {
- LY_CHECK_RET(lydjson_subtree_r(lydctx, NULL, &tree, NULL));
- *status = lyjson_ctx_status(lydctx->jsonctx, 0);
- }
+ do {
+ r = lydjson_subtree_r(lydctx, NULL, &child, NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
- /* restore parser options */
- lydctx->parse_opts = prev_parse_opts;
- lydctx->int_opts = prev_int_opts;
+ *status = lyjson_ctx_status(lydctx->jsonctx);
+ } while (*status == LYJSON_OBJECT_NEXT);
/* finish linking metadata */
- LY_CHECK_RET(lydjson_metadata_finish(lydctx, &tree));
+ r = lydjson_metadata_finish(lydctx, &child);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- LY_CHECK_RET(lyd_create_any(snode, tree, LYD_ANYDATA_DATATREE, 1, node));
- break;
- case LYJSON_ARRAY_EMPTY:
- /* store the empty array */
- if (asprintf(&val, "[]") == -1) {
- LOGMEM(lydctx->jsonctx->ctx);
- return LY_EMEM;
- }
- LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+ /* assign the data tree */
+ ((struct lyd_node_any *)*node)->value.tree = child;
+ child = NULL;
break;
case LYJSON_ARRAY:
/* skip until the array end */
in_start = *lydctx->jsonctx->in;
- LY_CHECK_RET(lydjson_data_skip(lydctx->jsonctx));
+ LY_CHECK_GOTO(rc = lydjson_data_skip(lydctx->jsonctx), cleanup);
+
+ /* return back by all the WS */
+ end = lydctx->jsonctx->in->current;
+ while (is_jsonws(end[-1])) {
+ --end;
+ }
/* make a copy of the whole array and store it */
- if (asprintf(&val, "[%.*s", (int)(lydctx->jsonctx->in->current - in_start.current), in_start.current) == -1) {
+ if (asprintf(&val, "[%.*s", (int)(end - in_start.current), in_start.current) == -1) {
LOGMEM(lydctx->jsonctx->ctx);
- return LY_EMEM;
+ rc = LY_EMEM;
+ goto cleanup;
}
- LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+ r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ val = NULL;
break;
case LYJSON_STRING:
/* string value */
if (lydctx->jsonctx->dynamic) {
- LY_CHECK_RET(lyd_create_any(snode, lydctx->jsonctx->value, LYD_ANYDATA_STRING, 1, node));
+ LY_CHECK_GOTO(rc = lyd_create_any(snode, lydctx->jsonctx->value, LYD_ANYDATA_STRING, 1, node), cleanup);
lydctx->jsonctx->dynamic = 0;
} else {
val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len);
- LY_CHECK_ERR_RET(!val, LOGMEM(lydctx->jsonctx->ctx), LY_EMEM);
+ LY_CHECK_ERR_GOTO(!val, LOGMEM(lydctx->jsonctx->ctx); rc = LY_EMEM, cleanup);
- LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node), val_err);
+ r = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ val = NULL;
}
break;
case LYJSON_NUMBER:
@@ -1313,26 +1355,32 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st
/* JSON value */
assert(!lydctx->jsonctx->dynamic);
val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len);
- LY_CHECK_ERR_RET(!val, LOGMEM(lydctx->jsonctx->ctx), LY_EMEM);
+ LY_CHECK_ERR_GOTO(!val, LOGMEM(lydctx->jsonctx->ctx); rc = LY_EMEM, cleanup);
- LY_CHECK_GOTO(rc = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node), val_err);
+ r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ val = NULL;
break;
case LYJSON_NULL:
/* no value */
- LY_CHECK_RET(lyd_create_any(snode, NULL, LYD_ANYDATA_JSON, 1, node));
- break;
- case LYJSON_OBJECT_EMPTY:
- /* empty object */
- LY_CHECK_RET(lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node));
+ r = lyd_create_any(snode, NULL, LYD_ANYDATA_JSON, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
break;
default:
- LOGINT_RET(lydctx->jsonctx->ctx);
+ LOGINT(lydctx->jsonctx->ctx);
+ rc = LY_EINT;
+ goto cleanup;
}
- return LY_SUCCESS;
-
-val_err:
+cleanup:
+ if (log_node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ lydctx->parse_opts = prev_parse_opts;
+ lydctx->int_opts = prev_int_opts;
+ lydctx->any_schema = NULL;
free(val);
+ lyd_free_tree(child);
return rc;
}
@@ -1352,10 +1400,10 @@ static LY_ERR
lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext,
enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
uint32_t prev_parse_opts = lydctx->parse_opts;
- LY_CHECK_RET((*status != LYJSON_OBJECT) && (*status != LYJSON_OBJECT_EMPTY), LY_ENOT);
+ LY_CHECK_RET(*status != LYJSON_OBJECT, LY_ENOT);
/* create inner node */
LY_CHECK_RET(lyd_create_inner(snode, node));
@@ -1369,38 +1417,43 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node
}
/* process children */
- while ((*status != LYJSON_OBJECT_CLOSED) && (*status != LYJSON_OBJECT_EMPTY)) {
- ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
- LY_CHECK_GOTO(ret, cleanup);
- *status = lyjson_ctx_status(lydctx->jsonctx, 0);
- }
+ do {
+ r = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
+ *status = lyjson_ctx_status(lydctx->jsonctx);
+ } while (*status == LYJSON_OBJECT_NEXT);
/* finish linking metadata */
- ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node));
- LY_CHECK_GOTO(ret, cleanup);
+ r = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node));
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (snode->nodetype == LYS_LIST) {
/* check all keys exist */
- ret = lyd_parse_check_keys(*node);
- LY_CHECK_GOTO(ret, cleanup);
+ r = lyd_parse_check_keys(*node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
- if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
- /* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) {
+ /* new node validation, autodelete CANNOT occur (it can if multi-error), all nodes are new */
+ r = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, lydctx->val_opts, NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
/* add any missing default children */
- ret = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when,
- &lydctx->node_types, &lydctx->ext_node,
- (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ r = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
+ &lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
cleanup:
lydctx->parse_opts = prev_parse_opts;
LOG_LOCBACK(0, 1, 0, 0);
- return ret;
+ if (!(*node)->hash) {
+ /* list without keys is unusable */
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+ return rc;
}
/**
@@ -1427,52 +1480,61 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str
const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len,
const char *prefix, size_t prefix_len, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
uint32_t type_hints = 0;
LOG_LOCSET(snode, NULL, NULL, NULL);
- ret = lydjson_data_check_opaq(lydctx, snode, &type_hints);
- if (ret == LY_SUCCESS) {
+ r = lydjson_data_check_opaq(lydctx, snode, &type_hints);
+ if (r == LY_SUCCESS) {
assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY));
if (snode->nodetype & LYD_NODE_TERM) {
if ((*status != LYJSON_ARRAY) && (*status != LYJSON_NUMBER) && (*status != LYJSON_STRING) &&
(*status != LYJSON_FALSE) && (*status != LYJSON_TRUE) && (*status != LYJSON_NULL)) {
- ret = LY_ENOT;
+ rc = LY_ENOT;
goto cleanup;
}
/* create terminal node */
- LY_CHECK_GOTO(ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value,
- lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node), cleanup);
+ r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value,
+ lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
/* move JSON parser */
if (*status == LYJSON_ARRAY) {
/* only [null], 2 more moves are needed */
- LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status), cleanup);
+ r = lyjson_ctx_next(lydctx->jsonctx, status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
assert(*status == LYJSON_NULL);
- LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status), cleanup);
+
+ r = lyjson_ctx_next(lydctx->jsonctx, status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
assert(*status == LYJSON_ARRAY_CLOSED);
}
} else if (snode->nodetype & LYD_NODE_INNER) {
/* create inner node */
- LY_CHECK_GOTO(ret = lydjson_parse_instance_inner(lydctx, snode, ext, status, node), cleanup);
+ r = lydjson_parse_instance_inner(lydctx, snode, ext, status, node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
} else {
/* create any node */
- LY_CHECK_GOTO(ret = lydjson_parse_any(lydctx, snode, ext, status, node), cleanup);
+ r = lydjson_parse_any(lydctx, snode, ext, status, node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
+ LY_CHECK_GOTO(!*node, cleanup);
/* add/correct flags */
- LY_CHECK_GOTO(ret = lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext), cleanup);
+ r = lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
/* store for ext instance node validation, if needed */
- LY_CHECK_GOTO(ret = lyd_validate_node_ext(*node, &lydctx->ext_node), cleanup);
+ r = lyd_validate_node_ext(*node, &lydctx->ext_node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
- } else if (ret == LY_ENOT) {
+ } else if (r == LY_ENOT) {
/* parse it again as an opaq node */
- LY_CHECK_GOTO(ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status,
- first_p, node), cleanup);
+ r = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status, first_p, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (snode->nodetype == LYS_LIST) {
((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LIST;
@@ -1481,12 +1543,13 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str
}
} else {
/* error */
+ rc = r;
goto cleanup;
}
cleanup:
LOG_LOCBACK(1, 0, 0, 0);
- return ret;
+ return rc;
}
/**
@@ -1501,36 +1564,80 @@ cleanup:
static LY_ERR
lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
{
- LY_ERR ret = LY_SUCCESS, r;
- enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ LY_ERR r, rc = LY_SUCCESS;
+ enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx);
const char *name, *prefix = NULL, *expected = NULL;
size_t name_len, prefix_len = 0;
ly_bool is_meta = 0, parse_subtree;
const struct lysc_node *snode = NULL;
- struct lysc_ext_instance *ext;
+ struct lysc_ext_instance *ext = NULL;
struct lyd_node *node = NULL, *attr_node = NULL;
const struct ly_ctx *ctx = lydctx->jsonctx->ctx;
char *value = NULL;
assert(parent || first_p);
- assert(status == LYJSON_OBJECT);
+ assert((status == LYJSON_OBJECT) || (status == LYJSON_OBJECT_NEXT));
parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
/* all descendants should be parsed */
lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ if (status == LYJSON_OBJECT_CLOSED) {
+ /* empty object, fine... */
+ goto cleanup;
+ }
+
/* process the node name */
+ assert(status == LYJSON_OBJECT_NAME);
lydjson_parse_name(lydctx->jsonctx->value, lydctx->jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_meta);
lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &value);
- if (!is_meta || name_len || prefix_len) {
+ if ((lydctx->int_opts & LYD_INTOPT_EVENTTIME) && !parent && !is_meta && name_len && !prefix_len &&
+ !ly_strncmp("eventTime", name, name_len)) {
+ /* parse eventTime */
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ if (status != LYJSON_STRING) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_STRING),
+ lyjson_token2str(status));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* create node */
+ r = lyd_create_opaq(lydctx->jsonctx->ctx, name, name_len, prefix, prefix_len, prefix, prefix_len,
+ lydctx->jsonctx->value, lydctx->jsonctx->value_len, NULL, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, &node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* validate the value */
+ r = lyd_parser_notif_eventtime_validate(node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ goto node_parsed;
+ } else if (!is_meta || name_len || prefix_len) {
/* get the schema node */
r = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode, &ext);
if (r == LY_ENOT) {
/* data parsed */
goto cleanup;
+ } else if ((r == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) {
+ rc = r;
+
+ /* skip the invalid data */
+ if ((r = lydjson_data_skip(lydctx->jsonctx))) {
+ rc = r;
+ }
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ goto cleanup;
+ } else if (r) {
+ /* error */
+ rc = r;
+ goto cleanup;
}
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
if (!snode) {
/* we will not be parsing it as metadata */
@@ -1540,39 +1647,44 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
if (is_meta) {
/* parse as metadata */
- if (!name_len && !prefix_len) {
+ if (!name_len && !prefix_len && !parent) {
+ LOGVAL(ctx, LYVE_SYNTAX_JSON,
+ "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ } else if (!name_len && !prefix_len) {
/* parent's metadata without a name - use the schema from the parent */
- if (!parent) {
- LOGVAL(ctx, LYVE_SYNTAX_JSON,
- "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries.");
- ret = LY_EVALID;
- goto cleanup;
- }
attr_node = parent;
snode = attr_node->schema;
}
- ret = lydjson_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, &status,
+ r = lydjson_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, &status,
first_p, &node);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
} else if (!snode) {
- /* parse as an opaq node */
- assert((lydctx->parse_opts & LYD_PARSE_OPAQ) || (lydctx->int_opts));
+ if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+ /* skip element with children */
+ r = lydjson_data_skip(lydctx->jsonctx);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ } else {
+ /* parse as an opaq node */
- /* opaq node cannot have an empty string as the name. */
- if (name_len == 0) {
- LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "A JSON object member name cannot be a zero-length string.");
- ret = LY_EVALID;
- goto cleanup;
- }
+ /* opaq node cannot have an empty string as the name. */
+ if (name_len == 0) {
+ LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "JSON object member name cannot be a zero-length string.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
- /* move to the second item in the name/X pair and parse opaq */
- ret = lydjson_ctx_next_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, &status, first_p, &node);
- LY_CHECK_GOTO(ret, cleanup);
+ /* move to the second item in the name/X pair and parse opaq */
+ r = lydjson_ctx_next_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, &status, first_p, &node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
} else {
/* parse as a standard lyd_node but it can still turn out to be an opaque node */
/* move to the second item in the name/X pair */
- LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
/* set expected representation */
switch (snode->nodetype) {
@@ -1609,30 +1721,31 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
switch (snode->nodetype) {
case LYS_LEAFLIST:
case LYS_LIST:
- if (status == LYJSON_ARRAY_EMPTY) {
- /* no instances, skip */
- break;
- }
LY_CHECK_GOTO(status != LYJSON_ARRAY, representation_error);
- /* move into array */
- ret = lyjson_ctx_next(lydctx->jsonctx, &status);
- LY_CHECK_GOTO(ret, cleanup);
-
/* process all the values/objects */
do {
- ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
+ /* move into array/next value */
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ if (status == LYJSON_ARRAY_CLOSED) {
+ /* empty array, fine... */
+ break;
+ }
+
+ r = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
&status, &node);
- if (ret == LY_ENOT) {
+ if (r == LY_ENOT) {
goto representation_error;
- } else if (ret) {
- goto cleanup;
}
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
/* move after the item(s) */
- LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
- } while (status != LYJSON_ARRAY_CLOSED);
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ } while (status == LYJSON_ARRAY_NEXT);
break;
case LYS_LEAF:
@@ -1643,13 +1756,12 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
case LYS_ANYDATA:
case LYS_ANYXML:
/* process the value/object */
- ret = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
+ r = lydjson_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len,
&status, &node);
- if (ret == LY_ENOT) {
+ if (r == LY_ENOT) {
goto representation_error;
- } else if (ret) {
- goto cleanup;
}
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
/* rememeber the RPC/action/notification */
@@ -1659,31 +1771,39 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l
}
}
- /* finally connect the parsed node */
- lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
-
+node_parsed:
/* rememeber a successfully parsed node */
if (parsed && node) {
ly_set_add(parsed, node, 1, NULL);
}
+ /* finally connect the parsed node, is zeroed */
+ lydjson_maintain_children(parent, first_p, &node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, ext);
+
if (!parse_subtree) {
/* move after the item(s) */
- LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup);
+ r = lyjson_ctx_next(lydctx->jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
/* success */
goto cleanup;
representation_error:
- LOGVAL(ctx, LYVE_SYNTAX_JSON, "The %s \"%s\" is expected to be represented as JSON %s, but input data contains name/%s.",
- lys_nodetype2str(snode->nodetype), snode->name, expected, lyjson_token2str(status));
- ret = LY_EVALID;
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as name/%s.",
+ expected, lys_nodetype2str(snode->nodetype), snode->name, lyjson_token2str(status));
+ rc = LY_EVALID;
+ if (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) {
+ /* try to skip the invalid data */
+ if ((r = lydjson_data_skip(lydctx->jsonctx))) {
+ rc = r;
+ }
+ }
cleanup:
free(value);
lyd_free_tree(node);
- return ret;
+ return rc;
}
/**
@@ -1694,20 +1814,17 @@ cleanup:
* @param[in] parse_opts Options for parser, see @ref dataparseroptions.
* @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
* @param[out] lydctx_p Data parser context to finish validation.
- * @param[out] status Storage for the current context's status
* @return LY_ERR value.
*/
static LY_ERR
lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts,
- struct lyd_json_ctx **lydctx_p, enum LYJSON_PARSER_STATUS *status)
+ struct lyd_json_ctx **lydctx_p)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_json_ctx *lydctx;
- size_t i;
- ly_bool subtree;
+ enum LYJSON_PARSER_STATUS status;
assert(lydctx_p);
- assert(status);
/* init context */
lydctx = calloc(1, sizeof *lydctx);
@@ -1716,28 +1833,20 @@ lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_o
lydctx->val_opts = val_opts;
lydctx->free = lyd_json_ctx_free;
- /* starting top-level */
- for (i = 0; in->current[i] != '\0' && is_jsonws(in->current[i]); i++) {
- if (in->current[i] == '\n') {
- /* new line */
- LY_IN_NEW_LINE(in);
- }
- }
-
- subtree = (parse_opts & LYD_PARSE_SUBTREE) ? 1 : 0;
- LY_CHECK_ERR_RET(ret = lyjson_ctx_new(ctx, in, subtree, &lydctx->jsonctx), free(lydctx), ret);
- *status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ LY_CHECK_ERR_RET(ret = lyjson_ctx_new(ctx, in, &lydctx->jsonctx), free(lydctx), ret);
+ status = lyjson_ctx_status(lydctx->jsonctx);
- if ((*status == LYJSON_END) || (*status == LYJSON_OBJECT_EMPTY) || (*status == LYJSON_OBJECT)) {
- *lydctx_p = lydctx;
- return LY_SUCCESS;
- } else {
+ /* parse_opts & LYD_PARSE_SUBTREE not implemented */
+ if (status != LYJSON_OBJECT) {
/* expecting top-level object */
- LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.", lyjson_token2str(*status));
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.", lyjson_token2str(status));
*lydctx_p = NULL;
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
return LY_EVALID;
}
+
+ *lydctx_p = lydctx;
+ return LY_SUCCESS;
}
LY_ERR
@@ -1745,14 +1854,12 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
{
- LY_ERR rc = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
struct lyd_json_ctx *lydctx = NULL;
enum LYJSON_PARSER_STATUS status;
- rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx, &status);
- LY_CHECK_GOTO(rc || status == LYJSON_END || status == LYJSON_OBJECT_EMPTY, cleanup);
-
- assert(status == LYJSON_OBJECT);
+ rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
+ LY_CHECK_GOTO(rc, cleanup);
lydctx->int_opts = int_opts;
lydctx->ext = ext;
@@ -1761,37 +1868,36 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
/* read subtree(s) */
- while (lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) {
- rc = lydjson_subtree_r(lydctx, parent, first_p, parsed);
- LY_CHECK_GOTO(rc, cleanup);
+ do {
+ r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
- status = lyjson_ctx_status(lydctx->jsonctx, 0);
+ status = lyjson_ctx_status(lydctx->jsonctx);
if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
break;
}
- }
+ } while (status == LYJSON_OBJECT_NEXT);
- if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] &&
- (lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_OBJECT_CLOSED)) {
+ if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) {
LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
- rc = LY_EVALID;
- goto cleanup;
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
- rc = LY_EVALID;
- goto cleanup;
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
/* finish linking metadata */
- rc = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
- LY_CHECK_GOTO(rc, cleanup);
+ r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (parse_opts & LYD_PARSE_SUBTREE) {
/* check for a sibling object */
assert(subtree_sibling);
- if (lydctx->jsonctx->in->current[0] == ',') {
+ if (status == LYJSON_OBJECT_NEXT) {
*subtree_sibling = 1;
/* move to the next object */
@@ -1806,6 +1912,198 @@ cleanup:
assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count &&
!lydctx->node_when.count));
+ if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
+ lyd_json_ctx_free((struct lyd_ctx *)lydctx);
+ } else {
+ *lydctx_p = (struct lyd_ctx *)lydctx;
+
+ /* the JSON context is no more needed, freeing it also stops logging line numbers which would be confusing now */
+ lyjson_ctx_free(lydctx->jsonctx);
+ lydctx->jsonctx = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse a specific JSON object into an opaque node.
+ *
+ * @param[in] jsonctx JSON parser context.
+ * @param[in] name Name of the object.
+ * @param[in] module Module name of the object, NULL if none expected.
+ * @param[out] evnp Parsed envelope (opaque node).
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the specified object did not match.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *module, struct lyd_node **envp)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx);
+ const char *nam, *prefix;
+ size_t nam_len, prefix_len;
+ ly_bool is_meta;
+
+ assert(status == LYJSON_OBJECT);
+
+ *envp = NULL;
+
+ r = lyjson_ctx_next(jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ if (status == LYJSON_OBJECT_CLOSED) {
+ LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON object.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* process the name */
+ assert(status == LYJSON_OBJECT_NAME);
+ lydjson_parse_name(jsonctx->value, jsonctx->value_len, &nam, &nam_len, &prefix, &prefix_len, &is_meta);
+ if (is_meta) {
+ LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected metadata.");
+ rc = LY_EVALID;
+ goto cleanup;
+ } else if (module && ly_strncmp(module, prefix, prefix_len)) {
+ LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected module \"%.*s\" instead of \"%s\".", (int)prefix_len, prefix, module);
+ rc = LY_EVALID;
+ goto cleanup;
+ } else if (ly_strncmp(name, nam, nam_len)) {
+ LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected object \"%.*s\" instead of \"%s\".", (int)nam_len, nam, name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ r = lyjson_ctx_next(jsonctx, &status);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* create node */
+ rc = lyd_create_opaq(jsonctx->ctx, name, strlen(name), prefix, prefix_len, prefix, prefix_len, NULL, 0, NULL,
+ LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+LY_ERR
+lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent,
+ struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type,
+ struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ struct lyd_json_ctx *lydctx = NULL;
+ struct lyd_node *node;
+ uint32_t i, int_opts = 0, close_elem = 0;
+
+ assert(ctx && in && lydctx_p);
+ assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
+ assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
+
+ assert((data_type == LYD_TYPE_RPC_RESTCONF) || (data_type == LYD_TYPE_NOTIF_RESTCONF) ||
+ (data_type == LYD_TYPE_REPLY_RESTCONF));
+ assert(!(parse_opts & LYD_PARSE_SUBTREE));
+
+ /* init context */
+ rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx);
+ LY_CHECK_GOTO(rc, cleanup);
+ lydctx->ext = ext;
+
+ switch (data_type) {
+ case LYD_TYPE_RPC_RESTCONF:
+ assert(parent);
+
+ /* parse "input" */
+ rc = lydjson_envelope(lydctx->jsonctx, "input", lyd_node_module(parent)->name, envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
+ close_elem = 1;
+ break;
+ case LYD_TYPE_NOTIF_RESTCONF:
+ assert(!parent);
+
+ /* parse "notification" */
+ rc = lydjson_envelope(lydctx->jsonctx, "notification", "ietf-restconf", envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* RESTCONF notification and eventTime */
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME;
+ close_elem = 1;
+ break;
+ case LYD_TYPE_REPLY_RESTCONF:
+ assert(parent);
+
+ /* parse "output" */
+ rc = lydjson_envelope(lydctx->jsonctx, "output", lyd_node_module(parent)->name, envp);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
+ close_elem = 1;
+ break;
+ default:
+ LOGINT(ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+
+ lydctx->int_opts = int_opts;
+
+ /* find the operation node if it exists already */
+ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+
+ /* read subtree(s) */
+ do {
+ r = lydjson_subtree_r(lydctx, parent, first_p, parsed);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ } while (lyjson_ctx_status(lydctx->jsonctx) == LYJSON_OBJECT_NEXT);
+
+ /* close all opened elements */
+ for (i = 0; i < close_elem; ++i) {
+ if (lyjson_ctx_status(lydctx->jsonctx) != LYJSON_OBJECT_CLOSED) {
+ LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_OBJECT_CLOSED),
+ lyjson_token2str(lyjson_ctx_status(lydctx->jsonctx)));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ r = lyjson_ctx_next(lydctx->jsonctx, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
+
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
+ LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+ if (int_opts & LYD_INTOPT_EVENTTIME) {
+ /* parse as a child of the envelope */
+ node = (*first_p)->prev;
+ if (node->schema) {
+ LOGVAL(ctx, LYVE_DATA, "Missing notification \"eventTime\" node.");
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ } else {
+ /* can be the only opaque node and an operation had to be parsed */
+ assert(!strcmp(LYD_NAME(node), "eventTime") && (*first_p)->next);
+ lyd_unlink(node);
+ assert(*envp);
+ lyd_insert_child(*envp, node);
+ }
+ }
+
+ /* finish linking metadata */
+ r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+cleanup:
+ /* there should be no unres stored if validation should be skipped */
+ assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count &&
+ !lydctx->node_when.count));
+
if (rc) {
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
} else {
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index f898085..788be94 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -49,11 +49,15 @@ lylyb_ctx_free(struct lylyb_ctx *ctx)
{
LY_ARRAY_COUNT_TYPE u;
+ if (!ctx) {
+ return;
+ }
+
LY_ARRAY_FREE(ctx->siblings);
LY_ARRAY_FREE(ctx->models);
LY_ARRAY_FOR(ctx->sib_hts, u) {
- lyht_free(ctx->sib_hts[u].ht);
+ lyht_free(ctx->sib_hts[u].ht, NULL);
}
LY_ARRAY_FREE(ctx->sib_hts);
@@ -65,6 +69,10 @@ lyd_lyb_ctx_free(struct lyd_ctx *lydctx)
{
struct lyd_lyb_ctx *ctx = (struct lyd_lyb_ctx *)lydctx;
+ if (!lydctx) {
+ return;
+ }
+
lyd_ctx_free(lydctx);
lylyb_ctx_free(ctx->lybctx);
free(ctx);
@@ -1082,7 +1090,7 @@ lyb_validate_node_inner(struct lyd_lyb_ctx *lybctx, const struct lysc_node *snod
if (!(lybctx->parse_opts & LYD_PARSE_ONLY)) {
/* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, NULL);
+ ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, 0, NULL);
LY_CHECK_RET(ret);
/* add any missing default children */
@@ -1160,9 +1168,12 @@ lyb_parse_node_opaq(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct
/* create node */
ret = lyd_create_opaq(ctx, name, strlen(name), prefix, ly_strlen(prefix), module_key, ly_strlen(module_key),
- value, strlen(value), &dynamic, format, val_prefix_data, 0, &node);
+ value, strlen(value), &dynamic, format, val_prefix_data, LYD_HINT_DATA, &node);
LY_CHECK_GOTO(ret, cleanup);
+ assert(node);
+ LOG_LOCSET(NULL, node, NULL, NULL);
+
/* process children */
ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
LY_CHECK_GOTO(ret, cleanup);
@@ -1170,8 +1181,12 @@ lyb_parse_node_opaq(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct
/* register parsed opaq node */
lyb_finish_opaq(lybctx, parent, flags, &attr, &node, first_p, parsed);
assert(!attr && !node);
+ LOG_LOCBACK(0, 1, 0, 0);
cleanup:
+ if (node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
free(prefix);
free(module_key);
free(name);
@@ -1257,9 +1272,13 @@ lyb_parse_node_any(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const st
goto error;
}
+ assert(node);
+ LOG_LOCSET(NULL, node, NULL, NULL);
+
/* register parsed anydata node */
lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+ LOG_LOCBACK(0, 1, 0, 0);
return LY_SUCCESS;
error:
@@ -1296,6 +1315,9 @@ lyb_parse_node_inner(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const
ret = lyd_create_inner(snode, &node);
LY_CHECK_GOTO(ret, error);
+ assert(node);
+ LOG_LOCSET(NULL, node, NULL, NULL);
+
/* process children */
ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
LY_CHECK_GOTO(ret, error);
@@ -1312,9 +1334,13 @@ lyb_parse_node_inner(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const
/* register parsed node */
lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+ LOG_LOCBACK(0, 1, 0, 0);
return LY_SUCCESS;
error:
+ if (node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
lyd_free_meta_siblings(meta);
lyd_free_tree(node);
return ret;
@@ -1347,8 +1373,12 @@ lyb_parse_node_leaf(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
ret = lyb_create_term(lybctx, snode, &node);
LY_CHECK_GOTO(ret, error);
+ assert(node);
+ LOG_LOCSET(NULL, node, NULL, NULL);
+
lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+ LOG_LOCBACK(0, 1, 0, 0);
return LY_SUCCESS;
error:
@@ -1408,6 +1438,7 @@ lyb_parse_node_list(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
struct lyd_node *node = NULL;
struct lyd_meta *meta = NULL;
uint32_t flags;
+ ly_bool log_node = 0;
/* register a new sibling */
ret = lyb_read_start_siblings(lybctx->lybctx);
@@ -1422,6 +1453,10 @@ lyb_parse_node_list(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
ret = lyd_create_inner(snode, &node);
LY_CHECK_GOTO(ret, error);
+ assert(node);
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ log_node = 1;
+
/* process children */
ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
LY_CHECK_GOTO(ret, error);
@@ -1437,6 +1472,9 @@ lyb_parse_node_list(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
/* register parsed list node */
lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+
+ LOG_LOCBACK(0, 1, 0, 0);
+ log_node = 0;
}
/* end the sibling */
@@ -1446,6 +1484,9 @@ lyb_parse_node_list(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
return LY_SUCCESS;
error:
+ if (log_node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
lyd_free_meta_siblings(meta);
lyd_free_tree(node);
return ret;
@@ -1484,7 +1525,7 @@ lyb_parse_node(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct lyd_n
case LYB_NODE_CHILD:
case LYB_NODE_OPAQ:
/* read hash, find the schema node starting from parent schema, if any */
- LY_CHECK_GOTO(ret = lyb_parse_schema_hash(lybctx, parent ? parent->schema : NULL, NULL, &snode), cleanup);
+ LY_CHECK_GOTO(ret = lyb_parse_schema_hash(lybctx, lyd_parser_node_schema(parent), NULL, &snode), cleanup);
break;
case LYB_NODE_EXT:
/* ext, read module name */
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 5a929da..5d97c8e 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -40,6 +40,9 @@
#include "validation.h"
#include "xml.h"
+static LY_ERR lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p,
+ struct ly_set *parsed);
+
void
lyd_xml_ctx_free(struct lyd_ctx *lydctx)
{
@@ -72,6 +75,8 @@ lydxml_metadata(struct lyd_xml_ctx *lydctx, const struct lysc_node *sparent, str
*meta = NULL;
+ LOG_LOCSET(sparent, NULL, NULL, NULL);
+
/* check for NETCONF filter unqualified attributes */
if (!strcmp(sparent->module->name, "notifications")) {
/* ancient module that does not even use the extension */
@@ -160,6 +165,7 @@ create_meta:
}
cleanup:
+ LOG_LOCBACK(1, 0, 0, 0);
if (ret) {
lyd_free_meta_siblings(*meta);
*meta = NULL;
@@ -283,7 +289,7 @@ lydxml_check_list(struct lyxml_ctx *xmlctx, const struct lysc_node *list)
assert(xmlctx->status == LYXML_ELEM_CONTENT);
if (i < key_set.count) {
/* validate the value */
- r = lys_value_validate(NULL, snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns);
+ r = ly_value_validate(NULL, snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA);
if (!r) {
/* key with a valid value, remove from the set */
ly_set_rm_index(&key_set, i, NULL);
@@ -389,8 +395,8 @@ lydxml_data_check_opaq(struct lyd_xml_ctx *lydctx, const struct lysc_node **snod
if ((*snode)->nodetype & LYD_NODE_TERM) {
/* value may not be valid in which case we parse it as an opaque node */
- if (lys_value_validate(NULL, *snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns)) {
- LOGVRB("Parsing opaque term node \"%s\" with invalid value \"%.*s\".", (*snode)->name, xmlctx->value_len,
+ if (ly_value_validate(NULL, *snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA)) {
+ LOGVRB("Parsing opaque term node \"%s\" with invalid value \"%.*s\".", (*snode)->name, (int)xmlctx->value_len,
xmlctx->value);
*snode = NULL;
}
@@ -430,7 +436,7 @@ restore:
* @param[out] anchor Anchor to insert after in case of a list.
*/
static void
-lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size_t value_len, struct lyd_node *first,
+lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size_t value_len, const struct lyd_node *first,
const char *ns, uint32_t *hints, struct lyd_node **anchor)
{
struct lyd_node_opaq *opaq;
@@ -441,8 +447,8 @@ lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size
*anchor = NULL;
if (!value_len) {
- /* no value */
- *hints |= LYD_VALHINT_EMPTY;
+ /* no value but it may also be zero-length string */
+ *hints |= LYD_VALHINT_EMPTY | LYD_VALHINT_STRING;
} else if (!strncmp(value, "true", value_len) || !strncmp(value, "false", value_len)) {
/* boolean value */
*hints |= LYD_VALHINT_BOOLEAN;
@@ -485,7 +491,7 @@ lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size
opaq->hints |= LYD_NODEHINT_LIST;
*hints |= LYD_NODEHINT_LIST;
}
- *anchor = first;
+ *anchor = (struct lyd_node *)first;
break;
}
} while (first->prev->next);
@@ -506,7 +512,7 @@ lydxml_get_hints_opaq(const char *name, size_t name_len, const char *value, size
* @return LY_ERR on error.
*/
static LY_ERR
-lydxml_subtree_snode(struct lyd_xml_ctx *lydctx, const struct lyd_node *parent, const char *prefix, size_t prefix_len,
+lydxml_subtree_get_snode(struct lyd_xml_ctx *lydctx, const struct lyd_node *parent, const char *prefix, size_t prefix_len,
const char *name, size_t name_len, const struct lysc_node **snode, struct lysc_ext_instance **ext)
{
LY_ERR r;
@@ -551,338 +557,569 @@ lydxml_subtree_snode(struct lyd_xml_ctx *lydctx, const struct lyd_node *parent,
unknown_module:
if (lydctx->parse_opts & LYD_PARSE_STRICT) {
- LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.", ns->uri);
+ if (ns) {
+ LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.", ns->uri);
+ } else if (prefix_len) {
+ LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%.*s\" in the context.", (int)prefix_len, prefix);
+ } else {
+ LOGVAL(ctx, LYVE_REFERENCE, "No default namespace in the context.");
+ }
return LY_EVALID;
}
return LY_SUCCESS;
}
/* get the schema node */
- if (mod) {
- if (!parent && lydctx->ext) {
- *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
- } else {
- *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
- }
- if (!*snode) {
- /* check for extension data */
- r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name,
- name_len, snode, ext);
- if (r != LY_ENOT) {
- /* success or error */
- return r;
- }
+ if (!parent && lydctx->ext) {
+ *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
+ } else {
+ /* try to find parent schema node even if it is an opaque node (not connected to the parent) */
+ *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts);
+ }
+ if (!*snode) {
+ /* check for extension data */
+ r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name,
+ name_len, snode, ext);
+ if (r != LY_ENOT) {
+ /* success or error */
+ return r;
+ }
- /* unknown data node */
- if (lydctx->parse_opts & LYD_PARSE_STRICT) {
- if (parent) {
- LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.",
- (int)name_len, name, LYD_NAME(parent));
- } else if (lydctx->ext) {
- if (lydctx->ext->argument) {
- LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
- (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
- } else {
- LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
- (int)name_len, name, lydctx->ext->def->name);
- }
+ /* unknown data node */
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ if (parent) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.",
+ (int)name_len, name, LYD_NAME(parent));
+ } else if (lydctx->ext) {
+ if (lydctx->ext->argument) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
+ (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
} else {
- LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
- (int)name_len, name, mod->name);
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
+ (int)name_len, name, lydctx->ext->def->name);
}
- return LY_EVALID;
+ } else {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
+ (int)name_len, name, mod->name);
}
- return LY_SUCCESS;
- } else {
- /* check that schema node is valid and can be used */
- LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode));
- LY_CHECK_RET(lydxml_data_check_opaq(lydctx, snode));
+ return LY_EVALID;
}
+ return LY_SUCCESS;
+ } else {
+ /* check that schema node is valid and can be used */
+ LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode));
+ LY_CHECK_RET(lydxml_data_check_opaq(lydctx, snode));
}
return LY_SUCCESS;
}
/**
- * @brief Parse XML subtree.
+ * @brief Parse an XML opque node.
*
* @param[in] lydctx XML YANG data parser context.
- * @param[in,out] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
- * @param[in,out] first_p Pointer to the first (@p parent or top-level) child. In case there were already some siblings,
- * this may point to a previously existing node.
- * @param[in,out] parsed Optional set to add all the parsed siblings into.
+ * @param[in] sibling Existing sibling node, if any.
+ * @param[in] prefix Parsed node prefix.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] name Parsed node name.
+ * @param[in] name_len Length of @p name.
+ * @param[out] insert_anchor Optional anchor node for inserting this node.
+ * @param[out] node Created node.
* @return LY_ERR value.
*/
static LY_ERR
-lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
+lydxml_subtree_opaq(struct lyd_xml_ctx *lydctx, const struct lyd_node *sibling, const char *prefix, uint32_t prefix_len,
+ const char *name, uint32_t name_len, struct lyd_node **insert_anchor, struct lyd_node **node)
{
- LY_ERR ret = LY_SUCCESS;
- const char *prefix, *name, *ns_uri;
- size_t prefix_len, name_len;
- struct lyxml_ctx *xmlctx;
- const struct ly_ctx *ctx;
+ LY_ERR rc = LY_SUCCESS;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+ struct lyd_node_opaq *opaq;
+ const char *ns_uri, *value = NULL;
+ size_t value_len;
+ ly_bool ws_only, dynamic = 0;
const struct lyxml_ns *ns;
- struct lyd_meta *meta = NULL;
- struct lyd_attr *attr = NULL;
- const struct lysc_node *snode;
- struct lysc_ext_instance *ext;
- uint32_t prev_parse_opts, orig_parse_opts, prev_int_opts, hints;
- struct lyd_node *node = NULL, *anchor, *insert_anchor = NULL;
+ uint32_t hints;
void *val_prefix_data = NULL;
LY_VALUE_FORMAT format;
- ly_bool parse_subtree;
- char *val;
- assert(parent || first_p);
+ assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
- xmlctx = lydctx->xmlctx;
- ctx = xmlctx->ctx;
+ *node = NULL;
- parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
- /* all descendants should be parsed */
- lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
- orig_parse_opts = lydctx->parse_opts;
+ /* remember the value */
+ value = xmlctx->value;
+ value_len = xmlctx->value_len;
+ ws_only = xmlctx->ws_only;
+ dynamic = xmlctx->dynamic;
+ if (dynamic) {
+ xmlctx->dynamic = 0;
+ }
- assert(xmlctx->status == LYXML_ELEMENT);
+ /* get value prefixes, if any */
+ rc = ly_store_prefix_data(xmlctx->ctx, value, value_len, LY_VALUE_XML, &xmlctx->ns, &format, &val_prefix_data);
+ LY_CHECK_GOTO(rc, cleanup);
- /* remember element prefix and name */
- prefix = xmlctx->prefix;
- prefix_len = xmlctx->prefix_len;
- name = xmlctx->name;
- name_len = xmlctx->name_len;
+ /* get NS again, it may have been backed up and restored */
+ ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
+ ns_uri = ns ? ns->uri : NULL;
- /* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ /* get best-effort node hints */
+ lydxml_get_hints_opaq(name, name_len, xmlctx->value, xmlctx->value_len, sibling, ns_uri, &hints, insert_anchor);
- /* get the schema node */
- LY_CHECK_GOTO(ret = lydxml_subtree_snode(lydctx, parent, prefix, prefix_len, name, name_len, &snode, &ext), error);
+ /* create the node without value */
+ rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len, ns_uri, ns_uri ? strlen(ns_uri) : 0, NULL, 0,
+ NULL, format, NULL, hints, node);
+ LY_CHECK_GOTO(rc, cleanup);
- if (!snode && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
- LOGVRB("Skipping parsing of unknown node \"%.*s\".", name_len, name);
+ assert(*node);
+ LOG_LOCSET(NULL, *node, NULL, NULL);
- /* skip element with children */
- LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), error);
- return LY_SUCCESS;
+ /* parser next */
+ rc = lyxml_ctx_next(xmlctx);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* process children */
+ while (xmlctx->status == LYXML_ELEMENT) {
+ rc = lydxml_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
+ LY_CHECK_GOTO(rc, cleanup);
}
- /* create metadata/attributes */
- if (xmlctx->status == LYXML_ATTRIBUTE) {
- if (snode) {
- ret = lydxml_metadata(lydctx, snode, &meta);
- LY_CHECK_GOTO(ret, error);
+ /* update the value */
+ opaq = (struct lyd_node_opaq *)*node;
+ if (opaq->child) {
+ if (!ws_only) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX_XML, "Mixed XML content node \"%s\" found, not supported.", LYD_NAME(opaq));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ } else if (value_len) {
+ lydict_remove(xmlctx->ctx, opaq->value);
+ if (dynamic) {
+ LY_CHECK_GOTO(rc = lydict_insert_zc(xmlctx->ctx, (char *)value, &opaq->value), cleanup);
+ dynamic = 0;
} else {
- assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
- ret = lydxml_attrs(xmlctx, &attr);
- LY_CHECK_GOTO(ret, error);
+ LY_CHECK_GOTO(rc = lydict_insert(xmlctx->ctx, value, value_len, &opaq->value), cleanup);
}
}
- assert(xmlctx->status == LYXML_ELEM_CONTENT);
- if (!snode) {
- assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
+ /* always store val_prefix_data because the format requires them */
+ assert(!opaq->val_prefix_data);
+ opaq->val_prefix_data = val_prefix_data;
+ val_prefix_data = NULL;
- if (xmlctx->ws_only) {
- /* ignore WS-only value */
- if (xmlctx->dynamic) {
- free((char *)xmlctx->value);
- }
- xmlctx->dynamic = 0;
- xmlctx->value = "";
- xmlctx->value_len = 0;
- format = LY_VALUE_XML;
- } else {
- /* get value prefixes */
- ret = ly_store_prefix_data(xmlctx->ctx, xmlctx->value, xmlctx->value_len, LY_VALUE_XML,
- &xmlctx->ns, &format, &val_prefix_data);
- LY_CHECK_GOTO(ret, error);
- }
+cleanup:
+ if (*node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ ly_free_prefix_data(format, val_prefix_data);
+ if (dynamic) {
+ free((char *)value);
+ }
+ if (rc) {
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+ return rc;
+}
- /* get NS again, it may have been backed up and restored */
- ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
- ns_uri = ns ? ns->uri : NULL;
+/**
+ * @brief Parse an XML leaf/leaf-list node.
+ *
+ * @param[in] lydctx XML YANG data parser context.
+ * @param[in] parent Parent node, if any.
+ * @param[in] snode Schema node of the new node.
+ * @param[out] node Created node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_subtree_term(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, const struct lysc_node *snode,
+ struct lyd_node **node)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+ struct lyd_node *anchor;
- /* get best-effort node hints */
- lydxml_get_hints_opaq(name, name_len, xmlctx->value, xmlctx->value_len, parent ? lyd_child(parent) : *first_p,
- ns_uri, &hints, &insert_anchor);
+ *node = NULL;
- /* create node */
- ret = lyd_create_opaq(ctx, name, name_len, prefix, prefix_len, ns_uri, ns_uri ? strlen(ns_uri) : 0,
- xmlctx->value, xmlctx->value_len, &xmlctx->dynamic, format, val_prefix_data, hints, &node);
- LY_CHECK_GOTO(ret, error);
+ /* create node */
+ r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic,
+ LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA, node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
- /* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ if (*node) {
+ LOG_LOCSET(NULL, *node, NULL, NULL);
+ }
- /* process children */
- while (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_subtree_r(lydctx, node, lyd_node_child_p(node), NULL);
- LY_CHECK_GOTO(ret, error);
- }
- } else if (snode->nodetype & LYD_NODE_TERM) {
- /* create node */
- LY_CHECK_GOTO(ret = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, xmlctx->value, xmlctx->value_len,
- &xmlctx->dynamic, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA, &node), error);
- LOG_LOCSET(snode, node, NULL, NULL);
-
- if (parent && (node->schema->flags & LYS_KEY)) {
- /* check the key order, the anchor must never be a key */
- anchor = lyd_insert_get_next_anchor(lyd_child(parent), node);
- if (anchor && anchor->schema && (anchor->schema->flags & LYS_KEY)) {
- if (lydctx->parse_opts & LYD_PARSE_STRICT) {
- LOGVAL(ctx, LYVE_DATA, "Invalid position of the key \"%s\" in a list.", node->schema->name);
- ret = LY_EVALID;
- goto error;
- } else {
- LOGWRN(ctx, "Invalid position of the key \"%s\" in a list.", node->schema->name);
- }
+ if (*node && parent && (snode->flags & LYS_KEY)) {
+ /* check the key order, the anchor must never be a key */
+ anchor = lyd_insert_get_next_anchor(lyd_child(parent), *node);
+ if (anchor && anchor->schema && (anchor->schema->flags & LYS_KEY)) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ LOGVAL(xmlctx->ctx, LYVE_DATA, "Invalid position of the key \"%s\" in a list.", snode->name);
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ } else {
+ LOGWRN(xmlctx->ctx, "Invalid position of the key \"%s\" in a list.", snode->name);
}
}
+ }
- /* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ /* parser next */
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- /* no children expected */
- if (xmlctx->status == LYXML_ELEMENT) {
- LOGVAL(ctx, LYVE_SYNTAX, "Child element \"%.*s\" inside a terminal node \"%s\" found.",
- (int)xmlctx->name_len, xmlctx->name, snode->name);
- ret = LY_EVALID;
- goto error;
- }
- } else if (snode->nodetype & LYD_NODE_INNER) {
- if (!xmlctx->ws_only) {
- /* value in inner node */
- LOGVAL(ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an inner node \"%s\" found.",
- (int)xmlctx->value_len, xmlctx->value, snode->name);
- ret = LY_EVALID;
- goto error;
- }
+ /* no children expected */
+ if (xmlctx->status == LYXML_ELEMENT) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Child element \"%.*s\" inside a terminal node \"%s\" found.",
+ (int)xmlctx->name_len, xmlctx->name, snode->name);
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+
+cleanup:
+ if (*node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse an XML inner node.
+ *
+ * @param[in] lydctx XML YANG data parser context.
+ * @param[in] snode Schema node of the new node.
+ * @param[in] ext Extension instance of @p snode, if any.
+ * @param[out] node Created node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_subtree_inner(struct lyd_xml_ctx *lydctx, const struct lysc_node *snode, const struct lysc_ext_instance *ext,
+ struct lyd_node **node)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+ uint32_t prev_parse_opts = lydctx->parse_opts;
+
+ *node = NULL;
+
+ if (!xmlctx->ws_only) {
+ /* value in inner node */
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an inner node \"%s\" found.",
+ (int)xmlctx->value_len, xmlctx->value, snode->name);
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+
+ /* create node */
+ rc = lyd_create_inner(snode, node);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ assert(*node);
+ LOG_LOCSET(NULL, *node, NULL, NULL);
+
+ /* parser next */
+ rc = lyxml_ctx_next(xmlctx);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if (ext) {
+ /* only parse these extension data and validate afterwards */
+ lydctx->parse_opts |= LYD_PARSE_ONLY;
+ }
+
+ /* process children */
+ while (xmlctx->status == LYXML_ELEMENT) {
+ r = lydxml_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+
+ /* restore options */
+ lydctx->parse_opts = prev_parse_opts;
+
+ if (snode->nodetype == LYS_LIST) {
+ /* check all keys exist */
+ r = lyd_parse_check_keys(*node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
+
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) {
+ /* new node validation, autodelete CANNOT occur (it can if multi-error), all nodes are new */
+ r = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, lydctx->val_opts, NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
+ /* add any missing default children */
+ r = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
+ &lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ }
+
+ if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+ /* rememeber the RPC/action/notification */
+ lydctx->op_node = *node;
+ }
+
+cleanup:
+ if (*node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ lydctx->parse_opts = prev_parse_opts;
+ if (rc && ((*node && !(*node)->hash) || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
+ /* list without keys is unusable or an error */
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse an XML anyxml/anydata node.
+ *
+ * @param[in] lydctx XML YANG data parser context.
+ * @param[in] snode Schema node of the new node.
+ * @param[in] ext Extension instance of @p snode, if any.
+ * @param[out] node Created node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_subtree_any(struct lyd_xml_ctx *lydctx, const struct lysc_node *snode, const struct lysc_ext_instance *ext,
+ struct lyd_node **node)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+ uint32_t prev_parse_opts = lydctx->parse_opts, prev_int_opts = lydctx->int_opts;
+ struct lyd_node *child = NULL;
+ char *val = NULL;
+ ly_bool log_node = 0;
+
+ *node = NULL;
+ if ((snode->nodetype == LYS_ANYDATA) && !xmlctx->ws_only) {
+ /* value in anydata node, we expect a tree */
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an anydata node \"%s\" found.",
+ xmlctx->value_len < 20 ? (int)xmlctx->value_len : 20, xmlctx->value, snode->name);
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+ }
+
+ if (!xmlctx->ws_only) {
+ /* use an arbitrary text value for anyxml */
+ val = strndup(xmlctx->value, xmlctx->value_len);
+ LY_CHECK_ERR_GOTO(!val, LOGMEM(xmlctx->ctx); rc = LY_EMEM, cleanup);
+
+ /* parser next */
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* create node */
+ r = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+ val = NULL;
+ } else {
/* create node */
- ret = lyd_create_inner(snode, &node);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- LOG_LOCSET(snode, node, NULL, NULL);
+ assert(*node);
+ LOG_LOCSET(NULL, *node, NULL, NULL);
+ log_node = 1;
/* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
- prev_parse_opts = lydctx->parse_opts;
- if (ext) {
- /* only parse these extension data and validate afterwards */
- lydctx->parse_opts |= LYD_PARSE_ONLY;
- }
+ /* update options so that generic data can be parsed */
+ lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
+ lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
- /* process children */
+ /* parse any data tree */
while (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_subtree_r(lydctx, node, lyd_node_child_p(node), NULL);
- LY_CHECK_GOTO(ret, error);
+ r = lydxml_subtree_r(lydctx, NULL, &child, NULL);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
- /* restore options */
- lydctx->parse_opts = prev_parse_opts;
+ /* assign the data tree */
+ ((struct lyd_node_any *)*node)->value.tree = child;
+ child = NULL;
+ }
- if (snode->nodetype == LYS_LIST) {
- /* check all keys exist */
- LY_CHECK_GOTO(ret = lyd_parse_check_keys(node), error);
- }
+cleanup:
+ if (log_node) {
+ LOG_LOCBACK(0, 1, 0, 0);
+ }
+ lydctx->parse_opts = prev_parse_opts;
+ lydctx->int_opts = prev_int_opts;
+ free(val);
+ lyd_free_tree(child);
+ if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
+ lyd_free_tree(*node);
+ *node = NULL;
+ }
+ return rc;
+}
- if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
- /* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, NULL);
- LY_CHECK_GOTO(ret, error);
-
- /* add any missing default children */
- ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
- &lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
- LY_CHECK_GOTO(ret, error);
- }
-
- if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
- /* rememeber the RPC/action/notification */
- lydctx->op_node = node;
- }
- } else if (snode->nodetype & LYD_NODE_ANY) {
- if ((snode->nodetype == LYS_ANYDATA) && !xmlctx->ws_only) {
- /* value in anydata node, we expect a tree */
- LOGVAL(ctx, LYVE_SYNTAX, "Text value \"%.*s\" inside an anydata node \"%s\" found.",
- (int)xmlctx->value_len < 20 ? xmlctx->value_len : 20, xmlctx->value, snode->name);
- ret = LY_EVALID;
- goto error;
- }
+/**
+ * @brief Parse an XML subtree, recursively.
+ *
+ * @param[in] lydctx XML YANG data parser context.
+ * @param[in,out] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
+ * @param[in,out] first_p Pointer to the first (@p parent or top-level) child.
+ * @param[in,out] parsed Optional set to add all the parsed siblings into.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
+ struct lyxml_ctx *xmlctx;
+ const struct ly_ctx *ctx;
+ struct lyd_meta *meta = NULL;
+ struct lyd_attr *attr = NULL;
+ const struct lysc_node *snode = NULL;
+ struct lysc_ext_instance *ext = NULL;
+ uint32_t orig_parse_opts;
+ struct lyd_node *node = NULL, *insert_anchor = NULL;
+ ly_bool parse_subtree;
- if (!xmlctx->ws_only) {
- /* use an arbitrary text value for anyxml */
- val = strndup(xmlctx->value, xmlctx->value_len);
- LY_CHECK_ERR_GOTO(!val, LOGMEM(xmlctx->ctx); ret = LY_EMEM, error);
+ assert(parent || first_p);
- /* parser next */
- LY_CHECK_ERR_GOTO(ret = lyxml_ctx_next(xmlctx), free(val), error);
+ xmlctx = lydctx->xmlctx;
+ ctx = xmlctx->ctx;
- /* create node */
- ret = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, &node);
- LY_CHECK_ERR_GOTO(ret, free(val), error);
- } else {
- /* parser next */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
-
- /* update options so that generic data can be parsed */
- prev_parse_opts = lydctx->parse_opts;
- lydctx->parse_opts &= ~LYD_PARSE_STRICT;
- lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0);
- prev_int_opts = lydctx->int_opts;
- lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
-
- /* parse any data tree */
- anchor = NULL;
- while (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_subtree_r(lydctx, NULL, &anchor, NULL);
- if (ret) {
- lyd_free_siblings(anchor);
- break;
- }
- }
+ parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0;
+ /* all descendants should be parsed */
+ lydctx->parse_opts &= ~LYD_PARSE_SUBTREE;
+ orig_parse_opts = lydctx->parse_opts;
+
+ assert(xmlctx->status == LYXML_ELEMENT);
+
+ /* remember element prefix and name */
+ prefix = xmlctx->prefix;
+ prefix_len = xmlctx->prefix_len;
+ name = xmlctx->name;
+ name_len = xmlctx->name_len;
+
+ /* parser next */
+ rc = lyxml_ctx_next(xmlctx);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if ((lydctx->int_opts & LYD_INTOPT_EVENTTIME) && !parent && name_len && !prefix_len &&
+ !ly_strncmp("eventTime", name, name_len)) {
+ /* parse eventTime, create node */
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len,
+ "urn:ietf:params:xml:ns:netconf:notification:1.0", 47, xmlctx->value,
+ xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_VALUE_XML, NULL, LYD_HINT_DATA, &node);
+ LY_CHECK_GOTO(rc, cleanup);
- /* restore options */
- lydctx->parse_opts = prev_parse_opts;
- lydctx->int_opts = prev_int_opts;
+ /* validate the value */
+ r = lyd_parser_notif_eventtime_validate(node);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
- LY_CHECK_GOTO(ret, error);
+ /* parser next */
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
+ if (xmlctx->status != LYXML_ELEM_CLOSE) {
+ LOGVAL(ctx, LYVE_DATA, "Unexpected notification \"eventTime\" node children.");
+ rc = LY_EVALID;
+ lyd_free_tree(node);
+ goto cleanup;
+ }
+
+ goto node_parsed;
+ }
+
+ /* get the schema node */
+ r = lydxml_subtree_get_snode(lydctx, parent, prefix, prefix_len, name, name_len, &snode, &ext);
+ if (r) {
+ rc = r;
+ if ((r == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) {
+ /* skip the invalid data */
+ if ((r = lydxml_data_skip(xmlctx))) {
+ rc = r;
+ }
+ }
+ goto cleanup;
+ } else if (!snode && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
+ LOGVRB("Skipping parsing of unknown node \"%.*s\".", (int)name_len, name);
- /* create node */
- ret = lyd_create_any(snode, anchor, LYD_ANYDATA_DATATREE, 1, &node);
- LY_CHECK_GOTO(ret, error);
+ /* skip element with children */
+ rc = lydxml_data_skip(xmlctx);
+ goto cleanup;
+ }
+
+ /* create metadata/attributes */
+ if (xmlctx->status == LYXML_ATTRIBUTE) {
+ if (snode) {
+ rc = lydxml_metadata(lydctx, snode, &meta);
+ LY_CHECK_GOTO(rc, cleanup);
+ } else {
+ assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
+ rc = lydxml_attrs(xmlctx, &attr);
+ LY_CHECK_GOTO(rc, cleanup);
}
}
- assert(node);
- if (snode) {
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ if (!snode) {
+ /* opaque */
+ r = lydxml_subtree_opaq(lydctx, parent ? lyd_child(parent) : *first_p, prefix, prefix_len, name, name_len,
+ &insert_anchor, &node);
+ } else if (snode->nodetype & LYD_NODE_TERM) {
+ /* term */
+ r = lydxml_subtree_term(lydctx, parent, snode, &node);
+ } else if (snode->nodetype & LYD_NODE_INNER) {
+ /* inner */
+ r = lydxml_subtree_inner(lydctx, snode, ext, &node);
+ } else {
+ /* any */
+ assert(snode->nodetype & LYD_NODE_ANY);
+ r = lydxml_subtree_any(lydctx, snode, ext, &node);
+ }
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
+node_parsed:
+ if (node && snode) {
/* add/correct flags */
- LY_CHECK_GOTO(ret = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext), error);
+ r = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
/* store for ext instance node validation, if needed */
- LY_CHECK_GOTO(ret = lyd_validate_node_ext(node, &lydctx->ext_node), error);
+ r = lyd_validate_node_ext(node, &lydctx->ext_node);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
}
/* parser next */
assert(xmlctx->status == LYXML_ELEM_CLOSE);
if (!parse_subtree) {
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
+ r = lyxml_ctx_next(xmlctx);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
}
+ LY_CHECK_GOTO(!node, cleanup);
+
/* add metadata/attributes */
if (snode) {
lyd_insert_meta(node, meta, 0);
+ meta = NULL;
} else {
lyd_insert_attr(node, attr);
+ attr = NULL;
}
/* insert, keep first pointer correct */
if (insert_anchor) {
lyd_insert_after(insert_anchor, node);
} else if (ext) {
- LY_CHECK_GOTO(ret = lyplg_ext_insert(parent, node), error);
+ r = lyplg_ext_insert(parent, node);
+ LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
} else {
lyd_insert_node(parent, first_p, node, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
}
@@ -895,17 +1132,11 @@ lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd
ly_set_add(parsed, node, 1, NULL);
}
+cleanup:
lydctx->parse_opts = orig_parse_opts;
- LOG_LOCBACK(node ? 1 : 0, node ? 1 : 0, 0, 0);
- return LY_SUCCESS;
-
-error:
- lydctx->parse_opts = orig_parse_opts;
- LOG_LOCBACK(node ? 1 : 0, node ? 1 : 0, 0, 0);
lyd_free_meta_siblings(meta);
lyd_free_attr_siblings(ctx, attr);
- lyd_free_tree(node);
- return ret;
+ return rc;
}
/**
@@ -929,7 +1160,11 @@ lydxml_envelope(struct lyxml_ctx *xmlctx, const char *name, const char *uri, ly_
const char *prefix;
size_t prefix_len;
- assert(xmlctx->status == LYXML_ELEMENT);
+ if (xmlctx->status != LYXML_ELEMENT) {
+ /* nothing to parse */
+ return LY_ENOT;
+ }
+
if (ly_strncmp(name, xmlctx->name, xmlctx->name_len)) {
/* not the expected element */
return LY_ENOT;
@@ -987,9 +1222,10 @@ lyd_parse_xml(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
{
- LY_ERR rc = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
struct lyd_xml_ctx *lydctx;
- ly_bool parsed_data_nodes = 0;
+ ly_bool parsed_data_nodes = 0, close_elem = 0;
+ struct lyd_node *act = NULL;
enum LYXML_PARSER_STATUS status;
assert(ctx && in && lydctx_p);
@@ -1009,9 +1245,19 @@ lyd_parse_xml(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
/* find the operation node if it exists already */
LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+ if ((int_opts & LYD_INTOPT_RPC) && (int_opts & LYD_INTOPT_ACTION)) {
+ /* can be either, try to parse "action" */
+ if (!lydxml_envelope(lydctx->xmlctx, "action", "urn:ietf:params:xml:ns:yang:1", 0, &act)) {
+ close_elem = 1;
+ int_opts &= ~LYD_INTOPT_RPC;
+ }
+ }
+
/* parse XML data */
while (lydctx->xmlctx->status == LYXML_ELEMENT) {
- LY_CHECK_GOTO(rc = lydxml_subtree_r(lydctx, parent, first_p, parsed), cleanup);
+ r = lydxml_subtree_r(lydctx, parent, first_p, parsed);
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
+
parsed_data_nodes = 1;
if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
@@ -1019,16 +1265,29 @@ lyd_parse_xml(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
}
}
+ /* close an opened element */
+ if (close_elem) {
+ if (lydctx->xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(lydctx->xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(lydctx->xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\".",
+ (int)lydctx->xmlctx->name_len, lydctx->xmlctx->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(lydctx->xmlctx), cleanup);
+ }
+
/* check final state */
if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && (lydctx->xmlctx->status == LYXML_ELEMENT)) {
LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
- rc = LY_EVALID;
- goto cleanup;
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
- rc = LY_EVALID;
- goto cleanup;
+ r = LY_EVALID;
+ LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
if (!parsed_data_nodes) {
@@ -1051,7 +1310,8 @@ cleanup:
assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
!lydctx->node_when.count));
- if (rc) {
+ lyd_free_tree(act);
+ if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
lyd_xml_ctx_free((struct lyd_ctx *)lydctx);
} else {
*lydctx_p = (struct lyd_ctx *)lydctx;
@@ -1081,12 +1341,6 @@ lydxml_env_netconf_rpc(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_
assert(envp && !*envp);
- if (xmlctx->status != LYXML_ELEMENT) {
- /* nothing to parse */
- assert(xmlctx->status == LYXML_END);
- goto cleanup;
- }
-
/* parse "rpc" */
r = lydxml_envelope(xmlctx, "rpc", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, envp);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
@@ -1118,123 +1372,6 @@ cleanup:
}
/**
- * @brief Validate eventTime date-and-time value.
- *
- * @param[in] node Opaque eventTime node.
- * @return LY_SUCCESS on success.
- * @return LY_ERR value on error.
- */
-static LY_ERR
-lydxml_env_netconf_eventtime_validate(const struct lyd_node *node)
-{
- LY_ERR rc = LY_SUCCESS;
- struct ly_ctx *ctx = (struct ly_ctx *)LYD_CTX(node);
- struct lysc_ctx cctx;
- const struct lys_module *mod;
- LY_ARRAY_COUNT_TYPE u;
- struct ly_err_item *err = NULL;
- struct lysp_type *type_p = NULL;
- struct lysc_pattern **patterns = NULL;
- const char *value;
-
- LYSC_CTX_INIT_CTX(cctx, ctx);
-
- /* get date-and-time parsed type */
- mod = ly_ctx_get_module_latest(ctx, "ietf-yang-types");
- assert(mod);
- LY_ARRAY_FOR(mod->parsed->typedefs, u) {
- if (!strcmp(mod->parsed->typedefs[u].name, "date-and-time")) {
- type_p = &mod->parsed->typedefs[u].type;
- break;
- }
- }
- assert(type_p);
-
- /* compile patterns */
- assert(type_p->patterns);
- LY_CHECK_GOTO(rc = lys_compile_type_patterns(&cctx, type_p->patterns, NULL, &patterns), cleanup);
-
- /* validate */
- value = lyd_get_value(node);
- rc = lyplg_type_validate_patterns(patterns, value, strlen(value), &err);
-
-cleanup:
- FREE_ARRAY(&cctx.free_ctx, patterns, lysc_pattern_free);
- if (rc && err) {
- LOGVAL_ERRITEM(ctx, err);
- ly_err_free(err);
- LOGVAL(ctx, LYVE_DATA, "Invalid \"eventTime\" in the notification.");
- }
- return rc;
-}
-
-/**
- * @brief Parse all expected non-data XML elements of a NETCONF notification message.
- *
- * @param[in] xmlctx XML parser context.
- * @param[out] evnp Parsed envelope(s) (opaque node).
- * @param[out] int_opts Internal options for parsing the rest of YANG data.
- * @param[out] close_elem Number of parsed opened elements that need to be closed.
- * @return LY_SUCCESS on success.
- * @return LY_ERR value on error.
- */
-static LY_ERR
-lydxml_env_netconf_notif(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
-{
- LY_ERR rc = LY_SUCCESS, r;
- struct lyd_node *child;
-
- assert(envp && !*envp);
-
- if (xmlctx->status != LYXML_ELEMENT) {
- /* nothing to parse */
- assert(xmlctx->status == LYXML_END);
- goto cleanup;
- }
-
- /* parse "notification" */
- r = lydxml_envelope(xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, envp);
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* parse "eventTime" */
- r = lydxml_envelope(xmlctx, "eventTime", "urn:ietf:params:xml:ns:netconf:notification:1.0", 1, &child);
- if (r == LY_ENOT) {
- LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unexpected element \"%.*s\" instead of \"eventTime\".",
- (int)xmlctx->name_len, xmlctx->name);
- r = LY_EVALID;
- }
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* insert */
- lyd_insert_node(*envp, NULL, child, 0);
-
- /* validate value */
- r = lydxml_env_netconf_eventtime_validate(child);
- LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
-
- /* finish child parsing */
- if (xmlctx->status != LYXML_ELEM_CLOSE) {
- assert(xmlctx->status == LYXML_ELEMENT);
- LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"eventTime\".",
- (int)xmlctx->name_len, xmlctx->name);
- rc = LY_EVALID;
- goto cleanup;
- }
- LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
-
- /* NETCONF notification */
- *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_NOTIF;
- *close_elem = 1;
-
-cleanup:
- if (rc) {
- lyd_free_tree(*envp);
- *envp = NULL;
- }
- return rc;
-}
-
-/**
* @brief Parse an XML element as an opaque node subtree.
*
* @param[in] xmlctx XML parser context.
@@ -1247,9 +1384,11 @@ lydxml_opaq_r(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
LY_ERR rc = LY_SUCCESS;
const struct lyxml_ns *ns;
struct lyd_attr *attr = NULL;
- struct lyd_node *child = NULL;
- const char *name, *prefix;
- size_t name_len, prefix_len;
+ struct lyd_node *node = NULL;
+ struct lyd_node_opaq *opaq;
+ const char *name, *prefix, *value = NULL;
+ size_t name_len, prefix_len, value_len;
+ ly_bool ws_only, dynamic = 0;
assert(xmlctx->status == LYXML_ELEMENT);
@@ -1270,14 +1409,23 @@ lydxml_opaq_r(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
LY_CHECK_RET(lydxml_attrs(xmlctx, &attr));
}
- /* create node */
+ /* remember the value */
assert(xmlctx->status == LYXML_ELEM_CONTENT);
- rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len, ns->uri, strlen(ns->uri), xmlctx->value,
- xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_VALUE_XML, NULL, 0, &child);
+ value = xmlctx->value;
+ value_len = xmlctx->value_len;
+ ws_only = xmlctx->ws_only;
+ dynamic = xmlctx->dynamic;
+ if (dynamic) {
+ xmlctx->dynamic = 0;
+ }
+
+ /* create the node without value */
+ rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len, ns->uri, strlen(ns->uri), NULL, 0, NULL,
+ LY_VALUE_XML, NULL, 0, &node);
LY_CHECK_GOTO(rc, cleanup);
/* assign atributes */
- ((struct lyd_node_opaq *)child)->attr = attr;
+ ((struct lyd_node_opaq *)node)->attr = attr;
attr = NULL;
/* parser next element */
@@ -1285,17 +1433,38 @@ lydxml_opaq_r(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
/* parse all the descendants */
while (xmlctx->status == LYXML_ELEMENT) {
- rc = lydxml_opaq_r(xmlctx, child);
+ rc = lydxml_opaq_r(xmlctx, node);
LY_CHECK_GOTO(rc, cleanup);
}
/* insert */
- lyd_insert_node(parent, NULL, child, 1);
+ lyd_insert_node(parent, NULL, node, 1);
+
+ /* update the value */
+ opaq = (struct lyd_node_opaq *)node;
+ if (opaq->child) {
+ if (!ws_only) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX_XML, "Mixed XML content node \"%s\" found, not supported.", LYD_NAME(node));
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ } else if (value_len) {
+ lydict_remove(xmlctx->ctx, opaq->value);
+ if (dynamic) {
+ LY_CHECK_GOTO(rc = lydict_insert_zc(xmlctx->ctx, (char *)value, &opaq->value), cleanup);
+ dynamic = 0;
+ } else {
+ LY_CHECK_GOTO(rc = lydict_insert(xmlctx->ctx, value, value_len, &opaq->value), cleanup);
+ }
+ }
cleanup:
lyd_free_attr_siblings(xmlctx->ctx, attr);
+ if (dynamic) {
+ free((char *)value);
+ }
if (rc) {
- lyd_free_tree(child);
+ lyd_free_tree(node);
}
return rc;
}
@@ -1607,12 +1776,6 @@ lydxml_env_netconf_reply(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint3
assert(envp && !*envp);
- if (xmlctx->status != LYXML_ELEMENT) {
- /* nothing to parse */
- assert(xmlctx->status == LYXML_END);
- goto cleanup;
- }
-
/* parse "rpc-reply" */
r = lydxml_envelope(xmlctx, "rpc-reply", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, envp);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
@@ -1702,15 +1865,13 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *
{
LY_ERR rc = LY_SUCCESS;
struct lyd_xml_ctx *lydctx;
+ struct lyd_node *node;
uint32_t i, int_opts = 0, close_elem = 0;
ly_bool parsed_data_nodes = 0;
assert(ctx && in && lydctx_p);
assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
-
- assert((data_type == LYD_TYPE_RPC_NETCONF) || (data_type == LYD_TYPE_NOTIF_NETCONF) ||
- (data_type == LYD_TYPE_REPLY_NETCONF));
assert(!(parse_opts & LYD_PARSE_SUBTREE));
/* init context */
@@ -1733,11 +1894,17 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *
break;
case LYD_TYPE_NOTIF_NETCONF:
assert(!parent);
- rc = lydxml_env_netconf_notif(lydctx->xmlctx, envp, &int_opts, &close_elem);
+
+ /* parse "notification" */
+ rc = lydxml_envelope(lydctx->xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, envp);
if (rc == LY_ENOT) {
LOGVAL(ctx, LYVE_DATA, "Missing NETCONF <notification> envelope or in incorrect namespace.");
}
LY_CHECK_GOTO(rc, cleanup);
+
+ /* NETCONF notification */
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_NOTIF | LYD_INTOPT_EVENTTIME;
+ close_elem = 1;
break;
case LYD_TYPE_REPLY_NETCONF:
assert(parent);
@@ -1747,6 +1914,32 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *
}
LY_CHECK_GOTO(rc, cleanup);
break;
+ case LYD_TYPE_RPC_RESTCONF:
+ assert(parent);
+
+ /* parse "input" */
+ rc = lydxml_envelope(lydctx->xmlctx, "input", lyd_node_module(parent)->ns, 0, envp);
+ if (rc == LY_ENOT) {
+ LOGVAL(ctx, LYVE_DATA, "Missing RESTCONF \"input\" object or in incorrect namespace.");
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
+ close_elem = 1;
+ break;
+ case LYD_TYPE_REPLY_RESTCONF:
+ assert(parent);
+
+ /* parse "output" */
+ rc = lydxml_envelope(lydctx->xmlctx, "output", lyd_node_module(parent)->ns, 0, envp);
+ if (rc == LY_ENOT) {
+ LOGVAL(ctx, LYVE_DATA, "Missing RESTCONF \"output\" object or in incorrect namespace.");
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ int_opts = LYD_INTOPT_WITH_SIBLINGS | LYD_INTOPT_REPLY;
+ close_elem = 1;
+ break;
default:
LOGINT(ctx);
rc = LY_EINT;
@@ -1792,6 +1985,21 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *
rc = LY_EVALID;
goto cleanup;
}
+ if (int_opts & LYD_INTOPT_EVENTTIME) {
+ /* parse as a child of the envelope */
+ node = (*first_p)->prev;
+ if (node->schema) {
+ LOGVAL(ctx, LYVE_DATA, "Missing notification \"eventTime\" node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ } else {
+ /* can be the only opaque node and an operation had to be parsed */
+ assert(!strcmp(LYD_NAME(node), "eventTime") && (*first_p)->next);
+ lyd_unlink(node);
+ assert(*envp);
+ lyd_insert_child(*envp, node);
+ }
+ }
if (!parsed_data_nodes) {
/* no data nodes were parsed */
diff --git a/src/parser_yang.c b/src/parser_yang.c
index dd84480..e18ed17 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -416,10 +416,14 @@ read_qstring(struct lysp_yang_ctx *ctx, enum yang_arg arg, char **word_p, char *
}
break;
case '\r':
- if (ctx->in->current[1] != '\n') {
+ /* newline may be escaped */
+ if ((ctx->in->current[1] != '\n') && strncmp(&ctx->in->current[1], "\\n", 2)) {
LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, ctx->in->current[0]);
return LY_EVALID;
}
+
+ /* skip this character, do not store it */
+ ++ctx->in->current;
/* fallthrough */
case '\n':
if (block_indent) {
@@ -804,7 +808,8 @@ keyword_start:
MOVE_INPUT(ctx, 1);
goto extension;
case '{':
- /* allowed only for input and output statements which can be without arguments */
+ case ';':
+ /* allowed only for input and output statements which are without arguments */
if ((*kw == LY_STMT_INPUT) || (*kw == LY_STMT_OUTPUT)) {
break;
}
@@ -932,7 +937,8 @@ parse_ext(struct lysp_yang_ctx *ctx, const char *ext_name, size_t ext_name_len,
LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *exts, e, LY_EMEM);
if (!ly_strnchr(ext_name, ':', ext_name_len)) {
- LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%*.s\" without the mandatory prefix.", ext_name_len, ext_name);
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%.*s\" without the mandatory prefix.",
+ (int)ext_name_len, ext_name);
return LY_EVALID;
}
@@ -1039,7 +1045,7 @@ parse_yangversion(struct lysp_yang_ctx *ctx, struct lysp_module *mod)
} else if ((word_len == ly_strlen_const("1.1")) && !strncmp(word, "1.1", word_len)) {
mod->version = LYS_VERSION_1_1;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "yang-version");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "yang-version");
free(buf);
return LY_EVALID;
}
@@ -1450,7 +1456,7 @@ parse_config(struct lysp_yang_ctx *ctx, uint16_t *flags, struct lysp_ext_instanc
} else if ((word_len == ly_strlen_const("false")) && !strncmp(word, "false", word_len)) {
*flags |= LYS_CONFIG_R;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "config");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "config");
free(buf);
return LY_EVALID;
}
@@ -1501,7 +1507,7 @@ parse_mandatory(struct lysp_yang_ctx *ctx, uint16_t *flags, struct lysp_ext_inst
} else if ((word_len == ly_strlen_const("false")) && !strncmp(word, "false", word_len)) {
*flags |= LYS_MAND_FALSE;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "mandatory");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "mandatory");
free(buf);
return LY_EVALID;
}
@@ -1622,7 +1628,7 @@ parse_status(struct lysp_yang_ctx *ctx, uint16_t *flags, struct lysp_ext_instanc
} else if ((word_len == ly_strlen_const("obsolete")) && !strncmp(word, "obsolete", word_len)) {
*flags |= LYS_STATUS_OBSLT;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "status");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "status");
free(buf);
return LY_EVALID;
}
@@ -1803,7 +1809,7 @@ parse_type_enum_value_pos(struct lysp_yang_ctx *ctx, enum ly_stmt val_kw, struct
LY_CHECK_RET(get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len));
if (!word_len || (word[0] == '+') || ((word[0] == '0') && (word_len > 1)) || ((val_kw == LY_STMT_POSITION) && !strncmp(word, "-0", 2))) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, lyplg_ext_stmt2str(val_kw));
ret = LY_EVALID;
goto cleanup;
}
@@ -1812,26 +1818,26 @@ parse_type_enum_value_pos(struct lysp_yang_ctx *ctx, enum ly_stmt val_kw, struct
if (val_kw == LY_STMT_VALUE) {
num = strtoll(word, &ptr, LY_BASE_DEC);
if ((num < INT64_C(-2147483648)) || (num > INT64_C(2147483647))) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, lyplg_ext_stmt2str(val_kw));
ret = LY_EVALID;
goto cleanup;
}
} else {
unum = strtoull(word, &ptr, LY_BASE_DEC);
if (unum > UINT64_C(4294967295)) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, lyplg_ext_stmt2str(val_kw));
ret = LY_EVALID;
goto cleanup;
}
}
/* we have not parsed the whole argument */
if ((size_t)(ptr - word) != word_len) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, lyplg_ext_stmt2str(val_kw));
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, lyplg_ext_stmt2str(val_kw));
ret = LY_EVALID;
goto cleanup;
}
if (errno == ERANGE) {
- LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, lyplg_ext_stmt2str(val_kw));
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, (int)word_len, word, lyplg_ext_stmt2str(val_kw));
ret = LY_EVALID;
goto cleanup;
}
@@ -1953,7 +1959,7 @@ parse_type_fracdigits(struct lysp_yang_ctx *ctx, struct lysp_type *type)
LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
if (!word_len || (word[0] == '0') || !isdigit(word[0])) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "fraction-digits");
ret = LY_EVALID;
goto cleanup;
}
@@ -1962,12 +1968,12 @@ parse_type_fracdigits(struct lysp_yang_ctx *ctx, struct lysp_type *type)
num = strtoull(word, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
if ((size_t)(ptr - word) != word_len) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "fraction-digits");
ret = LY_EVALID;
goto cleanup;
}
if ((errno == ERANGE) || (num > LY_TYPE_DEC64_FD_MAX)) {
- LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "fraction-digits");
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, (int)word_len, word, "fraction-digits");
ret = LY_EVALID;
goto cleanup;
}
@@ -2018,7 +2024,7 @@ parse_type_reqinstance(struct lysp_yang_ctx *ctx, struct lysp_type *type)
if ((word_len == ly_strlen_const("true")) && !strncmp(word, "true", word_len)) {
type->require_instance = 1;
} else if ((word_len != ly_strlen_const("false")) || strncmp(word, "false", word_len)) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "require-instance");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "require-instance");
ret = LY_EVALID;
goto cleanup;
}
@@ -2065,7 +2071,7 @@ parse_type_pattern_modifier(struct lysp_yang_ctx *ctx, struct lysp_restr *restr)
LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
if ((word_len != ly_strlen_const("invert-match")) || strncmp(word, "invert-match", word_len)) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "modifier");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "modifier");
ret = LY_EVALID;
goto cleanup;
}
@@ -2395,7 +2401,7 @@ parse_maxelements(struct lysp_yang_ctx *ctx, uint32_t *max, uint16_t *flags, str
LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
if (!word_len || (word[0] == '0') || ((word[0] != 'u') && !isdigit(word[0]))) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "max-elements");
ret = LY_EVALID;
goto cleanup;
}
@@ -2405,12 +2411,12 @@ parse_maxelements(struct lysp_yang_ctx *ctx, uint32_t *max, uint16_t *flags, str
num = strtoull(word, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
if ((size_t)(ptr - word) != word_len) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "max-elements");
ret = LY_EVALID;
goto cleanup;
}
if ((errno == ERANGE) || (num > UINT32_MAX)) {
- LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "max-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, (int)word_len, word, "max-elements");
ret = LY_EVALID;
goto cleanup;
}
@@ -2467,7 +2473,7 @@ parse_minelements(struct lysp_yang_ctx *ctx, uint32_t *min, uint16_t *flags, str
LY_CHECK_GOTO(ret = get_argument(ctx, Y_STR_ARG, NULL, &word, &buf, &word_len), cleanup);
if (!word_len || !isdigit(word[0]) || ((word[0] == '0') && (word_len > 1))) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "min-elements");
ret = LY_EVALID;
goto cleanup;
}
@@ -2476,12 +2482,12 @@ parse_minelements(struct lysp_yang_ctx *ctx, uint32_t *min, uint16_t *flags, str
num = strtoull(word, &ptr, LY_BASE_DEC);
/* we have not parsed the whole argument */
if ((size_t)(ptr - word) != word_len) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "min-elements");
ret = LY_EVALID;
goto cleanup;
}
if ((errno == ERANGE) || (num > UINT32_MAX)) {
- LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "min-elements");
+ LOGVAL_PARSER(ctx, LY_VCODE_OOB, (int)word_len, word, "min-elements");
ret = LY_EVALID;
goto cleanup;
}
@@ -2533,7 +2539,7 @@ parse_orderedby(struct lysp_yang_ctx *ctx, struct lysp_node *llist)
} else if ((word_len == ly_strlen_const("user")) && !strncmp(word, "user", word_len)) {
llist->flags |= LYS_ORDBY_USER;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "ordered-by");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "ordered-by");
ret = LY_EVALID;
goto cleanup;
}
@@ -2860,11 +2866,6 @@ parse_inout(struct lysp_yang_ctx *ctx, enum ly_stmt inout_kw, struct lysp_node *
YANG_READ_SUBSTMT_NEXT_ITER(ctx, kw, word, word_len, inout_p->exts, ret, cleanup);
}
- if (!inout_p->child) {
- LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "data-def-stmt", lyplg_ext_stmt2str(inout_kw));
- return LY_EVALID;
- }
-
cleanup:
return ret;
}
@@ -3715,7 +3716,7 @@ parse_yinelement(struct lysp_yang_ctx *ctx, struct lysp_ext *ext)
} else if ((word_len == ly_strlen_const("false")) && !strncmp(word, "false", word_len)) {
ext->flags |= LYS_YINELEM_FALSE;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "yin-element");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "yin-element");
free(buf);
return LY_EVALID;
}
@@ -3868,7 +3869,7 @@ parse_deviate(struct lysp_yang_ctx *ctx, struct lysp_deviate **deviates)
} else if ((word_len == ly_strlen_const("delete")) && !strncmp(word, "delete", word_len)) {
dev_mod = LYS_DEV_DELETE;
} else {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "deviate");
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)word_len, word, "deviate");
ret = LY_EVALID;
goto cleanup;
}
diff --git a/src/parser_yin.c b/src/parser_yin.c
index fa44968..36b49f1 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -488,7 +488,7 @@ yin_parse_attribute(struct lysp_yin_ctx *ctx, enum yin_argument arg_type, const
INSERT_STRING_RET(ctx->xmlctx->ctx, ctx->xmlctx->value, ctx->xmlctx->value_len, ctx->xmlctx->dynamic, *arg_val);
LY_CHECK_RET(!(*arg_val), LY_EMEM);
} else {
- LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_ATTR, ctx->xmlctx->name_len,
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_ATTR, (int)ctx->xmlctx->name_len,
ctx->xmlctx->name, lyplg_ext_stmt2str(current_element));
return LY_EVALID;
}
@@ -3185,7 +3185,7 @@ yin_parse_extension_instance_arg(struct lysp_yin_ctx *ctx, enum ly_stmt parent_s
ctx->xmlctx->prefix_len, parent_stmt);
if (((parent_stmt == LY_STMT_ERROR_MESSAGE) && (child != LY_STMT_ARG_VALUE)) ||
((parent_stmt != LY_STMT_ERROR_MESSAGE) && (child != LY_STMT_ARG_TEXT))) {
- LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, ctx->xmlctx->name_len, ctx->xmlctx->name,
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, (int)ctx->xmlctx->name_len, ctx->xmlctx->name,
lyplg_ext_stmt2str(parent_stmt));
return LY_EVALID;
}
@@ -3256,7 +3256,7 @@ yin_parse_element_generic(struct lysp_yin_ctx *ctx, enum ly_stmt parent_stmt, st
last = (*element)->child;
if ((*element)->kw == LY_STMT_NONE) {
/* unrecognized element */
- LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, ctx->xmlctx->name_len, ctx->xmlctx->name,
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, (int)ctx->xmlctx->name_len, ctx->xmlctx->name,
lyplg_ext_stmt2str(parent_stmt));
ret = LY_EVALID;
goto cleanup;
@@ -3422,7 +3422,7 @@ yin_parse_extension_instance(struct lysp_yin_ctx *ctx, const void *parent, enum
}
} else if (ctx->xmlctx->value_len) {
/* invalid text content */
- LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%s\" with unexpected text content \".*s\".", ext_name,
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Extension instance \"%s\" with unexpected text content \"%.*s\".", ext_name,
(int)ctx->xmlctx->value_len, ctx->xmlctx->value);
return LY_EVALID;
}
@@ -3479,7 +3479,7 @@ yin_parse_content(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info,
if ((parent_stmt == LY_STMT_DEVIATE) && isdevsub(cur_stmt)) {
LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_INDEV_YIN, lyplg_ext_stmt2str(cur_stmt));
} else {
- LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, ctx->xmlctx->name_len,
+ LOGVAL_PARSER((struct lysp_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, (int)ctx->xmlctx->name_len,
ctx->xmlctx->name, lyplg_ext_stmt2str(parent_stmt));
}
ret = LY_EVALID;
@@ -3718,7 +3718,6 @@ yin_parse_content(struct lysp_yin_ctx *ctx, struct yin_subelement *subelem_info,
ret = LY_EINT;
}
LY_CHECK_GOTO(ret, cleanup);
- subelem = NULL;
LY_CHECK_GOTO(ret = lyxml_ctx_next(ctx->xmlctx), cleanup);
}
diff --git a/src/path.c b/src/path.c
index 72e5fb5..73883d7 100644
--- a/src/path.c
+++ b/src/path.c
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Path functions
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -11,6 +11,9 @@
*
* https://opensource.org/licenses/BSD-3-Clause
*/
+
+#define _GNU_SOURCE
+
#include "path.h"
#include <assert.h>
@@ -45,7 +48,7 @@
*/
static LY_ERR
ly_path_check_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lyxp_expr *exp,
- uint32_t *tok_idx, uint8_t prefix, uint8_t pred)
+ uint32_t *tok_idx, uint16_t prefix, uint16_t pred)
{
LY_ERR ret = LY_SUCCESS;
struct ly_set *set = NULL;
@@ -106,8 +109,19 @@ ly_path_check_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_no
/* '=' */
LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error);
- /* Literal or Number */
- LY_CHECK_GOTO(lyxp_next_token2(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL, LYXP_TOKEN_NUMBER), token_error);
+ /* fill repeat */
+ exp->repeat[*tok_idx - 2] = calloc(2, sizeof *exp->repeat[*tok_idx]);
+ LY_CHECK_ERR_GOTO(!exp->repeat[*tok_idx - 2], LOGMEM(NULL); ret = LY_EMEM, cleanup);
+ exp->repeat[*tok_idx - 2][0] = LYXP_EXPR_EQUALITY;
+
+ /* Literal, Number, or VariableReference */
+ if (lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_LITERAL) &&
+ lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_NUMBER) &&
+ lyxp_next_token(NULL, exp, tok_idx, LYXP_TOKEN_VARREF)) {
+ /* error */
+ lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL);
+ goto token_error;
+ }
/* ']' */
LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_BRACK2), token_error);
@@ -121,6 +135,11 @@ ly_path_check_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_no
/* '=' */
LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error);
+ /* fill repeat */
+ exp->repeat[*tok_idx - 2] = calloc(2, sizeof *exp->repeat[*tok_idx]);
+ LY_CHECK_ERR_GOTO(!exp->repeat[*tok_idx - 2], LOGMEM(NULL); ret = LY_EMEM, cleanup);
+ exp->repeat[*tok_idx - 2][0] = LYXP_EXPR_EQUALITY;
+
/* Literal or Number */
LY_CHECK_GOTO(lyxp_next_token2(ctx, exp, tok_idx, LYXP_TOKEN_LITERAL, LYXP_TOKEN_NUMBER), token_error);
@@ -178,6 +197,11 @@ ly_path_check_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_no
/* '=' */
LY_CHECK_GOTO(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_OPER_EQUAL), token_error);
+ /* fill repeat */
+ exp->repeat[*tok_idx - 2] = calloc(2, sizeof *exp->repeat[*tok_idx]);
+ LY_CHECK_ERR_GOTO(!exp->repeat[*tok_idx - 2], LOGMEM(NULL); ret = LY_EMEM, cleanup);
+ exp->repeat[*tok_idx - 2][0] = LYXP_EXPR_EQUALITY;
+
/* FuncName */
LY_CHECK_GOTO(lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_FUNCNAME), token_error);
if ((exp->tok_len[*tok_idx] != ly_strlen_const("current")) ||
@@ -240,45 +264,121 @@ token_error:
return LY_EVALID;
}
+/**
+ * @brief Parse deref XPath function and perform all additional checks.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Optional context node, used for logging.
+ * @param[in] exp Parsed path.
+ * @param[in,out] tok_idx Index in @p exp, is adjusted.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_parse_deref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const struct lyxp_expr *exp,
+ uint32_t *tok_idx)
+{
+ size_t arg_len;
+ uint32_t begin_token, end_token;
+ struct lyxp_expr *arg_expr = NULL;
+
+ /* mandatory FunctionName */
+ LY_CHECK_RET(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_FUNCNAME), LY_EVALID);
+ if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
+ LOGVAL(ctx, LYVE_XPATH, "Unexpected XPath function \"%.*s\" in path, expected \"deref(...)\"",
+ exp->tok_len[*tok_idx], exp->expr + exp->tok_pos[*tok_idx]);
+ return LY_EVALID;
+ }
+
+ /* mandatory '(' */
+ LY_CHECK_RET(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR1), LY_EVALID);
+ begin_token = *tok_idx;
+
+ /* count tokens till ')' */
+ while (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_PAR2) && *tok_idx < exp->used) {
+ /* emebedded functions are not allowed */
+ if (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_FUNCNAME)) {
+ LOGVAL(ctx, LYVE_XPATH, "Embedded function XPath function inside deref function within the path"
+ "is not allowed");
+ return LY_EVALID;
+ }
+
+ (*tok_idx)++;
+ }
+
+ /* mandatory ')' */
+ LY_CHECK_RET(lyxp_next_token(ctx, exp, tok_idx, LYXP_TOKEN_PAR2), LY_EVALID);
+ end_token = *tok_idx - 1;
+
+ /* parse the path of deref argument */
+ arg_len = exp->tok_pos[end_token] - exp->tok_pos[begin_token];
+ LY_CHECK_RET(ly_path_parse(ctx, ctx_node, &exp->expr[exp->tok_pos[begin_token]], arg_len, 1,
+ LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &arg_expr), LY_EVALID);
+ lyxp_expr_free(ctx, arg_expr);
+
+ return LY_SUCCESS;
+}
+
LY_ERR
ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *str_path, size_t path_len,
- ly_bool lref, uint8_t begin, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr)
+ ly_bool lref, uint16_t begin, uint16_t prefix, uint16_t pred, struct lyxp_expr **expr)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *exp = NULL;
uint32_t tok_idx, cur_len;
const char *cur_node, *prev_prefix = NULL, *ptr;
+ ly_bool is_abs;
assert((begin == LY_PATH_BEGIN_ABSOLUTE) || (begin == LY_PATH_BEGIN_EITHER));
assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY) ||
- (prefix == LY_PATH_PREFIX_STRICT_INHERIT));
+ (prefix == LY_PATH_PREFIX_FIRST) || (prefix == LY_PATH_PREFIX_STRICT_INHERIT));
assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF));
LOG_LOCSET(ctx_node, NULL, NULL, NULL);
- /* parse as a generic XPath expression */
- LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 1, &exp), error);
+ /* parse as a generic XPath expression, reparse is performed manually */
+ LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 0, &exp), error);
tok_idx = 0;
+ /* alloc empty repeat (only '=', filled manually) */
+ exp->repeat = calloc(exp->size, sizeof *exp->repeat);
+ LY_CHECK_ERR_GOTO(!exp->repeat, LOGMEM(ctx); ret = LY_EMEM, error);
+
if (begin == LY_PATH_BEGIN_EITHER) {
/* is the path relative? */
if (lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH)) {
/* relative path check specific to leafref */
if (lref) {
+ /* optional function 'deref..' */
+ if ((ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_EXTENDED) &&
+ !lyxp_check_token(NULL, exp, tok_idx, LYXP_TOKEN_FUNCNAME)) {
+ LY_CHECK_ERR_GOTO(ly_path_parse_deref(ctx, ctx_node, exp, &tok_idx), ret = LY_EVALID, error);
+
+ /* '/' */
+ LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID,
+ error);
+ }
+
/* mandatory '..' */
LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_DDOT), ret = LY_EVALID, error);
do {
/* '/' */
- LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error);
+ LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID,
+ error);
/* optional '..' */
} while (!lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_DDOT));
}
+
+ is_abs = 0;
+ } else {
+ is_abs = 1;
}
} else {
/* '/' */
LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error);
+
+ is_abs = 1;
}
do {
@@ -294,8 +394,8 @@ ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
ret = LY_EVALID;
goto error;
}
- } else if (prefix == LY_PATH_PREFIX_STRICT_INHERIT) {
- if (!prev_prefix) {
+ } else if ((prefix == LY_PATH_PREFIX_FIRST) || (prefix == LY_PATH_PREFIX_STRICT_INHERIT)) {
+ if (!prev_prefix && is_abs) {
/* the first node must have a prefix */
if (!strnstr(cur_node, ":", cur_len)) {
LOGVAL(ctx, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", cur_len, cur_node);
@@ -305,7 +405,7 @@ ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
/* remember the first prefix */
prev_prefix = cur_node;
- } else {
+ } else if (prev_prefix && (prefix == LY_PATH_PREFIX_STRICT_INHERIT)) {
/* the prefix must be different, if any */
ptr = strnstr(cur_node, ":", cur_len);
if (ptr) {
@@ -349,7 +449,7 @@ error:
LY_ERR
ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const char *str_path,
- size_t path_len, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr)
+ size_t path_len, uint16_t prefix, uint16_t pred, struct lyxp_expr **expr)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *exp = NULL;
@@ -360,10 +460,14 @@ ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_no
LOG_LOCSET(cur_node, NULL, NULL, NULL);
- /* parse as a generic XPath expression */
+ /* parse as a generic XPath expression, reparse is performed manually */
LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 0, &exp), error);
tok_idx = 0;
+ /* alloc empty repeat (only '=', filled manually) */
+ exp->repeat = calloc(exp->size, sizeof *exp->repeat);
+ LY_CHECK_ERR_GOTO(!exp->repeat, LOGMEM(ctx); ret = LY_EMEM, error);
+
LY_CHECK_GOTO(ret = ly_path_check_predicate(ctx, cur_node, exp, &tok_idx, prefix, pred), error);
/* trailing token check */
@@ -521,7 +625,7 @@ error:
LY_ERR
ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
const struct lysc_node *ctx_node, const struct lyxp_expr *expr, uint32_t *tok_idx, LY_VALUE_FORMAT format,
- void *prefix_data, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
+ void *prefix_data, struct ly_path_predicate **predicates)
{
LY_ERR ret = LY_SUCCESS;
struct ly_path_predicate *p;
@@ -533,7 +637,7 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
LOG_LOCSET(cur_node, NULL, NULL, NULL);
- *pred_type = 0;
+ *predicates = NULL;
if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) {
/* '[', no predicate */
@@ -565,11 +669,7 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
}
++(*tok_idx);
- if (!*pred_type) {
- /* new predicate */
- *pred_type = LY_PATH_PREDTYPE_LIST;
- }
- assert(*pred_type == LY_PATH_PREDTYPE_LIST);
+ /* new predicate */
LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup);
p->key = key;
@@ -577,27 +677,38 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
++(*tok_idx);
- /* Literal or Number */
- assert((expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL) || (expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER));
- if (expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL) {
- /* skip quotes */
- val = expr->expr + expr->tok_pos[*tok_idx] + 1;
- val_len = expr->tok_len[*tok_idx] - 2;
+ /* Literal, Number, or VariableReference */
+ if (expr->tokens[*tok_idx] == LYXP_TOKEN_VARREF) {
+ /* store the variable name */
+ p->variable = strndup(expr->expr + expr->tok_pos[*tok_idx], expr->tok_len[*tok_idx]);
+ LY_CHECK_ERR_GOTO(!p->variable, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+
+ p->type = LY_PATH_PREDTYPE_LIST_VAR;
+ ++(*tok_idx);
} else {
- val = expr->expr + expr->tok_pos[*tok_idx];
- val_len = expr->tok_len[*tok_idx];
- }
+ if (expr->tokens[*tok_idx] == LYXP_TOKEN_LITERAL) {
+ /* skip quotes */
+ val = expr->expr + expr->tok_pos[*tok_idx] + 1;
+ val_len = expr->tok_len[*tok_idx] - 2;
+ } else {
+ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER);
+ val = expr->expr + expr->tok_pos[*tok_idx];
+ val_len = expr->tok_len[*tok_idx];
+ }
- /* store the value */
- LOG_LOCSET(key, NULL, NULL, NULL);
- ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaf *)key)->type, val, val_len, NULL, format,
- prefix_data, LYD_HINT_DATA, key, NULL);
- LOG_LOCBACK(key ? 1 : 0, 0, 0, 0);
- LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup);
- ++(*tok_idx);
+ /* store the value */
+ LOG_LOCSET(key, NULL, NULL, NULL);
+ ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaf *)key)->type, val, val_len, 0, NULL,
+ format, prefix_data, LYD_HINT_DATA, key, NULL);
+ LOG_LOCBACK(key ? 1 : 0, 0, 0, 0);
+ LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup);
+
+ /* "allocate" the type to avoid problems when freeing the value after the type was freed */
+ LY_ATOMIC_INC_BARRIER(((struct lysc_type *)p->value.realtype)->refcount);
- /* "allocate" the type to avoid problems when freeing the value after the type was freed */
- LY_ATOMIC_INC_BARRIER(((struct lysc_type *)p->value.realtype)->refcount);
+ p->type = LY_PATH_PREDTYPE_LIST;
+ ++(*tok_idx);
+ }
/* ']' */
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
@@ -615,7 +726,7 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
/* names (keys) are unique - it was checked when parsing */
LOGVAL(ctx, LYVE_XPATH, "Predicate missing for a key of %s \"%s\" in path.",
lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
- ly_path_predicates_free(ctx, LY_PATH_PREDTYPE_LIST, *predicates);
+ ly_path_predicates_free(ctx, *predicates);
*predicates = NULL;
ret = LY_EVALID;
goto cleanup;
@@ -631,8 +742,8 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
++(*tok_idx);
/* new predicate */
- *pred_type = LY_PATH_PREDTYPE_LEAFLIST;
LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup);
+ p->type = LY_PATH_PREDTYPE_LEAFLIST;
/* '=' */
assert(expr->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL);
@@ -651,8 +762,8 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
/* store the value */
LOG_LOCSET(ctx_node, NULL, NULL, NULL);
- ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaflist *)ctx_node)->type, val, val_len, NULL, format,
- prefix_data, LYD_HINT_DATA, ctx_node, NULL);
+ ret = lyd_value_store(ctx, &p->value, ((struct lysc_node_leaflist *)ctx_node)->type, val, val_len, 0, NULL,
+ format, prefix_data, LYD_HINT_DATA, ctx_node, NULL);
LOG_LOCBACK(ctx_node ? 1 : 0, 0, 0, 0);
LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup);
++(*tok_idx);
@@ -678,8 +789,8 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
}
/* new predicate */
- *pred_type = LY_PATH_PREDTYPE_POSITION;
LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup);
+ p->type = LY_PATH_PREDTYPE_POSITION;
/* syntax was already checked */
p->position = strtoull(expr->expr + expr->tok_pos[*tok_idx], (char **)&val, LY_BASE_DEC);
@@ -820,6 +931,197 @@ cleanup:
}
/**
+ * @brief Duplicate ly_path_predicate structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] pred The array of path predicates.
+ * @param[out] dup Duplicated predicates.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_dup_predicates(const struct ly_ctx *ctx, const struct ly_path_predicate *pred, struct ly_path_predicate **dup)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (!pred) {
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_CREATE_RET(ctx, *dup, LY_ARRAY_COUNT(pred), LY_EMEM);
+ LY_ARRAY_FOR(pred, u) {
+ LY_ARRAY_INCREMENT(*dup);
+ (*dup)[u].type = pred->type;
+
+ switch (pred[u].type) {
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position-predicate */
+ (*dup)[u].position = pred[u].position;
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* key-predicate or leaf-list-predicate */
+ (*dup)[u].key = pred[u].key;
+ pred[u].value.realtype->plugin->duplicate(ctx, &pred[u].value, &(*dup)[u].value);
+ LY_ATOMIC_INC_BARRIER(((struct lysc_type *)pred[u].value.realtype)->refcount);
+ break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ /* key-predicate with a variable */
+ (*dup)[u].key = pred[u].key;
+ (*dup)[u].variable = strdup(pred[u].variable);
+ break;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Appends path elements from source to destination array
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] src The source path
+ * @param[in,out] dst The destination path
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_append(const struct ly_ctx *ctx, const struct ly_path *src, struct ly_path **dst)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
+ struct ly_path *p;
+
+ if (!src) {
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_CREATE_RET(ctx, *dst, LY_ARRAY_COUNT(src), LY_EMEM);
+ LY_ARRAY_FOR(src, u) {
+ LY_ARRAY_NEW_GOTO(ctx, *dst, p, ret, cleanup);
+ p->node = src[u].node;
+ p->ext = src[u].ext;
+ LY_CHECK_GOTO(ret = ly_path_dup_predicates(ctx, src[u].predicates, &p->predicates), cleanup);
+ }
+
+cleanup:
+ return ret;
+}
+
+/**
+ * @brief Compile deref XPath function into ly_path structure.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Optional context node, mandatory of @p lref.
+ * @param[in] top_ext Extension instance containing the definition of the data being created. It is used to find
+ * the top-level node inside the extension instance instead of a module. Note that this is the case not only if
+ * the @p ctx_node is NULL, but also if the relative path starting in @p ctx_node reaches the document root
+ * via double dots.
+ * @param[in] expr Parsed path.
+ * @param[in] oper Oper option (@ref path_oper_options).
+ * @param[in] target Target option (@ref path_target_options).
+ * @param[in] format Format of the path.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in,out] tok_idx Index in @p exp, is adjusted.
+ * @param[out] path Compiled path.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ly_path_compile_deref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
+ LY_VALUE_FORMAT format, void *prefix_data, uint32_t *tok_idx, struct ly_path **path)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr expr2;
+ struct ly_path *path2 = NULL;
+ const struct lysc_node *node2;
+ const struct lysc_node_leaf *deref_leaf_node;
+ const struct lysc_type_leafref *lref;
+ uint32_t begin_token;
+
+ *path = NULL;
+
+ /* properly parsed path must always starts with 'deref' and '(' */
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_FUNCNAME));
+ assert(!strncmp(&expr->expr[expr->tok_pos[*tok_idx]], "deref", 5));
+ (*tok_idx)++;
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR1));
+ (*tok_idx)++;
+ begin_token = *tok_idx;
+
+ /* emebedded functions were already identified count tokens till ')' */
+ while (lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2) && (*tok_idx < expr->used)) {
+ (*tok_idx)++;
+ }
+
+ /* properly parsed path must have ')' within the tokens */
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2));
+
+ /* prepare expr representing just deref arg */
+ expr2.tokens = &expr->tokens[begin_token];
+ expr2.tok_pos = &expr->tok_pos[begin_token];
+ expr2.tok_len = &expr->tok_len[begin_token];
+ expr2.repeat = &expr->repeat[begin_token];
+ expr2.used = *tok_idx - begin_token;
+ expr2.size = expr->size - begin_token;
+ expr2.expr = expr->expr;
+
+ /* compile just deref arg, append it to the path and find dereferenced lref for next operations */
+ LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, ctx_node, top_ext, &expr2, oper, target, format, prefix_data,
+ &path2), cleanup);
+ node2 = path2[LY_ARRAY_COUNT(path2) - 1].node;
+ if ((node2->nodetype != LYS_LEAF) && (node2->nodetype != LYS_LEAFLIST)) {
+ LOGVAL(ctx, LYVE_XPATH, "The deref function target node \"%s\" is not leaf nor leaflist", node2->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ deref_leaf_node = (const struct lysc_node_leaf *)node2;
+ if (deref_leaf_node->type->basetype != LY_TYPE_LEAFREF) {
+ LOGVAL(ctx, LYVE_XPATH, "The deref function target node \"%s\" is not leafref", node2->name);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ lref = (const struct lysc_type_leafref *)deref_leaf_node->type;
+ LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
+ ly_path_free(ctx, path2);
+ path2 = NULL;
+
+ /* compile dereferenced leafref expression and append it to the path */
+ LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, node2, top_ext, lref->path, oper, target, format, prefix_data,
+ &path2), cleanup);
+ node2 = path2[LY_ARRAY_COUNT(path2) - 1].node;
+ LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
+ ly_path_free(ctx, path2);
+ path2 = NULL;
+
+ /* properly parsed path must always continue with ')' and '/' */
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_PAR2));
+ (*tok_idx)++;
+ assert(!lyxp_check_token(NULL, expr, *tok_idx, LYXP_TOKEN_OPER_PATH));
+ (*tok_idx)++;
+
+ /* prepare expr representing rest of the path after deref */
+ expr2.tokens = &expr->tokens[*tok_idx];
+ expr2.tok_pos = &expr->tok_pos[*tok_idx];
+ expr2.tok_len = &expr->tok_len[*tok_idx];
+ expr2.repeat = &expr->repeat[*tok_idx];
+ expr2.used = expr->used - *tok_idx;
+ expr2.size = expr->size - *tok_idx;
+ expr2.expr = expr->expr;
+
+ /* compile rest of the path and append it to the path */
+ LY_CHECK_GOTO(ret = ly_path_compile_leafref(ctx, node2, top_ext, &expr2, oper, target, format, prefix_data, &path2),
+ cleanup);
+ LY_CHECK_GOTO(ret = ly_path_append(ctx, path2, path), cleanup);
+
+cleanup:
+ ly_path_free(ctx, path2);
+ if (ret) {
+ ly_path_free(ctx, *path);
+ *path = NULL;
+ }
+ return ret;
+}
+
+/**
* @brief Compile path into ly_path structure. Any predicates of a leafref are only checked, not compiled.
*
* @param[in] ctx libyang context.
@@ -843,7 +1145,7 @@ cleanup:
*/
static LY_ERR
_ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
- const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, ly_bool lref, uint8_t oper, uint8_t target,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, ly_bool lref, uint16_t oper, uint16_t target,
ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path)
{
LY_ERR ret = LY_SUCCESS;
@@ -876,7 +1178,12 @@ _ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, con
getnext_opts = 0;
}
- if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
+ if (lref && (ly_ctx_get_options(ctx) & LY_CTX_LEAFREF_EXTENDED) &&
+ (expr->tokens[tok_idx] == LYXP_TOKEN_FUNCNAME)) {
+ /* deref function */
+ ret = ly_path_compile_deref(ctx, ctx_node, top_ext, expr, oper, target, format, prefix_data, &tok_idx, path);
+ goto cleanup;
+ } else if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
/* absolute path */
ctx_node = NULL;
@@ -940,7 +1247,7 @@ _ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, con
ret = ly_path_compile_predicate_leafref(ctx_node, cur_node, expr, &tok_idx, format, prefix_data);
} else {
ret = ly_path_compile_predicate(ctx, cur_node, cur_mod, ctx_node, expr, &tok_idx, format, prefix_data,
- &p->predicates, &p->pred_type);
+ &p->predicates);
}
LY_CHECK_GOTO(ret, cleanup);
} while (!lyxp_next_token(NULL, expr, &tok_idx, LYXP_TOKEN_OPER_PATH));
@@ -971,7 +1278,7 @@ cleanup:
LY_ERR
ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
- const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path)
{
return _ly_path_compile(ctx, cur_mod, ctx_node, top_ext, expr, 0, oper, target, limit_access_tree, format,
@@ -980,15 +1287,15 @@ ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, cons
LY_ERR
ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const struct lysc_ext_instance *top_ext,
- const struct lyxp_expr *expr, uint8_t oper, uint8_t target, LY_VALUE_FORMAT format, void *prefix_data,
+ const struct lyxp_expr *expr, uint16_t oper, uint16_t target, LY_VALUE_FORMAT format, void *prefix_data,
struct ly_path **path)
{
return _ly_path_compile(ctx, ctx_node->module, ctx_node, top_ext, expr, 1, oper, target, 1, format, prefix_data, path);
}
LY_ERR
-ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_COUNT_TYPE *path_idx,
- struct lyd_node **match)
+ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, const struct lyxp_var *vars,
+ ly_bool with_opaq, LY_ARRAY_COUNT_TYPE *path_idx, struct lyd_node **match)
{
LY_ARRAY_COUNT_TYPE u;
struct lyd_node *prev_node = NULL, *elem, *node = NULL, *target;
@@ -1010,35 +1317,43 @@ ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, L
}
LY_ARRAY_FOR(path, u) {
- switch (path[u].pred_type) {
- case LY_PATH_PREDTYPE_POSITION:
- /* we cannot use hashes and want an instance on a specific position */
- pos = 1;
- node = NULL;
- LYD_LIST_FOR_INST(start, path[u].node, elem) {
- if (pos == path[u].predicates[0].position) {
- node = elem;
- break;
+ if (path[u].predicates) {
+ switch (path[u].predicates[0].type) {
+ case LY_PATH_PREDTYPE_POSITION:
+ /* we cannot use hashes and want an instance on a specific position */
+ pos = 1;
+ node = NULL;
+ LYD_LIST_FOR_INST(start, path[u].node, elem) {
+ if (pos == path[u].predicates[0].position) {
+ node = elem;
+ break;
+ }
+ ++pos;
}
- ++pos;
+ break;
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* we will use hashes to find one leaf-list instance */
+ LY_CHECK_RET(lyd_create_term2(path[u].node, &path[u].predicates[0].value, &target));
+ lyd_find_sibling_first(start, target, &node);
+ lyd_free_tree(target);
+ break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ case LY_PATH_PREDTYPE_LIST:
+ /* we will use hashes to find one list instance */
+ LY_CHECK_RET(lyd_create_list(path[u].node, path[u].predicates, vars, &target));
+ lyd_find_sibling_first(start, target, &node);
+ lyd_free_tree(target);
+ break;
}
- break;
- case LY_PATH_PREDTYPE_LEAFLIST:
- /* we will use hashes to find one leaf-list instance */
- LY_CHECK_RET(lyd_create_term2(path[u].node, &path[u].predicates[0].value, &target));
- lyd_find_sibling_first(start, target, &node);
- lyd_free_tree(target);
- break;
- case LY_PATH_PREDTYPE_LIST:
- /* we will use hashes to find one list instance */
- LY_CHECK_RET(lyd_create_list(path[u].node, path[u].predicates, &target));
- lyd_find_sibling_first(start, target, &node);
- lyd_free_tree(target);
- break;
- case LY_PATH_PREDTYPE_NONE:
+ } else {
/* we will use hashes to find one any/container/leaf instance */
- lyd_find_sibling_val(start, path[u].node, NULL, 0, &node);
- break;
+ if (lyd_find_sibling_val(start, path[u].node, NULL, 0, &node) && with_opaq) {
+ if (!lyd_find_sibling_opaq_next(start, path[u].node->name, &node) &&
+ (lyd_node_module(node) != path[u].node->module)) {
+ /* non-matching opaque node */
+ node = NULL;
+ }
+ }
}
if (!node) {
@@ -1085,12 +1400,12 @@ ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, L
}
LY_ERR
-ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match)
+ly_path_eval(const struct ly_path *path, const struct lyd_node *start, const struct lyxp_var *vars, struct lyd_node **match)
{
LY_ERR ret;
struct lyd_node *m;
- ret = ly_path_eval_partial(path, start, NULL, &m);
+ ret = ly_path_eval_partial(path, start, vars, 0, NULL, &m);
if (ret == LY_SUCCESS) {
/* last node was found */
@@ -1110,7 +1425,8 @@ ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct ly
LY_ERR
ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path **dup)
{
- LY_ARRAY_COUNT_TYPE u, v;
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
if (!path) {
return LY_SUCCESS;
@@ -1120,37 +1436,15 @@ ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct ly_path
LY_ARRAY_FOR(path, u) {
LY_ARRAY_INCREMENT(*dup);
(*dup)[u].node = path[u].node;
- if (path[u].predicates) {
- LY_ARRAY_CREATE_RET(ctx, (*dup)[u].predicates, LY_ARRAY_COUNT(path[u].predicates), LY_EMEM);
- (*dup)[u].pred_type = path[u].pred_type;
- LY_ARRAY_FOR(path[u].predicates, v) {
- struct ly_path_predicate *pred = &path[u].predicates[v];
-
- LY_ARRAY_INCREMENT((*dup)[u].predicates);
- switch (path[u].pred_type) {
- case LY_PATH_PREDTYPE_POSITION:
- /* position-predicate */
- (*dup)[u].predicates[v].position = pred->position;
- break;
- case LY_PATH_PREDTYPE_LIST:
- case LY_PATH_PREDTYPE_LEAFLIST:
- /* key-predicate or leaf-list-predicate */
- (*dup)[u].predicates[v].key = pred->key;
- pred->value.realtype->plugin->duplicate(ctx, &pred->value, &(*dup)[u].predicates[v].value);
- LY_ATOMIC_INC_BARRIER(((struct lysc_type *)pred->value.realtype)->refcount);
- break;
- case LY_PATH_PREDTYPE_NONE:
- break;
- }
- }
- }
+ (*dup)[u].ext = path[u].ext;
+ LY_CHECK_RET(ret = ly_path_dup_predicates(ctx, path[u].predicates, &(*dup)[u].predicates), ret);
}
return LY_SUCCESS;
}
void
-ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_type, struct ly_path_predicate *predicates)
+ly_path_predicates_free(const struct ly_ctx *ctx, struct ly_path_predicate *predicates)
{
LY_ARRAY_COUNT_TYPE u;
struct lysf_ctx fctx = {.ctx = (struct ly_ctx *)ctx};
@@ -1160,9 +1454,8 @@ ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_ty
}
LY_ARRAY_FOR(predicates, u) {
- switch (pred_type) {
+ switch (predicates[u].type) {
case LY_PATH_PREDTYPE_POSITION:
- case LY_PATH_PREDTYPE_NONE:
/* nothing to free */
break;
case LY_PATH_PREDTYPE_LIST:
@@ -1172,6 +1465,9 @@ ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_ty
lysc_type_free(&fctx, (struct lysc_type *)predicates[u].value.realtype);
}
break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ free(predicates[u].variable);
+ break;
}
}
LY_ARRAY_FREE(predicates);
@@ -1187,7 +1483,7 @@ ly_path_free(const struct ly_ctx *ctx, struct ly_path *path)
}
LY_ARRAY_FOR(path, u) {
- ly_path_predicates_free(ctx, path[u].pred_type, path[u].predicates);
+ ly_path_predicates_free(ctx, path[u].predicates);
}
LY_ARRAY_FREE(path);
}
diff --git a/src/path.h b/src/path.h
index ca1c84a..9a9e342 100644
--- a/src/path.h
+++ b/src/path.h
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Path structure and manipulation routines.
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -29,24 +29,25 @@ struct lysc_node;
struct lyxp_expr;
enum ly_path_pred_type {
- LY_PATH_PREDTYPE_NONE = 0, /**< no predicate */
LY_PATH_PREDTYPE_POSITION, /**< position predicate - [2] */
LY_PATH_PREDTYPE_LIST, /**< keys predicate - [key1='val1'][key2='val2']... */
- LY_PATH_PREDTYPE_LEAFLIST /**< leaflist value predicate - [.='value'] */
+ LY_PATH_PREDTYPE_LEAFLIST, /**< leaflist value predicate - [.='value'] */
+ LY_PATH_PREDTYPE_LIST_VAR /**< keys predicate with variable instead of value - [key1=$USER]... */
};
/**
* @brief Structure for simple path predicate.
*/
struct ly_path_predicate {
+ enum ly_path_pred_type type; /**< Predicate type (see YANG ABNF) */
union {
- uint64_t position; /**< position value for the position-predicate */
-
+ uint64_t position; /**< position value for the position-predicate */
struct {
- const struct lysc_node *key; /**< key node of the predicate, NULL in
- case of a leaf-list predicate */
- struct lyd_value value; /**< value representation according to the
- key's type, its realtype is allocated */
+ const struct lysc_node *key; /**< key node of the predicate, NULL in case of a leaf-list predicate */
+ union {
+ struct lyd_value value; /**< stored value representation according to the key's type (realtype ref) */
+ char *variable; /**< XPath variable used instead of the value */
+ };
};
};
};
@@ -61,7 +62,6 @@ struct ly_path {
- is inner node - path is relative */
const struct lysc_ext_instance *ext; /**< Extension instance of @p node, if any */
struct ly_path_predicate *predicates; /**< [Sized array](@ref sizedarrays) of the path segment's predicates */
- enum ly_path_pred_type pred_type; /**< Predicate type (see YANG ABNF) */
};
/**
@@ -76,9 +76,10 @@ struct ly_path {
* @defgroup path_prefix_options Path prefix options.
* @{
*/
-#define LY_PATH_PREFIX_OPTIONAL 0x10 /**< prefixes in the path are optional */
+#define LY_PATH_PREFIX_OPTIONAL 0x10 /**< prefixes in the path are optional (XML path) */
#define LY_PATH_PREFIX_MANDATORY 0x20 /**< prefixes in the path are mandatory (XML instance-identifier) */
-#define LY_PATH_PREFIX_STRICT_INHERIT 0x30 /**< prefixes in the path are mandatory in case they differ from the
+#define LY_PATH_PREFIX_FIRST 0x40 /**< prefixes in the path are mandatory only in the first node of absolute path (JSON path) */
+#define LY_PATH_PREFIX_STRICT_INHERIT 0x80 /**< prefixes in the path are mandatory in case they differ from the
previous prefixes, otherwise they are prohibited (JSON instance-identifier) */
/** @} */
@@ -86,10 +87,10 @@ struct ly_path {
* @defgroup path_pred_options Path predicate options.
* @{
*/
-#define LY_PATH_PRED_KEYS 0x40 /* expected predicate only - [node='value']* */
-#define LY_PATH_PRED_SIMPLE 0x80 /* expected predicates - [node='value']*; [.='value']; [1] */
-#define LY_PATH_PRED_LEAFREF 0xC0 /* expected predicates only leafref - [node=current()/../../../node/node];
- at least 1 ".." and 1 "node" after */
+#define LY_PATH_PRED_KEYS 0x0100 /** expected predicate only - [node='value']* */
+#define LY_PATH_PRED_SIMPLE 0x0200 /** expected predicates - ( [node='value'] | [node=$VAR] )*; [.='value']; [1] */
+#define LY_PATH_PRED_LEAFREF 0x0400 /** expected predicates only leafref - [node=current()/../../../node/node];
+ at least 1 ".." and 1 "node" after */
/** @} */
/**
@@ -107,7 +108,7 @@ struct ly_path {
* @return LY_ERR value.
*/
LY_ERR ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *str_path, size_t path_len,
- ly_bool lref, uint8_t begin, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr);
+ ly_bool lref, uint16_t begin, uint16_t prefix, uint16_t pred, struct lyxp_expr **expr);
/**
* @brief Parse predicate into XPath token structure and perform all additional checks.
@@ -122,7 +123,7 @@ LY_ERR ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
* @return LY_ERR value.
*/
LY_ERR ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const char *str_path,
- size_t path_len, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr);
+ size_t path_len, uint16_t prefix, uint16_t pred, struct lyxp_expr **expr);
/**
* @defgroup path_oper_options Path operation options.
@@ -162,7 +163,7 @@ LY_ERR ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node
* @return LY_ERR value.
*/
LY_ERR ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, const struct lysc_node *ctx_node,
- const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
ly_bool limit_access_tree, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path);
/**
@@ -182,7 +183,7 @@ LY_ERR ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mo
* @return LY_ERR value.
*/
LY_ERR ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
- const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint8_t oper, uint8_t target,
+ const struct lysc_ext_instance *top_ext, const struct lyxp_expr *expr, uint16_t oper, uint16_t target,
LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path);
/**
@@ -198,18 +199,19 @@ LY_ERR ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node
* @param[in] format Format of the path.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
* @param[out] predicates Compiled predicates.
- * @param[out] pred_type Type of the compiled predicate(s).
* @return LY_ERR value.
*/
LY_ERR ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const struct lys_module *cur_mod,
const struct lysc_node *ctx_node, const struct lyxp_expr *expr, uint32_t *tok_idx, LY_VALUE_FORMAT format,
- void *prefix_data, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type);
+ void *prefix_data, struct ly_path_predicate **predicates);
/**
* @brief Resolve at least partially the target defined by ly_path structure. Not supported for leafref!
*
* @param[in] path Path structure specifying the target.
* @param[in] start Starting node for relative paths, can be any for absolute paths.
+ * @param[in] vars Array of defined variables to use in predicates, may be NULL.
+ * @param[in] with_opaq Whether to consider opaque nodes or not.
* @param[out] path_idx Last found path segment index, can be NULL, set to 0 if not found.
* @param[out] match Last found matching node, can be NULL, set to NULL if not found.
* @return LY_ENOTFOUND if no nodes were found,
@@ -217,20 +219,22 @@ LY_ERR ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_nod
* @return LY_SUCCESS when the last node in the path was found,
* @return LY_ERR on another error.
*/
-LY_ERR ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, LY_ARRAY_COUNT_TYPE *path_idx,
- struct lyd_node **match);
+LY_ERR ly_path_eval_partial(const struct ly_path *path, const struct lyd_node *start, const struct lyxp_var *vars,
+ ly_bool with_opaq, LY_ARRAY_COUNT_TYPE *path_idx, struct lyd_node **match);
/**
* @brief Resolve the target defined by ly_path structure. Not supported for leafref!
*
* @param[in] path Path structure specifying the target.
* @param[in] start Starting node for relative paths, can be any for absolute paths.
+ * @param[in] vars Array of defined variables to use in predicates, may be NULL.
* @param[out] match Found matching node, can be NULL, set to NULL if not found.
* @return LY_ENOTFOUND if no nodes were found,
* @return LY_SUCCESS when the last node in the path was found,
* @return LY_ERR on another error.
*/
-LY_ERR ly_path_eval(const struct ly_path *path, const struct lyd_node *start, struct lyd_node **match);
+LY_ERR ly_path_eval(const struct ly_path *path, const struct lyd_node *start, const struct lyxp_var *vars,
+ struct lyd_node **match);
/**
* @brief Duplicate ly_path structure.
@@ -246,11 +250,9 @@ LY_ERR ly_path_dup(const struct ly_ctx *ctx, const struct ly_path *path, struct
* @brief Free ly_path_predicate structure.
*
* @param[in] ctx libyang context.
- * @param[in] pred_type Predicate type.
* @param[in] predicates Predicates ([sized array](@ref sizedarrays)) to free.
*/
-void ly_path_predicates_free(const struct ly_ctx *ctx, enum ly_path_pred_type pred_type,
- struct ly_path_predicate *predicates);
+void ly_path_predicates_free(const struct ly_ctx *ctx, struct ly_path_predicate *predicates);
/**
* @brief Free ly_path structure.
diff --git a/src/plugins.c b/src/plugins.c
index d62da1c..011e464 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -70,6 +70,7 @@ extern const struct lyplg_type_record plugins_ipv6_prefix[];
* ietf-yang-types
*/
extern const struct lyplg_type_record plugins_date_and_time[];
+extern const struct lyplg_type_record plugins_hex_string[];
extern const struct lyplg_type_record plugins_xpath10[];
/*
@@ -475,6 +476,7 @@ lyplg_init(void)
/* ietf-yang-types */
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_date_and_time), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_hex_string), error);
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_xpath10), error);
/* ietf-netconf-acm */
diff --git a/src/plugins_exts.c b/src/plugins_exts.c
index 00970fa..f8cdeff 100644
--- a/src/plugins_exts.c
+++ b/src/plugins_exts.c
@@ -94,6 +94,8 @@ lys_compile_ext_instance_stmt(struct lysc_ctx *ctx, const void *parsed, struct l
ly_bool length_restr = 0;
LY_DATA_TYPE basetype;
+ assert(parsed);
+
/* compilation wthout any storage */
if (substmt->stmt == LY_STMT_IF_FEATURE) {
ly_bool enabled;
@@ -106,8 +108,8 @@ lys_compile_ext_instance_stmt(struct lysc_ctx *ctx, const void *parsed, struct l
}
}
- if (!substmt->storage || !parsed) {
- /* nothing to store or nothing parsed to compile */
+ if (!substmt->storage) {
+ /* nothing to store */
goto cleanup;
}
@@ -268,6 +270,7 @@ lys_compile_ext_instance_stmt(struct lysc_ctx *ctx, const void *parsed, struct l
/* compile */
LY_CHECK_GOTO(rc = lys_compile_type(ctx, NULL, flags, ext->def->name, ptype, substmt->storage, &units, NULL), cleanup);
+ LY_ATOMIC_INC_BARRIER((*(struct lysc_type **)substmt->storage)->refcount);
break;
}
case LY_STMT_EXTENSION_INSTANCE: {
@@ -335,8 +338,8 @@ lyplg_ext_compile_extension_instance(struct lysc_ctx *ctx, const struct lysp_ext
stmtp = extp->substmts[u].stmt;
storagep = *(void **)extp->substmts[u].storage;
- if (ly_set_contains(&storagep_compiled, storagep, NULL)) {
- /* this parsed statement has already been compiled (for example, if it is a linked list of parsed nodes) */
+ if (!storagep || ly_set_contains(&storagep_compiled, storagep, NULL)) {
+ /* nothing parsed or already compiled (for example, if it is a linked list of parsed nodes) */
continue;
}
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
index f005391..a2783ff 100644
--- a/src/plugins_exts.h
+++ b/src/plugins_exts.h
@@ -594,6 +594,14 @@ LIBYANG_API_DECL void lyplg_ext_compile_log_path(const char *path, const struct
LY_LOG_LEVEL level, LY_ERR err_no, const char *format, ...);
/**
+ * @brief Log a message from an extension plugin using the compiled extension instance and a generated error item.
+ *
+ * @param[in] err Error item to log.
+ * @param[in] ext Compiled extension instance.
+ */
+LIBYANG_API_DEF void lyplg_ext_compile_log_err(const struct ly_err_item *err, const struct lysc_ext_instance *ext);
+
+/**
* @brief YANG schema compilation context getter for libyang context.
*
* @param[in] ctx YANG schema compilation context.
@@ -915,7 +923,10 @@ typedef void (*lyplg_ext_compile_free_clb)(const struct ly_ctx *ctx, struct lysc
LIBYANG_API_DECL void lyplg_ext_cfree_instance_substatements(const struct ly_ctx *ctx, struct lysc_ext_substmt *substmts);
/**
- * @brief Extension plugin implementing various aspects of a YANG extension
+ * @brief Extension plugin implementing various aspects of a YANG extension.
+ *
+ * Every plugin should have at least either ::parse() or ::compile() callback defined but other than that **all**
+ * the callbacks are **optional**.
*/
struct lyplg_ext {
const char *id; /**< plugin identification (mainly for distinguish incompatible versions
@@ -1030,11 +1041,10 @@ LIBYANG_API_DECL LY_ERR lyplg_ext_schema_mount_get_parent_ref(const struct lysc_
/**
* @brief Allocate a new context for a particular instance of the yangmnt:mount-point extension.
- * Caller is responsible for destroying the resulting context.
+ * Caller is responsible for **freeing** the created context.
*
* @param[in] ext Compiled extension instance.
- * @param[out] ctx A context with modules loaded from the list found in
- * the extension data.
+ * @param[out] ctx Context with modules loaded from the list found in the extension data.
* @return LY_ERR value.
*/
LIBYANG_API_DECL LY_ERR lyplg_ext_schema_mount_create_context(const struct lysc_ext_instance *ext, struct ly_ctx **ctx);
diff --git a/src/plugins_exts/nacm.c b/src/plugins_exts/nacm.c
index 5ab8daa..df49721 100644
--- a/src/plugins_exts/nacm.c
+++ b/src/plugins_exts/nacm.c
@@ -101,7 +101,7 @@ nacm_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
/* check for duplication */
LY_ARRAY_FOR(parent->exts, u) {
- if ((&parent->exts[u] != ext) && parent->exts[u].record && (parent->exts[u].record->plugin.id == ext->record->plugin.id)) {
+ if ((&parent->exts[u] != ext) && parent->exts[u].record && !strcmp(parent->exts[u].record->plugin.id, ext->record->plugin.id)) {
/* duplication of a NACM extension on a single node
* We check for all NACM plugins since we want to catch even the situation that there is default-deny-all
* AND default-deny-write */
diff --git a/src/plugins_exts/schema_mount.c b/src/plugins_exts/schema_mount.c
index 9800760..cc63431 100644
--- a/src/plugins_exts/schema_mount.c
+++ b/src/plugins_exts/schema_mount.c
@@ -982,7 +982,7 @@ schema_mount_validate(struct lysc_ext_instance *ext, struct lyd_node *sibling, c
if (!err) {
lyplg_ext_compile_log(NULL, ext, LY_LLERR, ret, "Unknown validation error (err code %d).", ret);
} else {
- lyplg_ext_compile_log_path(err->path, ext, LY_LLERR, err->no, "%s", err->msg);
+ lyplg_ext_compile_log_err(err, ext);
}
goto cleanup;
}
diff --git a/src/plugins_types.c b/src/plugins_types.c
index cb4b896..de7273d 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -224,24 +224,52 @@ ly_schema_resolved_get_prefix(const struct lys_module *mod, void *prefix_data)
}
/**
- * @brief Simply return module local prefix. Also, store the module in a set.
+ * @brief Get prefix for XML print.
+ *
+ * @param[in] mod Module whose prefix to get.
+ * @param[in,out] prefix_data Set of used modules in the print. If @p mod is found in this set, no string (prefix) is
+ * returned.
+ * @return Prefix to print, may be NULL if the default namespace should be used.
*/
static const char *
ly_xml_get_prefix(const struct lys_module *mod, void *prefix_data)
{
- struct ly_set *ns_list = prefix_data;
+ struct ly_set *mods = prefix_data;
+ uint32_t i;
+
+ /* first is the local module */
+ assert(mods->count);
+ if (mods->objs[0] == mod) {
+ return NULL;
+ }
- LY_CHECK_RET(ly_set_add(ns_list, (void *)mod, 0, NULL), NULL);
+ /* check for duplicates in the rest of the modules and add there */
+ for (i = 1; i < mods->count; ++i) {
+ if (mods->objs[i] == mod) {
+ break;
+ }
+ }
+ if (i == mods->count) {
+ LY_CHECK_RET(ly_set_add(mods, (void *)mod, 1, NULL), NULL);
+ }
+
+ /* return the prefix */
return mod->prefix;
}
/**
- * @brief Simply return module name.
+ * @brief Get prefix for JSON print.
+ *
+ * @param[in] mod Module whose prefix to get.
+ * @param[in] prefix_data Current local module, may be NULL. If it matches @p mod, no string (preifx) is returned.
+ * @return Prefix (module name) to print, may be NULL if the default module should be used.
*/
static const char *
-ly_json_get_prefix(const struct lys_module *mod, void *UNUSED(prefix_data))
+ly_json_get_prefix(const struct lys_module *mod, void *prefix_data)
{
- return mod->name;
+ const struct lys_module *local_mod = prefix_data;
+
+ return (local_mod == mod) ? NULL : mod->name;
}
const char *
@@ -279,10 +307,6 @@ lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_simple(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
if (val1->_canonical == val2->_canonical) {
return LY_SUCCESS;
}
@@ -783,6 +807,8 @@ lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, size_t value_
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *exp = NULL;
uint32_t prefix_opt = 0;
+ struct ly_err_item *e;
+ const char *err_fmt = NULL;
LY_CHECK_ARG_RET(ctx, ctx, value, ctx_node, path, err, LY_EINVAL);
@@ -803,31 +829,43 @@ lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, size_t value_
break;
}
+ /* remember the current last error */
+ e = ly_err_last(ctx);
+
/* parse the value */
ret = ly_path_parse(ctx, ctx_node, value, value_len, 0, LY_PATH_BEGIN_ABSOLUTE, prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
if (ret) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
- "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, value);
+ err_fmt = "Invalid instance-identifier \"%.*s\" value - syntax error%s%s";
goto cleanup;
}
if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
/* implement all prefixes */
- LY_CHECK_GOTO(ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, unres, NULL), cleanup);
+ ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, unres, NULL);
+ if (ret) {
+ err_fmt = "Failed to implement a module referenced by instance-identifier \"%.*s\"%s%s";
+ goto cleanup;
+ }
}
/* resolve it on schema tree */
ret = ly_path_compile(ctx, NULL, ctx_node, NULL, exp, (ctx_node->flags & LYS_IS_OUTPUT) ?
LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, 1, format, prefix_data, path);
if (ret) {
- ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL,
- "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, value);
+ err_fmt = "Invalid instance-identifier \"%.*s\" value - semantic error%s%s";
goto cleanup;
}
cleanup:
lyxp_expr_free(ctx, exp);
if (ret) {
+ /* generate error, spend the context error, if any */
+ e = e ? e->next : ly_err_last(ctx);
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, err_fmt, (int)value_len, value, e ? ": " : ".", e ? e->msg : "");
+ if (e) {
+ ly_err_clean((struct ly_ctx *)ctx, e);
+ }
+
ly_path_free(ctx, *path);
*path = NULL;
}
@@ -1016,6 +1054,9 @@ lyplg_type_resolve_leafref(const struct lysc_type_leafref *lref, const struct ly
if (set.val.nodes[i].type != LYXP_NODE_ELEM) {
continue;
}
+ if (((struct lyd_node_term *)set.val.nodes[i].node)->value.realtype != value->realtype) {
+ continue;
+ }
if (!lref->plugin->compare(&((struct lyd_node_term *)set.val.nodes[i].node)->value, value)) {
break;
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 3ec1d3b..b4ecd43 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -367,7 +367,7 @@ LIBYANG_API_DEF LY_ERR lyplg_type_xpath10_print_token(const char *token, uint16_
* @param[in] format Format of the prefix (::lyplg_type_print_clb's format parameter).
* @param[in] prefix_data Format-specific data (::lyplg_type_print_clb's prefix_data parameter).
* @return Module prefix to print.
- * @return NULL on error.
+ * @return NULL on using the current module/namespace.
*/
LIBYANG_API_DECL const char *lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data);
@@ -473,6 +473,7 @@ LIBYANG_API_DECL LY_ERR lyplg_type_print_xpath10_value(const struct lyd_value_xp
value after calling the type's store callback with this option. */
#define LYPLG_TYPE_STORE_IMPLEMENT 0x02 /**< If a foreign module is needed to be implemented to successfully instantiate
the value, make the module implemented. */
+#define LYPLG_TYPE_STORE_IS_UTF8 0x04 /**< The value is guaranteed to be a valid UTF-8 string, if applicable for the type. */
/** @} plugintypestoreopts */
/**
@@ -522,7 +523,7 @@ LIBYANG_API_DECL typedef LY_ERR (*lyplg_type_store_clb)(const struct ly_ctx *ctx
* @param[in] tree External data tree (e.g. when validating RPC/Notification) with possibly referenced data.
* @param[in,out] storage Storage of the value successfully filled by ::lyplg_type_store_clb. May be modified.
* @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic
- * error message is prepared instead. The error structure can be created by ::ly_err_new().
+ * error message is prepared instead. The error structure can be created by ::ly_err_new().
* @return LY_SUCCESS on success,
* @return LY_ERR value on error.
*/
@@ -532,12 +533,12 @@ LIBYANG_API_DECL typedef LY_ERR (*lyplg_type_validate_clb)(const struct ly_ctx *
/**
* @brief Callback for comparing 2 values of the same type.
*
- * In case the value types (::lyd_value.realtype) are different, ::LY_ENOT must always be returned.
- * It can be assumed that the same context (dictionary) was used for storing both values.
+ * It can be assumed that the same context (dictionary) was used for storing both values and the realtype
+ * member of both the values is the same.
*
* @param[in] val1 First value to compare.
* @param[in] val2 Second value to compare.
- * @return LY_SUCCESS if values are same (according to the type's definition of being same).
+ * @return LY_SUCCESS if values are considered equal.
* @return LY_ENOT if values differ.
*/
typedef LY_ERR (*lyplg_type_compare_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
diff --git a/src/plugins_types/binary.c b/src/plugins_types/binary.c
index 519ec2e..542b54b 100644
--- a/src/plugins_types/binary.c
+++ b/src/plugins_types/binary.c
@@ -340,10 +340,6 @@ lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *
{
struct lyd_value_binary *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/bits.c b/src/plugins_types/bits.c
index 04adace..2f87ed3 100644
--- a/src/plugins_types/bits.c
+++ b/src/plugins_types/bits.c
@@ -373,10 +373,6 @@ lyplg_type_compare_bits(const struct lyd_value *val1, const struct lyd_value *va
struct lyd_value_bits *v1, *v2;
struct lysc_type_bits *type_bits = (struct lysc_type_bits *)val1->realtype;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/boolean.c b/src/plugins_types/boolean.c
index f8b19f6..415b8b9 100644
--- a/src/plugins_types/boolean.c
+++ b/src/plugins_types/boolean.c
@@ -106,10 +106,6 @@ cleanup:
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_boolean(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
if (val1->boolean != val2->boolean) {
return LY_ENOT;
}
diff --git a/src/plugins_types/date_and_time.c b/src/plugins_types/date_and_time.c
index 5ccb86d..594b0a8 100644
--- a/src/plugins_types/date_and_time.c
+++ b/src/plugins_types/date_and_time.c
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief ietf-yang-types date-and-time type plugin.
*
- * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ * Copyright (c) 2019-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -53,7 +53,6 @@ lyplg_type_store_date_and_time(const struct ly_ctx *ctx, const struct lysc_type
LY_ERR ret = LY_SUCCESS;
struct lysc_type_str *type_dat = (struct lysc_type_str *)type;
struct lyd_value_date_and_time *val;
- struct tm tm;
uint32_t i;
char c;
@@ -111,34 +110,17 @@ lyplg_type_store_date_and_time(const struct ly_ctx *ctx, const struct lysc_type
ret = lyplg_type_validate_patterns(type_dat->patterns, value, value_len, err);
LY_CHECK_GOTO(ret, cleanup);
- /* pattern validation succeeded, convert to UNIX time and fractions of second */
+ /* convert to UNIX time and fractions of second */
ret = ly_time_str2time(value, &val->time, &val->fractions_s);
- LY_CHECK_GOTO(ret, cleanup);
+ if (ret) {
+ ret = ly_err_new(err, ret, 0, NULL, NULL, "%s", ly_last_errmsg());
+ goto cleanup;
+ }
-#ifdef HAVE_TIME_H_TIMEZONE
if (!strncmp(((char *)value + value_len) - 6, "-00:00", 6)) {
- /* unknown timezone, move the timestamp to UTC */
- tzset();
- val->time += (time_t)timezone;
+ /* unknown timezone */
val->unknown_tz = 1;
-
- /* DST may apply, adjust accordingly */
- if (!localtime_r(&val->time, &tm)) {
- ret = ly_err_new(err, LY_ESYS, LYVE_DATA, NULL, NULL, "localtime_r() call failed (%s).", strerror(errno));
- goto cleanup;
- } else if (tm.tm_isdst < 0) {
- ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Failed to get DST information.");
- goto cleanup;
- }
- if (tm.tm_isdst) {
- /* move an hour back */
- val->time -= 3600;
- }
}
-#else
- (void)tm;
- val->unknown_tz = 1;
-#endif
if (format == LY_VALUE_CANON) {
/* store canonical value */
@@ -171,10 +153,6 @@ lyplg_type_compare_date_and_time(const struct lyd_value *val1, const struct lyd_
{
struct lyd_value_date_and_time *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
@@ -199,6 +177,7 @@ lyplg_type_print_date_and_time(const struct ly_ctx *ctx, const struct lyd_value
void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
{
struct lyd_value_date_and_time *val;
+ struct tm tm;
char *ret;
LYD_VALUE_GET(value, val);
@@ -229,14 +208,20 @@ lyplg_type_print_date_and_time(const struct ly_ctx *ctx, const struct lyd_value
/* generate canonical value if not already */
if (!value->_canonical) {
- /* get the canonical value */
- if (ly_time_time2str(val->time, val->fractions_s, &ret)) {
- return NULL;
- }
-
if (val->unknown_tz) {
- /* date and time is correct, fix only the timezone */
- strcpy((ret + strlen(ret)) - 6, "-00:00");
+ /* ly_time_time2str but always using GMT */
+ if (!gmtime_r(&val->time, &tm)) {
+ return NULL;
+ }
+ if (asprintf(&ret, "%04d-%02d-%02dT%02d:%02d:%02d%s%s-00:00",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+ val->fractions_s ? "." : "", val->fractions_s ? val->fractions_s : "") == -1) {
+ return NULL;
+ }
+ } else {
+ if (ly_time_time2str(val->time, val->fractions_s, &ret)) {
+ return NULL;
+ }
}
/* store it */
diff --git a/src/plugins_types/decimal64.c b/src/plugins_types/decimal64.c
index 25a88d9..cbd6934 100644
--- a/src/plugins_types/decimal64.c
+++ b/src/plugins_types/decimal64.c
@@ -161,10 +161,6 @@ cleanup:
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_decimal64(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
/* if type is the same, the fraction digits are, too */
if (val1->dec64 != val2->dec64) {
return LY_ENOT;
diff --git a/src/plugins_types/hex_string.c b/src/plugins_types/hex_string.c
new file mode 100644
index 0000000..f72ce66
--- /dev/null
+++ b/src/plugins_types/hex_string.c
@@ -0,0 +1,171 @@
+/**
+ * @file hex_string.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Built-in hex-string and associated types plugin.
+ *
+ * Copyright (c) 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "plugins_types.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesHexString phys-address, mac-address, hex-string, uuid (ietf-yang-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string itself |
+ */
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_hex_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ uint32_t i;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* make a copy, it is needed for canonization */
+ if ((format != LY_VALUE_CANON) && !(options & LYPLG_TYPE_STORE_DYNAMIC)) {
+ value = strndup(value, value_len);
+ LY_CHECK_ERR_GOTO(!value, ret = LY_EMEM, cleanup);
+ options |= LYPLG_TYPE_STORE_DYNAMIC;
+ }
+
+ /* store canonical value */
+ if (format != LY_VALUE_CANON) {
+ /* make lowercase and store, the value must be dynamic */
+ for (i = 0; i < value_len; ++i) {
+ ((char *)value)[i] = tolower(((char *)value)[i]);
+ }
+
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ /* store directly */
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for string type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_hex_string[] = {
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "phys-address",
+
+ .plugin.id = "libyang 2 - hex-string, version 1",
+ .plugin.store = lyplg_type_store_hex_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "mac-address",
+
+ .plugin.id = "libyang 2 - hex-string, version 1",
+ .plugin.store = lyplg_type_store_hex_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "hex-string",
+
+ .plugin.id = "libyang 2 - hex-string, version 1",
+ .plugin.store = lyplg_type_store_hex_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "uuid",
+
+ .plugin.id = "libyang 2 - hex-string, version 1",
+ .plugin.store = lyplg_type_store_hex_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/identityref.c b/src/plugins_types/identityref.c
index 8b7985d..588a969 100644
--- a/src/plugins_types/identityref.c
+++ b/src/plugins_types/identityref.c
@@ -1,9 +1,10 @@
/**
* @file identityref.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Built-in identityref type plugin.
*
- * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ * Copyright (c) 2019-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -50,8 +51,16 @@ static LY_ERR
identityref_ident2str(const struct lysc_ident *ident, LY_VALUE_FORMAT format, void *prefix_data, char **str, size_t *str_len)
{
int len;
+ const char *prefix;
- len = asprintf(str, "%s:%s", lyplg_type_get_prefix(ident->module, format, prefix_data), ident->name);
+ /* get the prefix, may be NULL for no prefix and the default namespace */
+ prefix = lyplg_type_get_prefix(ident->module, format, prefix_data);
+
+ if (prefix) {
+ len = asprintf(str, "%s:%s", prefix, ident->name);
+ } else {
+ len = asprintf(str, "%s", ident->name);
+ }
if (len == -1) {
return LY_EMEM;
}
@@ -270,8 +279,11 @@ lyplg_type_store_identityref(const struct ly_ctx *ctx, const struct lysc_type *t
}
} else {
/* JSON format with prefix is the canonical one */
- ret = identityref_ident2str(ident, LY_VALUE_JSON, NULL, &canon, NULL);
- LY_CHECK_GOTO(ret, cleanup);
+ if (asprintf(&canon, "%s:%s", ident->module->name, ident->name) == -1) {
+ LOGMEM(ctx);
+ ret = LY_EMEM;
+ goto cleanup;
+ }
ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
LY_CHECK_GOTO(ret, cleanup);
@@ -291,10 +303,6 @@ cleanup:
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_identityref(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
if (val1->ident == val2->ident) {
return LY_SUCCESS;
}
@@ -307,7 +315,7 @@ lyplg_type_print_identityref(const struct ly_ctx *UNUSED(ctx), const struct lyd_
{
char *ret;
- if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
+ if (format == LY_VALUE_CANON) {
if (dynamic) {
*dynamic = 0;
}
diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c
index d151d0a..c15ff64 100644
--- a/src/plugins_types/instanceid.c
+++ b/src/plugins_types/instanceid.c
@@ -50,12 +50,19 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u, v;
char *result = NULL, quot;
- const struct lys_module *mod = NULL;
+ const struct lys_module *mod = NULL, *local_mod = NULL;
+ struct ly_set *mods = NULL;
ly_bool inherit_prefix = 0, d;
const char *strval;
switch (format) {
case LY_VALUE_XML:
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+
+ /* fallthrough */
case LY_VALUE_SCHEMA:
case LY_VALUE_SCHEMA_RESOLVED:
/* everything is prefixed */
@@ -84,9 +91,7 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr
LY_ARRAY_FOR(path[u].predicates, v) {
struct ly_path_predicate *pred = &path[u].predicates[v];
- switch (path[u].pred_type) {
- case LY_PATH_PREDTYPE_NONE:
- break;
+ switch (pred->type) {
case LY_PATH_PREDTYPE_POSITION:
/* position predicate */
ret = ly_strcat(&result, "[%" PRIu64 "]", pred->position);
@@ -127,6 +132,10 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr
free((char *)strval);
}
break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ LOGINT(path[u].node->module->ctx);
+ ret = LY_EINT;
+ goto cleanup;
}
LY_CHECK_GOTO(ret, cleanup);
@@ -134,6 +143,9 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr
}
cleanup:
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
if (ret) {
free(result);
} else {
@@ -162,7 +174,7 @@ lyplg_type_store_instanceid(const struct ly_ctx *ctx, const struct lysc_type *ty
/* compile instance-identifier into path */
if (format == LY_VALUE_LYB) {
- /* The @p value in LYB format is the same as in JSON format. */
+ /* value in LYB format is the same as in JSON format. */
ret = lyplg_type_lypath_new(ctx, value, value_len, options, LY_VALUE_JSON, prefix_data, ctx_node,
unres, &path, err);
} else {
@@ -231,7 +243,7 @@ lyplg_type_validate_instanceid(const struct ly_ctx *ctx, const struct lysc_type
}
/* find the target in data */
- if ((ret = ly_path_eval(storage->target, tree, NULL))) {
+ if ((ret = ly_path_eval(storage->target, tree, NULL, NULL))) {
value = lyplg_type_print_instanceid(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
path = lyd_path(ctx_node, LYD_PATH_STD, NULL, 0);
return ly_err_new(err, ret, LYVE_DATA, path, strdup("instance-required"), LY_ERRMSG_NOINST, value);
@@ -245,10 +257,6 @@ lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_val
{
LY_ARRAY_COUNT_TYPE u, v;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
if (val1 == val2) {
return LY_SUCCESS;
} else if (LY_ARRAY_COUNT(val1->target) != LY_ARRAY_COUNT(val2->target)) {
@@ -259,37 +267,43 @@ lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_val
struct ly_path *s1 = &val1->target[u];
struct ly_path *s2 = &val2->target[u];
- if ((s1->node != s2->node) || (s1->pred_type != s2->pred_type) ||
- (s1->predicates && (LY_ARRAY_COUNT(s1->predicates) != LY_ARRAY_COUNT(s2->predicates)))) {
+ if ((s1->node != s2->node) || (s1->predicates && (LY_ARRAY_COUNT(s1->predicates) != LY_ARRAY_COUNT(s2->predicates)))) {
return LY_ENOT;
}
- if (s1->predicates) {
- LY_ARRAY_FOR(s1->predicates, v) {
- struct ly_path_predicate *pred1 = &s1->predicates[v];
- struct ly_path_predicate *pred2 = &s2->predicates[v];
-
- switch (s1->pred_type) {
- case LY_PATH_PREDTYPE_NONE:
- break;
- case LY_PATH_PREDTYPE_POSITION:
- /* position predicate */
- if (pred1->position != pred2->position) {
- return LY_ENOT;
- }
- break;
- case LY_PATH_PREDTYPE_LIST:
- /* key-predicate */
- if ((pred1->key != pred2->key) ||
- ((struct lysc_node_leaf *)pred1->key)->type->plugin->compare(&pred1->value, &pred2->value)) {
- return LY_ENOT;
- }
- break;
- case LY_PATH_PREDTYPE_LEAFLIST:
- /* leaf-list predicate */
- if (((struct lysc_node_leaflist *)s1->node)->type->plugin->compare(&pred1->value, &pred2->value)) {
- return LY_ENOT;
- }
+ LY_ARRAY_FOR(s1->predicates, v) {
+ struct ly_path_predicate *pred1 = &s1->predicates[v];
+ struct ly_path_predicate *pred2 = &s2->predicates[v];
+
+ if (pred1->type != pred2->type) {
+ return LY_ENOT;
+ }
+
+ switch (pred1->type) {
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position predicate */
+ if (pred1->position != pred2->position) {
+ return LY_ENOT;
}
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ /* key-predicate */
+ if ((pred1->key != pred2->key) ||
+ ((struct lysc_node_leaf *)pred1->key)->type->plugin->compare(&pred1->value, &pred2->value)) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* leaf-list predicate */
+ if (((struct lysc_node_leaflist *)s1->node)->type->plugin->compare(&pred1->value, &pred2->value)) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ /* key-predicate with a variable */
+ if ((pred1->key != pred2->key) || strcmp(pred1->variable, pred2->variable)) {
+ return LY_ENOT;
+ }
+ break;
}
}
}
diff --git a/src/plugins_types/instanceid_keys.c b/src/plugins_types/instanceid_keys.c
index 0cd08f7..ab7751c 100644
--- a/src/plugins_types/instanceid_keys.c
+++ b/src/plugins_types/instanceid_keys.c
@@ -67,10 +67,18 @@ instanceid_keys_print_value(const struct lyd_value_instance_identifier_keys *val
void *mem;
const char *cur_exp_ptr;
ly_bool is_nt;
- const struct lys_module *context_mod = NULL;
+ const struct lys_module *context_mod = NULL, *local_mod = NULL;
+ struct ly_set *mods;
*str_value = NULL;
+ if (format == LY_VALUE_XML) {
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+ }
+
while (cur_idx < val->keys->used) {
cur_tok = val->keys->tokens[cur_idx];
cur_exp_ptr = val->keys->expr + val->keys->tok_pos[cur_idx];
@@ -79,11 +87,15 @@ instanceid_keys_print_value(const struct lyd_value_instance_identifier_keys *val
/* tokens that may include prefixes, get them in the target format */
is_nt = (cur_tok == LYXP_TOKEN_NAMETEST) ? 1 : 0;
LY_CHECK_GOTO(ret = lyplg_type_xpath10_print_token(cur_exp_ptr, val->keys->tok_len[cur_idx], is_nt, &context_mod,
- val->ctx, val->format, val->prefix_data, format, prefix_data, &str_tok, err), error);
+ val->ctx, val->format, val->prefix_data, format, prefix_data, &str_tok, err), cleanup);
/* append the converted token */
mem = realloc(*str_value, str_len + strlen(str_tok) + 1);
- LY_CHECK_ERR_GOTO(!mem, free(str_tok), error_mem);
+ if (!mem) {
+ free(str_tok);
+ ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
+ goto cleanup;
+ }
*str_value = mem;
str_len += sprintf(*str_value + str_len, "%s", str_tok);
free(str_tok);
@@ -93,7 +105,10 @@ instanceid_keys_print_value(const struct lyd_value_instance_identifier_keys *val
} else {
/* just copy the token */
mem = realloc(*str_value, str_len + val->keys->tok_len[cur_idx] + 1);
- LY_CHECK_GOTO(!mem, error_mem);
+ if (!mem) {
+ ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
+ goto cleanup;
+ }
*str_value = mem;
str_len += sprintf(*str_value + str_len, "%.*s", (int)val->keys->tok_len[cur_idx], cur_exp_ptr);
@@ -102,13 +117,14 @@ instanceid_keys_print_value(const struct lyd_value_instance_identifier_keys *val
}
}
- return LY_SUCCESS;
-
-error_mem:
- ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
-
-error:
- free(*str_value);
+cleanup:
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
+ if (ret) {
+ free(*str_value);
+ *str_value = NULL;
+ }
return ret;
}
@@ -123,6 +139,7 @@ lyplg_type_store_instanceid_keys(const struct ly_ctx *ctx, const struct lysc_typ
LY_ERR ret = LY_SUCCESS;
struct lysc_type_str *type_str = (struct lysc_type_str *)type;
struct lyd_value_instance_identifier_keys *val;
+ uint32_t log_opts = LY_LOSTORE;
char *canon;
/* init storage */
@@ -152,10 +169,15 @@ lyplg_type_store_instanceid_keys(const struct ly_ctx *ctx, const struct lysc_typ
((char *)value)[0]);
goto cleanup;
}
+
+ /* do not log */
+ ly_temp_log_options(&log_opts);
ret = ly_path_parse_predicate(ctx, NULL, value_len ? value : "", value_len, LY_PATH_PREFIX_OPTIONAL,
LY_PATH_PRED_KEYS, &val->keys);
+ ly_temp_log_options(NULL);
if (ret) {
ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL, "%s", ly_errmsg(ctx));
+ ly_err_clean((struct ly_ctx *)ctx, NULL);
goto cleanup;
}
val->ctx = ctx;
diff --git a/src/plugins_types/integer.c b/src/plugins_types/integer.c
index 0903365..05d6801 100644
--- a/src/plugins_types/integer.c
+++ b/src/plugins_types/integer.c
@@ -373,10 +373,6 @@ cleanup:
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_uint(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
switch (val1->realtype->basetype) {
case LY_TYPE_UINT8:
if (val1->uint8 != val2->uint8) {
diff --git a/src/plugins_types/ipv4_address.c b/src/plugins_types/ipv4_address.c
index f7b297c..9e54a0b 100644
--- a/src/plugins_types/ipv4_address.c
+++ b/src/plugins_types/ipv4_address.c
@@ -214,10 +214,6 @@ lyplg_type_compare_ipv4_address(const struct lyd_value *val1, const struct lyd_v
{
struct lyd_value_ipv4_address *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/ipv4_address_no_zone.c b/src/plugins_types/ipv4_address_no_zone.c
index 91fe677..a693912 100644
--- a/src/plugins_types/ipv4_address_no_zone.c
+++ b/src/plugins_types/ipv4_address_no_zone.c
@@ -132,10 +132,6 @@ lyplg_type_compare_ipv4_address_no_zone(const struct lyd_value *val1, const stru
{
struct lyd_value_ipv4_address_no_zone *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/ipv4_prefix.c b/src/plugins_types/ipv4_prefix.c
index 6f13eee..b8d77b5 100644
--- a/src/plugins_types/ipv4_prefix.c
+++ b/src/plugins_types/ipv4_prefix.c
@@ -203,10 +203,6 @@ lyplg_type_compare_ipv4_prefix(const struct lyd_value *val1, const struct lyd_va
{
struct lyd_value_ipv4_prefix *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/ipv6_address.c b/src/plugins_types/ipv6_address.c
index 74f5c62..344a7fc 100644
--- a/src/plugins_types/ipv6_address.c
+++ b/src/plugins_types/ipv6_address.c
@@ -216,10 +216,6 @@ lyplg_type_compare_ipv6_address(const struct lyd_value *val1, const struct lyd_v
{
struct lyd_value_ipv6_address *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/ipv6_address_no_zone.c b/src/plugins_types/ipv6_address_no_zone.c
index 26fbf80..98ca754 100644
--- a/src/plugins_types/ipv6_address_no_zone.c
+++ b/src/plugins_types/ipv6_address_no_zone.c
@@ -180,10 +180,6 @@ lyplg_type_compare_ipv6_address_no_zone(const struct lyd_value *val1, const stru
{
struct lyd_value_ipv6_address_no_zone *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/ipv6_prefix.c b/src/plugins_types/ipv6_prefix.c
index 8e62311..aaec395 100644
--- a/src/plugins_types/ipv6_prefix.c
+++ b/src/plugins_types/ipv6_prefix.c
@@ -217,10 +217,6 @@ lyplg_type_compare_ipv6_prefix(const struct lyd_value *val1, const struct lyd_va
{
struct lyd_value_ipv6_prefix *v1, *v2;
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
LYD_VALUE_GET(val1, v1);
LYD_VALUE_GET(val2, v2);
diff --git a/src/plugins_types/node_instanceid.c b/src/plugins_types/node_instanceid.c
index 04fb164..7833263 100644
--- a/src/plugins_types/node_instanceid.c
+++ b/src/plugins_types/node_instanceid.c
@@ -50,7 +50,8 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u, v;
char *result = NULL, quot;
- const struct lys_module *mod = NULL;
+ const struct lys_module *mod = NULL, *local_mod = NULL;
+ struct ly_set *mods;
ly_bool inherit_prefix = 0, d;
const char *strval;
@@ -62,6 +63,12 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi
switch (format) {
case LY_VALUE_XML:
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+
+ /* fallthrough */
case LY_VALUE_SCHEMA:
case LY_VALUE_SCHEMA_RESOLVED:
/* everything is prefixed */
@@ -90,9 +97,7 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi
LY_ARRAY_FOR(path[u].predicates, v) {
struct ly_path_predicate *pred = &path[u].predicates[v];
- switch (path[u].pred_type) {
- case LY_PATH_PREDTYPE_NONE:
- break;
+ switch (pred->type) {
case LY_PATH_PREDTYPE_POSITION:
/* position predicate */
ret = ly_strcat(&result, "[%" PRIu64 "]", pred->position);
@@ -133,6 +138,16 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi
free((char *)strval);
}
break;
+ case LY_PATH_PREDTYPE_LIST_VAR:
+ /* key-predicate with a variable */
+ if (inherit_prefix) {
+ /* always the same prefix as the parent */
+ ret = ly_strcat(&result, "[%s=$%s]", pred->key->name, pred->variable);
+ } else {
+ ret = ly_strcat(&result, "[%s:%s=$%s]", lyplg_type_get_prefix(pred->key->module, format, prefix_data),
+ pred->key->name, pred->variable);
+ }
+ break;
}
LY_CHECK_GOTO(ret, cleanup);
@@ -140,6 +155,9 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi
}
cleanup:
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
if (ret) {
free(result);
} else {
@@ -193,7 +211,7 @@ lyplg_type_store_node_instanceid(const struct ly_ctx *ctx, const struct lysc_typ
ret = ly_path_parse(ctx, ctx_node, value, value_len, 0, LY_PATH_BEGIN_ABSOLUTE, prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
if (ret) {
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
- "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, (char *)value);
+ "Invalid node-instance-identifier \"%.*s\" value - syntax error.", (int)value_len, (char *)value);
goto cleanup;
}
@@ -209,7 +227,7 @@ lyplg_type_store_node_instanceid(const struct ly_ctx *ctx, const struct lysc_typ
LY_VALUE_JSON : format, prefix_data, &path);
if (ret) {
ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL,
- "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, (char *)value);
+ "Invalid node-instance-identifier \"%.*s\" value - semantic error.", (int)value_len, (char *)value);
goto cleanup;
}
diff --git a/src/plugins_types/string.c b/src/plugins_types/string.c
index 4f988ef..f8143d0 100644
--- a/src/plugins_types/string.c
+++ b/src/plugins_types/string.c
@@ -1,9 +1,10 @@
/**
* @file string.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Built-in string type plugin.
*
- * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ * Copyright (c) 2019 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -33,6 +34,29 @@
* | string length | yes | `char *` | string itself |
*/
+/**
+ * @brief Check string value for invalid characters.
+ *
+ * @param[in] value String to check.
+ * @param[in] value_len Length of @p value.
+ * @param[out] err Generated error on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+string_check_chars(const char *value, size_t value_len, struct ly_err_item **err)
+{
+ size_t len, parsed = 0;
+
+ while (value_len - parsed) {
+ if (ly_checkutf8(value + parsed, value_len - parsed, &len)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character 0x%hhx.", value[parsed]);
+ }
+ parsed += len;
+ }
+
+ return LY_SUCCESS;
+}
+
LIBYANG_API_DEF LY_ERR
lyplg_type_store_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
@@ -46,6 +70,12 @@ lyplg_type_store_string(const struct ly_ctx *ctx, const struct lysc_type *type,
memset(storage, 0, sizeof *storage);
storage->realtype = type;
+ if (!(options & LYPLG_TYPE_STORE_IS_UTF8)) {
+ /* check the UTF-8 encoding */
+ ret = string_check_chars(value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
/* check hints */
ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
LY_CHECK_GOTO(ret, cleanup);
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index 6e31d1e..a5b7610 100644
--- a/src/plugins_types/union.c
+++ b/src/plugins_types/union.c
@@ -172,6 +172,8 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, struct lyd_va
const void *value = NULL;
size_t value_len = 0;
+ *err = NULL;
+
if (subvalue->format == LY_VALUE_LYB) {
lyb_parse_union(subvalue->original, subvalue->orig_len, NULL, &value, &value_len);
} else {
@@ -220,36 +222,63 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_v
{
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u;
+ struct ly_err_item **errs = NULL, *e;
uint32_t temp_lo = 0;
+ char *msg = NULL;
+ int msg_len = 0;
+
+ *err = NULL;
if (!types || !LY_ARRAY_COUNT(types)) {
return LY_EINVAL;
}
- *err = NULL;
+ /* alloc errors */
+ errs = calloc(LY_ARRAY_COUNT(types), sizeof *errs);
+ LY_CHECK_RET(!errs, LY_EMEM);
/* turn logging temporarily off */
ly_temp_log_options(&temp_lo);
/* use the first usable subtype to store the value */
for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
- ret = union_store_type(ctx, types[u], subvalue, resolve, ctx_node, tree, unres, err);
+ ret = union_store_type(ctx, types[u], subvalue, resolve, ctx_node, tree, unres, &e);
if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) {
break;
}
- ly_err_free(*err);
- *err = NULL;
+ errs[u] = e;
}
if (u == LY_ARRAY_COUNT(types)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid union value \"%.*s\" - no matching subtype found.",
+ /* create the full error */
+ msg_len = asprintf(&msg, "Invalid union value \"%.*s\" - no matching subtype found:\n",
(int)subvalue->orig_len, (char *)subvalue->original);
+ if (msg_len == -1) {
+ LY_CHECK_ERR_GOTO(!errs, ret = LY_EMEM, cleanup);
+ }
+ for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+ if (!errs[u]) {
+ /* no error for some reason */
+ continue;
+ }
+
+ msg = ly_realloc(msg, msg_len + 4 + strlen(types[u]->plugin->id) + 2 + strlen(errs[u]->msg) + 2);
+ LY_CHECK_ERR_GOTO(!msg, ret = LY_EMEM, cleanup);
+ msg_len += sprintf(msg + msg_len, " %s: %s\n", types[u]->plugin->id, errs[u]->msg);
+ }
+
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "%s", msg);
} else if (type_idx) {
*type_idx = u;
}
- /* restore logging */
+cleanup:
+ for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+ ly_err_free(errs[u]);
+ }
+ free(errs);
+ free(msg);
ly_temp_log_options(NULL);
return ret;
}
@@ -281,29 +310,26 @@ lyb_fill_subvalue(const struct ly_ctx *ctx, struct lysc_type_union *type_u, cons
ret = lyb_union_validate(lyb_data, lyb_data_len, type_u, err);
LY_CHECK_RET(ret);
- /* Parse lyb_data and set the lyb_value and lyb_value_len. */
+ /* parse lyb_data and set the lyb_value and lyb_value_len */
lyb_parse_union(lyb_data, lyb_data_len, &type_idx, &lyb_value, &lyb_value_len);
LY_CHECK_RET(ret);
- /* Store lyb_data to subvalue. */
- ret = union_subvalue_assignment(lyb_data, lyb_data_len,
- &subvalue->original, &subvalue->orig_len, options);
+ /* store lyb_data to subvalue */
+ ret = union_subvalue_assignment(lyb_data, lyb_data_len, &subvalue->original, &subvalue->orig_len, options);
LY_CHECK_RET(ret);
if (lyb_value) {
- /* Resolve prefix_data and set format. */
+ /* resolve prefix_data and set format */
ret = lyplg_type_prefix_data_new(ctx, lyb_value, lyb_value_len, LY_VALUE_LYB, prefix_data, &subvalue->format,
&subvalue->prefix_data);
LY_CHECK_RET(ret);
assert(subvalue->format == LY_VALUE_LYB);
} else {
- /* The lyb_parse_union() did not find lyb_value.
- * Just set format.
- */
+ /* lyb_parse_union() did not find lyb_value, just set format */
subvalue->format = LY_VALUE_LYB;
}
- /* Use the specific type to store the value. */
+ /* use the specific type to store the value */
ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 0, NULL, NULL, unres, err);
return ret;
@@ -329,13 +355,11 @@ lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, c
subvalue->ctx_node = ctx_node;
if (format == LY_VALUE_LYB) {
- ret = lyb_fill_subvalue(ctx, type_u, value, value_len,
- prefix_data, subvalue, &options, unres, err);
+ ret = lyb_fill_subvalue(ctx, type_u, value, value_len, prefix_data, subvalue, &options, unres, err);
LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
} else {
/* Store @p value to subvalue. */
- ret = union_subvalue_assignment(value, value_len,
- &subvalue->original, &subvalue->orig_len, &options);
+ ret = union_subvalue_assignment(value, value_len, &subvalue->original, &subvalue->orig_len, &options);
LY_CHECK_GOTO(ret, cleanup);
/* store format-specific data for later prefix resolution */
@@ -370,6 +394,7 @@ lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type
LY_ERR ret = LY_SUCCESS;
struct lysc_type_union *type_u = (struct lysc_type_union *)type;
struct lyd_value_union *subvalue = storage->subvalue;
+ uint32_t type_idx;
*err = NULL;
@@ -378,9 +403,16 @@ lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type
* we have to perform union value storing again from scratch */
subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
- /* use the first usable subtype to store the value */
- ret = union_find_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, NULL, err);
- LY_CHECK_RET(ret);
+ if (subvalue->format == LY_VALUE_LYB) {
+ /* use the specific type to store the value */
+ lyb_parse_union(subvalue->original, 0, &type_idx, NULL, NULL);
+ ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 1, ctx_node, tree, NULL, err);
+ LY_CHECK_RET(ret);
+ } else {
+ /* use the first usable subtype to store the value */
+ ret = union_find_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, NULL, err);
+ LY_CHECK_RET(ret);
+ }
/* success, update the canonical value, if any generated */
lydict_remove(ctx, storage->_canonical);
@@ -391,10 +423,6 @@ lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_union(const struct lyd_value *val1, const struct lyd_value *val2)
{
- if (val1->realtype != val2->realtype) {
- return LY_ENOT;
- }
-
if (val1->subvalue->value.realtype != val2->subvalue->value.realtype) {
return LY_ENOT;
}
@@ -418,10 +446,10 @@ lyb_union_print(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct
void *prefix_data, size_t *value_len)
{
void *ret = NULL;
- LY_ERR retval;
+ LY_ERR r;
struct ly_err_item *err;
uint64_t num = 0;
- uint32_t type_idx;
+ uint32_t type_idx = 0;
ly_bool dynamic;
size_t pval_len;
void *pval;
@@ -435,8 +463,9 @@ lyb_union_print(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct
ctx = subvalue->ctx_node->module->ctx;
}
subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
- retval = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, &type_idx, NULL, &err);
- LY_CHECK_RET((retval != LY_SUCCESS) && (retval != LY_EINCOMPLETE), NULL);
+ r = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, &type_idx, NULL, &err);
+ ly_err_free(err);
+ LY_CHECK_RET((r != LY_SUCCESS) && (r != LY_EINCOMPLETE), NULL);
/* Print subvalue in LYB format. */
pval = (void *)subvalue->value.realtype->plugin->print(NULL, &subvalue->value, LY_VALUE_LYB, prefix_data, &dynamic,
diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c
index a15e5b7..fdfc2a9 100644
--- a/src/plugins_types/xpath1.0.c
+++ b/src/plugins_types/xpath1.0.c
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief ietf-yang-types xpath1.0 type plugin.
*
- * Copyright (c) 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2021 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -57,12 +57,9 @@ lyplg_type_xpath10_print_token(const char *token, uint16_t tok_len, ly_bool is_n
while (!(ret = ly_value_prefix_next(str_begin, token + tok_len, &len, &is_prefix, &str_next)) && len) {
if (!is_prefix) {
if (!has_prefix && is_nametest && (get_format == LY_VALUE_XML) && *context_mod) {
- /* prefix is always needed, get it in the target format */
+ /* get the prefix */
prefix = lyplg_type_get_prefix(*context_mod, get_format, get_prefix_data);
- if (!prefix) {
- ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Internal error.");
- goto cleanup;
- }
+ assert(prefix);
/* append the nametest and prefix */
mem = realloc(str, str_len + strlen(prefix) + 1 + len + 1);
@@ -94,10 +91,7 @@ lyplg_type_xpath10_print_token(const char *token, uint16_t tok_len, ly_bool is_n
if (mod) {
/* get the prefix in the target format */
prefix = lyplg_type_get_prefix(mod, get_format, get_prefix_data);
- if (!prefix) {
- ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Internal error.");
- goto cleanup;
- }
+ assert(prefix);
pref_len = strlen(prefix);
} else {
/* invalid prefix, just copy it */
@@ -223,13 +217,25 @@ lyplg_type_print_xpath10_value(const struct lyd_value_xpath10 *xp_val, LY_VALUE_
LY_ERR ret = LY_SUCCESS;
uint16_t expr_idx = 0;
uint32_t str_len = 0;
+ const struct lys_module *local_mod = NULL;
+ struct ly_set *mods;
*str_value = NULL;
*err = NULL;
+ if (format == LY_VALUE_XML) {
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+ }
+
/* recursively print the expression */
ret = xpath10_print_subexpr_r(&expr_idx, 0, NULL, xp_val, format, prefix_data, str_value, &str_len, err);
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
if (ret) {
free(*str_value);
*str_value = NULL;
@@ -326,6 +332,49 @@ cleanup:
}
/**
+ * @brief Create a namespace and add it into a set.
+ *
+ * @param[in] set Set of namespaces to add to.
+ * @param[in] pref Namespace prefix.
+ * @param[in] uri Namespace URI.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xpath10_add_ns(struct ly_set *set, const char *pref, const char *uri)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyxml_ns *ns = NULL;
+
+ /* create new ns */
+ ns = calloc(1, sizeof *ns);
+ if (!ns) {
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ ns->prefix = strdup(pref);
+ ns->uri = strdup(uri);
+ if (!ns->prefix || !ns->uri) {
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ ns->depth = 1;
+
+ /* add into the XML namespace set */
+ if ((rc = ly_set_add(set, ns, 1, NULL))) {
+ goto cleanup;
+ }
+ ns = NULL;
+
+cleanup:
+ if (ns) {
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
+ }
+ return rc;
+}
+
+/**
* @brief Implementation of ::lyplg_type_validate_clb for the xpath1.0 ietf-yang-types type.
*/
static LY_ERR
@@ -338,7 +387,6 @@ lyplg_type_validate_xpath10(const struct ly_ctx *UNUSED(ctx), const struct lysc_
struct ly_set *set = NULL;
uint32_t i;
const char *pref, *uri;
- struct lyxml_ns *ns;
*err = NULL;
LYD_VALUE_GET(storage, val);
@@ -371,28 +419,8 @@ lyplg_type_validate_xpath10(const struct ly_ctx *UNUSED(ctx), const struct lysc_
assert(!strcmp(LYD_NAME(lyd_child(set->dnodes[i])->next), "uri"));
uri = lyd_get_value(lyd_child(set->dnodes[i])->next);
- /* create new ns */
- ns = calloc(1, sizeof *ns);
- if (!ns) {
- ret = LY_EMEM;
- goto cleanup;
- }
- ns->prefix = strdup(pref);
- ns->uri = strdup(uri);
- if (!ns->prefix || !ns->uri) {
- free(ns->prefix);
- free(ns->uri);
- free(ns);
- ret = LY_EMEM;
- goto cleanup;
- }
- ns->depth = 1;
-
- /* add into the XML namespace set */
- if ((ret = ly_set_add(val->prefix_data, ns, 1, NULL))) {
- free(ns->prefix);
- free(ns->uri);
- free(ns);
+ /* new NS */
+ if ((ret = xpath10_add_ns(val->prefix_data, pref, uri))) {
goto cleanup;
}
}
diff --git a/src/printer_json.c b/src/printer_json.c
index 327799b..66ae154 100644
--- a/src/printer_json.c
+++ b/src/printer_json.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief JSON printer for libyang data structure
*
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
*/
#include <assert.h>
+#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
@@ -45,7 +46,7 @@ struct jsonpr_ctx {
uint16_t level_printed; /* level where some data were already printed */
struct ly_set open; /* currently open array(s) */
- const struct lyd_node *print_sibling_metadata;
+ const struct lyd_node *first_leaflist; /**< first printed leaf-list instance, used when printing its metadata/attributes */
};
/**
@@ -219,31 +220,38 @@ json_nscmp(const struct lyd_node *node1, const struct lyd_node *node2)
static LY_ERR
json_print_string(struct ly_out *out, const char *text)
{
- uint64_t i, n;
+ uint64_t i;
if (!text) {
return LY_SUCCESS;
}
ly_write_(out, "\"", 1);
- for (i = n = 0; text[i]; i++) {
- const unsigned char ascii = text[i];
+ for (i = 0; text[i]; i++) {
+ const unsigned char byte = text[i];
- if (ascii < 0x20) {
- /* control character */
- ly_print_(out, "\\u%.4X", ascii);
- } else {
- switch (ascii) {
- case '"':
- ly_print_(out, "\\\"");
- break;
- case '\\':
- ly_print_(out, "\\\\");
- break;
- default:
+ switch (byte) {
+ case '"':
+ ly_print_(out, "\\\"");
+ break;
+ case '\\':
+ ly_print_(out, "\\\\");
+ break;
+ case '\r':
+ ly_print_(out, "\\r");
+ break;
+ case '\t':
+ ly_print_(out, "\\t");
+ break;
+ default:
+ if (iscntrl(byte)) {
+ /* control character */
+ ly_print_(out, "\\u%.4X", byte);
+ } else {
+ /* printable character (even non-ASCII UTF8) */
ly_write_(out, &text[i], 1);
- n++;
}
+ break;
}
}
ly_write_(out, "\"", 1);
@@ -294,18 +302,21 @@ json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VA
/* determine prefix string */
if (name) {
- const struct lys_module *mod;
-
switch (format) {
case LY_VALUE_JSON:
module_name = name->module_name;
break;
- case LY_VALUE_XML:
- mod = ly_ctx_get_module_implemented_ns(pctx->ctx, name->module_ns);
+ case LY_VALUE_XML: {
+ const struct lys_module *mod = NULL;
+
+ if (name->module_ns) {
+ mod = ly_ctx_get_module_implemented_ns(pctx->ctx, name->module_ns);
+ }
if (mod) {
module_name = mod->name;
}
break;
+ }
default:
/* cannot be created */
LOGINT_RET(pctx->ctx);
@@ -332,15 +343,19 @@ json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VA
* @param[in] pctx JSON printer context.
* @param[in] ctx Context used to print the value.
* @param[in] val Data value to be printed.
+ * @param[in] local_mod Module of the current node.
* @return LY_ERR value.
*/
static LY_ERR
-json_print_value(struct jsonpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val)
+json_print_value(struct jsonpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val,
+ const struct lys_module *local_mod)
{
ly_bool dynamic;
LY_DATA_TYPE basetype;
- const char *value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, NULL, &dynamic, NULL);
+ const char *value;
+ value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, (void *)local_mod, &dynamic, NULL);
+ LY_CHECK_RET(!value, LY_EINVAL);
basetype = val->realtype->basetype;
print_val:
@@ -348,7 +363,8 @@ print_val:
switch (basetype) {
case LY_TYPE_UNION:
/* use the resolved type */
- basetype = val->subvalue->value.realtype->basetype;
+ val = &val->subvalue->value;
+ basetype = val->realtype->basetype;
goto print_val;
case LY_TYPE_BINARY:
@@ -394,19 +410,13 @@ print_val:
*
* @param[in] ctx JSON printer context.
* @param[in] node Opaq node where the attributes are placed.
- * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed.
* @return LY_ERR value.
*/
static LY_ERR
-json_print_attribute(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node, const struct lys_module *wdmod)
+json_print_attribute(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node)
{
struct lyd_attr *attr;
- if (wdmod) {
- ly_print_(pctx->out, "%*s\"%s:default\":true", INDENT, wdmod->name);
- LEVEL_PRINTED;
- }
-
for (attr = node->attr; attr; attr = attr->next) {
json_print_member2(pctx, &node->node, attr->format, &attr->name, 0);
@@ -440,14 +450,14 @@ json_print_metadata(struct jsonpr_ctx *pctx, const struct lyd_node *node, const
struct lyd_meta *meta;
if (wdmod) {
- ly_print_(pctx->out, "%*s\"%s:default\":true", INDENT, wdmod->name);
+ ly_print_(pctx->out, "%*s\"%s:default\":%strue", INDENT, wdmod->name, DO_FORMAT ? " " : "");
LEVEL_PRINTED;
}
for (meta = node->meta; meta; meta = meta->next) {
PRINT_COMMA;
ly_print_(pctx->out, "%*s\"%s:%s\":%s", INDENT, meta->annotation->module->name, meta->name, DO_FORMAT ? " " : "");
- LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &meta->value));
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &meta->value, NULL));
LEVEL_PRINTED;
}
@@ -495,7 +505,7 @@ json_print_attributes(struct jsonpr_ctx *pctx, const struct lyd_node *node, ly_b
}
ly_print_(pctx->out, "{%s", (DO_FORMAT ? "\n" : ""));
LEVEL_INC;
- LY_CHECK_RET(json_print_attribute(pctx, (struct lyd_node_opaq *)node, wdmod));
+ LY_CHECK_RET(json_print_attribute(pctx, (struct lyd_node_opaq *)node));
LEVEL_DEC;
ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
LEVEL_PRINTED;
@@ -515,7 +525,7 @@ static LY_ERR
json_print_leaf(struct jsonpr_ctx *pctx, const struct lyd_node *node)
{
LY_CHECK_RET(json_print_member(pctx, node, 0));
- LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, node->schema->module));
LEVEL_PRINTED;
/* print attributes as sibling */
@@ -780,16 +790,16 @@ json_print_leaf_list(struct jsonpr_ctx *pctx, const struct lyd_node *node)
} else {
assert(node->schema->nodetype == LYS_LEAFLIST);
- LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, node->schema->module));
- if (!pctx->print_sibling_metadata) {
+ if (!pctx->first_leaflist) {
if ((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) {
/* we have implicit OR explicit default node, get with-defaults module */
wdmod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-netconf-with-defaults");
}
if (node->meta || wdmod) {
/* we will be printing metadata for these siblings */
- pctx->print_sibling_metadata = node;
+ pctx->first_leaflist = node;
}
}
}
@@ -802,21 +812,20 @@ json_print_leaf_list(struct jsonpr_ctx *pctx, const struct lyd_node *node)
}
/**
- * @brief Print leaf-list's metadata in case they were marked in the last call to json_print_leaf_list().
+ * @brief Print leaf-list's metadata or opaque nodes attributes.
* This function is supposed to be called when the leaf-list array is closed.
*
* @param[in] ctx JSON printer context.
* @return LY_ERR value.
*/
static LY_ERR
-json_print_metadata_leaflist(struct jsonpr_ctx *pctx)
+json_print_meta_attr_leaflist(struct jsonpr_ctx *pctx)
{
const struct lyd_node *prev, *node, *iter;
const struct lys_module *wdmod = NULL;
+ const struct lyd_node_opaq *opaq = NULL;
- if (!pctx->print_sibling_metadata) {
- return LY_SUCCESS;
- }
+ assert(pctx->first_leaflist);
if (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG)) {
/* get with-defaults module */
@@ -824,19 +833,31 @@ json_print_metadata_leaflist(struct jsonpr_ctx *pctx)
}
/* node is the first instance of the leaf-list */
- for (node = pctx->print_sibling_metadata, prev = pctx->print_sibling_metadata->prev;
+ for (node = pctx->first_leaflist, prev = pctx->first_leaflist->prev;
prev->next && matching_node(node, prev);
node = prev, prev = node->prev) {}
- LY_CHECK_RET(json_print_member(pctx, node, 1));
+ if (node->schema) {
+ LY_CHECK_RET(json_print_member(pctx, node, 1));
+ } else {
+ opaq = (struct lyd_node_opaq *)node;
+ LY_CHECK_RET(json_print_member2(pctx, lyd_parent(node), opaq->format, &opaq->name, 1));
+ }
+
ly_print_(pctx->out, "[%s", (DO_FORMAT ? "\n" : ""));
LEVEL_INC;
LY_LIST_FOR(node, iter) {
PRINT_COMMA;
- if (iter->meta || (iter->flags & LYD_DEFAULT)) {
+ if ((iter->schema && (iter->meta || (iter->flags & LYD_DEFAULT))) || (opaq && opaq->attr)) {
ly_print_(pctx->out, "%*s%s", INDENT, DO_FORMAT ? "{\n" : "{");
LEVEL_INC;
- LY_CHECK_RET(json_print_metadata(pctx, iter, (iter->flags & LYD_DEFAULT) ? wdmod : NULL));
+
+ if (iter->schema) {
+ LY_CHECK_RET(json_print_metadata(pctx, iter, (iter->flags & LYD_DEFAULT) ? wdmod : NULL));
+ } else {
+ LY_CHECK_RET(json_print_attribute(pctx, (struct lyd_node_opaq *)iter));
+ }
+
LEVEL_DEC;
ly_print_(pctx->out, "%s%*s}", DO_FORMAT ? "\n" : "", INDENT);
} else {
@@ -897,12 +918,17 @@ json_print_opaq(struct jsonpr_ctx *pctx, const struct lyd_node_opaq *node)
ly_print_(pctx->out, "%s", node->value);
} else {
/* string or a large number */
- ly_print_(pctx->out, "\"%s\"", node->value);
+ json_print_string(pctx->out, node->value);
}
LEVEL_PRINTED;
- /* attributes */
- json_print_attributes(pctx, (const struct lyd_node *)node, 0);
+ if (!(node->hints & LYD_NODEHINT_LEAFLIST)) {
+ /* attributes */
+ json_print_attributes(pctx, (const struct lyd_node *)node, 0);
+ } else if (!pctx->first_leaflist && node->attr) {
+ /* attributes printed later */
+ pctx->first_leaflist = &node->node;
+ }
}
if (last && (node->hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) {
@@ -959,9 +985,9 @@ json_print_node(struct jsonpr_ctx *pctx, const struct lyd_node *node)
pctx->level_printed = pctx->level;
- if (pctx->print_sibling_metadata && !matching_node(node->next, pctx->print_sibling_metadata)) {
- json_print_metadata_leaflist(pctx);
- pctx->print_sibling_metadata = NULL;
+ if (pctx->first_leaflist && !matching_node(node->next, pctx->first_leaflist)) {
+ json_print_meta_attr_leaflist(pctx);
+ pctx->first_leaflist = NULL;
}
return LY_SUCCESS;
diff --git a/src/printer_lyb.c b/src/printer_lyb.c
index 686c2d8..820e81e 100644
--- a/src/printer_lyb.c
+++ b/src/printer_lyb.c
@@ -81,7 +81,7 @@ lyb_ptr_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(c
* @return LY_EEXIST when the whole hash sequence sollides.
*/
static LY_ERR
-lyb_hash_sequence_check(struct hash_table *ht, struct lysc_node *sibling, LYB_HASH ht_col_id, LYB_HASH compare_col_id)
+lyb_hash_sequence_check(struct ly_ht *ht, struct lysc_node *sibling, LYB_HASH ht_col_id, LYB_HASH compare_col_id)
{
struct lysc_node **col_node;
@@ -123,9 +123,9 @@ lyb_hash_sequence_check(struct hash_table *ht, struct lysc_node *sibling, LYB_HA
* @return LY_ERR value.
*/
static LY_ERR
-lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p)
+lyb_hash_siblings(struct lysc_node *sibling, struct ly_ht **ht_p)
{
- struct hash_table *ht;
+ struct ly_ht *ht;
const struct lysc_node *parent;
const struct lys_module *mod;
LYB_HASH i;
@@ -172,7 +172,7 @@ lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p)
if (lyht_insert(ht, &sibling, lyb_get_hash(sibling, i), NULL)) {
LOGINT(sibling->module->ctx);
lyht_set_cb(ht, lyb_hash_equal_cb);
- lyht_free(ht);
+ lyht_free(ht, NULL);
return LY_EINT;
}
lyht_set_cb(ht, lyb_hash_equal_cb);
@@ -184,7 +184,7 @@ lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p)
if (i == LYB_HASH_BITS) {
/* wow */
LOGINT(sibling->module->ctx);
- lyht_free(ht);
+ lyht_free(ht, NULL);
return LY_EINT;
}
}
@@ -205,7 +205,7 @@ lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p)
* @return LY_ERR value.
*/
static LY_ERR
-lyb_hash_find(struct hash_table *ht, struct lysc_node *node, LYB_HASH *hash_p)
+lyb_hash_find(struct ly_ht *ht, struct lysc_node *node, LYB_HASH *hash_p)
{
LYB_HASH hash;
uint32_t i;
@@ -701,7 +701,7 @@ lyb_print_term_value(struct lyd_node_term *term, struct ly_out *out, struct lyly
if (value_len > UINT32_MAX) {
LOGERR(lybctx->ctx, LY_EINT, "The maximum length of the LYB data "
- "from a term node must not exceed %lu.", UINT32_MAX);
+ "from a term node must not exceed %" PRIu32 ".", UINT32_MAX);
ret = LY_EINT;
goto cleanup;
}
@@ -856,7 +856,7 @@ lyb_print_attributes(struct ly_out *out, const struct lyd_node_opaq *node, struc
* @return LY_ERR value.
*/
static LY_ERR
-lyb_print_schema_hash(struct ly_out *out, struct lysc_node *schema, struct hash_table **sibling_ht, struct lylyb_ctx *lybctx)
+lyb_print_schema_hash(struct ly_out *out, struct lysc_node *schema, struct ly_ht **sibling_ht, struct lylyb_ctx *lybctx)
{
LY_ARRAY_COUNT_TYPE u;
uint32_t i;
@@ -1205,7 +1205,7 @@ lyb_print_node_list(struct ly_out *out, const struct lyd_node *node, struct lyd_
* @return LY_ERR value.
*/
static LY_ERR
-lyb_print_node(struct ly_out *out, const struct lyd_node **printed_node, struct hash_table **sibling_ht,
+lyb_print_node(struct ly_out *out, const struct lyd_node **printed_node, struct ly_ht **sibling_ht,
struct lyd_lyb_ctx *lybctx)
{
const struct lyd_node *node = *printed_node;
@@ -1256,32 +1256,23 @@ lyb_print_node(struct ly_out *out, const struct lyd_node **printed_node, struct
static LY_ERR
lyb_print_siblings(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx)
{
- struct hash_table *sibling_ht = NULL;
+ struct ly_ht *sibling_ht = NULL;
const struct lys_module *prev_mod = NULL;
- ly_bool top_level;
-
- top_level = !LY_ARRAY_COUNT(lybctx->lybctx->siblings);
LY_CHECK_RET(lyb_write_start_siblings(out, lybctx->lybctx));
- if (top_level) {
- /* write all the siblings */
- LY_LIST_FOR(node, node) {
- /* do not reuse sibling hash tables from different modules */
- if (!node->schema || (node->schema->module != prev_mod)) {
- sibling_ht = NULL;
- prev_mod = node->schema ? node->schema->module : NULL;
- }
+ /* write all the siblings */
+ LY_LIST_FOR(node, node) {
+ /* do not reuse top-level sibling hash tables from different modules */
+ if (!node->schema || (!lysc_data_parent(node->schema) && (node->schema->module != prev_mod))) {
+ sibling_ht = NULL;
+ prev_mod = node->schema ? node->schema->module : NULL;
+ }
- LY_CHECK_RET(lyb_print_node(out, &node, &sibling_ht, lybctx));
+ LY_CHECK_RET(lyb_print_node(out, &node, &sibling_ht, lybctx));
- if (!(lybctx->print_options & LYD_PRINT_WITHSIBLINGS)) {
- break;
- }
- }
- } else {
- LY_LIST_FOR(node, node) {
- LY_CHECK_RET(lyb_print_node(out, &node, &sibling_ht, lybctx));
+ if (!lyd_parent(node) && !(lybctx->print_options & LYD_PRINT_WITHSIBLINGS)) {
+ break;
}
}
@@ -1299,9 +1290,9 @@ lyb_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options
const struct ly_ctx *ctx = root ? LYD_CTX(root) : NULL;
lybctx = calloc(1, sizeof *lybctx);
- LY_CHECK_ERR_RET(!lybctx, LOGMEM(ctx), LY_EMEM);
+ LY_CHECK_ERR_GOTO(!lybctx, LOGMEM(ctx); ret = LY_EMEM, cleanup);
lybctx->lybctx = calloc(1, sizeof *lybctx->lybctx);
- LY_CHECK_ERR_RET(!lybctx->lybctx, LOGMEM(ctx), LY_EMEM);
+ LY_CHECK_ERR_GOTO(!lybctx->lybctx, LOGMEM(ctx); ret = LY_EMEM, cleanup);
lybctx->print_options = options;
if (root) {
diff --git a/src/printer_schema.c b/src/printer_schema.c
index 075c519..12213bb 100644
--- a/src/printer_schema.c
+++ b/src/printer_schema.c
@@ -70,11 +70,6 @@ lys_print_module(struct ly_out *out, const struct lys_module *module, LYS_OUTFOR
}
ret = tree_print_module(out, module, options, line_length);
break;
- /* TODO not yet implemented
- case LYS_OUT_INFO:
- ret = info_print_model(out, module, target_node);
- break;
- */
default:
LOGERR(module->ctx, LY_EINVAL, "Unsupported output format.");
ret = LY_EINVAL;
@@ -105,11 +100,6 @@ lys_print_submodule(struct ly_out *out, const struct lysp_submodule *submodule,
case LYS_OUT_TREE:
ret = tree_print_parsed_submodule(out, submodule, options, line_length);
break;
- /* TODO not yet implemented
- case LYS_OUT_INFO:
- ret = info_print_model(out, module, target_node);
- break;
- */
default:
LOGERR(submodule->mod->ctx, LY_EINVAL, "Unsupported output format.");
ret = LY_EINVAL;
@@ -204,11 +194,6 @@ lys_print_node(struct ly_out *out, const struct lysc_node *node, LYS_OUTFORMAT f
case LYS_OUT_YANG_COMPILED:
ret = yang_print_compiled_node(out, node, options);
break;
- /* TODO not yet implemented
- case LYS_OUT_YIN:
- ret = yin_print_parsed(out, module);
- break;
- */
case LYS_OUT_TREE:
ret = tree_print_compiled_node(out, node, options, line_length);
break;
diff --git a/src/printer_tree.c b/src/printer_tree.c
index 6a7e7ce..6aa2814 100644
--- a/src/printer_tree.c
+++ b/src/printer_tree.c
@@ -1873,7 +1873,7 @@ trp_ext_is_present(ly_bool lysc_tree, const void *node)
} else {
pn = (const struct lysp_node *)node;
LY_ARRAY_FOR(pn->exts, i) {
- if (!(pn->exts && pn->exts->record->plugin.printer_ptree)) {
+ if (!(pn->exts && pn->exts->record && pn->exts->record->plugin.printer_ptree)) {
continue;
}
if (!trp_ext_parent_is_valid(0, &pn->exts[i])) {
@@ -3426,7 +3426,7 @@ troc_modi_first_sibling(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
/* current node is top-node */
switch (tc->section) {
case TRD_SECT_MODULE:
- tc->cn = tc->cmod->data;
+ tc->cn = tc->cn->module->compiled->data;
break;
case TRD_SECT_RPCS:
tc->cn = (const struct lysc_node *)tc->cmod->rpcs;
@@ -3486,7 +3486,8 @@ trb_gap_to_opts(const struct trt_node *node)
}
if (node->name.module_prefix) {
- len += strlen(node->name.module_prefix);
+ /* prefix_name and ':' */
+ len += strlen(node->name.module_prefix) + 1;
}
if (node->name.str) {
len += strlen(node->name.str);
@@ -3753,7 +3754,10 @@ trb_print_parents(const struct lysc_node *node, struct trt_wrapper *wr_in, struc
/* print node */
ly_print_(pc->out, "\n");
print_node = pc->fp.read.node(TRP_EMPTY_PARENT_CACHE, tc);
+ /* siblings do not print, so the node is always considered the last */
+ print_node.last_one = 1;
max_gap_before_type = trb_max_gap_to_type(TRP_EMPTY_PARENT_CACHE, pc, tc);
+ tc->cn = node;
trb_print_entire_node(&print_node, max_gap_before_type, wr, pc, tc);
}
@@ -3856,22 +3860,36 @@ trb_ext_iter(const struct trt_tree_ctx *tc, uint64_t *i)
* @param[in] compiled if @p ext is lysc structure.
* @param[in] ext current processed extension.
* @param[out] plug_ctx is plugin context which will be initialized.
+ * @param[out] ignore plugin callback is NULL.
* @return LY_ERR value.
*/
static LY_ERR
-tro_ext_printer_tree(ly_bool compiled, void *ext, const struct lyspr_tree_ctx *plug_ctx)
+tro_ext_printer_tree(ly_bool compiled, void *ext, const struct lyspr_tree_ctx *plug_ctx, ly_bool *ignore)
{
struct lysc_ext_instance *ext_comp;
struct lysp_ext_instance *ext_pars;
+ const struct lyplg_ext *plugin;
const char *flags = NULL, *add_opts = NULL;
if (compiled) {
ext_comp = ext;
- return ext_comp->def->plugin->printer_ctree(ext, plug_ctx, &flags, &add_opts);
+ plugin = ext_comp->def->plugin;
+ if (!plugin->printer_ctree) {
+ *ignore = 1;
+ return LY_SUCCESS;
+ }
+ return plugin->printer_ctree(ext, plug_ctx, &flags, &add_opts);
} else {
ext_pars = ext;
- return ext_pars->record->plugin.printer_ptree(ext, plug_ctx, &flags, &add_opts);
+ plugin = &ext_pars->record->plugin;
+ if (!plugin->printer_ptree) {
+ *ignore = 1;
+ return LY_SUCCESS;
+ }
+ return plugin->printer_ptree(ext, plug_ctx, &flags, &add_opts);
}
+
+ return LY_SUCCESS;
}
/**
@@ -3986,7 +4004,7 @@ trb_ext_print_instances(struct trt_wrapper wr, struct trt_parent_cache ca, struc
LY_ARRAY_COUNT_TYPE i;
uint64_t last_instance = UINT64_MAX;
void *ext;
- ly_bool child_exists;
+ ly_bool child_exists, ignore = 0;
uint32_t max, max_gap_before_type = 0;
ca = tro_parent_cache_for_child(ca, tc);
@@ -4004,8 +4022,12 @@ trb_ext_print_instances(struct trt_wrapper wr, struct trt_parent_cache ca, struc
while ((ext = trb_ext_iter(tc, &i))) {
struct lyspr_tree_ctx plug_ctx = {0};
- rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx);
+ rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx, &ignore);
LY_CHECK_ERR_GOTO(rc, tc->last_error = rc, end);
+ if (ignore) {
+ ignore = 0;
+ continue;
+ }
trb_ext_try_unified_indent(&plug_ctx, ca, &max_gap_before_type, pc, tc);
if (plug_ctx.schemas) {
last_instance = i;
@@ -4024,8 +4046,12 @@ trb_ext_print_instances(struct trt_wrapper wr, struct trt_parent_cache ca, struc
while ((ext = trb_ext_iter(tc, &i))) {
struct lyspr_tree_ctx plug_ctx = {0};
- rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx);
+ rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx, &ignore);
LY_CHECK_ERR_GOTO(rc, tc->last_error = rc, end);
+ if (ignore) {
+ ignore = 0;
+ continue;
+ }
if (!child_exists && (last_instance == i)) {
trb_ext_print_schemas(&plug_ctx, 1, max_gap_before_type, wr, ca, pc, tc);
} else {
@@ -4491,6 +4517,7 @@ trm_print_plugin_ext(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
struct trt_printer_ctx pc_dupl;
struct trt_tree_ctx tc_dupl;
struct trt_node node;
+ ly_bool ignore = 0;
uint32_t max_gap_before_type;
void *ext;
@@ -4504,9 +4531,10 @@ trm_print_plugin_ext(struct trt_printer_ctx *pc, struct trt_tree_ctx *tc)
while ((ext = trb_mod_ext_iter(tc, &i))) {
struct lyspr_tree_ctx plug_ctx = {0};
- rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx);
+ rc = tro_ext_printer_tree(tc->lysc_tree, ext, &plug_ctx, &ignore);
LY_CHECK_ERR_GOTO(rc, tc->last_error = rc, end);
- if (!plug_ctx.schemas) {
+ if (!plug_ctx.schemas || ignore) {
+ ignore = 0;
continue;
}
diff --git a/src/printer_xml.c b/src/printer_xml.c
index a7f4c73..9cd6774 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -110,7 +110,9 @@ xml_print_ns_opaq(struct xmlpr_ctx *pctx, LY_VALUE_FORMAT format, const struct l
{
switch (format) {
case LY_VALUE_XML:
- return xml_print_ns(pctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
+ if (name->module_ns) {
+ return xml_print_ns(pctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts);
+ }
break;
case LY_VALUE_JSON:
if (name->module_name) {
@@ -178,6 +180,8 @@ xml_print_meta(struct xmlpr_ctx *pctx, const struct lyd_node *node)
struct ly_set ns_list = {0};
LY_ARRAY_COUNT_TYPE u;
ly_bool dynamic, filter_attrs = 0;
+ const char *value;
+ uint32_t i;
/* with-defaults */
if (node->schema->nodetype & LYD_NODE_TERM) {
@@ -205,11 +209,14 @@ xml_print_meta(struct xmlpr_ctx *pctx, const struct lyd_node *node)
}
for (meta = node->meta; meta; meta = meta->next) {
- const char *value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list,
- &dynamic, NULL);
+ /* store the module of the default namespace, NULL because there is none */
+ ly_set_add(&ns_list, NULL, 0, NULL);
+
+ /* print the value */
+ value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list, &dynamic, NULL);
/* print namespaces connected with the value's prefixes */
- for (uint32_t i = 0; i < ns_list.count; ++i) {
+ for (i = 1; i < ns_list.count; ++i) {
mod = ns_list.objs[i];
xml_print_ns(pctx, mod->ns, mod->prefix, 1);
}
@@ -314,22 +321,32 @@ static LY_ERR xml_print_node(struct xmlpr_ctx *pctx, const struct lyd_node *node
static LY_ERR
xml_print_term(struct xmlpr_ctx *pctx, const struct lyd_node_term *node)
{
+ LY_ERR rc = LY_SUCCESS;
struct ly_set ns_list = {0};
- ly_bool dynamic;
- const char *value;
+ ly_bool dynamic = 0;
+ const char *value = NULL;
+ const struct lys_module *mod;
+ uint32_t i;
- xml_print_node_open(pctx, &node->node);
+ /* store the module of the default namespace */
+ if ((rc = ly_set_add(&ns_list, node->schema->module, 0, NULL))) {
+ LOGMEM(pctx->ctx);
+ goto cleanup;
+ }
+
+ /* print the value */
value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(LYD_CTX(node), &node->value, LY_VALUE_XML,
&ns_list, &dynamic, NULL);
- LY_CHECK_RET(!value, LY_EINVAL);
+ LY_CHECK_ERR_GOTO(!value, rc = LY_EINVAL, cleanup);
- /* print namespaces connected with the values's prefixes */
- for (uint32_t u = 0; u < ns_list.count; ++u) {
- const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
+ /* print node opening */
+ xml_print_node_open(pctx, &node->node);
+ /* print namespaces connected with the values's prefixes */
+ for (i = 1; i < ns_list.count; ++i) {
+ mod = ns_list.objs[i];
ly_print_(pctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
}
- ly_set_erase(&ns_list, NULL);
if (!value[0]) {
ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
@@ -338,11 +355,13 @@ xml_print_term(struct xmlpr_ctx *pctx, const struct lyd_node_term *node)
lyxml_dump_text(pctx->out, value, 0);
ly_print_(pctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
}
+
+cleanup:
+ ly_set_erase(&ns_list, NULL);
if (dynamic) {
free((void *)value);
}
-
- return LY_SUCCESS;
+ return rc;
}
/**
diff --git a/src/printer_yang.c b/src/printer_yang.c
index ea643ac..8e0af18 100644
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -628,9 +628,7 @@ yprc_must(struct lys_ypr_ctx *pctx, const struct lysc_must *must, ly_bool *flag)
ly_bool inner_flag = 0;
ypr_open(pctx->out, flag);
- ly_print_(pctx->out, "%*smust \"", INDENT);
- ypr_encode(pctx->out, must->cond->expr, -1);
- ly_print_(pctx->out, "\"");
+ ypr_text(pctx, "must", must->cond->expr, 1, 0);
LEVEL++;
yprc_extension_instances(pctx, LY_STMT_MUST, 0, must->exts, &inner_flag);
@@ -773,9 +771,7 @@ yprp_when(struct lys_ypr_ctx *pctx, const struct lysp_when *when, ly_bool *flag)
}
ypr_open(pctx->out, flag);
- ly_print_(pctx->out, "%*swhen \"", INDENT);
- ypr_encode(pctx->out, when->cond, -1);
- ly_print_(pctx->out, "\"");
+ ypr_text(pctx, "when", when->cond, 1, 0);
LEVEL++;
yprp_extension_instances(pctx, LY_STMT_WHEN, 0, when->exts, &inner_flag);
@@ -795,9 +791,7 @@ yprc_when(struct lys_ypr_ctx *pctx, const struct lysc_when *when, ly_bool *flag)
}
ypr_open(pctx->out, flag);
- ly_print_(pctx->out, "%*swhen \"", INDENT);
- ypr_encode(pctx->out, when->cond->expr, -1);
- ly_print_(pctx->out, "\"");
+ ypr_text(pctx, "when", when->cond->expr, 1, 0);
LEVEL++;
yprc_extension_instances(pctx, LY_STMT_WHEN, 0, when->exts, &inner_flag);
diff --git a/src/schema_compile.c b/src/schema_compile.c
index ed768ba..1232228 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -437,6 +437,10 @@ lys_compile_expr_implement(const struct ly_ctx *ctx, const struct lyxp_expr *exp
assert(implement || mod_p);
+ if (mod_p) {
+ *mod_p = NULL;
+ }
+
for (i = 0; i < expr->used; ++i) {
if ((expr->tokens[i] != LYXP_TOKEN_NAMETEST) && (expr->tokens[i] != LYXP_TOKEN_LITERAL)) {
/* token cannot have a prefix */
@@ -476,38 +480,6 @@ lys_compile_expr_implement(const struct ly_ctx *ctx, const struct lyxp_expr *exp
}
/**
- * @brief Check and optionally implement modules referenced by a when expression.
- *
- * @param[in] ctx Compile context.
- * @param[in] when When to check.
- * @param[in,out] unres Global unres structure.
- * @return LY_ERECOMPILE if the whole dep set needs to be recompiled for these whens to evaluate.
- * @return LY_ENOT if full check of this when should be skipped.
- * @return LY_ERR value on error.
- */
-static LY_ERR
-lys_compile_unres_when_implement(struct lysc_ctx *ctx, const struct lysc_when *when, struct lys_glob_unres *unres)
-{
- LY_ERR rc = LY_SUCCESS;
- const struct lys_module *mod = NULL;
-
- /* check whether all the referenced modules are implemented */
- rc = lys_compile_expr_implement(ctx->ctx, when->cond, LY_VALUE_SCHEMA_RESOLVED, when->prefixes,
- ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED, unres, &mod);
- if (rc) {
- goto cleanup;
- } else if (mod) {
- LOGWRN(ctx->ctx, "When condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
- when->cond->expr, mod->name);
- rc = LY_ENOT;
- goto cleanup;
- }
-
-cleanup:
- return rc;
-}
-
-/**
* @brief Check when for cyclic dependencies.
*
* @param[in] set Set with all the referenced nodes.
@@ -519,7 +491,7 @@ lys_compile_unres_when_cyclic(struct lyxp_set *set, const struct lysc_node *node
{
struct lyxp_set tmp_set;
struct lyxp_set_scnode *xp_scnode;
- uint32_t i, j;
+ uint32_t i, j, idx;
LY_ARRAY_COUNT_TYPE u;
LY_ERR ret = LY_SUCCESS;
@@ -565,36 +537,46 @@ lys_compile_unres_when_cyclic(struct lyxp_set *set, const struct lysc_node *node
}
for (j = 0; j < tmp_set.used; ++j) {
- /* skip roots'n'stuff */
- if (tmp_set.val.scnodes[j].type == LYXP_NODE_ELEM) {
- /* try to find this node in our set */
- uint32_t idx;
-
- if (lyxp_set_scnode_contains(set, tmp_set.val.scnodes[j].scnode, LYXP_NODE_ELEM, -1, &idx) &&
- (set->val.scnodes[idx].in_ctx == LYXP_SET_SCNODE_START_USED)) {
- LOGVAL(set->ctx, LYVE_SEMANTICS, "When condition cyclic dependency on the node \"%s\".",
- tmp_set.val.scnodes[j].scnode->name);
- ret = LY_EVALID;
- LOG_LOCBACK(1, 0, 0, 0);
- goto cleanup;
- }
-
- /* needs to be checked, if in both sets, will be ignored */
- tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
- } else {
- /* no when, nothing to check */
+ if (tmp_set.val.scnodes[j].type != LYXP_NODE_ELEM) {
+ /* skip roots'n'stuff, no when, nothing to check */
tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
+ continue;
+ }
+
+ /* try to find this node in our set */
+ if (lyxp_set_scnode_contains(set, tmp_set.val.scnodes[j].scnode, LYXP_NODE_ELEM, -1, &idx) &&
+ (set->val.scnodes[idx].in_ctx == LYXP_SET_SCNODE_START_USED)) {
+ LOGVAL(set->ctx, LYVE_SEMANTICS, "When condition cyclic dependency on the node \"%s\".",
+ tmp_set.val.scnodes[j].scnode->name);
+ ret = LY_EVALID;
+ LOG_LOCBACK(1, 0, 0, 0);
+ goto cleanup;
+ }
+
+ /* needs to be checked, if in both sets, will be ignored */
+ tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ }
+
+ if (when->context != node) {
+ /* node actually depends on this "when", not the context node */
+ assert(tmp_set.val.scnodes[0].scnode == when->context);
+ if (tmp_set.val.scnodes[0].in_ctx == LYXP_SET_SCNODE_START_USED) {
+ /* replace the non-traversed context node with the dependent node */
+ tmp_set.val.scnodes[0].scnode = (struct lysc_node *)node;
+ } else {
+ /* context node was traversed, so just add the dependent node */
+ ret = lyxp_set_scnode_insert_node(&tmp_set, node, LYXP_SET_SCNODE_START_USED, LYXP_AXIS_CHILD, NULL);
+ LY_CHECK_ERR_GOTO(ret, LOG_LOCBACK(1, 0, 0, 0), cleanup);
}
}
/* merge this set into the global when set */
lyxp_set_scnode_merge(set, &tmp_set);
}
+ LOG_LOCBACK(1, 0, 0, 0);
/* check when of non-data parents as well */
node = node->parent;
-
- LOG_LOCBACK(1, 0, 0, 0);
} while (node && (node->nodetype & (LYS_CASE | LYS_CHOICE)));
/* this node when was checked (xp_scnode could have been reallocd) */
@@ -640,6 +622,7 @@ lys_compile_unres_when(struct lysc_ctx *ctx, const struct lysc_when *when, const
{
struct lyxp_set tmp_set = {0};
uint32_t i, opts;
+ struct lysc_node *schema;
LY_ERR ret = LY_SUCCESS;
opts = LYXP_SCNODE_SCHEMA | ((node->flags & LYS_IS_OUTPUT) ? LYXP_SCNODE_OUTPUT : 0);
@@ -655,28 +638,45 @@ lys_compile_unres_when(struct lysc_ctx *ctx, const struct lysc_when *when, const
ctx->path[0] = '\0';
lysc_path(node, LYSC_PATH_LOG, ctx->path, LYSC_CTX_BUFSIZE);
for (i = 0; i < tmp_set.used; ++i) {
- /* skip roots'n'stuff */
- if ((tmp_set.val.scnodes[i].type == LYXP_NODE_ELEM) &&
- (tmp_set.val.scnodes[i].in_ctx != LYXP_SET_SCNODE_START_USED)) {
- struct lysc_node *schema = tmp_set.val.scnodes[i].scnode;
-
- /* XPath expression cannot reference "lower" status than the node that has the definition */
- if (lysc_check_status(NULL, when->flags, node->module, node->name, schema->flags, schema->module,
- schema->name)) {
- LOGWRN(ctx->ctx, "When condition \"%s\" may be referencing %s node \"%s\".", when->cond->expr,
- (schema->flags == LYS_STATUS_OBSLT) ? "obsolete" : "deprecated", schema->name);
- }
+ if (tmp_set.val.scnodes[i].type != LYXP_NODE_ELEM) {
+ /* skip roots'n'stuff */
+ continue;
+ } else if (tmp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_START_USED) {
+ /* context node not actually traversed */
+ continue;
+ }
- /* check dummy node children/value accessing */
- if (lysc_data_parent(schema) == node) {
- LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node children.");
- ret = LY_EVALID;
- goto cleanup;
- } else if ((schema == node) && (tmp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL)) {
- LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node value.");
- ret = LY_EVALID;
- goto cleanup;
- }
+ schema = tmp_set.val.scnodes[i].scnode;
+
+ /* XPath expression cannot reference "lower" status than the node that has the definition */
+ if (lysc_check_status(NULL, when->flags, node->module, node->name, schema->flags, schema->module,
+ schema->name)) {
+ LOGWRN(ctx->ctx, "When condition \"%s\" may be referencing %s node \"%s\".", when->cond->expr,
+ (schema->flags == LYS_STATUS_OBSLT) ? "obsolete" : "deprecated", schema->name);
+ }
+
+ /* check dummy node children/value accessing */
+ if (lysc_data_parent(schema) == node) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node children.");
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if ((schema == node) && (tmp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node value.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ if (when->context != node) {
+ /* node actually depends on this "when", not the context node */
+ assert(tmp_set.val.scnodes[0].scnode == when->context);
+ if (tmp_set.val.scnodes[0].in_ctx == LYXP_SET_SCNODE_START_USED) {
+ /* replace the non-traversed context node with the dependent node */
+ tmp_set.val.scnodes[0].scnode = (struct lysc_node *)node;
+ } else {
+ /* context node was traversed, so just add the dependent node */
+ ret = lyxp_set_scnode_insert_node(&tmp_set, node, LYXP_SET_SCNODE_START_USED, LYXP_AXIS_CHILD, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
}
}
@@ -695,20 +695,16 @@ cleanup:
* @param[in] ctx Compile context.
* @param[in] node Node to check.
* @param[in] local_mods Sized array of local modules for musts of @p node at the same index.
- * @param[in,out] unres Global unres structure.
- * @return LY_ERECOMPILE
- * @return LY_ERR value
+ * @return LY_ERR value.
*/
static LY_ERR
-lys_compile_unres_must(struct lysc_ctx *ctx, const struct lysc_node *node, const struct lysp_module **local_mods,
- struct lys_glob_unres *unres)
+lys_compile_unres_must(struct lysc_ctx *ctx, const struct lysc_node *node, const struct lysp_module **local_mods)
{
struct lyxp_set tmp_set;
uint32_t i, opts;
LY_ARRAY_COUNT_TYPE u;
- struct lysc_must *musts = NULL;
+ struct lysc_must *musts;
LY_ERR ret = LY_SUCCESS;
- const struct lys_module *mod;
uint16_t flg;
LOG_LOCSET(node, NULL, NULL, NULL);
@@ -718,18 +714,6 @@ lys_compile_unres_must(struct lysc_ctx *ctx, const struct lysc_node *node, const
musts = lysc_node_musts(node);
LY_ARRAY_FOR(musts, u) {
- /* first check whether all the referenced modules are implemented */
- mod = NULL;
- ret = lys_compile_expr_implement(ctx->ctx, musts[u].cond, LY_VALUE_SCHEMA_RESOLVED, musts[u].prefixes,
- ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED, unres, &mod);
- if (ret) {
- goto cleanup;
- } else if (mod) {
- LOGWRN(ctx->ctx, "Must condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
- musts[u].cond->expr, mod->name);
- continue;
- }
-
/* check "must" */
ret = lyxp_atomize(ctx->ctx, musts[u].cond, node->module, LY_VALUE_SCHEMA_RESOLVED, musts[u].prefixes, node,
node, &tmp_set, opts);
@@ -815,6 +799,7 @@ lys_compile_unres_disabled_bitenum(struct lysc_ctx *ctx, struct lysc_node_leaf *
struct lysc_type **t;
LY_ARRAY_COUNT_TYPE u, count;
struct lysc_type_enum *ent;
+ ly_bool has_value = 0;
if (leaf->type->basetype == LY_TYPE_UNION) {
t = ((struct lysc_type_union *)leaf->type)->types;
@@ -829,14 +814,19 @@ lys_compile_unres_disabled_bitenum(struct lysc_ctx *ctx, struct lysc_node_leaf *
ent = (struct lysc_type_enum *)(t[u]);
lys_compile_unres_disabled_bitenum_remove(&ctx->free_ctx, ent->enums);
- if (!LY_ARRAY_COUNT(ent->enums)) {
- LOGVAL(ctx->ctx, LYVE_SEMANTICS, "%s type of node \"%s\" without any (or all disabled) valid values.",
- (ent->basetype == LY_TYPE_BITS) ? "Bits" : "Enumeration", leaf->name);
- return LY_EVALID;
+ if (LY_ARRAY_COUNT(ent->enums)) {
+ has_value = 1;
}
+ } else {
+ has_value = 1;
}
}
+ if (!has_value) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Node \"%s\" without any (or all disabled) valid values.", leaf->name);
+ return LY_EVALID;
+ }
+
return LY_SUCCESS;
}
@@ -847,13 +837,11 @@ lys_compile_unres_disabled_bitenum(struct lysc_ctx *ctx, struct lysc_node_leaf *
* @param[in] node Context node for the leafref.
* @param[in] lref Leafref to check/resolve.
* @param[in] local_mod Local module for the leafref type.
- * @param[in,out] unres Global unres structure.
- * @return LY_ERECOMPILE if context recompilation is needed,
* @return LY_ERR value.
*/
static LY_ERR
lys_compile_unres_leafref(struct lysc_ctx *ctx, const struct lysc_node *node, struct lysc_type_leafref *lref,
- const struct lysp_module *local_mod, struct lys_glob_unres *unres)
+ const struct lysp_module *local_mod)
{
const struct lysc_node *target = NULL;
struct ly_path *p;
@@ -862,8 +850,10 @@ lys_compile_unres_leafref(struct lysc_ctx *ctx, const struct lysc_node *node, st
assert(node->nodetype & (LYS_LEAF | LYS_LEAFLIST));
- /* first implement all the modules in the path */
- LY_CHECK_RET(lys_compile_expr_implement(ctx->ctx, lref->path, LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, 1, unres, NULL));
+ if (lref->realtype) {
+ /* already resolved, may happen (shared union typedef with a leafref) */
+ return LY_SUCCESS;
+ }
/* try to find the target, current module is that of the context node (RFC 7950 6.4.1 second bullet) */
LY_CHECK_RET(ly_path_compile_leafref(ctx->ctx, node, ctx->ext, lref->path,
@@ -934,6 +924,7 @@ lys_compile_unres_leafref(struct lysc_ctx *ctx, const struct lysc_node *node, st
* @param[in] dflt_pmod Parsed module of the @p dflt to resolve possible prefixes.
* @param[in,out] storage Storage for the compiled default value.
* @param[in,out] unres Global unres structure for newly implemented modules.
+ * @return LY_ERECOMPILE if the whole dep set needs to be recompiled for the value to be checked.
* @return LY_ERR value.
*/
static LY_ERR
@@ -1057,7 +1048,8 @@ lys_compile_unres_llist_dflts(struct lysc_ctx *ctx, struct lysc_node_leaflist *l
if (!llist->dflts[u]->realtype->plugin->compare(llist->dflts[u], llist->dflts[v])) {
lysc_update_path(ctx, llist->parent ? llist->parent->module : NULL, llist->name);
LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Configuration leaf-list has multiple defaults of the same value \"%s\".",
- llist->dflts[u]->realtype->plugin->print(ctx->ctx, llist->dflts[u], LY_VALUE_CANON, NULL, NULL, NULL));
+ (char *)llist->dflts[u]->realtype->plugin->print(ctx->ctx, llist->dflts[u], LY_VALUE_CANON,
+ NULL, NULL, NULL));
lysc_update_path(ctx, NULL, NULL);
return LY_EVALID;
}
@@ -1114,6 +1106,108 @@ lys_type_leafref_next(const struct lysc_node *node, uint64_t *index)
}
/**
+ * @brief Implement all referenced modules by leafrefs, when and must conditions.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] unres Global unres structure with the sets to resolve.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERECOMPILE if the whole dep set needs to be recompiled with the new implemented modules.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lys_compile_unres_depset_implement(struct ly_ctx *ctx, struct lys_glob_unres *unres)
+{
+ struct lys_depset_unres *ds_unres = &unres->ds_unres;
+ struct lysc_type_leafref *lref;
+ const struct lys_module *mod;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_unres_leafref *l;
+ struct lysc_unres_when *w;
+ struct lysc_unres_must *m;
+ struct lysc_must *musts;
+ ly_bool not_implemented;
+ uint32_t di = 0, li = 0, wi = 0, mi = 0;
+
+implement_all:
+ /* disabled leafrefs - even those because we need to check their target exists */
+ while (di < ds_unres->disabled_leafrefs.count) {
+ l = ds_unres->disabled_leafrefs.objs[di];
+
+ u = 0;
+ while ((lref = lys_type_leafref_next(l->node, &u))) {
+ LY_CHECK_RET(lys_compile_expr_implement(ctx, lref->path, LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, 1, unres, NULL));
+ }
+
+ ++di;
+ }
+
+ /* leafrefs */
+ while (li < ds_unres->leafrefs.count) {
+ l = ds_unres->leafrefs.objs[li];
+
+ u = 0;
+ while ((lref = lys_type_leafref_next(l->node, &u))) {
+ LY_CHECK_RET(lys_compile_expr_implement(ctx, lref->path, LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, 1, unres, NULL));
+ }
+
+ ++li;
+ }
+
+ /* when conditions */
+ while (wi < ds_unres->whens.count) {
+ w = ds_unres->whens.objs[wi];
+
+ LY_CHECK_RET(lys_compile_expr_implement(ctx, w->when->cond, LY_VALUE_SCHEMA_RESOLVED, w->when->prefixes,
+ ctx->flags & LY_CTX_REF_IMPLEMENTED, unres, &mod));
+ if (mod) {
+ LOGWRN(ctx, "When condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
+ w->when->cond->expr, mod->name);
+
+ /* remove from the set to skip the check */
+ ly_set_rm_index(&ds_unres->whens, wi, free);
+ continue;
+ }
+
+ ++wi;
+ }
+
+ /* must conditions */
+ while (mi < ds_unres->musts.count) {
+ m = ds_unres->musts.objs[mi];
+
+ not_implemented = 0;
+ musts = lysc_node_musts(m->node);
+ LY_ARRAY_FOR(musts, u) {
+ LY_CHECK_RET(lys_compile_expr_implement(ctx, musts[u].cond, LY_VALUE_SCHEMA_RESOLVED, musts[u].prefixes,
+ ctx->flags & LY_CTX_REF_IMPLEMENTED, unres, &mod));
+ if (mod) {
+ LOGWRN(ctx, "Must condition \"%s\" check skipped because referenced module \"%s\" is not implemented.",
+ musts[u].cond->expr, mod->name);
+
+ /* need to implement modules from all the expressions */
+ not_implemented = 1;
+ }
+ }
+
+ if (not_implemented) {
+ /* remove from the set to skip the check */
+ lysc_unres_must_free(m);
+ ly_set_rm_index(&ds_unres->musts, mi, NULL);
+ continue;
+ }
+
+ ++mi;
+ }
+
+ if ((di < ds_unres->disabled_leafrefs.count) || (li < ds_unres->leafrefs.count) || (wi < ds_unres->whens.count)) {
+ /* new items in the sets */
+ goto implement_all;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
* @brief Finish dependency set compilation by resolving all the unres sets.
*
* @param[in] ctx libyang context.
@@ -1125,7 +1219,7 @@ lys_type_leafref_next(const struct lysc_node *node, uint64_t *index)
static LY_ERR
lys_compile_unres_depset(struct ly_ctx *ctx, struct lys_glob_unres *unres)
{
- LY_ERR ret = LY_SUCCESS, r;
+ LY_ERR ret = LY_SUCCESS;
struct lysc_node *node;
struct lysc_type *typeiter;
struct lysc_type_leafref *lref;
@@ -1140,7 +1234,12 @@ lys_compile_unres_depset(struct ly_ctx *ctx, struct lys_glob_unres *unres)
uint32_t i, processed_leafrefs = 0;
resolve_all:
- /* check disabled leafrefs first */
+ /* implement all referenced modules to get final ds_unres set */
+ if ((ret = lys_compile_unres_depset_implement(ctx, unres))) {
+ goto cleanup;
+ }
+
+ /* check disabled leafrefs */
while (ds_unres->disabled_leafrefs.count) {
/* remember index, it can change before we get to free this item */
i = ds_unres->disabled_leafrefs.count - 1;
@@ -1150,7 +1249,7 @@ resolve_all:
LOG_LOCSET(l->node, NULL, NULL, NULL);
v = 0;
while ((ret == LY_SUCCESS) && (lref = lys_type_leafref_next(l->node, &v))) {
- ret = lys_compile_unres_leafref(&cctx, l->node, lref, l->local_mod, unres);
+ ret = lys_compile_unres_leafref(&cctx, l->node, lref, l->local_mod);
}
LOG_LOCBACK(1, 0, 0, 0);
LY_CHECK_GOTO(ret, cleanup);
@@ -1169,7 +1268,7 @@ resolve_all:
LOG_LOCSET(l->node, NULL, NULL, NULL);
v = 0;
while ((ret == LY_SUCCESS) && (lref = lys_type_leafref_next(l->node, &v))) {
- ret = lys_compile_unres_leafref(&cctx, l->node, lref, l->local_mod, unres);
+ ret = lys_compile_unres_leafref(&cctx, l->node, lref, l->local_mod);
}
LOG_LOCBACK(1, 0, 0, 0);
LY_CHECK_GOTO(ret, cleanup);
@@ -1193,29 +1292,7 @@ resolve_all:
processed_leafrefs++;
}
- /* check when, first implement all the referenced modules (for the cyclic check in the next loop to work) */
- i = 0;
- while (i < ds_unres->whens.count) {
- w = ds_unres->whens.objs[i];
- LYSC_CTX_INIT_PMOD(cctx, w->node->module->parsed, NULL);
-
- LOG_LOCSET(w->node, NULL, NULL, NULL);
- r = lys_compile_unres_when_implement(&cctx, w->when, unres);
- LOG_LOCBACK(w->node ? 1 : 0, 0, 0, 0);
-
- if (r == LY_ENOT) {
- /* skip full when check, remove from the set */
- free(w);
- ly_set_rm_index(&ds_unres->whens, i, NULL);
- continue;
- } else if (r) {
- /* error */
- ret = r;
- goto cleanup;
- }
-
- ++i;
- }
+ /* check when, the referenced modules must be implemented now */
while (ds_unres->whens.count) {
i = ds_unres->whens.count - 1;
w = ds_unres->whens.objs[i];
@@ -1237,7 +1314,7 @@ resolve_all:
LYSC_CTX_INIT_PMOD(cctx, m->node->module->parsed, m->ext);
LOG_LOCSET(m->node, NULL, NULL, NULL);
- ret = lys_compile_unres_must(&cctx, m->node, m->local_mods, unres);
+ ret = lys_compile_unres_must(&cctx, m->node, m->local_mods);
LOG_LOCBACK(1, 0, 0, 0);
LY_CHECK_GOTO(ret, cleanup);
@@ -1278,7 +1355,7 @@ resolve_all:
ly_set_rm_index(&ds_unres->dflts, i, NULL);
}
- /* some unres items may have been added */
+ /* some unres items may have been added by the default values */
if ((processed_leafrefs != ds_unres->leafrefs.count) || ds_unres->disabled_leafrefs.count ||
ds_unres->whens.count || ds_unres->musts.count || ds_unres->dflts.count) {
goto resolve_all;
@@ -1748,7 +1825,7 @@ lys_has_compiled_import_r(struct lys_module *mod)
LY_ERR
lys_implement(struct lys_module *mod, const char **features, struct lys_glob_unres *unres)
{
- LY_ERR ret;
+ LY_ERR r;
struct lys_module *m;
assert(!mod->implemented);
@@ -1757,21 +1834,15 @@ lys_implement(struct lys_module *mod, const char **features, struct lys_glob_unr
m = ly_ctx_get_module_implemented(mod->ctx, mod->name);
if (m) {
assert(m != mod);
- if (!strcmp(mod->name, "yang") && (strcmp(m->revision, mod->revision) > 0)) {
- /* special case for newer internal module, continue */
- LOGVRB("Internal module \"%s@%s\" is already implemented in revision \"%s\", using it instead.",
- mod->name, mod->revision ? mod->revision : "<none>", m->revision ? m->revision : "<none>");
- } else {
- LOGERR(mod->ctx, LY_EDENIED, "Module \"%s@%s\" is already implemented in revision \"%s\".",
- mod->name, mod->revision ? mod->revision : "<none>", m->revision ? m->revision : "<none>");
- return LY_EDENIED;
- }
+ LOGERR(mod->ctx, LY_EDENIED, "Module \"%s@%s\" is already implemented in revision \"%s\".",
+ mod->name, mod->revision ? mod->revision : "<none>", m->revision ? m->revision : "<none>");
+ return LY_EDENIED;
}
/* set features */
- ret = lys_set_features(mod->parsed, features);
- if (ret && (ret != LY_EEXIST)) {
- return ret;
+ r = lys_set_features(mod->parsed, features);
+ if (r && (r != LY_EEXIST)) {
+ return r;
}
/*
diff --git a/src/schema_compile_amend.c b/src/schema_compile_amend.c
index 9ca4e2e..8b570f6 100644
--- a/src/schema_compile_amend.c
+++ b/src/schema_compile_amend.c
@@ -1351,7 +1351,7 @@ lys_apply_deviate_replace(struct lysc_ctx *ctx, struct lysp_deviate_rpl *d, stru
LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &d->dflt, &((struct lysp_node_leaf *)target)->dflt), cleanup);
break;
case LYS_CHOICE:
- DEV_CHECK_PRESENCE(struct lysp_node_choice *, dflt.str, "replacing", "default", d->dflt);
+ DEV_CHECK_PRESENCE(struct lysp_node_choice *, dflt.str, "replacing", "default", d->dflt.str);
lydict_remove(ctx->ctx, ((struct lysp_node_choice *)target)->dflt.str);
LY_CHECK_GOTO(ret = lysp_qname_dup(ctx->ctx, &d->dflt, &((struct lysp_node_choice *)target)->dflt), cleanup);
@@ -1376,13 +1376,6 @@ lys_apply_deviate_replace(struct lysc_ctx *ctx, struct lysp_deviate_rpl *d, stru
AMEND_WRONG_NODETYPE("deviation", "replace", "config");
}
- if (!(target->flags & LYS_CONFIG_MASK)) {
- LOGVAL(ctx->ctx, LY_VCODE_DEV_NOT_PRESENT, "replacing", "config",
- d->flags & LYS_CONFIG_W ? "config true" : "config false");
- ret = LY_EVALID;
- goto cleanup;
- }
-
target->flags &= ~LYS_CONFIG_MASK;
target->flags |= d->flags & LYS_CONFIG_MASK;
}
diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c
index 0b64dcb..2723716 100644
--- a/src/schema_compile_node.c
+++ b/src/schema_compile_node.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Schema compilation of common nodes.
*
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -43,14 +43,13 @@
#include "tree_schema_internal.h"
#include "xpath.h"
-static struct lysc_ext_instance *
-lysc_ext_instance_dup(struct ly_ctx *ctx, struct lysc_ext_instance *orig)
-{
- /* TODO - extensions, increase refcount */
- (void) ctx;
- (void) orig;
- return NULL;
-}
+/**
+ * @brief Item for storing typedef chain item.
+ */
+struct lys_type_item {
+ const struct lysp_tpdf *tpdf;
+ struct lysp_node *node;
+};
/**
* @brief Add a node with a when to unres.
@@ -346,31 +345,48 @@ lysc_patterns_dup(struct ly_ctx *ctx, struct lysc_pattern **orig)
/**
* @brief Duplicate compiled range structure.
*
- * @param[in] ctx Libyang context for logging.
+ * @param[in] ctx Compile context.
* @param[in] orig The range structure to be duplicated.
+ * @param[in] tpdf_chain Chain of the used typedefs, traversed backwards.
+ * @param[in] tpdf_chain_last Index of the last (backwards) typedef in @p tpdf_chain to use.
* @return New compiled range structure as a copy of @p orig.
* @return NULL in case of memory allocation error.
*/
static struct lysc_range *
-lysc_range_dup(struct ly_ctx *ctx, const struct lysc_range *orig)
+lysc_range_dup(struct lysc_ctx *ctx, const struct lysc_range *orig, struct ly_set *tpdf_chain, uint32_t tpdf_chain_last)
{
struct lysc_range *dup;
LY_ERR ret;
+ struct lys_type_item *tpdf_item;
+ uint32_t i;
assert(orig);
dup = calloc(1, sizeof *dup);
- LY_CHECK_ERR_RET(!dup, LOGMEM(ctx), NULL);
+ LY_CHECK_ERR_RET(!dup, LOGMEM(ctx->ctx), NULL);
if (orig->parts) {
- LY_ARRAY_CREATE_GOTO(ctx, dup->parts, LY_ARRAY_COUNT(orig->parts), ret, cleanup);
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, dup->parts, LY_ARRAY_COUNT(orig->parts), ret, cleanup);
(*((LY_ARRAY_COUNT_TYPE *)(dup->parts) - 1)) = LY_ARRAY_COUNT(orig->parts);
memcpy(dup->parts, orig->parts, LY_ARRAY_COUNT(dup->parts) * sizeof *dup->parts);
}
- DUP_STRING_GOTO(ctx, orig->eapptag, dup->eapptag, ret, cleanup);
- DUP_STRING_GOTO(ctx, orig->emsg, dup->emsg, ret, cleanup);
- dup->exts = lysc_ext_instance_dup(ctx, orig->exts);
+ DUP_STRING_GOTO(ctx->ctx, orig->eapptag, dup->eapptag, ret, cleanup);
+ DUP_STRING_GOTO(ctx->ctx, orig->emsg, dup->emsg, ret, cleanup);
+
+ /* collect all range extensions */
+ if (tpdf_chain->count > tpdf_chain_last) {
+ i = tpdf_chain->count;
+ do {
+ --i;
+ tpdf_item = tpdf_chain->objs[i];
+ if (!tpdf_item->tpdf->type.range) {
+ continue;
+ }
+ COMPILE_EXTS_GOTO(ctx, tpdf_item->tpdf->type.range->exts, dup->exts, dup, ret, cleanup);
+ } while (i > tpdf_chain_last);
+ }
return dup;
+
cleanup:
free(dup);
(void) ret; /* set but not used due to the return type */
@@ -1595,6 +1611,17 @@ done:
return ret;
}
+/**
+ * @brief Compile union type.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] ptypes Parsed union types.
+ * @param[in] context_pnode Schema node where the type/typedef is placed to correctly find the base types.
+ * @param[in] context_flags Flags of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_name Name of the context node or referencing typedef for logging.
+ * @param[out] utypes_p Array of compiled union types.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_type_union(struct lysc_ctx *ctx, struct lysp_type *ptypes, struct lysp_node *context_pnode, uint16_t context_flags,
const char *context_name, struct lysc_type ***utypes_p)
@@ -1608,6 +1635,8 @@ lys_compile_type_union(struct lysc_ctx *ctx, struct lysp_type *ptypes, struct ly
ret = lys_compile_type(ctx, context_pnode, context_flags, context_name, &ptypes[u], &utypes[u + additional],
NULL, NULL);
LY_CHECK_GOTO(ret, error);
+ LY_ATOMIC_INC_BARRIER(utypes[u + additional]->refcount);
+
if (utypes[u + additional]->basetype == LY_TYPE_UNION) {
/* add space for additional types from the union subtype */
un_aux = (struct lysc_type_union *)utypes[u + additional];
@@ -1616,28 +1645,8 @@ lys_compile_type_union(struct lysc_ctx *ctx, struct lysp_type *ptypes, struct ly
/* copy subtypes of the subtype union */
for (LY_ARRAY_COUNT_TYPE v = 0; v < LY_ARRAY_COUNT(un_aux->types); ++v) {
- if (un_aux->types[v]->basetype == LY_TYPE_LEAFREF) {
- struct lysc_type_leafref *lref;
-
- /* duplicate the whole structure because of the instance-specific path resolving for realtype */
- utypes[u + additional] = calloc(1, sizeof(struct lysc_type_leafref));
- LY_CHECK_ERR_GOTO(!utypes[u + additional], LOGMEM(ctx->ctx); ret = LY_EMEM, error);
- lref = (struct lysc_type_leafref *)utypes[u + additional];
-
- lref->basetype = LY_TYPE_LEAFREF;
- ret = lyxp_expr_dup(ctx->ctx, ((struct lysc_type_leafref *)un_aux->types[v])->path, 0, 0, &lref->path);
- LY_CHECK_GOTO(ret, error);
- lref->refcount = 1;
- lref->require_instance = ((struct lysc_type_leafref *)un_aux->types[v])->require_instance;
- ret = lyplg_type_prefix_data_dup(ctx->ctx, LY_VALUE_SCHEMA_RESOLVED,
- ((struct lysc_type_leafref *)un_aux->types[v])->prefixes, (void **)&lref->prefixes);
- LY_CHECK_GOTO(ret, error);
- /* TODO extensions */
-
- } else {
- utypes[u + additional] = un_aux->types[v];
- LY_ATOMIC_INC_BARRIER(un_aux->types[v]->refcount);
- }
+ utypes[u + additional] = un_aux->types[v];
+ LY_ATOMIC_INC_BARRIER(un_aux->types[v]->refcount);
++additional;
LY_ARRAY_INCREMENT(utypes);
}
@@ -1664,7 +1673,68 @@ error:
}
/**
+ * @brief Allocate a new specific type structure according to the basetype.
+ *
+ * @param[in] basetype Base type of the new type.
+ * @return Specific type structure.
+ */
+static struct lysc_type *
+lys_new_type(LY_DATA_TYPE basetype)
+{
+ struct lysc_type *type = NULL;
+
+ switch (basetype) {
+ case LY_TYPE_BINARY:
+ type = calloc(1, sizeof(struct lysc_type_bin));
+ break;
+ case LY_TYPE_BITS:
+ type = calloc(1, sizeof(struct lysc_type_bits));
+ break;
+ case LY_TYPE_DEC64:
+ type = calloc(1, sizeof(struct lysc_type_dec));
+ break;
+ case LY_TYPE_STRING:
+ type = calloc(1, sizeof(struct lysc_type_str));
+ break;
+ case LY_TYPE_ENUM:
+ type = calloc(1, sizeof(struct lysc_type_enum));
+ break;
+ case LY_TYPE_INT8:
+ case LY_TYPE_UINT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_INT32:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT64:
+ case LY_TYPE_UINT64:
+ type = calloc(1, sizeof(struct lysc_type_num));
+ break;
+ case LY_TYPE_IDENT:
+ type = calloc(1, sizeof(struct lysc_type_identityref));
+ break;
+ case LY_TYPE_LEAFREF:
+ type = calloc(1, sizeof(struct lysc_type_leafref));
+ break;
+ case LY_TYPE_INST:
+ type = calloc(1, sizeof(struct lysc_type_instanceid));
+ break;
+ case LY_TYPE_UNION:
+ type = calloc(1, sizeof(struct lysc_type_union));
+ break;
+ case LY_TYPE_BOOL:
+ case LY_TYPE_EMPTY:
+ type = calloc(1, sizeof(struct lysc_type));
+ break;
+ case LY_TYPE_UNKNOWN:
+ break;
+ }
+
+ return type;
+}
+
+/**
* @brief The core of the lys_compile_type() - compile information about the given type (from typedef or leaf/leaf-list).
+ *
* @param[in] ctx Compile context.
* @param[in] context_pnode Schema node where the type/typedef is placed to correctly find the base types.
* @param[in] context_flags Flags of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
@@ -1672,16 +1742,19 @@ error:
* @param[in] type_p Parsed type to compile.
* @param[in] basetype Base YANG built-in type of the type to compile.
* @param[in] tpdfname Name of the type's typedef, serves as a flag - if it is leaf/leaf-list's type, it is NULL.
- * @param[in] base The latest base (compiled) type from which the current type is being derived.
- * @param[out] type Newly created type structure with the filled information about the type.
+ * @param[in] base Latest base (compiled) type from which the current type is being derived.
+ * @param[in] plugin Type plugin to use.
+ * @param[in] tpdf_chain Chain of the used typedefs, traversed backwards.
+ * @param[in] tpdf_chain_last Index of the last (backwards) typedef in @p tpdf_chain to use.
+ * @param[out] type Compiled type.
* @return LY_ERR value.
*/
static LY_ERR
lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t context_flags, const char *context_name,
- struct lysp_type *type_p, LY_DATA_TYPE basetype, const char *tpdfname, const struct lysc_type *base,
- struct lysc_type **type)
+ const struct lysp_type *type_p, LY_DATA_TYPE basetype, const char *tpdfname, const struct lysc_type *base,
+ struct lyplg_type *plugin, struct ly_set *tpdf_chain, uint32_t tpdf_chain_last, struct lysc_type **type)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR rc = LY_SUCCESS;
struct lysc_type_bin *bin;
struct lysc_type_num *num;
struct lysc_type_str *str;
@@ -1691,41 +1764,69 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
struct lysc_type_identityref *idref;
struct lysc_type_leafref *lref;
struct lysc_type_union *un;
+ struct lys_type_item *tpdf_item;
+ const struct lysp_type *base_type_p;
+ uint32_t i;
+
+ /* alloc and init */
+ *type = lys_new_type(basetype);
+ LY_CHECK_ERR_GOTO(!(*type), LOGMEM(ctx->ctx), cleanup);
+
+ (*type)->basetype = basetype;
+ (*type)->plugin = plugin;
switch (basetype) {
case LY_TYPE_BINARY:
- bin = (struct lysc_type_bin *)(*type);
+ bin = (struct lysc_type_bin *)*type;
/* RFC 7950 9.8.1, 9.4.4 - length, number of octets it contains */
if (type_p->length) {
- LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
- base ? ((struct lysc_type_bin *)base)->length : NULL, &bin->length));
+ LY_CHECK_GOTO(rc = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+ base ? ((struct lysc_type_bin *)base)->length : NULL, &bin->length), cleanup);
if (!tpdfname) {
- COMPILE_EXTS_GOTO(ctx, type_p->length->exts, bin->length->exts, bin->length, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, type_p->length->exts, bin->length->exts, bin->length, rc, cleanup);
}
}
break;
case LY_TYPE_BITS:
/* RFC 7950 9.7 - bits */
- bits = (struct lysc_type_bits *)(*type);
+ bits = (struct lysc_type_bits *)*type;
if (type_p->bits) {
- LY_CHECK_RET(lys_compile_type_enums(ctx, type_p->bits, basetype,
+ /* compile bits from this type */
+ LY_CHECK_GOTO(rc = lys_compile_type_enums(ctx, type_p->bits, basetype,
base ? (struct lysc_type_bitenum_item *)((struct lysc_type_bits *)base)->bits : NULL,
- (struct lysc_type_bitenum_item **)&bits->bits));
- }
+ (struct lysc_type_bitenum_item **)&bits->bits), cleanup);
+ } else if (base) {
+ /* recompile bits from the first superior type with bits */
+ assert(tpdf_chain->count > tpdf_chain_last);
+ base_type_p = NULL;
+ i = tpdf_chain->count;
+ do {
+ --i;
+ tpdf_item = tpdf_chain->objs[i];
+
+ if (tpdf_item->tpdf->type.bits) {
+ base_type_p = &tpdf_item->tpdf->type;
+ break;
+ }
+ } while (i > tpdf_chain_last);
+ assert(base_type_p);
- if (!base && !type_p->flags) {
+ LY_CHECK_GOTO(rc = lys_compile_type_enums(ctx, base_type_p->bits, basetype, NULL,
+ (struct lysc_type_bitenum_item **)&bits->bits), cleanup);
+ } else {
/* type derived from bits built-in type must contain at least one bit */
if (tpdfname) {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "bit", "bits type ", tpdfname);
} else {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "bit", "bits type", "");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
break;
case LY_TYPE_DEC64:
- dec = (struct lysc_type_dec *)(*type);
+ dec = (struct lysc_type_dec *)*type;
/* RFC 7950 9.3.4 - fraction-digits */
if (!base) {
@@ -1735,7 +1836,8 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
} else {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "fraction-digits", "decimal64 type", "");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
dec->fraction_digits = type_p->fraction_digits;
} else {
@@ -1749,59 +1851,76 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
"Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
dec->fraction_digits = ((struct lysc_type_dec *)base)->fraction_digits;
}
/* RFC 7950 9.2.4 - range */
if (type_p->range) {
- LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
- base ? ((struct lysc_type_dec *)base)->range : NULL, &dec->range));
+ LY_CHECK_GOTO(rc = lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
+ base ? ((struct lysc_type_dec *)base)->range : NULL, &dec->range), cleanup);
if (!tpdfname) {
- COMPILE_EXTS_GOTO(ctx, type_p->range->exts, dec->range->exts, dec->range, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, type_p->range->exts, dec->range->exts, dec->range, rc, cleanup);
}
}
break;
case LY_TYPE_STRING:
- str = (struct lysc_type_str *)(*type);
+ str = (struct lysc_type_str *)*type;
/* RFC 7950 9.4.4 - length */
if (type_p->length) {
- LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
- base ? ((struct lysc_type_str *)base)->length : NULL, &str->length));
+ LY_CHECK_GOTO(rc = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+ base ? ((struct lysc_type_str *)base)->length : NULL, &str->length), cleanup);
if (!tpdfname) {
- COMPILE_EXTS_GOTO(ctx, type_p->length->exts, str->length->exts, str->length, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, type_p->length->exts, str->length->exts, str->length, rc, cleanup);
}
} else if (base && ((struct lysc_type_str *)base)->length) {
- str->length = lysc_range_dup(ctx->ctx, ((struct lysc_type_str *)base)->length);
+ str->length = lysc_range_dup(ctx, ((struct lysc_type_str *)base)->length, tpdf_chain, tpdf_chain_last);
}
/* RFC 7950 9.4.5 - pattern */
if (type_p->patterns) {
- LY_CHECK_RET(lys_compile_type_patterns(ctx, type_p->patterns,
- base ? ((struct lysc_type_str *)base)->patterns : NULL, &str->patterns));
+ LY_CHECK_GOTO(rc = lys_compile_type_patterns(ctx, type_p->patterns,
+ base ? ((struct lysc_type_str *)base)->patterns : NULL, &str->patterns), cleanup);
} else if (base && ((struct lysc_type_str *)base)->patterns) {
str->patterns = lysc_patterns_dup(ctx->ctx, ((struct lysc_type_str *)base)->patterns);
}
break;
case LY_TYPE_ENUM:
- enumeration = (struct lysc_type_enum *)(*type);
+ enumeration = (struct lysc_type_enum *)*type;
/* RFC 7950 9.6 - enum */
if (type_p->enums) {
- LY_CHECK_RET(lys_compile_type_enums(ctx, type_p->enums, basetype,
- base ? ((struct lysc_type_enum *)base)->enums : NULL, &enumeration->enums));
- }
+ LY_CHECK_GOTO(rc = lys_compile_type_enums(ctx, type_p->enums, basetype,
+ base ? ((struct lysc_type_enum *)base)->enums : NULL, &enumeration->enums), cleanup);
+ } else if (base) {
+ /* recompile enums from the first superior type with enums */
+ assert(tpdf_chain->count > tpdf_chain_last);
+ base_type_p = NULL;
+ i = tpdf_chain->count;
+ do {
+ --i;
+ tpdf_item = tpdf_chain->objs[i];
+
+ if (tpdf_item->tpdf->type.enums) {
+ base_type_p = &tpdf_item->tpdf->type;
+ break;
+ }
+ } while (i > tpdf_chain_last);
+ assert(base_type_p);
- if (!base && !type_p->flags) {
+ LY_CHECK_GOTO(rc = lys_compile_type_enums(ctx, base_type_p->enums, basetype, NULL, &enumeration->enums), cleanup);
+ } else {
/* type derived from enumerations built-in type must contain at least one enum */
if (tpdfname) {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "enum", "enumeration type ", tpdfname);
} else {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "enum", "enumeration type", "");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
break;
case LY_TYPE_INT8:
@@ -1812,19 +1931,19 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
case LY_TYPE_UINT32:
case LY_TYPE_INT64:
case LY_TYPE_UINT64:
- num = (struct lysc_type_num *)(*type);
+ num = (struct lysc_type_num *)*type;
/* RFC 6020 9.2.4 - range */
if (type_p->range) {
- LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
- base ? ((struct lysc_type_num *)base)->range : NULL, &num->range));
+ LY_CHECK_GOTO(rc = lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
+ base ? ((struct lysc_type_num *)base)->range : NULL, &num->range), cleanup);
if (!tpdfname) {
- COMPILE_EXTS_GOTO(ctx, type_p->range->exts, num->range->exts, num->range, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, type_p->range->exts, num->range->exts, num->range, rc, cleanup);
}
}
break;
case LY_TYPE_IDENT:
- idref = (struct lysc_type_identityref *)(*type);
+ idref = (struct lysc_type_identityref *)*type;
/* RFC 7950 9.10.2 - base */
if (type_p->bases) {
@@ -1838,19 +1957,29 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
"Invalid base substatement for the type not directly derived from identityref built-in type.");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
- LY_CHECK_RET(lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases));
- }
-
- if (!base && !type_p->flags) {
+ LY_CHECK_GOTO(rc = lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases), cleanup);
+ } else if (base) {
+ /* copy all the bases */
+ const struct lysc_type_identityref *idref_base = (struct lysc_type_identityref *)base;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, idref->bases, LY_ARRAY_COUNT(idref_base->bases), rc, cleanup);
+ LY_ARRAY_FOR(idref_base->bases, u) {
+ idref->bases[u] = idref_base->bases[u];
+ LY_ARRAY_INCREMENT(idref->bases);
+ }
+ } else {
/* type derived from identityref built-in type must contain at least one base */
if (tpdfname) {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "base", "identityref type ", tpdfname);
} else {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "base", "identityref type", "");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
break;
case LY_TYPE_LEAFREF:
@@ -1866,7 +1995,8 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
LOGVAL(ctx->ctx, LYVE_SEMANTICS,
"Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
lref->require_instance = type_p->require_instance;
} else if (base) {
@@ -1879,32 +2009,35 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
if (type_p->path) {
LY_VALUE_FORMAT format;
- LY_CHECK_RET(lyxp_expr_dup(ctx->ctx, type_p->path, 0, 0, &lref->path));
- LY_CHECK_RET(lyplg_type_prefix_data_new(ctx->ctx, type_p->path->expr, strlen(type_p->path->expr),
- LY_VALUE_SCHEMA, type_p->pmod, &format, (void **)&lref->prefixes));
+ LY_CHECK_GOTO(rc = lyxp_expr_dup(ctx->ctx, type_p->path, 0, 0, &lref->path), cleanup);
+ LY_CHECK_GOTO(lyplg_type_prefix_data_new(ctx->ctx, type_p->path->expr, strlen(type_p->path->expr),
+ LY_VALUE_SCHEMA, type_p->pmod, &format, (void **)&lref->prefixes), cleanup);
} else if (base) {
- LY_CHECK_RET(lyxp_expr_dup(ctx->ctx, ((struct lysc_type_leafref *)base)->path, 0, 0, &lref->path));
- LY_CHECK_RET(lyplg_type_prefix_data_dup(ctx->ctx, LY_VALUE_SCHEMA_RESOLVED,
- ((struct lysc_type_leafref *)base)->prefixes, (void **)&lref->prefixes));
- } else if (tpdfname) {
- LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "path", "leafref type ", tpdfname);
- return LY_EVALID;
+ LY_CHECK_GOTO(rc = lyxp_expr_dup(ctx->ctx, ((struct lysc_type_leafref *)base)->path, 0, 0, &lref->path), cleanup);
+ LY_CHECK_GOTO(rc = lyplg_type_prefix_data_dup(ctx->ctx, LY_VALUE_SCHEMA_RESOLVED,
+ ((struct lysc_type_leafref *)base)->prefixes, (void **)&lref->prefixes), cleanup);
} else {
- LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "path", "leafref type", "");
- return LY_EVALID;
+ /* type derived from leafref built-in type must contain path */
+ if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "path", "leafref type ", tpdfname);
+ } else {
+ LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "path", "leafref type", "");
+ }
+ rc = LY_EVALID;
+ goto cleanup;
}
break;
case LY_TYPE_INST:
/* RFC 7950 9.9.3 - require-instance */
if (type_p->flags & LYS_SET_REQINST) {
- ((struct lysc_type_instanceid *)(*type))->require_instance = type_p->require_instance;
+ ((struct lysc_type_instanceid *)*type)->require_instance = type_p->require_instance;
} else {
/* default is true */
- ((struct lysc_type_instanceid *)(*type))->require_instance = 1;
+ ((struct lysc_type_instanceid *)*type)->require_instance = 1;
}
break;
case LY_TYPE_UNION:
- un = (struct lysc_type_union *)(*type);
+ un = (struct lysc_type_union *)*type;
/* RFC 7950 7.4 - type */
if (type_p->types) {
@@ -1918,20 +2051,32 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
"Invalid type substatement for the type not directly derived from union built-in type.");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
/* compile the type */
- LY_CHECK_RET(lys_compile_type_union(ctx, type_p->types, context_pnode, context_flags, context_name, &un->types));
- }
-
- if (!base && !type_p->flags) {
+ LY_CHECK_GOTO(rc = lys_compile_type_union(ctx, type_p->types, context_pnode, context_flags, context_name,
+ &un->types), cleanup);
+ } else if (base) {
+ /* copy all the types */
+ const struct lysc_type_union *un_base = (struct lysc_type_union *)base;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_CREATE_GOTO(ctx->ctx, un->types, LY_ARRAY_COUNT(un_base->types), rc, cleanup);
+ LY_ARRAY_FOR(un_base->types, u) {
+ un->types[u] = un_base->types[u];
+ LY_ATOMIC_INC_BARRIER(un->types[u]->refcount);
+ LY_ARRAY_INCREMENT(un->types);
+ }
+ } else {
/* type derived from union built-in type must contain at least one type */
if (tpdfname) {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "type", "union type ", tpdfname);
} else {
LOGVAL(ctx->ctx, LY_VCODE_MISSCHILDSTMT, "type", "union type", "");
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
break;
case LY_TYPE_BOOL:
@@ -1940,65 +2085,27 @@ lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_
break;
}
- if (tpdfname) {
- switch (basetype) {
- case LY_TYPE_BINARY:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_bin));
- break;
- case LY_TYPE_BITS:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_bits));
- break;
- case LY_TYPE_DEC64:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_dec));
- break;
- case LY_TYPE_STRING:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_str));
- break;
- case LY_TYPE_ENUM:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_enum));
- break;
- case LY_TYPE_INT8:
- case LY_TYPE_UINT8:
- case LY_TYPE_INT16:
- case LY_TYPE_UINT16:
- case LY_TYPE_INT32:
- case LY_TYPE_UINT32:
- case LY_TYPE_INT64:
- case LY_TYPE_UINT64:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_num));
- break;
- case LY_TYPE_IDENT:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_identityref));
- break;
- case LY_TYPE_LEAFREF:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_leafref));
- break;
- case LY_TYPE_INST:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_instanceid));
- break;
- case LY_TYPE_UNION:
- type_p->compiled = *type;
- *type = calloc(1, sizeof(struct lysc_type_union));
- break;
- case LY_TYPE_BOOL:
- case LY_TYPE_EMPTY:
- case LY_TYPE_UNKNOWN: /* just to complete switch */
- break;
- }
+ if (tpdf_chain->count > tpdf_chain_last) {
+ i = tpdf_chain->count;
+ do {
+ --i;
+ tpdf_item = tpdf_chain->objs[i];
+
+ /* compile previous typedefs extensions */
+ COMPILE_EXTS_GOTO(ctx, tpdf_item->tpdf->type.exts, (*type)->exts, *type, rc, cleanup);
+ } while (i > tpdf_chain_last);
}
- LY_CHECK_ERR_RET(!(*type), LOGMEM(ctx->ctx), LY_EMEM);
+
+ /* compile new parsed extensions */
+ COMPILE_EXTS_GOTO(ctx, type_p->exts, (*type)->exts, *type, rc, cleanup);
cleanup:
- return ret;
+ if (rc) {
+ LY_ATOMIC_INC_BARRIER((*type)->refcount);
+ lysc_type_free(&ctx->free_ctx, *type);
+ *type = NULL;
+ }
+ return rc;
}
LY_ERR
@@ -2007,16 +2114,13 @@ lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t
{
LY_ERR ret = LY_SUCCESS;
ly_bool dummyloops = 0;
- struct type_context {
- const struct lysp_tpdf *tpdf;
- struct lysp_node *node;
- } *tctx, *tctx_prev = NULL, *tctx_iter;
+ struct lys_type_item *tctx, *tctx_prev = NULL, *tctx_iter;
LY_DATA_TYPE basetype = LY_TYPE_UNKNOWN;
- struct lysc_type *base = NULL, *prev_type;
+ struct lysc_type *base = NULL;
struct ly_set tpdf_chain = {0};
struct lyplg_type *plugin;
- (*type) = NULL;
+ *type = NULL;
if (dflt) {
*dflt = NULL;
}
@@ -2046,7 +2150,7 @@ lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t
*dflt = (struct lysp_qname *)&tctx->tpdf->dflt;
}
if (dummyloops && (!units || *units) && dflt && *dflt) {
- basetype = ((struct type_context *)tpdf_chain.objs[tpdf_chain.count - 1])->tpdf->type.compiled->basetype;
+ basetype = ((struct lys_type_item *)tpdf_chain.objs[tpdf_chain.count - 1])->tpdf->type.compiled->basetype;
break;
}
@@ -2076,7 +2180,7 @@ lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t
/* circular typedef reference detection */
for (uint32_t u = 0; u < tpdf_chain.count; u++) {
/* local part */
- tctx_iter = (struct type_context *)tpdf_chain.objs[u];
+ tctx_iter = (struct lys_type_item *)tpdf_chain.objs[u];
if (tctx_iter->tpdf == tctx->tpdf) {
LOGVAL(ctx->ctx, LYVE_REFERENCE,
"Invalid \"%s\" type reference - circular chain of types detected.", tctx->tpdf->name);
@@ -2087,7 +2191,7 @@ lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_pnode, uint16_t
}
for (uint32_t u = 0; u < ctx->tpdf_chain.count; u++) {
/* global part for unions corner case */
- tctx_iter = (struct type_context *)ctx->tpdf_chain.objs[u];
+ tctx_iter = (struct lys_type_item *)ctx->tpdf_chain.objs[u];
if (tctx_iter->tpdf == tctx->tpdf) {
LOGVAL(ctx->ctx, LYVE_REFERENCE,
"Invalid \"%s\" type reference - circular chain of types detected.", tctx->tpdf->name);
@@ -2109,68 +2213,23 @@ preparenext:
}
free(tctx);
- /* allocate type according to the basetype */
- switch (basetype) {
- case LY_TYPE_BINARY:
- *type = calloc(1, sizeof(struct lysc_type_bin));
- break;
- case LY_TYPE_BITS:
- *type = calloc(1, sizeof(struct lysc_type_bits));
- break;
- case LY_TYPE_BOOL:
- case LY_TYPE_EMPTY:
- *type = calloc(1, sizeof(struct lysc_type));
- break;
- case LY_TYPE_DEC64:
- *type = calloc(1, sizeof(struct lysc_type_dec));
- break;
- case LY_TYPE_ENUM:
- *type = calloc(1, sizeof(struct lysc_type_enum));
- break;
- case LY_TYPE_IDENT:
- *type = calloc(1, sizeof(struct lysc_type_identityref));
- break;
- case LY_TYPE_INST:
- *type = calloc(1, sizeof(struct lysc_type_instanceid));
- break;
- case LY_TYPE_LEAFREF:
- *type = calloc(1, sizeof(struct lysc_type_leafref));
- break;
- case LY_TYPE_STRING:
- *type = calloc(1, sizeof(struct lysc_type_str));
- break;
- case LY_TYPE_UNION:
- *type = calloc(1, sizeof(struct lysc_type_union));
- break;
- case LY_TYPE_INT8:
- case LY_TYPE_UINT8:
- case LY_TYPE_INT16:
- case LY_TYPE_UINT16:
- case LY_TYPE_INT32:
- case LY_TYPE_UINT32:
- case LY_TYPE_INT64:
- case LY_TYPE_UINT64:
- *type = calloc(1, sizeof(struct lysc_type_num));
- break;
- case LY_TYPE_UNKNOWN:
+ /* basic checks */
+ if (basetype == LY_TYPE_UNKNOWN) {
LOGVAL(ctx->ctx, LYVE_REFERENCE,
"Referenced type \"%s\" not found.", tctx_prev ? tctx_prev->tpdf->type.name : type_p->name);
ret = LY_EVALID;
goto cleanup;
}
- LY_CHECK_ERR_GOTO(!(*type), LOGMEM(ctx->ctx), cleanup);
if (~type_substmt_map[basetype] & type_p->flags) {
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Invalid type restrictions for %s type.",
ly_data_type2str[basetype]);
- free(*type);
- (*type) = NULL;
ret = LY_EVALID;
goto cleanup;
}
/* get restrictions from the referred typedefs */
for (uint32_t u = tpdf_chain.count - 1; u + 1 > 0; --u) {
- tctx = (struct type_context *)tpdf_chain.objs[u];
+ tctx = (struct lys_type_item *)tpdf_chain.objs[u];
/* remember the typedef context for circular check */
ret = ly_set_add(&ctx->tpdf_chain, tctx, 1, NULL);
@@ -2195,15 +2254,14 @@ preparenext:
}
assert(plugin);
- if ((basetype != LY_TYPE_LEAFREF) && (u != tpdf_chain.count - 1) && !(tctx->tpdf->type.flags) &&
- (plugin == base->plugin)) {
+ if ((basetype != LY_TYPE_LEAFREF) && (u != tpdf_chain.count - 1) && !tctx->tpdf->type.flags &&
+ !tctx->tpdf->type.exts && (plugin == base->plugin)) {
/* no change, reuse the compiled base */
((struct lysp_tpdf *)tctx->tpdf)->type.compiled = base;
LY_ATOMIC_INC_BARRIER(base->refcount);
continue;
}
- LY_ATOMIC_INC_BARRIER((*type)->refcount);
if (~type_substmt_map[basetype] & tctx->tpdf->type.flags) {
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Invalid type \"%s\" restriction(s) for %s type.",
tctx->tpdf->name, ly_data_type2str[basetype]);
@@ -2217,40 +2275,31 @@ preparenext:
goto cleanup;
}
- (*type)->basetype = basetype;
- (*type)->plugin = plugin;
-
- /* collect extensions */
- COMPILE_EXTS_GOTO(ctx, tctx->tpdf->type.exts, (*type)->exts, (*type), ret, cleanup);
-
- /* compile the new typedef */
- prev_type = *type;
- ret = lys_compile_type_(ctx, tctx->node, tctx->tpdf->flags, tctx->tpdf->name,
- &((struct lysp_tpdf *)tctx->tpdf)->type, basetype, tctx->tpdf->name, base, type);
+ /* compile the typedef type */
+ ret = lys_compile_type_(ctx, tctx->node, tctx->tpdf->flags, tctx->tpdf->name, &tctx->tpdf->type, basetype,
+ tctx->tpdf->name, base, plugin, &tpdf_chain, u + 1, &base);
LY_CHECK_GOTO(ret, cleanup);
- base = prev_type;
+
+ /* store separately compiled typedef type to be reused */
+ ((struct lysp_tpdf *)tctx->tpdf)->type.compiled = base;
+ LY_ATOMIC_INC_BARRIER(base->refcount);
}
+
/* remove the processed typedef contexts from the stack for circular check */
ctx->tpdf_chain.count = ctx->tpdf_chain.count - tpdf_chain.count;
/* process the type definition in leaf */
- if (type_p->flags || !base || (basetype == LY_TYPE_LEAFREF)) {
- /* get restrictions from the node itself */
- (*type)->basetype = basetype;
- (*type)->plugin = base ? base->plugin : lyplg_type_plugin_find("", NULL, ly_data_type2str[basetype]);
- LY_ATOMIC_INC_BARRIER((*type)->refcount);
+ if (type_p->flags || type_p->exts || !base || (basetype == LY_TYPE_LEAFREF)) {
+ /* leaf type has changes that need to be compiled into the type */
+ plugin = base ? base->plugin : lyplg_type_plugin_find("", NULL, ly_data_type2str[basetype]);
ret = lys_compile_type_(ctx, context_pnode, context_flags, context_name, (struct lysp_type *)type_p, basetype,
- NULL, base, type);
+ NULL, base, plugin, &tpdf_chain, 0, type);
LY_CHECK_GOTO(ret, cleanup);
- } else if ((basetype != LY_TYPE_BOOL) && (basetype != LY_TYPE_EMPTY)) {
- /* no specific restriction in leaf's type definition, copy from the base */
- free(*type);
- (*type) = base;
- LY_ATOMIC_INC_BARRIER((*type)->refcount);
+ } else {
+ /* no changes of the type in the leaf, just use the base compiled type */
+ *type = base;
}
- COMPILE_EXTS_GOTO(ctx, type_p->exts, (*type)->exts, (*type), ret, cleanup);
-
cleanup:
ly_set_erase(&tpdf_chain, free);
return ret;
@@ -2874,6 +2923,7 @@ lys_compile_node_type(struct lysc_ctx *ctx, struct lysp_node *context_node, stru
LY_CHECK_RET(lys_compile_type(ctx, context_node, leaf->flags, leaf->name, type_p, &leaf->type,
leaf->units ? NULL : &leaf->units, &dflt));
+ LY_ATOMIC_INC_BARRIER(leaf->type->refcount);
/* store default value, if any */
if (dflt && !(leaf->flags & LYS_SET_DFLT)) {
@@ -3367,7 +3417,7 @@ lys_compile_node_list(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc
/* YANG 1.0 denies key to be of empty type */
if (key->type->basetype == LY_TYPE_EMPTY) {
LOGVAL(ctx->ctx, LYVE_SEMANTICS,
- "List's key cannot be of \"empty\" type until it is in YANG 1.1 module.");
+ "List key of the \"empty\" type is allowed only in YANG 1.1 modules.");
return LY_EVALID;
}
} else {
diff --git a/src/schema_features.c b/src/schema_features.c
index dca998e..b4273df 100644
--- a/src/schema_features.c
+++ b/src/schema_features.c
@@ -411,7 +411,7 @@ lys_compile_iffeature(const struct ly_ctx *ctx, const struct lysp_qname *qname,
LY_ARRAY_CREATE_RET(ctx, iff->features, f_size, LY_EMEM);
iff->expr = calloc((j = (expr_size / IFF_RECORDS_IN_BYTE) + ((expr_size % IFF_RECORDS_IN_BYTE) ? 1 : 0)), sizeof *iff->expr);
stack.stack = malloc(expr_size * sizeof *stack.stack);
- LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx); rc = LY_EMEM, error);
+ LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx); rc = LY_EMEM, cleanup);
stack.size = expr_size;
f_size--; expr_size--; /* used as indexes from now */
@@ -473,7 +473,7 @@ lys_compile_iffeature(const struct ly_ctx *ctx, const struct lysp_qname *qname,
LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".",
qname->str, (int)(j - i), &c[i]);
rc = LY_EVALID;
- goto error;
+ goto cleanup;
}
iff->features[f_size] = f;
LY_ARRAY_INCREMENT(iff->features);
@@ -489,14 +489,16 @@ lys_compile_iffeature(const struct ly_ctx *ctx, const struct lysp_qname *qname,
/* not all expected operators and operands found */
LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - processing error.", qname->str);
rc = LY_EINT;
- } else {
- rc = LY_SUCCESS;
}
-error:
- /* cleanup */
+cleanup:
+ if (rc) {
+ LY_ARRAY_FREE(iff->features);
+ iff->features = NULL;
+ free(iff->expr);
+ iff->expr = NULL;
+ }
iff_stack_clean(&stack);
-
return rc;
}
diff --git a/src/set.c b/src/set.c
index 1b8bfa5..f36ab5f 100644
--- a/src/set.c
+++ b/src/set.c
@@ -227,9 +227,11 @@ ly_set_rm(struct ly_set *set, void *object, void (*destructor)(void *obj))
return ly_set_rm_index(set, i, destructor);
}
-LY_ERR
+LIBYANG_API_DEF LY_ERR
ly_set_rm_index_ordered(struct ly_set *set, uint32_t index, void (*destructor)(void *obj))
{
+ LY_CHECK_ARG_RET(NULL, set, set->count, LY_EINVAL);
+
if (destructor) {
destructor(set->objs[index]);
}
diff --git a/src/set.h b/src/set.h
index 3f79916..08b2782 100644
--- a/src/set.h
+++ b/src/set.h
@@ -1,9 +1,10 @@
/**
* @file set.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief Generic set structure and manipulation routines.
*
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -81,8 +82,8 @@ LIBYANG_API_DECL LY_ERR ly_set_dup(const struct ly_set *set, void *(*duplicator)
/**
* @brief Add an object into the set
*
- * @param[in] set Set where the \p object will be added.
- * @param[in] object Object to be added into the \p set;
+ * @param[in] set Set where the @p object will be added.
+ * @param[in] object Object to be added into the @p set;
* @param[in] list flag to handle set as a list (without checking for (ignoring) duplicit items)
* @param[out] index_p Optional pointer to return index of the added @p object. Usually it is the last index (::ly_set::count - 1),
* but in case the duplicities are checked and the object is already in the set, the @p object is not added and index of the
@@ -94,7 +95,7 @@ LIBYANG_API_DECL LY_ERR ly_set_dup(const struct ly_set *set, void *(*duplicator)
LIBYANG_API_DECL LY_ERR ly_set_add(struct ly_set *set, const void *object, ly_bool list, uint32_t *index_p);
/**
- * @brief Add all objects from \p src to \p trg.
+ * @brief Add all objects from @p src to @p trg.
*
* Since it is a set, the function checks for duplicities.
*
@@ -102,8 +103,8 @@ LIBYANG_API_DECL LY_ERR ly_set_add(struct ly_set *set, const void *object, ly_bo
* @param[in] src Source set.
* @param[in] list flag to handle set as a list (without checking for (ignoring) duplicit items)
* @param[in] duplicator Optional pointer to function that duplicates the objects being added
- * from \p src into \p trg set. If not provided, the \p trg set will point to the exact same
- * objects as the \p src set.
+ * from @p src into @p trg set. If not provided, the @p trg set will point to the exact same
+ * objects as the @p src set.
* @return LY_SUCCESS in case of success
* @return LY_EINVAL in case of invalid input parameters.
* @return LY_EMEM in case of memory allocation failure.
@@ -134,8 +135,8 @@ LIBYANG_API_DECL void ly_set_clean(struct ly_set *set, void (*destructor)(void *
* Note that after removing the object from a set, indexes of other objects in the set can change
* (the last object is placed instead of the removed object).
*
- * @param[in] set Set from which the \p node will be removed.
- * @param[in] object The object to be removed from the \p set.
+ * @param[in] set Set from which to remove.
+ * @param[in] object The object to be removed from the @p set.
* @param[in] destructor Optional function to free the objects being removed.
* @return LY_ERR return value.
*/
@@ -147,14 +148,26 @@ LIBYANG_API_DECL LY_ERR ly_set_rm(struct ly_set *set, void *object, void (*destr
* Note that after removing the object from a set, indexes of other nodes in the set can change
* (the last object is placed instead of the removed object).
*
- * @param[in] set Set from which a node will be removed.
- * @param[in] index Index of the object to remove in the \p set.
+ * @param[in] set Set from which to remove.
+ * @param[in] index Index of the object to remove in the @p set.
* @param[in] destructor Optional function to free the objects being removed.
* @return LY_ERR return value.
*/
LIBYANG_API_DECL LY_ERR ly_set_rm_index(struct ly_set *set, uint32_t index, void (*destructor)(void *obj));
/**
+ * @brief Remove an object on the specific set index.
+ *
+ * Unlike ::ly_set_rm_indes(), this function moves all the items following the removed one.
+ *
+ * @param[in] set Set from which to remove.
+ * @param[in] index Index of the object to remove in the @p set.
+ * @param[in] destructor Optional function to free the objects being removed.
+ * @return LY_ERR return value.
+ */
+LIBYANG_API_DECL LY_ERR ly_set_rm_index_ordered(struct ly_set *set, uint32_t index, void (*destructor)(void *obj));
+
+/**
* @brief Free the ::ly_set data. If the destructor is not provided, it frees only the set structure
* content, not the referred data.
*
diff --git a/src/tree_data.c b/src/tree_data.c
index d6a04ff..82a1ae8 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -52,6 +52,9 @@
#include "xml.h"
#include "xpath.h"
+static LY_ERR lyd_compare_siblings_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
+ ly_bool parental_schemas_checked);
+
static LYD_FORMAT
lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format)
{
@@ -96,10 +99,9 @@ static LY_ERR
lyd_parse(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, struct lyd_node **first_p,
struct ly_in *in, LYD_FORMAT format, uint32_t parse_opts, uint32_t val_opts, struct lyd_node **op)
{
- LY_ERR rc = LY_SUCCESS;
+ LY_ERR r = LY_SUCCESS, rc = LY_SUCCESS;
struct lyd_ctx *lydctx = NULL;
struct ly_set parsed = {0};
- struct lyd_node *first;
uint32_t i, int_opts = 0;
ly_bool subtree_sibling = 0;
@@ -121,36 +123,40 @@ lyd_parse(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct
/* parse the data */
switch (format) {
case LYD_XML:
- rc = lyd_parse_xml(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
+ r = lyd_parse_xml(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
&subtree_sibling, &lydctx);
break;
case LYD_JSON:
- rc = lyd_parse_json(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
+ r = lyd_parse_json(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
&subtree_sibling, &lydctx);
break;
case LYD_LYB:
- rc = lyd_parse_lyb(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
+ r = lyd_parse_lyb(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed,
&subtree_sibling, &lydctx);
break;
case LYD_UNKNOWN:
LOGARG(ctx, format);
- rc = LY_EINVAL;
+ r = LY_EINVAL;
break;
}
- LY_CHECK_GOTO(rc, cleanup);
+ if (r) {
+ rc = r;
+ if ((r != LY_EVALID) || !lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) ||
+ (ly_vecode(ctx) == LYVE_SYNTAX)) {
+ goto cleanup;
+ }
+ }
- if (parent) {
- /* get first top-level sibling */
- for (first = parent; first->parent; first = lyd_parent(first)) {}
- first = lyd_first_sibling(first);
- first_p = &first;
+ if (parent && parsed.count) {
+ /* use the first parsed node */
+ first_p = &parsed.dnodes[0];
}
if (!(parse_opts & LYD_PARSE_ONLY)) {
/* validate data */
- rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types,
+ r = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types,
&lydctx->ext_node, &lydctx->ext_val, NULL);
- LY_CHECK_GOTO(rc, cleanup);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
/* set the operation node */
@@ -252,28 +258,7 @@ lyd_parse_data_path(const struct ly_ctx *ctx, const char *path, LYD_FORMAT forma
*
* At least one of @p parent, @p tree, or @p op must always be set.
*
- * Specific @p data_type values have different parameter meaning as follows:
- * - ::LYD_TYPE_RPC_NETCONF:
- * - @p parent - must be NULL, the whole RPC is expected;
- * - @p format - must be ::LYD_XML, NETCONF supports only this format;
- * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- * a separate opaque data tree, even if the function fails, this may be returned;
- * - @p op - must be provided, the RPC/action data tree itself will be returned here, pointing to the operation;
- *
- * - ::LYD_TYPE_NOTIF_NETCONF:
- * - @p parent - must be NULL, the whole notification is expected;
- * - @p format - must be ::LYD_XML, NETCONF supports only this format;
- * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- * a separate opaque data tree, even if the function fails, this may be returned;
- * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation;
- *
- * - ::LYD_TYPE_REPLY_NETCONF:
- * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
- * - @p format - must be ::LYD_XML, NETCONF supports only this format;
- * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
- * a separate opaque data tree, even if the function fails, this may be returned;
- * - @p op - must be NULL, the reply is appended to the RPC;
- * Note that there are 3 kinds of NETCONF replies - ok, error, and data. Only data reply appends any nodes to the RPC.
+ * Specific @p data_type values have different parameter meaning as mentioned for ::lyd_parse_op().
*
* @param[in] ctx libyang context.
* @param[in] ext Extension instance providing the specific schema tree to match with the data being parsed.
@@ -295,6 +280,7 @@ lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
struct ly_set parsed = {0};
struct lyd_node *first = NULL, *envp = NULL;
uint32_t i, parse_opts, val_opts, int_opts = 0;
+ ly_bool proto_msg = 0;
if (!ctx) {
ctx = LYD_CTX(parent);
@@ -316,20 +302,51 @@ lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
val_opts = 0;
switch (data_type) {
-
- /* special XML NETCONF data types */
case LYD_TYPE_RPC_NETCONF:
case LYD_TYPE_NOTIF_NETCONF:
LY_CHECK_ARG_RET(ctx, format == LYD_XML, !parent, tree, op, LY_EINVAL);
- /* fallthrough */
+ proto_msg = 1;
+ break;
case LYD_TYPE_REPLY_NETCONF:
- if (data_type == LYD_TYPE_REPLY_NETCONF) {
- LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op,
- LY_EINVAL);
- }
+ LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION),
+ tree, !op, LY_EINVAL);
+ proto_msg = 1;
+ break;
+ case LYD_TYPE_RPC_RESTCONF:
+ case LYD_TYPE_REPLY_RESTCONF:
+ LY_CHECK_ARG_RET(ctx, parent, parent->schema, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, !op, LY_EINVAL);
+ proto_msg = 1;
+ break;
+ case LYD_TYPE_NOTIF_RESTCONF:
+ LY_CHECK_ARG_RET(ctx, format == LYD_JSON, !parent, tree, op, LY_EINVAL);
+ proto_msg = 1;
+ break;
- /* parse the NETCONF message */
- rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ /* set internal opts */
+ case LYD_TYPE_RPC_YANG:
+ int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS);
+ break;
+ case LYD_TYPE_NOTIF_YANG:
+ int_opts = LYD_INTOPT_NOTIF | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS);
+ break;
+ case LYD_TYPE_REPLY_YANG:
+ int_opts = LYD_INTOPT_REPLY | (parent ? LYD_INTOPT_WITH_SIBLINGS : LYD_INTOPT_NO_SIBLINGS);
+ break;
+ case LYD_TYPE_DATA_YANG:
+ LOGINT(ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+
+ /* parse a full protocol message */
+ if (proto_msg) {
+ if (format == LYD_XML) {
+ /* parse the NETCONF (or RESTCONF XML) message */
+ rc = lyd_parse_xml_netconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ } else {
+ /* parse the RESTCONF message */
+ rc = lyd_parse_json_restconf(ctx, ext, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ }
if (rc) {
if (envp) {
/* special situation when the envelopes were parsed successfully */
@@ -349,21 +366,6 @@ lyd_parse_op_(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, str
*op = lydctx->op_node;
}
goto cleanup;
-
- /* set internal opts */
- case LYD_TYPE_RPC_YANG:
- int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
- break;
- case LYD_TYPE_NOTIF_YANG:
- int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS;
- break;
- case LYD_TYPE_REPLY_YANG:
- int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS;
- break;
- case LYD_TYPE_DATA_YANG:
- LOGINT(ctx);
- rc = LY_EINT;
- goto cleanup;
}
/* parse the data */
@@ -405,7 +407,7 @@ cleanup:
lyd_free_tree(parsed.dnodes[i]);
} while (i);
}
- if (tree && ((format != LYD_XML) || !envp)) {
+ if (tree && !envp) {
*tree = NULL;
}
if (op) {
@@ -484,6 +486,10 @@ lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct ly
/* get the first schema sibling */
schema = lys_getnext(NULL, sparent, new_node->schema->module->compiled, getnext_opts);
+ if (!schema) {
+ /* must be a top-level extension instance data, no anchor */
+ return NULL;
+ }
found = 0;
LY_LIST_FOR(match, match) {
@@ -506,8 +512,12 @@ lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct ly
/* current node (match) is a data node still before the new node, continue search in data */
break;
}
+
schema = lys_getnext(schema, sparent, new_node->schema->module->compiled, getnext_opts);
- assert(schema);
+ if (!schema) {
+ /* must be a top-level extension instance data, no anchor */
+ return NULL;
+ }
}
if (found && (match->schema != new_node->schema)) {
@@ -660,6 +670,14 @@ lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling_p, stru
} else {
/* find the anchor, our next node, so we can insert before it */
anchor = lyd_insert_get_next_anchor(first_sibling, node);
+
+ /* cannot insert data node after opaque nodes */
+ if (!anchor && node->schema && first_sibling && !first_sibling->prev->schema) {
+ anchor = first_sibling->prev;
+ while ((anchor != first_sibling) && !anchor->prev->schema) {
+ anchor = anchor->prev;
+ }
+ }
}
if (anchor) {
@@ -684,7 +702,7 @@ lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling_p, stru
lyd_insert_hash(node);
/* finish hashes for our parent, if needed and possible */
- if (node->schema && (node->schema->flags & LYS_KEY) && parent && lyd_insert_has_keys(parent)) {
+ if (node->schema && (node->schema->flags & LYS_KEY) && parent && parent->schema && lyd_insert_has_keys(parent)) {
lyd_hash(parent);
/* now we can insert even the list into its parent HT */
@@ -756,12 +774,12 @@ lyd_insert_child(struct lyd_node *parent, struct lyd_node *node)
}
if (node->parent || node->prev->next) {
- lyd_unlink_tree(node);
+ lyd_unlink(node);
}
while (node) {
iter = node->next;
- lyd_unlink_tree(node);
+ lyd_unlink(node);
lyd_insert_node(parent, NULL, node, 0);
node = iter;
}
@@ -783,7 +801,7 @@ lyplg_ext_insert(struct lyd_node *parent, struct lyd_node *first)
while (first) {
iter = first->next;
- lyd_unlink_tree(first);
+ lyd_unlink(first);
lyd_insert_node(parent, NULL, first, 1);
first = iter;
}
@@ -807,7 +825,7 @@ lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_n
}
if (node->parent || node->prev->next) {
- lyd_unlink_tree(node);
+ lyd_unlink(node);
}
while (node) {
@@ -817,7 +835,7 @@ lyd_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct lyd_n
}
iter = node->next;
- lyd_unlink_tree(node);
+ lyd_unlink(node);
lyd_insert_node(NULL, &sibling, node, 0);
node = iter;
}
@@ -850,7 +868,7 @@ lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node)
return LY_EINVAL;
}
- lyd_unlink_tree(node);
+ lyd_unlink(node);
lyd_insert_before_node(sibling, node);
lyd_insert_hash(node);
@@ -874,26 +892,15 @@ lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node)
return LY_EINVAL;
}
- lyd_unlink_tree(node);
+ lyd_unlink(node);
lyd_insert_after_node(sibling, node);
lyd_insert_hash(node);
return LY_SUCCESS;
}
-LIBYANG_API_DEF void
-lyd_unlink_siblings(struct lyd_node *node)
-{
- struct lyd_node *next, *elem, *first = NULL;
-
- LY_LIST_FOR_SAFE(node, next, elem) {
- lyd_unlink_tree(elem);
- lyd_insert_node(NULL, &first, elem, 1);
- }
-}
-
-LIBYANG_API_DEF void
-lyd_unlink_tree(struct lyd_node *node)
+void
+lyd_unlink(struct lyd_node *node)
{
struct lyd_node *iter;
@@ -941,6 +948,35 @@ lyd_unlink_tree(struct lyd_node *node)
node->prev = node;
}
+LIBYANG_API_DEF void
+lyd_unlink_siblings(struct lyd_node *node)
+{
+ struct lyd_node *next, *elem, *first = NULL;
+
+ LY_LIST_FOR_SAFE(node, next, elem) {
+ if (lysc_is_key(elem->schema) && elem->parent) {
+ LOGERR(LYD_CTX(elem), LY_EINVAL, "Cannot unlink a list key \"%s\", unlink the list instance instead.",
+ LYD_NAME(elem));
+ return;
+ }
+
+ lyd_unlink(elem);
+ lyd_insert_node(NULL, &first, elem, 1);
+ }
+}
+
+LIBYANG_API_DEF void
+lyd_unlink_tree(struct lyd_node *node)
+{
+ if (node && lysc_is_key(node->schema) && node->parent) {
+ LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot unlink a list key \"%s\", unlink the list instance instead.",
+ LYD_NAME(node));
+ return;
+ }
+
+ lyd_unlink(node);
+}
+
void
lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_dflt)
{
@@ -973,7 +1009,7 @@ lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_df
LY_ERR
lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
- size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
+ size_t name_len, const char *value, size_t value_len, ly_bool is_utf8, ly_bool *dynamic, LY_VALUE_FORMAT format,
void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, ly_bool clear_dflt, ly_bool *incomplete)
{
LY_ERR ret = LY_SUCCESS;
@@ -1005,7 +1041,7 @@ lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct ly
mt->parent = parent;
mt->annotation = ant;
lyplg_ext_get_storage(ant, LY_STMT_TYPE, sizeof ant_type, (const void **)&ant_type);
- ret = lyd_value_store(mod->ctx, &mt->value, ant_type, value, value_len, dynamic, format, prefix_data, hints,
+ ret = lyd_value_store(mod->ctx, &mt->value, ant_type, value, value_len, is_utf8, dynamic, format, prefix_data, hints,
ctx_node, incomplete);
LY_CHECK_ERR_GOTO(ret, free(mt), cleanup);
ret = lydict_insert(mod->ctx, name, name_len, &mt->name);
@@ -1110,11 +1146,9 @@ finish:
LIBYANG_API_DEF const struct lyd_node_term *
lyd_target(const struct ly_path *path, const struct lyd_node *tree)
{
- struct lyd_node *target;
+ struct lyd_node *target = NULL;
- if (ly_path_eval(path, tree, &target)) {
- return NULL;
- }
+ lyd_find_target(path, tree, &target);
return (struct lyd_node_term *)target;
}
@@ -1149,15 +1183,6 @@ lyd_compare_schema_equal(const struct lysc_node *schema1, const struct lysc_node
return 0;
}
- if (schema1->module->revision || schema2->module->revision) {
- if (!schema1->module->revision || !schema2->module->revision) {
- return 0;
- }
- if (strcmp(schema1->module->revision, schema2->module->revision)) {
- return 0;
- }
- }
-
return 1;
}
@@ -1269,30 +1294,21 @@ lyd_compare_single_value(const struct lyd_node *node1, const struct lyd_node *no
}
/**
- * @brief Internal implementation of @ref lyd_compare_single.
- * @copydoc lyd_compare_single
- * @param[in] parental_schemas_checked Flag used for optimization.
- * When this function is called for the first time, the flag must be set to 0.
- * The @ref lyd_compare_schema_parents_equal should be called only once during
- * recursive calls, and this is accomplished by setting to 1 in the lyd_compare_single_ body.
+ * @brief Compare 2 data nodes if they are equivalent regarding the schema tree.
+ *
+ * Works correctly even if @p node1 and @p node2 have different contexts.
+ *
+ * @param[in] node1 The first node to compare.
+ * @param[in] node2 The second node to compare.
+ * @param[in] options Various @ref datacompareoptions.
+ * @param[in] parental_schemas_checked Flag set if parent schemas were checked for match.
+ * @return LY_SUCCESS if the nodes are equivalent.
+ * @return LY_ENOT if the nodes are not equivalent.
*/
static LY_ERR
-lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
+lyd_compare_single_schema(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
ly_bool parental_schemas_checked)
{
- const struct lyd_node *iter1, *iter2;
- struct lyd_node_any *any1, *any2;
- int len1, len2;
- LY_ERR r;
-
- if (!node1 || !node2) {
- if (node1 == node2) {
- return LY_SUCCESS;
- } else {
- return LY_ENOT;
- }
- }
-
if (LYD_CTX(node1) == LYD_CTX(node2)) {
/* same contexts */
if (options & LYD_COMPARE_OPAQ) {
@@ -1317,6 +1333,28 @@ lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2,
}
}
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compare 2 data nodes if they are equivalent regarding the data they contain.
+ *
+ * Works correctly even if @p node1 and @p node2 have different contexts.
+ *
+ * @param[in] node1 The first node to compare.
+ * @param[in] node2 The second node to compare.
+ * @param[in] options Various @ref datacompareoptions.
+ * @return LY_SUCCESS if the nodes are equivalent.
+ * @return LY_ENOT if the nodes are not equivalent.
+ */
+static LY_ERR
+lyd_compare_single_data(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+{
+ const struct lyd_node *iter1, *iter2;
+ struct lyd_node_any *any1, *any2;
+ int len1, len2;
+ LY_ERR r;
+
if (!(options & LYD_COMPARE_OPAQ) && (node1->hash != node2->hash)) {
return LY_ENOT;
}
@@ -1326,14 +1364,16 @@ lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2,
if (!(options & LYD_COMPARE_OPAQ) && ((node1->schema && !node2->schema) || (!node1->schema && node2->schema))) {
return LY_ENOT;
}
- if ((r = lyd_compare_single_value(node1, node2))) {
- return r;
+ if ((!node1->schema && !node2->schema) || (node1->schema && (node1->schema->nodetype & LYD_NODE_TERM)) ||
+ (node2->schema && (node2->schema->nodetype & LYD_NODE_TERM))) {
+ /* compare values only if there are any to compare */
+ if ((r = lyd_compare_single_value(node1, node2))) {
+ return r;
+ }
}
if (options & LYD_COMPARE_FULL_RECURSION) {
- iter1 = lyd_child(node1);
- iter2 = lyd_child(node2);
- goto all_children_compare;
+ return lyd_compare_siblings_(lyd_child(node1), lyd_child(node2), options, 1);
}
return LY_SUCCESS;
} else {
@@ -1354,50 +1394,38 @@ lyd_compare_single_(const struct lyd_node *node1, const struct lyd_node *node2,
case LYS_RPC:
case LYS_ACTION:
case LYS_NOTIF:
- if (options & LYD_COMPARE_DEFAULTS) {
- if ((node1->flags & LYD_DEFAULT) != (node2->flags & LYD_DEFAULT)) {
- return LY_ENOT;
- }
- }
+ /* implicit container is always equal to a container with non-default descendants */
if (options & LYD_COMPARE_FULL_RECURSION) {
- iter1 = lyd_child(node1);
- iter2 = lyd_child(node2);
- goto all_children_compare;
+ return lyd_compare_siblings_(lyd_child(node1), lyd_child(node2), options, 1);
}
return LY_SUCCESS;
case LYS_LIST:
iter1 = lyd_child(node1);
iter2 = lyd_child(node2);
- if (!(node1->schema->flags & LYS_KEYLESS) && !(options & LYD_COMPARE_FULL_RECURSION)) {
- /* lists with keys, their equivalence is based on their keys */
- for (const struct lysc_node *key = lysc_node_child(node1->schema);
- key && (key->flags & LYS_KEY);
- key = key->next) {
- if (lyd_compare_single_(iter1, iter2, options, parental_schemas_checked)) {
- return LY_ENOT;
- }
- iter1 = iter1->next;
- iter2 = iter2->next;
- }
- } else {
- /* lists without keys, their equivalence is based on equivalence of all the children (both direct and indirect) */
+ if (options & LYD_COMPARE_FULL_RECURSION) {
+ return lyd_compare_siblings_(iter1, iter2, options, 1);
+ } else if (node1->schema->flags & LYS_KEYLESS) {
+ /* always equal */
+ return LY_SUCCESS;
+ }
-all_children_compare:
- if (!iter1 && !iter2) {
- /* no children, nothing to compare */
- return LY_SUCCESS;
+ /* lists with keys, their equivalence is based on their keys */
+ for (const struct lysc_node *key = lysc_node_child(node1->schema);
+ key && (key->flags & LYS_KEY);
+ key = key->next) {
+ if (!iter1 || !iter2) {
+ return (iter1 == iter2) ? LY_SUCCESS : LY_ENOT;
}
+ r = lyd_compare_single_schema(iter1, iter2, options, 1);
+ LY_CHECK_RET(r);
+ r = lyd_compare_single_data(iter1, iter2, options);
+ LY_CHECK_RET(r);
- for ( ; iter1 && iter2; iter1 = iter1->next, iter2 = iter2->next) {
- if (lyd_compare_single_(iter1, iter2, options | LYD_COMPARE_FULL_RECURSION, parental_schemas_checked)) {
- return LY_ENOT;
- }
- }
- if (iter1 || iter2) {
- return LY_ENOT;
- }
+ iter1 = iter1->next;
+ iter2 = iter2->next;
}
+
return LY_SUCCESS;
case LYS_ANYXML:
case LYS_ANYDATA:
@@ -1409,9 +1437,7 @@ all_children_compare:
}
switch (any1->value_type) {
case LYD_ANYDATA_DATATREE:
- iter1 = any1->value.tree;
- iter2 = any2->value.tree;
- goto all_children_compare;
+ return lyd_compare_siblings_(any1->value.tree, any2->value.tree, options, 1);
case LYD_ANYDATA_STRING:
case LYD_ANYDATA_XML:
case LYD_ANYDATA_JSON:
@@ -1441,23 +1467,77 @@ all_children_compare:
return LY_EINT;
}
-LIBYANG_API_DEF LY_ERR
-lyd_compare_single(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+/**
+ * @brief Compare all siblings at a node level.
+ *
+ * @param[in] node1 First sibling list.
+ * @param[in] node2 Second sibling list.
+ * @param[in] options Various @ref datacompareoptions.
+ * @param[in] parental_schemas_checked Flag set if parent schemas were checked for match.
+ * @return LY_SUCCESS if equal.
+ * @return LY_ENOT if not equal.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lyd_compare_siblings_(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options,
+ ly_bool parental_schemas_checked)
{
- return lyd_compare_single_(node1, node2, options, 0);
+ LY_ERR r;
+ const struct lyd_node *iter2;
+
+ while (node1 && node2) {
+ /* schema match */
+ r = lyd_compare_single_schema(node1, node2, options, parental_schemas_checked);
+ LY_CHECK_RET(r);
+
+ if (node1->schema && (((node1->schema->nodetype == LYS_LIST) && !(node1->schema->flags & LYS_KEYLESS)) ||
+ ((node1->schema->nodetype == LYS_LEAFLIST) && (node1->schema->flags & LYS_CONFIG_W))) &&
+ (node1->schema->flags & LYS_ORDBY_SYSTEM)) {
+ /* find a matching instance in case they are ordered differently */
+ r = lyd_find_sibling_first(node2, node1, (struct lyd_node **)&iter2);
+ if (r == LY_ENOTFOUND) {
+ /* no matching instance, data not equal */
+ r = LY_ENOT;
+ }
+ LY_CHECK_RET(r);
+ } else {
+ /* compare with the current node */
+ iter2 = node2;
+ }
+
+ /* data match */
+ r = lyd_compare_single_data(node1, iter2, options | LYD_COMPARE_FULL_RECURSION);
+ LY_CHECK_RET(r);
+
+ node1 = node1->next;
+ node2 = node2->next;
+ }
+
+ return (node1 || node2) ? LY_ENOT : LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
-lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+lyd_compare_single(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
{
- for ( ; node1 && node2; node1 = node1->next, node2 = node2->next) {
- LY_CHECK_RET(lyd_compare_single(node1, node2, options));
+ LY_ERR r;
+
+ if (!node1 || !node2) {
+ return (node1 == node2) ? LY_SUCCESS : LY_ENOT;
}
- if (node1 == node2) {
- return LY_SUCCESS;
+ /* schema match */
+ if ((r = lyd_compare_single_schema(node1, node2, options, 0))) {
+ return r;
}
- return LY_ENOT;
+
+ /* data match */
+ return lyd_compare_single_data(node1, node2, options);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_compare_siblings(const struct lyd_node *node1, const struct lyd_node *node2, uint32_t options)
+{
+ return lyd_compare_siblings_(node1, node2, options, 0);
}
LIBYANG_API_DEF LY_ERR
@@ -1676,6 +1756,9 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_
} else {
dup->flags = (node->flags & (LYD_DEFAULT | LYD_EXT)) | LYD_NEW;
}
+ if (options & LYD_DUP_WITH_PRIV) {
+ dup->priv = node->priv;
+ }
if (trg_ctx == LYD_CTX(node)) {
dup->schema = node->schema;
} else {
@@ -1736,7 +1819,7 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_
/* store canonical value in the target context */
val_can = lyd_get_value(node);
type = ((struct lysc_node_leaf *)term->schema)->type;
- ret = lyd_value_store(trg_ctx, &term->value, type, val_can, strlen(val_can), NULL, LY_VALUE_CANON, NULL,
+ ret = lyd_value_store(trg_ctx, &term->value, type, val_can, strlen(val_can), 1, NULL, LY_VALUE_CANON, NULL,
LYD_HINT_DATA, term->schema, NULL);
LY_CHECK_GOTO(ret, error);
}
@@ -1787,10 +1870,11 @@ error:
* @return LY_ERR value.
*/
static LY_ERR
-lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_ctx, const struct lyd_node_inner *parent,
- uint32_t options, struct lyd_node **dup_parent, struct lyd_node_inner **local_parent)
+lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent,
+ uint32_t options, struct lyd_node **dup_parent, struct lyd_node **local_parent)
{
- const struct lyd_node_inner *orig_parent, *iter;
+ const struct lyd_node *orig_parent;
+ struct lyd_node *iter = NULL;
ly_bool repeat = 1, ext_parent = 0;
*dup_parent = NULL;
@@ -1799,38 +1883,38 @@ lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_c
if (node->flags & LYD_EXT) {
ext_parent = 1;
}
- for (orig_parent = node->parent; repeat && orig_parent; orig_parent = orig_parent->parent) {
+ for (orig_parent = lyd_parent(node); repeat && orig_parent; orig_parent = lyd_parent(orig_parent)) {
if (ext_parent) {
/* use the standard context */
trg_ctx = LYD_CTX(orig_parent);
}
- if (parent && (parent->schema == orig_parent->schema)) {
+ if (parent && (LYD_CTX(parent) == LYD_CTX(orig_parent)) && (parent->schema == orig_parent->schema)) {
/* stop creating parents, connect what we have into the provided parent */
iter = parent;
repeat = 0;
+ } else if (parent && (LYD_CTX(parent) != LYD_CTX(orig_parent)) &&
+ lyd_compare_schema_equal(parent->schema, orig_parent->schema) &&
+ lyd_compare_schema_parents_equal(parent, orig_parent)) {
+ iter = parent;
+ repeat = 0;
} else {
iter = NULL;
- LY_CHECK_RET(lyd_dup_r((struct lyd_node *)orig_parent, trg_ctx, NULL, 0, (struct lyd_node **)&iter, options,
- (struct lyd_node **)&iter));
- }
- if (!*local_parent) {
- *local_parent = (struct lyd_node_inner *)iter;
- }
- if (iter->child) {
- /* 1) list - add after keys
- * 2) provided parent with some children */
- iter->child->prev->next = *dup_parent;
+ LY_CHECK_RET(lyd_dup_r(orig_parent, trg_ctx, NULL, 0, &iter, options, &iter));
+
+ /* insert into the previous duplicated parent */
if (*dup_parent) {
- (*dup_parent)->prev = iter->child->prev;
- iter->child->prev = *dup_parent;
+ lyd_insert_node(iter, NULL, *dup_parent, 0);
}
- } else {
- ((struct lyd_node_inner *)iter)->child = *dup_parent;
+
+ /* update the last duplicated parent */
+ *dup_parent = iter;
}
- if (*dup_parent) {
- (*dup_parent)->parent = (struct lyd_node_inner *)iter;
+
+ /* set the first parent */
+ if (!*local_parent) {
+ *local_parent = iter;
}
- *dup_parent = (struct lyd_node *)iter;
+
if (orig_parent->flags & LYD_EXT) {
ext_parent = 1;
}
@@ -1838,23 +1922,27 @@ lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_c
if (repeat && parent) {
/* given parent and created parents chain actually do not interconnect */
- LOGERR(trg_ctx, LY_EINVAL,
- "Invalid argument parent (%s()) - does not interconnect with the created node's parents chain.", __func__);
+ LOGERR(trg_ctx, LY_EINVAL, "None of the duplicated node \"%s\" schema parents match the provided parent \"%s\".",
+ LYD_NAME(node), LYD_NAME(parent));
return LY_EINVAL;
}
+ if (*dup_parent && parent) {
+ /* last insert into a prevously-existing parent */
+ lyd_insert_node(parent, NULL, *dup_parent, 0);
+ }
return LY_SUCCESS;
}
static LY_ERR
-lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node_inner *parent, uint32_t options,
+lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, uint32_t options,
ly_bool nosiblings, struct lyd_node **dup)
{
LY_ERR rc;
const struct lyd_node *orig; /* original node to be duplicated */
struct lyd_node *first = NULL; /* the first duplicated node, this is returned */
struct lyd_node *top = NULL; /* the most higher created node */
- struct lyd_node_inner *local_parent = NULL; /* the direct parent node for the duplicated node(s) */
+ struct lyd_node *local_parent = NULL; /* the direct parent node for the duplicated node(s) */
assert(node && trg_ctx);
@@ -1869,7 +1957,7 @@ lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_no
if (lysc_is_key(orig->schema)) {
if (local_parent) {
/* the key must already exist in the parent */
- rc = lyd_find_sibling_schema(local_parent->child, orig->schema, first ? NULL : &first);
+ rc = lyd_find_sibling_schema(lyd_child(local_parent), orig->schema, first ? NULL : &first);
LY_CHECK_ERR_GOTO(rc, LOGINT(trg_ctx), error);
} else {
assert(!(options & LYD_DUP_WITH_PARENTS));
@@ -1879,8 +1967,7 @@ lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_no
}
} else {
/* if there is no local parent, it will be inserted into first */
- rc = lyd_dup_r(orig, trg_ctx, local_parent ? &local_parent->node : NULL, 0, &first, options,
- first ? NULL : &first);
+ rc = lyd_dup_r(orig, trg_ctx, local_parent, 0, &first, options, first ? NULL : &first);
LY_CHECK_GOTO(rc, error);
}
if (nosiblings) {
@@ -1923,7 +2010,7 @@ lyd_dup_ctx_check(const struct lyd_node *node, const struct lyd_node_inner *pare
for (iter = node; iter && !(iter->flags & LYD_EXT); iter = lyd_parent(iter)) {}
if (!iter || !lyd_parent(iter) || (LYD_CTX(lyd_parent(iter)) != LYD_CTX(parent))) {
- LOGERR(NULL, LY_EINVAL, "Different contexts used in node duplication.");
+ LOGERR(LYD_CTX(node), LY_EINVAL, "Different contexts used in node duplication.");
return LY_EINVAL;
}
}
@@ -1937,7 +2024,7 @@ lyd_dup_single(const struct lyd_node *node, struct lyd_node_inner *parent, uint3
LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
LY_CHECK_RET(lyd_dup_ctx_check(node, parent));
- return lyd_dup(node, LYD_CTX(node), parent, options, 1, dup);
+ return lyd_dup(node, LYD_CTX(node), (struct lyd_node *)parent, options, 1, dup);
}
LIBYANG_API_DEF LY_ERR
@@ -1946,7 +2033,7 @@ lyd_dup_single_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx,
{
LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL);
- return lyd_dup(node, trg_ctx, parent, options, 1, dup);
+ return lyd_dup(node, trg_ctx, (struct lyd_node *)parent, options, 1, dup);
}
LIBYANG_API_DEF LY_ERR
@@ -1955,7 +2042,7 @@ lyd_dup_siblings(const struct lyd_node *node, struct lyd_node_inner *parent, uin
LY_CHECK_ARG_RET(NULL, node, LY_EINVAL);
LY_CHECK_RET(lyd_dup_ctx_check(node, parent));
- return lyd_dup(node, LYD_CTX(node), parent, options, 0, dup);
+ return lyd_dup(node, LYD_CTX(node), (struct lyd_node *)parent, options, 0, dup);
}
LIBYANG_API_DEF LY_ERR
@@ -1964,7 +2051,7 @@ lyd_dup_siblings_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ct
{
LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL);
- return lyd_dup(node, trg_ctx, parent, options, 0, dup);
+ return lyd_dup(node, trg_ctx, (struct lyd_node *)parent, options, 0, dup);
}
LIBYANG_API_DEF LY_ERR
@@ -2019,13 +2106,13 @@ finish:
*/
static LY_ERR
lyd_merge_sibling_r(struct lyd_node **first_trg, struct lyd_node *parent_trg, const struct lyd_node **sibling_src_p,
- lyd_merge_cb merge_cb, void *cb_data, uint16_t options, struct lyd_dup_inst **dup_inst)
+ lyd_merge_cb merge_cb, void *cb_data, uint16_t options, struct ly_ht **dup_inst)
{
const struct lyd_node *child_src, *tmp, *sibling_src;
struct lyd_node *match_trg, *dup_src, *elem;
struct lyd_node_opaq *opaq_trg, *opaq_src;
struct lysc_type *type;
- struct lyd_dup_inst *child_dup_inst = NULL;
+ struct ly_ht *child_dup_inst = NULL;
LY_ERR ret;
ly_bool first_inst = 0;
@@ -2110,7 +2197,7 @@ lyd_merge_sibling_r(struct lyd_node **first_trg, struct lyd_node *parent_trg, co
/* node not found, merge it */
if (options & LYD_MERGE_DESTRUCT) {
dup_src = (struct lyd_node *)sibling_src;
- lyd_unlink_tree(dup_src);
+ lyd_unlink(dup_src);
/* spend it */
*sibling_src_p = NULL;
} else {
@@ -2147,7 +2234,7 @@ lyd_merge(struct lyd_node **target, const struct lyd_node *source, const struct
lyd_merge_cb merge_cb, void *cb_data, uint16_t options, ly_bool nosiblings)
{
const struct lyd_node *sibling_src, *tmp;
- struct lyd_dup_inst *dup_inst = NULL;
+ struct ly_ht *dup_inst = NULL;
ly_bool first;
LY_ERR ret = LY_SUCCESS;
@@ -2357,9 +2444,9 @@ lyd_path(const struct lyd_node *node, LYD_PATH_TYPE pathtype, char *buffer, size
for (iter = node, i = 1; i < depth; iter = lyd_parent(iter), ++i) {}
iter_print:
/* get the module */
- mod = iter->schema ? iter->schema->module : lyd_owner_module(iter);
+ mod = lyd_node_module(iter);
parent = lyd_parent(iter);
- prev_mod = (parent && parent->schema) ? parent->schema->module : lyd_owner_module(parent);
+ prev_mod = lyd_node_module(parent);
if (prev_mod == mod) {
mod = NULL;
}
@@ -2429,15 +2516,17 @@ lyd_path_set(const struct ly_set *dnodes, LYD_PATH_TYPE pathtype)
for (depth = 1; depth <= dnodes->count; ++depth) {
/* current node */
iter = dnodes->dnodes[depth - 1];
- mod = iter->schema ? iter->schema->module : lyd_owner_module(iter);
+ mod = lyd_node_module(iter);
/* parent */
parent = (depth > 1) ? dnodes->dnodes[depth - 2] : NULL;
- assert(!parent || !iter->schema || !parent->schema || (lysc_data_parent(iter->schema) == parent->schema) ||
- (!lysc_data_parent(iter->schema) && (LYD_CTX(iter) != LYD_CTX(parent))));
+ assert(!parent || !iter->schema || !parent->schema || (parent->schema->nodetype & LYD_NODE_ANY) ||
+ (lysc_data_parent(iter->schema) == parent->schema) ||
+ (!lysc_data_parent(iter->schema) && (LYD_CTX(iter) != LYD_CTX(parent))) ||
+ (parent->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)));
/* get module to print, if any */
- prev_mod = (parent && parent->schema) ? parent->schema->module : lyd_owner_module(parent);
+ prev_mod = lyd_node_module(parent);
if (prev_mod == mod) {
mod = NULL;
}
@@ -2567,14 +2656,14 @@ lyd_find_sibling_first(const struct lyd_node *siblings, const struct lyd_node *t
siblings = lyd_first_sibling(siblings);
parent = siblings->parent;
- if (parent && parent->schema && parent->children_ht) {
+ if (target->schema && parent && parent->schema && parent->children_ht) {
assert(target->hash);
if (lysc_is_dup_inst_list(target->schema)) {
/* we must search the instances from beginning to find the first matching one */
found = 0;
LYD_LIST_FOR_INST(siblings, target->schema, iter) {
- if (!lyd_compare_single(target, iter, 0)) {
+ if (!lyd_compare_single(target, iter, LYD_COMPARE_FULL_RECURSION)) {
found = 1;
break;
}
@@ -2594,10 +2683,16 @@ lyd_find_sibling_first(const struct lyd_node *siblings, const struct lyd_node *t
}
}
} else {
- /* no children hash table */
+ /* no children hash table or cannot be used */
for ( ; siblings; siblings = siblings->next) {
- if (!lyd_compare_single(siblings, target, LYD_COMPARE_OPAQ)) {
- break;
+ if (lysc_is_dup_inst_list(target->schema)) {
+ if (!lyd_compare_single(siblings, target, LYD_COMPARE_FULL_RECURSION)) {
+ break;
+ }
+ } else {
+ if (!lyd_compare_single(siblings, target, 0)) {
+ break;
+ }
}
}
}
@@ -2661,7 +2756,7 @@ lyd_find_sibling_val(const struct lyd_node *siblings, const struct lysc_node *sc
/* create a data node and find the instance */
if (schema->nodetype == LYS_LEAFLIST) {
/* target used attributes: schema, hash, value */
- rc = lyd_create_term(schema, key_or_value, val_len, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &target);
+ rc = lyd_create_term(schema, key_or_value, val_len, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &target);
LY_CHECK_RET(rc);
} else {
/* target used attributes: schema, hash, child (all keys) */
@@ -2684,6 +2779,7 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_
{
struct lyd_node **match_p, *first, *iter;
struct lyd_node_inner *parent;
+ uint32_t comp_opts;
LY_CHECK_ARG_RET(NULL, target, set, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(siblings ? LYD_CTX(siblings) : NULL, LYD_CTX(target), LY_EINVAL);
@@ -2695,6 +2791,9 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_
return LY_ENOTFOUND;
}
+ /* set options */
+ comp_opts = (lysc_is_dup_inst_list(target->schema) ? LYD_COMPARE_FULL_RECURSION : 0);
+
/* get first sibling */
siblings = lyd_first_sibling(siblings);
@@ -2719,7 +2818,7 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_
}
while (iter) {
/* add all found nodes into the set */
- if ((iter != first) && !lyd_compare_single(iter, target, 0) && ly_set_add(*set, iter, 1, NULL)) {
+ if ((iter != first) && !lyd_compare_single(iter, target, comp_opts) && ly_set_add(*set, iter, 1, NULL)) {
goto error;
}
@@ -2734,7 +2833,7 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_
} else {
/* no children hash table */
LY_LIST_FOR(siblings, siblings) {
- if (!lyd_compare_single(target, siblings, LYD_COMPARE_OPAQ)) {
+ if (!lyd_compare_single(target, siblings, comp_opts)) {
ly_set_add(*set, (void *)siblings, 1, NULL);
}
}
@@ -2756,8 +2855,22 @@ lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struc
{
LY_CHECK_ARG_RET(NULL, name, LY_EINVAL);
+ if (first && first->schema) {
+ first = first->prev;
+ if (first->schema) {
+ /* no opaque nodes */
+ first = NULL;
+ } else {
+ /* opaque nodes are at the end, find quickly the first */
+ while (!first->prev->schema) {
+ first = first->prev;
+ }
+ }
+ }
+
for ( ; first; first = first->next) {
- if (!first->schema && !strcmp(LYD_NAME(first), name)) {
+ assert(!first->schema);
+ if (!strcmp(LYD_NAME(first), name)) {
break;
}
}
@@ -2769,58 +2882,19 @@ lyd_find_sibling_opaq_next(const struct lyd_node *first, const char *name, struc
}
LIBYANG_API_DEF LY_ERR
-lyd_find_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath, LY_VALUE_FORMAT format,
- void *prefix_data, const struct lyxp_var *vars, struct ly_set **set)
+lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, struct ly_set **set)
{
- LY_ERR ret = LY_SUCCESS;
- struct lyxp_set xp_set = {0};
- struct lyxp_expr *exp = NULL;
- uint32_t i;
-
- LY_CHECK_ARG_RET(NULL, tree, xpath, format, set, LY_EINVAL);
-
- *set = NULL;
-
- /* parse expression */
- ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(tree), xpath, 0, 1, &exp);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* evaluate expression */
- ret = lyxp_eval(LYD_CTX(tree), exp, NULL, format, prefix_data, ctx_node, ctx_node, tree, vars, &xp_set,
- LYXP_IGNORE_WHEN);
- LY_CHECK_GOTO(ret, cleanup);
-
- if (xp_set.type != LYXP_SET_NODE_SET) {
- LOGERR(LYD_CTX(tree), LY_EINVAL, "XPath \"%s\" result is not a node set.", xpath);
- ret = LY_EINVAL;
- goto cleanup;
- }
-
- /* allocate return set */
- ret = ly_set_new(set);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
- /* transform into ly_set, allocate memory for all the elements once (even though not all items must be
- * elements but most likely will be) */
- (*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
- LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(LYD_CTX(tree)); ret = LY_EMEM, cleanup);
- (*set)->size = xp_set.used;
+ return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set);
+}
- for (i = 0; i < xp_set.used; ++i) {
- if (xp_set.val.nodes[i].type == LYXP_NODE_ELEM) {
- ret = ly_set_add(*set, xp_set.val.nodes[i].node, 1, NULL);
- LY_CHECK_GOTO(ret, cleanup);
- }
- }
+LIBYANG_API_DEF LY_ERR
+lyd_find_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, struct ly_set **set)
+{
+ LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
-cleanup:
- lyxp_set_free_content(&xp_set);
- lyxp_expr_free((struct ly_ctx *)LYD_CTX(tree), exp);
- if (ret) {
- ly_set_free(*set, NULL);
- *set = NULL;
- }
- return ret;
+ return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set);
}
LIBYANG_API_DEF LY_ERR
@@ -2833,63 +2907,263 @@ lyd_find_xpath3(const struct lyd_node *ctx_node, const struct lyd_node *tree, co
}
LIBYANG_API_DEF LY_ERR
-lyd_find_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, struct ly_set **set)
+lyd_find_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const char *xpath, LY_VALUE_FORMAT format,
+ void *prefix_data, const struct lyxp_var *vars, struct ly_set **set)
{
- LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, tree, xpath, set, LY_EINVAL);
- return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, vars, set);
+ *set = NULL;
+
+ return lyd_eval_xpath4(ctx_node, tree, NULL, xpath, format, prefix_data, vars, NULL, set, NULL, NULL, NULL);
}
LIBYANG_API_DEF LY_ERR
-lyd_find_xpath(const struct lyd_node *ctx_node, const char *xpath, struct ly_set **set)
+lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result)
{
- LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
+ return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result);
+}
- return lyd_find_xpath4(ctx_node, ctx_node, xpath, LY_VALUE_JSON, NULL, NULL, set);
+LIBYANG_API_DEF LY_ERR
+lyd_eval_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, ly_bool *result)
+{
+ return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, vars, result);
}
LIBYANG_API_DEF LY_ERR
lyd_eval_xpath3(const struct lyd_node *ctx_node, const struct lys_module *cur_mod, const char *xpath,
LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, ly_bool *result)
{
+ return lyd_eval_xpath4(ctx_node, ctx_node, cur_mod, xpath, format, prefix_data, vars, NULL, NULL, NULL, NULL, result);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_eval_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, const struct lys_module *cur_mod,
+ const char *xpath, LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, LY_XPATH_TYPE *ret_type,
+ struct ly_set **node_set, char **string, long double *number, ly_bool *boolean)
+{
LY_ERR ret = LY_SUCCESS;
struct lyxp_set xp_set = {0};
struct lyxp_expr *exp = NULL;
+ uint32_t i;
- LY_CHECK_ARG_RET(NULL, ctx_node, xpath, result, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, tree, xpath, ((ret_type && node_set && string && number && boolean) ||
+ (node_set && !string && !number && !boolean) || (!node_set && string && !number && !boolean) ||
+ (!node_set && !string && number && !boolean) || (!node_set && !string && !number && boolean)), LY_EINVAL);
- /* compile expression */
- ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(ctx_node), xpath, 0, 1, &exp);
+ /* parse expression */
+ ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(tree), xpath, 0, 1, &exp);
LY_CHECK_GOTO(ret, cleanup);
/* evaluate expression */
- ret = lyxp_eval(LYD_CTX(ctx_node), exp, cur_mod, format, prefix_data, ctx_node, ctx_node, ctx_node, vars, &xp_set,
+ ret = lyxp_eval(LYD_CTX(tree), exp, cur_mod, format, prefix_data, ctx_node, ctx_node, tree, vars, &xp_set,
LYXP_IGNORE_WHEN);
LY_CHECK_GOTO(ret, cleanup);
- /* transform into boolean */
- ret = lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
- LY_CHECK_GOTO(ret, cleanup);
+ /* return expected result type without or with casting */
+ if (node_set) {
+ /* node set */
+ if (xp_set.type == LYXP_SET_NODE_SET) {
+ /* transform into a set */
+ LY_CHECK_GOTO(ret = ly_set_new(node_set), cleanup);
+ (*node_set)->objs = malloc(xp_set.used * sizeof *(*node_set)->objs);
+ LY_CHECK_ERR_GOTO(!(*node_set)->objs, LOGMEM(LYD_CTX(tree)); ret = LY_EMEM, cleanup);
+ (*node_set)->size = xp_set.used;
+ for (i = 0; i < xp_set.used; ++i) {
+ if (xp_set.val.nodes[i].type == LYXP_NODE_ELEM) {
+ ret = ly_set_add(*node_set, xp_set.val.nodes[i].node, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+ if (ret_type) {
+ *ret_type = LY_XPATH_NODE_SET;
+ }
+ } else if (!string && !number && !boolean) {
+ LOGERR(LYD_CTX(tree), LY_EINVAL, "XPath \"%s\" result is not a node set.", xpath);
+ ret = LY_EINVAL;
+ goto cleanup;
+ }
+ }
- /* set result */
- *result = xp_set.val.bln;
+ if (string) {
+ if ((xp_set.type != LYXP_SET_STRING) && !node_set) {
+ /* cast into string */
+ LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_STRING), cleanup);
+ }
+ if (xp_set.type == LYXP_SET_STRING) {
+ /* string */
+ *string = xp_set.val.str;
+ xp_set.val.str = NULL;
+ if (ret_type) {
+ *ret_type = LY_XPATH_STRING;
+ }
+ }
+ }
+
+ if (number) {
+ if ((xp_set.type != LYXP_SET_NUMBER) && !node_set) {
+ /* cast into number */
+ LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_NUMBER), cleanup);
+ }
+ if (xp_set.type == LYXP_SET_NUMBER) {
+ /* number */
+ *number = xp_set.val.num;
+ if (ret_type) {
+ *ret_type = LY_XPATH_NUMBER;
+ }
+ }
+ }
+
+ if (boolean) {
+ if ((xp_set.type != LYXP_SET_BOOLEAN) && !node_set) {
+ /* cast into boolean */
+ LY_CHECK_GOTO(ret = lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN), cleanup);
+ }
+ if (xp_set.type == LYXP_SET_BOOLEAN) {
+ /* boolean */
+ *boolean = xp_set.val.bln;
+ if (ret_type) {
+ *ret_type = LY_XPATH_BOOLEAN;
+ }
+ }
+ }
cleanup:
lyxp_set_free_content(&xp_set);
- lyxp_expr_free((struct ly_ctx *)LYD_CTX(ctx_node), exp);
+ lyxp_expr_free((struct ly_ctx *)LYD_CTX(tree), exp);
return ret;
}
-LIBYANG_API_DEF LY_ERR
-lyd_eval_xpath2(const struct lyd_node *ctx_node, const char *xpath, const struct lyxp_var *vars, ly_bool *result)
+/**
+ * @brief Hash table node equal callback.
+ */
+static ly_bool
+lyd_trim_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
{
- return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, vars, result);
+ struct lyd_node *node1, *node2;
+
+ node1 = *(struct lyd_node **)val1_p;
+ node2 = *(struct lyd_node **)val2_p;
+
+ return node1 == node2;
}
LIBYANG_API_DEF LY_ERR
-lyd_eval_xpath(const struct lyd_node *ctx_node, const char *xpath, ly_bool *result)
+lyd_trim_xpath(struct lyd_node **tree, const char *xpath, const struct lyxp_var *vars)
{
- return lyd_eval_xpath3(ctx_node, NULL, xpath, LY_VALUE_JSON, NULL, NULL, result);
+ LY_ERR ret = LY_SUCCESS;
+ struct ly_ctx *ctx;
+ struct lyxp_set xp_set = {0};
+ struct lyxp_expr *exp = NULL;
+ struct lyd_node *node, *parent;
+ struct lyxp_set_hash_node hnode;
+ struct ly_ht *parent_ht = NULL;
+ struct ly_set free_set = {0};
+ uint32_t i, hash;
+ ly_bool is_result;
+
+ LY_CHECK_ARG_RET(NULL, tree, xpath, LY_EINVAL);
+
+ if (!*tree) {
+ /* nothing to do */
+ goto cleanup;
+ }
+
+ *tree = lyd_first_sibling(*tree);
+ ctx = (struct ly_ctx *)LYD_CTX(*tree);
+
+ /* parse expression */
+ ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* evaluate expression */
+ ret = lyxp_eval(ctx, exp, NULL, LY_VALUE_JSON, NULL, *tree, *tree, *tree, vars, &xp_set, LYXP_IGNORE_WHEN);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* create hash table for all the parents of results */
+ parent_ht = lyht_new(32, sizeof node, lyd_trim_equal_cb, NULL, 1);
+ LY_CHECK_GOTO(!parent_ht, cleanup);
+
+ for (i = 0; i < xp_set.used; ++i) {
+ if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) {
+ /* ignore */
+ continue;
+ }
+
+ for (parent = lyd_parent(xp_set.val.nodes[i].node); parent; parent = lyd_parent(parent)) {
+ /* add the parent into parent_ht */
+ ret = lyht_insert(parent_ht, &parent, parent->hash, NULL);
+ if (ret == LY_EEXIST) {
+ /* shared parent, we are done */
+ break;
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+ hnode.type = LYXP_NODE_ELEM;
+ LY_LIST_FOR(*tree, parent) {
+ LYD_TREE_DFS_BEGIN(parent, node) {
+ if (lysc_is_key(node->schema)) {
+ /* ignore */
+ goto next_iter;
+ }
+
+ /* check the results */
+ is_result = 0;
+ if (xp_set.ht) {
+ hnode.node = node;
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
+
+ if (!lyht_find(xp_set.ht, &hnode, hash, NULL)) {
+ is_result = 1;
+ }
+ } else {
+ /* not enough elements for a hash table */
+ for (i = 0; i < xp_set.used; ++i) {
+ if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) {
+ /* ignore */
+ continue;
+ }
+
+ if (xp_set.val.nodes[i].node == node) {
+ is_result = 1;
+ break;
+ }
+ }
+ }
+
+ if (is_result) {
+ /* keep the whole subtree if the node is in the results */
+ LYD_TREE_DFS_continue = 1;
+ } else if (lyht_find(parent_ht, &node, node->hash, NULL)) {
+ /* free the whole subtree if the node is not even among the selected parents */
+ ret = ly_set_add(&free_set, node, 1, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+ LYD_TREE_DFS_continue = 1;
+ } /* else keep the parent node because a subtree is in the results */
+
+next_iter:
+ LYD_TREE_DFS_END(parent, node);
+ }
+ }
+
+ /* free */
+ for (i = 0; i < free_set.count; ++i) {
+ node = free_set.dnodes[i];
+ if (*tree == node) {
+ *tree = (*tree)->next;
+ }
+ lyd_free_tree(node);
+ }
+
+cleanup:
+ lyxp_set_free_content(&xp_set);
+ lyxp_expr_free(ctx, exp);
+ lyht_free(parent_ht, NULL);
+ ly_set_erase(&free_set, NULL);
+ return ret;
}
LIBYANG_API_DEF LY_ERR
@@ -2903,7 +3177,7 @@ lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output,
/* parse the path */
ret = ly_path_parse(LYD_CTX(ctx_node), ctx_node->schema, path, strlen(path), 0, LY_PATH_BEGIN_EITHER,
- LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_SIMPLE, &expr);
+ LY_PATH_PREFIX_FIRST, LY_PATH_PRED_SIMPLE, &expr);
LY_CHECK_GOTO(ret, cleanup);
/* compile the path */
@@ -2912,7 +3186,7 @@ lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output,
LY_CHECK_GOTO(ret, cleanup);
/* evaluate the path */
- ret = ly_path_eval_partial(lypath, ctx_node, NULL, match);
+ ret = ly_path_eval_partial(lypath, ctx_node, NULL, 0, NULL, match);
cleanup:
lyxp_expr_free(LYD_CTX(ctx_node), expr);
@@ -2928,7 +3202,7 @@ lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct
LY_CHECK_ARG_RET(NULL, path, LY_EINVAL);
- ret = ly_path_eval(path, tree, &m);
+ ret = ly_path_eval(path, tree, NULL, &m);
if (ret) {
if (match) {
*match = NULL;
@@ -2941,3 +3215,55 @@ lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct
}
return LY_SUCCESS;
}
+
+LIBYANG_API_DEF struct lyd_node *
+lyd_parent(const struct lyd_node *node)
+{
+ if (!node || !node->parent) {
+ return NULL;
+ }
+
+ return &node->parent->node;
+}
+
+LIBYANG_API_DEF struct lyd_node *
+lyd_child(const struct lyd_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ if (!node->schema) {
+ /* opaq node */
+ return ((const struct lyd_node_opaq *)node)->child;
+ }
+
+ switch (node->schema->nodetype) {
+ case LYS_CONTAINER:
+ case LYS_LIST:
+ case LYS_RPC:
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ return ((const struct lyd_node_inner *)node)->child;
+ default:
+ return NULL;
+ }
+}
+
+LIBYANG_API_DEF const char *
+lyd_get_value(const struct lyd_node *node)
+{
+ if (!node) {
+ return NULL;
+ }
+
+ if (!node->schema) {
+ return ((const struct lyd_node_opaq *)node)->value;
+ } else if (node->schema->nodetype & LYD_NODE_TERM) {
+ const struct lyd_value *value = &((const struct lyd_node_term *)node)->value;
+
+ return value->_canonical ? value->_canonical : lyd_value_get_canonical(LYD_CTX(node), value);
+ }
+
+ return NULL;
+}
diff --git a/src/tree_data.h b/src/tree_data.h
index ff98f60..4d87fba 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -822,7 +822,7 @@ struct lyd_node_inner {
}; /**< common part corresponding to ::lyd_node */
struct lyd_node *child; /**< pointer to the first child node. */
- struct hash_table *children_ht; /**< hash table with all the direct children (except keys for a list, lists without keys) */
+ struct ly_ht *children_ht; /**< hash table with all the direct children (except keys for a list, lists without keys) */
#define LYD_HT_MIN_ITEMS 4 /**< minimal number of children to create ::lyd_node_inner.children_ht hash table. */
};
@@ -1004,15 +1004,7 @@ struct lyd_node_opaq {
* @return Pointer to the parent node of the @p node.
* @return NULL in case of the top-level node or if the @p node is NULL itself.
*/
-static inline struct lyd_node *
-lyd_parent(const struct lyd_node *node)
-{
- if (!node || !node->parent) {
- return NULL;
- }
-
- return &node->parent->node;
-}
+LIBYANG_API_DECL struct lyd_node *lyd_parent(const struct lyd_node *node);
/**
* @brief Get the child pointer of a generic data node.
@@ -1024,29 +1016,7 @@ lyd_parent(const struct lyd_node *node)
* @param[in] node Node to use.
* @return Pointer to the first child node (if any) of the @p node.
*/
-static inline struct lyd_node *
-lyd_child(const struct lyd_node *node)
-{
- if (!node) {
- return NULL;
- }
-
- if (!node->schema) {
- /* opaq node */
- return ((const struct lyd_node_opaq *)node)->child;
- }
-
- switch (node->schema->nodetype) {
- case LYS_CONTAINER:
- case LYS_LIST:
- case LYS_RPC:
- case LYS_ACTION:
- case LYS_NOTIF:
- return ((const struct lyd_node_inner *)node)->child;
- default:
- return NULL;
- }
-}
+LIBYANG_API_DECL struct lyd_node *lyd_child(const struct lyd_node *node);
/**
* @brief Get the child pointer of a generic data node but skip its keys in case it is ::LYS_LIST.
@@ -1072,6 +1042,14 @@ LIBYANG_API_DECL struct lyd_node *lyd_child_no_keys(const struct lyd_node *node)
LIBYANG_API_DECL const struct lys_module *lyd_owner_module(const struct lyd_node *node);
/**
+ * @brief Get the module of a node. Useful mainly for opaque nodes.
+ *
+ * @param[in] node Node to examine.
+ * @return Module of the node.
+ */
+LIBYANG_API_DECL const struct lys_module *lyd_node_module(const struct lyd_node *node);
+
+/**
* @brief Check whether a node value equals to its default one.
*
* @param[in] node Term node to test.
@@ -1133,23 +1111,7 @@ LIBYANG_API_DECL const char *lyd_value_get_canonical(const struct ly_ctx *ctx, c
* @param[in] node Data node to use.
* @return Canonical value.
*/
-static inline const char *
-lyd_get_value(const struct lyd_node *node)
-{
- if (!node) {
- return NULL;
- }
-
- if (!node->schema) {
- return ((const struct lyd_node_opaq *)node)->value;
- } else if (node->schema->nodetype & LYD_NODE_TERM) {
- const struct lyd_value *value = &((const struct lyd_node_term *)node)->value;
-
- return value->_canonical ? value->_canonical : lyd_value_get_canonical(LYD_CTX(node), value);
- }
-
- return NULL;
-}
+LIBYANG_API_DECL const char *lyd_get_value(const struct lyd_node *node);
/**
* @brief Get anydata string value.
@@ -1172,6 +1134,14 @@ LIBYANG_API_DECL LY_ERR lyd_any_copy_value(struct lyd_node *trg, const union lyd
LYD_ANYDATA_VALUETYPE value_type);
/**
+ * @brief Get schema node of a data node. Useful especially for opaque nodes.
+ *
+ * @param[in] node Data node to use.
+ * @return Schema node represented by data @p node, NULL if there is none.
+ */
+LIBYANG_API_DECL const struct lysc_node *lyd_node_schema(const struct lyd_node *node);
+
+/**
* @brief Create a new inner node in the data tree.
*
* To create list, use ::lyd_new_list() or ::lyd_new_list2().
@@ -1287,6 +1257,57 @@ LIBYANG_API_DECL LY_ERR lyd_new_list2(struct lyd_node *parent, const struct lys_
const char *keys, ly_bool output, struct lyd_node **node);
/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] key_values Ordered key string values of the new list instance, all must be set.
+ * @param[in] value_lengths Array of lengths of each @p key_values, may be NULL if @p key_values are 0-terminated strings.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_list3(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const char **key_values, uint32_t *value_lengths, ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] key_values Ordered key binary (LYB) values of the new list instance, all must be set.
+ * @param[in] value_lengths Array of lengths of each @p key_values.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_list3_bin(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const void **key_values, uint32_t *value_lengths, ly_bool output, struct lyd_node **node);
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] key_values Ordered key canonical values of the new list instance, all must be set.
+ * @param[in] value_lengths Array of lengths of each @p key_values, may be NULL if @p key_values are 0-terminated strings.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyd_new_list3_canon(struct lyd_node *parent, const struct lys_module *module, const char *name,
+ const char **key_values, uint32_t *value_lengths, ly_bool output, struct lyd_node **node);
+
+/**
* @brief Create a new term node in the data tree.
*
* To create a top-level term node defined in an extension instance, use ::lyd_new_ext_term().
@@ -1506,6 +1527,7 @@ LIBYANG_API_DECL LY_ERR lyd_new_attr2(struct lyd_node *parent, const char *modul
#define LYD_NEW_PATH_CANON_VALUE 0x10 /**< Interpret the provided leaf/leaf-list @p value as being in the canonical
(or JSON if no defined) ::LY_VALUE_CANON format. If it is not, it may lead
to unexpected behavior. */
+#define LYD_NEW_PATH_WITH_OPAQ 0x20 /**< Consider opaque nodes normally when searching for existing nodes. */
/** @} pathoptions */
@@ -1519,7 +1541,8 @@ LIBYANG_API_DECL LY_ERR lyd_new_attr2(struct lyd_node *parent, const char *modul
* Also, if a leaf-list is being created and both a predicate is defined in @p path
* and @p value is set, the predicate is preferred.
*
- * For key-less lists and non-configuration leaf-lists, positional predicates should be used (indices starting from 1).
+ * For key-less lists, positional predicates must be used (indices starting from 1). For non-configuration leaf-lists
+ * either positional predicate can be used or leaf-list predicate, when an instance is always created at the end.
* If no predicate is used for these nodes, they are always created.
*
* @param[in] parent Data parent to add to/modify, can be NULL. Note that in case a first top-level sibling is used,
@@ -1531,7 +1554,11 @@ LIBYANG_API_DECL LY_ERR lyd_new_attr2(struct lyd_node *parent, const char *modul
* For other node types, it should be NULL.
* @param[in] options Bitmask of options, see @ref pathoptions.
* @param[out] node Optional first created node.
- * @return LY_ERR value.
+ * @return LY_SUCCESS on success.
+ * @return LY_EEXIST if the final node to create exists (unless ::LYD_NEW_PATH_UPDATE is used).
+ * @return LY_EINVAL on invalid arguments including invalid @p path.
+ * @return LY_EVALID on invalid @p value.
+ * @return LY_ERR on other errors.
*/
LIBYANG_API_DECL LY_ERR lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const char *value,
uint32_t options, struct lyd_node **node);
@@ -1554,7 +1581,11 @@ LIBYANG_API_DECL LY_ERR lyd_new_path(struct lyd_node *parent, const struct ly_ct
* @param[in] options Bitmask of options, see @ref pathoptions.
* @param[out] new_parent Optional first parent node created. If only one node was created, equals to @p new_node.
* @param[out] new_node Optional last node created.
- * @return LY_ERR value.
+ * @return LY_SUCCESS on success.
+ * @return LY_EEXIST if the final node to create exists (unless ::LYD_NEW_PATH_UPDATE is used).
+ * @return LY_EINVAL on invalid arguments including invalid @p path.
+ * @return LY_EVALID on invalid @p value.
+ * @return LY_ERR on other errors.
*/
LIBYANG_API_DECL LY_ERR lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path, const void *value,
size_t value_len, LYD_ANYDATA_VALUETYPE value_type, uint32_t options, struct lyd_node **new_parent,
@@ -1576,7 +1607,11 @@ LIBYANG_API_DECL LY_ERR lyd_new_path2(struct lyd_node *parent, const struct ly_c
* @param[in] value Value of the new leaf/leaf-list. For other node types, it should be NULL.
* @param[in] options Bitmask of options, see @ref pathoptions.
* @param[out] node Optional first created node.
- * @return LY_ERR value.
+ * @return LY_SUCCESS on success.
+ * @return LY_EEXIST if the final node to create exists (unless ::LYD_NEW_PATH_UPDATE is used).
+ * @return LY_EINVAL on invalid arguments including invalid @p path.
+ * @return LY_EVALID on invalid @p value.
+ * @return LY_ERR on other errors.
*/
LIBYANG_API_DECL LY_ERR lyd_new_ext_path(struct lyd_node *parent, const struct lysc_ext_instance *ext, const char *path,
const void *value, uint32_t options, struct lyd_node **node);
@@ -1662,7 +1697,7 @@ LIBYANG_API_DECL LY_ERR lyd_change_term(struct lyd_node *term, const char *val_s
* is always cleared.
*
* @param[in] term Term node to change.
- * @param[in] value New value to set in binary format, see @ref howtoDataLYB.
+ * @param[in] value New value to set in binary format (usually a pointer), see @ref howtoDataLYB.
* @param[in] value_len Length of @p value.
* @return LY_SUCCESS if value was changed,
* @return LY_EEXIST if value was the same and only the default flag was cleared,
@@ -1927,6 +1962,8 @@ LIBYANG_API_DECL LY_ERR lyd_compare_meta(const struct lyd_meta *meta1, const str
#define LYD_DUP_WITH_FLAGS 0x08 /**< Also copy any data node flags. That will cause the duplicated data to preserve
its validation/default node state. */
#define LYD_DUP_NO_EXT 0x10 /**< Do not duplicate nodes with the ::LYD_EXT flag (nested extension instance data). */
+#define LYD_DUP_WITH_PRIV 0x20 /**< Also copy data node private pointer. Only the pointer is copied, it still points
+ to the same data. */
/** @} dupoptions */
@@ -2131,7 +2168,7 @@ LIBYANG_API_DECL LY_ERR lyd_merge_module(struct lyd_node **target, const struct
* @param[in] first First data tree.
* @param[in] second Second data tree.
* @param[in] options Bitmask of options flags, see @ref diffoptions.
- * @param[out] diff Generated diff.
+ * @param[out] diff Generated diff, NULL if there are no differences.
* @return LY_SUCCESS on success,
* @return LY_ERR on error.
*/
@@ -2146,7 +2183,7 @@ LIBYANG_API_DECL LY_ERR lyd_diff_tree(const struct lyd_node *first, const struct
* @param[in] first First data tree.
* @param[in] second Second data tree.
* @param[in] options Bitmask of options flags, see @ref diffoptions.
- * @param[out] diff Generated diff.
+ * @param[out] diff Generated diff, NULL if there are no differences.
* @return LY_SUCCESS on success,
* @return LY_ERR on error.
*/
@@ -2302,6 +2339,10 @@ typedef enum {
/**
* @brief Generate path of the given node in the requested format.
*
+ * The path is constructed based on the parent node(s) of this node. When run on a node which is disconnected
+ * from its parent(s), this function might yield unexpected results such as `/example:b` instead of the expected
+ * `/example:a/b`.
+ *
* @param[in] node Data path of this node will be generated.
* @param[in] pathtype Format of the path to generate.
* @param[in,out] buffer Prepared buffer of the @p buflen length to store the generated path.
@@ -2419,6 +2460,8 @@ LIBYANG_API_DECL void lyxp_vars_free(struct lyxp_var *vars);
* `leaf-list[.=...]`, these instances are found using hashes with constant (*O(1)*) complexity
* (unless they are defined in top-level). Other predicates can still follow the aforementioned ones.
*
+ * Opaque nodes are part of the evaluation.
+ *
* @param[in] ctx_node XPath context node.
* @param[in] xpath [XPath](@ref howtoXPath) to select in JSON format. It must evaluate into a node set.
* @param[out] set Set of found data nodes. In case the result is a number, a string, or a boolean,
@@ -2527,11 +2570,61 @@ LIBYANG_API_DECL LY_ERR lyd_eval_xpath3(const struct lyd_node *ctx_node, const s
const char *xpath, LY_VALUE_FORMAT format, void *prefix_data, const struct lyxp_var *vars, ly_bool *result);
/**
+ * @brief XPath result type.
+ */
+typedef enum {
+ LY_XPATH_NODE_SET, /**< XPath node set */
+ LY_XPATH_STRING, /**< XPath string */
+ LY_XPATH_NUMBER, /**< XPath number */
+ LY_XPATH_BOOLEAN /**< XPath boolean */
+} LY_XPATH_TYPE;
+
+/**
+ * @brief Evaluate an XPath on data and return the result or convert it first to an expected result type.
+ *
+ * Either all return type parameters @p node_set, @p string, @p number, and @p boolean with @p ret_type
+ * are provided or exactly one of @p node_set, @p string, @p number, and @p boolean is provided with @p ret_type
+ * being obvious and hence optional.
+ *
+ * @param[in] ctx_node XPath context node, NULL for the root node.
+ * @param[in] tree Data tree to evaluate on.
+ * @param[in] cur_mod Current module of @p xpath, needed for some kinds of @p format.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select.
+ * @param[in] format Format of any prefixes in @p xpath.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[in] vars Optional [sized array](@ref sizedarrays) of XPath variables.
+ * @param[out] ret_type XPath type of the result selecting which of @p node_set, @p string, @p number, and @p boolean to use.
+ * @param[out] node_set XPath node set result.
+ * @param[out] string XPath string result.
+ * @param[out] number XPath number result.
+ * @param[out] boolean XPath boolean result.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lyd_eval_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree,
+ const struct lys_module *cur_mod, const char *xpath, LY_VALUE_FORMAT format, void *prefix_data,
+ const struct lyxp_var *vars, LY_XPATH_TYPE *ret_type, struct ly_set **node_set, char **string,
+ long double *number, ly_bool *boolean);
+
+/**
+ * @brief Evaluate an XPath on data and free all the nodes except the subtrees selected by the expression.
+ *
+ * @param[in,out] tree Data tree to evaluate on and trim.
+ * @param[in] xpath [XPath](@ref howtoXPath) to select in JSON format.
+ * @param[in] vars Optional [sized array](@ref sizedarrays) of XPath variables.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DEF LY_ERR lyd_trim_xpath(struct lyd_node **tree, const char *xpath, const struct lyxp_var *vars);
+
+/**
* @brief Search in given data for a node uniquely identified by a path.
*
* Always works in constant (*O(1)*) complexity. To be exact, it is *O(n)* where *n* is the depth
* of the path used.
*
+ * Opaque nodes are NEVER found/traversed.
+ *
* @param[in] ctx_node Path context node.
* @param[in] path [Path](@ref howtoXPath) to find.
* @param[in] output Whether to search in RPC/action output nodes or in input nodes.
@@ -2557,6 +2650,21 @@ LIBYANG_API_DECL LY_ERR lyd_find_path(const struct lyd_node *ctx_node, const cha
LIBYANG_API_DECL LY_ERR lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct lyd_node **match);
/**
+ * @brief Get current timezone (including DST setting) UTC (GMT) time offset in seconds.
+ *
+ * @return Timezone shift in seconds.
+ */
+LIBYANG_API_DECL int ly_time_tz_offset(void);
+
+/**
+ * @brief Get UTC (GMT) timezone offset in seconds at a specific timestamp (including DST setting).
+ *
+ * @param[in] time Timestamp to get the offset at.
+ * @return Timezone shift in seconds.
+ */
+LIBYANG_API_DECL int ly_time_tz_offset_at(time_t time);
+
+/**
* @brief Convert date-and-time from string to UNIX timestamp and fractions of a second.
*
* @param[in] value Valid string date-and-time value.
diff --git a/src/tree_data_common.c b/src/tree_data_common.c
index f35f8f5..672f720 100644
--- a/src/tree_data_common.c
+++ b/src/tree_data_common.c
@@ -44,32 +44,65 @@
#include "xpath.h"
/**
+ * @brief Callback for checking first instance hash table values equivalence.
+ *
+ * @param[in] val1_p If not @p mod, pointer to the first instance.
+ * @param[in] val2_p If not @p mod, pointer to the found dup inst item.
+ */
+static ly_bool
+lyht_dup_inst_ht_equal_cb(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(cb_data))
+{
+ if (mod) {
+ struct lyd_dup_inst **item1 = val1_p, **item2 = val2_p;
+
+ /* equal on 2 dup inst items */
+ return *item1 == *item2 ? 1 : 0;
+ } else {
+ struct lyd_node **first_inst = val1_p;
+ struct lyd_dup_inst **item = val2_p;
+
+ /* equal on dup inst item and a first instance */
+ return (*item)->set->dnodes[0] == *first_inst ? 1 : 0;
+ }
+}
+
+/**
* @brief Find an entry in duplicate instance cache for an instance. Create it if it does not exist.
*
- * @param[in] first_inst Instance of the cache entry.
- * @param[in,out] dup_inst_cache Duplicate instance cache.
+ * @param[in] first_inst First instance of the cache entry.
+ * @param[in] dup_inst_ht Duplicate instance cache hash table.
* @return Instance cache entry.
*/
static struct lyd_dup_inst *
-lyd_dup_inst_get(const struct lyd_node *first_inst, struct lyd_dup_inst **dup_inst_cache)
+lyd_dup_inst_get(const struct lyd_node *first_inst, struct ly_ht **dup_inst_ht)
{
- struct lyd_dup_inst *item;
- LY_ARRAY_COUNT_TYPE u;
+ struct lyd_dup_inst **item_p, *item;
- LY_ARRAY_FOR(*dup_inst_cache, u) {
- if ((*dup_inst_cache)[u].inst_set->dnodes[0] == first_inst) {
- return &(*dup_inst_cache)[u];
+ if (*dup_inst_ht) {
+ /* find the item of the first instance */
+ if (!lyht_find(*dup_inst_ht, &first_inst, first_inst->hash, (void **)&item_p)) {
+ return *item_p;
}
+ } else {
+ /* create the hash table */
+ *dup_inst_ht = lyht_new(2, sizeof item, lyht_dup_inst_ht_equal_cb, NULL, 1);
+ LY_CHECK_RET(!*dup_inst_ht, NULL);
}
- /* it was not added yet, add it now */
- LY_ARRAY_NEW_RET(LYD_CTX(first_inst), *dup_inst_cache, item, NULL);
+ /* first instance has no dup inst item, create it */
+ item = calloc(1, sizeof *item);
+ LY_CHECK_RET(!item, NULL);
+
+ /* add into the hash table */
+ if (lyht_insert(*dup_inst_ht, &item, first_inst->hash, NULL)) {
+ return NULL;
+ }
return item;
}
LY_ERR
-lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struct lyd_dup_inst **dup_inst_cache)
+lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struct ly_ht **dup_inst_ht)
{
struct lyd_dup_inst *dup_inst;
@@ -80,40 +113,47 @@ lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struc
/* there can be more exact same instances (even if not allowed in invalid data) and we must make sure we do not
* match a single node more times */
- dup_inst = lyd_dup_inst_get(*inst, dup_inst_cache);
+ dup_inst = lyd_dup_inst_get(*inst, dup_inst_ht);
LY_CHECK_ERR_RET(!dup_inst, LOGMEM(LYD_CTX(siblings)), LY_EMEM);
if (!dup_inst->used) {
/* we did not cache these instances yet, do so */
- lyd_find_sibling_dup_inst_set(siblings, *inst, &dup_inst->inst_set);
- assert(dup_inst->inst_set->count && (dup_inst->inst_set->dnodes[0] == *inst));
+ lyd_find_sibling_dup_inst_set(siblings, *inst, &dup_inst->set);
+ assert(dup_inst->set->count && (dup_inst->set->dnodes[0] == *inst));
}
- if (dup_inst->used == dup_inst->inst_set->count) {
+ if (dup_inst->used == dup_inst->set->count) {
if (lysc_is_dup_inst_list((*inst)->schema)) {
/* we have used all the instances */
*inst = NULL;
} /* else just keep using the last (ideally only) instance */
} else {
- assert(dup_inst->used < dup_inst->inst_set->count);
+ assert(dup_inst->used < dup_inst->set->count);
/* use another instance */
- *inst = dup_inst->inst_set->dnodes[dup_inst->used];
+ *inst = dup_inst->set->dnodes[dup_inst->used];
++dup_inst->used;
}
return LY_SUCCESS;
}
-void
-lyd_dup_inst_free(struct lyd_dup_inst *dup_inst)
+/**
+ * @brief Callback for freeing first instance hash table values.
+ */
+static void
+lyht_dup_inst_ht_free_cb(void *val_p)
{
- LY_ARRAY_COUNT_TYPE u;
+ struct lyd_dup_inst **item = val_p;
- LY_ARRAY_FOR(dup_inst, u) {
- ly_set_free(dup_inst[u].inst_set, NULL);
- }
- LY_ARRAY_FREE(dup_inst);
+ ly_set_free((*item)->set, NULL);
+ free(*item);
+}
+
+void
+lyd_dup_inst_free(struct ly_ht *dup_inst_ht)
+{
+ lyht_free(dup_inst_ht, lyht_dup_inst_ht_free_cb);
}
struct lyd_node *
@@ -180,12 +220,12 @@ lyxp_vars_set(struct lyxp_var **vars, const char *name, const char *value)
return LY_EINVAL;
}
- /* If variable is already defined then change its value. */
- if (*vars && !lyxp_vars_find(*vars, name, 0, &item)) {
+ /* if variable is already defined then change its value */
+ if (*vars && !lyxp_vars_find(NULL, *vars, name, 0, &item)) {
var_value = strdup(value);
LY_CHECK_RET(!var_value, LY_EMEM);
- /* Set new value. */
+ /* update value */
free(item->value);
item->value = var_value;
} else {
@@ -193,7 +233,7 @@ lyxp_vars_set(struct lyxp_var **vars, const char *name, const char *value)
var_value = strdup(value);
LY_CHECK_ERR_GOTO(!var_name || !var_value, ret = LY_EMEM, error);
- /* Add new variable. */
+ /* add new variable */
LY_ARRAY_NEW_GOTO(NULL, *vars, item, ret, error);
item->name = var_name;
item->value = var_value;
@@ -260,21 +300,68 @@ lyd_owner_module(const struct lyd_node *node)
return NULL;
}
+ while (!node->schema && node->parent) {
+ node = lyd_parent(node);
+ }
+
if (!node->schema) {
+ /* top-level opaque node */
opaq = (struct lyd_node_opaq *)node;
switch (opaq->format) {
case LY_VALUE_XML:
- return opaq->name.module_ns ? ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns) : NULL;
+ if (opaq->name.module_ns) {
+ return ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns);
+ }
+ break;
case LY_VALUE_JSON:
- return opaq->name.module_name ? ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name) : NULL;
+ if (opaq->name.module_name) {
+ return ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name);
+ }
+ break;
default:
return NULL;
}
+
+ return NULL;
}
return lysc_owner_module(node->schema);
}
+LIBYANG_API_DEF const struct lys_module *
+lyd_node_module(const struct lyd_node *node)
+{
+ const struct lyd_node_opaq *opaq;
+
+ while (node) {
+ /* data node */
+ if (node->schema) {
+ return node->schema->module;
+ }
+
+ /* opaque node */
+ opaq = (struct lyd_node_opaq *)node;
+ switch (opaq->format) {
+ case LY_VALUE_XML:
+ if (opaq->name.module_ns) {
+ return ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns);
+ }
+ break;
+ case LY_VALUE_JSON:
+ if (opaq->name.module_name) {
+ return ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name);
+ }
+ break;
+ default:
+ break;
+ }
+
+ node = lyd_parent(node);
+ }
+
+ return NULL;
+}
+
void
lyd_first_module_sibling(struct lyd_node **node, const struct lys_module *mod)
{
@@ -389,12 +476,12 @@ lyd_data_next_module(struct lyd_node **next, struct lyd_node **first)
LY_ERR
lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct lysc_type *type, const void *value,
- size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ size_t value_len, ly_bool is_utf8, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
const struct lysc_node *ctx_node, ly_bool *incomplete)
{
LY_ERR ret;
struct ly_err_item *err = NULL;
- uint32_t options = (dynamic && *dynamic ? LYPLG_TYPE_STORE_DYNAMIC : 0);
+ uint32_t options = 0;
if (!value) {
value = "";
@@ -403,6 +490,13 @@ lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct ly
*incomplete = 0;
}
+ if (dynamic && *dynamic) {
+ options |= LYPLG_TYPE_STORE_DYNAMIC;
+ }
+ if (is_utf8) {
+ options |= LYPLG_TYPE_STORE_IS_UTF8;
+ }
+
ret = type->plugin->store(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node, val, NULL, &err);
if (dynamic) {
*dynamic = 0;
@@ -440,8 +534,8 @@ lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc_type *
LOGVAL_ERRITEM(ctx, err);
ly_err_free(err);
} else {
- LOGVAL(ctx, LYVE_OTHER, "Resolving value \"%s\" failed.", type->plugin->print(ctx, val, LY_VALUE_CANON,
- NULL, NULL, NULL));
+ LOGVAL(ctx, LYVE_OTHER, "Resolving value \"%s\" failed.",
+ (char *)type->plugin->print(ctx, val, LY_VALUE_CANON, NULL, NULL, NULL));
}
return ret;
}
@@ -450,15 +544,15 @@ lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc_type *
}
LY_ERR
-lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
- LY_VALUE_FORMAT format, void *prefix_data)
+ly_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
+ LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints)
{
LY_ERR rc = LY_SUCCESS;
struct ly_err_item *err = NULL;
struct lyd_value storage;
struct lysc_type *type;
- LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL);
+ LY_CHECK_ARG_RET(ctx, node, LY_EINVAL);
if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
LOGARG(ctx, node);
@@ -466,8 +560,8 @@ lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const
}
type = ((struct lysc_node_leaf *)node)->type;
- rc = type->plugin->store(ctx ? ctx : node->module->ctx, type, value, value_len, 0, format, prefix_data,
- LYD_HINT_SCHEMA, node, &storage, NULL, &err);
+ rc = type->plugin->store(ctx ? ctx : node->module->ctx, type, value, value_len, 0, format, prefix_data, hints, node,
+ &storage, NULL, &err);
if (rc == LY_EINCOMPLETE) {
/* actually success since we do not provide the context tree and call validation with
* LY_TYPE_OPTS_INCOMPLETE_DATA */
@@ -478,14 +572,13 @@ lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const
if (err->path) {
LOG_LOCSET(NULL, NULL, err->path, NULL);
} else {
- /* use at least the schema path */
LOG_LOCSET(node, NULL, NULL, NULL);
}
LOGVAL_ERRITEM(ctx, err);
if (err->path) {
LOG_LOCBACK(0, 0, 1, 0);
} else {
- LOG_LOCBACK(1, 0, 0, 0);
+ LOG_LOCBACK(1, 0, 1, 0);
}
}
ly_err_free(err);
@@ -590,7 +683,7 @@ lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t va
/* store the value */
LOG_LOCSET(node->schema, &node->node, NULL, NULL);
- ret = lyd_value_store(ctx, &val, type, value, value_len, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, node->schema, NULL);
+ ret = lyd_value_store(ctx, &val, type, value, value_len, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, node->schema, NULL);
LOG_LOCBACK(1, 1, 0, 0);
LY_CHECK_RET(ret);
@@ -704,24 +797,15 @@ lyd_parse_opaq_list_error(const struct lyd_node *node, const struct lysc_node *s
assert(!node->schema);
/* get all keys into a set */
- while ((key = lys_getnext(key, snode, NULL, 0)) && (snode->flags & LYS_KEY)) {
- LY_CHECK_GOTO(ret = ly_set_add(&key_set, (void *)snode, 1, NULL), cleanup);
+ while ((key = lys_getnext(key, snode, NULL, 0)) && (key->flags & LYS_KEY)) {
+ LY_CHECK_GOTO(ret = ly_set_add(&key_set, (void *)key, 1, NULL), cleanup);
}
LY_LIST_FOR(lyd_child(node), child) {
- if (child->schema) {
- LOGERR(LYD_CTX(node), LY_EINVAL, "Unexpected node %s \"%s\".", lys_nodetype2str(child->schema->nodetype),
- LYD_NAME(child));
- ret = LY_EINVAL;
- goto cleanup;
- }
-
- opaq_k = (struct lyd_node_opaq *)child;
-
/* find the key schema node */
for (i = 0; i < key_set.count; ++i) {
key = key_set.snodes[i];
- if (!strcmp(key->name, opaq_k->name.name)) {
+ if (!strcmp(key->name, LYD_NAME(child))) {
break;
}
}
@@ -733,9 +817,15 @@ lyd_parse_opaq_list_error(const struct lyd_node *node, const struct lysc_node *s
/* key found */
ly_set_rm_index(&key_set, i, NULL);
+ if (child->schema) {
+ /* valid key */
+ continue;
+ }
+
/* check value */
- ret = lys_value_validate(LYD_CTX(node), key, opaq_k->value, strlen(opaq_k->value), opaq_k->format,
- opaq_k->val_prefix_data);
+ opaq_k = (struct lyd_node_opaq *)child;
+ ret = ly_value_validate(LYD_CTX(node), key, opaq_k->value, strlen(opaq_k->value), opaq_k->format,
+ opaq_k->val_prefix_data, opaq_k->hints);
LY_CHECK_GOTO(ret, cleanup);
}
@@ -754,93 +844,124 @@ cleanup:
LIBYANG_API_DEF LY_ERR
lyd_parse_opaq_error(const struct lyd_node *node)
{
+ LY_ERR rc = LY_SUCCESS;
const struct ly_ctx *ctx;
const struct lyd_node_opaq *opaq;
const struct lyd_node *parent;
const struct lys_module *mod;
- const struct lysc_node *snode;
+ const struct lysc_node *sparent, *snode;
+ uint32_t loc_node = 0, loc_path = 0;
- LY_CHECK_ARG_RET(LYD_CTX(node), node, !node->schema, !lyd_parent(node) || lyd_parent(node)->schema, LY_EINVAL);
+ LY_CHECK_ARG_RET(LYD_CTX(node), node, !node->schema, LY_EINVAL);
ctx = LYD_CTX(node);
opaq = (struct lyd_node_opaq *)node;
parent = lyd_parent(node);
+ sparent = lyd_node_schema(parent);
+
+ if (parent) {
+ LOG_LOCSET(NULL, parent, NULL, NULL);
+ ++loc_node;
+ } else {
+ LOG_LOCSET(NULL, NULL, "/", NULL);
+ ++loc_path;
+ }
if (!opaq->name.module_ns) {
LOGVAL(ctx, LYVE_REFERENCE, "Unknown module of node \"%s\".", opaq->name.name);
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
/* module */
switch (opaq->format) {
case LY_VALUE_XML:
- if (!parent || strcmp(opaq->name.module_ns, parent->schema->module->ns)) {
+ if (!sparent || strcmp(opaq->name.module_ns, sparent->module->ns)) {
mod = ly_ctx_get_module_implemented_ns(ctx, opaq->name.module_ns);
if (!mod) {
LOGVAL(ctx, LYVE_REFERENCE, "No (implemented) module with namespace \"%s\" of node \"%s\" in the context.",
opaq->name.module_ns, opaq->name.name);
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
} else {
/* inherit */
- mod = parent->schema->module;
+ mod = sparent->module;
}
break;
case LY_VALUE_JSON:
case LY_VALUE_LYB:
- if (!parent || strcmp(opaq->name.module_name, parent->schema->module->name)) {
+ if (!sparent || strcmp(opaq->name.module_name, sparent->module->name)) {
mod = ly_ctx_get_module_implemented(ctx, opaq->name.module_name);
if (!mod) {
LOGVAL(ctx, LYVE_REFERENCE, "No (implemented) module named \"%s\" of node \"%s\" in the context.",
opaq->name.module_name, opaq->name.name);
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
} else {
/* inherit */
- mod = parent->schema->module;
+ mod = sparent->module;
}
break;
default:
LOGERR(ctx, LY_EINVAL, "Unsupported value format.");
- return LY_EINVAL;
+ rc = LY_EINVAL;
+ goto cleanup;
}
/* schema */
- snode = lys_find_child(parent ? parent->schema : NULL, mod, opaq->name.name, 0, 0, 0);
- if (!snode && parent && parent->schema && (parent->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
+ snode = lys_find_child(sparent, mod, opaq->name.name, 0, 0, 0);
+ if (!snode && sparent && (sparent->nodetype & (LYS_RPC | LYS_ACTION))) {
/* maybe output node */
- snode = lys_find_child(parent->schema, mod, opaq->name.name, 0, 0, LYS_GETNEXT_OUTPUT);
+ snode = lys_find_child(sparent, mod, opaq->name.name, 0, 0, LYS_GETNEXT_OUTPUT);
}
if (!snode) {
- if (parent) {
+ if (sparent) {
LOGVAL(ctx, LYVE_REFERENCE, "Node \"%s\" not found as a child of \"%s\" node.", opaq->name.name,
- LYD_NAME(parent));
+ sparent->name);
} else {
LOGVAL(ctx, LYVE_REFERENCE, "Node \"%s\" not found in the \"%s\" module.", opaq->name.name, mod->name);
}
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
+ /* schema node exists */
+ LOG_LOCBACK(0, loc_node, loc_path, 0);
+ loc_node = 0;
+ loc_path = 0;
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ ++loc_node;
+
if (snode->nodetype & LYD_NODE_TERM) {
/* leaf / leaf-list */
- LY_CHECK_RET(lys_value_validate(ctx, snode, opaq->value, strlen(opaq->value), opaq->format, opaq->val_prefix_data));
+ rc = ly_value_validate(ctx, snode, opaq->value, strlen(opaq->value), opaq->format, opaq->val_prefix_data, opaq->hints);
+ LY_CHECK_GOTO(rc, cleanup);
} else if (snode->nodetype == LYS_LIST) {
/* list */
- LY_CHECK_RET(lyd_parse_opaq_list_error(node, snode));
+ rc = lyd_parse_opaq_list_error(node, snode);
+ LY_CHECK_GOTO(rc, cleanup);
} else if (snode->nodetype & LYD_NODE_INNER) {
/* inner node */
if (opaq->value) {
LOGVAL(ctx, LYVE_DATA, "Invalid value \"%s\" for %s \"%s\".", opaq->value,
lys_nodetype2str(snode->nodetype), snode->name);
- return LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
}
} else {
LOGERR(ctx, LY_EINVAL, "Unexpected opaque schema node %s \"%s\".", lys_nodetype2str(snode->nodetype), snode->name);
- return LY_EINVAL;
+ rc = LY_EINVAL;
+ goto cleanup;
}
LOGERR(ctx, LY_EINVAL, "Unexpected valid opaque node %s \"%s\".", lys_nodetype2str(snode->nodetype), snode->name);
- return LY_EINVAL;
+ rc = LY_EINVAL;
+
+cleanup:
+ LOG_LOCBACK(0, loc_node, loc_path, 0);
+ return rc;
}
LIBYANG_API_DEF const char *
@@ -970,7 +1091,7 @@ lyd_any_copy_value(struct lyd_node *trg, const union lyd_any_value *value, LYD_A
return LY_SUCCESS;
}
-const struct lysc_node *
+LIBYANG_API_DEF const struct lysc_node *
lyd_node_schema(const struct lyd_node *node)
{
const struct lysc_node *schema = NULL;
@@ -983,27 +1104,30 @@ lyd_node_schema(const struct lyd_node *node)
return node->schema;
}
+ /* find the first schema node in the parents */
+ for (iter = lyd_parent(node); iter && !iter->schema; iter = lyd_parent(iter)) {}
+ if (iter) {
+ prev_iter = iter;
+ schema = prev_iter->schema;
+ }
+
/* get schema node of an opaque node */
do {
/* get next data node */
for (iter = node; lyd_parent(iter) != prev_iter; iter = lyd_parent(iter)) {}
- /* get equivalent schema node */
- if (iter->schema) {
- schema = iter->schema;
- } else {
- /* get module */
- mod = lyd_owner_module(iter);
- if (!mod && !schema) {
- /* top-level opaque node has unknown module */
- break;
- }
-
- /* get schema node */
- schema = lys_find_child(schema, mod ? mod : schema->module, LYD_NAME(iter), 0, 0, 0);
+ /* get module */
+ mod = lyd_node_module(iter);
+ if (!mod) {
+ /* unknown module, no schema node */
+ schema = NULL;
+ break;
}
- /* remember to move to the descendant */
+ /* get schema node */
+ schema = lys_find_child(schema, mod, LYD_NAME(iter), 0, 0, 0);
+
+ /* move to the descendant */
prev_iter = iter;
} while (schema && (iter != node));
@@ -1068,9 +1192,7 @@ lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node
{
struct lyd_node **match_p;
struct lyd_node_inner *parent;
- const struct lysc_node *cur_schema;
uint32_t hash;
- lyht_value_equal_cb ht_cb;
assert(schema);
if (!siblings) {
@@ -1084,23 +1206,17 @@ lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node
parent = siblings->parent;
if (parent && parent->schema && parent->children_ht) {
/* calculate our hash */
- hash = dict_hash_multi(0, schema->module->name, strlen(schema->module->name));
- hash = dict_hash_multi(hash, schema->name, strlen(schema->name));
- hash = dict_hash_multi(hash, NULL, 0);
-
- /* use special hash table function */
- ht_cb = lyht_set_cb(parent->children_ht, lyd_hash_table_schema_val_equal);
+ hash = lyht_hash_multi(0, schema->module->name, strlen(schema->module->name));
+ hash = lyht_hash_multi(hash, schema->name, strlen(schema->name));
+ hash = lyht_hash_multi(hash, NULL, 0);
- /* find by hash */
- if (!lyht_find(parent->children_ht, &schema, hash, (void **)&match_p)) {
+ /* find by hash but use special hash table function (and stay thread-safe) */
+ if (!lyht_find_with_val_cb(parent->children_ht, &schema, hash, lyd_hash_table_schema_val_equal, (void **)&match_p)) {
siblings = *match_p;
} else {
/* not found */
siblings = NULL;
}
-
- /* set the original hash table compare function back */
- lyht_set_cb(parent->children_ht, ht_cb);
} else {
/* find first sibling */
if (siblings->parent) {
@@ -1111,25 +1227,22 @@ lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node
}
}
- /* search manually without hashes */
- for ( ; siblings; siblings = siblings->next) {
- cur_schema = lyd_node_schema(siblings);
- if (!cur_schema) {
- /* some unknown opaque node */
- continue;
- }
-
+ /* search manually without hashes and ignore opaque nodes (cannot be found by hashes) */
+ for ( ; siblings && siblings->schema; siblings = siblings->next) {
/* schema match is enough */
- if (cur_schema->module->ctx == schema->module->ctx) {
- if (cur_schema == schema) {
+ if (LYD_CTX(siblings) == schema->module->ctx) {
+ if (siblings->schema == schema) {
break;
}
} else {
- if (!strcmp(cur_schema->name, schema->name) && !strcmp(cur_schema->module->name, schema->module->name)) {
+ if (!strcmp(LYD_NAME(siblings), schema->name) && !strcmp(siblings->schema->module->name, schema->module->name)) {
break;
}
}
}
+ if (siblings && !siblings->schema) {
+ siblings = NULL;
+ }
}
if (!siblings) {
@@ -1468,6 +1581,52 @@ ly_format2str(LY_VALUE_FORMAT format)
return NULL;
}
+LIBYANG_API_DEF int
+ly_time_tz_offset(void)
+{
+ return ly_time_tz_offset_at(time(NULL));
+}
+
+LIBYANG_API_DEF int
+ly_time_tz_offset_at(time_t time)
+{
+ struct tm tm_local, tm_utc;
+ int result = 0;
+
+ /* init timezone */
+ tzset();
+
+ /* get local and UTC time */
+ localtime_r(&time, &tm_local);
+ gmtime_r(&time, &tm_utc);
+
+ /* account for year/month/day change by adding/subtracting from the hours, the change cannot be more than 1 day */
+ if (tm_local.tm_year < tm_utc.tm_year) {
+ tm_utc.tm_hour += 24;
+ } else if (tm_local.tm_year > tm_utc.tm_year) {
+ tm_local.tm_hour += 24;
+ } else if (tm_local.tm_mon < tm_utc.tm_mon) {
+ tm_utc.tm_hour += 24;
+ } else if (tm_local.tm_mon > tm_utc.tm_mon) {
+ tm_local.tm_hour += 24;
+ } else if (tm_local.tm_mday < tm_utc.tm_mday) {
+ tm_utc.tm_hour += 24;
+ } else if (tm_local.tm_mday > tm_utc.tm_mday) {
+ tm_local.tm_hour += 24;
+ }
+
+ /* hours shift in seconds */
+ result += (tm_local.tm_hour - tm_utc.tm_hour) * 3600;
+
+ /* minutes shift in seconds */
+ result += (tm_local.tm_min - tm_utc.tm_min) * 60;
+
+ /* seconds shift */
+ result += tm_local.tm_sec - tm_utc.tm_sec;
+
+ return result;
+}
+
LIBYANG_API_DEF LY_ERR
ly_time_str2time(const char *value, time_t *time, char **fractions_s)
{
@@ -1486,6 +1645,28 @@ ly_time_str2time(const char *value, time_t *time, char **fractions_s)
tm.tm_min = atoi(&value[14]);
tm.tm_sec = atoi(&value[17]);
+ /* explicit checks for some gross errors */
+ if (tm.tm_mon > 11) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time month \"%d\".", tm.tm_mon);
+ return LY_EINVAL;
+ }
+ if ((tm.tm_mday < 1) || (tm.tm_mday > 31)) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time day of month \"%d\".", tm.tm_mday);
+ return LY_EINVAL;
+ }
+ if (tm.tm_hour > 23) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time hours \"%d\".", tm.tm_hour);
+ return LY_EINVAL;
+ }
+ if (tm.tm_min > 59) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time minutes \"%d\".", tm.tm_min);
+ return LY_EINVAL;
+ }
+ if (tm.tm_sec > 60) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time seconds \"%d\".", tm.tm_sec);
+ return LY_EINVAL;
+ }
+
t = timegm(&tm);
i = 19;
@@ -1506,12 +1687,24 @@ ly_time_str2time(const char *value, time_t *time, char **fractions_s)
shift = 0;
} else {
shift = strtol(&value[i], NULL, 10);
+ if (shift > 23) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time timezone hour \"%" PRIi64 "\".", shift);
+ return LY_EINVAL;
+ }
shift = shift * 60 * 60; /* convert from hours to seconds */
- shift_m = strtol(&value[i + 4], NULL, 10) * 60; /* includes conversion from minutes to seconds */
+
+ shift_m = strtol(&value[i + 4], NULL, 10);
+ if (shift_m > 59) {
+ LOGERR(NULL, LY_EINVAL, "Invalid date-and-time timezone minutes \"%" PRIi64 "\".", shift_m);
+ return LY_EINVAL;
+ }
+ shift_m *= 60; /* convert from minutes to seconds */
+
/* correct sign */
if (shift < 0) {
shift_m *= -1;
}
+
/* connect hours and minutes of the shift */
shift = shift + shift_m;
}
@@ -1535,41 +1728,24 @@ LIBYANG_API_DEF LY_ERR
ly_time_time2str(time_t time, const char *fractions_s, char **str)
{
struct tm tm;
- char zoneshift[8];
- int32_t zonediff_h, zonediff_m;
+ char zoneshift[12];
+ int zonediff_s, zonediff_h, zonediff_m;
LY_CHECK_ARG_RET(NULL, str, LY_EINVAL);
- /* initialize the local timezone */
+ /* init timezone */
tzset();
-#ifdef HAVE_TM_GMTOFF
/* convert */
if (!localtime_r(&time, &tm)) {
return LY_ESYS;
}
- /* get timezone offset */
- if (tm.tm_gmtoff == 0) {
- /* time is Zulu (UTC) */
- zonediff_h = 0;
- zonediff_m = 0;
- } else {
- /* timezone offset */
- zonediff_h = tm.tm_gmtoff / 60 / 60;
- zonediff_m = tm.tm_gmtoff / 60 % 60;
- }
+ /* get timezone offset (do not use tm_gmtoff to avoid portability problems) */
+ zonediff_s = ly_time_tz_offset_at(time);
+ zonediff_h = zonediff_s / 60 / 60;
+ zonediff_m = zonediff_s / 60 % 60;
sprintf(zoneshift, "%+03d:%02d", zonediff_h, zonediff_m);
-#else
- /* convert */
- if (!gmtime_r(&time, &tm)) {
- return LY_ESYS;
- }
-
- (void)zonediff_h;
- (void)zonediff_m;
- sprintf(zoneshift, "-00:00");
-#endif
/* print */
if (asprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d%s%s%s",
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index bf17a91..0281ae5 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -166,7 +166,7 @@ lyd_free_subtree(struct lyd_node *node, ly_bool top)
ly_free_prefix_data(opaq->format, opaq->val_prefix_data);
} else if (node->schema->nodetype & LYD_NODE_INNER) {
/* remove children hash table in case of inner data node */
- lyht_free(((struct lyd_node_inner *)node)->children_ht);
+ lyht_free(((struct lyd_node_inner *)node)->children_ht, NULL);
((struct lyd_node_inner *)node)->children_ht = NULL;
/* free the children */
@@ -189,7 +189,7 @@ lyd_free_subtree(struct lyd_node *node, ly_bool top)
/* unlink only the nodes from the first level, nodes in subtree are freed all, so no unlink is needed */
if (top) {
- lyd_unlink_tree(node);
+ lyd_unlink(node);
}
free(node);
@@ -202,6 +202,11 @@ lyd_free_tree(struct lyd_node *node)
return;
}
+ if (lysc_is_key(node->schema) && node->parent) {
+ LOGERR(LYD_CTX(node), LY_EINVAL, "Cannot free a list key \"%s\", free the list instance instead.", LYD_NAME(node));
+ return;
+ }
+
lyd_free_subtree(node, 1);
}
@@ -223,6 +228,11 @@ lyd_free_(struct lyd_node *node, ly_bool top)
}
LY_LIST_FOR_SAFE(node, next, iter) {
+ if (lysc_is_key(iter->schema) && iter->parent) {
+ LOGERR(LYD_CTX(iter), LY_EINVAL, "Cannot free a list key \"%s\", free the list instance instead.", LYD_NAME(iter));
+ return;
+ }
+
/* in case of the top-level nodes (node->parent is NULL), no unlinking needed */
lyd_free_subtree(iter, iter->parent ? 1 : 0);
}
diff --git a/src/tree_data_hash.c b/src/tree_data_hash.c
index 52f99a8..7235c27 100644
--- a/src/tree_data_hash.c
+++ b/src/tree_data_hash.c
@@ -39,15 +39,15 @@ lyd_hash(struct lyd_node *node)
}
/* hash always starts with the module and schema name */
- node->hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
- node->hash = dict_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
+ node->hash = lyht_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+ node->hash = lyht_hash_multi(node->hash, node->schema->name, strlen(node->schema->name));
if (node->schema->nodetype == LYS_LIST) {
if (node->schema->flags & LYS_KEYLESS) {
/* key-less list simply calls hash function again with empty key,
* just so that it differs from the first-instance hash
*/
- node->hash = dict_hash_multi(node->hash, NULL, 0);
+ node->hash = lyht_hash_multi(node->hash, NULL, 0);
} else {
struct lyd_node_inner *list = (struct lyd_node_inner *)node;
@@ -56,7 +56,7 @@ lyd_hash(struct lyd_node *node)
struct lyd_node_term *key = (struct lyd_node_term *)iter;
hash_key = key->value.realtype->plugin->print(NULL, &key->value, LY_VALUE_LYB, NULL, &dyn, &key_len);
- node->hash = dict_hash_multi(node->hash, hash_key, key_len);
+ node->hash = lyht_hash_multi(node->hash, hash_key, key_len);
if (dyn) {
free((void *)hash_key);
}
@@ -67,14 +67,14 @@ lyd_hash(struct lyd_node *node)
struct lyd_node_term *llist = (struct lyd_node_term *)node;
hash_key = llist->value.realtype->plugin->print(NULL, &llist->value, LY_VALUE_LYB, NULL, &dyn, &key_len);
- node->hash = dict_hash_multi(node->hash, hash_key, key_len);
+ node->hash = lyht_hash_multi(node->hash, hash_key, key_len);
if (dyn) {
free((void *)hash_key);
}
}
/* finish the hash */
- node->hash = dict_hash_multi(node->hash, NULL, 0);
+ node->hash = lyht_hash_multi(node->hash, NULL, 0);
return LY_SUCCESS;
}
@@ -121,14 +121,14 @@ lyd_hash_table_val_equal(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(c
* @return LY_ERR value.
*/
static LY_ERR
-lyd_insert_hash_add(struct hash_table *ht, struct lyd_node *node, ly_bool empty_ht)
+lyd_insert_hash_add(struct ly_ht *ht, struct lyd_node *node, ly_bool empty_ht)
{
uint32_t hash;
assert(ht && node && node->schema);
/* add node itself */
- if (lyht_insert(ht, &node, node->hash, NULL)) {
+ if (lyht_insert_no_check(ht, &node, node->hash, NULL)) {
LOGINT_RET(LYD_CTX(node));
}
@@ -136,9 +136,9 @@ lyd_insert_hash_add(struct hash_table *ht, struct lyd_node *node, ly_bool empty_
if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) &&
(!node->prev->next || (node->prev->schema != node->schema))) {
/* get the simple hash */
- hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
- hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+ hash = lyht_hash_multi(hash, node->schema->name, strlen(node->schema->name));
+ hash = lyht_hash_multi(hash, NULL, 0);
/* remove any previous stored instance, only if we did not start with an empty HT */
if (!empty_ht && node->next && (node->next->schema == node->schema)) {
@@ -182,8 +182,7 @@ lyd_insert_hash(struct lyd_node *node)
}
}
if (u >= LYD_HT_MIN_ITEMS) {
- /* create hash table, insert all the children */
- node->parent->children_ht = lyht_new(1, sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1);
+ node->parent->children_ht = lyht_new(lyht_get_fixed_size(u), sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1);
LY_LIST_FOR(node->parent->child, iter) {
if (iter->schema) {
LY_CHECK_RET(lyd_insert_hash_add(node->parent->children_ht, iter, 1));
@@ -216,9 +215,9 @@ lyd_unlink_hash(struct lyd_node *node)
/* first instance of the (leaf-)list, needs to be removed from HT */
if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (!node->prev->next || (node->prev->schema != node->schema))) {
/* get the simple hash */
- hash = dict_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
- hash = dict_hash_multi(hash, node->schema->name, strlen(node->schema->name));
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, node->schema->module->name, strlen(node->schema->module->name));
+ hash = lyht_hash_multi(hash, node->schema->name, strlen(node->schema->name));
+ hash = lyht_hash_multi(hash, NULL, 0);
/* remove the instance */
if (lyht_remove(node->parent->children_ht, &node, hash)) {
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index fd0792d..cfb93f1 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief internal functions for YANG schema trees.
*
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -34,31 +34,30 @@ struct lysc_module;
#define LY_LYB_SUFFIX_LEN 4
/**
- * @brief Internal structure for remembering "used" instances of lists with duplicate instances allowed.
+ * @brief Internal item structure for remembering "used" instances of duplicate node instances.
*/
struct lyd_dup_inst {
- struct ly_set *inst_set;
+ struct ly_set *set;
uint32_t used;
};
/**
- * @brief Update a found inst using a duplicate instance cache. Needs to be called for every "used"
+ * @brief Update a found inst using a duplicate instance cache hash table. Needs to be called for every "used"
* (that should not be considered next time) instance.
*
* @param[in,out] inst Found instance, is updated so that the same instance is not returned twice.
* @param[in] siblings Siblings where @p inst was found.
- * @param[in,out] dup_inst_cache Duplicate instance cache.
+ * @param[in] dup_inst_ht Duplicate instance cache hash table.
* @return LY_ERR value.
*/
-LY_ERR lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings,
- struct lyd_dup_inst **dup_inst_cache);
+LY_ERR lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struct ly_ht **dup_inst_ht);
/**
* @brief Free duplicate instance cache.
*
- * @param[in] dup_inst Duplicate instance cache to free.
+ * @param[in] dup_inst Duplicate instance cache hash table to free.
*/
-void lyd_dup_inst_free(struct lyd_dup_inst *dup_inst);
+void lyd_dup_inst_free(struct ly_ht *dup_inst_ht);
/**
* @brief Just like ::lys_getnext() but iterates over all data instances of the schema nodes.
@@ -118,14 +117,6 @@ const struct lys_module *lyd_mod_next_module(struct lyd_node *tree, const struct
const struct lys_module *lyd_data_next_module(struct lyd_node **next, struct lyd_node **first);
/**
- * @brief Get schema node of a data node. Useful especially for opaque nodes.
- *
- * @param[in] node Data node to use.
- * @return Schema node represented by data @p node, NULL if there is none.
- */
-const struct lysc_node *lyd_node_schema(const struct lyd_node *node);
-
-/**
* @brief Set dflt flag for a NP container if applicable, recursively for parents.
*
* @param[in] node Node whose criteria for the dflt flag has changed.
@@ -233,6 +224,7 @@ const char *ly_format2str(LY_VALUE_FORMAT format);
* @param[in] schema Schema node of the new data node.
* @param[in] value String value to be parsed.
* @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in] is_utf8 Whether @p value is a valid UTF-8 string, if applicable.
* @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
* @param[in] format Input format of @p value.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
@@ -243,8 +235,9 @@ const char *ly_format2str(LY_VALUE_FORMAT format);
* @return LY_EINCOMPLETE in case data tree is needed to finish the validation.
* @return LY_ERR value if an error occurred.
*/
-LY_ERR lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool *dynamic,
- LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, ly_bool *incomplete, struct lyd_node **node);
+LY_ERR lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool is_utf8,
+ ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, ly_bool *incomplete,
+ struct lyd_node **node);
/**
* @brief Create a term (leaf/leaf-list) node from a parsed value by duplicating it.
@@ -280,11 +273,13 @@ LY_ERR lyd_create_inner(const struct lysc_node *schema, struct lyd_node **node);
*
* @param[in] schema Schema node of the new data node.
* @param[in] predicates Compiled key list predicates.
+ * @param[in] vars Array of defined variables to use in predicates, may be NULL.
* @param[out] node Created node.
* @return LY_SUCCESS on success.
* @return LY_ERR value if an error occurred.
*/
-LY_ERR lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates, struct lyd_node **node);
+LY_ERR lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates,
+ const struct lyxp_var *vars, struct lyd_node **node);
/**
* @brief Create a list with all its keys (cannot be used for key-less list).
@@ -403,6 +398,13 @@ void lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node);
void lyd_insert_node(struct lyd_node *parent, struct lyd_node **first_sibling, struct lyd_node *node, ly_bool last);
/**
+ * @brief Unlink the specified data subtree.
+ *
+ * @param[in] node Data tree node to be unlinked (together with all the children).
+ */
+void lyd_unlink(struct lyd_node *node);
+
+/**
* @brief Insert a metadata (last) into a parent
*
* @param[in] parent Parent of the metadata.
@@ -421,6 +423,7 @@ void lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool cle
* @param[in] name_len Length of @p name, must be set correctly.
* @param[in] value String value to be parsed.
* @param[in] value_len Length of @p value, must be set correctly.
+ * @param[in] is_utf8 Whether @p value is a valid UTF-8 string, if applicable.
* @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
* @param[in] format Input format of @p value.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
@@ -433,7 +436,7 @@ void lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool cle
* @return LY_ERR value if an error occurred.
*/
LY_ERR lyd_create_meta(struct lyd_node *parent, struct lyd_meta **meta, const struct lys_module *mod, const char *name,
- size_t name_len, const char *value, size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format,
+ size_t name_len, const char *value, size_t value_len, ly_bool is_utf8, ly_bool *dynamic, LY_VALUE_FORMAT format,
void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, ly_bool clear_dflt, ly_bool *incomplete);
/**
@@ -477,6 +480,7 @@ LY_ERR lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const st
* @param[in] type Type of the value.
* @param[in] value Value to be parsed, must not be NULL.
* @param[in] value_len Length of the give @p value, must be set correctly.
+ * @param[in] is_utf8 Whether @p value is a valid UTF-8 string, if applicable.
* @param[in,out] dynamic Flag if @p value is dynamically allocated, is adjusted when @p value is consumed.
* @param[in] format Input format of @p value.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
@@ -487,7 +491,7 @@ LY_ERR lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const st
* @return LY_ERR value on error.
*/
LY_ERR lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct lysc_type *type, const void *value,
- size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+ size_t value_len, ly_bool is_utf8, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
const struct lysc_node *ctx_node, ly_bool *incomplete);
/**
@@ -505,8 +509,7 @@ LY_ERR lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc
const struct lyd_node *ctx_node, const struct lyd_node *tree);
/**
- * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value coming
- * from a schema.
+ * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
*
* This function check just the type's restriction, if you want to check also the data tree context (e.g. in case of
* require-instance restriction), use ::lyd_value_validate().
@@ -517,11 +520,12 @@ LY_ERR lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc
* @param[in] value_len Length of the given @p value (mandatory).
* @param[in] format Value prefix format.
* @param[in] prefix_data Format-specific data for resolving any prefixes (see ::ly_resolve_prefix).
+ * @param[in] hints Value encoding hints.
* @return LY_SUCCESS on success
* @return LY_ERR value if an error occurred.
*/
-LY_ERR lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
- LY_VALUE_FORMAT format, void *prefix_data);
+LY_ERR ly_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
+ LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints);
/**
* @defgroup datahash Data nodes hash manipulation
diff --git a/src/tree_data_new.c b/src/tree_data_new.c
index 752b181..5d9f429 100644
--- a/src/tree_data_new.c
+++ b/src/tree_data_new.c
@@ -51,7 +51,7 @@
#include "xpath.h"
LY_ERR
-lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool *dynamic,
+lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_len, ly_bool is_utf8, ly_bool *dynamic,
LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, ly_bool *incomplete, struct lyd_node **node)
{
LY_ERR ret;
@@ -68,7 +68,7 @@ lyd_create_term(const struct lysc_node *schema, const char *value, size_t value_
LOG_LOCSET(schema, NULL, NULL, NULL);
ret = lyd_value_store(schema->module->ctx, &term->value, ((struct lysc_node_leaf *)term->schema)->type, value,
- value_len, dynamic, format, prefix_data, hints, schema, incomplete);
+ value_len, is_utf8, dynamic, format, prefix_data, hints, schema, incomplete);
LOG_LOCBACK(1, 0, 0, 0);
LY_CHECK_ERR_RET(ret, free(term), ret);
lyd_hash(&term->node);
@@ -134,10 +134,14 @@ lyd_create_inner(const struct lysc_node *schema, struct lyd_node **node)
}
LY_ERR
-lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates, struct lyd_node **node)
+lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *predicates, const struct lyxp_var *vars,
+ struct lyd_node **node)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *list = NULL, *key;
+ const struct lyd_value *value;
+ struct lyd_value val = {0};
+ struct lyxp_var *var;
LY_ARRAY_COUNT_TYPE u;
assert((schema->nodetype == LYS_LIST) && !(schema->flags & LYS_KEYLESS));
@@ -149,7 +153,31 @@ lyd_create_list(const struct lysc_node *schema, const struct ly_path_predicate *
/* create and insert all the keys */
LY_ARRAY_FOR(predicates, u) {
- LY_CHECK_GOTO(ret = lyd_create_term2(predicates[u].key, &predicates[u].value, &key), cleanup);
+ if (predicates[u].type == LY_PATH_PREDTYPE_LIST_VAR) {
+ /* find the var */
+ if ((ret = lyxp_vars_find(schema->module->ctx, vars, predicates[u].variable, 0, &var))) {
+ goto cleanup;
+ }
+
+ /* store the value */
+ LOG_LOCSET(predicates[u].key, NULL, NULL, NULL);
+ ret = lyd_value_store(schema->module->ctx, &val, ((struct lysc_node_leaf *)predicates[u].key)->type,
+ var->value, strlen(var->value), 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, predicates[u].key, NULL);
+ LOG_LOCBACK(1, 0, 0, 0);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ value = &val;
+ } else {
+ assert(predicates[u].type == LY_PATH_PREDTYPE_LIST);
+ value = &predicates[u].value;
+ }
+
+ ret = lyd_create_term2(predicates[u].key, value, &key);
+ if (val.realtype) {
+ val.realtype->plugin->free(schema->module->ctx, &val);
+ memset(&val, 0, sizeof val);
+ }
+ LY_CHECK_GOTO(ret, cleanup);
lyd_insert_node(list, NULL, key, 0);
}
@@ -172,7 +200,6 @@ lyd_create_list2(const struct lysc_node *schema, const char *keys, size_t keys_l
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *expr = NULL;
uint32_t exp_idx = 0;
- enum ly_path_pred_type pred_type = 0;
struct ly_path_predicate *predicates = NULL;
LOG_LOCSET(schema, NULL, NULL, NULL);
@@ -183,100 +210,111 @@ lyd_create_list2(const struct lysc_node *schema, const char *keys, size_t keys_l
/* compile them */
LY_CHECK_GOTO(ret = ly_path_compile_predicate(schema->module->ctx, NULL, NULL, schema, expr, &exp_idx, LY_VALUE_JSON,
- NULL, &predicates, &pred_type), cleanup);
+ NULL, &predicates), cleanup);
/* create the list node */
- LY_CHECK_GOTO(ret = lyd_create_list(schema, predicates, node), cleanup);
+ LY_CHECK_GOTO(ret = lyd_create_list(schema, predicates, NULL, node), cleanup);
cleanup:
LOG_LOCBACK(1, 0, 0, 0);
lyxp_expr_free(schema->module->ctx, expr);
- ly_path_predicates_free(schema->module->ctx, pred_type, predicates);
+ ly_path_predicates_free(schema->module->ctx, predicates);
return ret;
}
/**
- * @brief Convert an anydata value into a datatree.
+ * @brief Learn actual any value type in case it is currently ::LYD_ANYDATA_STRING.
+ *
+ * @param[in] value Any value.
+ * @param[out] value_type Detected value type.
+ */
+static void
+lyd_create_any_string_valtype(const void *value, LYD_ANYDATA_VALUETYPE *value_type)
+{
+ /* detect format */
+ if (!value) {
+ /* interpret it as an empty data tree */
+ *value_type = LYD_ANYDATA_DATATREE;
+ } else if (((char *)value)[0] == '<') {
+ *value_type = LYD_ANYDATA_XML;
+ } else if (((char *)value)[0] == '{') {
+ *value_type = LYD_ANYDATA_JSON;
+ } else if (!strncmp(value, "lyb", 3)) {
+ *value_type = LYD_ANYDATA_LYB;
+ } else {
+ /* really just some string */
+ *value_type = LYD_ANYDATA_STRING;
+ }
+}
+
+/**
+ * @brief Convert an any value into a datatree.
*
* @param[in] ctx libyang context.
- * @param[in] value Anydata value.
- * @param[in] value_type Anydata @p value type.
+ * @param[in] value_in Any value as an input.
+ * @param[in] value_type Any @p value type.
+ * @param[in] log Whether parsing errors should be logged.
* @param[out] tree Parsed data tree.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_create_anydata_datatree(const struct ly_ctx *ctx, const void *value, LYD_ANYDATA_VALUETYPE value_type,
+lyd_create_any_datatree(const struct ly_ctx *ctx, struct ly_in *value_in, LYD_ANYDATA_VALUETYPE value_type, ly_bool log,
struct lyd_node **tree)
{
- LY_ERR r;
- struct ly_in *in = NULL;
+ LY_ERR rc = LY_SUCCESS;
struct lyd_ctx *lydctx = NULL;
- uint32_t parse_opts, int_opts;
+ uint32_t parse_opts, int_opts, log_opts = 0;
*tree = NULL;
- if (!value) {
- /* empty data tree no matter the value type */
- return LY_SUCCESS;
- }
-
- if (value_type == LYD_ANYDATA_STRING) {
- /* detect format */
- if (((char *)value)[0] == '<') {
- value_type = LYD_ANYDATA_XML;
- } else if (((char *)value)[0] == '{') {
- value_type = LYD_ANYDATA_JSON;
- } else if (!strncmp(value, "lyb", 3)) {
- value_type = LYD_ANYDATA_LYB;
- } else {
- LOGERR(ctx, LY_EINVAL, "Invalid string value of an anydata node.");
- return LY_EINVAL;
- }
- }
-
- /* create input */
- LY_CHECK_RET(ly_in_new_memory(value, &in));
-
/* set options */
parse_opts = LYD_PARSE_ONLY | LYD_PARSE_OPAQ;
int_opts = LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS;
+ if (!log) {
+ /* no logging */
+ ly_temp_log_options(&log_opts);
+ }
+
switch (value_type) {
case LYD_ANYDATA_DATATREE:
case LYD_ANYDATA_STRING:
/* unreachable */
- ly_in_free(in, 0);
LOGINT_RET(ctx);
case LYD_ANYDATA_XML:
- r = lyd_parse_xml(ctx, NULL, NULL, tree, in, parse_opts, 0, int_opts, NULL, NULL, &lydctx);
+ rc = lyd_parse_xml(ctx, NULL, NULL, tree, value_in, parse_opts, 0, int_opts, NULL, NULL, &lydctx);
break;
case LYD_ANYDATA_JSON:
- r = lyd_parse_json(ctx, NULL, NULL, tree, in, parse_opts, 0, int_opts, NULL, NULL, &lydctx);
+ rc = lyd_parse_json(ctx, NULL, NULL, tree, value_in, parse_opts, 0, int_opts, NULL, NULL, &lydctx);
break;
case LYD_ANYDATA_LYB:
- r = lyd_parse_lyb(ctx, NULL, NULL, tree, in, parse_opts | LYD_PARSE_STRICT, 0, int_opts, NULL, NULL, &lydctx);
+ rc = lyd_parse_lyb(ctx, NULL, NULL, tree, value_in, parse_opts | LYD_PARSE_STRICT, 0, int_opts, NULL, NULL, &lydctx);
break;
}
if (lydctx) {
lydctx->free(lydctx);
}
- ly_in_free(in, 0);
- if (r) {
- LOGERR(ctx, LY_EINVAL, "Failed to parse anydata content into a data tree.");
- return LY_EINVAL;
+ if (!log) {
+ /* restore logging */
+ ly_temp_log_options(NULL);
}
- return LY_SUCCESS;
+ if (rc && *tree) {
+ lyd_free_siblings(*tree);
+ *tree = NULL;
+ }
+ return rc;
}
LY_ERR
lyd_create_any(const struct lysc_node *schema, const void *value, LYD_ANYDATA_VALUETYPE value_type, ly_bool use_value,
struct lyd_node **node)
{
- LY_ERR ret;
+ LY_ERR rc = LY_SUCCESS, r;
struct lyd_node *tree;
struct lyd_node_any *any = NULL;
union lyd_any_value any_val;
+ struct ly_in *in = NULL;
assert(schema->nodetype & LYD_NODE_ANY);
@@ -287,15 +325,70 @@ lyd_create_any(const struct lysc_node *schema, const void *value, LYD_ANYDATA_VA
any->prev = &any->node;
any->flags = LYD_NEW;
- if ((schema->nodetype == LYS_ANYDATA) && (value_type != LYD_ANYDATA_DATATREE)) {
- /* only a data tree can be stored */
- LY_CHECK_GOTO(ret = lyd_create_anydata_datatree(schema->module->ctx, value, value_type, &tree), error);
- if (use_value) {
- free((void *)value);
+ if (schema->nodetype == LYS_ANYDATA) {
+ /* anydata */
+ if (value_type == LYD_ANYDATA_STRING) {
+ /* detect value type */
+ lyd_create_any_string_valtype(value, &value_type);
+ }
+
+ if (value_type != LYD_ANYDATA_DATATREE) {
+ /* create input */
+ assert(value);
+ LY_CHECK_GOTO(rc = ly_in_new_memory(value, &in), cleanup);
+
+ /* parse as a data tree */
+ if ((r = lyd_create_any_datatree(schema->module->ctx, in, value_type, 1, &tree))) {
+ LOGERR(schema->module->ctx, rc, "Failed to parse any content into a data tree.");
+ rc = r;
+ goto cleanup;
+ }
+
+ /* use the parsed data tree */
+ if (use_value) {
+ free((void *)value);
+ }
+ use_value = 1;
+ value = tree;
+ value_type = LYD_ANYDATA_DATATREE;
+ }
+ } else {
+ /* anyxml */
+ switch (value_type) {
+ case LYD_ANYDATA_DATATREE:
+ /* fine, just use the value */
+ break;
+ case LYD_ANYDATA_STRING:
+ /* detect value type */
+ lyd_create_any_string_valtype(value, &value_type);
+ if ((value_type == LYD_ANYDATA_DATATREE) || (value_type == LYD_ANYDATA_STRING)) {
+ break;
+ }
+ /* fallthrough */
+ case LYD_ANYDATA_XML:
+ case LYD_ANYDATA_JSON:
+ case LYD_ANYDATA_LYB:
+ if (!value) {
+ /* nothing to parse */
+ break;
+ }
+
+ /* create input */
+ LY_CHECK_GOTO(rc = ly_in_new_memory(value, &in), cleanup);
+
+ /* try to parse as a data tree */
+ r = lyd_create_any_datatree(schema->module->ctx, in, value_type, 0, &tree);
+ if (!r) {
+ /* use the parsed data tree */
+ if (use_value) {
+ free((void *)value);
+ }
+ use_value = 1;
+ value = tree;
+ value_type = LYD_ANYDATA_DATATREE;
+ }
+ break;
}
- use_value = 1;
- value = tree;
- value_type = LYD_ANYDATA_DATATREE;
}
if (use_value) {
@@ -306,7 +399,7 @@ lyd_create_any(const struct lysc_node *schema, const void *value, LYD_ANYDATA_VA
case LYD_ANYDATA_STRING:
case LYD_ANYDATA_XML:
case LYD_ANYDATA_JSON:
- LY_CHECK_GOTO(ret = lydict_insert_zc(schema->module->ctx, (void *)value, &any->value.str), error);
+ LY_CHECK_GOTO(rc = lydict_insert_zc(schema->module->ctx, (void *)value, &any->value.str), cleanup);
break;
case LYD_ANYDATA_LYB:
any->value.mem = (void *)value;
@@ -315,16 +408,18 @@ lyd_create_any(const struct lysc_node *schema, const void *value, LYD_ANYDATA_VA
any->value_type = value_type;
} else {
any_val.str = value;
- LY_CHECK_GOTO(ret = lyd_any_copy_value(&any->node, &any_val, value_type), error);
+ LY_CHECK_GOTO(rc = lyd_any_copy_value(&any->node, &any_val, value_type), cleanup);
}
lyd_hash(&any->node);
- *node = &any->node;
- return LY_SUCCESS;
-
-error:
- free(any);
- return ret;
+cleanup:
+ if (rc) {
+ lyd_free_tree(&any->node);
+ } else {
+ *node = &any->node;
+ }
+ ly_in_free(in, 0);
+ return rc;
}
LY_ERR
@@ -444,33 +539,25 @@ lyd_new_ext_inner(const struct lysc_ext_instance *ext, const char *name, struct
}
/**
- * @brief Create a new list node in the data tree.
+ * @brief Create a new lits node instance without its keys.
*
+ * @param[in] ctx Context to use for logging.
* @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
* @param[in] module Module of the node being created. If NULL, @p parent module will be used.
* @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
- * @param[in] format Format of key values.
* @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
* taken into consideration. Otherwise, the output's data node is going to be created.
- * @param[out] node Optional created node.
- * @param[in] ap Ordered key values of the new list instance, all must be set. For ::LY_VALUE_LYB, every value must
- * be followed by the value length.
+ * @param[out] node Created node.
* @return LY_ERR value.
*/
static LY_ERR
-_lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const char *name, LY_VALUE_FORMAT format,
- ly_bool output, struct lyd_node **node, va_list ap)
+_lyd_new_list_node(const struct ly_ctx *ctx, const struct lyd_node *parent, const struct lys_module *module,
+ const char *name, ly_bool output, struct lyd_node **node)
{
- struct lyd_node *ret = NULL, *key;
- const struct lysc_node *schema, *key_s;
+ struct lyd_node *ret = NULL;
+ const struct lysc_node *schema;
struct lysc_ext_instance *ext = NULL;
- const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
- const void *key_val;
- uint32_t key_len;
- LY_ERR r, rc = LY_SUCCESS;
-
- LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
- LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
+ LY_ERR r;
if (!module) {
module = parent->schema->module;
@@ -487,8 +574,47 @@ _lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const ch
/* create list inner node */
LY_CHECK_RET(lyd_create_inner(schema, &ret));
+ if (ext) {
+ ret->flags |= LYD_EXT;
+ }
+
+ *node = ret;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @param[in] ap Ordered key values of the new list instance, all must be set. For ::LY_VALUE_LYB, every value must
+ * be followed by the value length.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+_lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const char *name, LY_VALUE_FORMAT format,
+ ly_bool output, struct lyd_node **node, va_list ap)
+{
+ struct lyd_node *ret = NULL, *key;
+ const struct lysc_node *key_s;
+ const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
+ const void *key_val;
+ uint32_t key_len;
+ LY_ERR rc = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
+
+ /* create the list node */
+ LY_CHECK_RET(_lyd_new_list_node(ctx, parent, module, name, output, &ret));
+
/* create and insert all the keys */
- for (key_s = lysc_node_child(schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
+ for (key_s = lysc_node_child(ret->schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
if (format == LY_VALUE_LYB) {
key_val = va_arg(ap, const void *);
key_len = va_arg(ap, uint32_t);
@@ -497,14 +623,11 @@ _lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const ch
key_len = key_val ? strlen((char *)key_val) : 0;
}
- rc = lyd_create_term(key_s, key_val, key_len, NULL, format, NULL, LYD_HINT_DATA, NULL, &key);
+ rc = lyd_create_term(key_s, key_val, key_len, 0, NULL, format, NULL, LYD_HINT_DATA, NULL, &key);
LY_CHECK_GOTO(rc, cleanup);
lyd_insert_node(ret, NULL, key, 1);
}
- if (ext) {
- ret->flags |= LYD_EXT;
- }
if (parent) {
lyd_insert_node(parent, NULL, ret, 0);
}
@@ -595,7 +718,7 @@ lyd_new_ext_list(const struct lysc_ext_instance *ext, const char *name, struct l
for (key_s = lysc_node_child(schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
key_val = va_arg(ap, const char *);
- rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA,
+ rc = lyd_create_term(key_s, key_val, key_val ? strlen(key_val) : 0, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA,
NULL, &key);
LY_CHECK_GOTO(rc, cleanup);
lyd_insert_node(ret, NULL, key, 1);
@@ -661,6 +784,84 @@ lyd_new_list2(struct lyd_node *parent, const struct lys_module *module, const ch
}
/**
+ * @brief Create a new list node in the data tree.
+ *
+ * @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
+ * @param[in] module Module of the node being created. If NULL, @p parent module will be used.
+ * @param[in] name Schema node name of the new data node. The node must be #LYS_LIST.
+ * @param[in] format Format of key values.
+ * @param[in] key_values Ordered key values of the new list instance ended with NULL, all must be set.
+ * @param[in] value_lengths Lengths of @p key_values, required for ::LY_VALUE_LYB, optional otherwise.
+ * @param[in] output Flag in case the @p parent is RPC/Action. If value is 0, the input's data nodes of the RPC/Action are
+ * taken into consideration. Otherwise, the output's data node is going to be created.
+ * @param[out] node Optional created node.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+_lyd_new_list3(struct lyd_node *parent, const struct lys_module *module, const char *name, LY_VALUE_FORMAT format,
+ const void **key_values, uint32_t *value_lengths, ly_bool output, struct lyd_node **node)
+{
+ struct lyd_node *ret = NULL, *key;
+ const struct lysc_node *key_s;
+ const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
+ const void *key_val;
+ uint32_t key_len, i;
+ LY_ERR rc = LY_SUCCESS;
+
+ LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, key_values, (format != LY_VALUE_LYB) || value_lengths,
+ LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
+
+ /* create the list node */
+ LY_CHECK_RET(_lyd_new_list_node(ctx, parent, module, name, output, &ret));
+
+ /* create and insert all the keys */
+ i = 0;
+ for (key_s = lysc_node_child(ret->schema); key_s && (key_s->flags & LYS_KEY); key_s = key_s->next) {
+ key_val = key_values[i] ? key_values[i] : "";
+ key_len = value_lengths ? value_lengths[i] : strlen(key_val);
+
+ rc = lyd_create_term(key_s, key_val, key_len, 0, NULL, format, NULL, LYD_HINT_DATA, NULL, &key);
+ LY_CHECK_GOTO(rc, cleanup);
+ lyd_insert_node(ret, NULL, key, 1);
+ }
+
+ if (parent) {
+ lyd_insert_node(parent, NULL, ret, 0);
+ }
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(ret);
+ ret = NULL;
+ } else if (node) {
+ *node = ret;
+ }
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_list3(struct lyd_node *parent, const struct lys_module *module, const char *name, const char **key_values,
+ uint32_t *value_lengths, ly_bool output, struct lyd_node **node)
+{
+ return _lyd_new_list3(parent, module, name, LY_VALUE_JSON, (const void **)key_values, value_lengths, output, node);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_list3_bin(struct lyd_node *parent, const struct lys_module *module, const char *name, const void **key_values,
+ uint32_t *value_lengths, ly_bool output, struct lyd_node **node)
+{
+ return _lyd_new_list3(parent, module, name, LY_VALUE_LYB, key_values, value_lengths, output, node);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_new_list3_canon(struct lyd_node *parent, const struct lys_module *module, const char *name, const char **key_values,
+ uint32_t *value_lengths, ly_bool output, struct lyd_node **node)
+{
+ return _lyd_new_list3(parent, module, name, LY_VALUE_CANON, (const void **)key_values, value_lengths, output, node);
+}
+
+/**
* @brief Create a new term node in the data tree.
*
* @param[in] parent Parent node for the node being created. NULL in case of creating a top level element.
@@ -699,7 +900,7 @@ _lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const ch
}
LY_CHECK_ERR_RET(!schema, LOGERR(ctx, LY_EINVAL, "Term node \"%s\" not found.", name), LY_ENOTFOUND);
- LY_CHECK_RET(lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA, NULL, &ret));
+ LY_CHECK_RET(lyd_create_term(schema, value, value_len, 0, NULL, format, NULL, LYD_HINT_DATA, NULL, &ret));
if (ext) {
ret->flags |= LYD_EXT;
}
@@ -754,7 +955,8 @@ lyd_new_ext_term(const struct lysc_ext_instance *ext, const char *name, const ch
}
return LY_ENOTFOUND;
}
- rc = lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, &ret);
+ rc = lyd_create_term(schema, val_str, val_str ? strlen(val_str) : 0, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA,
+ NULL, &ret);
LY_CHECK_RET(rc);
*node = ret;
@@ -772,7 +974,8 @@ lyd_new_any(struct lyd_node *parent, const struct lys_module *module, const char
struct lysc_ext_instance *ext = NULL;
const struct ly_ctx *ctx = parent ? LYD_CTX(parent) : (module ? module->ctx : NULL);
- LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, LY_EINVAL);
+ LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name,
+ (value_type == LYD_ANYDATA_DATATREE) || (value_type == LYD_ANYDATA_STRING) || value, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL);
if (!module) {
@@ -867,7 +1070,7 @@ lyd_new_meta(const struct ly_ctx *ctx, struct lyd_node *parent, const struct lys
val_str = "";
}
- return lyd_create_meta(parent, meta, module, name, name_len, val_str, strlen(val_str), NULL, LY_VALUE_JSON,
+ return lyd_create_meta(parent, meta, module, name, name_len, val_str, strlen(val_str), 0, NULL, LY_VALUE_JSON,
NULL, LYD_HINT_DATA, parent ? parent->schema : NULL, clear_dflt, NULL);
}
@@ -881,7 +1084,8 @@ lyd_new_meta2(const struct ly_ctx *ctx, struct lyd_node *parent, ly_bool clear_d
LY_CHECK_CTX_EQUAL_RET(ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL);
if (parent && !parent->schema) {
- LOGERR(ctx, LY_EINVAL, "Cannot add metadata to an opaque node \"%s\".", ((struct lyd_node_opaq *)parent)->name);
+ LOGERR(ctx, LY_EINVAL, "Cannot add metadata to an opaque node \"%s\".",
+ ((struct lyd_node_opaq *)parent)->name.name);
return LY_EINVAL;
}
if (meta) {
@@ -908,7 +1112,7 @@ lyd_new_meta2(const struct ly_ctx *ctx, struct lyd_node *parent, ly_bool clear_d
}
return lyd_create_meta(parent, meta, mod, attr->name.name, strlen(attr->name.name), attr->value, strlen(attr->value),
- NULL, attr->format, attr->val_prefix_data, attr->hints, parent ? parent->schema : NULL, clear_dflt, NULL);
+ 0, NULL, attr->format, attr->val_prefix_data, attr->hints, parent ? parent->schema : NULL, clear_dflt, NULL);
}
LIBYANG_API_DEF LY_ERR
@@ -1094,7 +1298,8 @@ _lyd_change_term(struct lyd_node *term, const void *value, size_t value_len, LY_
/* parse the new value */
LOG_LOCSET(term->schema, term, NULL, NULL);
- ret = lyd_value_store(LYD_CTX(term), &val, type, value, value_len, NULL, format, NULL, LYD_HINT_DATA, term->schema, NULL);
+ ret = lyd_value_store(LYD_CTX(term), &val, type, value, value_len, 0, NULL, format, NULL, LYD_HINT_DATA,
+ term->schema, NULL);
LOG_LOCBACK(term->schema ? 1 : 0, 1, 0, 0);
LY_CHECK_GOTO(ret, cleanup);
@@ -1195,7 +1400,7 @@ lyd_change_meta(struct lyd_meta *meta, const char *val_str)
/* parse the new value into a new meta structure */
ret = lyd_create_meta(NULL, &m2, meta->annotation->module, meta->name, strlen(meta->name), val_str, strlen(val_str),
- NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, meta->parent ? meta->parent->schema : NULL, 0, NULL);
+ 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, meta->parent ? meta->parent->schema : NULL, 0, NULL);
LY_CHECK_GOTO(ret, cleanup);
/* compare original and new value */
@@ -1320,18 +1525,20 @@ lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const
schema = path[u].node;
if (lysc_is_dup_inst_list(schema)) {
- if (path[u].pred_type == LY_PATH_PREDTYPE_NONE) {
+ if (!path[u].predicates ||
+ ((schema->nodetype == LYS_LEAFLIST) && (path[u].predicates[0].type == LY_PATH_PREDTYPE_LEAFLIST))) {
/* creating a new key-less list or state leaf-list instance */
create = 1;
new_count = u;
- } else if (path[u].pred_type != LY_PATH_PREDTYPE_POSITION) {
+ } else if (path[u].predicates[0].type != LY_PATH_PREDTYPE_POSITION) {
LOG_LOCSET(schema, NULL, NULL, NULL);
- LOGVAL(schema->module->ctx, LYVE_XPATH, "Invalid predicate for %s \"%s\" in path \"%s\".",
+ LOGVAL(schema->module->ctx, LYVE_XPATH, "Invalid predicate for state %s \"%s\" in path \"%s\".",
lys_nodetype2str(schema->nodetype), schema->name, str_path);
LOG_LOCBACK(1, 0, 0, 0);
return LY_EINVAL;
}
- } else if ((schema->nodetype == LYS_LIST) && (path[u].pred_type != LY_PATH_PREDTYPE_LIST)) {
+ } else if ((schema->nodetype == LYS_LIST) &&
+ (!path[u].predicates || (path[u].predicates[0].type != LY_PATH_PREDTYPE_LIST))) {
if ((u < LY_ARRAY_COUNT(path) - 1) || !(options & LYD_NEW_PATH_OPAQ)) {
LOG_LOCSET(schema, NULL, NULL, NULL);
LOGVAL(schema->module->ctx, LYVE_XPATH, "Predicate missing for %s \"%s\" in path \"%s\".",
@@ -1339,7 +1546,8 @@ lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const
LOG_LOCBACK(1, 0, 0, 0);
return LY_EINVAL;
} /* else creating an opaque list */
- } else if ((schema->nodetype == LYS_LEAFLIST) && (path[u].pred_type != LY_PATH_PREDTYPE_LEAFLIST)) {
+ } else if ((schema->nodetype == LYS_LEAFLIST) &&
+ (!path[u].predicates || (path[u].predicates[0].type != LY_PATH_PREDTYPE_LEAFLIST))) {
r = LY_SUCCESS;
if (options & LYD_NEW_PATH_OPAQ) {
r = lyd_value_validate(NULL, schema, value, value_len, NULL, NULL, NULL);
@@ -1347,12 +1555,12 @@ lyd_new_path_check_find_lypath(struct ly_path *path, const char *str_path, const
if (!r) {
/* try to store the value */
LY_CHECK_RET(lyd_value_store(schema->module->ctx, &val, ((struct lysc_node_leaflist *)schema)->type,
- value, value_len, NULL, format, NULL, LYD_HINT_DATA, schema, NULL));
+ value, value_len, 0, NULL, format, NULL, LYD_HINT_DATA, schema, NULL));
++((struct lysc_type *)val.realtype)->refcount;
/* store the new predicate so that it is used when searching for this instance */
- path[u].pred_type = LY_PATH_PREDTYPE_LEAFLIST;
LY_ARRAY_NEW_RET(schema->module->ctx, path[u].predicates, pred, LY_EMEM);
+ pred->type = LY_PATH_PREDTYPE_LEAFLIST;
pred->value = val;
} /* else we have opaq flag and the value is not valid, leave no predicate and then create an opaque node */
}
@@ -1429,7 +1637,7 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
}
/* parse path */
- LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_OPTIONAL,
+ LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST,
LY_PATH_PRED_SIMPLE, &exp), cleanup);
/* compile path */
@@ -1442,32 +1650,36 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
/* try to find any existing nodes in the path */
if (parent) {
- ret = ly_path_eval_partial(p, parent, &path_idx, &node);
- if (ret == LY_SUCCESS) {
+ r = ly_path_eval_partial(p, parent, NULL, options & LYD_NEW_PATH_WITH_OPAQ, &path_idx, &node);
+ if (r == LY_SUCCESS) {
if (orig_count == LY_ARRAY_COUNT(p)) {
/* the node exists, are we supposed to update it or is it just a default? */
if (!(options & LYD_NEW_PATH_UPDATE) && !(node->flags & LYD_DEFAULT)) {
LOG_LOCSET(NULL, node, NULL, NULL);
- LOGVAL(ctx, LYVE_REFERENCE, "Path \"%s\" already exists", path);
+ LOGVAL(ctx, LYVE_REFERENCE, "Path \"%s\" already exists.", path);
LOG_LOCBACK(0, 1, 0, 0);
ret = LY_EEXIST;
goto cleanup;
+ } else if ((options & LYD_NEW_PATH_UPDATE) && lysc_is_key(node->schema)) {
+ /* fine, the key value must not be changed and has to be in the predicate to be found */
+ goto cleanup;
}
/* update the existing node */
ret = lyd_new_path_update(node, value, value_len, value_type, format, &nparent, &nnode);
goto cleanup;
} /* else we were not searching for the whole path */
- } else if (ret == LY_EINCOMPLETE) {
+ } else if (r == LY_EINCOMPLETE) {
/* some nodes were found, adjust the iterator to the next segment */
++path_idx;
- } else if (ret == LY_ENOTFOUND) {
+ } else if (r == LY_ENOTFOUND) {
/* we will create the nodes from top-level, default behavior (absolute path), or from the parent (relative path) */
if (lysc_data_parent(p[0].node)) {
node = parent;
}
} else {
/* error */
+ ret = r;
goto cleanup;
}
}
@@ -1487,15 +1699,14 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
if (lysc_is_dup_inst_list(schema)) {
/* create key-less list instance */
LY_CHECK_GOTO(ret = lyd_create_inner(schema, &node), cleanup);
- } else if ((options & LYD_NEW_PATH_OPAQ) && (p[path_idx].pred_type == LY_PATH_PREDTYPE_NONE)) {
+ } else if ((options & LYD_NEW_PATH_OPAQ) && !p[path_idx].predicates) {
/* creating opaque list without keys */
LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0,
schema->module->name, strlen(schema->module->name), NULL, 0, NULL, LY_VALUE_JSON, NULL,
LYD_NODEHINT_LIST, &node), cleanup);
} else {
/* create standard list instance */
- assert(p[path_idx].pred_type == LY_PATH_PREDTYPE_LIST);
- LY_CHECK_GOTO(ret = lyd_create_list(schema, p[path_idx].predicates, &node), cleanup);
+ LY_CHECK_GOTO(ret = lyd_create_list(schema, p[path_idx].predicates, NULL, &node), cleanup);
}
break;
case LYS_CONTAINER:
@@ -1505,7 +1716,8 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
LY_CHECK_GOTO(ret = lyd_create_inner(schema, &node), cleanup);
break;
case LYS_LEAFLIST:
- if ((options & LYD_NEW_PATH_OPAQ) && (p[path_idx].pred_type != LY_PATH_PREDTYPE_LEAFLIST)) {
+ if ((options & LYD_NEW_PATH_OPAQ) &&
+ (!p[path_idx].predicates || (p[path_idx].predicates[0].type != LY_PATH_PREDTYPE_LEAFLIST))) {
/* we have not checked this only for dup-inst lists, otherwise it must be opaque */
r = LY_EVALID;
if (lysc_is_dup_inst_list(schema)) {
@@ -1522,15 +1734,15 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
}
/* get value to set */
- if (p[path_idx].pred_type == LY_PATH_PREDTYPE_LEAFLIST) {
+ if (p[path_idx].predicates && (p[path_idx].predicates[0].type == LY_PATH_PREDTYPE_LEAFLIST)) {
val = &p[path_idx].predicates[0].value;
}
/* create a leaf-list instance */
if (val) {
- LY_CHECK_GOTO(ret = lyd_create_term2(schema, &p[path_idx].predicates[0].value, &node), cleanup);
+ LY_CHECK_GOTO(ret = lyd_create_term2(schema, val, &node), cleanup);
} else {
- LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA,
+ LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, 0, NULL, format, NULL, LYD_HINT_DATA,
NULL, &node), cleanup);
}
break;
@@ -1560,7 +1772,7 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
}
/* create a leaf instance */
- LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, NULL, format, NULL, LYD_HINT_DATA, NULL,
+ LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, 0, NULL, format, NULL, LYD_HINT_DATA, NULL,
&node), cleanup);
break;
case LYS_ANYDATA:
@@ -1804,9 +2016,7 @@ lyd_new_implicit_tree(struct lyd_node *tree, uint32_t implicit_options, struct l
}
LYD_TREE_DFS_BEGIN(tree, node) {
- /* skip added default nodes */
- if (((node->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) &&
- (node->schema->nodetype & LYD_NODE_INNER)) {
+ if (node->schema->nodetype & LYD_NODE_INNER) {
LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &node_when, NULL,
NULL, implicit_options, diff), cleanup);
}
@@ -1891,16 +2101,12 @@ lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module,
/* process nested nodes */
LY_LIST_FOR(*tree, root) {
- /* skip added default nodes */
- if ((root->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) {
- LY_CHECK_GOTO(ret = lyd_new_implicit_tree(root, implicit_options, diff ? &d : NULL), cleanup);
-
- if (d) {
- /* merge into one diff */
- lyd_insert_sibling(*diff, d, diff);
+ LY_CHECK_GOTO(ret = lyd_new_implicit_tree(root, implicit_options, diff ? &d : NULL), cleanup);
- d = NULL;
- }
+ if (d) {
+ /* merge into one diff */
+ lyd_insert_sibling(*diff, d, diff);
+ d = NULL;
}
}
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 5c897bf..baf2c46 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -37,6 +37,7 @@
#include "parser_internal.h"
#include "parser_schema.h"
#include "path.h"
+#include "plugins_exts.h"
#include "plugins_internal.h"
#include "schema_compile.h"
#include "schema_compile_amend.h"
@@ -164,7 +165,11 @@ lys_getnext_(const struct lysc_node *last, const struct lysc_node *parent, const
const struct lysc_ext_instance *ext, uint32_t options)
{
const struct lysc_node *next = NULL;
- ly_bool action_flag = 0, notif_flag = 0;
+ ly_bool action_flag = 0, notif_flag = 0, sm_flag = options & LYS_GETNEXT_WITHSCHEMAMOUNT ? 0 : 1;
+ LY_ARRAY_COUNT_TYPE u;
+ struct ly_ctx *sm_ctx = NULL;
+ const struct lys_module *mod;
+ uint32_t idx;
LY_CHECK_ARG_RET(NULL, parent || module || ext, NULL);
@@ -172,7 +177,7 @@ next:
if (!last) {
/* first call */
- /* get know where to start */
+ /* learn where to start */
if (parent) {
/* schema subtree */
next = last = lysc_node_child(parent);
@@ -204,8 +209,29 @@ next:
repeat:
if (!next) {
- /* possibly go back to parent */
- if (last && (last->parent != parent)) {
+ if (last && !sm_flag && parent && (last->module->ctx != parent->module->ctx)) {
+ sm_flag = 1;
+
+ /* find the module of last */
+ sm_ctx = last->module->ctx;
+ idx = 0;
+ while ((mod = ly_ctx_get_module_iter(sm_ctx, &idx))) {
+ if (mod == last->module) {
+ break;
+ }
+ }
+ assert(mod);
+
+ /* get node from the next mounted module */
+ while (!next && (mod = ly_ctx_get_module_iter(sm_ctx, &idx))) {
+ if (!mod->implemented) {
+ continue;
+ }
+
+ next = lys_getnext(NULL, NULL, mod->compiled, options & ~LYS_GETNEXT_WITHSCHEMAMOUNT);
+ }
+ } else if (last && (last->parent != parent)) {
+ /* go back to parent */
last = last->parent;
goto next;
} else if (!action_flag) {
@@ -226,6 +252,35 @@ repeat:
} else {
next = (struct lysc_node *)module->notifs;
}
+ } else if (!sm_flag) {
+ sm_flag = 1;
+ if (parent) {
+ LY_ARRAY_FOR(parent->exts, u) {
+ if (!strcmp(parent->exts[u].def->name, "mount-point") &&
+ !strcmp(parent->exts[u].def->module->name, "ietf-yang-schema-mount")) {
+ lyplg_ext_schema_mount_create_context(&parent->exts[u], &sm_ctx);
+ if (sm_ctx) {
+ /* some usable context created */
+ break;
+ }
+ }
+ }
+ if (sm_ctx) {
+ /* get the first node from the first usable module */
+ idx = 0;
+ while (!next && (mod = ly_ctx_get_module_iter(sm_ctx, &idx))) {
+ if (!mod->implemented) {
+ continue;
+ }
+
+ next = lys_getnext(NULL, NULL, mod->compiled, options & ~LYS_GETNEXT_WITHSCHEMAMOUNT);
+ }
+ if (!next) {
+ /* no nodes found */
+ ly_ctx_destroy(sm_ctx);
+ }
+ }
+ }
} else {
return NULL;
}
@@ -387,7 +442,7 @@ lys_find_xpath_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
struct ly_set **set)
{
LY_ERR ret = LY_SUCCESS;
- struct lyxp_set xp_set;
+ struct lyxp_set xp_set = {0};
struct lyxp_expr *exp = NULL;
uint32_t i;
@@ -400,7 +455,9 @@ lys_find_xpath_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
ctx = ctx_node->module->ctx;
}
- memset(&xp_set, 0, sizeof xp_set);
+ /* allocate return set */
+ ret = ly_set_new(set);
+ LY_CHECK_GOTO(ret, cleanup);
/* compile expression */
ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp);
@@ -410,10 +467,6 @@ lys_find_xpath_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
ret = lyxp_atomize(ctx, exp, NULL, LY_VALUE_JSON, NULL, ctx_node, ctx_node, &xp_set, options);
LY_CHECK_GOTO(ret, cleanup);
- /* allocate return set */
- ret = ly_set_new(set);
- LY_CHECK_GOTO(ret, cleanup);
-
/* transform into ly_set */
(*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(ctx); ret = LY_EMEM, cleanup);
@@ -446,15 +499,15 @@ lys_find_expr_atoms(const struct lysc_node *ctx_node, const struct lys_module *c
options = LYXP_SCNODE;
}
+ /* allocate return set */
+ ret = ly_set_new(set);
+ LY_CHECK_GOTO(ret, cleanup);
+
/* atomize expression */
ret = lyxp_atomize(cur_mod->ctx, expr, cur_mod, LY_VALUE_SCHEMA_RESOLVED, (void *)prefixes, ctx_node, ctx_node,
&xp_set, options);
LY_CHECK_GOTO(ret, cleanup);
- /* allocate return set */
- ret = ly_set_new(set);
- LY_CHECK_GOTO(ret, cleanup);
-
/* transform into ly_set */
(*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(cur_mod->ctx); ret = LY_EMEM, cleanup);
@@ -497,6 +550,10 @@ lys_find_xpath(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
ctx = ctx_node->module->ctx;
}
+ /* allocate return set */
+ ret = ly_set_new(set);
+ LY_CHECK_GOTO(ret, cleanup);
+
/* compile expression */
ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp);
LY_CHECK_GOTO(ret, cleanup);
@@ -505,10 +562,6 @@ lys_find_xpath(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
ret = lyxp_atomize(ctx, exp, NULL, LY_VALUE_JSON, NULL, ctx_node, ctx_node, &xp_set, options);
LY_CHECK_GOTO(ret, cleanup);
- /* allocate return set */
- ret = ly_set_new(set);
- LY_CHECK_GOTO(ret, cleanup);
-
/* transform into ly_set */
(*set)->objs = malloc(xp_set.used * sizeof *(*set)->objs);
LY_CHECK_ERR_GOTO(!(*set)->objs, LOGMEM(ctx); ret = LY_EMEM, cleanup);
@@ -545,8 +598,8 @@ lys_find_lypath_atoms(const struct ly_path *path, struct ly_set **set)
LY_ARRAY_FOR(path, u) {
/* add nodes from the path */
LY_CHECK_GOTO(ret = ly_set_add(*set, (void *)path[u].node, 0, NULL), cleanup);
- if (path[u].pred_type == LY_PATH_PREDTYPE_LIST) {
- LY_ARRAY_FOR(path[u].predicates, v) {
+ LY_ARRAY_FOR(path[u].predicates, v) {
+ if ((path[u].predicates[v].type == LY_PATH_PREDTYPE_LIST) || (path[u].predicates[v].type == LY_PATH_PREDTYPE_LIST_VAR)) {
/* add all the keys in a predicate */
LY_CHECK_GOTO(ret = ly_set_add(*set, (void *)path[u].predicates[v].key, 0, NULL), cleanup);
}
@@ -578,7 +631,8 @@ lys_find_path_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node,
}
/* parse */
- ret = lyxp_expr_parse(ctx, path, strlen(path), 0, &expr);
+ ret = ly_path_parse(ctx, ctx_node, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST,
+ LY_PATH_PRED_SIMPLE, &expr);
LY_CHECK_GOTO(ret, cleanup);
/* compile */
@@ -599,7 +653,7 @@ LIBYANG_API_DEF const struct lysc_node *
lys_find_path(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *path, ly_bool output)
{
const struct lysc_node *snode = NULL;
- struct lyxp_expr *exp = NULL;
+ struct lyxp_expr *expr = NULL;
struct ly_path *p = NULL;
LY_ERR ret;
uint8_t oper;
@@ -612,12 +666,13 @@ lys_find_path(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
}
/* parse */
- ret = lyxp_expr_parse(ctx, path, strlen(path), 0, &exp);
+ ret = ly_path_parse(ctx, ctx_node, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST,
+ LY_PATH_PRED_SIMPLE, &expr);
LY_CHECK_GOTO(ret, cleanup);
/* compile */
oper = output ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
- ret = ly_path_compile(ctx, NULL, ctx_node, NULL, exp, oper, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p);
+ ret = ly_path_compile(ctx, NULL, ctx_node, NULL, expr, oper, LY_PATH_TARGET_MANY, 0, LY_VALUE_JSON, NULL, &p);
LY_CHECK_GOTO(ret, cleanup);
/* get last node */
@@ -625,7 +680,7 @@ lys_find_path(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const
cleanup:
ly_path_free(ctx, p);
- lyxp_expr_free(ctx, exp);
+ lyxp_expr_free(ctx, expr);
return snode;
}
@@ -1457,9 +1512,10 @@ error:
static LY_ERR
lysp_add_internal_ietf_netconf(struct lysp_ctx *pctx, struct lysp_module *mod)
{
- struct lysp_ext_instance *extp;
+ struct lysp_ext_instance *extp, *prev_exts = mod->exts;
struct lysp_stmt *stmt;
struct lysp_import *imp;
+ uint32_t idx;
/*
* 1) edit-config's operation
@@ -1599,10 +1655,16 @@ lysp_add_internal_ietf_netconf(struct lysp_ctx *pctx, struct lysp_module *mod)
stmt->prefix_data = mod;
stmt->kw = LY_STMT_TYPE;
- if (LY_ARRAY_COUNT(mod->exts) == 3) {
+ if (!prev_exts) {
/* first extension instances */
assert(pctx->main_ctx == pctx);
LY_CHECK_RET(ly_set_add(&pctx->ext_inst, mod->exts, 1, NULL));
+ } else {
+ /* replace previously stored extension instances */
+ if (!ly_set_contains(&pctx->ext_inst, prev_exts, &idx)) {
+ LOGINT_RET(mod->mod->ctx);
+ }
+ pctx->ext_inst.objs[idx] = mod->exts;
}
/* create new imports for the used prefixes */
@@ -1630,9 +1692,10 @@ lysp_add_internal_ietf_netconf(struct lysp_ctx *pctx, struct lysp_module *mod)
static LY_ERR
lysp_add_internal_ietf_netconf_with_defaults(struct lysp_ctx *pctx, struct lysp_module *mod)
{
- struct lysp_ext_instance *extp;
+ struct lysp_ext_instance *extp, *prev_exts = mod->exts;
struct lysp_stmt *stmt;
struct lysp_import *imp;
+ uint32_t idx;
/* add new extension instance */
LY_ARRAY_NEW_RET(mod->mod->ctx, mod->exts, extp, LY_EMEM);
@@ -1654,10 +1717,16 @@ lysp_add_internal_ietf_netconf_with_defaults(struct lysp_ctx *pctx, struct lysp_
stmt->prefix_data = mod;
stmt->kw = LY_STMT_TYPE;
- if (LY_ARRAY_COUNT(mod->exts) == 1) {
- /* first extension instance */
+ if (!prev_exts) {
+ /* first extension instances */
assert(pctx->main_ctx == pctx);
LY_CHECK_RET(ly_set_add(&pctx->ext_inst, mod->exts, 1, NULL));
+ } else {
+ /* replace previously stored extension instances */
+ if (!ly_set_contains(&pctx->ext_inst, prev_exts, &idx)) {
+ LOGINT_RET(mod->mod->ctx);
+ }
+ pctx->ext_inst.objs[idx] = mod->exts;
}
/* create new import for the used prefix */
@@ -1680,8 +1749,6 @@ lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format,
struct lysp_yin_ctx *yinctx = NULL;
struct lysp_ctx *pctx = NULL;
struct lysf_ctx fctx = {.ctx = ctx};
- char *filename, *rev, *dot;
- size_t len;
ly_bool module_created = 0;
assert(ctx && in && new_mods);
@@ -1762,30 +1829,7 @@ lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format,
switch (in->type) {
case LY_IN_FILEPATH:
- /* check that name and revision match filename */
- filename = strrchr(in->method.fpath.filepath, '/');
- if (!filename) {
- filename = in->method.fpath.filepath;
- } else {
- filename++;
- }
- rev = strchr(filename, '@');
- dot = strrchr(filename, '.');
-
- /* name */
- len = strlen(mod->name);
- if (strncmp(filename, mod->name, len) ||
- ((rev && (rev != &filename[len])) || (!rev && (dot != &filename[len])))) {
- LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
- }
- if (rev) {
- len = dot - ++rev;
- if (!mod->parsed->revs || (len != LY_REV_SIZE - 1) || strncmp(mod->parsed->revs[0].date, rev, len)) {
- LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
- mod->parsed->revs ? mod->parsed->revs[0].date : "none");
- }
- }
-
+ ly_check_module_filename(ctx, mod->name, mod->parsed->revs ? mod->parsed->revs[0].date : NULL, in->method.fpath.filepath);
break;
case LY_IN_FD:
case LY_IN_FILE:
diff --git a/src/tree_schema.h b/src/tree_schema.h
index c57a0fc..e712239 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1579,9 +1579,7 @@ struct lysc_node_choice {
};
};
- struct lysc_node_case *cases; /**< list of the cases (linked list). Note that all the children of all the cases are linked each other
- as siblings. Their parent pointers points to the specific case they belongs to, so distinguish the
- case is simple. */
+ struct lysc_node_case *cases; /**< list of all the cases (linked list) */
struct lysc_when **when; /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
struct lysc_node_case *dflt; /**< default case of the choice, only a pointer into the cases array. */
};
@@ -1887,6 +1885,14 @@ LIBYANG_API_DECL struct lysc_must *lysc_node_musts(const struct lysc_node *node)
LIBYANG_API_DECL struct lysc_when **lysc_node_when(const struct lysc_node *node);
/**
+ * @brief Get the target node of a leafref node.
+ *
+ * @param[in] node Leafref node.
+ * @return Leafref target, NULL on any error.
+ */
+LIBYANG_API_DECL const struct lysc_node *lysc_node_lref_target(const struct lysc_node *node);
+
+/**
* @brief Callback to be called for every schema node in a DFS traversal.
*
* @param[in] node Current node.
@@ -1971,6 +1977,8 @@ LIBYANG_API_DECL struct lysp_feature *lysp_feature_next(const struct lysp_featur
#define LYS_FIND_XP_OUTPUT 0x10 /**< Search RPC/action output nodes instead of input ones. */
#define LYS_FIND_NO_MATCH_ERROR 0x40 /**< Return error if a path segment matches no nodes, otherwise only warning
is printed. */
+#define LYS_FIND_SCHEMAMOUNT 0x0200 /**< Traverse also nodes from mounted modules. If any such nodes are returned,
+ the caller **must free** their context! */
/** @} findxpathoptions */
/**
@@ -2189,6 +2197,9 @@ LIBYANG_API_DECL const struct lysc_node *lys_getnext_ext(const struct lysc_node
#define LYS_GETNEXT_INTONPCONT 0x08 /**< ::lys_getnext() option to look into non-presence container, instead of returning container itself */
#define LYS_GETNEXT_OUTPUT 0x10 /**< ::lys_getnext() option to provide RPC's/action's output schema nodes instead of input schema nodes
provided by default */
+#define LYS_GETNEXT_WITHSCHEMAMOUNT 0x20 /**< ::lys_getnext() option to also traverse top-level nodes of all the mounted modules
+ on the parent mount point but note that if any such nodes are returned,
+ the caller **must free** their context */
/** @} sgetnextflags */
/**
diff --git a/src/tree_schema_common.c b/src/tree_schema_common.c
index bbdd676..6d3b710 100644
--- a/src/tree_schema_common.c
+++ b/src/tree_schema_common.c
@@ -32,6 +32,7 @@
#include "in_internal.h"
#include "log.h"
#include "parser_schema.h"
+#include "path.h"
#include "schema_compile.h"
#include "schema_features.h"
#include "set.h"
@@ -104,7 +105,7 @@ lysp_check_date(struct lysp_ctx *ctx, const char *date, size_t date_len, const c
error:
if (stmt) {
- LOGVAL_PARSER(ctx, LY_VCODE_INVAL, date_len, date, stmt);
+ LOGVAL_PARSER(ctx, LY_VCODE_INVAL, (int)date_len, date, stmt);
}
return LY_EINVAL;
}
@@ -140,10 +141,10 @@ lysp_check_enum_name(struct lysp_ctx *ctx, const char *name, size_t name_len)
(int)name_len, name);
return LY_EVALID;
} else {
- for (size_t u = 0; u < name_len; ++u) {
+ for (uint32_t u = 0; u < name_len; ++u) {
if (iscntrl(name[u])) {
- LOGWRN(PARSER_CTX(ctx), "Control characters in enum name should be avoided (\"%.*s\", character number %d).",
- (int)name_len, name, u + 1);
+ LOGWRN(PARSER_CTX(ctx), "Control characters in enum name should be avoided "
+ "(\"%.*s\", character number %" PRIu32 ").", (int)name_len, name, u + 1);
break;
}
}
@@ -350,19 +351,18 @@ lysp_type_find(const char *id, struct lysp_node *start_node, const struct lysp_m
* @param[in,out] ctx Context to log the error.
* @param[in,out] ht Hash table with top-level names.
* @param[in] name Inserted top-level identifier.
- * @param[in] statement The name of the statement type from which
- * @p name originated (eg typedef, feature, ...).
+ * @param[in] statement The name of the statement type from which @p name originated (eg typedef, feature, ...).
* @param[in] err_detail Optional error specification.
* @return LY_ERR, but LY_EEXIST is mapped to LY_EVALID.
*/
static LY_ERR
-lysp_check_dup_ht_insert(struct lysp_ctx *ctx, struct hash_table *ht,
- const char *name, const char *statement, const char *err_detail)
+lysp_check_dup_ht_insert(struct lysp_ctx *ctx, struct ly_ht *ht, const char *name, const char *statement,
+ const char *err_detail)
{
LY_ERR ret;
uint32_t hash;
- hash = dict_hash(name, strlen(name));
+ hash = lyht_hash(name, strlen(name));
ret = lyht_insert(ht, &name, hash, NULL);
if (ret == LY_EEXIST) {
if (err_detail) {
@@ -388,7 +388,7 @@ lysp_check_dup_ht_insert(struct lysp_ctx *ctx, struct hash_table *ht,
*/
static LY_ERR
lysp_check_dup_typedef(struct lysp_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
- struct hash_table *tpdfs_global)
+ struct ly_ht *tpdfs_global)
{
struct lysp_node *parent;
uint32_t hash;
@@ -434,7 +434,7 @@ lysp_check_dup_typedef(struct lysp_ctx *ctx, struct lysp_node *node, const struc
/* check collision with the top-level typedefs */
if (node) {
- hash = dict_hash(name, name_len);
+ hash = lyht_hash(name, name_len);
if (!lyht_find(tpdfs_global, &name, hash, NULL)) {
LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
"Duplicate identifier \"%s\" of typedef statement - scoped type collide with a top-level type.", name);
@@ -469,7 +469,7 @@ lysp_id_cmp(void *val1, void *val2, ly_bool UNUSED(mod), void *UNUSED(cb_data))
LY_ERR
lysp_check_dup_typedefs(struct lysp_ctx *ctx, struct lysp_module *mod)
{
- struct hash_table *ids_global;
+ struct ly_ht *ids_global;
const struct lysp_tpdf *typedefs;
LY_ARRAY_COUNT_TYPE u, v;
uint32_t i;
@@ -496,7 +496,7 @@ lysp_check_dup_typedefs(struct lysp_ctx *ctx, struct lysp_module *mod)
}
cleanup:
- lyht_free(ids_global);
+ lyht_free(ids_global, NULL);
return ret;
}
@@ -528,7 +528,7 @@ lysp_grouping_match(const char *name, struct lysp_node *node)
*/
static LY_ERR
lysp_check_dup_grouping(struct lysp_ctx *ctx, struct lysp_node *node, const struct lysp_node_grp *grp,
- struct hash_table *grps_global)
+ struct ly_ht *grps_global)
{
struct lysp_node *parent;
uint32_t hash;
@@ -567,7 +567,7 @@ lysp_check_dup_grouping(struct lysp_ctx *ctx, struct lysp_node *node, const stru
/* check collision with the top-level groupings */
if (node) {
- hash = dict_hash(name, name_len);
+ hash = lyht_hash(name, name_len);
if (!lyht_find(grps_global, &name, hash, NULL)) {
LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
"Duplicate identifier \"%s\" of grouping statement - scoped grouping collide with a top-level grouping.", name);
@@ -584,7 +584,7 @@ lysp_check_dup_grouping(struct lysp_ctx *ctx, struct lysp_node *node, const stru
LY_ERR
lysp_check_dup_groupings(struct lysp_ctx *ctx, struct lysp_module *mod)
{
- struct hash_table *ids_global;
+ struct ly_ht *ids_global;
const struct lysp_node_grp *groupings, *grp_iter;
LY_ARRAY_COUNT_TYPE u;
uint32_t i;
@@ -610,7 +610,7 @@ lysp_check_dup_groupings(struct lysp_ctx *ctx, struct lysp_module *mod)
}
cleanup:
- lyht_free(ids_global);
+ lyht_free(ids_global, NULL);
return ret;
}
@@ -626,7 +626,7 @@ LY_ERR
lysp_check_dup_features(struct lysp_ctx *ctx, struct lysp_module *mod)
{
LY_ARRAY_COUNT_TYPE u;
- struct hash_table *ht;
+ struct ly_ht *ht;
struct lysp_feature *f;
LY_ERR ret = LY_SUCCESS;
@@ -650,7 +650,7 @@ lysp_check_dup_features(struct lysp_ctx *ctx, struct lysp_module *mod)
}
cleanup:
- lyht_free(ht);
+ lyht_free(ht, NULL);
return ret;
}
@@ -658,7 +658,7 @@ LY_ERR
lysp_check_dup_identities(struct lysp_ctx *ctx, struct lysp_module *mod)
{
LY_ARRAY_COUNT_TYPE u;
- struct hash_table *ht;
+ struct ly_ht *ht;
struct lysp_ident *i;
LY_ERR ret = LY_SUCCESS;
@@ -682,7 +682,7 @@ lysp_check_dup_identities(struct lysp_ctx *ctx, struct lysp_module *mod)
}
cleanup:
- lyht_free(ht);
+ lyht_free(ht, NULL);
return ret;
}
@@ -697,9 +697,8 @@ static LY_ERR
lysp_load_module_check(const struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *data)
{
struct lysp_load_module_check_data *info = data;
- const char *filename, *dot, *rev, *name;
+ const char *name;
uint8_t latest_revision;
- size_t len;
struct lysp_revision *revs;
name = mod ? mod->mod->name : submod->name;
@@ -740,29 +739,7 @@ lysp_load_module_check(const struct ly_ctx *ctx, struct lysp_module *mod, struct
}
}
if (info->path) {
- /* check that name and revision match filename */
- filename = strrchr(info->path, '/');
- if (!filename) {
- filename = info->path;
- } else {
- filename++;
- }
- /* name */
- len = strlen(name);
- rev = strchr(filename, '@');
- dot = strrchr(info->path, '.');
- if (strncmp(filename, name, len) ||
- ((rev && (rev != &filename[len])) || (!rev && (dot != &filename[len])))) {
- LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, name);
- }
- /* revision */
- if (rev) {
- len = dot - ++rev;
- if (!revs || (len != LY_REV_SIZE - 1) || strncmp(revs[0].date, rev, len)) {
- LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
- revs ? revs[0].date : "none");
- }
- }
+ ly_check_module_filename(ctx, name, revs ? revs[0].date : NULL, info->path);
}
return LY_SUCCESS;
}
@@ -921,7 +898,7 @@ lys_get_module_without_revision(struct ly_ctx *ctx, const char *name)
struct lys_module *mod, *mod_impl;
uint32_t index;
- /* Try to find module with LYS_MOD_IMPORTED_REV flag. */
+ /* try to find module with LYS_MOD_IMPORTED_REV flag */
index = 0;
while ((mod = ly_ctx_get_module_iter(ctx, &index))) {
if (!strcmp(mod->name, name) && (mod->latest_revision & LYS_MOD_IMPORTED_REV)) {
@@ -929,17 +906,19 @@ lys_get_module_without_revision(struct ly_ctx *ctx, const char *name)
}
}
- /* Try to find the implemented module. */
+ /* try to find the implemented module */
mod_impl = ly_ctx_get_module_implemented(ctx, name);
- if (mod && mod_impl) {
- LOGVRB("Implemented module \"%s@%s\" is not used for import, revision \"%s\" is imported instead.",
- mod_impl->name, mod_impl->revision, mod->revision);
+ if (mod) {
+ if (mod_impl && (mod != mod_impl)) {
+ LOGVRB("Implemented module \"%s@%s\" is not used for import, revision \"%s\" is imported instead.",
+ mod_impl->name, mod_impl->revision, mod->revision);
+ }
return mod;
} else if (mod_impl) {
return mod_impl;
}
- /* Try to find the latest module in the current context. */
+ /* try to find the latest module in the current context */
mod = ly_ctx_get_module_latest(ctx, name);
return mod;
@@ -949,10 +928,8 @@ lys_get_module_without_revision(struct ly_ctx *ctx, const char *name)
* @brief Check if a circular dependency exists between modules.
*
* @param[in] ctx libyang context for log an error.
- * @param[in,out] mod Examined module which is set to NULL
- * if the circular dependency is detected.
- * @return LY_SUCCESS if no circular dependecy is detected,
- * otherwise LY_EVALID.
+ * @param[in,out] mod Examined module which is set to NULL if the circular dependency is detected.
+ * @return LY_SUCCESS if no circular dependecy is detected, otherwise LY_EVALID.
*/
static LY_ERR
lys_check_circular_dependency(struct ly_ctx *ctx, struct lys_module **mod)
@@ -1184,7 +1161,6 @@ lysp_inject_submodule(struct lysp_ctx *pctx, struct lysp_include *inc)
DUP_STRING_RET(PARSER_CTX(pctx), inc->name, inc_new->name);
DUP_STRING_RET(PARSER_CTX(pctx), inc->dsc, inc_new->dsc);
DUP_STRING_RET(PARSER_CTX(pctx), inc->ref, inc_new->ref);
- /* TODO duplicate extensions */
memcpy(inc_new->rev, inc->rev, LY_REV_SIZE);
inc_new->injected = 1;
}
@@ -1826,6 +1802,36 @@ lysc_node_when(const struct lysc_node *node)
}
}
+LIBYANG_API_DEF const struct lysc_node *
+lysc_node_lref_target(const struct lysc_node *node)
+{
+ struct lysc_type_leafref *lref;
+ struct ly_path *p;
+ const struct lysc_node *target;
+
+ if (!node || !(node->nodetype & LYD_NODE_TERM)) {
+ return NULL;
+ }
+
+ lref = (struct lysc_type_leafref *)((struct lysc_node_leaf *)node)->type;
+ if (lref->basetype != LY_TYPE_LEAFREF) {
+ return NULL;
+ }
+
+ /* compile the path */
+ if (ly_path_compile_leafref(node->module->ctx, node, NULL, lref->path,
+ (node->flags & LYS_IS_OUTPUT) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY,
+ LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, &p)) {
+ return NULL;
+ }
+
+ /* get the target node */
+ target = p[LY_ARRAY_COUNT(p) - 1].node;
+ ly_path_free(node->module->ctx, p);
+
+ return target;
+}
+
enum ly_stmt
lysp_match_kw(struct ly_in *in, uint64_t *indent)
{
@@ -2615,3 +2621,41 @@ lys_stmt_flags(enum ly_stmt stmt)
return 0;
}
+
+void
+ly_check_module_filename(const struct ly_ctx *ctx, const char *name, const char *revision, const char *filename)
+{
+ const char *basename, *rev, *dot;
+ size_t len;
+
+ /* check that name and revision match filename */
+ basename = strrchr(filename, '/');
+#ifdef _WIN32
+ const char *backslash = strrchr(filename, '\\');
+
+ if (!basename || (basename && backslash && (backslash > basename))) {
+ basename = backslash;
+ }
+#endif
+ if (!basename) {
+ basename = filename;
+ } else {
+ basename++; /* leading slash */
+ }
+ rev = strchr(basename, '@');
+ dot = strrchr(basename, '.');
+
+ /* name */
+ len = strlen(name);
+ if (strncmp(basename, name, len) ||
+ ((rev && (rev != &basename[len])) || (!rev && (dot != &basename[len])))) {
+ LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", basename, name);
+ }
+ if (rev) {
+ len = dot - ++rev;
+ if (!revision || (len != LY_REV_SIZE - 1) || strncmp(revision, rev, len)) {
+ LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", basename,
+ revision ? revision : "none");
+ }
+ }
+}
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 95dee0b..0a2ad3f 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -732,4 +732,14 @@ uint8_t lys_stmt_flags(enum ly_stmt stmt);
*/
LY_ERR lyplg_ext_get_storage_p(const struct lysc_ext_instance *ext, int stmt, const void ***storage_p);
+/**
+ * @brief Warning if the filename does not match the expected module name and version
+ *
+ * @param[in] ctx Context for logging
+ * @param[in] name Expected module name
+ * @param[in] revision Expected module revision, or NULL if not to be checked
+ * @param[in] filename File path to be checked
+ */
+void ly_check_module_filename(const struct ly_ctx *ctx, const char *name, const char *revision, const char *filename);
+
#endif /* LY_TREE_SCHEMA_INTERNAL_H_ */
diff --git a/src/validation.c b/src/validation.c
index 6db020a..35136ad 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Validation
*
- * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2019 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -40,6 +40,22 @@
#include "tree_schema_internal.h"
#include "xpath.h"
+/**
+ * @brief Check validation error taking into account multi-error validation.
+ *
+ * @param[in] r Local return value.
+ * @param[in] err_cmd Command to perform on any error.
+ * @param[in] val_opts Validation options.
+ * @param[in] label Label to go to on fatal error.
+ */
+#define LY_VAL_ERR_GOTO(r, err_cmd, val_opts, label) \
+ if (r) { \
+ err_cmd; \
+ if ((r != LY_EVALID) || !(val_opts & LYD_VALIDATE_MULTI_ERROR)) { \
+ goto label; \
+ } \
+ }
+
LY_ERR
lyd_val_diff_add(const struct lyd_node *node, enum lyd_diff_op op, struct lyd_node **diff)
{
@@ -126,7 +142,7 @@ static LY_ERR
lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node, const struct lysc_node *schema,
uint32_t xpath_options, const struct lysc_when **disabled)
{
- LY_ERR ret;
+ LY_ERR r;
const struct lyd_node *ctx_node;
struct lyxp_set xp_set;
LY_ARRAY_COUNT_TYPE u;
@@ -152,12 +168,12 @@ lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node,
/* evaluate when */
memset(&xp_set, 0, sizeof xp_set);
- ret = lyxp_eval(LYD_CTX(node), when->cond, schema->module, LY_VALUE_SCHEMA_RESOLVED, when->prefixes,
+ r = lyxp_eval(LYD_CTX(node), when->cond, schema->module, LY_VALUE_SCHEMA_RESOLVED, when->prefixes,
ctx_node, ctx_node, tree, NULL, &xp_set, LYXP_SCHEMA | xpath_options);
lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
/* return error or LY_EINCOMPLETE for dependant unresolved when */
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(r);
if (!xp_set.val.bln) {
/* false when */
@@ -173,6 +189,62 @@ lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node,
}
/**
+ * @brief Properly delete a node as part of auto-delete validation tasks.
+ *
+ * @param[in,out] first First sibling, is updated if needed.
+ * @param[in] del Node instance to delete.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in] np_cont_diff Whether to put NP container into diff or only its children.
+ * @param[in,out] node Optional current iteration node, update it if it is deleted.
+ * @param[in,out] node_when Optional set with nodes with "when" conditions, may be removed from.
+ * @param[in,out] diff Validation diff.
+ * @return 1 if @p node auto-deleted and updated to its next sibling.
+ * @return 0 if @p node was not auto-deleted.
+ */
+static ly_bool
+lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *del, const struct lys_module *mod,
+ int np_cont_diff, struct lyd_node **node, struct ly_set *node_types, struct lyd_node **diff)
+{
+ struct lyd_node *iter;
+ ly_bool node_autodel = 0;
+ uint32_t idx;
+
+ /* update pointers */
+ lyd_del_move_root(first, del, mod);
+ if (node && (del == *node)) {
+ *node = (*node)->next;
+ node_autodel = 1;
+ }
+
+ if (diff) {
+ /* add into diff */
+ if (!np_cont_diff && (del->schema->nodetype == LYS_CONTAINER) && !(del->schema->flags & LYS_PRESENCE)) {
+ /* we do not want to track NP container changes, but remember any removed children */
+ LY_LIST_FOR(lyd_child(del), iter) {
+ lyd_val_diff_add(iter, LYD_DIFF_OP_DELETE, diff);
+ }
+ } else {
+ lyd_val_diff_add(del, LYD_DIFF_OP_DELETE, diff);
+ }
+ }
+
+ if (node_types && node_types->count) {
+ /* remove from node_types set */
+ LYD_TREE_DFS_BEGIN(del, iter) {
+ if (ly_set_contains(node_types, iter, &idx)) {
+ ly_set_rm_index(node_types, idx, NULL);
+ }
+ LYD_TREE_DFS_END(del, iter);
+ }
+ }
+
+ /* free */
+ lyd_free_tree(del);
+
+ return node_autodel;
+}
+
+/**
* @brief Evaluate when conditions of collected unres nodes.
*
* @param[in,out] tree Data tree, is updated if some nodes are autodeleted.
@@ -180,6 +252,7 @@ lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node,
* If set, it is expected @p tree should point to the first node of @p mod. Otherwise it will simply be
* the first top-level sibling.
* @param[in] node_when Set with nodes with "when" conditions.
+ * @param[in] val_opts Validation options.
* @param[in] xpath_options Additional XPath options to use.
* @param[in,out] node_types Set with nodes with unresolved types, remove any with false "when" parents.
* @param[in,out] diff Validation diff.
@@ -187,13 +260,13 @@ lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node,
* @return LY_ERR value on error.
*/
static LY_ERR
-lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when,
+lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, struct ly_set *node_when, uint32_t val_opts,
uint32_t xpath_options, struct ly_set *node_types, struct lyd_node **diff)
{
- LY_ERR rc, r;
- uint32_t i, idx;
+ LY_ERR rc = LY_SUCCESS, r;
+ uint32_t i;
const struct lysc_when *disabled;
- struct lyd_node *node = NULL, *elem;
+ struct lyd_node *node = NULL;
if (!node_when->count) {
return LY_SUCCESS;
@@ -212,32 +285,15 @@ lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, st
/* when false */
if (node->flags & LYD_WHEN_TRUE) {
/* autodelete */
- lyd_del_move_root(tree, node, mod);
- if (diff) {
- /* add into diff */
- LY_CHECK_GOTO(rc = lyd_val_diff_add(node, LYD_DIFF_OP_DELETE, diff), error);
- }
-
- /* remove from node types set, if present */
- if (node_types && node_types->count) {
- LYD_TREE_DFS_BEGIN(node, elem) {
- /* only term nodes with a validation callback can be in node_types */
- if ((elem->schema->nodetype & LYD_NODE_TERM) &&
- ((struct lysc_node_leaf *)elem->schema)->type->plugin->validate &&
- ly_set_contains(node_types, elem, &idx)) {
- LY_CHECK_GOTO(rc = ly_set_rm_index(node_types, idx, NULL), error);
- }
- LYD_TREE_DFS_END(node, elem);
- }
- }
-
- /* free */
- lyd_free_tree(node);
+ lyd_validate_autodel_node_del(tree, node, mod, 1, NULL, node_types, diff);
+ } else if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ LOGWRN(LYD_CTX(node), "When condition \"%s\" not satisfied.", disabled->cond->expr);
} else {
/* invalid data */
LOGVAL(LYD_CTX(node), LY_VCODE_NOWHEN, disabled->cond->expr);
- rc = LY_EVALID;
- goto error;
+ r = LY_EVALID;
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, error);
}
} else {
/* when true */
@@ -248,14 +304,13 @@ lyd_validate_unres_when(struct lyd_node **tree, const struct lys_module *mod, st
ly_set_rm_index_ordered(node_when, i, NULL);
} else if (r != LY_EINCOMPLETE) {
/* error */
- rc = r;
- goto error;
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, error);
}
LOG_LOCBACK(1, 1, 0, 0);
} while (i);
- return LY_SUCCESS;
+ return rc;
error:
LOG_LOCBACK(1, 1, 0, 0);
@@ -267,7 +322,7 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
uint32_t when_xp_opts, struct ly_set *node_types, struct ly_set *meta_types, struct ly_set *ext_node,
struct ly_set *ext_val, uint32_t val_opts, struct lyd_node **diff)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
uint32_t i;
if (ext_val && ext_val->count) {
@@ -279,8 +334,8 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
struct lyd_ctx_ext_val *ext_v = ext_val->objs[i];
/* validate extension data */
- ret = ext_v->ext->def->plugin->validate(ext_v->ext, ext_v->sibling, *tree, data_type, val_opts, diff);
- LY_CHECK_RET(ret);
+ r = ext_v->ext->def->plugin->validate(ext_v->ext, ext_v->sibling, *tree, data_type, val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* remove this item from the set */
ly_set_rm_index(ext_val, i, free);
@@ -296,8 +351,8 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
struct lyd_ctx_ext_node *ext_n = ext_node->objs[i];
/* validate the node */
- ret = ext_n->ext->def->plugin->node(ext_n->ext, ext_n->node, val_opts);
- LY_CHECK_RET(ret);
+ r = ext_n->ext->def->plugin->node(ext_n->ext, ext_n->node, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* remove this item from the set */
ly_set_rm_index(ext_node, i, free);
@@ -310,12 +365,14 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
do {
prev_count = node_when->count;
- LY_CHECK_RET(lyd_validate_unres_when(tree, mod, node_when, when_xp_opts, node_types, diff));
+ r = lyd_validate_unres_when(tree, mod, node_when, val_opts, when_xp_opts, node_types, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
+
/* there must have been some when conditions resolved */
} while (prev_count > node_when->count);
/* there could have been no cyclic when dependencies, checked during compilation */
- assert(!node_when->count);
+ assert(!node_when->count || ((rc == LY_EVALID) && (val_opts & LYD_VALIDATE_MULTI_ERROR)));
}
if (node_types && node_types->count) {
@@ -329,9 +386,9 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
/* resolve the value of the node */
LOG_LOCSET(NULL, &node->node, NULL, NULL);
- ret = lyd_value_validate_incomplete(LYD_CTX(node), type, &node->value, &node->node, *tree);
+ r = lyd_value_validate_incomplete(LYD_CTX(node), type, &node->value, &node->node, *tree);
LOG_LOCBACK(0, 1, 0, 0);
- LY_CHECK_RET(ret);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* remove this node from the set */
ly_set_rm_index(node_types, i, NULL);
@@ -349,15 +406,16 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
/* validate and store the value of the metadata */
lyplg_ext_get_storage(meta->annotation, LY_STMT_TYPE, sizeof type, (const void **)&type);
- ret = lyd_value_validate_incomplete(LYD_CTX(meta->parent), type, &meta->value, meta->parent, *tree);
- LY_CHECK_RET(ret);
+ r = lyd_value_validate_incomplete(LYD_CTX(meta->parent), type, &meta->value, meta->parent, *tree);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* remove this attr from the set */
ly_set_rm_index(meta_types, i, NULL);
} while (i);
}
- return ret;
+cleanup:
+ return rc;
}
/**
@@ -365,12 +423,13 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly
*
* @param[in] first First sibling to search in.
* @param[in] node Data node instance to check.
+ * @param[in] val_opts Validation options.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *node)
+lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *node, uint32_t val_opts)
{
- struct lyd_node **match_p;
+ struct lyd_node **match_p, *match;
ly_bool fail = 0;
assert(node->flags & LYD_NEW);
@@ -383,7 +442,12 @@ lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *nod
/* find exactly the same next instance using hashes if possible */
if (node->parent && node->parent->children_ht) {
- if (!lyht_find_next(node->parent->children_ht, &node, node->hash, (void **)&match_p)) {
+ lyd_find_sibling_first(first, node, &match);
+ assert(match);
+
+ if (match != node) {
+ fail = 1;
+ } else if (!lyht_find_next(node->parent->children_ht, &node, node->hash, (void **)&match_p)) {
fail = 1;
}
} else {
@@ -405,8 +469,17 @@ lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *nod
}
if (fail) {
- LOGVAL(node->schema->module->ctx, LY_VCODE_DUP, node->schema->name);
- return LY_EVALID;
+ if ((node->schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) && (val_opts & LYD_VALIDATE_OPERATIONAL)) {
+ /* only a warning */
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ LOGWRN(node->schema->module->ctx, "Duplicate instance of \"%s\".", node->schema->name);
+ LOG_LOCBACK(0, 1, 0, 0);
+ } else {
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ LOGVAL(node->schema->module->ctx, LY_VCODE_DUP, node->schema->name);
+ LOG_LOCBACK(0, 1, 0, 0);
+ return LY_EVALID;
+ }
}
return LY_SUCCESS;
}
@@ -530,45 +603,6 @@ lyd_val_has_default(const struct lysc_node *schema)
}
/**
- * @brief Properly delete a node as part of auto-delete validation tasks.
- *
- * @param[in,out] first First sibling, is updated if needed.
- * @param[in] del Node instance to delete.
- * @param[in] mod Module of the siblings, NULL for nested siblings.
- * @param[in,out] node Current iteration node, update it if it is deleted.
- * @param[in,out] diff Validation diff.
- * @return 1 if @p node auto-deleted and updated to its next sibling.
- * @return 0 if @p node was not auto-deleted.
- */
-static ly_bool
-lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *del, const struct lys_module *mod,
- struct lyd_node **node, struct lyd_node **diff)
-{
- struct lyd_node *iter;
- ly_bool node_autodel = 0;
-
- lyd_del_move_root(first, del, mod);
- if (del == *node) {
- *node = (*node)->next;
- node_autodel = 1;
- }
- if (diff) {
- /* add into diff */
- if ((del->schema->nodetype == LYS_CONTAINER) && !(del->schema->flags & LYS_PRESENCE)) {
- /* we do not want to track NP container changes, but remember any removed children */
- LY_LIST_FOR(lyd_child(del), iter) {
- lyd_val_diff_add(iter, LYD_DIFF_OP_DELETE, diff);
- }
- } else {
- lyd_val_diff_add(del, LYD_DIFF_OP_DELETE, diff);
- }
- }
- lyd_free_tree(del);
-
- return node_autodel;
-}
-
-/**
* @brief Auto-delete leaf-list default instances to prevent validation errors.
*
* @param[in,out] first First sibling to search in, is updated if needed.
@@ -606,7 +640,7 @@ lyd_validate_autodel_leaflist_dflt(struct lyd_node **first, struct lyd_node **no
LYD_LIST_FOR_INST_SAFE(*first, schema, next, iter) {
if (iter->flags & LYD_DEFAULT) {
/* default instance found, remove it */
- if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+ if (lyd_validate_autodel_node_del(first, iter, mod, 0, node, NULL, diff)) {
node_autodel = 1;
}
}
@@ -651,7 +685,7 @@ lyd_validate_autodel_cont_leaf_dflt(struct lyd_node **first, struct lyd_node **n
LYD_LIST_FOR_INST_SAFE(*first, schema, next, iter) {
if (iter->flags & LYD_DEFAULT) {
/* default instance, remove it */
- if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+ if (lyd_validate_autodel_node_del(first, iter, mod, 0, node, NULL, diff)) {
node_autodel = 1;
}
}
@@ -661,7 +695,7 @@ lyd_validate_autodel_cont_leaf_dflt(struct lyd_node **first, struct lyd_node **n
LYD_LIST_FOR_INST(*first, schema, iter) {
if ((iter->flags & LYD_DEFAULT) && !(iter->flags & LYD_NEW)) {
/* old default instance, remove it */
- if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+ if (lyd_validate_autodel_node_del(first, iter, mod, 0, node, NULL, diff)) {
node_autodel = 1;
}
break;
@@ -719,7 +753,7 @@ lyd_validate_autodel_case_dflt(struct lyd_node **first, struct lyd_node **node,
if (!iter) {
/* there are only default nodes of the case meaning it does not exist and neither should any default nodes
* of the case, remove this one default node */
- if (lyd_validate_autodel_node_del(first, *node, mod, node, diff)) {
+ if (lyd_validate_autodel_node_del(first, *node, mod, 0, node, NULL, diff)) {
node_autodel = 1;
}
}
@@ -733,40 +767,46 @@ lyd_validate_autodel_case_dflt(struct lyd_node **first, struct lyd_node **node,
* @param[in,out] first First sibling.
* @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
* @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in] val_opts Validation options.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_choice_r(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
- struct lyd_node **diff)
+ uint32_t val_opts, struct lyd_node **diff)
{
+ LY_ERR r, rc = LY_SUCCESS;
const struct lysc_node *snode = NULL;
while (*first && (snode = lys_getnext(snode, sparent, mod ? mod->compiled : NULL, LYS_GETNEXT_WITHCHOICE))) {
/* check case duplicites */
if (snode->nodetype == LYS_CHOICE) {
- LY_CHECK_RET(lyd_validate_cases(first, mod, (struct lysc_node_choice *)snode, diff));
+ r = lyd_validate_cases(first, mod, (struct lysc_node_choice *)snode, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* check for nested choice */
- LY_CHECK_RET(lyd_validate_choice_r(first, snode, mod, diff));
+ r = lyd_validate_choice_r(first, snode, mod, val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
}
- return LY_SUCCESS;
+cleanup:
+ return rc;
}
LY_ERR
lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
- struct lyd_node **diff)
+ uint32_t val_opts, struct lyd_node **diff)
{
- LY_ERR r;
+ LY_ERR r, rc = LY_SUCCESS;
struct lyd_node *node;
const struct lysc_node *last_dflt_schema = NULL;
assert(first && (sparent || mod));
/* validate choices */
- LY_CHECK_RET(lyd_validate_choice_r(first, sparent, mod, diff));
+ r = lyd_validate_choice_r(first, sparent, mod, val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
node = *first;
while (node) {
@@ -797,10 +837,8 @@ lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const
if (node->flags & LYD_NEW) {
/* then check new node instance duplicities */
- LOG_LOCSET(NULL, node, NULL, NULL);
- r = lyd_validate_duplicates(*first, node);
- LOG_LOCBACK(0, 1, 0, 0);
- LY_CHECK_RET(r);
+ r = lyd_validate_duplicates(*first, node, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* this node is valid */
node->flags &= ~LYD_NEW;
@@ -817,7 +855,8 @@ lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const
node = node->next;
}
- return LY_SUCCESS;
+cleanup:
+ return rc;
}
/**
@@ -833,7 +872,7 @@ static LY_ERR
lyd_validate_dummy_when(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
const struct lysc_when **disabled)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR rc = LY_SUCCESS;
struct lyd_node *tree, *dummy = NULL;
uint32_t xp_opts;
@@ -850,8 +889,8 @@ lyd_validate_dummy_when(const struct lyd_node *first, const struct lyd_node *par
}
/* create dummy opaque node */
- ret = lyd_new_opaq((struct lyd_node *)parent, snode->module->ctx, snode->name, NULL, NULL, snode->module->name, &dummy);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lyd_new_opaq((struct lyd_node *)parent, snode->module->ctx, snode->name, NULL, NULL, snode->module->name, &dummy);
+ LY_CHECK_GOTO(rc, cleanup);
/* connect it if needed */
if (!parent) {
@@ -871,20 +910,20 @@ lyd_validate_dummy_when(const struct lyd_node *first, const struct lyd_node *par
}
/* evaluate all when */
- ret = lyd_validate_node_when(tree, dummy, snode, xp_opts, disabled);
- if (ret == LY_EINCOMPLETE) {
+ rc = lyd_validate_node_when(tree, dummy, snode, xp_opts, disabled);
+ if (rc == LY_EINCOMPLETE) {
/* all other when must be resolved by now */
LOGINT(snode->module->ctx);
- ret = LY_EINT;
+ rc = LY_EINT;
goto cleanup;
- } else if (ret) {
+ } else if (rc) {
/* error */
goto cleanup;
}
cleanup:
lyd_free_tree(dummy);
- return ret;
+ return rc;
}
/**
@@ -893,10 +932,12 @@ cleanup:
* @param[in] first First sibling to search in.
* @param[in] parent Data parent.
* @param[in] snode Schema node to validate.
+ * @param[in] val_opts Validation options.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode)
+lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
+ uint32_t val_opts)
{
const struct lysc_when *disabled;
@@ -921,13 +962,26 @@ lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *pare
}
if (!disabled) {
- /* node instance not found */
- if (snode->nodetype == LYS_CHOICE) {
- LOGVAL_APPTAG(snode->module->ctx, "missing-choice", LY_VCODE_NOMAND_CHOIC, snode->name);
+ if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ LOG_LOCSET(parent ? NULL : snode, parent, NULL, NULL);
+ if (snode->nodetype == LYS_CHOICE) {
+ LOGWRN(snode->module->ctx, "Mandatory choice \"%s\" data do not exist.", snode->name);
+ } else {
+ LOGWRN(snode->module->ctx, "Mandatory node \"%s\" instance does not exist.", snode->name);
+ }
+ LOG_LOCBACK(parent ? 0 : 1, parent ? 1 : 0, 0, 0);
} else {
- LOGVAL(snode->module->ctx, LY_VCODE_NOMAND, snode->name);
+ /* node instance not found */
+ LOG_LOCSET(parent ? NULL : snode, parent, NULL, NULL);
+ if (snode->nodetype == LYS_CHOICE) {
+ LOGVAL_APPTAG(snode->module->ctx, "missing-choice", LY_VCODE_NOMAND_CHOIC, snode->name);
+ } else {
+ LOGVAL(snode->module->ctx, LY_VCODE_NOMAND, snode->name);
+ }
+ LOG_LOCBACK(parent ? 0 : 1, parent ? 1 : 0, 0, 0);
+ return LY_EVALID;
}
- return LY_EVALID;
}
return LY_SUCCESS;
@@ -941,16 +995,16 @@ lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *pare
* @param[in] snode Schema node to validate.
* @param[in] min Minimum number of elements, 0 for no restriction.
* @param[in] max Max number of elements, 0 for no restriction.
+ * @param[in] val_opts Validation options.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
- uint32_t min, uint32_t max)
+ uint32_t min, uint32_t max, uint32_t val_opts)
{
uint32_t count = 0;
struct lyd_node *iter;
const struct lysc_when *disabled;
- ly_bool invalid_instance = 0;
assert(min || max);
@@ -967,8 +1021,6 @@ lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent,
}
if (max && (count > max)) {
/* not satisifed */
- LOG_LOCSET(NULL, iter, NULL, NULL);
- invalid_instance = 1;
break;
}
}
@@ -982,20 +1034,42 @@ lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent,
LY_CHECK_RET(lyd_validate_dummy_when(first, parent, snode, &disabled));
}
- if (!disabled) {
- LOGVAL_APPTAG(snode->module->ctx, "too-few-elements", LY_VCODE_NOMIN, snode->name);
- goto failure;
+ if (disabled) {
+ /* satisfied */
+ min = 0;
}
- } else if (max && (count > max)) {
- LOGVAL_APPTAG(snode->module->ctx, "too-many-elements", LY_VCODE_NOMAX, snode->name);
- goto failure;
+ }
+ if (max && (count <= max)) {
+ /* satisfied */
+ max = 0;
}
+ if (min) {
+ if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ LOG_LOCSET(snode, NULL, NULL, NULL);
+ LOGWRN(snode->module->ctx, "Too few \"%s\" instances.", snode->name);
+ LOG_LOCBACK(1, 0, 0, 0);
+ } else {
+ LOG_LOCSET(snode, NULL, NULL, NULL);
+ LOGVAL_APPTAG(snode->module->ctx, "too-few-elements", LY_VCODE_NOMIN, snode->name);
+ LOG_LOCBACK(1, 0, 0, 0);
+ return LY_EVALID;
+ }
+ } else if (max) {
+ if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ LOG_LOCSET(NULL, iter, NULL, NULL);
+ LOGWRN(snode->module->ctx, "Too many \"%s\" instances.", snode->name);
+ LOG_LOCBACK(0, 1, 0, 0);
+ } else {
+ LOG_LOCSET(NULL, iter, NULL, NULL);
+ LOGVAL_APPTAG(snode->module->ctx, "too-many-elements", LY_VCODE_NOMAX, snode->name);
+ LOG_LOCBACK(0, 1, 0, 0);
+ return LY_EVALID;
+ }
+ }
return LY_SUCCESS;
-
-failure:
- LOG_LOCBACK(0, invalid_instance, 0, 0);
- return LY_EVALID;
}
/**
@@ -1035,11 +1109,17 @@ lyd_val_uniq_find_leaf(const struct lysc_node_leaf *uniq_leaf, const struct lyd_
}
/**
+ * @brief Unique list validation callback argument.
+ */
+struct lyd_val_uniq_arg {
+ LY_ARRAY_COUNT_TYPE action; /**< Action to perform - 0 to compare all uniques, n to compare only n-th unique. */
+ uint32_t val_opts; /**< Validation options. */
+};
+
+/**
* @brief Callback for comparing 2 list unique leaf values.
*
* Implementation of ::lyht_value_equal_cb.
- *
- * @param[in] cb_data 0 to compare all uniques, n to compare only n-th unique.
*/
static ly_bool
lyd_val_uniq_list_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
@@ -1049,13 +1129,14 @@ lyd_val_uniq_list_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *c
struct lyd_node *diter, *first, *second;
struct lyd_value *val1, *val2;
char *path1, *path2, *uniq_str, *ptr;
- LY_ARRAY_COUNT_TYPE u, v, action;
+ LY_ARRAY_COUNT_TYPE u, v;
+ struct lyd_val_uniq_arg *arg = cb_data;
+ const uint32_t uniq_err_msg_size = 1024;
assert(val1_p && val2_p);
first = *((struct lyd_node **)val1_p);
second = *((struct lyd_node **)val2_p);
- action = (uintptr_t)cb_data;
assert(first && (first->schema->nodetype == LYS_LIST));
assert(second && (second->schema == first->schema));
@@ -1065,8 +1146,8 @@ lyd_val_uniq_list_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *c
slist = (struct lysc_node_list *)first->schema;
/* compare unique leaves */
- if (action > 0) {
- u = action - 1;
+ if (arg->action > 0) {
+ u = arg->action - 1;
if (u < LY_ARRAY_COUNT(slist->uniques)) {
goto uniquecheck;
}
@@ -1098,13 +1179,12 @@ uniquecheck:
}
}
if (v && (v == LY_ARRAY_COUNT(slist->uniques[u]))) {
- /* all unique leafs are the same in this set, create this nice error */
+ /* all unique leaves are the same in this set, create this nice error */
path1 = lyd_path(first, LYD_PATH_STD, NULL, 0);
path2 = lyd_path(second, LYD_PATH_STD, NULL, 0);
/* use buffer to rebuild the unique string */
-#define UNIQ_BUF_SIZE 1024
- uniq_str = malloc(UNIQ_BUF_SIZE);
+ uniq_str = malloc(uniq_err_msg_size);
uniq_str[0] = '\0';
ptr = uniq_str;
LY_ARRAY_FOR(slist->uniques[u], v) {
@@ -1113,7 +1193,7 @@ uniquecheck:
++ptr;
}
ptr = lysc_path_until((struct lysc_node *)slist->uniques[u][v], &slist->node, LYSC_PATH_LOG,
- ptr, UNIQ_BUF_SIZE - (ptr - uniq_str));
+ ptr, uniq_err_msg_size - (ptr - uniq_str));
if (!ptr) {
/* path will be incomplete, whatever */
break;
@@ -1122,18 +1202,24 @@ uniquecheck:
ptr += strlen(ptr);
}
LOG_LOCSET(NULL, second, NULL, NULL);
- LOGVAL_APPTAG(ctx, "data-not-unique", LY_VCODE_NOUNIQ, uniq_str, path1, path2);
+ if (arg->val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ LOGWRN(ctx, "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\".", uniq_str, path1, path2);
+ } else {
+ LOGVAL_APPTAG(ctx, "data-not-unique", LY_VCODE_NOUNIQ, uniq_str, path1, path2);
+ }
LOG_LOCBACK(0, 1, 0, 0);
free(path1);
free(path2);
free(uniq_str);
-#undef UNIQ_BUF_SIZE
- return 1;
+ if (!(arg->val_opts & LYD_VALIDATE_OPERATIONAL)) {
+ return 1;
+ }
}
- if (action > 0) {
+ if (arg->action > 0) {
/* done */
return 0;
}
@@ -1148,10 +1234,12 @@ uniquecheck:
* @param[in] first First sibling to search in.
* @param[in] snode Schema node to validate.
* @param[in] uniques List unique arrays to validate.
+ * @param[in] val_opts Validation options.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode, const struct lysc_node_leaf ***uniques)
+lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode, const struct lysc_node_leaf ***uniques,
+ uint32_t val_opts)
{
const struct lyd_node *diter;
struct ly_set *set;
@@ -1161,8 +1249,8 @@ lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode,
size_t key_len;
ly_bool dyn;
const void *hash_key;
- void *cb_data;
- struct hash_table **uniqtables = NULL;
+ struct lyd_val_uniq_arg arg, *args = NULL;
+ struct ly_ht **uniqtables = NULL;
struct lyd_value *val;
struct ly_ctx *ctx = snode->module->ctx;
@@ -1179,7 +1267,9 @@ lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode,
if (set->count == 2) {
/* simple comparison */
- if (lyd_val_uniq_list_equal(&set->objs[0], &set->objs[1], 0, (void *)0)) {
+ arg.action = 0;
+ arg.val_opts = val_opts;
+ if (lyd_val_uniq_list_equal(&set->objs[0], &set->objs[1], 0, &arg)) {
/* instance duplication */
ret = LY_EVALID;
goto cleanup;
@@ -1187,12 +1277,14 @@ lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode,
} else if (set->count > 2) {
/* use hashes for comparison */
uniqtables = malloc(LY_ARRAY_COUNT(uniques) * sizeof *uniqtables);
- LY_CHECK_ERR_GOTO(!uniqtables, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ args = malloc(LY_ARRAY_COUNT(uniques) * sizeof *args);
+ LY_CHECK_ERR_GOTO(!uniqtables || !args, LOGMEM(ctx); ret = LY_EMEM, cleanup);
x = LY_ARRAY_COUNT(uniques);
for (v = 0; v < x; v++) {
- cb_data = (void *)(uintptr_t)(v + 1L);
+ args[v].action = v + 1;
+ args[v].val_opts = val_opts;
uniqtables[v] = lyht_new(lyht_get_fixed_size(set->count), sizeof(struct lyd_node *),
- lyd_val_uniq_list_equal, cb_data, 0);
+ lyd_val_uniq_list_equal, &args[v], 0);
LY_CHECK_ERR_GOTO(!uniqtables[v], LOGMEM(ctx); ret = LY_EMEM, cleanup);
}
@@ -1215,7 +1307,7 @@ lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode,
/* get hash key */
hash_key = val->realtype->plugin->print(NULL, val, LY_VALUE_LYB, NULL, &dyn, &key_len);
- hash = dict_hash_multi(hash, hash_key, key_len);
+ hash = lyht_hash_multi(hash, hash_key, key_len);
if (dyn) {
free((void *)hash_key);
}
@@ -1226,7 +1318,7 @@ lyd_validate_unique(const struct lyd_node *first, const struct lysc_node *snode,
}
/* finish the hash value */
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(hash, NULL, 0);
/* insert into the hashtable */
ret = lyht_insert(uniqtables[u], &set->objs[i], hash, NULL);
@@ -1246,9 +1338,10 @@ cleanup:
/* failed when allocating uniquetables[j], following j are not allocated */
break;
}
- lyht_free(uniqtables[v]);
+ lyht_free(uniqtables[v], NULL);
}
free(uniqtables);
+ free(args);
return ret;
}
@@ -1268,7 +1361,7 @@ static LY_ERR
lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_node *parent,
const struct lysc_node *sparent, const struct lysc_module *mod, uint32_t val_opts, uint32_t int_opts)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
const struct lysc_node *snode = NULL, *scase;
struct lysc_node_list *slist;
struct lysc_node_leaflist *sllist;
@@ -1282,34 +1375,32 @@ lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_no
continue;
}
- LOG_LOCSET(snode, NULL, NULL, NULL);
-
/* check min-elements and max-elements */
if (snode->nodetype == LYS_LIST) {
slist = (struct lysc_node_list *)snode;
if (slist->min || slist->max) {
- ret = lyd_validate_minmax(first, parent, snode, slist->min, slist->max);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_validate_minmax(first, parent, snode, slist->min, slist->max, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
} else if (snode->nodetype == LYS_LEAFLIST) {
sllist = (struct lysc_node_leaflist *)snode;
if (sllist->min || sllist->max) {
- ret = lyd_validate_minmax(first, parent, snode, sllist->min, sllist->max);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_validate_minmax(first, parent, snode, sllist->min, sllist->max, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
} else if (snode->flags & LYS_MAND_TRUE) {
/* check generic mandatory existence */
- ret = lyd_validate_mandatory(first, parent, snode);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_validate_mandatory(first, parent, snode, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
/* check unique */
if (snode->nodetype == LYS_LIST) {
slist = (struct lysc_node_list *)snode;
if (slist->uniques) {
- ret = lyd_validate_unique(first, snode, (const struct lysc_node_leaf ***)slist->uniques);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_validate_unique(first, snode, (const struct lysc_node_leaf ***)slist->uniques, val_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
}
@@ -1318,21 +1409,16 @@ lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_no
LY_LIST_FOR(lysc_node_child(snode), scase) {
if (lys_getnext_data(NULL, first, NULL, scase, NULL)) {
/* validate only this case */
- ret = lyd_validate_siblings_schema_r(first, parent, scase, mod, val_opts, int_opts);
- LY_CHECK_GOTO(ret, error);
+ r = lyd_validate_siblings_schema_r(first, parent, scase, mod, val_opts, int_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
break;
}
}
}
-
- LOG_LOCBACK(1, 0, 0, 0);
}
- return LY_SUCCESS;
-
-error:
- LOG_LOCBACK(1, 0, 0, 0);
- return ret;
+cleanup:
+ return rc;
}
/**
@@ -1348,7 +1434,9 @@ lyd_validate_obsolete(const struct lyd_node *node)
snode = node->schema;
do {
if (snode->flags & LYS_STATUS_OBSLT) {
+ LOG_LOCSET(NULL, node, NULL, NULL);
LOGWRN(snode->module->ctx, "Obsolete schema node \"%s\" instantiated in data.", snode->name);
+ LOG_LOCBACK(0, 1, 0, 0);
break;
}
@@ -1360,14 +1448,15 @@ lyd_validate_obsolete(const struct lyd_node *node)
* @brief Validate must conditions of a data node.
*
* @param[in] node Node to validate.
+ * @param[in] val_opts Validation options.
* @param[in] int_opts Internal parser options.
* @param[in] xpath_options Additional XPath options to use.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_must(const struct lyd_node *node, uint32_t int_opts, uint32_t xpath_options)
+lyd_validate_must(const struct lyd_node *node, uint32_t val_opts, uint32_t int_opts, uint32_t xpath_options)
{
- LY_ERR ret;
+ LY_ERR r, rc = LY_SUCCESS;
struct lyxp_set xp_set;
struct lysc_must *musts;
const struct lyd_node *tree;
@@ -1403,30 +1492,46 @@ lyd_validate_must(const struct lyd_node *node, uint32_t int_opts, uint32_t xpath
memset(&xp_set, 0, sizeof xp_set);
/* evaluate must */
- ret = lyxp_eval(LYD_CTX(node), musts[u].cond, node->schema->module, LY_VALUE_SCHEMA_RESOLVED,
+ r = lyxp_eval(LYD_CTX(node), musts[u].cond, node->schema->module, LY_VALUE_SCHEMA_RESOLVED,
musts[u].prefixes, node, node, tree, NULL, &xp_set, LYXP_SCHEMA | xpath_options);
- if (ret == LY_EINCOMPLETE) {
- LOGINT_RET(LYD_CTX(node));
- } else if (ret) {
- return ret;
+ if (r == LY_EINCOMPLETE) {
+ LOGERR(LYD_CTX(node), LY_EINCOMPLETE,
+ "Must \"%s\" depends on a node with a when condition, which has not been evaluated.", musts[u].cond->expr);
}
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
/* check the result */
lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
if (!xp_set.val.bln) {
- /* use specific error information */
- emsg = musts[u].emsg;
- eapptag = musts[u].eapptag ? musts[u].eapptag : "must-violation";
- if (emsg) {
- LOGVAL_APPTAG(LYD_CTX(node), eapptag, LYVE_DATA, "%s", emsg);
+ if (val_opts & LYD_VALIDATE_OPERATIONAL) {
+ /* only a warning */
+ emsg = musts[u].emsg;
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ if (emsg) {
+ LOGWRN(LYD_CTX(node), "%s", emsg);
+ } else {
+ LOGWRN(LYD_CTX(node), "Must condition \"%s\" not satisfied.", musts[u].cond->expr);
+ }
+ LOG_LOCBACK(0, 1, 0, 0);
} else {
- LOGVAL_APPTAG(LYD_CTX(node), eapptag, LY_VCODE_NOMUST, musts[u].cond->expr);
+ /* use specific error information */
+ emsg = musts[u].emsg;
+ eapptag = musts[u].eapptag ? musts[u].eapptag : "must-violation";
+ LOG_LOCSET(NULL, node, NULL, NULL);
+ if (emsg) {
+ LOGVAL_APPTAG(LYD_CTX(node), eapptag, LYVE_DATA, "%s", emsg);
+ } else {
+ LOGVAL_APPTAG(LYD_CTX(node), eapptag, LY_VCODE_NOMUST, musts[u].cond->expr);
+ }
+ LOG_LOCBACK(0, 1, 0, 0);
+ r = LY_EVALID;
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
- return LY_EVALID;
}
}
- return LY_SUCCESS;
+cleanup:
+ return rc;
}
/**
@@ -1445,29 +1550,25 @@ static LY_ERR
lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *sparent,
const struct lys_module *mod, uint32_t val_opts, uint32_t int_opts, uint32_t must_xp_opts)
{
- LY_ERR r;
+ LY_ERR r, rc = LY_SUCCESS;
const char *innode;
- struct lyd_node *next = NULL, *node;
+ struct lyd_node *node;
/* validate all restrictions of nodes themselves */
- LY_LIST_FOR_SAFE(first, next, node) {
+ LY_LIST_FOR(first, node) {
if (node->flags & LYD_EXT) {
/* ext instance data should have already been validated */
continue;
}
- LOG_LOCSET(node->schema, node, NULL, NULL);
-
/* opaque data */
if (!node->schema) {
r = lyd_parse_opaq_error(node);
- LOG_LOCBACK(0, 1, 0, 0);
- return r;
+ goto next_iter;
}
if (!node->parent && mod && (lyd_owner_module(node) != mod)) {
/* all top-level data from this module checked */
- LOG_LOCBACK(1, 1, 0, 0);
break;
}
@@ -1487,43 +1588,47 @@ lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, cons
innode = "notification";
}
if (innode) {
+ LOG_LOCSET(NULL, node, NULL, NULL);
LOGVAL(LYD_CTX(node), LY_VCODE_UNEXPNODE, innode, node->schema->name);
- LOG_LOCBACK(1, 1, 0, 0);
- return LY_EVALID;
+ LOG_LOCBACK(0, 1, 0, 0);
+ r = LY_EVALID;
+ goto next_iter;
}
/* obsolete data */
lyd_validate_obsolete(node);
/* node's musts */
- if ((r = lyd_validate_must(node, int_opts, must_xp_opts))) {
- LOG_LOCBACK(1, 1, 0, 0);
- return r;
+ if ((r = lyd_validate_must(node, val_opts, int_opts, must_xp_opts))) {
+ goto next_iter;
}
/* node value was checked by plugins */
- /* next iter */
- LOG_LOCBACK(1, 1, 0, 0);
+next_iter:
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
/* validate schema-based restrictions */
- LY_CHECK_RET(lyd_validate_siblings_schema_r(first, parent, sparent, mod ? mod->compiled : NULL, val_opts, int_opts));
+ r = lyd_validate_siblings_schema_r(first, parent, sparent, mod ? mod->compiled : NULL, val_opts, int_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
LY_LIST_FOR(first, node) {
- if (!node->parent && mod && (lyd_owner_module(node) != mod)) {
- /* all top-level data from this module checked */
+ if (!node->schema || (!node->parent && mod && (lyd_owner_module(node) != mod))) {
+ /* only opaque data following or all top-level data from this module checked */
break;
}
/* validate all children recursively */
- LY_CHECK_RET(lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, val_opts, int_opts, must_xp_opts));
+ r = lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, val_opts, int_opts, must_xp_opts);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* set default for containers */
lyd_cont_set_dflt(node);
}
- return LY_SUCCESS;
+cleanup:
+ return rc;
}
/**
@@ -1609,18 +1714,20 @@ lyd_validate_node_ext(struct lyd_node *node, struct ly_set *ext_node)
* @param[in,out] meta_types Set for unres metadata types.
* @param[in,out] ext_node Set with nodes with extensions to validate.
* @param[in,out] ext_val Set for parsed extension data to validate.
- * @param[in] impl_opts Implicit options, see @ref implicitoptions.
+ * @param[in] val_opts Validation options.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_set *node_types,
- struct ly_set *meta_types, struct ly_set *ext_node, struct ly_set *ext_val, uint32_t impl_opts,
+ struct ly_set *meta_types, struct ly_set *ext_node, struct ly_set *ext_val, uint32_t val_opts,
struct lyd_node **diff)
{
+ LY_ERR r, rc = LY_SUCCESS;
const struct lyd_meta *meta;
const struct lysc_type *type;
struct lyd_node *node;
+ uint32_t impl_opts;
LYD_TREE_DFS_BEGIN(root, node) {
if (node->flags & LYD_EXT) {
@@ -1637,34 +1744,48 @@ lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_
lyplg_ext_get_storage(meta->annotation, LY_STMT_TYPE, sizeof type, (const void **)&type);
if (type->plugin->validate) {
/* metadata type resolution */
- LY_CHECK_RET(ly_set_add(meta_types, (void *)meta, 1, NULL));
+ r = ly_set_add(meta_types, (void *)meta, 1, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
}
if ((node->schema->nodetype & LYD_NODE_TERM) && ((struct lysc_node_leaf *)node->schema)->type->plugin->validate) {
/* node type resolution */
- LY_CHECK_RET(ly_set_add(node_types, (void *)node, 1, NULL));
+ r = ly_set_add(node_types, (void *)node, 1, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
} else if (node->schema->nodetype & LYD_NODE_INNER) {
/* new node validation, autodelete */
- LY_CHECK_RET(lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, diff));
+ r = lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* add nested defaults */
- LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL, NULL, NULL, impl_opts, diff));
+ impl_opts = 0;
+ if (val_opts & LYD_VALIDATE_NO_STATE) {
+ impl_opts |= LYD_IMPLICIT_NO_STATE;
+ }
+ if (val_opts & LYD_VALIDATE_NO_DEFAULTS) {
+ impl_opts |= LYD_IMPLICIT_NO_DEFAULTS;
+ }
+ r = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL, NULL, NULL, impl_opts, diff);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
if (lysc_has_when(node->schema)) {
/* when evaluation */
- LY_CHECK_RET(ly_set_add(node_when, (void *)node, 1, NULL));
+ r = ly_set_add(node_when, (void *)node, 1, NULL);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
/* store for ext instance node validation, if needed */
- LY_CHECK_RET(lyd_validate_node_ext(node, ext_node));
+ r = lyd_validate_node_ext(node, ext_node);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
next_node:
LYD_TREE_DFS_END(root, node);
}
- return LY_SUCCESS;
+cleanup:
+ return rc;
}
LY_ERR
@@ -1672,11 +1793,11 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR r, rc = LY_SUCCESS;
struct lyd_node *first, *next, **first2, *iter;
const struct lys_module *mod;
struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_node = {0}, ext_val = {0};
- uint32_t i = 0;
+ uint32_t i = 0, impl_opts;
assert(tree && ctx);
assert((node_when_p && node_types_p && meta_types_p && ext_node_p && ext_val_p) ||
@@ -1708,15 +1829,21 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
}
/* validate new top-level nodes of this module, autodelete */
- ret = lyd_validate_new(first2, NULL, mod, diff);
- LY_CHECK_GOTO(ret, cleanup);
+ r = lyd_validate_new(first2, *first2 ? lysc_data_parent((*first2)->schema) : NULL, mod, val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* add all top-level defaults for this module, if going to validate subtree, do not add into unres sets
* (lyd_validate_subtree() adds all the nodes in that case) */
- ret = lyd_new_implicit_r(NULL, first2, NULL, mod, validate_subtree ? NULL : node_when_p,
- validate_subtree ? NULL : node_types_p, validate_subtree ? NULL : ext_node_p,
- (val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, diff);
- LY_CHECK_GOTO(ret, cleanup);
+ impl_opts = 0;
+ if (val_opts & LYD_VALIDATE_NO_STATE) {
+ impl_opts |= LYD_IMPLICIT_NO_STATE;
+ }
+ if (val_opts & LYD_VALIDATE_NO_DEFAULTS) {
+ impl_opts |= LYD_IMPLICIT_NO_DEFAULTS;
+ }
+ r = lyd_new_implicit_r(lyd_parent(*first2), first2, NULL, mod, validate_subtree ? NULL : node_when_p,
+ validate_subtree ? NULL : node_types_p, validate_subtree ? NULL : ext_node_p, impl_opts, diff);
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
/* our first module node pointer may no longer be the first */
first = *first2;
@@ -1734,20 +1861,22 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
break;
}
- ret = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p,
- (val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, diff);
- LY_CHECK_GOTO(ret, cleanup);
+ r = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p,
+ val_opts, diff);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
}
/* finish incompletely validated terminal values/attributes and when conditions */
- ret = lyd_validate_unres(first2, mod, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p,
+ r = lyd_validate_unres(first2, mod, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p,
ext_node_p, ext_val_p, val_opts, diff);
- LY_CHECK_GOTO(ret, cleanup);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
- /* perform final validation that assumes the data tree is final */
- ret = lyd_validate_final_r(*first2, NULL, NULL, mod, val_opts, 0, 0);
- LY_CHECK_GOTO(ret, cleanup);
+ if (!(val_opts & LYD_VALIDATE_NOT_FINAL)) {
+ /* perform final validation that assumes the data tree is final */
+ r = lyd_validate_final_r(*first2, NULL, NULL, mod, val_opts, 0, 0);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
+ }
}
cleanup:
@@ -1756,7 +1885,7 @@ cleanup:
ly_set_erase(&meta_types, NULL);
ly_set_erase(&ext_node, free);
ly_set_erase(&ext_val, free);
- return ret;
+ return rc;
}
LIBYANG_API_DEF LY_ERR
@@ -1777,14 +1906,36 @@ lyd_validate_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t val_
LIBYANG_API_DEF LY_ERR
lyd_validate_module(struct lyd_node **tree, const struct lys_module *module, uint32_t val_opts, struct lyd_node **diff)
{
- LY_CHECK_ARG_RET(NULL, tree, *tree || module, LY_EINVAL);
- LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, module ? module->ctx : NULL, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, tree, module, !(val_opts & LYD_VALIDATE_PRESENT), LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, module->ctx, LY_EINVAL);
if (diff) {
*diff = NULL;
}
- return lyd_validate(tree, module, (*tree) ? LYD_CTX(*tree) : module->ctx, val_opts, 1, NULL, NULL, NULL, NULL, NULL,
- diff);
+ return lyd_validate(tree, module, module->ctx, val_opts, 1, NULL, NULL, NULL, NULL, NULL, diff);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyd_validate_module_final(struct lyd_node *tree, const struct lys_module *module, uint32_t val_opts)
+{
+ LY_ERR r, rc = LY_SUCCESS;
+ struct lyd_node *first;
+ const struct lys_module *mod;
+ uint32_t i = 0;
+
+ LY_CHECK_ARG_RET(NULL, module, !(val_opts & (LYD_VALIDATE_PRESENT | LYD_VALIDATE_NOT_FINAL)), LY_EINVAL);
+ LY_CHECK_CTX_EQUAL_RET(LYD_CTX(tree), module->ctx, LY_EINVAL);
+
+ /* module is unchanged but we need to get the first module data node */
+ mod = lyd_mod_next_module(tree, module, module->ctx, &i, &first);
+ assert(mod);
+
+ /* perform final validation that assumes the data tree is final */
+ r = lyd_validate_final_r(first, NULL, NULL, mod, val_opts, 0, 0);
+ LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
+
+cleanup:
+ return rc;
}
/**
@@ -1895,14 +2046,12 @@ _lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struc
op_sibling_after = op_subtree->next;
op_parent = lyd_parent(op_subtree);
- lyd_unlink_tree(op_subtree);
+ lyd_unlink(op_subtree);
lyd_insert_node(tree_parent, &tree_sibling, op_subtree, 0);
if (!dep_tree) {
dep_tree = tree_sibling;
}
- LOG_LOCSET(NULL, op_node, NULL, NULL);
-
if (int_opts & LYD_INTOPT_REPLY) {
/* add output children defaults */
rc = lyd_new_implicit_r(op_node, lyd_node_child_p(op_node), NULL, NULL, node_when_p, node_types_p,
@@ -1931,21 +2080,21 @@ _lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struc
/* perform final validation of the operation/notification */
lyd_validate_obsolete(op_node);
- LY_CHECK_GOTO(rc = lyd_validate_must(op_node, int_opts, LYXP_IGNORE_WHEN), cleanup);
+ LY_CHECK_GOTO(rc = lyd_validate_must(op_node, 0, int_opts, LYXP_IGNORE_WHEN), cleanup);
/* final validation of all the descendants */
rc = lyd_validate_final_r(lyd_child(op_node), op_node, op_node->schema, NULL, 0, int_opts, LYXP_IGNORE_WHEN);
LY_CHECK_GOTO(rc, cleanup);
cleanup:
- LOG_LOCBACK(0, 1, 0, 0);
-
/* restore operation tree */
- lyd_unlink_tree(op_subtree);
+ lyd_unlink(op_subtree);
if (op_sibling_before) {
lyd_insert_after_node(op_sibling_before, op_subtree);
+ lyd_insert_hash(op_subtree);
} else if (op_sibling_after) {
lyd_insert_before_node(op_sibling_after, op_subtree);
+ lyd_insert_hash(op_subtree);
} else if (op_parent) {
lyd_insert_node(op_parent, NULL, op_subtree, 0);
}
diff --git a/src/validation.h b/src/validation.h
index c9f5da0..db24ef6 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -69,11 +69,12 @@ LY_ERR lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod,
* @param[in,out] first First sibling.
* @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
* @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in] val_opts Validation options.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
LY_ERR lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
- struct lyd_node **diff);
+ uint32_t val_opts, struct lyd_node **diff);
/**
* @brief Validate data node with an extension instance, if any, by storing it in its unres set.
diff --git a/src/xml.c b/src/xml.c
index 97e6abb..2bdbdf3 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -222,17 +222,29 @@ cleanup:
void
lyxml_ns_rm(struct lyxml_ctx *xmlctx)
{
- for (uint32_t u = xmlctx->ns.count - 1; u + 1 > 0; --u) {
- if (((struct lyxml_ns *)xmlctx->ns.objs[u])->depth != xmlctx->elements.count + 1) {
+ struct lyxml_ns *ns;
+ uint32_t u;
+
+ if (!xmlctx->ns.count) {
+ return;
+ }
+
+ u = xmlctx->ns.count;
+ do {
+ --u;
+ ns = (struct lyxml_ns *)xmlctx->ns.objs[u];
+
+ if (ns->depth != xmlctx->elements.count + 1) {
/* we are done, the namespaces from a single element are supposed to be together */
break;
}
+
/* remove the ns structure */
- free(((struct lyxml_ns *)xmlctx->ns.objs[u])->prefix);
- free(((struct lyxml_ns *)xmlctx->ns.objs[u])->uri);
- free(xmlctx->ns.objs[u]);
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
--xmlctx->ns.count;
- }
+ } while (u);
if (!xmlctx->ns.count) {
/* cleanup the xmlctx's namespaces storage */
@@ -244,9 +256,17 @@ const struct lyxml_ns *
lyxml_ns_get(const struct ly_set *ns_set, const char *prefix, size_t prefix_len)
{
struct lyxml_ns *ns;
+ uint32_t u;
- for (uint32_t u = ns_set->count - 1; u + 1 > 0; --u) {
+ if (!ns_set->count) {
+ return NULL;
+ }
+
+ u = ns_set->count;
+ do {
+ --u;
ns = (struct lyxml_ns *)ns_set->objs[u];
+
if (prefix && prefix_len) {
if (ns->prefix && !ly_strncmp(ns->prefix, prefix, prefix_len)) {
return ns;
@@ -255,7 +275,7 @@ lyxml_ns_get(const struct ly_set *ns_set, const char *prefix, size_t prefix_len)
/* default namespace */
return ns;
}
- }
+ } while (u);
return NULL;
}
@@ -415,12 +435,11 @@ static LY_ERR
lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *length, ly_bool *ws_only, ly_bool *dynamic)
{
const struct ly_ctx *ctx = xmlctx->ctx; /* shortcut */
- const char *in = xmlctx->in->current, *start, *in_aux;
+ const char *in = xmlctx->in->current, *start, *in_aux, *p;
char *buf = NULL;
size_t offset; /* read offset in input buffer */
size_t len; /* length of the output string (write offset in output buffer) */
size_t size = 0; /* size of the output buffer */
- void *p;
uint32_t n;
size_t u;
ly_bool ws = 1;
@@ -467,7 +486,7 @@ lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *
}
offset = 0;
} else {
- p = (void *)&in[offset - 1];
+ p = &in[offset - 1];
/* character reference */
++offset;
if (isdigit(in[offset])) {
@@ -486,7 +505,7 @@ lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *
n = (LY_BASE_HEX * n) + u;
}
} else {
- LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\".", 12, p);
+ LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.12s\".", p);
goto error;
}
@@ -497,7 +516,7 @@ lyxml_parse_value(struct lyxml_ctx *xmlctx, char endchar, char **value, size_t *
}
++offset;
if (ly_pututf8(&buf[len], n, &u)) {
- LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).", 12, p, n);
+ LOGVAL(ctx, LYVE_SYNTAX, "Invalid character reference \"%.12s\" (0x%08x).", p, n);
goto error;
}
len += u;
diff --git a/src/xpath.c b/src/xpath.c
index ab7921e..bc5f695 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -43,8 +43,6 @@
#include "tree_schema_internal.h"
#include "xml.h"
-static LY_ERR set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
- enum lyxp_axis axis, uint32_t *index_p);
static LY_ERR reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth);
static LY_ERR eval_expr_select(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_expr_type etype,
struct lyxp_set *set, uint32_t options);
@@ -123,6 +121,8 @@ lyxp_token2str(enum lyxp_token tok)
return "@";
case LYXP_TOKEN_COMMA:
return ",";
+ case LYXP_TOKEN_DCOLON:
+ return "::";
case LYXP_TOKEN_NAMETEST:
return "NameTest";
case LYXP_TOKEN_NODETYPE:
@@ -147,6 +147,8 @@ lyxp_token2str(enum lyxp_token tok)
return "Operator(Path)";
case LYXP_TOKEN_OPER_RPATH:
return "Operator(Recursive Path)";
+ case LYXP_TOKEN_AXISNAME:
+ return "AxisName";
case LYXP_TOKEN_LITERAL:
return "Literal";
case LYXP_TOKEN_NUMBER:
@@ -217,16 +219,53 @@ str2axis(const char *str, uint32_t str_len)
}
/**
- * @brief Print the whole expression \p exp to debug output.
+ * @brief Append a string to a dynamic string variable.
+ *
+ * @param[in,out] str String to use.
+ * @param[in,out] size String size.
+ * @param[in,out] used String used size excluding terminating zero.
+ * @param[in] format Message format.
+ * @param[in] ... Message format arguments.
+ */
+static void
+print_expr_str(char **str, size_t *size, size_t *used, const char *format, ...)
+{
+ int p;
+ va_list ap;
+
+ va_start(ap, format);
+
+ /* try to append the string */
+ p = vsnprintf(*str ? *str + *used : NULL, *size - *used, format, ap);
+
+ if ((unsigned)p >= *size - *used) {
+ /* realloc */
+ *str = ly_realloc(*str, *size + p + 1);
+ *size += p + 1;
+
+ /* restart ap */
+ va_end(ap);
+ va_start(ap, format);
+
+ /* print */
+ p = vsnprintf(*str + *used, *size - *used, format, ap);
+ }
+
+ *used += p;
+ va_end(ap);
+}
+
+/**
+ * @brief Print the whole expression @p exp to debug output.
*
* @param[in] exp Expression to use.
*/
static void
print_expr_struct_debug(const struct lyxp_expr *exp)
{
-#define MSG_BUFFER_SIZE 128
- char tmp[MSG_BUFFER_SIZE];
+ char *buf = NULL;
uint32_t i, j;
+ size_t size = 0, used = 0;
if (!exp || (ly_ll < LY_LLDBG)) {
return;
@@ -234,18 +273,21 @@ print_expr_struct_debug(const struct lyxp_expr *exp)
LOGDBG(LY_LDGXPATH, "expression \"%s\":", exp->expr);
for (i = 0; i < exp->used; ++i) {
- sprintf(tmp, "\ttoken %s, in expression \"%.*s\"", lyxp_token2str(exp->tokens[i]), exp->tok_len[i],
- &exp->expr[exp->tok_pos[i]]);
+ print_expr_str(&buf, &size, &used, "\ttoken %s, in expression \"%.*s\"",
+ lyxp_token2str(exp->tokens[i]), exp->tok_len[i], &exp->expr[exp->tok_pos[i]]);
+
if (exp->repeat && exp->repeat[i]) {
- sprintf(tmp + strlen(tmp), " (repeat %d", exp->repeat[i][0]);
+ print_expr_str(&buf, &size, &used, " (repeat %d", exp->repeat[i][0]);
for (j = 1; exp->repeat[i][j]; ++j) {
- sprintf(tmp + strlen(tmp), ", %d", exp->repeat[i][j]);
+ print_expr_str(&buf, &size, &used, ", %d", exp->repeat[i][j]);
}
- strcat(tmp, ")");
+ print_expr_str(&buf, &size, &used, ")");
}
- LOGDBG(LY_LDGXPATH, tmp);
+ LOGDBG(LY_LDGXPATH, buf);
+ used = 0;
}
-#undef MSG_BUFFER_SIZE
+
+ free(buf);
}
#ifndef NDEBUG
@@ -284,18 +326,19 @@ print_set_debug(struct lyxp_set *set)
LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ROOT CONFIG", i + 1, item->pos);
break;
case LYXP_NODE_ELEM:
- if ((item->node->schema->nodetype == LYS_LIST) && (lyd_child(item->node)->schema->nodetype == LYS_LEAF)) {
+ if (item->node->schema && (item->node->schema->nodetype == LYS_LIST) &&
+ (lyd_child(item->node)->schema->nodetype == LYS_LEAF)) {
LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s (1st child val: %s)", i + 1, item->pos,
item->node->schema->name, lyd_get_value(lyd_child(item->node)));
- } else if (item->node->schema->nodetype == LYS_LEAFLIST) {
+ } else if ((!item->node->schema && !lyd_child(item->node)) || (item->node->schema->nodetype == LYS_LEAFLIST)) {
LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s (val: %s)", i + 1, item->pos,
- item->node->schema->name, lyd_get_value(item->node));
+ LYD_NAME(item->node), lyd_get_value(item->node));
} else {
- LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s", i + 1, item->pos, item->node->schema->name);
+ LOGDBG(LY_LDGXPATH, "\t%d (pos %u): ELEM %s", i + 1, item->pos, LYD_NAME(item->node));
}
break;
case LYXP_NODE_TEXT:
- if (item->node->schema->nodetype & LYS_ANYDATA) {
+ if (item->node->schema && (item->node->schema->nodetype & LYS_ANYDATA)) {
LOGDBG(LY_LDGXPATH, "\t%d (pos %u): TEXT <%s>", i + 1, item->pos,
item->node->schema->nodetype == LYS_ANYXML ? "anyxml" : "anydata");
} else {
@@ -416,13 +459,14 @@ cast_string_recursive(const struct lyd_node *node, struct lyxp_set *set, uint32_
{
char *buf, *line, *ptr = NULL;
const char *value_str;
+ uint16_t nodetype;
const struct lyd_node *child;
enum lyxp_node_type child_type;
struct lyd_node *tree;
struct lyd_node_any *any;
LY_ERR rc;
- if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && node && (node->schema->flags & LYS_CONFIG_R)) {
+ if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && node && node->schema && (node->schema->flags & LYS_CONFIG_R)) {
return LY_SUCCESS;
}
@@ -448,7 +492,15 @@ cast_string_recursive(const struct lyd_node *node, struct lyxp_set *set, uint32_
--indent;
} else {
- switch (node->schema->nodetype) {
+ if (node->schema) {
+ nodetype = node->schema->nodetype;
+ } else if (lyd_child(node)) {
+ nodetype = LYS_CONTAINER;
+ } else {
+ nodetype = LYS_LEAF;
+ }
+
+ switch (nodetype) {
case LYS_CONTAINER:
case LYS_LIST:
case LYS_RPC:
@@ -691,29 +743,29 @@ set_insert_node_hash(struct lyxp_set *set, struct lyd_node *node, enum lyxp_node
hnode.node = set->val.nodes[i].node;
hnode.type = set->val.nodes[i].type;
- hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
- hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
r = lyht_insert(set->ht, &hnode, hash, NULL);
assert(!r);
(void)r;
- if (hnode.node == node) {
+ if ((hnode.node == node) && (hnode.type == type)) {
/* it was just added, do not add it twice */
- node = NULL;
+ return;
}
}
}
- if (set->ht && node) {
+ if (set->ht) {
/* add the new node into hash table */
hnode.node = node;
hnode.type = type;
- hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
- hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
r = lyht_insert(set->ht, &hnode, hash, NULL);
assert(!r);
@@ -739,16 +791,16 @@ set_remove_node_hash(struct lyxp_set *set, struct lyd_node *node, enum lyxp_node
hnode.node = node;
hnode.type = type;
- hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
- hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
r = lyht_remove(set->ht, &hnode, hash);
assert(!r);
(void)r;
if (!set->ht->used) {
- lyht_free(set->ht);
+ lyht_free(set->ht, NULL);
set->ht = NULL;
}
}
@@ -772,9 +824,9 @@ set_dup_node_hash_check(const struct lyxp_set *set, struct lyd_node *node, enum
hnode.node = node;
hnode.type = type;
- hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
- hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
if (!lyht_find(set->ht, &hnode, hash, (void **)&match_p)) {
if ((skip_idx > -1) && (set->val.nodes[skip_idx].node == match_p->node) && (set->val.nodes[skip_idx].type == match_p->type)) {
@@ -802,10 +854,10 @@ lyxp_set_free_content(struct lyxp_set *set)
if (set->type == LYXP_SET_NODE_SET) {
free(set->val.nodes);
- lyht_free(set->ht);
+ lyht_free(set->ht, NULL);
} else if (set->type == LYXP_SET_SCNODE_SET) {
free(set->val.scnodes);
- lyht_free(set->ht);
+ lyht_free(set->ht, NULL);
} else {
if (set->type == LYXP_SET_STRING) {
free(set->val.str);
@@ -847,18 +899,21 @@ static void
set_init(struct lyxp_set *new, const struct lyxp_set *set)
{
memset(new, 0, sizeof *new);
- if (set) {
- new->non_child_axis = set->non_child_axis;
- new->ctx = set->ctx;
- new->cur_node = set->cur_node;
- new->root_type = set->root_type;
- new->context_op = set->context_op;
- new->tree = set->tree;
- new->cur_mod = set->cur_mod;
- new->format = set->format;
- new->prefix_data = set->prefix_data;
- new->vars = set->vars;
+ if (!set) {
+ return;
}
+
+ new->non_child_axis = set->non_child_axis;
+ new->not_found = set->not_found;
+ new->ctx = set->ctx;
+ new->cur_node = set->cur_node;
+ new->root_type = set->root_type;
+ new->context_op = set->context_op;
+ new->tree = set->tree;
+ new->cur_mod = set->cur_mod;
+ new->format = set->format;
+ new->prefix_data = set->prefix_data;
+ new->vars = set->vars;
}
/**
@@ -889,7 +944,7 @@ set_copy(struct lyxp_set *set)
(set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_START)) {
uint32_t idx;
- LY_CHECK_ERR_RET(set_scnode_insert_node(ret, set->val.scnodes[i].scnode, set->val.scnodes[i].type,
+ LY_CHECK_ERR_RET(lyxp_set_scnode_insert_node(ret, set->val.scnodes[i].scnode, set->val.scnodes[i].type,
set->val.scnodes[i].axis, &idx), lyxp_set_free(ret), NULL);
/* coverity seems to think scnodes can be NULL */
if (!ret->val.scnodes) {
@@ -989,11 +1044,7 @@ set_fill_set(struct lyxp_set *trg, const struct lyxp_set *src)
return;
}
- if (trg->type == LYXP_SET_NODE_SET) {
- free(trg->val.nodes);
- } else if (trg->type == LYXP_SET_STRING) {
- free(trg->val.str);
- }
+ lyxp_set_free_content(trg);
set_init(trg, src);
if (src->type == LYXP_SET_SCNODE_SET) {
@@ -1314,19 +1365,8 @@ set_insert_node(struct lyxp_set *set, const struct lyd_node *node, uint32_t pos,
set_insert_node_hash(set, (struct lyd_node *)node, node_type);
}
-/**
- * @brief Insert schema node into set.
- *
- * @param[in] set Set to insert into.
- * @param[in] node Node to insert.
- * @param[in] node_type Node type of @p node.
- * @param[in] axis Axis that @p node was reached on.
- * @param[out] index_p Optional pointer to store index if the inserted @p node.
- * @return LY_SUCCESS on success.
- * @return LY_EMEM on memory allocation failure.
- */
-static LY_ERR
-set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
+LY_ERR
+lyxp_set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
enum lyxp_axis axis, uint32_t *index_p)
{
uint32_t index;
@@ -1346,7 +1386,7 @@ set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum
}
if (lyxp_set_scnode_contains(set, node, node_type, -1, &index)) {
- /* BUG if axes differs, this new one is thrown away */
+ /* BUG if axes differ, this new one is thrown away */
set->val.scnodes[index].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
} else {
if (set->used == set->size) {
@@ -1445,7 +1485,7 @@ dfs_search:
LYD_TREE_DFS_continue = 0;
}
- if (!elem->schema || ((root_type == LYXP_NODE_ROOT_CONFIG) && (elem->schema->flags & LYS_CONFIG_R))) {
+ if ((root_type == LYXP_NODE_ROOT_CONFIG) && elem->schema && (elem->schema->flags & LYS_CONFIG_R)) {
/* skip */
LYD_TREE_DFS_continue = 1;
} else {
@@ -1662,7 +1702,7 @@ set_comp_canonize(struct lyxp_set *set, const struct lyxp_set_node *xp_node)
/* is there anything to canonize even? */
if (set->type == LYXP_SET_STRING) {
/* do we have a type to use for canonization? */
- if ((xp_node->type == LYXP_NODE_ELEM) && (xp_node->node->schema->nodetype & LYD_NODE_TERM)) {
+ if ((xp_node->type == LYXP_NODE_ELEM) && xp_node->node->schema && (xp_node->node->schema->nodetype & LYD_NODE_TERM)) {
type = ((struct lyd_node_term *)xp_node->node)->value.realtype;
} else if (xp_node->type == LYXP_NODE_META) {
type = ((struct lyd_meta *)xp_node->node)->value.realtype;
@@ -1785,9 +1825,9 @@ set_sort(struct lyxp_set *set)
hnode.node = set->val.nodes[i].node;
hnode.type = set->val.nodes[i].type;
- hash = dict_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
- hash = dict_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
- hash = dict_hash_multi(hash, NULL, 0);
+ hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
+ hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
+ hash = lyht_hash_multi(hash, NULL, 0);
assert(!lyht_find(set->ht, &hnode, hash, NULL));
}
@@ -1993,11 +2033,11 @@ lyxp_next_token2(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t
* @brief Stack operation push on the repeat array.
*
* @param[in] exp Expression to use.
- * @param[in] tok_idx Position in the expresion \p exp.
- * @param[in] repeat_op_idx Index from \p exp of the operator token. This value is pushed.
+ * @param[in] tok_idx Position in the expresion @p exp.
+ * @param[in] repeat_expr_type Repeated expression type, this value is pushed.
*/
static void
-exp_repeat_push(struct lyxp_expr *exp, uint32_t tok_idx, uint32_t repeat_op_idx)
+exp_repeat_push(struct lyxp_expr *exp, uint32_t tok_idx, enum lyxp_expr_type repeat_expr_type)
{
uint32_t i;
@@ -2005,12 +2045,12 @@ exp_repeat_push(struct lyxp_expr *exp, uint32_t tok_idx, uint32_t repeat_op_idx)
for (i = 0; exp->repeat[tok_idx][i]; ++i) {}
exp->repeat[tok_idx] = realloc(exp->repeat[tok_idx], (i + 2) * sizeof *exp->repeat[tok_idx]);
LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
- exp->repeat[tok_idx][i] = repeat_op_idx;
+ exp->repeat[tok_idx][i] = repeat_expr_type;
exp->repeat[tok_idx][i + 1] = 0;
} else {
exp->repeat[tok_idx] = calloc(2, sizeof *exp->repeat[tok_idx]);
LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
- exp->repeat[tok_idx][0] = repeat_op_idx;
+ exp->repeat[tok_idx][0] = repeat_expr_type;
}
}
@@ -2994,7 +3034,7 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
parsed++;
ncname_len = parse_ncname(&expr_str[parsed]);
LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
- parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ (uint32_t)(parsed - ncname_len + 1), expr_str); ret = LY_EVALID, error);
tok_len = ncname_len;
LY_CHECK_ERR_GOTO(expr_str[parsed + tok_len] == ':',
LOGVAL(ctx, LYVE_XPATH, "Variable with prefix is not supported."); ret = LY_EVALID,
@@ -3084,7 +3124,7 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
ret = LY_EVALID;
goto error;
} else {
- LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], parsed + 1, expr_str);
+ LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], (uint32_t)(parsed + 1), expr_str);
ret = LY_EVALID;
goto error;
}
@@ -3096,7 +3136,7 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
} else {
ncname_len = parse_ncname(&expr_str[parsed]);
LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
- parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ (uint32_t)(parsed - ncname_len + 1), expr_str); ret = LY_EVALID, error);
}
tok_len = ncname_len;
@@ -3104,7 +3144,8 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
if (!strncmp(&expr_str[parsed + tok_len], "::", 2)) {
/* axis */
LY_CHECK_ERR_GOTO(expr_parse_axis(&expr_str[parsed], ncname_len),
- LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], parsed + 1, expr_str); ret = LY_EVALID, error);
+ LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed], (uint32_t)(parsed + 1), expr_str); ret = LY_EVALID,
+ error);
tok_type = LYXP_TOKEN_AXISNAME;
LY_CHECK_GOTO(ret = exp_add_token(ctx, expr, tok_type, parsed, tok_len), error);
@@ -3122,7 +3163,7 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
} else {
ncname_len = parse_ncname(&expr_str[parsed]);
LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
- parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ (uint32_t)(parsed - ncname_len + 1), expr_str); ret = LY_EVALID, error);
}
tok_len = ncname_len;
@@ -3136,7 +3177,7 @@ lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len,
} else {
ncname_len = parse_ncname(&expr_str[parsed + tok_len]);
LY_CHECK_ERR_GOTO(ncname_len < 1, LOGVAL(ctx, LY_VCODE_XP_INEXPR, expr_str[parsed - ncname_len],
- parsed - ncname_len + 1, expr_str); ret = LY_EVALID, error);
+ (uint32_t)(parsed - ncname_len + 1), expr_str); ret = LY_EVALID, error);
tok_len += ncname_len;
}
/* remove old flags to prevent ambiguities */
@@ -3934,10 +3975,10 @@ xpath_current(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set,
set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
if (set->cur_scnode) {
- LY_CHECK_RET(set_scnode_insert_node(set, set->cur_scnode, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, set->cur_scnode, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
} else {
/* root node */
- LY_CHECK_RET(set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
}
} else {
lyxp_set_free_content(set);
@@ -4003,7 +4044,7 @@ xpath_deref(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set
target = p[LY_ARRAY_COUNT(p) - 1].node;
ly_path_free(set->ctx, p);
- LY_CHECK_RET(set_scnode_insert_node(set, target, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, target, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
} /* else the target was found before but is disabled so it was removed */
}
@@ -4030,7 +4071,7 @@ xpath_deref(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set
}
} else {
assert(sleaf->type->basetype == LY_TYPE_INST);
- if (ly_path_eval(leaf->value.target, set->tree, &node)) {
+ if (ly_path_eval(leaf->value.target, set->tree, NULL, &node)) {
LOGERR(set->ctx, LY_EVALID, "Invalid instance-identifier \"%s\" value - required instance not found.",
lyd_get_value(&leaf->node));
return LY_EVALID;
@@ -4274,9 +4315,25 @@ xpath_false(struct lyxp_set **UNUSED(args), uint32_t UNUSED(arg_count), struct l
* @return LY_ERR
*/
static LY_ERR
-xpath_floor(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t UNUSED(options))
+xpath_floor(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ struct lysc_node_leaf *sleaf;
+ LY_ERR rc = LY_SUCCESS;
+
+ if (options & LYXP_SCNODE_ALL) {
+ if (args[0]->type != LYXP_SET_SCNODE_SET) {
+ LOGWRN(set->ctx, "Argument #1 of %s not a node-set as expected.", __func__);
+ } else if ((sleaf = (struct lysc_node_leaf *)warn_get_scnode_in_ctx(args[0]))) {
+ if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGWRN(set->ctx, "Argument #1 of %s is a %s node \"%s\".", __func__, lys_nodetype2str(sleaf->nodetype),
+ sleaf->name);
+ } else if (!warn_is_specific_type(sleaf->type, LY_TYPE_DEC64)) {
+ LOGWRN(set->ctx, "Argument #1 of %s is node \"%s\", not of type \"decimal64\".", __func__, sleaf->name);
+ }
+ }
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_VAL);
+ return rc;
+ }
rc = lyxp_set_cast(args[0], LYXP_SET_NUMBER);
LY_CHECK_RET(rc);
@@ -4474,7 +4531,7 @@ xpath_local_name(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *se
set_fill_string(set, "", 0);
break;
case LYXP_NODE_ELEM:
- set_fill_string(set, item->node->schema->name, strlen(item->node->schema->name));
+ set_fill_string(set, LYD_NAME(item->node), strlen(LYD_NAME(item->node)));
break;
case LYXP_NODE_META:
set_fill_string(set, ((struct lyd_meta *)item->node)->name, strlen(((struct lyd_meta *)item->node)->name));
@@ -4498,7 +4555,7 @@ static LY_ERR
xpath_name(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
{
struct lyxp_set_node *item;
- struct lys_module *mod = NULL;
+ const struct lys_module *mod = NULL;
char *str;
const char *name = NULL;
@@ -4544,8 +4601,8 @@ xpath_name(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uin
/* keep NULL */
break;
case LYXP_NODE_ELEM:
- mod = item->node->schema->module;
- name = item->node->schema->name;
+ mod = lyd_node_module(item->node);
+ name = LYD_NAME(item->node);
break;
case LYXP_NODE_META:
mod = ((struct lyd_meta *)item->node)->annotation->module;
@@ -4580,7 +4637,7 @@ static LY_ERR
xpath_namespace_uri(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set *set, uint32_t options)
{
struct lyxp_set_node *item;
- struct lys_module *mod;
+ const struct lys_module *mod;
/* suppress unused variable warning */
(void)options;
@@ -4630,7 +4687,7 @@ xpath_namespace_uri(struct lyxp_set **args, uint32_t arg_count, struct lyxp_set
case LYXP_NODE_ELEM:
case LYXP_NODE_META:
if (item->type == LYXP_NODE_ELEM) {
- mod = item->node->schema->module;
+ mod = lyd_node_module(item->node);
} else { /* LYXP_NODE_META */
/* annotations */
mod = ((struct lyd_meta *)item->node)->annotation->module;
@@ -5541,7 +5598,7 @@ xpath_pi_text(struct lyxp_set *set, enum lyxp_axis axis, uint32_t options)
case LYXP_NODE_NONE:
LOGINT_RET(set->ctx);
case LYXP_NODE_ELEM:
- if (set->val.nodes[i].node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+ if (!set->val.nodes[i].node->schema || (set->val.nodes[i].node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
set->val.nodes[i].type = LYXP_NODE_TEXT;
break;
}
@@ -5588,7 +5645,7 @@ moveto_resolve_model(const char **qname, uint32_t *qname_len, const struct lyxp_
/* check for errors and non-implemented modules, as they are not valid */
if (!mod || !mod->implemented) {
- LOGVAL(set->ctx, LY_VCODE_XP_INMOD, pref_len, *qname);
+ LOGVAL(set->ctx, LY_VCODE_XP_INMOD, (int)pref_len, *qname);
return LY_EVALID;
}
@@ -5641,10 +5698,9 @@ moveto_root(struct lyxp_set *set, uint32_t options)
if (options & LYXP_SCNODE_ALL) {
set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
- LY_CHECK_RET(set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
} else {
- set->type = LYXP_SET_NODE_SET;
- set->used = 0;
+ lyxp_set_free_content(set);
set_insert_node(set, NULL, 0, set->root_type, 0);
set->non_child_axis = 0;
}
@@ -5668,6 +5724,8 @@ static LY_ERR
moveto_node_check(const struct lyd_node *node, enum lyxp_node_type node_type, const struct lyxp_set *set,
const char *node_name, const struct lys_module *moveto_mod, uint32_t options)
{
+ const struct lysc_node *schema;
+
if ((node_type == LYXP_NODE_ROOT_CONFIG) || (node_type == LYXP_NODE_ROOT)) {
assert(node_type == set->root_type);
@@ -5681,39 +5739,40 @@ moveto_node_check(const struct lyd_node *node, enum lyxp_node_type node_type, co
return LY_ENOT;
}
- if (!node->schema) {
- /* opaque node never matches */
+ /* get schema node even of an opaque node */
+ schema = lyd_node_schema(node);
+ if (!schema) {
+ /* unknown opaque node never matches */
return LY_ENOT;
}
/* module check */
if (moveto_mod) {
- if ((set->ctx == LYD_CTX(node)) && (node->schema->module != moveto_mod)) {
+ if ((set->ctx == LYD_CTX(node)) && (schema->module != moveto_mod)) {
return LY_ENOT;
- } else if ((set->ctx != LYD_CTX(node)) && strcmp(node->schema->module->name, moveto_mod->name)) {
+ } else if ((set->ctx != LYD_CTX(node)) && strcmp(schema->module->name, moveto_mod->name)) {
return LY_ENOT;
}
}
/* context check */
- if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (node->schema->flags & LYS_CONFIG_R)) {
+ if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (schema->flags & LYS_CONFIG_R)) {
return LY_EINVAL;
- } else if (set->context_op && (node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) &&
- (node->schema != set->context_op)) {
+ } else if (set->context_op && (schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && (schema != set->context_op)) {
return LY_EINVAL;
}
/* name check */
if (node_name) {
- if ((set->ctx == LYD_CTX(node)) && (node->schema->name != node_name)) {
+ if ((set->ctx == LYD_CTX(node)) && (schema->name != node_name)) {
return LY_ENOT;
- } else if ((set->ctx != LYD_CTX(node)) && strcmp(node->schema->name, node_name)) {
+ } else if ((set->ctx != LYD_CTX(node)) && strcmp(schema->name, node_name)) {
return LY_ENOT;
}
}
/* when check, accept the context node because it should only be the path ".", we have checked the when is valid before */
- if (!(options & LYXP_IGNORE_WHEN) && lysc_has_when(node->schema) && !(node->flags & LYD_WHEN_TRUE) &&
+ if (!(options & LYXP_IGNORE_WHEN) && lysc_has_when(schema) && !(node->flags & LYD_WHEN_TRUE) &&
(node != set->cur_node)) {
return LY_EINCOMPLETE;
}
@@ -6144,7 +6203,7 @@ moveto_node_hash_child(struct lyxp_set *set, const struct lysc_node *scnode, con
/* create specific data instance if needed */
if (scnode->nodetype == LYS_LIST) {
- LY_CHECK_GOTO(ret = lyd_create_list(scnode, predicates, &inst), cleanup);
+ LY_CHECK_GOTO(ret = lyd_create_list(scnode, predicates, NULL, &inst), cleanup);
} else if (scnode->nodetype == LYS_LEAFLIST) {
LY_CHECK_GOTO(ret = lyd_create_term2(scnode, &predicates[0].value, &inst), cleanup);
}
@@ -6168,6 +6227,10 @@ moveto_node_hash_child(struct lyxp_set *set, const struct lysc_node *scnode, con
} else {
r = lyd_find_sibling_val(siblings, scnode, NULL, 0, &sub);
}
+ if (r == LY_ENOTFOUND) {
+ /* may still be an opaque node */
+ r = lyd_find_sibling_opaq_next(siblings, scnode->name, &sub);
+ }
LY_CHECK_ERR_GOTO(r && (r != LY_ENOTFOUND), ret = r, cleanup);
/* when check */
@@ -6655,7 +6718,7 @@ moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const c
{
ly_bool temp_ctx = 0;
uint32_t getnext_opts, orig_used, i, mod_idx, idx;
- const struct lys_module *mod;
+ const struct lys_module *mod = NULL;
const struct lysc_node *iter;
enum lyxp_node_type iter_type;
@@ -6673,6 +6736,9 @@ moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const c
if (options & LYXP_SCNODE_OUTPUT) {
getnext_opts |= LYS_GETNEXT_OUTPUT;
}
+ if (options & LYXP_SCNODE_SCHEMAMOUNT) {
+ getnext_opts |= LYS_GETNEXT_WITHSCHEMAMOUNT;
+ }
orig_used = set->used;
for (i = 0; i < orig_used; ++i) {
@@ -6691,7 +6757,7 @@ moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const c
}
/* insert */
- LY_CHECK_RET(set_scnode_insert_node(set, iter, iter_type, axis, &idx));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, iter, iter_type, axis, &idx));
/* we need to prevent these nodes from being considered in this moveto */
if ((idx < orig_used) && (idx > i)) {
@@ -6704,7 +6770,7 @@ moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const c
(set->val.scnodes[i].type == LYXP_NODE_ELEM) && !ly_nested_ext_schema(NULL, set->val.scnodes[i].scnode,
moveto_mod->name, strlen(moveto_mod->name), LY_VALUE_JSON, NULL, ncname, strlen(ncname), &iter, NULL)) {
/* there is a matching node from an extension, use it */
- LY_CHECK_RET(set_scnode_insert_node(set, iter, LYXP_NODE_ELEM, axis, &idx));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM, axis, &idx));
if ((idx < orig_used) && (idx > i)) {
set->val.scnodes[idx].in_ctx = LYXP_SET_SCNODE_ATOM_NEW_CTX;
temp_ctx = 1;
@@ -6848,7 +6914,7 @@ moveto_scnode_dfs(struct lyxp_set *set, const struct lysc_node *start, uint32_t
goto skip_children;
}
} else {
- LY_CHECK_RET(set_scnode_insert_node(set, elem, LYXP_NODE_ELEM, LYXP_AXIS_DESCENDANT, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, elem, LYXP_NODE_ELEM, LYXP_AXIS_DESCENDANT, NULL));
}
} else if (rc == LY_EINVAL) {
goto skip_children;
@@ -7459,7 +7525,11 @@ only_parse:
*tok_idx = orig_exp;
rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
- if (rc != LY_SUCCESS) {
+ if (!rc && set2.not_found) {
+ set->not_found = 1;
+ break;
+ }
+ if (rc) {
lyxp_set_free_content(&set2);
return rc;
}
@@ -7508,6 +7578,9 @@ only_parse:
*tok_idx = orig_exp;
rc = eval_expr_select(exp, tok_idx, 0, set, options);
+ if (!rc && set->not_found) {
+ break;
+ }
LY_CHECK_RET(rc);
set->val.scnodes[i].in_ctx = pred_in_ctx;
@@ -7527,7 +7600,7 @@ only_parse:
set_fill_set(&set2, set);
rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
- if (rc != LY_SUCCESS) {
+ if (rc) {
lyxp_set_free_content(&set2);
return rc;
}
@@ -7702,14 +7775,13 @@ cleanup:
* @param[in] ctx_scnode Found schema node as the context for the predicate.
* @param[in] set Context set.
* @param[out] predicates Parsed predicates.
- * @param[out] pred_type Type of @p predicates.
* @return LY_SUCCESS on success,
* @return LY_ENOT if a predicate could not be compiled.
* @return LY_ERR on any error.
*/
static LY_ERR
eval_name_test_try_compile_predicates(const struct lyxp_expr *exp, uint32_t *tok_idx, const struct lysc_node *ctx_scnode,
- const struct lyxp_set *set, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
+ const struct lyxp_set *set, struct ly_path_predicate **predicates)
{
LY_ERR rc = LY_SUCCESS;
uint32_t e_idx, val_start_idx, pred_idx = 0, temp_lo = 0, pred_len = 0, nested_pred;
@@ -7848,7 +7920,7 @@ eval_name_test_try_compile_predicates(const struct lyxp_expr *exp, uint32_t *tok
/* compile */
rc = ly_path_compile_predicate(set->ctx, set->cur_node ? set->cur_node->schema : NULL, set->cur_mod, ctx_scnode, exp2,
- &pred_idx, LY_VALUE_JSON, NULL, predicates, pred_type);
+ &pred_idx, LY_VALUE_JSON, NULL, predicates);
LY_CHECK_GOTO(rc, cleanup);
/* success, the predicate must include all the needed information for hash-based search */
@@ -7881,7 +7953,7 @@ eval_name_test_with_predicate_get_scnode(const struct ly_ctx *ctx, const struct
uint32_t name_len, const struct lys_module *moveto_mod, enum lyxp_node_type root_type, LY_VALUE_FORMAT format,
const struct lysc_node **found)
{
- const struct lysc_node *scnode;
+ const struct lysc_node *scnode, *scnode2;
const struct lys_module *mod;
uint32_t idx = 0;
@@ -7919,7 +7991,19 @@ continue_search:
}
/* search in children, do not repeat the same search */
- scnode = lys_find_child(node->schema, moveto_mod, name, name_len, 0, 0);
+ if (node->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
+ /* make sure the node is unique, whether in input or output */
+ scnode = lys_find_child(node->schema, moveto_mod, name, name_len, 0, 0);
+ scnode2 = lys_find_child(node->schema, moveto_mod, name, name_len, 0, LYS_GETNEXT_OUTPUT);
+ if (scnode && scnode2) {
+ /* conflict, do not use hashes */
+ scnode = NULL;
+ } else if (scnode2) {
+ scnode = scnode2;
+ }
+ } else {
+ scnode = lys_find_child(node->schema, moveto_mod, name, name_len, 0, 0);
+ }
} /* else skip redundant search */
/* additional context check */
@@ -7977,14 +8061,14 @@ eval_name_test_scnode_no_match_msg(struct lyxp_set *set, const struct lyxp_set_s
if (ppath) {
format = "Schema node \"%.*s\" for parent \"%s\" not found; in expr \"%.*s\" with context node \"%s\".";
if (options & LYXP_SCNODE_ERROR) {
- LOGERR(set->ctx, LY_EVALID, format, ncname_len, ncname, ppath, (ncname - expr) + ncname_len, expr, path);
+ LOGERR(set->ctx, LY_ENOTFOUND, format, ncname_len, ncname, ppath, (ncname - expr) + ncname_len, expr, path);
} else {
LOGWRN(set->ctx, format, ncname_len, ncname, ppath, (ncname - expr) + ncname_len, expr, path);
}
} else {
format = "Schema node \"%.*s\" not found; in expr \"%.*s\" with context node \"%s\".";
if (options & LYXP_SCNODE_ERROR) {
- LOGERR(set->ctx, LY_EVALID, format, ncname_len, ncname, (ncname - expr) + ncname_len, expr, path);
+ LOGERR(set->ctx, LY_ENOTFOUND, format, ncname_len, ncname, (ncname - expr) + ncname_len, expr, path);
} else {
LOGWRN(set->ctx, format, ncname_len, ncname, (ncname - expr) + ncname_len, expr, path);
}
@@ -8018,7 +8102,6 @@ eval_name_test_with_predicate(const struct lyxp_expr *exp, uint32_t *tok_idx, en
const struct lys_module *moveto_mod = NULL;
const struct lysc_node *scnode = NULL;
struct ly_path_predicate *predicates = NULL;
- enum ly_path_pred_type pred_type = 0;
int scnode_skip_pred = 0;
LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (options & LYXP_SKIP_EXPR ? "skipped" : "parsed"),
@@ -8060,7 +8143,7 @@ eval_name_test_with_predicate(const struct lyxp_expr *exp, uint32_t *tok_idx, en
if (scnode && (scnode->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
/* try to create the predicates */
- if (eval_name_test_try_compile_predicates(exp, tok_idx, scnode, set, &predicates, &pred_type)) {
+ if (eval_name_test_try_compile_predicates(exp, tok_idx, scnode, set, &predicates)) {
/* hashes cannot be used */
scnode = NULL;
}
@@ -8132,8 +8215,7 @@ moveto:
if (options & LYXP_SCNODE_ERROR) {
/* error */
- rc = LY_EVALID;
- goto cleanup;
+ set->not_found = 1;
}
/* skip the predicates and the rest of this path to not generate invalid warnings */
@@ -8175,10 +8257,8 @@ cleanup:
/* restore options */
options &= ~LYXP_SKIP_EXPR;
}
- if (!(options & LYXP_SKIP_EXPR)) {
- lydict_remove(set->ctx, ncname_dict);
- ly_path_predicates_free(set->ctx, pred_type, predicates);
- }
+ lydict_remove(set->ctx, ncname_dict);
+ ly_path_predicates_free(set->ctx, predicates);
return rc;
}
@@ -8350,8 +8430,9 @@ step:
rc = eval_name_test_with_predicate(exp, tok_idx, axis, all_desc, set, options);
if (rc == LY_ENOT) {
assert(options & LYXP_SCNODE_ALL);
- /* skip the rest of this path */
rc = LY_SUCCESS;
+
+ /* skip the rest of this path */
scnode_skip_path = 1;
options |= LYXP_SKIP_EXPR;
}
@@ -8600,8 +8681,9 @@ eval_function_call(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_s
rc = eval_expr_select(exp, tok_idx, 0, args[0], options);
LY_CHECK_GOTO(rc, cleanup);
+ set->not_found = args[0]->not_found;
} else {
- rc = eval_expr_select(exp, tok_idx, 0, set, options | LYXP_SKIP_EXPR);
+ rc = eval_expr_select(exp, tok_idx, 0, set, options);
LY_CHECK_GOTO(rc, cleanup);
}
}
@@ -8623,8 +8705,11 @@ eval_function_call(const struct lyxp_expr *exp, uint32_t *tok_idx, struct lyxp_s
rc = eval_expr_select(exp, tok_idx, 0, args[arg_count - 1], options);
LY_CHECK_GOTO(rc, cleanup);
+ if (args[arg_count - 1]->not_found) {
+ set->not_found = 1;
+ }
} else {
- rc = eval_expr_select(exp, tok_idx, 0, set, options | LYXP_SKIP_EXPR);
+ rc = eval_expr_select(exp, tok_idx, 0, set, options);
LY_CHECK_GOTO(rc, cleanup);
}
}
@@ -8698,27 +8783,30 @@ eval_number(struct ly_ctx *ctx, const struct lyxp_expr *exp, uint32_t *tok_idx,
}
LY_ERR
-lyxp_vars_find(struct lyxp_var *vars, const char *name, size_t name_len, struct lyxp_var **var)
+lyxp_vars_find(const struct ly_ctx *ctx, const struct lyxp_var *vars, const char *name, size_t name_len,
+ struct lyxp_var **var)
{
- LY_ERR ret = LY_ENOTFOUND;
LY_ARRAY_COUNT_TYPE u;
- assert(vars && name);
+ assert(name);
- name_len = name_len ? name_len : strlen(name);
+ if (!name_len) {
+ name_len = strlen(name);
+ }
LY_ARRAY_FOR(vars, u) {
if (!strncmp(vars[u].name, name, name_len)) {
- ret = LY_SUCCESS;
- break;
+ if (var) {
+ *var = (struct lyxp_var *)&vars[u];
+ }
+ return LY_SUCCESS;
}
}
- if (var && !ret) {
- *var = &vars[u];
+ if (ctx) {
+ LOGERR(ctx, LY_ENOTFOUND, "Variable \"%.*s\" not defined.", (int)name_len, name);
}
-
- return ret;
+ return LY_ENOTFOUND;
}
/**
@@ -8737,17 +8825,14 @@ eval_variable_reference(const struct lyxp_expr *exp, uint32_t *tok_idx, struct l
LY_ERR ret;
const char *name;
struct lyxp_var *var;
- const struct lyxp_var *vars;
struct lyxp_expr *tokens = NULL;
uint32_t token_index, name_len;
- vars = set->vars;
-
/* find out the name and value of the variable */
name = &exp->expr[exp->tok_pos[*tok_idx]];
name_len = exp->tok_len[*tok_idx];
- ret = lyxp_vars_find((struct lyxp_var *)vars, name, name_len, &var);
- LY_CHECK_ERR_RET(ret, LOGERR(set->ctx, ret, "XPath variable \"%.*s\" not defined.", (int)name_len, name), ret);
+ ret = lyxp_vars_find(set->ctx, set->vars, name, name_len, &var);
+ LY_CHECK_RET(ret);
/* parse value */
ret = lyxp_expr_parse(set->ctx, var->value, 0, 1, &tokens);
@@ -8917,8 +9002,9 @@ static LY_ERR
eval_union_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
LY_ERR rc = LY_SUCCESS;
- struct lyxp_set orig_set, set2;
uint32_t i;
+ struct lyxp_set orig_set, set2;
+ ly_bool found = 0;
assert(repeat);
@@ -8929,6 +9015,11 @@ eval_union_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat,
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, set, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set->not_found) {
+ set->not_found = 0;
+ } else {
+ found = 1;
+ }
/* ('|' PathExpr)* */
for (i = 0; i < repeat; ++i) {
@@ -8946,6 +9037,9 @@ eval_union_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat,
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (!set2.not_found) {
+ found = 1;
+ }
/* eval */
if (options & LYXP_SCNODE_ALL) {
@@ -8959,6 +9053,9 @@ eval_union_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat,
cleanup:
lyxp_set_free_content(&orig_set);
lyxp_set_free_content(&set2);
+ if (!found) {
+ set->not_found = 1;
+ }
return rc;
}
@@ -9026,7 +9123,7 @@ static LY_ERR
eval_multiplicative_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set,
uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
uint32_t i, this_op;
struct lyxp_set orig_set, set2;
@@ -9058,6 +9155,9 @@ eval_multiplicative_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval */
if (options & LYXP_SCNODE_ALL) {
@@ -9093,7 +9193,7 @@ cleanup:
static LY_ERR
eval_additive_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
uint32_t i, this_op;
struct lyxp_set orig_set, set2;
@@ -9125,6 +9225,9 @@ eval_additive_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repe
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval */
if (options & LYXP_SCNODE_ALL) {
@@ -9162,7 +9265,7 @@ cleanup:
static LY_ERR
eval_relational_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
uint32_t i, this_op;
struct lyxp_set orig_set, set2;
@@ -9194,6 +9297,9 @@ eval_relational_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t re
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval */
if (options & LYXP_SCNODE_ALL) {
@@ -9231,7 +9337,7 @@ cleanup:
static LY_ERR
eval_equality_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
uint32_t i, this_op;
struct lyxp_set orig_set, set2;
@@ -9263,6 +9369,9 @@ eval_equality_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repe
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval */
if (options & LYXP_SCNODE_ALL) {
@@ -9301,7 +9410,7 @@ cleanup:
static LY_ERR
eval_and_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
struct lyxp_set orig_set, set2;
uint32_t i;
@@ -9315,11 +9424,13 @@ eval_and_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, s
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, set, options);
LY_CHECK_GOTO(rc, cleanup);
- /* cast to boolean, we know that will be the final result */
- if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
- set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
- } else {
- lyxp_set_cast(set, LYXP_SET_BOOLEAN);
+ if (!(options & LYXP_SKIP_EXPR)) {
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ } else {
+ /* cast to boolean, we know that will be the final result */
+ lyxp_set_cast(set, LYXP_SET_BOOLEAN);
+ }
}
/* ('and' EqualityExpr)* */
@@ -9339,6 +9450,9 @@ eval_and_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, s
set_fill_set(&set2, &orig_set);
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval - just get boolean value actually */
if (set->type == LYXP_SET_SCNODE_SET) {
@@ -9371,7 +9485,7 @@ cleanup:
static LY_ERR
eval_or_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, struct lyxp_set *set, uint32_t options)
{
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
struct lyxp_set orig_set, set2;
uint32_t i;
@@ -9385,11 +9499,13 @@ eval_or_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, st
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, set, options);
LY_CHECK_GOTO(rc, cleanup);
- /* cast to boolean, we know that will be the final result */
- if (!(options & LYXP_SKIP_EXPR) && (options & LYXP_SCNODE_ALL)) {
- set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
- } else {
- lyxp_set_cast(set, LYXP_SET_BOOLEAN);
+ if (!(options & LYXP_SKIP_EXPR)) {
+ if (options & LYXP_SCNODE_ALL) {
+ set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
+ } else {
+ /* cast to boolean, we know that will be the final result */
+ lyxp_set_cast(set, LYXP_SET_BOOLEAN);
+ }
}
/* ('or' AndExpr)* */
@@ -9411,6 +9527,9 @@ eval_or_expr(const struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t repeat, st
* but it does not matter */
rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, &set2, options);
LY_CHECK_GOTO(rc, cleanup);
+ if (set2.not_found) {
+ set->not_found = 1;
+ }
/* eval - just get boolean value actually */
if (set->type == LYXP_SET_SCNODE_SET) {
@@ -9523,7 +9642,7 @@ lyxp_get_root_type(const struct lyd_node *ctx_node, const struct lysc_node *ctx_
/* schema */
for (op = ctx_scnode; op && !(op->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)); op = op->parent) {}
- if (op || (options & LYXP_SCNODE)) {
+ if (op || !(options & LYXP_SCNODE_SCHEMA)) {
/* general root that can access everything */
return LYXP_NODE_ROOT;
} else if (!ctx_scnode || (ctx_scnode->flags & LYS_CONFIG_W)) {
@@ -9597,7 +9716,10 @@ lyxp_eval(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct ly
/* evaluate */
rc = eval_expr_select(exp, &tok_idx, 0, set, options);
- if (rc != LY_SUCCESS) {
+ if (!rc && set->not_found) {
+ rc = LY_ENOTFOUND;
+ }
+ if (rc) {
lyxp_set_free_content(set);
}
@@ -9838,7 +9960,7 @@ lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct
LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *cur_scnode,
const struct lysc_node *ctx_scnode, struct lyxp_set *set, uint32_t options)
{
- LY_ERR ret;
+ LY_ERR rc;
uint32_t tok_idx = 0;
LY_CHECK_ARG_RET(ctx, ctx, exp, set, LY_EINVAL);
@@ -9851,7 +9973,7 @@ lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct
memset(set, 0, sizeof *set);
set->type = LYXP_SET_SCNODE_SET;
set->root_type = lyxp_get_root_type(NULL, ctx_scnode, options);
- LY_CHECK_RET(set_scnode_insert_node(set, ctx_scnode, ctx_scnode ? LYXP_NODE_ELEM : set->root_type, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, ctx_scnode, ctx_scnode ? LYXP_NODE_ELEM : set->root_type, LYXP_AXIS_SELF, NULL));
set->val.scnodes[0].in_ctx = LYXP_SET_SCNODE_START;
set->ctx = (struct ly_ctx *)ctx;
@@ -9866,10 +9988,13 @@ lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const struct
LOG_LOCSET(set->cur_scnode, NULL, NULL, NULL);
/* evaluate */
- ret = eval_expr_select(exp, &tok_idx, 0, set, options);
+ rc = eval_expr_select(exp, &tok_idx, 0, set, options);
+ if (!rc && set->not_found) {
+ rc = LY_ENOTFOUND;
+ }
LOG_LOCBACK(set->cur_scnode ? 1 : 0, 0, 0, 0);
- return ret;
+ return rc;
}
LIBYANG_API_DEF const char *
diff --git a/src/xpath.h b/src/xpath.h
index 3e61bb0..17bda6c 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -291,13 +291,15 @@ struct lyxp_set {
/* this is valid only for type LYXP_SET_NODE_SET and LYXP_SET_SCNODE_SET */
uint32_t used; /**< Number of nodes in the set. */
uint32_t size; /**< Allocated size for the set. */
- struct hash_table *ht; /**< Hash table for quick determination of whether a node is in the set. */
+ struct ly_ht *ht; /**< Hash table for quick determination of whether a node is in the set. */
/* XPath context information, this is valid only for type LYXP_SET_NODE_SET */
uint32_t ctx_pos; /**< Position of the current examined node in the set. */
uint32_t ctx_size; /**< Position of the last node at the time the node was examined. */
ly_bool non_child_axis; /**< Whether any node change was performed on a non-child axis. */
+ ly_bool not_found; /**< Set if a node is not found and it is considered an error. */
+
/* general context */
struct ly_ctx *ctx; /**< General context for logging. */
@@ -327,6 +329,8 @@ const char *lyxp_token2str(enum lyxp_token tok);
* @brief Evaluate an XPath expression on data. Be careful when using this function, the result can often
* be confusing without thorough understanding of XPath evaluation rules defined in RFC 7950.
*
+ * Traverses (valid) opaque nodes in the evaluation.
+ *
* @param[in] ctx libyang context to use.
* @param[in] exp Parsed XPath expression to be evaluated.
* @param[in] cur_mod Current module for the expression (where it was "instantiated").
@@ -381,6 +385,7 @@ LY_ERR lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const
warning is printed. */
#define LYXP_ACCESS_TREE_ALL 0x80 /**< Explicit accessible tree of all the nodes. */
#define LYXP_ACCESS_TREE_CONFIG 0x0100 /**< Explicit accessible tree of only configuration data. */
+#define LYXP_SCNODE_SCHEMAMOUNT LYS_FIND_SCHEMAMOUNT /**< Nodes from mounted modules are also accessible. */
/**
* @brief Cast XPath set to another type.
@@ -421,6 +426,20 @@ ly_bool lyxp_set_scnode_contains(struct lyxp_set *set, const struct lysc_node *n
void lyxp_set_scnode_merge(struct lyxp_set *set1, struct lyxp_set *set2);
/**
+ * @brief Insert schema node into set.
+ *
+ * @param[in] set Set to insert into.
+ * @param[in] node Node to insert.
+ * @param[in] node_type Node type of @p node.
+ * @param[in] axis Axis that @p node was reached on.
+ * @param[out] index_p Optional pointer to store the index of the inserted @p node.
+ * @return LY_SUCCESS on success.
+ * @return LY_EMEM on memory allocation failure.
+ */
+LY_ERR lyxp_set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
+ enum lyxp_axis axis, uint32_t *index_p);
+
+/**
* @brief Parse an XPath expression into a structure of tokens.
* Logs directly.
*
@@ -496,15 +515,17 @@ LY_ERR lyxp_next_token2(const struct ly_ctx *ctx, const struct lyxp_expr *exp, u
enum lyxp_token want_tok1, enum lyxp_token want_tok2);
/**
- * @brief Find variable named @name in @p vars.
+ * @brief Find variable named @p name in @p vars.
*
+ * @param[in] ctx Context for logging, not logged if NULL.
* @param[in] vars [Sized array](@ref sizedarrays) of XPath variables.
* @param[in] name Name of the variable being searched.
* @param[in] name_len Name length can be set to 0 if @p name is terminated by null byte.
* @param[out] var Variable that was found. The parameter is optional.
* @return LY_SUCCESS if the variable was found, otherwise LY_ENOTFOUND.
*/
-LY_ERR lyxp_vars_find(struct lyxp_var *vars, const char *name, size_t name_len, struct lyxp_var **var);
+LY_ERR lyxp_vars_find(const struct ly_ctx *ctx, const struct lyxp_var *vars, const char *name, size_t name_len,
+ struct lyxp_var **var);
/**
* @brief Frees a parsed XPath expression. @p expr should not be used afterwards.
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 6f36f31..259ef34 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -45,6 +45,8 @@ if(ENABLE_TESTS)
add_subdirectory(style)
add_subdirectory(fuzz)
endif()
+ add_subdirectory(yanglint)
+ add_subdirectory(yangre)
endif()
if(ENABLE_PERF_TESTS)
add_subdirectory(perf)
diff --git a/tests/tool_i.tcl b/tests/tool_i.tcl
new file mode 100644
index 0000000..d0f3d4b
--- /dev/null
+++ b/tests/tool_i.tcl
@@ -0,0 +1,156 @@
+# @brief Common functions and variables for Tool Under Test (TUT).
+#
+# The script requires variables:
+# TUT_PATH - Assumed absolute path to the directory in which the TUT is located.
+# TUT_NAME - TUT name (without path).
+#
+# The script sets the variables:
+# TUT - The path (including the name) of the executable TUT.
+# error_prompt - Delimiter on error.
+# error_head - Header on error.
+
+package require Expect
+
+# Complete the path for Tool Under Test (TUT). For example, on Windows, TUT can be located in the Debug or Release
+# subdirectory. Note that Release build takes precedence over Debug.
+set conftypes {{} Release Debug}
+foreach i $conftypes {
+ if { [file executable "$TUT_PATH/$i/$TUT_NAME"] || [file executable "$TUT_PATH/$i/$TUT_NAME.exe"] } {
+ set TUT "$TUT_PATH/$i/$TUT_NAME"
+ break
+ }
+}
+if {![info exists TUT]} {
+ error "$TUT_NAME executable not found"
+}
+
+# prompt of error message
+set error_prompt ">>>"
+# the beginning of error message
+set error_head "$error_prompt Check-failed"
+
+# detection on eof and timeout will be on every expect command
+expect_after {
+ eof {
+ global error_head
+ error "$error_head unexpected termination"
+ } timeout {
+ global error_head
+ error "$error_head timeout"
+ }
+}
+
+# Run commands from command line
+tcltest::loadTestedCommands
+
+# namespace of internal functions
+namespace eval ly::private {}
+
+# Send command 'cmd' to the process, then check output string by 'pattern'.
+# Parameter cmd is a string of arguments.
+# Parameter pattern is a regex or an exact string to match. If is not specified, only prompt assumed afterwards.
+# It must not contain a prompt. There can be an '$' character at the end of the pattern, in which case the regex
+# matches the characters before the prompt.
+# Parameter 'opt' can contain:
+# -ex has a similar meaning to the expect command. The 'pattern' parameter is used as a simple string
+# for exact matching of the output. So 'pattern' is not a regular expression but some characters
+# must still be escaped, eg ][.
+proc ly_cmd {cmd {pattern ""} {opt ""}} {
+ global prompt
+
+ send -- "${cmd}\r"
+ expect -- "${cmd}\r\n"
+
+ if { $pattern eq "" } {
+ # command without output
+ expect ^$prompt
+ return
+ }
+
+ # definition of an expression that matches failure
+ set failure_pattern "\r\n${prompt}$"
+
+ if { $opt eq "" && [string index $pattern end] eq "$"} {
+ # check output by regular expression
+ # It was explicitly specified how the expression should end.
+ set pattern [string replace $pattern end end]
+ expect {
+ -re "${pattern}\r\n${prompt}$" {}
+ -re $failure_pattern {
+ error "unexpected output:\n$expect_out(buffer)"
+ }
+ }
+ } elseif { $opt eq "" } {
+ # check output by regular expression
+ expect {
+ -re "${pattern}.*\r\n${prompt}$" {}
+ -re $failure_pattern {
+ error "unexpected output:\n$expect_out(buffer)"
+ }
+ }
+ } elseif { $opt eq "-ex" } {
+ # check output by exact matching
+ expect {
+ -ex "${pattern}\r\n${prompt}" {}
+ -re $failure_pattern {
+ error "unexpected output:\n$expect_out(buffer)"
+ }
+ }
+ } else {
+ global error_head
+ error "$error_head unrecognized value of parameter 'opt'"
+ }
+}
+
+# Send command 'cmd' to the process, expect some header and then check output string by 'pattern'.
+# This function is useful for checking an error that appears in the form of a header.
+# Parameter header is the expected header on the output.
+# Parameter cmd is a string of arguments.
+# Parameter pattern is a regex. It must not contain a prompt.
+proc ly_cmd_header {cmd header pattern} {
+ global prompt
+
+ send -- "${cmd}\r"
+ expect -- "${cmd}\r\n"
+
+ expect {
+ -re "$header .*${pattern}.*\r\n${prompt}$" {}
+ -re "\r\n${prompt}$" {
+ error "unexpected output:\n$expect_out(buffer)"
+ }
+ }
+}
+
+# Whatever is written is sent, output is ignored and then another prompt is expected.
+# Parameter cmd is optional and any output is ignored.
+proc ly_ignore {{cmd ""}} {
+ global prompt
+
+ send "${cmd}\r"
+ expect -re "$prompt$"
+}
+
+# Send a completion request and check if the anchored regex output matches.
+proc ly_completion {input output} {
+ global prompt
+
+ send -- "${input}\t"
+ # expecting echoing input, output and 10 terminal control characters
+ expect -re "^${input}\r${prompt}${output}.*\r.*$"
+}
+
+# Send a completion request and check if the anchored regex hint options match.
+proc ly_hint {input prev_input hints} {
+ global prompt
+
+ set output {}
+ foreach i $hints {
+ # each element might have some number of spaces and CRLF around it
+ append output "${i} *(?:\\r\\n)?"
+ }
+
+ send -- "${input}\t"
+ # expecting the hints, previous input from which the hints were generated
+ # and some number of terminal control characters
+ expect -re "${output}\r${prompt}${prev_input}.*\r.*$"
+}
diff --git a/tests/tool_ni.tcl b/tests/tool_ni.tcl
new file mode 100644
index 0000000..7282d35
--- /dev/null
+++ b/tests/tool_ni.tcl
@@ -0,0 +1,141 @@
+# @brief Common functions and variables for Tool Under Test (TUT).
+#
+# The script requires variables:
+# TUT_PATH - Assumed absolute path to the directory in which the TUT is located.
+# TUT_NAME - TUT name (without path).
+#
+# The script sets the variables:
+# TUT - The path (including the name) of the executable TUT.
+# error_prompt - Delimiter on error.
+# error_head - Header on error.
+
+# Complete the path for Tool Under Test (TUT). For example, on Windows, TUT can be located in the Debug or Release
+# subdirectory. Note that Release build takes precedence over Debug.
+set conftypes {{} Release Debug}
+foreach i $conftypes {
+ if { [file executable "$TUT_PATH/$i/$TUT_NAME"] || [file executable "$TUT_PATH/$i/$TUT_NAME.exe"] } {
+ set TUT "$TUT_PATH/$i/$TUT_NAME"
+ break
+ }
+}
+if {![info exists TUT]} {
+ error "$TUT_NAME executable not found"
+}
+
+# prompt of error message
+set error_prompt ">>>"
+# the beginning of error message
+set error_head "$error_prompt Check-failed"
+
+# Run commands from command line
+tcltest::loadTestedCommands
+
+# namespace of internal functions
+namespace eval ly::private {
+ namespace export *
+}
+
+# Run the process with arguments.
+# Parameter cmd is a string with arguments.
+# Parameter wrn is a flag. Set to 1 if stderr should be ignored.
+# Returns a pair where the first is the return code and the second is the output.
+proc ly::private::ly_exec {cmd {wrn ""}} {
+ global TUT
+ try {
+ set results [exec -- $TUT {*}$cmd]
+ set status 0
+ } trap CHILDSTATUS {results options} {
+ # return code is not 0
+ set status [lindex [dict get $options -errorcode] 2]
+ } trap NONE results {
+ if { $wrn == 1 } {
+ set status 0
+ } else {
+ error "return code is 0 but something was written to stderr:\n$results\n"
+ }
+ } trap CHILDKILLED {results options} {
+ set status [lindex [dict get $options -errorcode] 2]
+ error "process was killed: $status"
+ }
+ list $status $results
+}
+
+# Internal function.
+# Check the output with pattern.
+# Parameter pattern is a regex or an exact string to match.
+# Parameter msg is the output to check.
+# Parameter 'opt' is optional. If contains '-ex', then the 'pattern' parameter is
+# used as a simple string for exact matching of the output.
+proc ly::private::output_check {pattern msg {opt ""}} {
+ if { $opt eq "" } {
+ expr {![regexp -- $pattern $msg]}
+ } elseif { $opt eq "-ex" } {
+ expr {![string equal "$pattern" $msg]}
+ } else {
+ global error_head
+ error "$error_head unrecognized value of parameter 'opt'"
+ }
+}
+
+# Execute yanglint with arguments and expect success.
+# Parameter cmd is a string of arguments.
+# Parameter pattern is a regex or an exact string to match.
+# Parameter 'opt' is optional. If contains '-ex', then the 'pattern' parameter is
+# used as a simple string for exact matching of the output.
+proc ly_cmd {cmd {pattern ""} {opt ""}} {
+ namespace import ly::private::*
+ lassign [ly_exec $cmd] rc msg
+ if { $rc != 0 } {
+ error "unexpected return code $rc:\n$msg\n"
+ }
+ if { $pattern ne "" && [output_check $pattern $msg $opt] } {
+ error "unexpected output:\n$msg\n"
+ }
+ return
+}
+
+# Execute yanglint with arguments and expect error.
+# Parameter cmd is a string of arguments.
+# Parameter pattern is a regex.
+proc ly_cmd_err {cmd pattern} {
+ namespace import ly::private::*
+ lassign [ly_exec $cmd] rc msg
+ if { $rc == 0 } {
+ error "unexpected return code $rc"
+ }
+ if { [output_check $pattern $msg] } {
+ error "unexpected output:\n$msg\n"
+ }
+ return
+}
+
+# Execute yanglint with arguments, expect warning in stderr but success.
+# Parameter cmd is a string of arguments.
+# Parameter pattern is a regex.
+proc ly_cmd_wrn {cmd pattern} {
+ namespace import ly::private::*
+ lassign [ly_exec $cmd 1] rc msg
+ if { $rc != 0 } {
+ error "unexpected return code $rc:\n$msg\n"
+ }
+ if { [output_check $pattern $msg] } {
+ error "unexpected output:\n$msg\n"
+ }
+ return
+}
+
+# Check if yanglint supports the specified option.
+# Parameter opt is a option to be found.
+# Return true if option is found otherwise false.
+proc ly_opt_exists {opt} {
+ namespace import ly::private::*
+ lassign [ly_exec "--help"] rc msg
+ if { $rc != 0 } {
+ error "unexpected return code $rc:\n$msg\n"
+ }
+ if { [output_check $opt $msg] } {
+ return false
+ } else {
+ return true
+ }
+}
diff --git a/tests/utests/basic/test_common.c b/tests/utests/basic/test_common.c
index 75235a2..46b80ab 100644
--- a/tests/utests/basic/test_common.c
+++ b/tests/utests/basic/test_common.c
@@ -1,4 +1,4 @@
-/*
+/**
* @file test_common.c
* @author: Radek Krejci <rkrejci@cesnet.cz>
* @brief unit tests for functions from common.c
diff --git a/tests/utests/basic/test_context.c b/tests/utests/basic/test_context.c
index 4c4cc3f..cfba1d3 100644
--- a/tests/utests/basic/test_context.c
+++ b/tests/utests/basic/test_context.c
@@ -1,4 +1,4 @@
-/*
+/**
* @file test_context.c
* @author: Radek Krejci <rkrejci@cesnet.cz>
* @brief unit tests for functions from context.c
@@ -298,8 +298,8 @@ test_models(void **state)
assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1));
lys_unres_glob_erase(&unres);
ly_in_free(in, 0);
- CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL,
- "Name collision between module and submodule of name \"y\".", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL);
+ CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", "Line number 1.");
assert_int_equal(LY_SUCCESS, ly_in_new_memory("module a {namespace urn:a;prefix a;include y;revision 2018-10-30; }", &in));
assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1));
@@ -308,8 +308,8 @@ test_models(void **state)
assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1));
lys_unres_glob_erase(&unres);
ly_in_free(in, 0);
- CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL,
- "Name collision between module and submodule of name \"y\".", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL);
+ CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", "Line number 1.");
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to b {prefix b;}}");
assert_int_equal(LY_SUCCESS, ly_in_new_memory("module b {namespace urn:b;prefix b;include y;}", &in));
@@ -317,10 +317,10 @@ test_models(void **state)
lys_unres_glob_revert(UTEST_LYCTX, &unres);
lys_unres_glob_erase(&unres);
ly_in_free(in, 0);
- CHECK_LOG_CTX("Parsing module \"b\" failed.", NULL,
- "Including \"y\" submodule into \"b\" failed.", NULL,
- "Parsing submodule failed.", NULL,
- "Name collision between submodules of name \"y\".", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"b\" failed.", NULL);
+ CHECK_LOG_CTX("Including \"y\" submodule into \"b\" failed.", NULL);
+ CHECK_LOG_CTX("Parsing submodule failed.", NULL);
+ CHECK_LOG_CTX("Name collision between submodules of name \"y\".", "Line number 1.");
/* selecting correct revision of the submodules */
ly_ctx_reset_latests(UTEST_LYCTX);
@@ -338,19 +338,6 @@ test_models(void **state)
assert_non_null(mod1->compiled);
assert_non_null(mod1->parsed);
-#if 0
- /* TODO in case we are able to remove the parsed schema, here we will test how it will handle missing import parsed schema */
-
- assert_int_equal(LY_SUCCESS, ly_in_new_memory("module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", &in));
- /* mod1->parsed is necessary to compile mod2 because of possible groupings, typedefs, ... */
- ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL);
- assert_int_equal(LY_ENOTFOUND, lys_create_module(UTEST_LYCTX, in, LYS_IN_YANG, 1, NULL, NULL, &mod2));
- /*logbuf_assert("Unable to reload \"w\" module to import it into \"z\", source data not found.");*/
- CHECK_LOG_CTX("Recompilation of module \"w\" failed.", NULL);
- assert_null(mod2);
- ly_in_free(in, 0);
-#endif
-
assert_int_equal(LY_SUCCESS, ly_in_new_memory("module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", &in));
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module w {namespace urn:w;prefix w;revision 2018-10-24;}");
assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod2));
@@ -425,6 +412,7 @@ test_imports(void **state)
"import a {prefix a;}"
"}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ ly_err_clean(UTEST_LYCTX, NULL);
}
static void
diff --git a/tests/utests/basic/test_hash_table.c b/tests/utests/basic/test_hash_table.c
index 25b595a..78950cd 100644
--- a/tests/utests/basic/test_hash_table.c
+++ b/tests/utests/basic/test_hash_table.c
@@ -1,4 +1,4 @@
-/*
+/**
* @file test_hash_table.c
* @author: Radek Krejci <rkrejci@cesnet.cz>
* @brief unit tests for functions from hash_table.c
@@ -19,8 +19,6 @@
#include "common.h"
#include "hash_table.h"
-struct ht_rec *lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx);
-
static void
test_invalid_arguments(void **state)
{
@@ -83,7 +81,7 @@ static void
test_ht_basic(void **state)
{
uint32_t i;
- struct hash_table *ht;
+ struct ly_ht *ht;
assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 0));
@@ -96,15 +94,14 @@ test_ht_basic(void **state)
assert_int_equal(LY_ENOTFOUND, lyht_remove(ht, &i, i));
CHECK_LOG("Invalid argument hash (lyht_remove_with_resize_cb()).", NULL);
- lyht_free(ht);
+ lyht_free(ht, NULL);
}
static void
test_ht_resize(void **state)
{
uint32_t i;
- struct ht_rec *rec;
- struct hash_table *ht;
+ struct ly_ht *ht;
assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1));
assert_int_equal(8, ht->size);
@@ -120,13 +117,12 @@ test_ht_resize(void **state)
for (i = 0; i < 16; ++i) {
if ((i >= 2) && (i < 8)) {
/* inserted data on indexes 2-7 */
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(1, rec->hits);
- assert_int_equal(i, rec->hash);
+ assert_int_not_equal(UINT32_MAX, ht->hlists[i].first);
+ assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, i, NULL));
} else {
/* nothing otherwise */
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(0, rec->hits);
+ assert_int_equal(UINT32_MAX, ht->hlists[i].first);
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, i, NULL));
}
}
@@ -153,7 +149,7 @@ test_ht_resize(void **state)
}
/* cleanup */
- lyht_free(ht);
+ lyht_free(ht, NULL);
}
static void
@@ -162,8 +158,10 @@ test_ht_collisions(void **UNUSED(state))
#define GET_REC_INT(rec) (*((uint32_t *)&(rec)->val))
uint32_t i;
- struct ht_rec *rec;
- struct hash_table *ht;
+ struct ly_ht_rec *rec;
+ struct ly_ht *ht;
+ uint32_t rec_idx;
+ int count;
assert_non_null(ht = lyht_new(8, sizeof(int), ht_equal_clb, NULL, 1));
@@ -172,66 +170,69 @@ test_ht_collisions(void **UNUSED(state))
}
/* check all records */
- for (i = 0; i < 2; ++i) {
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, 0);
+ for (i = 0; i < 8; ++i) {
+ if (i == 2) {
+ assert_int_not_equal(UINT32_MAX, ht->hlists[i].first);
+ } else {
+ assert_int_equal(UINT32_MAX, ht->hlists[i].first);
+ }
}
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, 4);
- assert_int_equal(GET_REC_INT(rec), i);
- ++i;
- for ( ; i < 6; ++i) {
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, 1);
- assert_int_equal(GET_REC_INT(rec), i);
+ for (i = 0; i < 8; ++i) {
+ if ((i >= 2) && (i < 6)) {
+ assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, 2, NULL));
+ } else {
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, 2, NULL));
+ }
}
- for ( ; i < 8; ++i) {
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, 0);
+ rec_idx = ht->hlists[2].first;
+ count = 0;
+ while (rec_idx != UINT32_MAX) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
+ rec_idx = rec->next;
+ assert_int_equal(rec->hash, 2);
+ count++;
}
+ assert_int_equal(count, 4);
i = 4;
assert_int_equal(lyht_remove(ht, &i, 2), 0);
rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, -1);
i = 2;
assert_int_equal(lyht_remove(ht, &i, 2), 0);
/* check all records */
- for (i = 0; i < 2; ++i) {
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, 0);
+ for (i = 0; i < 8; ++i) {
+ if (i == 2) {
+ assert_int_not_equal(UINT32_MAX, ht->hlists[i].first);
+ } else {
+ assert_int_equal(UINT32_MAX, ht->hlists[i].first);
+ }
}
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, 2);
- assert_int_equal(GET_REC_INT(rec), 5);
- ++i;
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, 1);
- assert_int_equal(GET_REC_INT(rec), 3);
- ++i;
- for ( ; i < 6; ++i) {
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, -1);
+ for (i = 0; i < 8; ++i) {
+ if ((i == 3) || (i == 5)) {
+ assert_int_equal(LY_SUCCESS, lyht_find(ht, &i, 2, NULL));
+ } else {
+ assert_int_equal(LY_ENOTFOUND, lyht_find(ht, &i, 2, NULL));
+ }
}
- for ( ; i < 8; ++i) {
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, 0);
+ rec_idx = ht->hlists[2].first;
+ count = 0;
+ while (rec_idx != UINT32_MAX) {
+ rec = lyht_get_rec(ht->recs, ht->rec_size, rec_idx);
+ rec_idx = rec->next;
+ assert_int_equal(rec->hash, 2);
+ count++;
}
+ assert_int_equal(count, 2);
- for (i = 0; i < 3; ++i) {
- assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
- }
- assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS);
- ++i;
- assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
- ++i;
- assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS);
- ++i;
- for ( ; i < 8; ++i) {
- assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
+ for (i = 0; i < 8; ++i) {
+ if ((i == 3) || (i == 5)) {
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_SUCCESS);
+ } else {
+ assert_int_equal(lyht_find(ht, &i, 2, NULL), LY_ENOTFOUND);
+ }
}
i = 3;
@@ -240,20 +241,11 @@ test_ht_collisions(void **UNUSED(state))
assert_int_equal(lyht_remove(ht, &i, 2), 0);
/* check all records */
- for (i = 0; i < 2; ++i) {
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, 0);
- }
- for ( ; i < 6; ++i) {
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, -1);
- }
- for ( ; i < 8; ++i) {
- rec = lyht_get_rec(ht->recs, ht->rec_size, i);
- assert_int_equal(rec->hits, 0);
+ for (i = 0; i < 8; ++i) {
+ assert_int_equal(UINT32_MAX, ht->hlists[i].first);
}
- lyht_free(ht);
+ lyht_free(ht, NULL);
}
int
diff --git a/tests/utests/basic/test_inout.c b/tests/utests/basic/test_inout.c
index be27510..3f83568 100644
--- a/tests/utests/basic/test_inout.c
+++ b/tests/utests/basic/test_inout.c
@@ -84,8 +84,11 @@ test_input_mem(void **UNUSED(state))
char *str1 = "a", *str2 = "b";
assert_int_equal(LY_EINVAL, ly_in_new_memory(NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument str (ly_in_new_memory()).");
assert_int_equal(LY_EINVAL, ly_in_new_memory(str1, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument in (ly_in_new_memory()).");
assert_null(ly_in_memory(NULL, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument in (ly_in_memory()).");
assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in));
assert_int_equal(LY_IN_MEMORY, ly_in_type(in));
@@ -103,12 +106,15 @@ test_input_fd(void **UNUSED(state))
struct stat statbuf;
assert_int_equal(LY_EINVAL, ly_in_new_fd(-1, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument fd >= 0 (ly_in_new_fd()).");
assert_int_equal(-1, ly_in_fd(NULL, -1));
+ CHECK_LOG_LASTMSG("Invalid argument in (ly_in_fd()).");
assert_int_not_equal(-1, fd1 = open(TEST_INPUT_FILE, O_RDONLY));
assert_int_not_equal(-1, fd2 = open(TEST_INPUT_FILE, O_RDONLY));
assert_int_equal(LY_EINVAL, ly_in_new_fd(fd1, NULL));
+ CHECK_LOG_LASTMSG("Invalid argument in (ly_in_new_fd()).");
assert_int_equal(LY_SUCCESS, ly_in_new_fd(fd1, &in));
assert_int_equal(LY_IN_FD, ly_in_type(in));
diff --git a/tests/utests/basic/test_json.c b/tests/utests/basic/test_json.c
index 1896b8a..08b7719 100644
--- a/tests/utests/basic/test_json.c
+++ b/tests/utests/basic/test_json.c
@@ -1,4 +1,4 @@
-/*
+/**
* @file test_json.c
* @author: Radek Krejci <rkrejci@cesnet.cz>
* @brief unit tests for a generic JSON parser
@@ -27,39 +27,40 @@ test_general(void **state)
/* empty */
str = "";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
- lyjson_ctx_free(jsonctx);
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Empty JSON file.", "Line number 1.");
str = " \n\t \n";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
- lyjson_ctx_free(jsonctx);
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ CHECK_LOG_CTX("Empty JSON file.", "Line number 3.");
/* constant values */
str = "true";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
lyjson_ctx_free(jsonctx);
str = "false";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
lyjson_ctx_free(jsonctx);
str = "null";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
lyjson_ctx_free(jsonctx);
ly_in_free(in, 0);
@@ -75,8 +76,8 @@ test_number(void **state)
/* simple value */
str = "11";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("11", jsonctx->value);
assert_int_equal(2, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
@@ -85,8 +86,8 @@ test_number(void **state)
/* fraction number */
str = "37.7668";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("37.7668", jsonctx->value);
assert_int_equal(7, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
@@ -95,8 +96,8 @@ test_number(void **state)
/* negative number */
str = "-122.3959";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("-122.3959", jsonctx->value);
assert_int_equal(9, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
@@ -105,8 +106,8 @@ test_number(void **state)
/* integer, positive exponent */
str = "550E3";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("550000", jsonctx->value);
assert_int_equal(6, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -114,8 +115,8 @@ test_number(void **state)
str = "-550E3";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("-550000", jsonctx->value);
assert_int_equal(7, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -124,8 +125,8 @@ test_number(void **state)
/* integer, negative exponent */
str = "1E-1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.1", jsonctx->value);
assert_int_equal(3, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -133,8 +134,8 @@ test_number(void **state)
str = "15E-1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("1.5", jsonctx->value);
assert_int_equal(3, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -142,8 +143,8 @@ test_number(void **state)
str = "-15E-1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("-1.5", jsonctx->value);
assert_int_equal(4, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -151,8 +152,8 @@ test_number(void **state)
str = "16E-2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.16", jsonctx->value);
assert_int_equal(4, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -160,8 +161,8 @@ test_number(void **state)
str = "-16E-2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("-0.16", jsonctx->value);
assert_int_equal(5, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -169,8 +170,8 @@ test_number(void **state)
str = "17E-3";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.017", jsonctx->value);
assert_int_equal(5, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -178,8 +179,8 @@ test_number(void **state)
str = "-17E-3";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("-0.017", jsonctx->value);
assert_int_equal(6, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -187,8 +188,8 @@ test_number(void **state)
str = "21000E-2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("210", jsonctx->value);
assert_int_equal(3, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -196,8 +197,8 @@ test_number(void **state)
str = "21000E-4";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("2.1", jsonctx->value);
assert_int_equal(3, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -205,8 +206,8 @@ test_number(void **state)
str = "21000E-7";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.0021", jsonctx->value);
assert_int_equal(6, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -215,8 +216,8 @@ test_number(void **state)
/* decimal number, positive exponent */
str = "5.087E1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("50.87", jsonctx->value);
assert_int_equal(5, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -224,8 +225,8 @@ test_number(void **state)
str = "-5.087E1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("-50.87", jsonctx->value);
assert_int_equal(6, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -233,8 +234,8 @@ test_number(void **state)
str = "5.087E5";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("508700", jsonctx->value);
assert_int_equal(6, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -242,8 +243,8 @@ test_number(void **state)
str = "59.1e+1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("591", jsonctx->value);
assert_int_equal(3, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -251,8 +252,8 @@ test_number(void **state)
str = "0.005087E1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.05087", jsonctx->value);
assert_int_equal(7, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -260,8 +261,8 @@ test_number(void **state)
str = "0.005087E2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.5087", jsonctx->value);
assert_int_equal(6, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -269,8 +270,8 @@ test_number(void **state)
str = "0.005087E6";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("5087", jsonctx->value);
assert_int_equal(4, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -278,8 +279,8 @@ test_number(void **state)
str = "0.05087E6";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("50870", jsonctx->value);
assert_int_equal(5, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -287,8 +288,8 @@ test_number(void **state)
str = "0.005087E8";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("508700", jsonctx->value);
assert_int_equal(6, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -297,8 +298,8 @@ test_number(void **state)
/* decimal number, negative exponent */
str = "35.94e-1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("3.594", jsonctx->value);
assert_int_equal(5, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -306,8 +307,8 @@ test_number(void **state)
str = "-35.94e-1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("-3.594", jsonctx->value);
assert_int_equal(6, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -315,8 +316,8 @@ test_number(void **state)
str = "35.94e-2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.3594", jsonctx->value);
assert_int_equal(6, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -324,8 +325,8 @@ test_number(void **state)
str = "35.94e-3";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.03594", jsonctx->value);
assert_int_equal(7, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -333,8 +334,8 @@ test_number(void **state)
str = "0.3594e-1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.03594", jsonctx->value);
assert_int_equal(7, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -342,8 +343,8 @@ test_number(void **state)
str = "0.03594e-1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.003594", jsonctx->value);
assert_int_equal(8, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -351,8 +352,8 @@ test_number(void **state)
str = "0.003594e-1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.0003594", jsonctx->value);
assert_int_equal(9, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -360,8 +361,8 @@ test_number(void **state)
str = "0.3594e-2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.003594", jsonctx->value);
assert_int_equal(8, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -369,8 +370,8 @@ test_number(void **state)
str = "0.03594e-2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.0003594", jsonctx->value);
assert_int_equal(9, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -378,8 +379,8 @@ test_number(void **state)
str = "0.003594e-2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.00003594", jsonctx->value);
assert_int_equal(10, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -388,8 +389,8 @@ test_number(void **state)
/* zero */
str = "0";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_true(jsonctx->value[0] == '0');
assert_int_equal(1, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
@@ -397,8 +398,8 @@ test_number(void **state)
str = "-0";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_true(jsonctx->value[0] == '-');
assert_true(jsonctx->value[1] == '0');
assert_int_equal(2, jsonctx->value_len);
@@ -407,8 +408,8 @@ test_number(void **state)
str = "94E0";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_true(jsonctx->value[0] == '9');
assert_true(jsonctx->value[1] == '4');
assert_int_equal(2, jsonctx->value_len);
@@ -417,8 +418,8 @@ test_number(void **state)
str = "0E2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_true(jsonctx->value[0] == '0');
assert_int_equal(1, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
@@ -426,8 +427,8 @@ test_number(void **state)
str = "-0E2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_true(jsonctx->value[0] == '-');
assert_true(jsonctx->value[1] == '0');
assert_int_equal(2, jsonctx->value_len);
@@ -436,8 +437,8 @@ test_number(void **state)
str = "5.320e+2";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("532", jsonctx->value);
assert_int_equal(3, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -445,8 +446,8 @@ test_number(void **state)
str = "5.320e-1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_NUMBER, lyjson_ctx_status(jsonctx));
assert_string_equal("0.532", jsonctx->value);
assert_int_equal(5, jsonctx->value_len);
assert_int_equal(1, jsonctx->dynamic);
@@ -455,69 +456,64 @@ test_number(void **state)
/* various invalid inputs */
str = "-x";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Invalid character in JSON Number value (\"x\").", "Line number 1.");
str = " -";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1.");
str = "--1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Invalid character in JSON Number value (\"-\").", "Line number 1.");
str = "+1";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Invalid character sequence \"+1\", expected a JSON value.", "Line number 1.");
str = " 1.x ";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Invalid character in JSON Number value (\"x\").", "Line number 1.");
str = "1.";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1.");
str = " 1eo ";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Invalid character in JSON Number value (\"o\").", "Line number 1.");
str = "1e";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1.");
str = "1E1000";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Number encoded as a string exceeded the LY_NUMBER_MAXLEN limit.", "Line number 1.");
str = "1e9999999999999999999";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1e9999999999999999999).", "Line number 1.");
str = "1.1e66000";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1.1e66000).", "Line number 1.");
str = "1.1e-66000";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Exponent out-of-bounds in a JSON Number value (1.1e-66000).", "Line number 1.");
- str = "-2.1e0.";
- assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- CHECK_LOG_CTX("Unexpected character \".\" after JSON number.", "Line number 1.");
-
ly_in_free(in, 0);
}
@@ -532,68 +528,12 @@ test_string(void **state)
str = "";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
-#if 0
- /* simple string */
- str = "\"hello\"";
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
- assert_ptr_equal(&str[1], jsonctx->value);
- assert_int_equal(5, jsonctx->value_len);
- assert_int_equal(0, jsonctx->dynamic);
- lyjson_ctx_free(jsonctx);
-
- /* 4-byte utf8 character */
- str = "\"\\t𠜎\"";
- assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
- assert_string_equal("\t𠜎", jsonctx->value);
- assert_int_equal(5, jsonctx->value_len);
- assert_int_equal(1, jsonctx->dynamic);
- lyjson_ctx_free(jsonctx);
-
- /* valid escape sequences - note that here it mixes valid JSON string characters (RFC 7159, sec. 7) and
- * valid characters in YANG string type (RFC 7950, sec. 9.4). Since the latter is a subset of JSON string,
- * the YANG string type's restrictions apply to the JSON escape sequences */
- str = "\"\\\" \\\\ \\r \\/ \\n \\t \\u20ac\"";
- assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
- assert_string_equal("\" \\ \r / \n \t €", jsonctx->value);
- assert_int_equal(15, jsonctx->value_len);
- assert_int_equal(1, jsonctx->dynamic);
- lyjson_ctx_free(jsonctx);
-
- /* backspace and form feed are valid JSON escape sequences, but the control characters they represents are not allowed values for YANG string type */
- str = "\"\\b\"";
- assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- CHECK_LOG_CTX("Invalid character reference \"\\b\" (0x00000008).", "Line number 1.");
-
- str = "\"\\f\"";
- assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- CHECK_LOG_CTX("Invalid character reference \"\\f\" (0x0000000c).", "Line number 1.");
-#endif
-
/* unterminated string */
str = "\"unterminated string";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
CHECK_LOG_CTX("Missing quotation-mark at the end of a JSON string.", "Line number 1.");
-#if 0
- /* invalid escape sequence */
- str = "\"char \\x \"";
- assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- CHECK_LOG_CTX("Invalid character escape sequence \\x.", "Line number 1.");
-
- /* new line is allowed only as escaped character in JSON */
- str = "\"\n\"";
- assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- CHECK_LOG_CTX("Invalid character in JSON string \"\n\" (0x0000000a).", "Line number 1.");
-#endif
+ CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1.");
ly_in_free(in, 0);
}
@@ -608,95 +548,122 @@ test_object(void **state)
/* empty */
str = " { } ";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_OBJECT_EMPTY, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
lyjson_ctx_free(jsonctx);
/* simple value */
str = "{\"name\" : \"Radek\"}";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
assert_ptr_equal(&str[2], jsonctx->value);
assert_int_equal(4, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("\"Radek\"}", jsonctx->in->current);
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx));
assert_string_equal("Radek\"}", jsonctx->value);
assert_int_equal(5, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("}", jsonctx->in->current);
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
lyjson_ctx_free(jsonctx);
/* two values */
str = "{\"smart\" : true,\"handsom\":false}";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
assert_string_equal("smart\" : true,\"handsom\":false}", jsonctx->value);
assert_int_equal(5, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("true,\"handsom\":false}", jsonctx->in->current);
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_TRUE, lyjson_ctx_status(jsonctx, 0));
- assert_string_equal(",\"handsom\":false}", jsonctx->in->current);
+ assert_int_equal(LYJSON_OBJECT_NEXT, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
assert_string_equal("handsom\":false}", jsonctx->value);
assert_int_equal(7, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("false}", jsonctx->in->current);
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx, 0));
- assert_string_equal("}", jsonctx->in->current);
+ assert_int_equal(LYJSON_FALSE, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
lyjson_ctx_free(jsonctx);
/* inherited objects */
str = "{\"person\" : {\"name\":\"Radek\"}}";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
assert_string_equal("person\" : {\"name\":\"Radek\"}}", jsonctx->value);
assert_int_equal(6, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("{\"name\":\"Radek\"}}", jsonctx->in->current);
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
assert_string_equal("name\":\"Radek\"}}", jsonctx->value);
assert_int_equal(4, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("\"Radek\"}}", jsonctx->in->current);
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx));
assert_string_equal("Radek\"}}", jsonctx->value);
assert_int_equal(5, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("}}", jsonctx->in->current);
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
- assert_string_equal("}", jsonctx->in->current);
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
lyjson_ctx_free(jsonctx);
- /* new line is allowed only as escaped character in JSON */
+ /* unquoted string */
str = "{ unquoted : \"data\"}";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_EVALID, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- CHECK_LOG_CTX("Invalid character sequence \"unquoted : \"data\"}\", expected a JSON object's member.", "Line number 1.");
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_EVALID, lyjson_ctx_next(jsonctx, NULL));
+ CHECK_LOG_CTX("Invalid character sequence \"unquoted : \"data\"}\", expected a JSON object name.", "Line number 1.");
+ lyjson_ctx_free(jsonctx);
ly_in_free(in, 0);
}
@@ -711,67 +678,79 @@ test_array(void **state)
/* empty */
str = " [ ] ";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_ARRAY_EMPTY, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
lyjson_ctx_free(jsonctx);
/* simple value */
str = "[ null]";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx));
assert_null(jsonctx->value);
assert_int_equal(0, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("null]", jsonctx->in->current);
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx, 0));
- assert_string_equal("]", jsonctx->in->current);
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
lyjson_ctx_free(jsonctx);
/* two values */
str = "[{\"a\":null},\"x\"]";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
- assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
+ assert_int_equal(LYJSON_ARRAY, lyjson_ctx_status(jsonctx));
assert_null(jsonctx->value);
assert_int_equal(0, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("{\"a\":null},\"x\"]", jsonctx->in->current);
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_OBJECT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_OBJECT_NAME, lyjson_ctx_status(jsonctx));
assert_string_equal("a\":null},\"x\"]", jsonctx->value);
assert_int_equal(1, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("null},\"x\"]", jsonctx->in->current);
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx, 0));
- assert_string_equal("},\"x\"]", jsonctx->in->current);
+ assert_int_equal(LYJSON_NULL, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx, 0));
- assert_string_equal(",\"x\"]", jsonctx->in->current);
+ assert_int_equal(LYJSON_OBJECT_CLOSED, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_ARRAY_NEXT, lyjson_ctx_status(jsonctx));
+
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
+ assert_int_equal(LYJSON_STRING, lyjson_ctx_status(jsonctx));
assert_string_equal("x\"]", jsonctx->value);
assert_int_equal(1, jsonctx->value_len);
assert_int_equal(0, jsonctx->dynamic);
- assert_string_equal("]", jsonctx->in->current);
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_ARRAY_CLOSED, lyjson_ctx_status(jsonctx));
+
assert_int_equal(LY_SUCCESS, lyjson_ctx_next(jsonctx, NULL));
- assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx, 0));
+ assert_int_equal(LYJSON_END, lyjson_ctx_status(jsonctx));
lyjson_ctx_free(jsonctx);
/* new line is allowed only as escaped character in JSON */
str = "[ , null]";
assert_non_null(ly_in_memory(in, str));
- assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, 0, &jsonctx));
+ assert_int_equal(LY_SUCCESS, lyjson_ctx_new(UTEST_LYCTX, in, &jsonctx));
assert_int_equal(LY_EVALID, lyjson_ctx_next(jsonctx, NULL));
CHECK_LOG_CTX("Invalid character sequence \", null]\", expected a JSON value.", "Line number 1.");
lyjson_ctx_free(jsonctx);
diff --git a/tests/utests/basic/test_plugins.c b/tests/utests/basic/test_plugins.c
index 7f3fe40..de13c0b 100644
--- a/tests/utests/basic/test_plugins.c
+++ b/tests/utests/basic/test_plugins.c
@@ -1,4 +1,4 @@
-/*
+/**
* @file test_plugins.c
* @author: Radek Krejci <rkrejci@cesnet.cz>
* @brief unit tests for functions from set.c
diff --git a/tests/utests/basic/test_set.c b/tests/utests/basic/test_set.c
index af39afa..9485927 100644
--- a/tests/utests/basic/test_set.c
+++ b/tests/utests/basic/test_set.c
@@ -1,4 +1,4 @@
-/*
+/**
* @file test_set.c
* @author: Radek Krejci <rkrejci@cesnet.cz>
* @brief unit tests for functions from set.c
diff --git a/tests/utests/basic/test_xml.c b/tests/utests/basic/test_xml.c
index 668de4b..071846a 100644
--- a/tests/utests/basic/test_xml.c
+++ b/tests/utests/basic/test_xml.c
@@ -1,4 +1,4 @@
-/*
+/**
* @file test_xml.c
* @author: Radek Krejci <rkrejci@cesnet.cz>
* @brief unit tests for functions from xml.c
diff --git a/tests/utests/basic/test_xpath.c b/tests/utests/basic/test_xpath.c
index b388dc3..754abf2 100644
--- a/tests/utests/basic/test_xpath.c
+++ b/tests/utests/basic/test_xpath.c
@@ -88,6 +88,19 @@ const char *schema_a =
" type string;\n"
" }\n"
" }\n"
+ "\n"
+ " rpc r {\n"
+ " input {\n"
+ " leaf l {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " output {\n"
+ " leaf l {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
"}";
static int
@@ -254,9 +267,11 @@ test_invalid(void **state)
assert_int_equal(LY_EVALID, lyd_find_xpath(tree, "/a:foo2[.=]", &set));
assert_null(set);
+ CHECK_LOG_CTX("Unexpected XPath token \"]\" (\"]\").", NULL);
assert_int_equal(LY_EVALID, lyd_find_xpath(tree, "/a:", &set));
assert_null(set);
+ CHECK_LOG_CTX("Invalid character 'a'[2] of expression '/a:'.", NULL);
lyd_free_all(tree);
}
@@ -381,6 +396,31 @@ test_hash(void **state)
}
static void
+test_rpc(void **state)
+{
+ const char *data =
+ "<r xmlns=\"urn:tests:a\">\n"
+ " <l>val</l>\n"
+ "</r>";
+ struct ly_in *in;
+ struct lyd_node *tree;
+ struct ly_set *set;
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_REPLY_YANG, &tree, NULL));
+ ly_in_free(in, 0);
+ assert_non_null(tree);
+
+ /* name collision input/output, hashes are not used */
+ assert_int_equal(LY_SUCCESS, lyd_find_xpath(tree, "/a:r/l", &set));
+ assert_int_equal(1, set->count);
+
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+}
+
+static void
test_toplevel(void **state)
{
const char *schema_b =
@@ -479,7 +519,7 @@ test_atomize(void **state)
/* some random paths just making sure the API function works */
assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*", 0, &set));
- assert_int_equal(6, set->count);
+ assert_int_equal(7, set->count);
ly_set_free(set, NULL);
/* all nodes from all modules (including internal, which can change easily, so check just the test modules) */
@@ -496,7 +536,7 @@ test_atomize(void **state)
ly_set_free(set, NULL);
assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/*", 0, &set));
- assert_int_equal(13, set->count);
+ assert_int_equal(14, set->count);
ly_set_free(set, NULL);
/*
@@ -530,7 +570,7 @@ test_atomize(void **state)
/* descendant-or-self */
assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*/descendant-or-self::c", 0, &set));
- assert_int_equal(7, set->count);
+ assert_int_equal(8, set->count);
ly_set_free(set, NULL);
/* following */
@@ -545,11 +585,11 @@ test_atomize(void **state)
/* parent */
assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:*/c/parent::l1", 0, &set));
- assert_int_equal(7, set->count);
+ assert_int_equal(8, set->count);
ly_set_free(set, NULL);
assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:c//..", 0, &set));
- assert_int_equal(8, set->count);
+ assert_int_equal(11, set->count);
ly_set_free(set, NULL);
/* preceding */
@@ -898,6 +938,7 @@ test_variables(void **state)
LOCAL_SETUP(data, tree);
assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"mstr\""));
assert_int_equal(LY_ENOTFOUND, lyd_find_xpath2(tree, "/foo[text() = $var55]", vars, &set));
+ CHECK_LOG_CTX("Variable \"var55\" not defined.", NULL);
LOCAL_TEARDOWN(set, tree, vars);
/* Syntax error in value. */
@@ -906,6 +947,7 @@ test_variables(void **state)
LOCAL_SETUP(data, tree);
assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\""));
assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$var]", vars, &set));
+ CHECK_LOG_CTX("Unterminated string delimited with \" (\").", "Data location \"/a:foo\".");
LOCAL_TEARDOWN(set, tree, vars);
/* Prefix is not supported. */
@@ -914,7 +956,7 @@ test_variables(void **state)
LOCAL_SETUP(data, tree);
assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\""));
assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$pref:var]", vars, &set));
- assert_string_equal("Variable with prefix is not supported.", _UC->err_msg);
+ CHECK_LOG_CTX("Variable with prefix is not supported.", NULL);
LOCAL_TEARDOWN(set, tree, vars);
#undef LOCAL_SETUP
@@ -1050,6 +1092,154 @@ test_axes(void **state)
lyd_free_all(tree);
}
+static void
+test_trim(void **state)
+{
+ const char *data;
+ char *str1;
+ struct lyd_node *tree;
+
+ data =
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a1</a>"
+ " <b>b1</b>"
+ " <c>c1</c>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a2</a>"
+ " <b>b2</b>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a3</a>"
+ " <b>b3</b>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a4</a>"
+ " <b>b4</b>"
+ " <c>c4</c>"
+ "</l1>"
+ "<l1 xmlns=\"urn:tests:a\">"
+ " <a>a5</a>"
+ " <b>b5</b>"
+ " <c>c5</c>"
+ "</l1>"
+ "<foo2 xmlns=\"urn:tests:a\">50</foo2>"
+ "<c xmlns=\"urn:tests:a\">"
+ " <x>key2</x>"
+ " <ll>"
+ " <a>key1</a>"
+ " <ll>"
+ " <a>key11</a>"
+ " <b>val11</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key12</a>"
+ " <b>val12</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key13</a>"
+ " <b>val13</b>"
+ " </ll>"
+ " </ll>"
+ " <ll>"
+ " <a>key2</a>"
+ " <ll>"
+ " <a>key21</a>"
+ " <b>val21</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key22</a>"
+ " <b>val22</b>"
+ " </ll>"
+ " </ll>"
+ " <ll>"
+ " <a>key3</a>"
+ " <ll>"
+ " <a>key31</a>"
+ " <b>val31</b>"
+ " </ll>"
+ " <ll>"
+ " <a>key32</a>"
+ " <b>val32</b>"
+ " </ll>"
+ " </ll>"
+ "</c>";
+
+ /* trim #1 */
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/a:c/ll/ll[a='key11']", NULL));
+ lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str1,
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <ll>\n"
+ " <a>key1</a>\n"
+ " <ll>\n"
+ " <a>key11</a>\n"
+ " <b>val11</b>\n"
+ " </ll>\n"
+ " </ll>\n"
+ "</c>\n");
+
+ free(str1);
+ lyd_free_all(tree);
+
+ /* trim #2 */
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/a:c/ll/ll[contains(.,'2')]", NULL));
+ lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str1,
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <ll>\n"
+ " <a>key1</a>\n"
+ " <ll>\n"
+ " <a>key12</a>\n"
+ " <b>val12</b>\n"
+ " </ll>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>key2</a>\n"
+ " <ll>\n"
+ " <a>key21</a>\n"
+ " <b>val21</b>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>key22</a>\n"
+ " <b>val22</b>\n"
+ " </ll>\n"
+ " </ll>\n"
+ " <ll>\n"
+ " <a>key3</a>\n"
+ " <ll>\n"
+ " <a>key32</a>\n"
+ " <b>val32</b>\n"
+ " </ll>\n"
+ " </ll>\n"
+ "</c>\n");
+
+ free(str1);
+ lyd_free_all(tree);
+
+ /* trim #3 */
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
+ assert_non_null(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/l1[4]//.", NULL));
+ lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str1,
+ "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>a4</a>\n"
+ " <b>b4</b>\n"
+ " <c>c4</c>\n"
+ "</l1>\n");
+
+ free(str1);
+ lyd_free_all(tree);
+}
+
int
main(void)
{
@@ -1058,6 +1248,7 @@ main(void)
UTEST(test_union, setup),
UTEST(test_invalid, setup),
UTEST(test_hash, setup),
+ UTEST(test_rpc, setup),
UTEST(test_toplevel, setup),
UTEST(test_atomize, setup),
UTEST(test_canonize, setup),
@@ -1065,6 +1256,7 @@ main(void)
UTEST(test_augment, setup),
UTEST(test_variables, setup),
UTEST(test_axes, setup),
+ UTEST(test_trim, setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/data/test_diff.c b/tests/utests/data/test_diff.c
index 1b7592a..4400b5d 100644
--- a/tests/utests/data/test_diff.c
+++ b/tests/utests/data/test_diff.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief tests for lyd_diff()
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -17,15 +17,15 @@
#include "libyang.h"
-#define CHECK_PARSE_LYD(INPUT, MODEL) \
- CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, MODEL)
+#define CHECK_PARSE_LYD(INPUT, OUTPUT) \
+ CHECK_PARSE_LYD_PARAM(INPUT, LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, OUTPUT)
-#define CHECK_LYD_STRING(IN_MODEL, TEXT) \
- CHECK_LYD_STRING_PARAM(IN_MODEL, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS)
+#define CHECK_LYD_STRING(INPUT, TEXT) \
+ CHECK_LYD_STRING_PARAM(INPUT, TEXT, LYD_XML, LYD_PRINT_WITHSIBLINGS)
-#define CHECK_PARSE_LYD_DIFF(INPUT_1, INPUT_2, OUT_MODEL) \
- assert_int_equal(LY_SUCCESS, lyd_diff_siblings(INPUT_1, INPUT_2, 0, &OUT_MODEL));\
- assert_non_null(OUT_MODEL)
+#define CHECK_PARSE_LYD_DIFF(INPUT_1, INPUT_2, OUT_DIFF) \
+ assert_int_equal(LY_SUCCESS, lyd_diff_siblings(INPUT_1, INPUT_2, 0, &OUT_DIFF));\
+ assert_non_null(OUT_DIFF)
#define TEST_DIFF_3(XML1, XML2, XML3, DIFF1, DIFF2, MERGE) \
{ \
@@ -117,6 +117,12 @@ const char *schema1 =
" leaf l2 {\n"
" type int32;\n"
" }\n"
+ ""
+ " container cont {\n"
+ " leaf l3 {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
" }\n"
""
" leaf-list dllist {\n"
@@ -312,6 +318,8 @@ test_invalid(void **state)
struct lyd_node *diff = NULL;
assert_int_equal(lyd_diff_siblings(model_1, lyd_child(model_1), 0, &diff), LY_EINVAL);
+ CHECK_LOG_CTX("Invalid arguments - cannot create diff for unrelated data (lyd_diff()).", NULL);
+
assert_int_equal(lyd_diff_siblings(NULL, NULL, 0, NULL), LY_EINVAL);
lyd_free_all(model_1);
@@ -637,6 +645,126 @@ test_list(void **state)
}
static void
+test_nested_list(void **state)
+{
+ struct lyd_node *data1, *data2, *diff;
+ const char *xml1, *xml2;
+
+ (void) state;
+
+ xml1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">"
+ " <list>"
+ " <name>n1</name>"
+ " <value>25</value>"
+ " <list2>"
+ " <name2>n22</name2>"
+ " <value2>26</value2>"
+ " </list2>"
+ " </list>"
+ " <list>"
+ " <name>n2</name>"
+ " <value>25</value>"
+ " <list2>"
+ " <name2>n22</name2>"
+ " <value2>26</value2>"
+ " </list2>"
+ " </list>"
+ " <list>"
+ " <name>n3</name>"
+ " <value>25</value>"
+ " <list2>"
+ " <name2>n22</name2>"
+ " <value2>26</value2>"
+ " </list2>"
+ " </list>"
+ " <list>"
+ " <name>n4</name>"
+ " <value>25</value>"
+ " <list2>"
+ " <name2>n22</name2>"
+ " <value2>26</value2>"
+ " </list2>"
+ " </list>"
+ " <list>"
+ " <name>n0</name>"
+ " <value>26</value>"
+ " <list2>"
+ " <name2>n22</name2>"
+ " <value2>26</value2>"
+ " </list2>"
+ " <list2>"
+ " <name2>n23</name2>"
+ " <value2>26</value2>"
+ " </list2>"
+ " </list>"
+ "</df>";
+ xml2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">"
+ " <list>"
+ " <name>n0</name>"
+ " <value>30</value>"
+ " <list2>"
+ " <name2>n23</name2>"
+ " <value2>26</value2>"
+ " </list2>"
+ " </list>"
+ "</df>";
+
+ CHECK_PARSE_LYD(xml1, data1);
+ CHECK_PARSE_LYD(xml2, data2);
+ CHECK_PARSE_LYD_DIFF(data1, data2, diff);
+
+ CHECK_LYD_STRING(diff,
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <list yang:operation=\"delete\">\n"
+ " <name>n1</name>\n"
+ " <value>25</value>\n"
+ " <list2>\n"
+ " <name2>n22</name2>\n"
+ " <value2>26</value2>\n"
+ " </list2>\n"
+ " </list>\n"
+ " <list yang:operation=\"delete\">\n"
+ " <name>n2</name>\n"
+ " <value>25</value>\n"
+ " <list2>\n"
+ " <name2>n22</name2>\n"
+ " <value2>26</value2>\n"
+ " </list2>\n"
+ " </list>\n"
+ " <list yang:operation=\"delete\">\n"
+ " <name>n3</name>\n"
+ " <value>25</value>\n"
+ " <list2>\n"
+ " <name2>n22</name2>\n"
+ " <value2>26</value2>\n"
+ " </list2>\n"
+ " </list>\n"
+ " <list yang:operation=\"delete\">\n"
+ " <name>n4</name>\n"
+ " <value>25</value>\n"
+ " <list2>\n"
+ " <name2>n22</name2>\n"
+ " <value2>26</value2>\n"
+ " </list2>\n"
+ " </list>\n"
+ " <list yang:operation=\"none\">\n"
+ " <name>n0</name>\n"
+ " <value yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"26\">30</value>\n"
+ " <list2 yang:operation=\"delete\">\n"
+ " <name2>n22</name2>\n"
+ " <value2>26</value2>\n"
+ " </list2>\n"
+ " </list>\n"
+ "</df>\n");
+
+ lyd_free_all(data1);
+ lyd_free_all(data2);
+ lyd_free_all(diff);
+}
+
+static void
test_userord_llist(void **state)
{
(void) state;
@@ -940,6 +1068,118 @@ test_userord_list2(void **state)
}
static void
+test_userord_list3(void **state)
+{
+ (void) state;
+ const char *xml1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " <cont>\n"
+ " <l3>val1</l3>\n"
+ " </cont>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>d</l1>\n"
+ " <l2>4</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *xml2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " <cont>\n"
+ " <l3>val2</l3>\n"
+ " </cont>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>a</l1>\n"
+ " <l2>1</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>d</l1>\n"
+ " <l2>44</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *xml3 =
+ "<df xmlns=\"urn:libyang:tests:defaults\">\n"
+ " <ul>\n"
+ " <l1>a</l1>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>c</l1>\n"
+ " <l2>3</l2>\n"
+ " <cont>\n"
+ " <l3>val2</l3>\n"
+ " </cont>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>d</l1>\n"
+ " <l2>44</l2>\n"
+ " </ul>\n"
+ " <ul>\n"
+ " <l1>b</l1>\n"
+ " <l2>2</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+
+ const char *out_diff_1 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='b']\">\n"
+ " <l1>c</l1>\n"
+ " <l2 yang:operation=\"create\">3</l2>\n"
+ " <cont yang:operation=\"none\">\n"
+ " <l3 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"val1\">val2</l3>\n"
+ " </cont>\n"
+ " </ul>\n"
+ " <ul yang:operation=\"replace\" yang:key=\"[l1='a']\" yang:orig-key=\"[l1='b']\">\n"
+ " <l1>d</l1>\n"
+ " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"4\">44</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *out_diff_2 =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='c']\">\n"
+ " <l1>a</l1>\n"
+ " <l2 yang:operation=\"delete\">1</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+ const char *out_merge =
+ "<df xmlns=\"urn:libyang:tests:defaults\" xmlns:yang=\"urn:ietf:params:xml:ns:yang:1\" yang:operation=\"none\">\n"
+ " <ul yang:operation=\"replace\" yang:key=\"\" yang:orig-key=\"[l1='b']\">\n"
+ " <l1>c</l1>\n"
+ " <l2 yang:operation=\"create\">3</l2>\n"
+ " <cont yang:operation=\"none\">\n"
+ " <l3 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"val1\">val2</l3>\n"
+ " </cont>\n"
+ " </ul>\n"
+ " <ul yang:operation=\"replace\" yang:key=\"[l1='a']\" yang:orig-key=\"[l1='b']\">\n"
+ " <l1>d</l1>\n"
+ " <l2 yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\"4\">44</l2>\n"
+ " </ul>\n"
+ " <ul yang:key=\"\" yang:orig-key=\"[l1='c']\" yang:operation=\"replace\">\n"
+ " <l1>a</l1>\n"
+ " <l2 yang:operation=\"delete\">1</l2>\n"
+ " </ul>\n"
+ "</df>\n";
+
+ TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge);
+}
+
+static void
test_keyless_list(void **state)
{
(void) state;
@@ -1207,11 +1447,13 @@ main(void)
UTEST(test_delete_merge, setup),
UTEST(test_leaf, setup),
UTEST(test_list, setup),
+ UTEST(test_nested_list, setup),
UTEST(test_userord_llist, setup),
UTEST(test_userord_llist2, setup),
UTEST(test_userord_mix, setup),
UTEST(test_userord_list, setup),
UTEST(test_userord_list2, setup),
+ UTEST(test_userord_list3, setup),
UTEST(test_keyless_list, setup),
UTEST(test_state_llist, setup),
UTEST(test_wd, setup),
diff --git a/tests/utests/data/test_new.c b/tests/utests/data/test_new.c
index 7642960..5cee903 100644
--- a/tests/utests/data/test_new.c
+++ b/tests/utests/data/test_new.c
@@ -55,6 +55,7 @@ const char *schema_a = "module a {\n"
" anydata any {\n"
" config false;\n"
" }\n"
+ " anyxml anyx;\n"
" leaf-list ll2 {\n"
" config false;\n"
" type string;\n"
@@ -128,6 +129,16 @@ test_top_level(void **state)
assert_int_equal(lyd_new_list2(NULL, mod, "l1", "[a= 'a']\n[b =\t'b']", 0, &node), LY_SUCCESS);
lyd_free_tree(node);
+ const char *key_vals[] = {"a", "b"};
+
+ assert_int_equal(lyd_new_list3(NULL, mod, "l1", key_vals, NULL, 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
+ uint32_t val_lens[] = {1, 1};
+
+ assert_int_equal(lyd_new_list3_bin(NULL, mod, "l1", (const void **)key_vals, val_lens, 0, &node), LY_SUCCESS);
+ lyd_free_tree(node);
+
/* leaf */
assert_int_equal(lyd_new_term(NULL, mod, "foo", "[a='a'][b='b'][c='c']", 0, &node), LY_EVALID);
CHECK_LOG_CTX("Invalid type uint16 value \"[a='a'][b='b'][c='c']\".", "Schema location \"/a:foo\".");
@@ -290,6 +301,7 @@ test_path(void **state)
ret = lyd_new_path2(root, NULL, "/a:c2/l3[1]", NULL, 0, 0, 0, NULL, &node);
assert_int_equal(ret, LY_EEXIST);
+ CHECK_LOG_CTX("Path \"/a:c2/l3[1]\" already exists.", "Data location \"/a:c2/l3[1]\".");
ret = lyd_new_path2(root, NULL, "/a:c2/l3[2]/x", "val2", 0, 0, 0, NULL, &node);
assert_int_equal(ret, LY_SUCCESS);
@@ -348,6 +360,7 @@ test_path(void **state)
ret = lyd_new_path2(root, NULL, "/a:ll2[1]", "", 0, 0, 0, NULL, &node);
assert_int_equal(ret, LY_EEXIST);
+ CHECK_LOG_CTX("Path \"/a:ll2[1]\" already exists.", "Data location \"/a:ll2[1]\".");
ret = lyd_new_path2(root, NULL, "/a:ll2[2]", "val2", 0, 0, 0, NULL, &node);
assert_int_equal(ret, LY_SUCCESS);
@@ -362,6 +375,7 @@ test_path(void **state)
ret = lyd_new_path2(root, NULL, "/a:ll2[3][.='val3']", NULL, 0, 0, 0, NULL, &node);
assert_int_equal(ret, LY_EVALID);
+ CHECK_LOG_CTX("Unparsed characters \"[.='val3']\" left at the end of path.", NULL);
lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS);
assert_string_equal(str,
@@ -391,6 +405,55 @@ test_path(void **state)
"}\n");
free(str);
lyd_free_siblings(root);
+
+ /* anyxml */
+ ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", "<a/><b/><c/>", 0, LYD_ANYDATA_XML, 0, &root, NULL);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+
+ lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str,
+ "<anyx xmlns=\"urn:tests:a\">\n"
+ " <a/>\n"
+ " <b/>\n"
+ " <c/>\n"
+ "</anyx>\n");
+ free(str);
+ lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str,
+ "{\n"
+ " \"a:anyx\": {\n"
+ " \"a\": [null],\n"
+ " \"b\": [null],\n"
+ " \"c\": [null]\n"
+ " }\n"
+ "}\n");
+ free(str);
+ lyd_free_siblings(root);
+
+ ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", "{\"a\":[null],\"b\":[null],\"c\":[null]}", 0, LYD_ANYDATA_JSON, 0, &root, NULL);
+ assert_int_equal(ret, LY_SUCCESS);
+ assert_non_null(root);
+
+ lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str,
+ "<anyx xmlns=\"urn:tests:a\">\n"
+ " <a/>\n"
+ " <b/>\n"
+ " <c/>\n"
+ "</anyx>\n");
+ free(str);
+ lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+ assert_string_equal(str,
+ "{\n"
+ " \"a:anyx\": {\n"
+ " \"a\": [null],\n"
+ " \"b\": [null],\n"
+ " \"c\": [null]\n"
+ " }\n"
+ "}\n");
+ free(str);
+ lyd_free_siblings(root);
}
static void
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
index d341e31..8feed9c 100644
--- a/tests/utests/data/test_parser_json.c
+++ b/tests/utests/data/test_parser_json.c
@@ -1,9 +1,10 @@
-/*
+/**
* @file test_parser_json.c
- * @author: Radek Krejci <rkrejci@cesnet.cz>
- * @brief unit tests for functions from parser_xml.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for JSON parser
*
- * Copyright (c) 2019 CESNET, z.s.p.o.
+ * Copyright (c) 2019 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -44,7 +45,10 @@ setup(void **state)
"leaf-list ll1 { type uint8; }"
"leaf foo2 { type string; default \"default-val\"; }"
"leaf foo3 { type uint32; }"
- "notification n2;}";
+ "leaf foo4 { type uint64; }"
+ "rpc r1 {input {leaf l1 {type string;} leaf l2 {type string;}}}"
+ "notification n2;"
+ "}";
UTEST_SETUP;
@@ -160,6 +164,7 @@ test_leaf(void **state)
/* reverse solidus in JSON object member name */
data = "{\"@a:foo\":{\"a:hi\\nt\":1},\"a:foo\":\"xxx\"}";
assert_int_equal(LY_EINVAL, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+ CHECK_LOG_CTX("Annotation definition for attribute \"a:hi\nt\" not found.", "Path \"/@a:foo/@a:hi\nt\", line number 1.");
}
static void
@@ -427,12 +432,10 @@ test_list(void **state)
assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR, 1, "d", 1, LYS_LEAF, 1, 0, NULL, 0);
CHECK_LOG_CTX(NULL, NULL);
-
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS,
"{\"a:l1\":[{\"a\":\"a\",\"b\":\"b\",\"c\":1,\"d\":\"d\"}]}");
lyd_free_all(tree);
- /* */
CHECK_PARSE_LYD("{\"a:l1\":[{\"c\":1,\"b\":\"b\",\"a\":\"a\"}]}", LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree);
CHECK_LYSC_NODE(tree->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_ORDBY_SYSTEM, 1, "l1",
1, LYS_LIST, 0, 0, NULL, 0);
@@ -447,11 +450,16 @@ test_list(void **state)
CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c",
1, LYS_LEAF, 1, 0, NULL, 0);
CHECK_LOG_CTX(NULL, NULL);
-
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS,
"{\"a:l1\":[{\"a\":\"a\",\"b\":\"b\",\"c\":1}]}");
lyd_free_all(tree);
+ /* skip unknown nested nodes */
+ data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":3,\"counters\":{\"count1\":\"c1\",\"count2\":\"c2\"}}]}";
+ CHECK_PARSE_LYD(data, LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"c\":3}]}");
+ lyd_free_all(tree);
+
data = "{\"a:cp\":{\"@\":{\"a:hint\":1}}}";
CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
assert_non_null(tree);
@@ -460,7 +468,6 @@ test_list(void **state)
1, LYS_CONTAINER, 0, 0, NULL, 0);
CHECK_LYD_META(tree->meta, 1, "hint", 0, 1, INT8, "1", 1);
assert_null(tree->meta->next);
-
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
lyd_free_all(tree);
}
@@ -516,6 +523,25 @@ test_opaq(void **state)
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
lyd_free_all(tree);
+ /* special chars */
+ data = "{\"a:foo3\":\"ab\\\"\\\\\\r\\t\"}";
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ /* wrong encoding */
+ data = "{\"a:foo3\":\"25\"}";
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_JSON, "foo3", 0, 0, NULL, 0, "25");
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
+ data = "{\"a:foo4\":25}";
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_JSON, "foo4", 0, 0, NULL, 0, "25");
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+
/* missing key, no flags */
data = "{\"a:l1\":[{\"a\":\"val_a\",\"b\":\"val_b\",\"d\":\"val_d\"}]}";
PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
@@ -555,11 +581,12 @@ test_opaq(void **state)
/* invalid metadata */
data = "{\"@a:foo\":\"str\",\"@a:foo3\":1,\"a:foo3\":2}";
PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
- "Unknown module of node \"@a:foo\".", "Data location \"/@a:foo\".");
+ "Unknown module of node \"@a:foo\".", "Path \"/\".");
+ CHECK_LOG_CTX("Missing JSON data instance to be coupled with @a:foo metadata.", "Data location \"/@a:foo\", line number 1.");
/* empty name */
PARSER_CHECK_ERROR("{\"@a:foo\":{\"\":0}}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
- "A JSON object member name cannot be a zero-length string.", "Line number 1.");
+ "JSON object member name cannot be a zero-length string.", "Data location \"/@a:foo\", line number 1.");
/* opaque data tree format print */
data =
@@ -626,6 +653,7 @@ static void
test_rpc(void **state)
{
const char *data;
+ char *str;
struct ly_in *in;
struct lyd_node *tree, *op;
const struct lyd_node *node;
@@ -675,8 +703,16 @@ test_rpc(void **state)
CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
lyd_free_all(tree);
- /* wrong namespace, element name, whatever... */
- /* TODO */
+ /* append to parent */
+ assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:r1", NULL, 0, &op));
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory("{\"l1\": \"some str\", \"l2\": \"some other str\"}", &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_JSON, LYD_TYPE_RPC_YANG, &tree, NULL));
+ ly_in_free(in, 0);
+
+ assert_int_equal(LY_SUCCESS, lyd_print_mem(&str, op, LYD_JSON, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK));
+ lyd_free_tree(op);
+ assert_string_equal(str, "{\"a:r1\":{\"l1\":\"some str\",\"l2\":\"some other str\"}}");
+ free(str);
}
static void
@@ -772,6 +808,112 @@ test_reply(void **state)
/* TODO */
}
+static void
+test_restconf_rpc(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *envp;
+
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL)));
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/ietf-netconf-nmda:edit-data", NULL, 0, &tree));
+
+ data = "{\"ietf-netconf-nmda:input\":{"
+ "\"datastore\":\"ietf-datastores:running\","
+ "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}},"
+ "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}"
+ "}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, tree, in, LYD_JSON, LYD_TYPE_RPC_RESTCONF, &envp, NULL));
+ ly_in_free(in, 0);
+
+ /* the same just connected to the edit-data RPC */
+ data = "{\"ietf-netconf-nmda:edit-data\":{"
+ "\"datastore\":\"ietf-datastores:running\","
+ "\"config\":{\"a:cp\":{\"z\":[null],\"@z\":{\"ietf-netconf:operation\":\"replace\"}},"
+ "\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}"
+ "}}";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+}
+
+static void
+test_restconf_notification(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *ntf;
+
+ data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\",\"a:c\":{\"n1\":{\"nl\":\"value\"}}}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf));
+ ly_in_free(in, 0);
+
+ /* envelopes separately */
+ data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\"}}";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+
+ /* notification with the parent node */
+ data = "{\"a:c\":{\"n1\":{\"nl\":\"value\"}}}";
+ CHECK_LYD_STRING(lyd_parent(ntf), LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+
+ lyd_free_all(tree);
+ lyd_free_all(ntf);
+
+ /* wrong order */
+ data = "{\"ietf-restconf:notification\":{\"a:n2\":{},\"eventTime\":\"2013-12-21T00:01:00Z\"}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf));
+ ly_in_free(in, 0);
+
+ lyd_free_all(tree);
+ lyd_free_all(ntf);
+
+ /* unknown notification */
+ data = "{\"ietf-restconf:notification\":{\"eventTime\":\"2013-12-21T00:01:00Z\",\"invalid:n2\":{}}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_EVALID, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_NOTIF_RESTCONF, &tree, &ntf));
+ UTEST_LOG_CTX_CLEAN;
+ ly_in_free(in, 0);
+ lyd_free_all(tree);
+}
+
+static void
+test_restconf_reply(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *envp;
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:c/act", NULL, 0, &tree));
+
+ data = "{\"a:output\":{\"al\":25}}";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, lyd_child(tree), in, LYD_JSON, LYD_TYPE_REPLY_RESTCONF, &envp, NULL));
+ ly_in_free(in, 0);
+
+ /* connected to the RPC with the parent */
+ data = "{\"a:c\":{\"act\":{\"al\":25}}}";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+}
+
+static void
+test_metadata(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+
+ /* invalid metadata value */
+ data = "{\"a:c\":{\"x\":\"xval\",\"@x\":{\"a:hint\":\"value\"}}}";
+ assert_int_equal(LY_EVALID, lyd_parse_data_mem(_UC->ctx, data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, &tree));
+ assert_null(tree);
+ CHECK_LOG_CTX("Invalid non-number-encoded int8 value \"value\".", "Path \"/a:c/x/@a:hint\", line number 1.");
+}
+
int
main(void)
{
@@ -787,6 +929,10 @@ main(void)
UTEST(test_action, setup),
UTEST(test_notification, setup),
UTEST(test_reply, setup),
+ UTEST(test_restconf_rpc, setup),
+ UTEST(test_restconf_notification, setup),
+ UTEST(test_restconf_reply, setup),
+ UTEST(test_metadata, setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index 7defd9c..4f33f00 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -31,6 +31,7 @@ setup(void **state)
" namespace urn:tests:a;\n"
" prefix a;\n"
" yang-version 1.1;\n"
+ " import ietf-yang-metadata {prefix md;}"
" list l1 { key \"a b c\"; leaf a {type string;} leaf b {type string;} leaf c {type int16;}"
" leaf d {type string;}"
" container cont {leaf e {type boolean;}}"
@@ -42,9 +43,12 @@ setup(void **state)
" notification n1 { leaf nl {type string;}}}\n"
" container cp {presence \"container switch\"; leaf y {type string;} leaf z {type int8;}}\n"
" anydata any {config false;}\n"
+ " anyxml anyx;\n"
" leaf foo2 { type string; default \"default-val\"; }\n"
" leaf foo3 { type uint32; }\n"
- " notification n2;}";
+ " notification n2;"
+ " md:annotation attr {type enumeration {enum val;}}"
+ "}";
UTEST_SETUP;
@@ -122,6 +126,7 @@ static void
test_anydata(void **state)
{
const char *data;
+ char *str;
struct lyd_node *tree;
data = "<any xmlns=\"urn:tests:a\">\n"
@@ -145,6 +150,49 @@ test_anydata(void **state)
"</any>\n";
CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected);
+
+ assert_int_equal(LY_SUCCESS, lyd_any_value_str(tree, &str));
+ lyd_free_all(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path2(NULL, UTEST_LYCTX, "/a:any", str, strlen(str), LYD_ANYDATA_XML, 0, &tree, NULL));
+ free(str);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected);
+ lyd_free_all(tree);
+}
+
+static void
+test_anyxml(void **state)
+{
+ const char *data;
+ char *str;
+ struct lyd_node *tree;
+
+ data = "<anyx xmlns=\"urn:tests:a\">\n"
+ " <element1>\n"
+ " <element2 x:attr2=\"test\" xmlns:x=\"urn:x\">data</element2>\n"
+ " </element1>\n"
+ " <element1a/>\n"
+ "</anyx>\n";
+ CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree);
+ assert_non_null(tree);
+ tree = tree->next;
+
+ const char *data_expected =
+ "<anyx xmlns=\"urn:tests:a\">\n"
+ " <element1>\n"
+ " <element2 xmlns:x=\"urn:x\" x:attr2=\"test\">data</element2>\n"
+ " </element1>\n"
+ " <element1a/>\n"
+ "</anyx>\n";
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected);
+
+ assert_int_equal(LY_SUCCESS, lyd_any_value_str(tree, &str));
+ lyd_free_all(tree);
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", str, strlen(str), LYD_ANYDATA_XML, 0, &tree, NULL));
+ free(str);
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data_expected);
lyd_free_all(tree);
}
@@ -170,17 +218,21 @@ test_list(void **state)
/* missing keys */
PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
"List instance is missing its key \"a\".", "Data location \"/a:l1[b='b'][c='1']\", line number 1.");
+ CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL);
PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><a>a</a></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
"List instance is missing its key \"b\".", "Data location \"/a:l1[a='a']\", line number 1.");
PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><b>b</b><a>a</a></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
"List instance is missing its key \"c\".", "Data location \"/a:l1[a='a'][b='b']\", line number 1.");
+ CHECK_LOG_CTX("Invalid position of the key \"a\" in a list.", NULL);
/* key duplicate */
PARSER_CHECK_ERROR("<l1 xmlns=\"urn:tests:a\"><c>1</c><b>b</b><a>a</a><c>1</c></l1>", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
"Duplicate instance of \"c\".",
"Data location \"/a:l1[a='a'][b='b'][c='1'][c='1']/c\", line number 1.");
+ CHECK_LOG_CTX("Invalid position of the key \"a\" in a list.", NULL);
+ CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL);
/* keys order */
CHECK_PARSE_LYD("<l1 xmlns=\"urn:tests:a\"><d>d</d><a>a</a><c>1</c><b>b</b></l1>", 0, LYD_VALIDATE_PRESENT, tree);
@@ -209,6 +261,7 @@ test_list(void **state)
assert_non_null(leaf = (struct lyd_node_term *)leaf->next);
CHECK_LYSC_NODE(leaf->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_KEY, 1, "c", 1, LYS_LEAF, 1, 0, NULL, 0);
CHECK_LOG_CTX("Invalid position of the key \"a\" in a list.", NULL);
+ CHECK_LOG_CTX("Invalid position of the key \"b\" in a list.", NULL);
lyd_free_all(tree);
PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
@@ -250,14 +303,14 @@ test_opaq(void **state)
/* opaq flag */
CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
- CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "foo3", 0, 0, NULL, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "foo3", 0, 0, NULL, 1, "");
CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<foo3 xmlns=\"urn:tests:a\"/>\n");
lyd_free_all(tree);
/* list, opaq flag */
data = "<l1 xmlns=\"urn:tests:a\"/>";
CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
- CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "l1", 0, 0, NULL, 1, "");
CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<l1 xmlns=\"urn:tests:a\"/>\n");
lyd_free_all(tree);
@@ -273,7 +326,7 @@ test_opaq(void **state)
/* opaq flag */
CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
- CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, "");
CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
lyd_free_all(tree);
@@ -289,7 +342,7 @@ test_opaq(void **state)
/* opaq flag */
CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
- CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, "");
CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
lyd_free_all(tree);
@@ -300,7 +353,7 @@ test_opaq(void **state)
" <c xmld:id=\"D\">1</c>\n"
"</a>\n",
LYD_XML, LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree));
- CHECK_LOG_CTX("Unknown XML prefix \"xmld\".", "Line number 3.");
+ CHECK_LOG_CTX("Unknown XML prefix \"xmld\".", "Data location \"/a\", line number 3.");
}
static void
@@ -358,10 +411,10 @@ test_rpc(void **state)
node = lyd_child(node);
/* z has no value */
- CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 1, "");
node = node->parent->next;
/* l1 key c has invalid value so it is at the end */
- CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, "");
CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
"<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
@@ -554,10 +607,10 @@ test_netconf_rpc(void **state)
node = lyd_child(node);
/* z has no value */
- CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_VALUE_XML, "z", 0, 0, NULL, 1, "");
node = node->parent->next;
/* l1 key c has invalid value so it is at the end */
- CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_VALUE_XML, "l1", 0, 0, NULL, 1, "");
CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
"<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"25\"/>\n");
@@ -581,8 +634,33 @@ test_netconf_rpc(void **state)
lyd_free_all(tree);
lyd_free_all(op);
- /* wrong namespace, element name, whatever... */
- /* TODO */
+ /* invalid anyxml nested metadata value */
+ data = "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"1\" pid=\"4114692032\">\n"
+ " <copy-config>\n"
+ " <target>\n"
+ " <running/>\n"
+ " </target>\n"
+ " <source>\n"
+ " <config>\n"
+ " <l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <a>val_a</a>\n"
+ " <b>val_b</b>\n"
+ " <c>5</c>\n"
+ " <cont nc:operation=\"merge\">\n"
+ " <e nc:operation=\"merge2\">false</e>\n"
+ " </cont>\n"
+ " </l1>\n"
+ " </config>\n"
+ " </source>\n"
+ " </copy-config>\n"
+ "</rpc>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_EVALID, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_RPC_NETCONF, &tree, &op));
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Invalid enumeration value \"merge2\".",
+ "Path \"/ietf-netconf:copy-config/source/config/a:l1[a='val_a'][b='val_b'][c='5']/cont/e/@ietf-netconf:operation\", line number 13.");
+ lyd_free_all(tree);
+ assert_null(op);
}
static void
@@ -678,6 +756,22 @@ test_netconf_reply_or_notification(void **state)
lyd_free_all(tree);
lyd_free_all(op2);
+ /* notification with a different order */
+ data = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n"
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <n1>\n"
+ " <nl>value</nl>\n"
+ " </n1>\n"
+ "</c>\n"
+ "<eventTime>2010-12-06T08:00:01Z</eventTime>\n"
+ "</notification>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NOTIF_NETCONF, &tree, &op2));
+ ly_in_free(in, 0);
+
+ lyd_free_all(tree);
+ lyd_free_all(op2);
+
/* parse a data reply */
data = "<rpc-reply message-id=\"55\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
" <al xmlns=\"urn:tests:a\">25</al>\n"
@@ -739,6 +833,64 @@ test_netconf_reply_or_notification(void **state)
}
static void
+test_restconf_rpc(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *envp;
+
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf-nmda", "2019-01-07", NULL)));
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/ietf-netconf-nmda:edit-data", NULL, 0, &tree));
+
+ data = "<input xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-nmda\">"
+ "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore>"
+ "<config>"
+ "<cp xmlns=\"urn:tests:a\"><z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\"/></cp>"
+ "<l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">"
+ "<a>val_a</a><b>val_b</b><c>val_c</c>"
+ "</l1>"
+ "</config></input>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, tree, in, LYD_XML, LYD_TYPE_RPC_RESTCONF, &envp, NULL));
+ ly_in_free(in, 0);
+
+ /* the same just connected to the edit-data RPC */
+ data = "<edit-data xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-nmda\">"
+ "<datastore xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">ds:running</datastore>"
+ "<config>"
+ "<cp xmlns=\"urn:tests:a\"><z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\"/></cp>"
+ "<l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">"
+ "<a>val_a</a><b>val_b</b><c>val_c</c>"
+ "</l1>"
+ "</config></edit-data>";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+}
+
+static void
+test_restconf_reply(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *envp;
+
+ assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:c/act", NULL, 0, &tree));
+
+ data = "<output xmlns=\"urn:tests:a\"><al>25</al></output>";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, lyd_child(tree), in, LYD_XML, LYD_TYPE_REPLY_RESTCONF, &envp, NULL));
+ ly_in_free(in, 0);
+
+ /* connected to the RPC with the parent */
+ data = "<c xmlns=\"urn:tests:a\"><act><al>25</al></act></c>";
+ CHECK_LYD_STRING(tree, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS, data);
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+}
+
+static void
test_filter_attributes(void **state)
{
const char *data;
@@ -812,12 +964,58 @@ test_data_skip(void **state)
lyd_free_all(tree);
}
+static void
+test_metadata(void **state)
+{
+ const char *data;
+ struct lyd_node *tree;
+
+ /* invalid metadata value */
+ data = "<c xmlns=\"urn:tests:a\" xmlns:a=\"urn:tests:a\"><x a:attr=\"value\">xval</x></c>";
+ assert_int_equal(LY_EVALID, lyd_parse_data_mem(_UC->ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+ assert_null(tree);
+ CHECK_LOG_CTX("Invalid enumeration value \"value\".", "Path \"/a:c/x/@a:attr\", line number 1.");
+}
+
+static void
+test_subtree(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree;
+
+ /* prepare data with the parent */
+ data = "<l1 xmlns=\"urn:tests:a\">\n"
+ " <a>val_a</a>\n"
+ " <b>val_b</b>\n"
+ " <c>1</c>\n"
+ "</l1>\n";
+ assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
+
+ /* parse a subtree of it */
+ data = "<cont xmlns=\"urn:tests:a\">\n"
+ " <e>true</e>\n"
+ "</cont>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_data(UTEST_LYCTX, tree, in, LYD_XML, 0, LYD_VALIDATE_PRESENT, NULL));
+ ly_in_free(in, 0);
+
+ /* parse another container, fails */
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_EVALID, lyd_parse_data(UTEST_LYCTX, tree, in, LYD_XML, 0, LYD_VALIDATE_PRESENT, NULL));
+ ly_in_free(in, 0);
+ CHECK_LOG_CTX("Duplicate instance of \"cont\".", "Data location \"/a:l1[a='val_a'][b='val_b'][c='1']/cont\".");
+
+ lyd_free_all(tree);
+}
+
int
main(void)
{
const struct CMUnitTest tests[] = {
UTEST(test_leaf, setup),
UTEST(test_anydata, setup),
+ UTEST(test_anyxml, setup),
UTEST(test_list, setup),
UTEST(test_container, setup),
UTEST(test_opaq, setup),
@@ -828,8 +1026,12 @@ main(void)
UTEST(test_netconf_rpc, setup),
UTEST(test_netconf_action, setup),
UTEST(test_netconf_reply_or_notification, setup),
+ UTEST(test_restconf_rpc, setup),
+ UTEST(test_restconf_reply, setup),
UTEST(test_filter_attributes, setup),
UTEST(test_data_skip, setup),
+ UTEST(test_metadata, setup),
+ UTEST(test_subtree, setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/data/test_printer_xml.c b/tests/utests/data/test_printer_xml.c
index d533c41..6213a37 100644
--- a/tests/utests/data/test_printer_xml.c
+++ b/tests/utests/data/test_printer_xml.c
@@ -145,7 +145,8 @@ test_anydata(void **state)
" <cont>\n"
" <elem1 xmlns=\"urn:tests:defs\">\n"
" <elem2 xmlns=\"urn:tests:types\" xmlns:defs=\"urn:tests:defs\" xmlns:defaults=\"urn:defaults\" "
- "defs:attr1=\"defaults:val\" attr2=\"/defaults:node/defs:node2\"/>\n"
+ "defs:attr1=\"defaults:val\" attr2=\"/defaults:node/defs:node2\">\n"
+ " </elem2>\n"
" </elem1>\n"
" </cont>\n"
"</any>\n";
diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c
index 27dba42..494fdf3 100644
--- a/tests/utests/data/test_tree_data.c
+++ b/tests/utests/data/test_tree_data.c
@@ -1,9 +1,9 @@
/**
* @file test_tree_data.c
- * @author: Radek Krejci <rkrejci@cesnet.cz>
- * @brief unit tests for functions from tress_data.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from tree_data.c
*
- * Copyright (c) 2018-2019 CESNET, z.s.p.o.
+ * Copyright (c) 2018-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -100,7 +100,7 @@ test_compare(void **state)
data2 = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>";
CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1);
CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2);
- assert_int_equal(LY_ENOT, lyd_compare_single(tree1->next, tree2->next, 0));
+ assert_int_equal(LY_ENOT, lyd_compare_single(tree1->next, tree2->next, LYD_COMPARE_FULL_RECURSION));
assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next->next, tree2->next, 0));
lyd_free_all(tree1);
lyd_free_all(tree2);
@@ -145,6 +145,14 @@ test_compare(void **state)
assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0));
lyd_free_all(tree1);
lyd_free_all(tree2);
+
+ data1 = "<c xmlns=\"urn:tests:a\"><x>c</x><x>a</x><x>b</x></c>";
+ data2 = "<c xmlns=\"urn:tests:a\"><x>a</x><x>b</x><x>c</x></c>";
+ CHECK_PARSE_LYD(data1, 0, LYD_VALIDATE_PRESENT, tree1);
+ CHECK_PARSE_LYD(data2, 0, LYD_VALIDATE_PRESENT, tree2);
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, LYD_COMPARE_FULL_RECURSION));
+ lyd_free_all(tree1);
+ lyd_free_all(tree2);
}
static void
@@ -196,7 +204,7 @@ test_compare_diff_ctx(void **state)
data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1);
CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2);
- assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0));
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0));
lyd_free_all(tree1);
lyd_free_all(tree2);
@@ -210,7 +218,7 @@ test_compare_diff_ctx(void **state)
data2 = "<l2 xmlns=\"urn:tests:b\"><c><x>b</x></c></l2>";
CHECK_PARSE_LYD_PARAM_CTX(UTEST_LYCTX, data1, 0, tree1);
CHECK_PARSE_LYD_PARAM_CTX(ctx2, data2, 0, tree2);
- assert_int_equal(LY_ENOT, lyd_compare_single(tree1, tree2, 0));
+ assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1, tree2, 0));
lyd_free_all(tree1);
lyd_free_all(tree2);
@@ -329,8 +337,7 @@ test_dup(void **state)
data = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>";
CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
- assert_int_equal(LY_SUCCESS, lyd_dup_single(((struct lyd_node_inner *)((struct lyd_node_inner *)tree1->next)->child)->child, NULL,
- LYD_DUP_WITH_PARENTS, &tree2));
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(lyd_child(lyd_child(tree1->next)), NULL, LYD_DUP_WITH_PARENTS, &tree2));
int unsigned flag = LYS_CONFIG_R | LYS_SET_ENUM;
CHECK_LYSC_NODE(tree2->schema, NULL, 0, flag, 1, "x", 1, LYS_LEAF, 1, 0, NULL, 0);
@@ -352,8 +359,8 @@ test_dup(void **state)
data = "<l2 xmlns=\"urn:tests:a\"><c><x>b</x></c></l2>";
CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
assert_int_equal(LY_SUCCESS, lyd_dup_single(tree1->next, NULL, 0, &tree2));
- assert_int_equal(LY_SUCCESS, lyd_dup_single(((struct lyd_node_inner *)((struct lyd_node_inner *)tree1->next)->child)->child,
- (struct lyd_node_inner *)tree2, LYD_DUP_WITH_PARENTS, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_dup_single(lyd_child(lyd_child(tree1->next)), (struct lyd_node_inner *)tree2,
+ LYD_DUP_WITH_PARENTS, NULL));
assert_int_equal(LY_SUCCESS, lyd_compare_single(tree1->next, tree2, LYD_COMPARE_FULL_RECURSION));
lyd_free_all(tree1);
lyd_free_all(tree2);
@@ -363,6 +370,8 @@ test_dup(void **state)
CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree1);
assert_int_equal(LY_EINVAL, lyd_dup_single(((struct lyd_node_inner *)tree1)->child->prev,
(struct lyd_node_inner *)tree1->next, LYD_DUP_WITH_PARENTS, NULL));
+ CHECK_LOG_CTX("None of the duplicated node \"c\" schema parents match the provided parent \"c\".",
+ NULL);
lyd_free_all(tree1);
}
@@ -488,6 +497,11 @@ test_find_path(void **state)
assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='10.0.0.1']", 0, NULL));
assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='2100::1']", 0, NULL));
assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/pref[.='fc00::/64']", 0, NULL));
+
+ assert_int_equal(LY_EVALID, lyd_find_path(root, "/cont", 0, NULL));
+ CHECK_LOG_CTX("Prefix missing for \"cont\" in path.", "Schema location \"/c:cont\".");
+ assert_int_equal(LY_SUCCESS, lyd_find_path(root, "nexthop[gateway='2100::1']", 0, NULL));
+
lyd_free_all(root);
}
@@ -525,6 +539,7 @@ test_data_hash(void **state)
/* The run must not crash due to the assert that checks the hash. */
CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Duplicate instance of \"ll\".", "Data location \"/test-data-hash:c/ll[.='']\", line number 1.");
lyd_free_all(tree);
}
diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c
index 415e16a..d0dcae5 100644
--- a/tests/utests/data/test_validation.c
+++ b/tests/utests/data/test_validation.c
@@ -1154,6 +1154,92 @@ test_must(void **state)
CHECK_LOG_CTX_APPTAG("l leaf is not left", "Data location \"/i:cont/l3\".", "not-left");
}
+static void
+test_multi_error(void **state)
+{
+ struct lyd_node *tree;
+ const char *schema =
+ "module ii {\n"
+ " namespace urn:tests:ii;\n"
+ " prefix ii;\n"
+ " yang-version 1.1;\n"
+ "\n"
+ " container cont {\n"
+ " leaf l {\n"
+ " type string;\n"
+ " }\n"
+ " leaf l2 {\n"
+ " must \"../l = 'right'\";\n"
+ " type string;\n"
+ " }\n"
+ " leaf l3 {\n"
+ " must \"../l = 'left'\" {\n"
+ " error-app-tag \"not-left\";\n"
+ " error-message \"l leaf is not left\";\n"
+ " }\n"
+ " type string;\n"
+ " }\n"
+ " leaf-list ll {\n"
+ " type uint32;\n"
+ " min-elements 2;\n"
+ " }\n"
+ " }\n"
+ "}";
+ const char *data;
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* xml */
+ data =
+ "<cont xmlns=\"urn:tests:ii\">\n"
+ " <l>wrong</l>\n"
+ " <l>wrong2</l>\n"
+ " <l2>val</l2>\n"
+ " <l3>val</l3>\n"
+ " <ll>ahoy</ll>\n"
+ "</cont>\n";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "Schema location \"/ii:cont/ll\".", "too-few-elements");
+ CHECK_LOG_CTX_APPTAG("l leaf is not left", "Data location \"/ii:cont/l3\".", "not-left");
+ CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "Data location \"/ii:cont/l2\".", "must-violation");
+ CHECK_LOG_CTX_APPTAG("Invalid type uint32 value \"ahoy\".", "Data location \"/ii:cont/ll\", line number 6.", NULL);
+
+ /* json */
+ data = "{\n"
+ " \"ii:cont\": {\n"
+ " \"l\": \"wrong\",\n"
+ " \"l\": \"wrong2\",\n"
+ " \"l2\": \"val\",\n"
+ " \"l3\": \"val\",\n"
+ " \"ll\": [\"ahoy\"]\n"
+ " }\n"
+ "}\n";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, LY_EVALID, tree);
+ CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "Schema location \"/ii:cont/ll\".", "too-few-elements");
+ CHECK_LOG_CTX_APPTAG("l leaf is not left", "Data location \"/ii:cont/l3\".", "not-left");
+ CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "Data location \"/ii:cont/l2\".", "must-violation");
+ CHECK_LOG_CTX_APPTAG("Invalid non-number-encoded uint32 value \"ahoy\".", "Data location \"/ii:cont/ll\", line number 7.", NULL);
+
+ /* validation */
+ data = "{\n"
+ " \"ii:cont\": {\n"
+ " \"l\": \"wrong\",\n"
+ " \"l\": \"wrong2\",\n"
+ " \"l2\": \"val\",\n"
+ " \"l3\": \"val\",\n"
+ " \"ll\": [25]\n"
+ " }\n"
+ "}\n";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
+ assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT | LYD_VALIDATE_MULTI_ERROR, NULL));
+ lyd_free_tree(tree);
+ CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "Schema location \"/ii:cont/ll\".", "too-few-elements");
+ CHECK_LOG_CTX_APPTAG("l leaf is not left", "Data location \"/ii:cont/l3\".", "not-left");
+ CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "Data location \"/ii:cont/l2\".", "must-violation");
+ CHECK_LOG_CTX_APPTAG("Duplicate instance of \"l\".", "Data location \"/ii:cont/l\".", NULL);
+ CHECK_LOG_CTX_APPTAG("Duplicate instance of \"l\".", "Data location \"/ii:cont/l\".", NULL);
+}
+
const char *schema_j =
"module j {\n"
" namespace urn:tests:j;\n"
@@ -1212,6 +1298,7 @@ test_action(void **state)
struct lyd_node *tree, *op_tree;
UTEST_ADD_MODULE(schema_j, LYS_IN_YANG, feats_j, NULL);
+ UTEST_LOG_CTX_CLEAN;
assert_int_equal(LY_SUCCESS, ly_in_new_memory(
"<cont xmlns=\"urn:tests:j\">\n"
@@ -1304,6 +1391,7 @@ test_reply(void **state)
struct lyd_node *tree, *op_tree;
UTEST_ADD_MODULE(schema_j, LYS_IN_YANG, feats_j, NULL);
+ UTEST_LOG_CTX_CLEAN;
assert_int_equal(LY_SUCCESS, ly_in_new_memory(
"<cont xmlns=\"urn:tests:j\">\n"
@@ -1422,7 +1510,7 @@ test_case(void **state)
" }\n"
"}\n", LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
CHECK_LOG_CTX("Data for both cases \"v0\" and \"v2\" exist.",
- "Data location \"/k:ch\", line number 5.");
+ "Data location \"/k:ch\", line number 6.");
CHECK_PARSE_LYD_PARAM(
"{\n"
@@ -1432,7 +1520,7 @@ test_case(void **state)
" }\n"
"}\n", LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
CHECK_LOG_CTX("Data for both cases \"v0\" and \"v2\" exist.",
- "Data location \"/k:ch\", line number 5.");
+ "Data location \"/k:ch\", line number 6.");
}
int
@@ -1450,6 +1538,7 @@ main(void)
UTEST(test_defaults),
UTEST(test_state),
UTEST(test_must),
+ UTEST(test_multi_error),
UTEST(test_action),
UTEST(test_rpc),
UTEST(test_reply),
diff --git a/tests/utests/extensions/test_metadata.c b/tests/utests/extensions/test_metadata.c
index 39d29be..f1edd29 100644
--- a/tests/utests/extensions/test_metadata.c
+++ b/tests/utests/extensions/test_metadata.c
@@ -53,7 +53,7 @@ test_yang(void **state)
"md:annotation aa;}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Missing mandatory keyword \"type\" as a child of \"md:annotation aa\".",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
/* not allowed substatement */
data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
@@ -61,7 +61,7 @@ test_yang(void **state)
"md:annotation aa {default x;}}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"md:annotation aa\" extension instance.",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
/* invalid cardinality of units substatement */
data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
@@ -69,7 +69,7 @@ test_yang(void **state)
"md:annotation aa {type string; units x; units y;}}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Duplicate keyword \"units\".",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
/* invalid cardinality of status substatement */
data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
@@ -77,7 +77,7 @@ test_yang(void **state)
"md:annotation aa {type string; status current; status obsolete;}}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Duplicate keyword \"status\".",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
/* invalid cardinality of status substatement */
data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
@@ -85,7 +85,7 @@ test_yang(void **state)
"md:annotation aa {type string; type uint8;}}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Duplicate keyword \"type\".",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
/* duplication of the same annotation */
data = "module aa {yang-version 1.1; namespace urn:tests:extensions:metadata:aa; prefix aa;"
@@ -93,7 +93,7 @@ test_yang(void **state)
"md:annotation aa {type string;} md:annotation aa {type uint8;}}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Extension md:annotation is instantiated multiple times.",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
}
static void
@@ -134,7 +134,7 @@ test_yin(void **state)
"</module>";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Missing mandatory keyword \"type\" as a child of \"md:annotation aa\".",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
/* not allowed substatement */
data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
@@ -145,7 +145,7 @@ test_yin(void **state)
"</md:annotation></module>";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
CHECK_LOG_CTX("Invalid keyword \"default\" as a child of \"md:annotation aa\" extension instance.",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
/* invalid cardinality of units substatement */
data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
@@ -158,7 +158,7 @@ test_yin(void **state)
"</md:annotation></module>";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
CHECK_LOG_CTX("Duplicate keyword \"units\".",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
/* invalid cardinality of status substatement */
data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
@@ -171,7 +171,7 @@ test_yin(void **state)
"</md:annotation></module>";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
CHECK_LOG_CTX("Duplicate keyword \"status\".",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
/* invalid cardinality of status substatement */
data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
@@ -183,7 +183,7 @@ test_yin(void **state)
"</md:annotation></module>";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
CHECK_LOG_CTX("Duplicate keyword \"type\".",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
/* duplication of the same annotation */
data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" xmlns:md=\"urn:ietf:params:xml:ns:yang:ietf-yang-metadata\" name=\"aa\">\n"
@@ -196,7 +196,7 @@ test_yin(void **state)
"</md:annotation></module>";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YIN, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 metadata v1\": Extension md:annotation is instantiated multiple times.",
- "/aa:{extension='md:annotation'}/aa");
+ "Path \"/aa:{extension='md:annotation'}/aa\".");
}
int
diff --git a/tests/utests/extensions/test_nacm.c b/tests/utests/extensions/test_nacm.c
index 1c999fb..5f7028e 100644
--- a/tests/utests/extensions/test_nacm.c
+++ b/tests/utests/extensions/test_nacm.c
@@ -58,7 +58,7 @@ test_deny_all(void **state)
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": "
"Extension nacm:default-deny-all is allowed only in a data nodes, but it is placed in \"module\" statement.",
- "/b:{extension='nacm:default-deny-all'}");
+ "Path \"/b:{extension='nacm:default-deny-all'}\".");
/* invalid */
data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;"
@@ -67,7 +67,7 @@ test_deny_all(void **state)
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": "
"Extension nacm:default-deny-write is mixed with nacm:default-deny-all.",
- "/aa:l/{extension='nacm:default-deny-all'}");
+ "Path \"/aa:l/{extension='nacm:default-deny-all'}\".");
}
static void
@@ -100,7 +100,7 @@ test_deny_write(void **state)
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": "
"Extension nacm:default-deny-write is not allowed in notification statement.",
- "/b:notif/{extension='nacm:default-deny-write'}");
+ "Path \"/b:notif/{extension='nacm:default-deny-write'}\".");
/* invalid */
data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;"
@@ -109,7 +109,7 @@ test_deny_write(void **state)
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 NACM v1\": "
"Extension nacm:default-deny-write is instantiated multiple times.",
- "/aa:l/{extension='nacm:default-deny-write'}");
+ "Path \"/aa:l/{extension='nacm:default-deny-write'}\".");
}
int
diff --git a/tests/utests/extensions/test_schema_mount.c b/tests/utests/extensions/test_schema_mount.c
index be879ec..17a4c94 100644
--- a/tests/utests/extensions/test_schema_mount.c
+++ b/tests/utests/extensions/test_schema_mount.c
@@ -75,7 +75,7 @@ test_schema(void **state)
assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
"Extension \"yangmnt:mount-point\" instance not allowed in YANG version 1 module.",
- "/sm:root/{extension='yangmnt:mount-point'}/root");
+ "Path \"/sm:root/{extension='yangmnt:mount-point'}/root\".");
schema =
"module sm {\n"
@@ -92,7 +92,7 @@ test_schema(void **state)
assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
"Extension \"yangmnt:mount-point\" instance allowed only in container or list statement.",
- "/sm:{extension='yangmnt:mount-point'}/root");
+ "Path \"/sm:{extension='yangmnt:mount-point'}/root\".");
schema =
"module sm {\n"
@@ -114,7 +114,7 @@ test_schema(void **state)
assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
"Extension \"yangmnt:mount-point\" instance allowed only in container or list statement.",
- "/sm:root/l/{extension='yangmnt:mount-point'}/root");
+ "Path \"/sm:root/l/{extension='yangmnt:mount-point'}/root\".");
schema =
"module sm {\n"
@@ -138,7 +138,7 @@ test_schema(void **state)
assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
"Multiple extension \"yangmnt:mount-point\" instances.",
- "/sm:l/{extension='yangmnt:mount-point'}/root");
+ "Path \"/sm:l/{extension='yangmnt:mount-point'}/root\".");
/* valid */
schema =
@@ -410,25 +410,25 @@ test_parse_invalid(void **state)
CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
"Mandatory node \"type\" instance does not exist.",
- "Schema location \"/ietf-interfaces:interfaces/interface/type\".");
+ "Data location \"/ietf-interfaces:interfaces/interface[name='bu']\".");
CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
"Mandatory node \"type\" instance does not exist.",
- "Schema location \"/ietf-interfaces:interfaces/interface/type\".");
+ "Data location \"/ietf-interfaces:interfaces/interface[name='bu']\".");
/* same validation fail in separate validation */
CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, data);
assert_int_equal(LY_EVALID, lyd_validate_all(&data, NULL, LYD_VALIDATE_PRESENT, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
"Mandatory node \"type\" instance does not exist.",
- "Schema location \"/ietf-interfaces:interfaces/interface/type\".");
+ "Data location \"/ietf-interfaces:interfaces/interface[name='bu']\".");
lyd_free_siblings(data);
CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, data);
assert_int_equal(LY_EVALID, lyd_validate_all(&data, NULL, LYD_VALIDATE_PRESENT, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
"Mandatory node \"type\" instance does not exist.",
- "Schema location \"/ietf-interfaces:interfaces/interface/type\".");
+ "Data location \"/ietf-interfaces:interfaces/interface[name='bu']\".");
lyd_free_siblings(data);
/* success */
@@ -878,7 +878,7 @@ test_parse_shared(void **state)
CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, data);
CHECK_LOG_CTX("Ext plugin \"ly2 schema mount v1\": "
"Shared-schema yang-library content-id \"2\" differs from \"1\" used previously.",
- "/ietf-yang-library:yang-library/content-id");
+ "Path \"/ietf-yang-library:yang-library/content-id\".");
/* data for 2 mount points */
ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
@@ -1549,6 +1549,108 @@ test_new(void **state)
lyd_free_siblings(data);
}
+static void
+test_lys_getnext(void **state)
+{
+ const struct lysc_node *parent, *node;
+ struct ly_ctx *sm_ctx;
+
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
+ "<yang-library xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
+ " xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores\">"
+ " <module-set>"
+ " <name>test-set</name>"
+ " <module>"
+ " <name>ietf-datastores</name>"
+ " <revision>2018-02-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-datastores</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-library</name>"
+ " <revision>2019-01-04</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-library</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-yang-schema-mount</name>"
+ " <revision>2019-01-14</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-interfaces</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-interfaces</namespace>"
+ " </module>"
+ " <module>"
+ " <name>iana-if-type</name>"
+ " <revision>2014-05-08</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:iana-if-type</namespace>"
+ " </module>"
+ " <module>"
+ " <name>ietf-ip</name>"
+ " <revision>2014-06-16</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-ip</namespace>"
+ " </module>"
+ " <import-only-module>"
+ " <name>ietf-yang-types</name>"
+ " <revision>2013-07-15</revision>"
+ " <namespace>urn:ietf:params:xml:ns:yang:ietf-yang-types</namespace>"
+ " </import-only-module>"
+ " </module-set>"
+ " <schema>"
+ " <name>test-schema</name>"
+ " <module-set>test-set</module-set>"
+ " </schema>"
+ " <datastore>"
+ " <name>ds:running</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <datastore>"
+ " <name>ds:operational</name>"
+ " <schema>test-schema</schema>"
+ " </datastore>"
+ " <content-id>1</content-id>"
+ "</yang-library>"
+ "<modules-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\">"
+ " <module-set-id>1</module-set-id>"
+ "</modules-state>"
+ "<schema-mounts xmlns=\"urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount\">"
+ " <mount-point>"
+ " <module>sm</module>"
+ " <label>root</label>"
+ " <shared-schema/>"
+ " </mount-point>"
+ "</schema-mounts>");
+
+ parent = lys_find_path(UTEST_LYCTX, NULL, "/sm:root", 0);
+ assert_non_null(parent);
+
+ node = lys_getnext(NULL, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT);
+ assert_non_null(node);
+ assert_string_equal(node->name, "schema-mounts");
+ sm_ctx = node->module->ctx;
+
+ node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT);
+ assert_non_null(node);
+ assert_string_equal(node->name, "yang-library");
+
+ node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT);
+ assert_non_null(node);
+ assert_string_equal(node->name, "modules-state");
+
+ node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT);
+ assert_non_null(node);
+ assert_string_equal(node->name, "interfaces");
+
+ node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT);
+ assert_non_null(node);
+ assert_string_equal(node->name, "interfaces-state");
+
+ node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHSCHEMAMOUNT);
+ assert_null(node);
+
+ ly_ctx_destroy(sm_ctx);
+}
+
int
main(void)
{
@@ -1560,6 +1662,7 @@ main(void)
UTEST(test_parse_shared_parent_ref, setup),
UTEST(test_parse_config, setup),
UTEST(test_new, setup),
+ UTEST(test_lys_getnext, setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/extensions/test_structure.c b/tests/utests/extensions/test_structure.c
index 23af450..9bad7a7 100644
--- a/tests/utests/extensions/test_structure.c
+++ b/tests/utests/extensions/test_structure.c
@@ -148,7 +148,7 @@ test_schema_invalid(void **state)
"sx:structure struct {import yang;}}";
UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Invalid keyword \"import\" as a child of \"sx:structure struct\" extension instance.",
- "/a:{extension='sx:structure'}/struct");
+ "Path \"/a:{extension='sx:structure'}/struct\".");
data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;"
"import ietf-yang-structure-ext {prefix sx;}"
@@ -156,14 +156,14 @@ test_schema_invalid(void **state)
UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": "
"Extension sx:structure must not be used as a non top-level statement in \"container\" statement.",
- "/a:b/{extension='sx:structure'}/struct");
+ "Path \"/a:b/{extension='sx:structure'}/struct\".");
data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;"
"import ietf-yang-structure-ext {prefix sx;}"
"sx:structure { container x { leaf x {type string;}}}}";
UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Extension instance \"sx:structure\" missing argument element \"name\".", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Extension instance \"sx:structure\" missing argument element \"name\".", NULL);
data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;"
"import ietf-yang-structure-ext {prefix sx;}"
@@ -171,7 +171,7 @@ test_schema_invalid(void **state)
"sx:structure struct { container y { leaf y {type string;}}}}";
UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": Extension sx:structure is instantiated multiple times.",
- "/a:{extension='sx:structure'}/struct");
+ "Path \"/a:{extension='sx:structure'}/struct\".");
data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix self;"
"import ietf-yang-structure-ext {prefix sx;}"
@@ -179,7 +179,7 @@ test_schema_invalid(void **state)
"choice struct { container y { leaf y {type string;}}}}";
UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Ext plugin \"ly2 structure v1\": Extension sx:structure collides with a choice with the same identifier.",
- "/a:{extension='sx:structure'}/struct");
+ "Path \"/a:{extension='sx:structure'}/struct\".");
/* augment-structure */
data = "module a {yang-version 1.1; namespace urn:tests:extensions:structure:a; prefix a;"
@@ -199,7 +199,7 @@ test_schema_invalid(void **state)
"}}";
UTEST_INVALID_MODULE(data, LYS_IN_YANG, NULL, LY_ENOTFOUND);
CHECK_LOG_CTX("Augment extension target node \"/a:n1\" from module \"b\" was not found.",
- "/b:{extension='sx:augment-structure'}/{augment='/a:n1'}");
+ "Path \"/b:{extension='sx:augment-structure'}/{augment='/a:n1'}\".");
}
static void
diff --git a/tests/utests/extensions/test_yangdata.c b/tests/utests/extensions/test_yangdata.c
index 8c0176f..57caaf2 100644
--- a/tests/utests/extensions/test_yangdata.c
+++ b/tests/utests/extensions/test_yangdata.c
@@ -119,7 +119,7 @@ test_schema(void **state)
assert_null(mod->compiled->exts);
CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
"Extension rc:yang-data is ignored since it appears as a non top-level statement in \"container\" statement.",
- "/b:b/{extension='rc:yang-data'}/template");
+ "Path \"/b:b/{extension='rc:yang-data'}/template\".");
assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_YANG_COMPILED, 0));
assert_string_equal(printed, info);
free(printed);
@@ -168,7 +168,7 @@ test_schema_invalid(void **state)
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Invalid keyword \"leaf\" as a child of \"rc:yang-data template\" extension instance.",
- "/a:{extension='rc:yang-data'}/template");
+ "Path \"/a:{extension='rc:yang-data'}/template\".");
data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
"import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
@@ -177,7 +177,7 @@ test_schema_invalid(void **state)
CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
"Extension rc:yang-data is instantiated with leaf top level data node (inside a choice), "
"but only a single container data node is allowed.",
- "/a:{extension='rc:yang-data'}/template");
+ "Path \"/a:{extension='rc:yang-data'}/template\".");
data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
"import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
@@ -186,7 +186,7 @@ test_schema_invalid(void **state)
CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
"Extension rc:yang-data is instantiated with multiple top level data nodes (inside a single choice's case), "
"but only a single container data node is allowed.",
- "/a:{extension='rc:yang-data'}/template");
+ "Path \"/a:{extension='rc:yang-data'}/template\".");
data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
"import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
@@ -195,7 +195,7 @@ test_schema_invalid(void **state)
CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
"Extension rc:yang-data is instantiated with multiple top level data nodes, "
"but only a single container data node is allowed.",
- "/a:{extension='rc:yang-data'}/template");
+ "Path \"/a:{extension='rc:yang-data'}/template\".");
data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
"import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
@@ -204,14 +204,14 @@ test_schema_invalid(void **state)
CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
"Extension rc:yang-data is instantiated without any top level data node, "
"but exactly one container data node is expected.",
- "/a:{extension='rc:yang-data'}/template");
+ "Path \"/a:{extension='rc:yang-data'}/template\".");
data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
"import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
"rc:yang-data { container x { leaf x {type string;}}}}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Extension instance \"rc:yang-data\" missing argument element \"name\".", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Extension instance \"rc:yang-data\" missing argument element \"name\".", NULL);
data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
"import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
@@ -220,7 +220,7 @@ test_schema_invalid(void **state)
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, data, LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
"Extension rc:yang-data is instantiated multiple times.",
- "/a:{extension='rc:yang-data'}/template");
+ "Path \"/a:{extension='rc:yang-data'}/template\".");
data = "module a {yang-version 1.1; namespace urn:tests:extensions:yangdata:a; prefix self;"
"import ietf-restconf {revision-date 2017-01-26; prefix rc;}"
@@ -230,7 +230,7 @@ test_schema_invalid(void **state)
CHECK_LOG_CTX("Ext plugin \"ly2 yang-data v1\": "
"Extension rc:yang-data is instantiated with leaf-list top level data node, "
"but only a single container data node is allowed.",
- "/a:{extension='rc:yang-data'}/template");
+ "Path \"/a:{extension='rc:yang-data'}/template\".");
}
static void
diff --git a/tests/utests/node/list.c b/tests/utests/node/list.c
index 8b14ece..987b416 100644
--- a/tests/utests/node/list.c
+++ b/tests/utests/node/list.c
@@ -169,8 +169,8 @@ test_schema_yang(void **state)
"leaf group{type string;}"
"}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
- "Invalid value \"-1\" of \"max-elements\".", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"-1\" of \"max-elements\".", "Line number 5.");
schema = MODULE_CREATE_YANG("TERR_0", "list user {"
"key uid;"
@@ -181,8 +181,8 @@ test_schema_yang(void **state)
"leaf group{type string;}"
"}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
- "Value \"4294967298\" is out of \"max-elements\" bounds.", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL);
+ CHECK_LOG_CTX("Value \"4294967298\" is out of \"max-elements\" bounds.", "Line number 5.");
schema = MODULE_CREATE_YANG("TERR_0", "list user {"
"key uid;"
@@ -193,7 +193,7 @@ test_schema_yang(void **state)
"leaf group{type string;}"
"}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("List min-elements 20 is bigger than max-elements 10.", "/TERR_0:user");
+ CHECK_LOG_CTX("List min-elements 20 is bigger than max-elements 10.", "Path \"/TERR_0:user\".");
schema = MODULE_CREATE_YANG("TERR_0", "list user {"
"key uid;"
@@ -204,8 +204,8 @@ test_schema_yang(void **state)
"leaf group{type string;}"
"}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
- "Invalid value \"-1\" of \"min-elements\".", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"-1\" of \"min-elements\".", "Line number 5.");
schema = MODULE_CREATE_YANG("TERR_0", "list user {"
"key uid;"
@@ -215,8 +215,8 @@ test_schema_yang(void **state)
"leaf group{type string;}"
"}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
- "Duplicate keyword \"key\".", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate keyword \"key\".", "Line number 5.");
schema = MODULE_CREATE_YANG("T6", "list user {"
"config false;"
@@ -282,8 +282,8 @@ test_schema_yang(void **state)
"leaf group{type string;}"
"}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL,
- "Invalid value \"systeme\" of \"ordered-by\".", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"systeme\" of \"ordered-by\".", "Line number 5.");
schema = MODULE_CREATE_YANG("TERROR0", "list \"\" {"
"key uid;"
@@ -294,8 +294,8 @@ test_schema_yang(void **state)
"leaf group{type string;}"
"}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL,
- "Statement argument is required.", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL);
+ CHECK_LOG_CTX("Statement argument is required.", "Line number 5.");
schema = MODULE_CREATE_YANG("T9", "list user {"
"key uid;"
@@ -389,7 +389,7 @@ test_schema_yin(void **state)
" <leaf name=\"group\"><type name=\"string\"/></leaf>"
"</list>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("The list's key \"u<id\" not found.", "/T00:user");
+ CHECK_LOG_CTX("The list's key \"u<id\" not found.", "Path \"/T00:user\".");
schema = MODULE_CREATE_YIN("T1", "<list name=\"user\"> "
" <key value=\"uid\"/>"
@@ -498,8 +498,8 @@ test_schema_yin(void **state)
" <leaf name=\"uid\"> <type name=\"int32\"> </leaf>"
"</list>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
- "Invalid value \"-1\" of \"value\" attribute in \"max-elements\" element.", "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"-1\" of \"value\" attribute in \"max-elements\" element.", "Line number 8.");
schema = MODULE_CREATE_YIN("TERR_0",
"<list name=\"user\">"
@@ -511,8 +511,8 @@ test_schema_yin(void **state)
" <leaf name=\"group\"> <type name=\"string\"/> </leaf>"
"</list>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
- "Value \"4294967298\" of \"value\" attribute in \"max-elements\" element is out of bounds.", "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL);
+ CHECK_LOG_CTX("Value \"4294967298\" of \"value\" attribute in \"max-elements\" element is out of bounds.", "Line number 8.");
schema = MODULE_CREATE_YIN("TERR_0",
"<list name=\"user\">"
@@ -524,8 +524,9 @@ test_schema_yin(void **state)
" <leaf name=\"group\"> <type name=\"string\"/> </leaf>"
"</list>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
- "Invalid combination of min-elements and max-elements: min value 20 is bigger than the max value 10.", "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid combination of min-elements and max-elements: min value 20 is bigger than the max value 10.",
+ "Line number 8.");
schema = MODULE_CREATE_YIN("TERR_0",
"<list name=\"user\">"
@@ -537,8 +538,8 @@ test_schema_yin(void **state)
" <leaf name=\"group\"> <type name=\"string\"/> </leaf>"
"</list>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
- "Value \"-1\" of \"value\" attribute in \"min-elements\" element is out of bounds.", "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL);
+ CHECK_LOG_CTX("Value \"-1\" of \"value\" attribute in \"min-elements\" element is out of bounds.", "Line number 8.");
schema = MODULE_CREATE_YIN("TERR_0",
"<list name=\"user\">"
@@ -549,8 +550,8 @@ test_schema_yin(void **state)
" <leaf name=\"group\"> <type name=\"string\"/> </leaf>"
"</list>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL,
- "Redefinition of \"key\" sub-element in \"list\" element.", "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_0\" failed.", NULL);
+ CHECK_LOG_CTX("Redefinition of \"key\" sub-element in \"list\" element.", "Line number 8.");
schema = MODULE_CREATE_YIN("T6",
"<list name=\"user\">"
@@ -620,8 +621,8 @@ test_schema_yin(void **state)
" <leaf name=\"group\"><type name=\"string\"/> </leaf>"
"</list>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL,
- "Invalid value \"systeme\" of \"value\" attribute in \"ordered-by\" element. Valid values are \"system\" and \"user\".",
+ CHECK_LOG_CTX("Parsing module \"TERROR0\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"systeme\" of \"value\" attribute in \"ordered-by\" element. Valid values are \"system\" and \"user\".",
"Line number 8.");
schema = MODULE_CREATE_YIN("T_DEFS1",
diff --git a/tests/utests/schema/test_printer_tree.c b/tests/utests/schema/test_printer_tree.c
index c076ece..40fb15f 100644
--- a/tests/utests/schema/test_printer_tree.c
+++ b/tests/utests/schema/test_printer_tree.c
@@ -1574,6 +1574,7 @@ print_compiled_node(void **state)
" yang-version 1.1;\n"
" namespace \"x:y\";\n"
" prefix x;\n"
+ "\n"
" container g {\n"
" leaf a {\n"
" type string;\n"
@@ -1586,6 +1587,12 @@ print_compiled_node(void **state)
" leaf c {\n"
" type string;\n"
" }\n"
+ " list l {\n"
+ " key \"ip\";\n"
+ " leaf ip {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
" }\n"
" }\n"
"}\n";
@@ -1610,13 +1617,31 @@ print_compiled_node(void **state)
ly_out_reset(UTEST_OUT);
+ /* pyang -f tree --tree-path /g/h/l */
+ expect =
+ "module: a26\n"
+ " +--rw g\n"
+ " +--rw h\n"
+ " +--rw l* [ip]\n"
+ " +--rw ip string\n";
+
+ node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h/l", 0);
+ CHECK_POINTER(node, 1);
+ assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0));
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+
+ ly_out_reset(UTEST_OUT);
+
/* pyang -f tree --tree-path /g/h */
expect =
"module: a26\n"
" +--rw g\n"
" +--rw h\n"
" +--rw b string\n"
- " +--rw c? string\n";
+ " +--rw c? string\n"
+ " +--rw l* [ip]\n"
+ " +--rw ip string\n";
node = lys_find_path(UTEST_LYCTX, NULL, "/a26:g/h", 0);
CHECK_POINTER(node, 1);
@@ -1643,6 +1668,59 @@ print_compiled_node(void **state)
TEST_LOCAL_TEARDOWN;
}
+static void
+print_compiled_node_augment(void **state)
+{
+ TEST_LOCAL_SETUP;
+ const struct lysc_node *node;
+
+ orig =
+ "module b26xx {\n"
+ " yang-version 1.1;\n"
+ " namespace \"xx:y\";\n"
+ " prefix xx;\n"
+ " container c;\n"
+ "}\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+
+ /* module with import statement */
+ orig =
+ "module b26 {\n"
+ " yang-version 1.1;\n"
+ " namespace \"x:y\";\n"
+ " prefix x;\n"
+ "\n"
+ " import b26xx {\n"
+ " prefix xx;\n"
+ " }\n"
+ "\n"
+ " augment \"/xx:c\" {\n"
+ " container e;\n"
+ " }\n"
+ "}\n";
+
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+
+ /* pyang -f tree --tree-path /c/e ... but prefixes modified */
+ expect =
+ "module: b26\n"
+ " +--rw xx:c\n"
+ " +--rw e\n";
+
+ /* using lysc tree */
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+ node = lys_find_path(UTEST_LYCTX, NULL, "/b26xx:c/b26:e", 0);
+ CHECK_POINTER(node, 1);
+ assert_int_equal(LY_SUCCESS, lys_print_node(UTEST_OUT, node, LYS_OUT_TREE, 72, 0));
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
static LY_ERR
local_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name),
const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format,
@@ -2365,6 +2443,43 @@ structure(void **state)
TEST_LOCAL_TEARDOWN;
}
+static void
+annotation(void **state)
+{
+ TEST_LOCAL_SETUP;
+
+ orig =
+ "module ann {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:example:ann\";\n"
+ " prefix an;\n"
+ "\n"
+ " import ietf-yang-metadata {\n"
+ " prefix md;\n"
+ " }\n"
+ "\n"
+ " leaf lf1 {\n"
+ " type string;\n"
+ " }\n"
+ " md:annotation avalue {\n"
+ " type string;\n"
+ " }\n"
+ "}\n";
+
+ expect =
+ "module: ann\n"
+ " +--rw lf1? string\n";
+
+ /* annotation is ignored without error message */
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
+ TEST_LOCAL_TEARDOWN;
+}
+
int
main(void)
{
@@ -2395,10 +2510,12 @@ main(void)
UTEST(transition_between_rpc_and_notif),
UTEST(local_augment),
UTEST(print_compiled_node),
+ UTEST(print_compiled_node_augment),
UTEST(print_parsed_submodule),
UTEST(yang_data),
UTEST(mount_point),
UTEST(structure),
+ UTEST(annotation),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/schema/test_schema.c b/tests/utests/schema/test_schema.c
index 175b569..17c4e4f 100644
--- a/tests/utests/schema/test_schema.c
+++ b/tests/utests/schema/test_schema.c
@@ -91,7 +91,8 @@ test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const ch
const char *test_str__; \
TEST_SCHEMA_STR(RFC7950, YIN, MOD_NAME, CONTENT, test_str__) \
assert_int_not_equal(lys_parse_mem(UTEST_LYCTX, test_str__, YIN ? LYS_IN_YIN : LYS_IN_YANG, NULL), LY_SUCCESS); \
- CHECK_LOG_CTX("Parsing module \""MOD_NAME"\" failed.", NULL, ERRMSG, ERRPATH); \
+ CHECK_LOG_CTX("Parsing module \""MOD_NAME"\" failed.", NULL); \
+ CHECK_LOG_CTX(ERRMSG, ERRPATH); \
}
#define TEST_STMT_DUP(RFC7950, YIN, STMT, MEMBER, VALUE1, VALUE2, LINE) \
@@ -283,12 +284,12 @@ test_revisions(void **state)
strcpy(rev->date, "2018-12-31");
assert_int_equal(2, LY_ARRAY_COUNT(revs));
- assert_string_equal("2018-01-01", &revs[0]);
- assert_string_equal("2018-12-31", &revs[1]);
+ assert_string_equal("2018-01-01", revs[0].date);
+ assert_string_equal("2018-12-31", revs[1].date);
/* the order should be fixed, so the newest revision will be the first in the array */
lysp_sort_revisions(revs);
- assert_string_equal("2018-12-31", &revs[0]);
- assert_string_equal("2018-01-01", &revs[1]);
+ assert_string_equal("2018-12-31", revs[0].date);
+ assert_string_equal("2018-01-01", revs[1].date);
LY_ARRAY_FREE(revs);
}
@@ -306,80 +307,80 @@ test_collision_typedef(void **state)
/* collision with a built-in type */
str = "module a {namespace urn:a; prefix a; typedef binary {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"binary\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"binary\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef bits {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"bits\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"bits\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef boolean {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"boolean\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"boolean\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef decimal64 {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"decimal64\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"decimal64\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef empty {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"empty\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"empty\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef enumeration {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"enumeration\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"enumeration\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef int8 {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"int8\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"int8\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef int16 {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"int16\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"int16\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef int32 {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"int32\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"int32\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef int64 {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"int64\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"int64\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef instance-identifier {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"instance-identifier\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"instance-identifier\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef identityref {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"identityref\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"identityref\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef leafref {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"leafref\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"leafref\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef string {type int8;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"string\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"string\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef union {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"union\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"union\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef uint8 {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"uint8\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"uint8\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef uint16 {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"uint16\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"uint16\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef uint32 {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"uint32\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"uint32\" of typedef statement - name collision with a built-in type.", NULL);
str = "module a {namespace urn:a; prefix a; typedef uint64 {type string;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"uint64\" of typedef statement - name collision with a built-in type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"uint64\" of typedef statement - name collision with a built-in type.", NULL);
str = "module mytypes {namespace urn:types; prefix t; typedef binary_ {type string;} typedef bits_ {type string;} typedef boolean_ {type string;} "
"typedef decimal64_ {type string;} typedef empty_ {type string;} typedef enumeration_ {type string;} typedef int8_ {type string;} typedef int16_ {type string;}"
@@ -391,34 +392,34 @@ test_collision_typedef(void **state)
/* collision in node's scope */
str = "module a {namespace urn:a; prefix a; container c {typedef y {type int8;} typedef y {type string;}}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"y\" of typedef statement - name collision with sibling type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"y\" of typedef statement - name collision with sibling type.", NULL);
/* collision with parent node */
str = "module a {namespace urn:a; prefix a; container c {container d {typedef y {type int8;}} typedef y {type string;}}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"y\" of typedef statement - name collision with another scoped type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"y\" of typedef statement - name collision with another scoped type.", NULL);
/* collision with module's top-level */
str = "module a {namespace urn:a; prefix a; typedef x {type string;} container c {typedef x {type int8;}}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL);
/* collision of submodule's node with module's top-level */
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} container c {typedef x {type string;}}}");
str = "module a {namespace urn:a; prefix a; include b; typedef x {type int8;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL);
/* collision of module's node with submodule's top-level */
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} typedef x {type int8;}}");
str = "module a {namespace urn:a; prefix a; include b; container c {typedef x {type string;}}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - scoped type collide with a top-level type.", NULL);
/* collision of submodule's node with another submodule's top-level */
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
@@ -426,29 +427,29 @@ test_collision_typedef(void **state)
list[1].data = "submodule bsub {belongs-to a {prefix a;} container c {typedef g {type int;}}}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of typedef statement - scoped type collide with a top-level type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of typedef statement - scoped type collide with a top-level type.", NULL);
/* collision of module's top-levels */
str = "module a {namespace urn:a; prefix a; typedef test {type string;} typedef test {type int8;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"test\" of typedef statement - name collision with another top-level type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"test\" of typedef statement - name collision with another top-level type.", NULL);
/* collision of submodule's top-levels */
submod = "submodule asub {belongs-to a {prefix a;} typedef g {type int;} typedef g {type int;}}";
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of typedef statement - name collision with another top-level type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of typedef statement - name collision with another top-level type.", NULL);
/* collision of module's top-level with submodule's top-levels */
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} typedef x {type string;}}");
str = "module a {namespace urn:a; prefix a; include b; typedef x {type int8;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"x\" of typedef statement - name collision with another top-level type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of typedef statement - name collision with another top-level type.", NULL);
/* collision of submodule's top-level with another submodule's top-levels */
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
@@ -456,15 +457,14 @@ test_collision_typedef(void **state)
list[1].data = "submodule bsub {belongs-to a {prefix a;} typedef g {type int;}}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of typedef statement - name collision with another top-level type.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of typedef statement - name collision with another top-level type.", NULL);
/* error in type-stmt */
str = "module a {namespace urn:a; prefix a; container c {typedef x {type t{}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Unexpected end-of-input.", "Line number 1.");
- UTEST_LOG_CLEAN;
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Unexpected end-of-input.", "Line number 1.");
/* no collision if the same names are in different scope */
str = "module a {yang-version 1.1; namespace urn:a; prefix a;"
@@ -485,34 +485,34 @@ test_collision_grouping(void **state)
/* collision in node's scope */
str = "module a {namespace urn:a; prefix a; container c {grouping y; grouping y;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"y\" of grouping statement - name collision with sibling grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"y\" of grouping statement - name collision with sibling grouping.", NULL);
/* collision with parent node */
str = "module a {namespace urn:a; prefix a; container c {container d {grouping y;} grouping y;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"y\" of grouping statement - name collision with another scoped grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"y\" of grouping statement - name collision with another scoped grouping.", NULL);
/* collision with module's top-level */
str = "module a {namespace urn:a; prefix a; grouping x; container c {grouping x;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
/* collision of submodule's node with module's top-level */
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} container c {grouping x;}}");
str = "module a {namespace urn:a; prefix a; include b; grouping x;}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
/* collision of module's node with submodule's top-level */
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} grouping x;}");
str = "module a {namespace urn:a; prefix a; include b; container c {grouping x;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
/* collision of submodule's node with another submodule's top-level */
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
@@ -520,29 +520,29 @@ test_collision_grouping(void **state)
list[1].data = "submodule bsub {belongs-to a {prefix a;} container c {grouping g;}}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
/* collision of module's top-levels */
str = "module a {namespace urn:a; prefix a; grouping test; grouping test;}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"test\" of grouping statement - name collision with another top-level grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"test\" of grouping statement - name collision with another top-level grouping.", NULL);
/* collision of submodule's top-levels */
submod = "submodule asub {belongs-to a {prefix a;} grouping g; grouping g;}";
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of grouping statement - name collision with another top-level grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - name collision with another top-level grouping.", NULL);
/* collision of module's top-level with submodule's top-levels */
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule b {belongs-to a {prefix a;} grouping x;}");
str = "module a {namespace urn:a; prefix a; include b; grouping x;}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"x\" of grouping statement - name collision with another top-level grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of grouping statement - name collision with another top-level grouping.", NULL);
/* collision of submodule's top-level with another submodule's top-levels */
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
@@ -550,25 +550,27 @@ test_collision_grouping(void **state)
list[1].data = "submodule bsub {belongs-to a {prefix a;} grouping g;}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of grouping statement - name collision with another top-level grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - name collision with another top-level grouping.", NULL);
/* collision in nested groupings, top-level */
str = "module a {namespace urn:a; prefix a; grouping g {grouping g;}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - scoped grouping collide with a top-level grouping.", NULL);
/* collision in nested groupings, in node */
str = "module a {namespace urn:a; prefix a; container c {grouping g {grouping g;}}}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of grouping statement - name collision with another scoped grouping.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of grouping statement - name collision with another scoped grouping.", NULL);
/* no collision if the same names are in different scope */
str = "module a {yang-version 1.1; namespace urn:a; prefix a;"
"container c {grouping g;} container d {grouping g;}}";
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
+ CHECK_LOG_CTX("Locally scoped grouping \"g\" not used.", NULL);
+ CHECK_LOG_CTX("Locally scoped grouping \"g\" not used.", NULL);
}
static void
@@ -584,24 +586,24 @@ test_collision_identity(void **state)
/* collision of module's top-levels */
str = "module a {yang-version 1.1; namespace urn:a; prefix a; identity g; identity g;}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
/* collision of submodule's top-levels */
submod = "submodule asub {belongs-to a {prefix a;} identity g; identity g;}";
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
/* collision of module's top-level with submodule's top-levels */
submod = "submodule asub {belongs-to a {prefix a;} identity g;}";
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; identity g;}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
/* collision of submodule's top-level with another submodule's top-levels */
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
@@ -609,8 +611,8 @@ test_collision_identity(void **state)
list[1].data = "submodule bsub {belongs-to a {prefix a;} identity g;}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of identity statement - name collision with another top-level identity.", NULL);
}
static void
@@ -626,24 +628,24 @@ test_collision_feature(void **state)
/* collision of module's top-levels */
str = "module a {yang-version 1.1; namespace urn:a; prefix a; feature g; feature g;}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
/* collision of submodule's top-levels */
submod = "submodule asub {belongs-to a {prefix a;} feature g; feature g;}";
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub;}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
/* collision of module's top-level with submodule's top-levels */
submod = "submodule asub {belongs-to a {prefix a;} feature g;}";
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; feature g;}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, submod);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
/* collision of submodule's top-level with another submodule's top-levels */
str = "module a {yang-version 1.1; namespace urn:a; prefix a; include asub; include bsub;}";
@@ -651,8 +653,8 @@ test_collision_feature(void **state)
list[1].data = "submodule bsub {belongs-to a {prefix a;} feature g;}";
ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL,
- "Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
+ CHECK_LOG_CTX("Parsing module \"a\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"g\" of feature statement - name collision with another top-level feature.", NULL);
}
static void
@@ -1046,15 +1048,15 @@ test_includes(void **state)
ly_ctx_set_module_imp_clb(UTEST_LYCTX, module_clb, list);
mod = ly_ctx_load_module(UTEST_LYCTX, "main_b", NULL, NULL);
assert_null(mod);
- CHECK_LOG_CTX("Loading \"main_b\" module failed.", NULL,
- "Data model \"main_b\" not found in local searchdirs.", NULL,
- "Parsing module \"main_b\" failed.", NULL,
- "Including \"sub_b_one\" submodule into \"main_b\" failed.", NULL,
- "Data model \"sub_b_one\" not found in local searchdirs.", NULL,
- "Parsing submodule \"sub_b_one\" failed.", NULL,
- "YANG 1.1 requires all submodules to be included from main module. But submodule \"sub_b_one\" includes "
- "submodule \"sub_b_two\" which is not included by main module \"main_b\".", NULL,
- "YANG version 1.1 expects all includes in main module, includes in submodules (sub_b_one) are not necessary.", NULL);
+ CHECK_LOG_CTX("Loading \"main_b\" module failed.", NULL);
+ CHECK_LOG_CTX("Data model \"main_b\" not found in local searchdirs.", NULL);
+ CHECK_LOG_CTX("Parsing module \"main_b\" failed.", NULL);
+ CHECK_LOG_CTX("Including \"sub_b_one\" submodule into \"main_b\" failed.", NULL);
+ CHECK_LOG_CTX("Data model \"sub_b_one\" not found in local searchdirs.", NULL);
+ CHECK_LOG_CTX("Parsing submodule \"sub_b_one\" failed.", NULL);
+ CHECK_LOG_CTX("YANG 1.1 requires all submodules to be included from main module. But submodule \"sub_b_one\" includes "
+ "submodule \"sub_b_two\" which is not included by main module \"main_b\".", NULL);
+ CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (sub_b_one) are not necessary.", NULL);
}
{
@@ -1073,6 +1075,7 @@ test_includes(void **state)
assert_false(mod->parsed->includes[1].injected);
/* result is ok, but log includes the warning */
CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (sub_c_two) are not necessary.", NULL);
+ CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (sub_c_one) are not necessary.", NULL);
}
}
@@ -1083,7 +1086,8 @@ test_key_order(void **state)
const struct lysc_node *node;
struct module_clb_list list1[] = {
- {"a", "module a {"
+ {
+ "a", "module a {"
"yang-version 1.1;"
"namespace urn:test:a;"
"prefix a;"
@@ -1092,7 +1096,8 @@ test_key_order(void **state)
" leaf k2 {type string;}"
" leaf k1 {type string;}"
"}"
- "}"},
+ "}"
+ },
{NULL, NULL}
};
@@ -1106,7 +1111,8 @@ test_key_order(void **state)
assert_string_equal("k2", node->name);
struct module_clb_list list2[] = {
- {"b", "module b {"
+ {
+ "b", "module b {"
"yang-version 1.1;"
"namespace urn:test:b;"
"prefix b;"
@@ -1121,7 +1127,8 @@ test_key_order(void **state)
" leaf k1 {type string;}"
" leaf k3 {type string;}"
"}"
- "}"},
+ "}"
+ },
{NULL, NULL}
};
@@ -1156,7 +1163,7 @@ test_disabled_enum(void **state)
"}}"
"}";
assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Enumeration type of node \"l\" without any (or all disabled) valid values.", "Schema location \"/a:l\".");
+ CHECK_LOG_CTX("Node \"l\" without any (or all disabled) valid values.", "Schema location \"/a:l\".");
/* disabled default value */
str = "module a {"
@@ -1263,10 +1270,10 @@ test_identity(void **state)
assert_ptr_equal(mod->identities[1].derived[0], &mod->identities[0]);
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule inv_sub {belongs-to inv {prefix inv;} identity i1;}");
- TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i2;}", "Unable to find base (i2) of identity \"i1\".", "/inv:{identity='i1'}");
- TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i1;}", "Identity \"i1\" is derived from itself.", "/inv:{identity='i1'}");
+ TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i2;}", "Unable to find base (i2) of identity \"i1\".", "Path \"/inv:{identity='i1'}\".");
+ TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i1;}", "Identity \"i1\" is derived from itself.", "Path \"/inv:{identity='i1'}\".");
TEST_SCHEMA_ERR(0, 0, "inv", "identity i1 {base i2;}identity i2 {base i3;}identity i3 {base i1;}",
- "Identity \"i1\" is indirectly derived from itself.", "/inv:{identity='i3'}");
+ "Identity \"i1\" is indirectly derived from itself.", "Path \"/inv:{identity='i3'}\".");
/* base in non-implemented module */
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb,
@@ -1606,8 +1613,8 @@ test_extension_argument_element(void **state)
/* invalid */
mod_test_yang = "module x { namespace \"urn:x\"; prefix x; import a { prefix a; } a:e; }";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yang, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL,
- "Extension instance \"a:e\" missing argument element \"name\".", NULL);
+ CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL);
+ CHECK_LOG_CTX("Extension instance \"a:e\" missing argument element \"name\".", NULL);
mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<module name=\"x\"\n"
@@ -1622,8 +1629,8 @@ test_extension_argument_element(void **state)
" <a:e/>\n"
"</module>\n";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
- CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL,
- "Extension instance \"a:e\" missing argument element \"name\".", NULL);
+ CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL);
+ CHECK_LOG_CTX("Extension instance \"a:e\" missing argument element \"name\".", NULL);
mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<module name=\"x\"\n"
@@ -1638,8 +1645,8 @@ test_extension_argument_element(void **state)
" <a:e name=\"xxx\"/>\n"
"</module>\n";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
- CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL,
- "Extension instance \"a:e\" missing argument element \"name\".", NULL);
+ CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL);
+ CHECK_LOG_CTX("Extension instance \"a:e\" missing argument element \"name\".", NULL);
mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<module name=\"x\"\n"
@@ -1656,8 +1663,8 @@ test_extension_argument_element(void **state)
" </a:e>\n"
"</module>\n";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
- CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL,
- "Extension instance \"a:e\" element and its argument element \"name\" are expected in the same namespace, but they differ.",
+ CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL);
+ CHECK_LOG_CTX("Extension instance \"a:e\" element and its argument element \"name\" are expected in the same namespace, but they differ.",
NULL);
mod_test_yin = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
@@ -1675,8 +1682,8 @@ test_extension_argument_element(void **state)
" </a:e>\n"
"</module>\n";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, mod_test_yin, LYS_IN_YIN, NULL));
- CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL,
- "Extension instance \"a:e\" expects argument element \"name\" as its first XML child, but \"value\" element found.",
+ CHECK_LOG_CTX("Parsing module \"x\" failed.", NULL);
+ CHECK_LOG_CTX("Extension instance \"a:e\" expects argument element \"name\" as its first XML child, but \"value\" element found.",
NULL);
}
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index d6f0538..85da486 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -1,9 +1,10 @@
-/*
+/**
* @file test_tree_schema_compile.c
- * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
* @brief unit tests for functions from parser_yang.c
*
- * Copyright (c) 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2018 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -82,6 +83,7 @@ test_module(void **state)
ly_in_free(in, 0);
assert_int_equal(0, mod->implemented);
assert_int_equal(LY_EINVAL, lys_set_implemented(mod, feats));
+ CHECK_LOG_CTX("Feature \"invalid\" not found in module \"test\".", NULL);
assert_int_equal(LY_SUCCESS, lys_set_implemented(mod, NULL));
assert_non_null(mod->compiled);
assert_string_equal("test", mod->name);
@@ -112,7 +114,7 @@ test_module(void **state)
assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
assert_int_equal(LY_EEXIST, lys_parse(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &mod));
ly_in_free(in, 0);
- CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/aa:a");
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "Path \"/aa:a\".");
}
static void
@@ -127,7 +129,7 @@ test_name_collisions(void **state)
" leaf c {type empty;}"
"}";
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "/a:c");
+ CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "Path \"/a:c\".");
UTEST_LOG_CLEAN;
yang_data = "module a {namespace urn:a;prefix a;"
@@ -136,7 +138,7 @@ test_name_collisions(void **state)
" notification c;"
"}";
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "/a:c");
+ CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "Path \"/a:c\".");
UTEST_LOG_CLEAN;
yang_data = "module a {namespace urn:a;prefix a;"
@@ -145,7 +147,7 @@ test_name_collisions(void **state)
" rpc c;"
"}";
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "/a:c");
+ CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "Path \"/a:c\".");
UTEST_LOG_CLEAN;
yang_data = "module a {namespace urn:a;prefix a;"
@@ -159,7 +161,7 @@ test_name_collisions(void **state)
" }"
"}";
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "/a:ch/c/c");
+ CHECK_LOG_CTX("Duplicate identifier \"c\" of data definition/RPC/action/notification statement.", "Path \"/a:ch/c/c\".");
UTEST_LOG_CLEAN;
/* nested */
@@ -168,7 +170,7 @@ test_name_collisions(void **state)
"container a;"
"}}}";
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/a:c/l/a");
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "Path \"/a:c/l/a\".");
UTEST_LOG_CLEAN;
yang_data = "module a {yang-version 1.1;namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}"
@@ -176,7 +178,7 @@ test_name_collisions(void **state)
"notification a;"
"}}}";
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/a:c/l/a");
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "Path \"/a:c/l/a\".");
UTEST_LOG_CLEAN;
yang_data = "module a {yang-version 1.1;namespace urn:a;prefix a;container c { list l {key \"k\"; leaf k {type string;}"
@@ -184,7 +186,7 @@ test_name_collisions(void **state)
"action a;"
"}}}";
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, yang_data, LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/a:c/l/a");
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "Path \"/a:c/l/a\".");
UTEST_LOG_CLEAN;
/* grouping */
@@ -250,15 +252,6 @@ test_node_leaflist(void **state)
assert_non_null(((struct lysc_type_leafref *)type)->realtype);
assert_int_equal(LY_TYPE_INT8, ((struct lysc_type_leafref *)type)->realtype->basetype);
- /* now test for string type is in file ./tests/utests/types/string.c */
-#if 0
- assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;leaf-list ll {type string;}}", LYS_IN_YANG, &mod));
- assert_non_null(mod->compiled);
- assert_non_null((ll = (struct lysc_node_leaflist *)mod->compiled->data));
- assert_int_equal(0, ll->min);
- assert_int_equal((uint32_t)-1, ll->max);
-#endif
-
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module c {yang-version 1.1;namespace urn:c;prefix c;typedef mytype {type int8;default 10;}"
"leaf-list ll1 {type mytype;default 1; default 1; config false;}"
"leaf-list ll2 {type mytype; ordered-by user;}}", LYS_IN_YANG, &mod));
@@ -304,7 +297,7 @@ test_node_leaflist(void **state)
/* invalid */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;leaf-list ll {type empty;}}",
LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "/aa:ll");
+ CHECK_LOG_CTX("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "Path \"/aa:ll\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {yang-version 1.1;namespace urn:bb;prefix bb;leaf-list ll {type empty; default x;}}", LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "Schema location \"/bb:ll\".");
@@ -317,12 +310,12 @@ test_node_leaflist(void **state)
assert_int_equal(3, LY_ARRAY_COUNT(ll->dflts));
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;"
"leaf-list ll {type string; default one;default two;default one;}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Configuration leaf-list has multiple defaults of the same value \"one\".", "/dd:ll");
+ CHECK_LOG_CTX("Configuration leaf-list has multiple defaults of the same value \"one\".", "Path \"/dd:ll\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;"
"leaf ref {type instance-identifier {require-instance true;} default \"/ee:g\";}}", LYS_IN_YANG, NULL));
CHECK_LOG_CTX("Invalid default - value does not fit the type "
- "(Invalid instance-identifier \"/ee:g\" value - semantic error.).", "Schema location \"/ee:ref\".");
+ "(Invalid instance-identifier \"/ee:g\" value - semantic error: Not found node \"g\" in path.).", "Schema location \"/ee:ref\".");
}
static void
@@ -412,11 +405,11 @@ test_node_list(void **state)
/* invalid */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;list l;}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Missing key in list representing configuration data.", "/aa:l");
+ CHECK_LOG_CTX("Missing key in list representing configuration data.", "Path \"/aa:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {yang-version 1.1; namespace urn:bb;prefix bb;"
"list l {key x; leaf x {type string; when 1;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("List's key must not have any \"when\" statement.", "/bb:l/x");
+ CHECK_LOG_CTX("List's key must not have any \"when\" statement.", "Path \"/bb:l/x\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;feature f;"
"list l {key x; leaf x {type string; if-feature f;}}}", LYS_IN_YANG, NULL));
@@ -424,43 +417,43 @@ test_node_list(void **state)
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;"
"list l {key x; leaf x {type string; config false;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Key of a configuration list must not be a state leaf.", "/dd:l/x");
+ CHECK_LOG_CTX("Key of a configuration list must not be a state leaf.", "Path \"/dd:l/x\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;"
"list l {config false;key x; leaf x {type string; config true;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "/ee:l/x");
+ CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "Path \"/ee:l/x\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;"
"list l {key x; leaf-list x {type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("The list's key \"x\" not found.", "/ff:l");
+ CHECK_LOG_CTX("The list's key \"x\" not found.", "Path \"/ff:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;"
"list l {key x; unique y;leaf x {type string;} leaf-list y {type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Unique's descendant-schema-nodeid \"y\" refers to leaf-list node instead of a leaf.", "/gg:l");
+ CHECK_LOG_CTX("Unique's descendant-schema-nodeid \"y\" refers to leaf-list node instead of a leaf.", "Path \"/gg:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;"
"list l {key x; unique \"x y\";leaf x {type string;} leaf y {config false; type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Unique statement \"x y\" refers to leaves with different config type.", "/hh:l");
+ CHECK_LOG_CTX("Unique statement \"x y\" refers to leaves with different config type.", "Path \"/hh:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;"
"list l {key x; unique a:x;leaf x {type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"a:x\" - prefix \"a\" not defined in module \"ii\".", "/ii:l");
+ CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"a:x\" - prefix \"a\" not defined in module \"ii\".", "Path \"/ii:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;"
"list l {key x; unique c/x;leaf x {type string;}container c {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"c/x\" - target node not found.", "/jj:l");
+ CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"c/x\" - target node not found.", "Path \"/jj:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;"
"list l {key x; unique c^y;leaf x {type string;}container c {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"c^\" - missing \"/\" as node-identifier separator.", "/kk:l");
+ CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"c^\" - missing \"/\" as node-identifier separator.", "Path \"/kk:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;"
"list l {key \"x y x\";leaf x {type string;}leaf y {type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicated key identifier \"x\".", "/ll:l");
+ CHECK_LOG_CTX("Duplicated key identifier \"x\".", "Path \"/ll:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;"
"list l {key x;leaf x {type empty;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("List's key cannot be of \"empty\" type until it is in YANG 1.1 module.", "/mm:l/x");
+ CHECK_LOG_CTX("List key of the \"empty\" type is allowed only in YANG 1.1 modules.", "Path \"/mm:l/x\".");
}
static void
@@ -499,26 +492,26 @@ test_node_choice(void **state)
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
"choice ch {case a {leaf x {type string;}}leaf x {type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"x\" of data definition/RPC/action/notification statement.", "/aa:ch/x/x");
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of data definition/RPC/action/notification statement.", "Path \"/aa:ch/x/x\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module aa2 {namespace urn:aa2;prefix aa;"
"choice ch {case a {leaf y {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"y\" of data definition/RPC/action/notification statement.", "/aa2:ch/b/y");
+ CHECK_LOG_CTX("Duplicate identifier \"y\" of data definition/RPC/action/notification statement.", "Path \"/aa2:ch/b/y\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;"
"choice ch {case a {leaf x {type string;}}leaf a {type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"a\" of case statement.", "/bb:ch/a");
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of case statement.", "Path \"/bb:ch/a\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb2 {namespace urn:bb2;prefix bb;"
"choice ch {case b {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"b\" of case statement.", "/bb2:ch/b");
+ CHECK_LOG_CTX("Duplicate identifier \"b\" of case statement.", "Path \"/bb2:ch/b\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ca {namespace urn:ca;prefix ca;"
"choice ch {default c;case a {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Default case \"c\" not found.", "/ca:ch");
+ CHECK_LOG_CTX("Default case \"c\" not found.", "Path \"/ca:ch\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cb {namespace urn:cb;prefix cb; import a {prefix a;}"
"choice ch {default a:a;case a {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Default case \"a:a\" not found.", "/cb:ch");
+ CHECK_LOG_CTX("Default case \"a:a\" not found.", "Path \"/cb:ch\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;"
"choice ch {default a;case a {leaf x {mandatory true;type string;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Mandatory node \"x\" under the default case \"a\".", "/cc:ch");
+ CHECK_LOG_CTX("Mandatory node \"x\" under the default case \"a\".", "Path \"/cc:ch\".");
/* TODO check with mandatory nodes from augment placed into the case */
}
@@ -544,8 +537,9 @@ test_node_anydata(void **state)
/* invalid */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;anydata any;}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Invalid keyword \"anydata\" as a child of \"module\" - the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid keyword \"anydata\" as a child of \"module\" - the statement is allowed only in YANG 1.1 modules.",
+ "Line number 1.");
}
static void
@@ -579,31 +573,32 @@ test_action(void **state)
/* invalid */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container top {action x;}}",
LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Invalid keyword \"action\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid keyword \"action\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules.",
+ "Line number 1.");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf x{type string;} rpc x;}",
LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"x\" of data definition/RPC/action/notification statement.", "/bb:x");
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of data definition/RPC/action/notification statement.", "Path \"/bb:x\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1; namespace urn:cc;prefix cc;container c {leaf y {type string;} action y;}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"y\" of data definition/RPC/action/notification statement.", "/cc:c/y");
+ CHECK_LOG_CTX("Duplicate identifier \"y\" of data definition/RPC/action/notification statement.", "Path \"/cc:c/y\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {action z; action z;}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"z\" of data definition/RPC/action/notification statement.", "/dd:c/z");
+ CHECK_LOG_CTX("Duplicate identifier \"z\" of data definition/RPC/action/notification statement.", "Path \"/dd:c/z\".");
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} notification w;}");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; rpc w;}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"w\" of data definition/RPC/action/notification statement.", "/ee:w");
+ CHECK_LOG_CTX("Duplicate identifier \"w\" of data definition/RPC/action/notification statement.", "Path \"/ee:w\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}"
"augment /test/input/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Action \"invalid\" is placed inside another RPC/action.", "/ff:{augment='/test/input/a'}/invalid");
+ CHECK_LOG_CTX("Action \"invalid\" is placed inside another RPC/action.", "Path \"/ff:{augment='/test/input/a'}/invalid\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}"
"augment /test/a {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Action \"invalid\" is placed inside notification.", "/gg:{augment='/test/a'}/invalid");
+ CHECK_LOG_CTX("Action \"invalid\" is placed inside notification.", "Path \"/gg:{augment='/test/a'}/invalid\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; notification test {container a {uses grp;}}"
"grouping grp {action invalid {input {leaf x {type string;}}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Action \"invalid\" is placed inside notification.", "/hh:test/a/{uses='grp'}/invalid");
+ CHECK_LOG_CTX("Action \"invalid\" is placed inside notification.", "Path \"/hh:test/a/{uses='grp'}/invalid\".");
}
static void
@@ -650,30 +645,31 @@ test_notification(void **state)
/* invalid */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container top {notification x;}}",
LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Invalid keyword \"notification\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid keyword \"notification\" as a child of \"container\" - the statement is allowed only in YANG 1.1 modules.",
+ "Line number 1.");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf x{type string;} notification x;}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"x\" of data definition/RPC/action/notification statement.", "/bb:x");
+ CHECK_LOG_CTX("Duplicate identifier \"x\" of data definition/RPC/action/notification statement.", "Path \"/bb:x\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1; namespace urn:cc;prefix cc;container c {leaf y {type string;} notification y;}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"y\" of data definition/RPC/action/notification statement.", "/cc:c/y");
+ CHECK_LOG_CTX("Duplicate identifier \"y\" of data definition/RPC/action/notification statement.", "Path \"/cc:c/y\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1; namespace urn:dd;prefix dd;container c {notification z; notification z;}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"z\" of data definition/RPC/action/notification statement.", "/dd:c/z");
+ CHECK_LOG_CTX("Duplicate identifier \"z\" of data definition/RPC/action/notification statement.", "Path \"/dd:c/z\".");
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule eesub {belongs-to ee {prefix ee;} rpc w;}");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {yang-version 1.1; namespace urn:ee;prefix ee;include eesub; notification w;}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Duplicate identifier \"w\" of data definition/RPC/action/notification statement.", "/ee:w");
+ CHECK_LOG_CTX("Duplicate identifier \"w\" of data definition/RPC/action/notification statement.", "Path \"/ee:w\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {yang-version 1.1; namespace urn:ff;prefix ff; rpc test {input {container a {leaf b {type string;}}}}"
"augment /test/input/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Notification \"invalid\" is placed inside RPC/action.", "/ff:{augment='/test/input/a'}/invalid");
+ CHECK_LOG_CTX("Notification \"invalid\" is placed inside RPC/action.", "Path \"/ff:{augment='/test/input/a'}/invalid\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {yang-version 1.1; namespace urn:gg;prefix gg; notification test {container a {leaf b {type string;}}}"
"augment /test/a {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Notification \"invalid\" is placed inside another notification.", "/gg:{augment='/test/a'}/invalid");
+ CHECK_LOG_CTX("Notification \"invalid\" is placed inside another notification.", "Path \"/gg:{augment='/test/a'}/invalid\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1; namespace urn:hh;prefix hh; rpc test {input {container a {uses grp;}}}"
"grouping grp {notification invalid {leaf x {type string;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Notification \"invalid\" is placed inside RPC/action.", "/hh:test/input/a/{uses='grp'}/invalid");
+ CHECK_LOG_CTX("Notification \"invalid\" is placed inside RPC/action.", "Path \"/hh:test/input/a/{uses='grp'}/invalid\".");
}
/**
@@ -686,21 +682,6 @@ test_type_range(void **state)
struct lys_module *mod;
struct lysc_type *type;
-#if 0
- /*test about int8 should be in tests/utests/types/int8.c*/
- assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module a {namespace urn:a;prefix a;leaf l {type int8 {range min..10|max;}}}", LYS_IN_YANG, &mod));
- type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
- assert_non_null(type);
- assert_int_equal(LY_TYPE_INT8, type->basetype);
- assert_non_null(((struct lysc_type_num *)type)->range);
- assert_non_null(((struct lysc_type_num *)type)->range->parts);
- assert_int_equal(2, LY_ARRAY_COUNT(((struct lysc_type_num *)type)->range->parts));
- assert_int_equal(-128, ((struct lysc_type_num *)type)->range->parts[0].min_64);
- assert_int_equal(10, ((struct lysc_type_num *)type)->range->parts[0].max_64);
- assert_int_equal(127, ((struct lysc_type_num *)type)->range->parts[1].min_64);
- assert_int_equal(127, ((struct lysc_type_num *)type)->range->parts[1].max_64);
-#endif
-
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module b {namespace urn:b;prefix b;leaf l {type int16 {range min..10|max;}}}", LYS_IN_YANG, &mod));
type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
assert_non_null(type);
@@ -937,83 +918,56 @@ test_type_length(void **state)
assert_int_equal(10, ((struct lysc_type_bin *)type)->length->parts[0].min_u64);
assert_int_equal(100, ((struct lysc_type_bin *)type)->length->parts[0].max_u64);
- /* new string is tested in file ./tests/utests/types/string.c */
-#if 0
- assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l;typedef mytype {type string {length 10..100;}}"
- "typedef mytype2 {type mytype {pattern '[0-9]*';}} leaf l {type mytype2 {pattern '[0-4]*';}}}", LYS_IN_YANG, &mod));
- type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
- assert_non_null(type);
- assert_int_equal(LY_TYPE_STRING, type->basetype);
- assert_int_equal(1, type->refcount);
- assert_non_null(((struct lysc_type_str *)type)->length);
- assert_non_null(((struct lysc_type_str *)type)->length->parts);
- assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_str *)type)->length->parts));
- assert_int_equal(10, ((struct lysc_type_str *)type)->length->parts[0].min_u64);
- assert_int_equal(100, ((struct lysc_type_str *)type)->length->parts[0].max_u64);
-
- assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;typedef mytype {type string {length 10;}}"
- "leaf l {type mytype {length min..max;}}}", LYS_IN_YANG, &mod));
- type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
- assert_non_null(type);
- assert_int_equal(LY_TYPE_STRING, type->basetype);
- assert_int_equal(1, type->refcount);
- assert_non_null(((struct lysc_type_str *)type)->length);
- assert_non_null(((struct lysc_type_str *)type)->length->parts);
- assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_type_str *)type)->length->parts));
- assert_int_equal(10, ((struct lysc_type_str *)type)->length->parts[0].min_u64);
- assert_int_equal(10, ((struct lysc_type_str *)type)->length->parts[0].max_u64);
-#endif
-
/* invalid values */
assert_int_equal(LY_EDENIED, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;leaf l {type binary {length -10;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - value \"-10\" does not fit the type limitations.", "/aa:l");
+ CHECK_LOG_CTX("Invalid length restriction - value \"-10\" does not fit the type limitations.", "Path \"/aa:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf l {type binary {length 18446744073709551616;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "/bb:l");
+ CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "Path \"/bb:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;leaf l {type binary {length \"max .. 10\";}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - unexpected data after max keyword (.. 10).", "/cc:l");
+ CHECK_LOG_CTX("Invalid length restriction - unexpected data after max keyword (.. 10).", "Path \"/cc:l\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;leaf l {type binary {length 50..10;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (10).", "/dd:l");
+ CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (10).", "Path \"/dd:l\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;leaf l {type binary {length \"50 | 10\";}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (10).", "/ee:l");
+ CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (10).", "Path \"/ee:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;leaf l {type binary {length \"x\";}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - unexpected data (x).", "/ff:l");
+ CHECK_LOG_CTX("Invalid length restriction - unexpected data (x).", "Path \"/ff:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;leaf l {type binary {length \"50 | min\";}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - unexpected data before min keyword (50 | ).", "/gg:l");
+ CHECK_LOG_CTX("Invalid length restriction - unexpected data before min keyword (50 | ).", "Path \"/gg:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;leaf l {type binary {length \"| 50\";}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - unexpected beginning of the expression (| 50).", "/hh:l");
+ CHECK_LOG_CTX("Invalid length restriction - unexpected beginning of the expression (| 50).", "Path \"/hh:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;leaf l {type binary {length \"10 ..\";}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - unexpected end of the expression after \"..\" (10 ..).", "/ii:l");
+ CHECK_LOG_CTX("Invalid length restriction - unexpected end of the expression after \"..\" (10 ..).", "Path \"/ii:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;leaf l {type binary {length \".. 10\";}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - unexpected \"..\" without a lower bound.", "/jj:l");
+ CHECK_LOG_CTX("Invalid length restriction - unexpected \"..\" without a lower bound.", "Path \"/jj:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;leaf l {type binary {length \"10 |\";}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - unexpected end of the expression (10 |).", "/kk:l");
+ CHECK_LOG_CTX("Invalid length restriction - unexpected end of the expression (10 |).", "Path \"/kk:l\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module kl {namespace urn:kl;prefix kl;leaf l {type binary {length \"10..20 | 15..30\";}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (15).", "/kl:l");
+ CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (15).", "Path \"/kl:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;typedef mytype {type binary {length 10;}}"
"leaf l {type mytype {length 11;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - the derived restriction (11) is not equally or more limiting.", "/ll:l");
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (11) is not equally or more limiting.", "Path \"/ll:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;typedef mytype {type binary {length 10..100;}}"
"leaf l {type mytype {length 1..11;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - the derived restriction (1..11) is not equally or more limiting.", "/mm:l");
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (1..11) is not equally or more limiting.", "Path \"/mm:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn {namespace urn:nn;prefix nn;typedef mytype {type binary {length 10..100;}}"
"leaf l {type mytype {length 20..110;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - the derived restriction (20..110) is not equally or more limiting.", "/nn:l");
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (20..110) is not equally or more limiting.", "Path \"/nn:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo {namespace urn:oo;prefix oo;typedef mytype {type binary {length 10..100;}}"
"leaf l {type mytype {length 20..30|110..120;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - the derived restriction (20..30|110..120) is not equally or more limiting.", "/oo:l");
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (20..30|110..120) is not equally or more limiting.", "Path \"/oo:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp;typedef mytype {type binary {length 10..11;}}"
"leaf l {type mytype {length 15;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - the derived restriction (15) is not equally or more limiting.", "/pp:l");
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (15) is not equally or more limiting.", "Path \"/pp:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module qq {namespace urn:qq;prefix qq;typedef mytype {type binary {length 10..20|30..40;}}"
"leaf l {type mytype {length 15..35;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - the derived restriction (15..35) is not equally or more limiting.", "/qq:l");
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (15..35) is not equally or more limiting.", "Path \"/qq:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module rr {namespace urn:rr;prefix rr;typedef mytype {type binary {length 10;}}"
"leaf l {type mytype {length 10..35;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid length restriction - the derived restriction (10..35) is not equally or more limiting.", "/rr:l");
+ CHECK_LOG_CTX("Invalid length restriction - the derived restriction (10..35) is not equally or more limiting.", "Path \"/rr:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ss {namespace urn:ss;prefix ss;leaf l {type binary {pattern '[0-9]*';}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid type restrictions for binary type.", "/ss:l");
+ CHECK_LOG_CTX("Invalid type restrictions for binary type.", "Path \"/ss:l\".");
}
static void
@@ -1152,61 +1106,63 @@ test_type_enum(void **state)
/* invalid cases */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; feature f; leaf l {type enumeration {"
"enum one {if-feature f;}}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Invalid keyword \"if-feature\" as a child of \"enum\" - the statement is allowed only in YANG 1.1 modules.", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid keyword \"if-feature\" as a child of \"enum\" - the statement is allowed only in YANG 1.1 modules.",
+ "Line number 1.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
"enum one {value -2147483649;}}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Invalid value \"-2147483649\" of \"value\".", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"-2147483649\" of \"value\".", "Line number 1.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
"enum one {value 2147483648;}}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Invalid value \"2147483648\" of \"value\".", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"2147483648\" of \"value\".", "Line number 1.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
"enum one; enum one;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Duplicate identifier \"one\" of enum statement.", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"one\" of enum statement.", "Line number 1.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
"enum '';}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Enum name must not be zero-length.", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Enum name must not be zero-length.", "Line number 1.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
"enum ' x';}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Enum name must not have any leading or trailing whitespaces (\" x\").", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Enum name must not have any leading or trailing whitespaces (\" x\").", "Line number 1.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
"enum 'x ';}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Enum name must not have any leading or trailing whitespaces (\"x \").", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Enum name must not have any leading or trailing whitespaces (\"x \").", "Line number 1.");
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type enumeration {"
"enum 'inva\nlid';}}}", LYS_IN_YANG, &mod));
CHECK_LOG_CTX("Control characters in enum name should be avoided (\"inva\nlid\", character number 5).", NULL);
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; leaf l {type enumeration;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Missing enum substatement for enumeration type.", "/bb:l");
+ CHECK_LOG_CTX("Missing enum substatement for enumeration type.", "Path \"/bb:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;typedef mytype {type enumeration {enum one;}}"
"leaf l {type mytype {enum two;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid enumeration - derived type adds new item \"two\".", "/cc:l");
+ CHECK_LOG_CTX("Invalid enumeration - derived type adds new item \"two\".", "Path \"/cc:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {yang-version 1.1;namespace urn:dd;prefix dd;typedef mytype {type enumeration {enum one;}}"
"leaf l {type mytype {enum one {value 1;}}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid enumeration - value of the item \"one\" has changed from 0 to 1 in the derived type.", "/dd:l");
+ CHECK_LOG_CTX("Invalid enumeration - value of the item \"one\" has changed from 0 to 1 in the derived type.", "Path \"/dd:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;leaf l {type enumeration {enum x {value 2147483647;}enum y;}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid enumeration - it is not possible to auto-assign enum value for \"y\" since the highest value is already 2147483647.", "/ee:l");
+ CHECK_LOG_CTX("Invalid enumeration - it is not possible to auto-assign enum value for \"y\" since the highest value is already 2147483647.",
+ "Path \"/ee:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;leaf l {type enumeration {enum x {value 1;}enum y {value 1;}}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid enumeration - value 1 collide in items \"y\" and \"x\".", "/ff:l");
+ CHECK_LOG_CTX("Invalid enumeration - value 1 collide in items \"y\" and \"x\".", "Path \"/ff:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;typedef mytype {type enumeration;}"
"leaf l {type mytype {enum one;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Missing enum substatement for enumeration type mytype.", "/gg:l");
+ CHECK_LOG_CTX("Missing enum substatement for enumeration type mytype.", "Path \"/gg:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh; typedef mytype {type enumeration {enum one;}}"
"leaf l {type mytype {enum one;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Enumeration type can be subtyped only in YANG 1.1 modules.", "/hh:l");
+ CHECK_LOG_CTX("Enumeration type can be subtyped only in YANG 1.1 modules.", "Path \"/hh:l\".");
}
static void
@@ -1256,43 +1212,43 @@ test_type_dec64(void **state)
/* invalid cases */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 0;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Invalid value \"0\" of \"fraction-digits\".", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"0\" of \"fraction-digits\".", "Line number 1.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits -1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Invalid value \"-1\" of \"fraction-digits\".", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"-1\" of \"fraction-digits\".", "Line number 1.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 19;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Value \"19\" is out of \"fraction-digits\" bounds.", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Value \"19\" is out of \"fraction-digits\" bounds.", "Line number 1.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Missing fraction-digits substatement for decimal64 type.", "/aa:l");
+ CHECK_LOG_CTX("Missing fraction-digits substatement for decimal64 type.", "Path \"/aa:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ab {namespace urn:ab;prefix ab; typedef mytype {type decimal64;}leaf l {type mytype;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Missing fraction-digits substatement for decimal64 type mytype.", "/ab:l");
+ CHECK_LOG_CTX("Missing fraction-digits substatement for decimal64 type mytype.", "Path \"/ab:l\".");
assert_int_equal(LY_EINVAL, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; leaf l {type decimal64 {fraction-digits 2;"
"range '3.142';}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Range boundary \"3.142\" of decimal64 type exceeds defined number (2) of fraction digits.", "/bb:l");
+ CHECK_LOG_CTX("Range boundary \"3.142\" of decimal64 type exceeds defined number (2) of fraction digits.", "Path \"/bb:l\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; leaf l {type decimal64 {fraction-digits 2;"
"range '4 | 3.14';}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (3.14).", "/cc:l");
+ CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (3.14).", "Path \"/cc:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; typedef mytype {type decimal64 {fraction-digits 2;}}"
"leaf l {type mytype {fraction-digits 3;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.", "/dd:l");
+ CHECK_LOG_CTX("Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.", "Path \"/dd:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module de {namespace urn:de;prefix de; typedef mytype {type decimal64 {fraction-digits 2;}}"
"typedef mytype2 {type mytype {fraction-digits 3;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid fraction-digits substatement for type \"mytype2\" not directly derived from decimal64 built-in type.", "/de:l");
+ CHECK_LOG_CTX("Invalid fraction-digits substatement for type \"mytype2\" not directly derived from decimal64 built-in type.", "Path \"/de:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type decimal64 {"
"fraction-digits 18;range '-10 .. 0';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid range restriction - invalid value \"-10000000000000000000\".", "/ee:l");
+ CHECK_LOG_CTX("Invalid range restriction - invalid value \"-10000000000000000000\".", "Path \"/ee:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type decimal64 {"
"fraction-digits 18;range '0 .. 10';}}leaf l {type mytype;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid range restriction - invalid value \"10000000000000000000\".", "/ee:l");
+ CHECK_LOG_CTX("Invalid range restriction - invalid value \"10000000000000000000\".", "Path \"/ee:l\".");
}
static void
@@ -1321,11 +1277,11 @@ test_type_instanceid(void **state)
/* invalid cases */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type instance-identifier {require-instance yes;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL,
- "Invalid value \"yes\" of \"require-instance\".", "Line number 1.");
+ CHECK_LOG_CTX("Parsing module \"aa\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"yes\" of \"require-instance\".", "Line number 1.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type instance-identifier {fraction-digits 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid type restrictions for instance-identifier type.", "/aa:l");
+ CHECK_LOG_CTX("Invalid type restrictions for instance-identifier type.", "Path \"/aa:l\".");
}
static ly_bool
@@ -1721,29 +1677,29 @@ test_type_identityref(void **state)
/* invalid cases */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; leaf l {type identityref;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Missing base substatement for identityref type.", "/aa:l");
+ CHECK_LOG_CTX("Missing base substatement for identityref type.", "Path \"/aa:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; typedef mytype {type identityref;}"
"leaf l {type mytype;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Missing base substatement for identityref type mytype.", "/bb:l");
+ CHECK_LOG_CTX("Missing base substatement for identityref type mytype.", "Path \"/bb:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; identity i; typedef mytype {type identityref {base i;}}"
"leaf l {type mytype {base i;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid base substatement for the type not directly derived from identityref built-in type.", "/cc:l");
+ CHECK_LOG_CTX("Invalid base substatement for the type not directly derived from identityref built-in type.", "Path \"/cc:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; identity i; typedef mytype {type identityref {base i;}}"
"typedef mytype2 {type mytype {base i;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid base substatement for the type \"mytype2\" not directly derived from identityref built-in type.", "/dd:l");
+ CHECK_LOG_CTX("Invalid base substatement for the type \"mytype2\" not directly derived from identityref built-in type.", "Path \"/dd:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee; identity i; identity j;"
"leaf l {type identityref {base i;base j;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Multiple bases in identityref type are allowed only in YANG 1.1 modules.", "/ee:l");
+ CHECK_LOG_CTX("Multiple bases in identityref type are allowed only in YANG 1.1 modules.", "Path \"/ee:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff; identity i;leaf l {type identityref {base j;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Unable to find base (j) of identityref.", "/ff:l");
+ CHECK_LOG_CTX("Unable to find base (j) of identityref.", "Path \"/ff:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;leaf l {type identityref {base x:j;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid prefix used for base (x:j) of identityref.", "/gg:l");
+ CHECK_LOG_CTX("Invalid prefix used for base (x:j) of identityref.", "Path \"/gg:l\".");
}
static void
@@ -1759,18 +1715,27 @@ test_type_leafref(void **state)
path = "invalid_path";
assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ CHECK_LOG_CTX("Unexpected XPath token \"NameTest\" (\"invalid_path\"), expected \"..\".", NULL);
+
path = "..";
assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ CHECK_LOG_CTX("Unexpected XPath expression end.", NULL);
+
path = "..[";
assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ CHECK_LOG_CTX("Unexpected XPath token \"[\" (\"[\"), expected \"Operator(Path)\".", NULL);
+
path = "../";
assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ CHECK_LOG_CTX("Unexpected XPath expression end.", NULL);
+
path = "/";
assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_LEAFREF, &expr));
+ CHECK_LOG_CTX("Unexpected XPath expression end.", NULL);
path = "../../pref:id/xxx[predicate]/invalid!!!";
assert_int_equal(LY_EVALID, ly_path_parse(UTEST_LYCTX, NULL, path, strlen(path), 1, LY_PATH_BEGIN_EITHER,
@@ -1861,6 +1826,7 @@ test_type_leafref(void **state)
"leaf target {if-feature 'f1'; type boolean;}}";
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, &mod));
CHECK_LOG_CTX("Target of leafref \"ref1\" cannot be referenced because it is disabled.", "Schema location \"/e:ref1\".");
+ CHECK_LOG_CTX("Not found node \"target\" in path.", "Schema location \"/e:ref1\".");
str = "module en {yang-version 1.1;namespace urn:en;prefix en;feature f1;"
"leaf ref1 {if-feature 'f1'; type leafref {path /target;}}"
@@ -1881,6 +1847,7 @@ test_type_leafref(void **state)
"leaf ref {must \"/cl:h > 0\"; type uint16;}}", LYS_IN_YANG, &mod));
ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED);
CHECK_LOG_CTX("Target of leafref \"g\" cannot be referenced because it is disabled.", "Schema location \"/cl:g\".");
+ CHECK_LOG_CTX("Not found node \"f\" in path.", "Schema location \"/cl:g\".");
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module f {namespace urn:f;prefix f;"
"list interface{key name;leaf name{type string;}list address {key ip;leaf ip {type string;}}}"
@@ -1956,6 +1923,13 @@ test_type_leafref(void **state)
assert_int_equal(LY_TYPE_BOOL, ((struct lysc_node_leaf *)mod->compiled->data)->dflt->realtype->basetype);
assert_int_equal(1, ((struct lysc_node_leaf *)mod->compiled->data)->dflt->boolean);
+ /* union reference */
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;"
+ "typedef s-ref {type union {type leafref {path '/str';}}}"
+ "leaf str {type string {length \"1..16\" {error-message \"Custom message\";}}}"
+ "leaf ref1 {type s-ref;}"
+ "leaf ref2 {type s-ref;}}", LYS_IN_YANG, NULL));
+
/* invalid paths */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;container a {leaf target2 {type uint8;}}"
"leaf ref1 {type leafref {path ../a/invalid;}}}", LYS_IN_YANG, &mod));
@@ -1972,8 +1946,8 @@ test_type_leafref(void **state)
CHECK_LOG_CTX("List predicate defined for container \"a\" in path.", "Schema location \"/dd:ref1\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;\n container a {leaf target2 {type uint8;}}\n"
"leaf ref1 {type leafref {path /a!invalid;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"ee\" failed.", NULL,
- "Invalid character 0x21 ('!'), perhaps \"a\" is supposed to be a function call.", "Line number 3.");
+ CHECK_LOG_CTX("Parsing module \"ee\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid character 0x21 ('!'), perhaps \"a\" is supposed to be a function call.", "Line number 3.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;container a {leaf target2 {type uint8;}}"
"leaf ref1 {type leafref {path /a;}}}", LYS_IN_YANG, &mod));
CHECK_LOG_CTX("Invalid leafref path \"/a\" - target node is container instead of leaf or leaf-list.", "Schema location \"/ff:ref1\".");
@@ -1983,36 +1957,36 @@ test_type_leafref(void **state)
"Schema location \"/gg:ref1\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;"
"leaf ref1 {type leafref;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Missing path substatement for leafref type.", "/hh:ref1");
+ CHECK_LOG_CTX("Missing path substatement for leafref type.", "Path \"/hh:ref1\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;typedef mytype {type leafref;}"
"leaf ref1 {type mytype;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Missing path substatement for leafref type mytype.", "/ii:ref1");
+ CHECK_LOG_CTX("Missing path substatement for leafref type mytype.", "Path \"/ii:ref1\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;"
"leaf ref {type leafref {path /target;}}leaf target {type string;config false;}}", LYS_IN_YANG, &mod));
CHECK_LOG_CTX("Invalid leafref path \"/target\" - target is supposed to represent configuration data (as the leafref does), but it does not.", "Schema location \"/kk:ref\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;"
"leaf ref {type leafref {path /target; require-instance true;}}leaf target {type string;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.", "/ll:ref");
+ CHECK_LOG_CTX("Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.", "Path \"/ll:ref\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;typedef mytype {type leafref {path /target;require-instance false;}}"
"leaf ref {type mytype;}leaf target {type string;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Leafref type \"mytype\" can be restricted by require-instance statement only in YANG 1.1 modules.", "/mm:ref");
+ CHECK_LOG_CTX("Leafref type \"mytype\" can be restricted by require-instance statement only in YANG 1.1 modules.", "Path \"/mm:ref\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn {namespace urn:nn;prefix nn;\n"
"list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
"leaf ifname{type leafref{ path \"../interface/name\";}}\n"
"leaf address {type leafref{\n path \"/interface[name is current()/../ifname]/ip\";}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"nn\" failed.", NULL,
- "Invalid character 0x69 ('i'), perhaps \"name\" is supposed to be a function call.", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"nn\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid character 0x69 ('i'), perhaps \"name\" is supposed to be a function call.", "Line number 5.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo {namespace urn:oo;prefix oo;\n"
"list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
"leaf ifname{type leafref{ path \"../interface/name\";}}\n"
"leaf address {type leafref{\n path \"/interface[name=current()/../ifname/ip\";}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"oo\" failed.", NULL,
- "Unexpected XPath expression end.", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"oo\" failed.", NULL);
+ CHECK_LOG_CTX("Unexpected XPath expression end.", "Line number 5.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp {namespace urn:pp;prefix pp;"
"list interface{key name;leaf name{type string;}leaf ip {type string;}}"
@@ -2034,56 +2008,56 @@ test_type_leafref(void **state)
"leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
"leaf address {type leafref{ path \"/interface[name=current() / .. / ifname][name=current()/../test]/ip\";}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"rr\" failed.", NULL,
- "Duplicate predicate key \"name\" in path.", "Line number 4.");
+ CHECK_LOG_CTX("Parsing module \"rr\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate predicate key \"name\" in path.", "Line number 4.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ss {namespace urn:ss;prefix ss;\n"
"list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
"leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
"leaf address {type leafref{ path \"/interface[name = ../ifname]/ip\";}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"ss\" failed.", NULL,
- "Unexpected XPath token \"..\" (\"../ifname]/ip\"), expected \"FunctionName\".", "Line number 4.");
+ CHECK_LOG_CTX("Parsing module \"ss\" failed.", NULL);
+ CHECK_LOG_CTX("Unexpected XPath token \"..\" (\"../ifname]/ip\"), expected \"FunctionName\".", "Line number 4.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module tt {namespace urn:tt;prefix tt;\n"
"list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
"leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
"leaf address {type leafref{ path \"/interface[name = current()../ifname]/ip\";}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"tt\" failed.", NULL,
- "Unexpected XPath token \"..\" (\"../ifname]/ip\"), expected \"]\".", "Line number 4.");
+ CHECK_LOG_CTX("Parsing module \"tt\" failed.", NULL);
+ CHECK_LOG_CTX("Unexpected XPath token \"..\" (\"../ifname]/ip\"), expected \"Operator(Path)\".", "Line number 4.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module uu {namespace urn:uu;prefix uu;\n"
"list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
"leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
"leaf address {type leafref{ path \"/interface[name = current()/..ifname]/ip\";}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"uu\" failed.", NULL,
- "Invalid character 'i'[31] of expression '/interface[name = current()/..ifname]/ip'.", "Line number 4.");
+ CHECK_LOG_CTX("Parsing module \"uu\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid character 'i'[31] of expression '/interface[name = current()/..ifname]/ip'.", "Line number 4.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module vv {namespace urn:vv;prefix vv;\n"
"list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
"leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
"leaf address {type leafref{ path \"/interface[name = current()/ifname]/ip\";}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"vv\" failed.", NULL,
- "Unexpected XPath token \"NameTest\" (\"ifname]/ip\"), expected \"..\".", "Line number 4.");
+ CHECK_LOG_CTX("Parsing module \"vv\" failed.", NULL);
+ CHECK_LOG_CTX("Unexpected XPath token \"NameTest\" (\"ifname]/ip\"), expected \"..\".", "Line number 4.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ww {namespace urn:ww;prefix ww;\n"
"list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
"leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
"leaf address {type leafref{ path \"/interface[name = current()/../]/ip\";}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"ww\" failed.", NULL,
- "Unexpected XPath token \"]\" (\"]/ip\").", "Line number 4.");
+ CHECK_LOG_CTX("Parsing module \"ww\" failed.", NULL);
+ CHECK_LOG_CTX("Unexpected XPath token \"]\" (\"]/ip\"), expected \"NameTest\".", "Line number 4.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module xx {namespace urn:xx;prefix xx;\n"
"list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
"leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
"leaf address {type leafref{ path \"/interface[name = current()/../#node]/ip\";}}}",
LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Parsing module \"xx\" failed.", NULL,
- "Invalid character '#'[32] of expression '/interface[name = current()/../#node]/ip'.", "Line number 4.");
+ CHECK_LOG_CTX("Parsing module \"xx\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid character '#'[32] of expression '/interface[name = current()/../#node]/ip'.", "Line number 4.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module yy {namespace urn:yy;prefix yy;\n"
"list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
@@ -2143,7 +2117,7 @@ test_type_empty(void **state)
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;typedef mytype {type empty; default x;}"
"leaf l {type mytype;}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid type \"mytype\" - \"empty\" type must not have a default value (x).", "/bb:l");
+ CHECK_LOG_CTX("Invalid type \"mytype\" - \"empty\" type must not have a default value (x).", "Path \"/bb:l\".");
}
static void
@@ -2197,25 +2171,25 @@ test_type_union(void **state)
/* invalid unions */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;typedef mytype {type union;}"
"leaf l {type mytype;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Missing type substatement for union type mytype.", "/aa:l");
+ CHECK_LOG_CTX("Missing type substatement for union type mytype.", "Path \"/aa:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;leaf l {type union;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Missing type substatement for union type.", "/bb:l");
+ CHECK_LOG_CTX("Missing type substatement for union type.", "Path \"/bb:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;typedef mytype {type union{type int8; type string;}}"
"leaf l {type mytype {type string;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid type substatement for the type not directly derived from union built-in type.", "/cc:l");
+ CHECK_LOG_CTX("Invalid type substatement for the type not directly derived from union built-in type.", "Path \"/cc:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;typedef mytype {type union{type int8; type string;}}"
"typedef mytype2 {type mytype {type string;}}leaf l {type mytype2;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid type substatement for the type \"mytype2\" not directly derived from union built-in type.", "/dd:l");
+ CHECK_LOG_CTX("Invalid type substatement for the type \"mytype2\" not directly derived from union built-in type.", "Path \"/dd:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;typedef mytype {type union{type mytype; type string;}}"
"leaf l {type mytype;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid \"mytype\" type reference - circular chain of types detected.", "/ee:l");
+ CHECK_LOG_CTX("Invalid \"mytype\" type reference - circular chain of types detected.", "Path \"/ee:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ef {namespace urn:ef;prefix ef;typedef mytype {type mytype2;}"
"typedef mytype2 {type mytype;} leaf l {type mytype;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid \"mytype\" type reference - circular chain of types detected.", "/ef:l");
+ CHECK_LOG_CTX("Invalid \"mytype\" type reference - circular chain of types detected.", "Path \"/ef:l\".");
}
static void
@@ -2304,20 +2278,173 @@ test_type_dflt(void **state)
}
static void
+test_type_exts(void **state)
+{
+ const char *schema1, *schema2, *schema3, *schema4;
+ struct lys_module *mod;
+ const struct lysc_node *snode;
+ struct lysc_type *type;
+ struct lysc_type_union *type_u;
+
+ schema1 = "module my-extensions {\n"
+ " namespace \"urn:my-extensions\";\n"
+ " prefix my-ext;\n"
+ "\n"
+ " extension shortdesc {\n"
+ " argument shortdsc;\n"
+ " }\n"
+ "}\n";
+ schema2 = "module module-inet {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:module-inet\";\n"
+ " prefix mod-inet;\n"
+ "\n"
+ " import ietf-inet-types {\n"
+ " prefix inet;\n"
+ " }\n"
+ "\n"
+ " import my-extensions {\n"
+ " prefix my-ext;\n"
+ " }\n"
+ "\n"
+ " typedef domain-name {\n"
+ " type inet:domain-name {\n"
+ " my-ext:shortdesc \"<host-name>\";\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " typedef ipv4-address {\n"
+ " type inet:ipv4-address-no-zone {\n"
+ " my-ext:shortdesc \"<A.B.C.D>\";\n"
+ " }\n"
+ " }\n"
+ " typedef my-enum {\n"
+ " type enumeration {\n"
+ " enum one;\n"
+ " enum two;\n"
+ " enum three;\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ schema3 = "module module-a {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:module-a\";\n"
+ " prefix mod-a;\n"
+ "\n"
+ " import module-inet {\n"
+ " prefix mod-inet;\n"
+ " }\n"
+ "\n"
+ " import my-extensions {\n"
+ " prefix my-ext;\n"
+ " }\n"
+ "\n"
+ " typedef server-address {\n"
+ " type union {\n"
+ " type mod-inet:ipv4-address {\n"
+ " my-ext:shortdesc \"<ipv4-address>\";\n"
+ " }\n"
+ " type mod-inet:domain-name {\n"
+ " my-ext:shortdesc \"<fqdn>\";\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n";
+ schema4 = "module main-module {\n"
+ " yang-version 1.1;\n"
+ " namespace \"urn:main-module\";\n"
+ " prefix main;\n"
+ "\n"
+ " import module-a {\n"
+ " prefix mod-a;\n"
+ " }\n"
+ "\n"
+ " import module-inet {\n"
+ " prefix mod-inet;\n"
+ " }\n"
+ "\n"
+ " import my-extensions {\n"
+ " prefix my-ext;\n"
+ " }\n"
+ "\n"
+ " container config {\n"
+ " leaf server {\n"
+ " type mod-a:server-address {\n"
+ " my-ext:shortdesc \"<server-address>\";\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " leaf hostname {\n"
+ " type union {\n"
+ " type mod-inet:domain-name;\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " leaf my-leaf {\n"
+ " type mod-inet:my-enum {\n"
+ " my-ext:shortdesc \"my enum\";\n"
+ " }\n"
+ " }\n"
+ "}\n";
+
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema1, LYS_IN_YANG, NULL));
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema2, LYS_IN_YANG, NULL));
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema3, LYS_IN_YANG, NULL));
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema4, LYS_IN_YANG, &mod));
+
+ /* server */
+ snode = lys_find_path(UTEST_LYCTX, NULL, "/main-module:config/server", 0);
+ assert_non_null(snode);
+
+ type = ((struct lysc_node_leaf *)snode)->type;
+ assert_int_equal(LY_ARRAY_COUNT(type->exts), 1);
+ assert_string_equal(type->exts[0].argument, "<server-address>");
+ type_u = (struct lysc_type_union *)type;
+ assert_int_equal(LY_ARRAY_COUNT(type_u->types), 2);
+
+ type = type_u->types[0];
+ assert_int_equal(LY_ARRAY_COUNT(type->exts), 2);
+ assert_string_equal(type->exts[0].argument, "<A.B.C.D>");
+ assert_string_equal(type->exts[1].argument, "<ipv4-address>");
+
+ type = type_u->types[1];
+ assert_int_equal(LY_ARRAY_COUNT(type->exts), 2);
+ assert_string_equal(type->exts[0].argument, "<host-name>");
+ assert_string_equal(type->exts[1].argument, "<fqdn>");
+
+ /* hostname */
+ snode = lys_find_path(UTEST_LYCTX, NULL, "/main-module:config/hostname", 0);
+ assert_non_null(snode);
+ type = ((struct lysc_node_leaf *)snode)->type;
+ assert_int_equal(LY_ARRAY_COUNT(type->exts), 0);
+ type_u = (struct lysc_type_union *)type;
+ assert_int_equal(LY_ARRAY_COUNT(type_u->types), 2);
+
+ type = type_u->types[0];
+ assert_int_equal(LY_ARRAY_COUNT(type->exts), 1);
+ assert_string_equal(type->exts[0].argument, "<host-name>");
+
+ type = type_u->types[1];
+ assert_int_equal(LY_ARRAY_COUNT(type->exts), 0);
+}
+
+static void
test_status(void **state)
{
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
"container c {status deprecated; leaf l {status current; type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Status \"current\" of \"l\" is in conflict with \"deprecated\" status of parent \"c\".", "/aa:c/l");
+ CHECK_LOG_CTX("Status \"current\" of \"l\" is in conflict with \"deprecated\" status of parent \"c\".", "Path \"/aa:c/l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;"
"container c {status obsolete; leaf l {status current; type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Status \"current\" of \"l\" is in conflict with \"obsolete\" status of parent \"c\".", "/bb:c/l");
+ CHECK_LOG_CTX("Status \"current\" of \"l\" is in conflict with \"obsolete\" status of parent \"c\".", "Path \"/bb:c/l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;"
"container c {status obsolete; leaf l {status deprecated; type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Status \"deprecated\" of \"l\" is in conflict with \"obsolete\" status of parent \"c\".", "/cc:c/l");
+ CHECK_LOG_CTX("Status \"deprecated\" of \"l\" is in conflict with \"obsolete\" status of parent \"c\".", "Path \"/cc:c/l\".");
/* just a warning */
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:dd;prefix d;"
@@ -2344,11 +2471,12 @@ test_grouping(void **state)
/* invalid - error in a non-instantiated grouping */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
"grouping grp {leaf x {type leafref;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Missing path substatement for leafref type.", "/aa:{grouping='grp'}/x");
- UTEST_LOG_CLEAN;
+ CHECK_LOG_CTX("Missing path substatement for leafref type.", "Path \"/aa:{grouping='grp'}/x\".");
+
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
"container a {grouping grp {leaf x {type leafref;}}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Missing path substatement for leafref type.", "/aa:a/{grouping='grp'}/x");
+ CHECK_LOG_CTX("Missing path substatement for leafref type.", "Path \"/aa:a/{grouping='grp'}/x\".");
+ CHECK_LOG_CTX("Locally scoped grouping \"grp\" not used.", NULL);
/* config check */
ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "module z1 {namespace urn:z1;prefix z1;"
@@ -2519,52 +2647,53 @@ test_uses(void **state)
/* invalid */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;uses missinggrp;}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Grouping \"missinggrp\" referenced by a uses statement not found.", "/aa:{uses='missinggrp'}");
+ CHECK_LOG_CTX("Grouping \"missinggrp\" referenced by a uses statement not found.", "Path \"/aa:{uses='missinggrp'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;uses grp;"
"grouping grp {leaf a{type string;}uses grp1;}"
"grouping grp1 {leaf b {type string;}uses grp2;}"
"grouping grp2 {leaf c {type string;}uses grp;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Grouping \"grp\" references itself through a uses statement.", "/bb:{uses='grp'}/{uses='grp1'}/{uses='grp2'}/{uses='grp'}");
+ CHECK_LOG_CTX("Grouping \"grp\" references itself through a uses statement.", "Path \"/bb:{uses='grp'}/{uses='grp1'}/{uses='grp2'}/{uses='grp'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;uses a:missingprefix;}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid prefix used for grouping \"a:missingprefix\" reference.", "/cc:{uses='a:missingprefix'}");
+ CHECK_LOG_CTX("Invalid prefix used for grouping \"a:missingprefix\" reference.", "Path \"/cc:{uses='a:missingprefix'}\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;grouping grp{leaf a{type string;}}"
"leaf a {type string;}uses grp;}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/dd:{uses='grp'}/dd:a");
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "Path \"/dd:{uses='grp'}/dd:a\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;grouping grp {leaf l {type string; status deprecated;}}"
"uses grp {status obsolete;}}", LYS_IN_YANG, &mod));
CHECK_LOG_CTX("Inherited schema-only status \"obsolete\" is in conflict with \"deprecated\" status of \"l\".",
- "/ee:{uses='grp'}/ee:l");
+ "Path \"/ee:{uses='grp'}/ee:l\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;grouping grp {leaf l {type string;}}"
"leaf l {type int8;}uses grp;}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Duplicate identifier \"l\" of data definition/RPC/action/notification statement.", "/ff:{uses='grp'}/ff:l");
+ CHECK_LOG_CTX("Duplicate identifier \"l\" of data definition/RPC/action/notification statement.", "Path \"/ff:{uses='grp'}/ff:l\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module fg {namespace urn:fg;prefix fg;grouping grp {leaf m {type string;}}"
"uses grp;leaf m {type int8;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Duplicate identifier \"m\" of data definition/RPC/action/notification statement.", "/fg:m");
+ CHECK_LOG_CTX("Duplicate identifier \"m\" of data definition/RPC/action/notification statement.", "Path \"/fg:m\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg; grouping grp {container g;}"
"leaf g {type string;}"
"container top {uses grp {augment /g {leaf x {type int8;}}}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"/g\" - name test expected instead of \"/\".", "/gg:top/{uses='grp'}/{augment='/g'}");
+ CHECK_LOG_CTX("Invalid descendant-schema-nodeid value \"/g\" - name test expected instead of \"/\".",
+ "Path \"/gg:top/{uses='grp'}/{augment='/g'}\".");
assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module hh {yang-version 1.1;namespace urn:hh;prefix hh;"
"grouping grp {notification g { description \"super g\";}}"
"container top {notification h; uses grp {refine h {description \"ultra h\";}}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Refine(s) target node \"h\" in grouping \"grp\" was not found.", "/hh:top/{uses='grp'}");
+ CHECK_LOG_CTX("Refine(s) target node \"h\" in grouping \"grp\" was not found.", "Path \"/hh:top/{uses='grp'}\".");
assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module ii {yang-version 1.1;namespace urn:ii;prefix ii;"
"grouping grp {action g { description \"super g\";}}"
"container top {action i; uses grp {refine i {description \"ultra i\";}}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Refine(s) target node \"i\" in grouping \"grp\" was not found.", "/ii:top/{uses='grp'}");
+ CHECK_LOG_CTX("Refine(s) target node \"i\" in grouping \"grp\" was not found.", "Path \"/ii:top/{uses='grp'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {yang-version 1.1;namespace urn:jj;prefix jj;"
"grouping grp {leaf j { when \"1\"; type invalid;}}"
"container top {uses grp;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Referenced type \"invalid\" not found.", "/jj:top/{uses='grp'}/j");
+ CHECK_LOG_CTX("Referenced type \"invalid\" not found.", "Path \"/jj:top/{uses='grp'}/j\".");
}
static void
@@ -2666,65 +2795,71 @@ test_refine(void **state)
/* invalid */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;import grp {prefix g;}"
"uses g:grp {refine c {default hello;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid refine of container node - it is not possible to replace \"default\" property.", "/aa:{uses='g:grp'}/aa:c/{refine='c'}");
+ CHECK_LOG_CTX("Invalid refine of container node - it is not possible to replace \"default\" property.",
+ "Path \"/aa:{uses='g:grp'}/aa:c/{refine='c'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;import grp {prefix g;}"
"uses g:grp {refine c/l {default hello; default world;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid refine of leaf with too many (2) default properties.", "/bb:{uses='g:grp'}/bb:c/l/{refine='c/l'}");
+ CHECK_LOG_CTX("Invalid refine of leaf with too many (2) default properties.", "Path \"/bb:{uses='g:grp'}/bb:c/l/{refine='c/l'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc;import grp {prefix g;}"
"uses g:grp {refine c/ll {default hello; default world;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid refine of default in leaf-list - the default statement is allowed only in YANG 1.1 modules.", "/cc:{uses='g:grp'}/cc:c/ll/{refine='c/ll'}");
+ CHECK_LOG_CTX("Invalid refine of default in leaf-list - the default statement is allowed only in YANG 1.1 modules.",
+ "Path \"/cc:{uses='g:grp'}/cc:c/ll/{refine='c/ll'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd;import grp {prefix g;}"
"uses g:grp {refine c/ll {mandatory true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid refine of leaf-list node - it is not possible to replace \"mandatory\" property.", "/dd:{uses='g:grp'}/dd:c/ll/{refine='c/ll'}");
+ CHECK_LOG_CTX("Invalid refine of leaf-list node - it is not possible to replace \"mandatory\" property.",
+ "Path \"/dd:{uses='g:grp'}/dd:c/ll/{refine='c/ll'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee;import grp {prefix g;}"
"uses g:grp {refine c/l {mandatory true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ee:{uses='g:grp'}/ee:c/l",
- "Invalid mandatory leaf with a default value.", "/ee:{uses='g:grp'}/ee:c/l");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/ee:{uses='g:grp'}/ee:c/l\".");
+ CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "Path \"/ee:{uses='g:grp'}/ee:c/l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ef {namespace urn:ef;prefix ef;import grp {prefix g;}"
"uses g:grp {refine c/ch {mandatory true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ef:{uses='g:grp'}/ef:c/ch",
- "Invalid mandatory choice with a default case.", "/ef:{uses='g:grp'}/ef:c/ch");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/ef:{uses='g:grp'}/ef:c/ch\".");
+ CHECK_LOG_CTX("Invalid mandatory choice with a default case.", "Path \"/ef:{uses='g:grp'}/ef:c/ch\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff;import grp {prefix g;}"
"uses g:grp {refine c/ch/ca/ca {mandatory true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Mandatory node \"ca\" under the default case \"ca\".", "/ff:{uses='g:grp'}/ff:c/ch");
+ CHECK_LOG_CTX("Mandatory node \"ca\" under the default case \"ca\".", "Path \"/ff:{uses='g:grp'}/ff:c/ch\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg;import grp {prefix g;}"
"uses g:grp {refine c/x {default hello;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/gg:{uses='g:grp'}/gg:c/x",
- "Invalid mandatory leaf with a default value.", "/gg:{uses='g:grp'}/gg:c/x");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/gg:{uses='g:grp'}/gg:c/x\".");
+ CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "Path \"/gg:{uses='g:grp'}/gg:c/x\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;import grp {prefix g;}"
"uses g:grp {refine c/c/l {config true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/hh:{uses='g:grp'}/hh:c/c/l",
- "Configuration node cannot be child of any state data node.", "/hh:{uses='g:grp'}/hh:c/c/l");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/hh:{uses='g:grp'}/hh:c/c/l\".");
+ CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "Path \"/hh:{uses='g:grp'}/hh:c/c/l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii {namespace urn:ii;prefix ii;grouping grp {leaf l {type string; status deprecated;}}"
"uses grp {status obsolete;}}", LYS_IN_YANG, &mod));
CHECK_LOG_CTX("Inherited schema-only status \"obsolete\" is in conflict with \"deprecated\" status of \"l\".",
- "/ii:{uses='grp'}/ii:l");
+ "Path \"/ii:{uses='grp'}/ii:l\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj {namespace urn:jj;prefix jj;import grp {prefix g;}"
"uses g:grp {refine c/x {presence nonsence;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid refine of leaf node - it is not possible to replace \"presence\" property.", "/jj:{uses='g:grp'}/jj:c/x/{refine='c/x'}");
+ CHECK_LOG_CTX("Invalid refine of leaf node - it is not possible to replace \"presence\" property.",
+ "Path \"/jj:{uses='g:grp'}/jj:c/x/{refine='c/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk {namespace urn:kk;prefix kk;import grp {prefix g;}"
"uses g:grp {refine c/ch {must 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid refine of choice node - it is not possible to add \"must\" property.", "/kk:{uses='g:grp'}/kk:c/ch/{refine='c/ch'}");
+ CHECK_LOG_CTX("Invalid refine of choice node - it is not possible to add \"must\" property.",
+ "Path \"/kk:{uses='g:grp'}/kk:c/ch/{refine='c/ch'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll {namespace urn:ll;prefix ll;import grp {prefix g;}"
"uses g:grp {refine c/x {min-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid refine of leaf node - it is not possible to replace \"min-elements\" property.", "/ll:{uses='g:grp'}/ll:c/x/{refine='c/x'}");
+ CHECK_LOG_CTX("Invalid refine of leaf node - it is not possible to replace \"min-elements\" property.",
+ "Path \"/ll:{uses='g:grp'}/ll:c/x/{refine='c/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm {namespace urn:mm;prefix mm;import grp {prefix g;}"
"uses g:grp {refine c/ll {min-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm:{uses='g:grp'}/mm:c/ll",
- "The default statement is present on leaf-list with a nonzero min-elements.", "/mm:{uses='g:grp'}/mm:c/ll");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/mm:{uses='g:grp'}/mm:c/ll\".");
+ CHECK_LOG_CTX("The default statement is present on leaf-list with a nonzero min-elements.", "Path \"/mm:{uses='g:grp'}/mm:c/ll\".");
}
static void
@@ -2867,40 +3002,41 @@ test_augment(void **state)
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; container c {leaf a {type string;}}"
"augment /x/ {leaf a {type int8;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"/x/\" - unexpected end of expression.", "/aa:{augment='/x/'}");
+ CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"/x/\" - unexpected end of expression.", "Path \"/aa:{augment='/x/'}\".");
assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; container c {leaf a {type string;}}"
"augment /x {leaf a {type int8;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Augment target node \"/x\" from module \"aa\" was not found.", "/aa:{augment='/x'}");
+ CHECK_LOG_CTX("Augment target node \"/x\" from module \"aa\" was not found.", "Path \"/aa:{augment='/x'}\".");
assert_int_equal(LY_EEXIST, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb; container c {leaf a {type string;}}"
"augment /c {leaf a {type int8;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "/bb:{augment='/c'}/a");
+ CHECK_LOG_CTX("Duplicate identifier \"a\" of data definition/RPC/action/notification statement.", "Path \"/bb:{augment='/c'}/a\".");
assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; container c {leaf a {type string;}}"
"augment /c/a {leaf a {type int8;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Augment target node \"/c/a\" from module \"cc\" was not found.", "/cc:{augment='/c/a'}");
+ CHECK_LOG_CTX("Augment target node \"/c/a\" from module \"cc\" was not found.", "Path \"/cc:{augment='/c/a'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd {namespace urn:dd;prefix dd; container c {leaf a {type string;}}"
"augment /c {case b {leaf d {type int8;}}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid augment of container node which is not allowed to contain case node \"b\".", "/dd:{augment='/c'}");
+ CHECK_LOG_CTX("Invalid augment of container node which is not allowed to contain case node \"b\".", "Path \"/dd:{augment='/c'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee {namespace urn:ee;prefix ee; import himp {prefix hi;}"
"augment /hi:top {container c {leaf d {mandatory true; type int8;}}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid augment adding mandatory node \"c\" without making it conditional via when statement.", "/ee:{augment='/hi:top'}");
+ CHECK_LOG_CTX("Invalid augment adding mandatory node \"c\" without making it conditional via when statement.", "Path \"/ee:{augment='/hi:top'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff {namespace urn:ff;prefix ff; container top;"
"augment ../top {leaf x {type int8;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"../top\" - \"/\" expected instead of \"..\".", "/ff:{augment='../top'}");
+ CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"../top\" - \"/\" expected instead of \"..\".", "Path \"/ff:{augment='../top'}\".");
assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module gg {namespace urn:gg;prefix gg; rpc func;"
"augment /func {leaf x {type int8;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Augment target node \"/func\" from module \"gg\" was not found.", "/gg:{augment='/func'}");
+ CHECK_LOG_CTX("Augment target node \"/func\" from module \"gg\" was not found.", "Path \"/gg:{augment='/func'}\".");
assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module hh {namespace urn:hh;prefix hh;import himp {prefix hi;}"
"augment /hi:func/input {leaf x {type string;}}"
"augment /hi:func/output {leaf y {type string;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Augment target node \"/hi:func/input\" from module \"hh\" was not found.", "/hh:{augment='/hi:func/input'}");
+ CHECK_LOG_CTX("Augment target node \"/hi:func/input\" from module \"hh\" was not found.", "Path \"/hh:{augment='/hi:func/input'}\".");
+ CHECK_LOG_CTX("Augment target node \"/hi:func/output\" from module \"hh\" was not found.", "Path \"/hh:{augment='/hi:func/output'}\".");
}
static void
@@ -3104,18 +3240,18 @@ test_deviation(void **state)
assert_true(node->flags & LYS_CONFIG_R);
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l; leaf a {config false; type string;}"
- "container top {config false; leaf x {type string;}}"
+ "container top {leaf x {type string;}}"
"deviation /a {deviate replace {config true;}}"
- "deviation /top {deviate replace {config true;}}}", LYS_IN_YANG, &mod));
+ "deviation /top {deviate replace {config false;}}}", LYS_IN_YANG, &mod));
assert_non_null(node = mod->compiled->data);
assert_string_equal("a", node->name);
assert_true(node->flags & LYS_CONFIG_W);
assert_non_null(node = node->next);
assert_string_equal("top", node->name);
- assert_true(node->flags & LYS_CONFIG_W);
+ assert_true(node->flags & LYS_CONFIG_R);
assert_non_null(node = lysc_node_child(node));
assert_string_equal("x", node->name);
- assert_true(node->flags & LYS_CONFIG_W);
+ assert_true(node->flags & LYS_CONFIG_R);
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;"
"container a {leaf a {type string;}}"
@@ -3356,194 +3492,199 @@ test_deviation(void **state)
assert_int_equal(LY_ENOTFOUND, lys_parse_mem(UTEST_LYCTX, "module aa1 {namespace urn:aa1;prefix aa1;import a {prefix a;}"
"deviation /a:top/a:z {deviate not-supported;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Deviation(s) target node \"/a:top/a:z\" from module \"aa1\" was not found.", "/a:{deviation='/a:top/a:z'}");
+ CHECK_LOG_CTX("Deviation(s) target node \"/a:top/a:z\" from module \"aa1\" was not found.", "Path \"/a:{deviation='/a:top/a:z'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa2 {namespace urn:aa2;prefix aa2;import a {prefix a;}"
"deviation /a:top/a:a {deviate not-supported;}"
"deviation /a:top/a:a {deviate add {default error;}}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Multiple deviations of \"/a:top/a:a\" with one of them being \"not-supported\".", "/aa2:{deviation='/a:top/a:a'}");
+ CHECK_LOG_CTX("Multiple deviations of \"/a:top/a:a\" with one of them being \"not-supported\".", "Path \"/aa2:{deviation='/a:top/a:a'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;import a {prefix a;}"
"deviation a:top/a:a {deviate not-supported;}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"a:top/a:a\" - \"/\" expected instead of \"a:top\".", "/bb:{deviation='a:top/a:a'}");
+ CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"a:top/a:a\" - \"/\" expected instead of \"a:top\".", "Path \"/bb:{deviation='a:top/a:a'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cc {namespace urn:cc;prefix cc; container c;"
"deviation /c {deviate add {units meters;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to add \"units\" property.", "/cc:{deviation='/c'}");
+ CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to add \"units\" property.", "Path \"/cc:{deviation='/c'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module cd {namespace urn:cd;prefix cd; leaf c {type string; units centimeters;}"
"deviation /c {deviate add {units meters;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation adding \"units\" property which already exists (with value \"centimeters\").", "/cd:{deviation='/c'}");
+ CHECK_LOG_CTX("Invalid deviation adding \"units\" property which already exists (with value \"centimeters\").", "Path \"/cd:{deviation='/c'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd1 {namespace urn:dd1;prefix dd1; container c;"
"deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to delete \"units\" property.", "/dd1:{deviation='/c'}");
+ CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to delete \"units\" property.", "Path \"/dd1:{deviation='/c'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd2 {namespace urn:dd2;prefix dd2; leaf c {type string;}"
"deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation deleting \"units\" property \"meters\" which is not present.", "/dd2:{deviation='/c'}");
+ CHECK_LOG_CTX("Invalid deviation deleting \"units\" property \"meters\" which is not present.", "Path \"/dd2:{deviation='/c'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module dd3 {namespace urn:dd3;prefix dd3; leaf c {type string; units centimeters;}"
"deviation /c {deviate delete {units meters;}}}", LYS_IN_YANG, &mod));
CHECK_LOG_CTX("Invalid deviation deleting \"units\" property \"meters\" which does not match the target's property value \"centimeters\".",
- "/dd3:{deviation='/c'}");
+ "Path \"/dd3:{deviation='/c'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee1 {namespace urn:ee1;prefix ee1; container c;"
"deviation /c {deviate replace {units meters;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to replace \"units\" property.", "/ee1:{deviation='/c'}");
+ CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to replace \"units\" property.", "Path \"/ee1:{deviation='/c'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ee2 {namespace urn:ee2;prefix ee2; leaf c {type string;}"
"deviation /c {deviate replace {units meters;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation replacing \"units\" property \"meters\" which is not present.", "/ee2:{deviation='/c'}");
+ CHECK_LOG_CTX("Invalid deviation replacing \"units\" property \"meters\" which is not present.", "Path \"/ee2:{deviation='/c'}\".");
/* the default is already deleted in /e:a byt module f */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff1 {namespace urn:ff1;prefix ff1; import e {prefix e;}"
"deviation /e:a {deviate delete {default x:aa;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"x:aa\" which is not present.", "/ff1:{deviation='/e:a'}");
+ CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"x:aa\" which is not present.", "Path \"/ff1:{deviation='/e:a'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff3 {namespace urn:ff3;prefix ff3; import e {prefix e;}"
"deviation /e:b {deviate delete {default e:b;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"e:b\" which does not match the target's property value \"x:ba\".", "/ff3:{deviation='/e:b'}");
+ CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"e:b\" which does not match the target's property value \"x:ba\".",
+ "Path \"/ff3:{deviation='/e:b'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff5 {namespace urn:ff5;prefix ff5; anyxml a;"
"deviation /a {deviate delete {default x;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of anyxml node - it is not possible to delete \"default\" property.", "/ff5:{deviation='/a'}");
+ CHECK_LOG_CTX("Invalid deviation of anyxml node - it is not possible to delete \"default\" property.", "Path \"/ff5:{deviation='/a'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff6 {namespace urn:ff6;prefix ff6; import e {prefix e;}"
"deviation /e:c {deviate delete {default hi;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"hi\" which does not match the target's property value \"hello\".", "/ff6:{deviation='/e:c'}");
+ CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"hi\" which does not match the target's property value \"hello\".",
+ "Path \"/ff6:{deviation='/e:c'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ff7 {namespace urn:ff7;prefix ff7; import e {prefix e;}"
"deviation /e:d {deviate delete {default hi;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"hi\" which does not match any of the target's property values.", "/ff7:{deviation='/e:d'}");
+ CHECK_LOG_CTX("Invalid deviation deleting \"default\" property \"hi\" which does not match any of the target's property values.",
+ "Path \"/ff7:{deviation='/e:d'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg1 {namespace urn:gg1;prefix gg1; import e {prefix e;}"
"deviation /e:b {deviate add {default e:a;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation adding \"default\" property which already exists (with value \"x:ba\").", "/gg1:{deviation='/e:b'}");
+ CHECK_LOG_CTX("Invalid deviation adding \"default\" property which already exists (with value \"x:ba\").", "Path \"/gg1:{deviation='/e:b'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg2 {namespace urn:gg2;prefix gg2; import e {prefix e;}"
"deviation /e:a {deviate add {default x:a;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a",
- "Default case prefix \"x\" not found in imports of \"gg2\".", "/e:a");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/e:a\".");
+ CHECK_LOG_CTX("Default case prefix \"x\" not found in imports of \"gg2\".", "Path \"/e:a\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg3 {namespace urn:gg3;prefix gg3; import e {prefix e;}"
"deviation /e:a {deviate add {default a;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a",
- "Default case \"a\" not found.", "/e:a");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/e:a\".");
+ CHECK_LOG_CTX("Default case \"a\" not found.", "Path \"/e:a\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg4 {namespace urn:gg4;prefix gg4; import e {prefix e;}"
"deviation /e:c {deviate add {default hi;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation adding \"default\" property which already exists (with value \"hello\").", "/gg4:{deviation='/e:c'}");
+ CHECK_LOG_CTX("Invalid deviation adding \"default\" property which already exists (with value \"hello\").", "Path \"/gg4:{deviation='/e:c'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg4 {namespace urn:gg4;prefix gg4; import e {prefix e;}"
"deviation /e:a {deviate add {default e:ac;}}}", LYS_IN_YANG, &mod));
- /*CHECK_LOG_CTX("Invalid deviation adding \"default\" property \"e:ac\" of choice - mandatory node \"ac\" under the default case.", "/gg4:{deviation='/e:a'}");*/
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/e:a");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/e:a\".");
+ CHECK_LOG_CTX("Mandatory node \"ac\" under the default case \"e:ac\".", "Path \"/e:a\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module gg5 {namespace urn:gg5;prefix gg5; leaf x {type string; mandatory true;}"
"deviation /x {deviate add {default error;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/gg5:{deviation='/x'}",
- "Invalid mandatory leaf with a default value.", "/gg5:{deviation='/x'}");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/gg5:{deviation='/x'}\".");
+ CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "Path \"/gg5:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module hh1 {yang-version 1.1; namespace urn:hh1;prefix hh1; import e {prefix e;}"
"deviation /e:d {deviate replace {default hi;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of leaf-list node - it is not possible to replace \"default\" property.", "/hh1:{deviation='/e:d'}");
+ CHECK_LOG_CTX("Invalid deviation of leaf-list node - it is not possible to replace \"default\" property.",
+ "Path \"/hh1:{deviation='/e:d'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii1 {namespace urn:ii1;prefix ii1; import i {prefix i;}"
"deviation /i:l1 {deviate delete {unique x;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation deleting \"unique\" property \"x\" which does not match any of the target's property values.", "/ii1:{deviation='/i:l1'}");
+ CHECK_LOG_CTX("Invalid deviation deleting \"unique\" property \"x\" which does not match any of the target's property values.",
+ "Path \"/ii1:{deviation='/i:l1'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii2 {namespace urn:ii2;prefix ii2; import i {prefix i;} leaf x { type string;}"
"deviation /i:l2 {deviate delete {unique d;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation deleting \"unique\" property \"d\" which does not match any of the target's property values.", "/ii2:{deviation='/i:l2'}");
+ CHECK_LOG_CTX("Invalid deviation deleting \"unique\" property \"d\" which does not match any of the target's property values.",
+ "Path \"/ii2:{deviation='/i:l2'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii3 {namespace urn:ii3;prefix ii3; leaf x { type string;}"
"deviation /x {deviate delete {unique d;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of leaf node - it is not possible to delete \"unique\" property.", "/ii3:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation of leaf node - it is not possible to delete \"unique\" property.", "Path \"/ii3:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ii4 {namespace urn:ii4;prefix ii4; leaf x { type string;}"
"deviation /x {deviate add {unique d;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of leaf node - it is not possible to add \"unique\" property.", "/ii4:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation of leaf node - it is not possible to add \"unique\" property.", "Path \"/ii4:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj1 {namespace urn:jj1;prefix jj1; choice ch {case a {leaf a{type string;}}}"
"deviation /ch/a {deviate add {config false;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of case node - it is not possible to add \"config\" property.", "/jj1:{deviation='/ch/a'}");
+ CHECK_LOG_CTX("Invalid deviation of case node - it is not possible to add \"config\" property.", "Path \"/jj1:{deviation='/ch/a'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj2 {namespace urn:jj2;prefix jj2; container top {config false; leaf x {type string;}}"
"deviation /top/x {deviate add {config true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/jj2:{deviation='/top/x'}",
- "Configuration node cannot be child of any state data node.", "/jj2:{deviation='/top/x'}");
- assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj3 {namespace urn:jj3;prefix jj3; container top {leaf x {type string;}}"
- "deviation /top/x {deviate replace {config false;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation replacing \"config\" property \"config false\" which is not present.", "/jj3:{deviation='/top/x'}");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/jj2:{deviation='/top/x'}\".");
+ CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "Path \"/jj2:{deviation='/top/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj4 {namespace urn:jj4;prefix jj4; choice ch {case a {leaf a{type string;}}}"
"deviation /ch/a {deviate replace {config false;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of case node - it is not possible to replace \"config\" property.", "/jj4:{deviation='/ch/a'}");
+ CHECK_LOG_CTX("Invalid deviation of case node - it is not possible to replace \"config\" property.", "Path \"/jj4:{deviation='/ch/a'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj5 {namespace urn:jj5;prefix jj5; container top {leaf x {type string; config true;}}"
"deviation /top {deviate add {config false;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/jj5:top",
- "Configuration node cannot be child of any state data node.", "/jj5:top/x");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/jj5:top\".");
+ CHECK_LOG_CTX("Configuration node cannot be child of any state data node.", "Path \"/jj5:top/x\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module jj6 {namespace urn:jj6;prefix jj6; leaf x {config false; type string;}"
"deviation /x {deviate add {config true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation adding \"config\" property which already exists (with value \"config false\").", "/jj6:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation adding \"config\" property which already exists (with value \"config false\").",
+ "Path \"/jj6:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk1 {namespace urn:kk1;prefix kk1; container top {leaf a{type string;}}"
"deviation /top {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to add \"mandatory\" property.", "/kk1:{deviation='/top'}");
+ CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to add \"mandatory\" property.", "Path \"/kk1:{deviation='/top'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk2 {namespace urn:kk2;prefix kk2; container top {leaf a{type string;}}"
"deviation /top {deviate replace {mandatory true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to replace \"mandatory\" property.", "/kk2:{deviation='/top'}");
+ CHECK_LOG_CTX("Invalid deviation of container node - it is not possible to replace \"mandatory\" property.", "Path \"/kk2:{deviation='/top'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk3 {namespace urn:kk3;prefix kk3; container top {leaf x {type string;}}"
"deviation /top/x {deviate replace {mandatory true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation replacing \"mandatory\" property \"mandatory true\" which is not present.", "/kk3:{deviation='/top/x'}");
+ CHECK_LOG_CTX("Invalid deviation replacing \"mandatory\" property \"mandatory true\" which is not present.", "Path \"/kk3:{deviation='/top/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module kk4 {namespace urn:kk4;prefix kk4; leaf x {mandatory true; type string;}"
"deviation /x {deviate add {mandatory false;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation adding \"mandatory\" property which already exists (with value \"mandatory true\").", "/kk4:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation adding \"mandatory\" property which already exists (with value \"mandatory true\").",
+ "Path \"/kk4:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll1 {namespace urn:ll1;prefix ll1; leaf x {default test; type string;}"
"deviation /x {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll1:{deviation='/x'}",
- "Invalid mandatory leaf with a default value.", "/ll1:{deviation='/x'}");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/ll1:{deviation='/x'}\".");
+ CHECK_LOG_CTX("Invalid mandatory leaf with a default value.", "Path \"/ll1:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll2 {yang-version 1.1; namespace urn:ll2;prefix ll2; leaf-list x {default test; type string;}"
"deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll2:{deviation='/x'}",
- "The default statement is present on leaf-list with a nonzero min-elements.", "/ll2:{deviation='/x'}");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/ll2:{deviation='/x'}\".");
+ CHECK_LOG_CTX("The default statement is present on leaf-list with a nonzero min-elements.", "Path \"/ll2:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module ll2 {namespace urn:ll2;prefix ll2; choice ch {default a; leaf a {type string;} leaf b {type string;}}"
"deviation /ch {deviate add {mandatory true;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/ll2:ch",
- "Invalid mandatory choice with a default case.", "/ll2:ch");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/ll2:ch\".");
+ CHECK_LOG_CTX("Invalid mandatory choice with a default case.", "Path \"/ll2:ch\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm1 {namespace urn:mm1;prefix mm1; leaf-list x {min-elements 10; type string;}"
"deviation /x {deviate add {max-elements 5;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm1:{deviation='/x'}",
- "Leaf-list min-elements 10 is bigger than max-elements 5.", "/mm1:{deviation='/x'}");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/mm1:{deviation='/x'}\".");
+ CHECK_LOG_CTX("Leaf-list min-elements 10 is bigger than max-elements 5.", "Path \"/mm1:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm2 {namespace urn:mm2;prefix mm2; leaf-list x {max-elements 10; type string;}"
"deviation /x {deviate add {min-elements 20;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm2:{deviation='/x'}",
- "Leaf-list min-elements 20 is bigger than max-elements 10.", "/mm2:{deviation='/x'}");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/mm2:{deviation='/x'}\".");
+ CHECK_LOG_CTX("Leaf-list min-elements 20 is bigger than max-elements 10.", "Path \"/mm2:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm3 {namespace urn:mm3;prefix mm3; list x {min-elements 5; max-elements 10; config false;}"
"deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm3:{deviation='/x'}",
- "List min-elements 5 is bigger than max-elements 1.", "/mm3:{deviation='/x'}");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/mm3:{deviation='/x'}\".");
+ CHECK_LOG_CTX("List min-elements 5 is bigger than max-elements 1.", "Path \"/mm3:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm4 {namespace urn:mm4;prefix mm4; list x {min-elements 5; max-elements 10; config false;}"
"deviation /x {deviate replace {min-elements 20;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/mm4:{deviation='/x'}",
- "List min-elements 20 is bigger than max-elements 10.", "/mm4:{deviation='/x'}");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/mm4:{deviation='/x'}\".");
+ CHECK_LOG_CTX("List min-elements 20 is bigger than max-elements 10.", "Path \"/mm4:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm5 {namespace urn:mm5;prefix mm5; leaf-list x {type string; min-elements 5;}"
"deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation adding \"min-elements\" property which already exists (with value \"5\").", "/mm5:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation adding \"min-elements\" property which already exists (with value \"5\").", "Path \"/mm5:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm6 {namespace urn:mm6;prefix mm6; list x {config false; min-elements 5;}"
"deviation /x {deviate add {min-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation adding \"min-elements\" property which already exists (with value \"5\").", "/mm6:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation adding \"min-elements\" property which already exists (with value \"5\").", "Path \"/mm6:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm7 {namespace urn:mm7;prefix mm7; leaf-list x {type string; max-elements 5;}"
"deviation /x {deviate add {max-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation adding \"max-elements\" property which already exists (with value \"5\").", "/mm7:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation adding \"max-elements\" property which already exists (with value \"5\").", "Path \"/mm7:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm8 {namespace urn:mm8;prefix mm8; list x {config false; max-elements 5;}"
"deviation /x {deviate add {max-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation adding \"max-elements\" property which already exists (with value \"5\").", "/mm8:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation adding \"max-elements\" property which already exists (with value \"5\").", "Path \"/mm8:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm9 {namespace urn:mm9;prefix mm9; leaf-list x {type string;}"
"deviation /x {deviate replace {min-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation replacing \"min-elements\" property which is not present.", "/mm9:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation replacing \"min-elements\" property which is not present.", "Path \"/mm9:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm10 {namespace urn:mm10;prefix mm10; list x {config false;}"
"deviation /x {deviate replace {min-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation replacing \"min-elements\" property which is not present.", "/mm10:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation replacing \"min-elements\" property which is not present.", "Path \"/mm10:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm11 {namespace urn:mm11;prefix mm11; leaf-list x {type string;}"
"deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation replacing \"max-elements\" property which is not present.", "/mm11:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation replacing \"max-elements\" property which is not present.", "Path \"/mm11:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module mm12 {namespace urn:mm12;prefix mm12; list x {config false; }"
"deviation /x {deviate replace {max-elements 1;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation replacing \"max-elements\" property which is not present.", "/mm12:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation replacing \"max-elements\" property which is not present.", "Path \"/mm12:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn1 {namespace urn:nn1;prefix nn1; anyxml x;"
"deviation /x {deviate replace {type string;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Invalid deviation of anyxml node - it is not possible to replace \"type\" property.", "/nn1:{deviation='/x'}");
+ CHECK_LOG_CTX("Invalid deviation of anyxml node - it is not possible to replace \"type\" property.", "Path \"/nn1:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module nn2 {namespace urn:nn2;prefix nn2; leaf-list x {type string;}"
"deviation /x {deviate replace {type empty;}}}", LYS_IN_YANG, &mod));
- CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "/nn2:{deviation='/x'}",
- "Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "/nn2:{deviation='/x'}");
+ CHECK_LOG_CTX("Compilation of a deviated and/or refined node failed.", "Path \"/nn2:{deviation='/x'}\".");
+ CHECK_LOG_CTX("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "Path \"/nn2:{deviation='/x'}\".");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module oo1 {namespace urn:oo1;prefix oo1; leaf x {type uint16; default 300;}"
"deviation /x {deviate replace {type uint8;}}}", LYS_IN_YANG, &mod));
@@ -3563,6 +3704,7 @@ test_deviation(void **state)
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module pp1 {namespace urn:pp1;prefix pp1; import pp {prefix pp;}"
"deviation /pp:c/pp:x {deviate not-supported;}}", LYS_IN_YANG, &mod));
CHECK_LOG_CTX("Target of leafref \"l\" cannot be referenced because it is disabled.", "Schema location \"/pp:l\".");
+ CHECK_LOG_CTX("Not found node \"x\" in path.", "Schema location \"/pp:l\".");
}
static void
@@ -3755,6 +3897,55 @@ test_when(void **state)
" }"
"}",
LYS_IN_YANG, NULL));
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb,
+ "module d1 {"
+ " namespace urn:d1;"
+ " prefix d1;"
+ " container ifm {"
+ " container interfaces {"
+ " list interface {"
+ " key \"name\";"
+ " leaf name {"
+ " type string;"
+ " }"
+ " container ethernet {"
+ " container main-interface {"
+ " container l2-attribute {"
+ " when \"not(/d1:ifm/d1:interfaces/d1:interface/d1:trunk/d1:members/d1:member[d1:name=current()/../../../d1:name])\";"
+ " presence \"\";"
+ " }"
+ " }"
+ " }"
+ " container trunk {"
+ " container members {"
+ " list member {"
+ " key \"name\";"
+ " leaf name {"
+ " type string;"
+ " }"
+ " }"
+ " }"
+ " }"
+ " }"
+ " }"
+ " }"
+ "}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX,
+ "module d2 {"
+ " namespace \"urn:d2\";"
+ " prefix d2;"
+ " import d1 {"
+ " prefix d1;"
+ " }"
+ " augment \"/d1:ifm/d1:interfaces/d1:interface/d1:ethernet/d1:main-interface\" {"
+ " when \"not(d1:l2-attribute)\";"
+ " container extra-attribute {"
+ " presence \"\";"
+ " }"
+ " }"
+ "}",
+ LYS_IN_YANG, NULL));
}
static void
@@ -3796,6 +3987,68 @@ test_must(void **state)
LYS_IN_YANG, NULL));
/* no warnings */
CHECK_LOG_CTX(NULL, NULL);
+
+ /* must referencing disabled leafref in another module */
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb,
+ "module b-imp {"
+ " yang-version 1.1;"
+ " namespace \"urn:b-imp\";"
+ " prefix \"bi\";"
+ ""
+ " feature feat;"
+ ""
+ " grouping band-capabilities {"
+ " leaf band-number {"
+ " type uint16;"
+ " }"
+ ""
+ " container sub-band-info {"
+ " when \"../band-number = '46'\";"
+ " if-feature \"bi:feat\";"
+ " leaf number-of-laa-scarriers {"
+ " type uint8;"
+ " }"
+ " }"
+ " }"
+ ""
+ " container module-capability {"
+ " list band-capabilities {"
+ " key band-number;"
+ " config false;"
+ " uses band-capabilities;"
+ " }"
+ " container rw-sub-band-info {"
+ " if-feature \"bi:feat\";"
+ " leaf rw-number-of-laa-scarriers {"
+ " type leafref {"
+ " path \"/module-capability/band-capabilities/sub-band-info/number-of-laa-scarriers\";"
+ " require-instance false;"
+ " }"
+ " }"
+ " }"
+ " }"
+ "}");
+
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX,
+ "module b {"
+ " yang-version 1.1;"
+ " namespace \"urn:b\";"
+ " prefix \"b\";"
+ ""
+ " import b-imp {"
+ " prefix \"bi\";"
+ " }"
+ ""
+ " container laa-config {"
+ " must \"number-of-laa-scarriers <= /bi:module-capability/bi:rw-sub-band-info/bi:rw-number-of-laa-scarriers\";"
+ " }"
+ "}",
+ LYS_IN_YANG, NULL));
+ ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_REF_IMPLEMENTED);
+
+ CHECK_LOG_CTX("Schema node \"number-of-laa-scarriers\" not found; in expr \"number-of-laa-scarriers\" "
+ "with context node \"/b:laa-config\".", NULL);
}
int
@@ -3816,6 +4069,7 @@ main(void)
UTEST(test_type_empty, setup),
UTEST(test_type_union, setup),
UTEST(test_type_dflt, setup),
+ UTEST(test_type_exts, setup),
UTEST(test_status, setup),
UTEST(test_node_container, setup),
UTEST(test_node_leaflist, setup),
diff --git a/tests/utests/schema/test_yang.c b/tests/utests/schema/test_yang.c
index 78b1798..6d1c2ae 100644
--- a/tests/utests/schema/test_yang.c
+++ b/tests/utests/schema/test_yang.c
@@ -155,6 +155,7 @@ test_helpers(void **state)
assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
in.current = ":";
assert_int_equal(LY_EVALID, buf_store_char(YCTX, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
+ UTEST_LOG_CTX_CLEAN;
/* valid colon for prefixed identifiers */
len = size = 0;
p = NULL;
@@ -183,6 +184,7 @@ test_helpers(void **state)
assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix));
assert_int_equal(1, prefix);
assert_int_equal(LY_EVALID, lysp_check_identifierchar((struct lysp_ctx *)YCTX, ':', 0, &prefix));
+ CHECK_LOG_CTX("Invalid identifier first character ':' (0x003a).", "Line number 1.");
assert_int_equal(1, prefix);
assert_int_equal(LY_SUCCESS, lysp_check_identifierchar((struct lysp_ctx *)YCTX, 'b', 0, &prefix));
assert_int_equal(2, prefix);
@@ -887,12 +889,18 @@ test_module(void **state)
ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "module xxx { namespace urn:xxx; prefix x;}");
in.current = "module" SCHEMA_BEGINNING "include xxx;}";
assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL, "Including \"xxx\" submodule into \"name\" failed.", NULL);
+ CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL);
+ CHECK_LOG_CTX("Including \"xxx\" submodule into \"name\" failed.", NULL);
+ CHECK_LOG_CTX("Data model \"xxx\" not found in local searchdirs.", NULL);
+ CHECK_LOG_CTX("Parsing submodule failed.", NULL);
+ CHECK_LOG_CTX("Input data contains module in situation when a submodule is expected.", NULL);
ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "submodule xxx {belongs-to wrong-name {prefix w;}}");
in.current = "module" SCHEMA_BEGINNING "include xxx;}";
assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL, "Including \"xxx\" submodule into \"name\" failed.", NULL);
+ CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL);
+ CHECK_LOG_CTX("Including \"xxx\" submodule into \"name\" failed.", NULL);
+ UTEST_LOG_CTX_CLEAN;
ly_ctx_set_module_imp_clb(PARSER_CUR_PMOD(YCTX)->mod->ctx, test_imp_clb, "submodule xxx {belongs-to name {prefix x;}}");
TEST_GENERIC("include xxx;}", mod->includes,
diff --git a/tests/utests/schema/test_yin.c b/tests/utests/schema/test_yin.c
index 0ce3abc..a531b64 100644
--- a/tests/utests/schema/test_yin.c
+++ b/tests/utests/schema/test_yin.c
@@ -395,11 +395,13 @@ test_validate_value(void **state)
YCTX->xmlctx->value = "pre:b";
YCTX->xmlctx->value_len = 5;
assert_int_equal(yin_validate_value(YCTX, Y_IDENTIF_ARG), LY_EVALID);
+ CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", "Line number 1.");
assert_int_equal(yin_validate_value(YCTX, Y_PREF_IDENTIF_ARG), LY_SUCCESS);
YCTX->xmlctx->value = "pre:pre:b";
YCTX->xmlctx->value_len = 9;
assert_int_equal(yin_validate_value(YCTX, Y_PREF_IDENTIF_ARG), LY_EVALID);
+ CHECK_LOG_CTX("Invalid identifier character ':' (0x003a).", "Line number 1.");
}
static void
@@ -3097,7 +3099,7 @@ test_module_elem(void **state)
assert_int_equal(yin_parse_mod(YCTX, lysp_mod), LY_SUCCESS);
assert_string_equal(lysp_mod->mod->name, "mod");
- assert_string_equal(lysp_mod->revs, "2019-02-02");
+ assert_string_equal(lysp_mod->revs[0].date, "2019-02-02");
assert_string_equal(lysp_mod->mod->ns, "ns");
assert_string_equal(lysp_mod->mod->prefix, "pref");
assert_null(lysp_mod->mod->filepath);
@@ -3230,8 +3232,10 @@ test_submodule_elem(void **state)
assert_int_equal(lyxml_ctx_new(UTEST_LYCTX, UTEST_IN, &YCTX->xmlctx), LY_SUCCESS);
assert_int_equal(yin_parse_submod(YCTX, lysp_submod), LY_SUCCESS);
+ CHECK_LOG_CTX("YANG version 1.1 expects all includes in main module, includes in submodules (mod) are not necessary.",
+ NULL);
assert_string_equal(lysp_submod->name, "mod");
- assert_string_equal(lysp_submod->revs, "2019-02-02");
+ assert_string_equal(lysp_submod->revs[0].date, "2019-02-02");
assert_string_equal(lysp_submod->prefix, "pref");
assert_null(lysp_submod->filepath);
assert_string_equal(lysp_submod->org, "org");
diff --git a/tests/utests/types/binary.c b/tests/utests/types/binary.c
index 05b6b97..4f3ea66 100644
--- a/tests/utests/types/binary.c
+++ b/tests/utests/types/binary.c
@@ -52,14 +52,15 @@ test_plugin_store(void **state)
struct lys_module *mod;
struct lyd_value value = {0};
struct lyplg_type *type = lyplg_type_plugin_find("", NULL, ly_data_type2str[LY_TYPE_BINARY]);
- struct lysc_type *lysc_type;
+ struct lysc_type *lysc_type, *lysc_type2;
LY_ERR ly_ret;
const char *schema;
/* create schema. Prepare common used variables */
- schema = MODULE_CREATE_YANG("a", "leaf l {type binary;}");
+ schema = MODULE_CREATE_YANG("a", "leaf l {type binary;} leaf k {type binary {length 4..8;}}");
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+ lysc_type2 = ((struct lysc_node_leaf *)mod->compiled->data->next)->type;
/* check proper type */
assert_string_equal("libyang 2 - binary, version 1", type->id);
@@ -176,6 +177,35 @@ test_plugin_store(void **state)
assert_ptr_equal(value.realtype, lysc_type);
type->free(UTEST_LYCTX, &value);
+ /* length check */
+ val = "Zm91cg==";
+ dec_val = "four";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type2, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type2);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type2, dec_val, strlen(dec_val),
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type2);
+ type->free(UTEST_LYCTX, &value);
+
+ val = "ZWlnaHQwMTI=";
+ dec_val = "eight012";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type2, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type2);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type2, dec_val, strlen(dec_val),
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type2);
+ type->free(UTEST_LYCTX, &value);
+
/*
* ERROR TESTS
*/
@@ -194,6 +224,14 @@ test_plugin_store(void **state)
assert_int_equal(LY_EVALID, ly_ret);
assert_string_equal(err->msg, "Base64 encoded value length must be divisible by 4.");
ly_err_free(err);
+
+ val = "MTIz";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type2, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ assert_string_equal(err->msg, "Unsatisfied length - string \"MTIz\" length is not allowed.");
+ ly_err_free(err);
}
static void
diff --git a/tests/utests/types/bits.c b/tests/utests/types/bits.c
index 3d42ebc..eb40965 100644
--- a/tests/utests/types/bits.c
+++ b/tests/utests/types/bits.c
@@ -179,7 +179,7 @@ test_schema_yang(void **state)
"leaf port {type my_type {"
" bit ten {position 11;} bit two;}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Invalid bits - position of the item \"ten\" has changed from 10 to 11 in the derived type.", "/TERR_0:port");
+ CHECK_LOG_CTX("Invalid bits - position of the item \"ten\" has changed from 10 to 11 in the derived type.", "Path \"/TERR_0:port\".");
/* add new bit */
schema = MODULE_CREATE_YANG("TERR_1", "typedef my_type{type bits {"
@@ -187,15 +187,15 @@ test_schema_yang(void **state)
"leaf port {type my_type {"
" bit ten {position 10;} bit two; bit test;}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Invalid bits - derived type adds new item \"test\".", "/TERR_1:port");
+ CHECK_LOG_CTX("Invalid bits - derived type adds new item \"test\".", "Path \"/TERR_1:port\".");
/* different max value => autoadd index */
schema = MODULE_CREATE_YANG("TERR_2", "leaf port {type bits {"
" bit first {position -1;} bit second;"
"}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_2\" failed.", NULL,
- "Invalid value \"-1\" of \"position\".", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERR_2\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"-1\" of \"position\".", "Line number 5.");
/* different max value => autoadd index */
schema = MODULE_CREATE_YANG("TERR_3", "leaf port {type bits {"
@@ -203,48 +203,48 @@ test_schema_yang(void **state)
"}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Invalid bits - it is not possible to auto-assign bit position for \"second\" since the highest value is already 4294967295.",
- "/TERR_3:port");
+ "Path \"/TERR_3:port\".");
schema = MODULE_CREATE_YANG("TERR_4", "leaf port {type bits {"
" bit first {position 10;} bit \"\";"
"}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_4\" failed.", NULL,
- "Statement argument is required.", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERR_4\" failed.", NULL);
+ CHECK_LOG_CTX("Statement argument is required.", "Line number 5.");
/* wrong character */
schema = MODULE_CREATE_YANG("TERR_5", "leaf port {type bits {"
" bit first {position 10;} bit abcd^;"
"}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_5\" failed.", NULL,
- "Invalid identifier character '^' (0x005e).", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERR_5\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid identifier character '^' (0x005e).", "Line number 5.");
schema = MODULE_CREATE_YANG("TERR_6", "leaf port {type bits {"
" bit hi; bit hi;"
"}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_6\" failed.", NULL,
- "Duplicate identifier \"hi\" of bit statement.", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERR_6\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"hi\" of bit statement.", "Line number 5.");
/* wrong character */
schema = MODULE_CREATE_YANG("TERR_7", "leaf port {type bits {"
" bit first {position 10;} bit \"ab&cd\";"
"}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_7\" failed.", NULL,
- "Invalid identifier character '&' (0x0026).", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERR_7\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid identifier character '&' (0x0026).", "Line number 5.");
schema = MODULE_CREATE_YANG("TERR_8", "leaf port {type bits {"
" bit first {position 10;} bit \"4abcd\";"
"}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_8\" failed.", NULL,
- "Invalid identifier first character '4' (0x0034).", "Line number 5.");
+ CHECK_LOG_CTX("Parsing module \"TERR_8\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid identifier first character '4' (0x0034).", "Line number 5.");
schema = MODULE_CREATE_YANG("TERR_9", "leaf port {type bits;}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Missing bit substatement for bits type.", "/TERR_9:port");
+ CHECK_LOG_CTX("Missing bit substatement for bits type.", "Path \"/TERR_9:port\".");
/* new features of YANG 1.1 in YANG 1.0 */
schema = "module TERR_10 {"
@@ -256,8 +256,8 @@ test_schema_yang(void **state)
" }}"
"}";
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_10\" failed.", NULL,
- "Invalid keyword \"if-feature\" as a child of \"bit\" - the statement is allowed only in YANG 1.1 modules.",
+ CHECK_LOG_CTX("Parsing module \"TERR_10\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid keyword \"if-feature\" as a child of \"bit\" - the statement is allowed only in YANG 1.1 modules.",
"Line number 1.");
schema = "module TERR_11 {"
@@ -267,7 +267,7 @@ test_schema_yang(void **state)
" leaf l {type mytype {bit one;}}"
"}";
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Bits type can be subtyped only in YANG 1.1 modules.", "/TERR_11:l");
+ CHECK_LOG_CTX("Bits type can be subtyped only in YANG 1.1 modules.", "Path \"/TERR_11:l\".");
/* feature is not present */
schema = MODULE_CREATE_YANG("IF_0", "feature f;"
@@ -415,7 +415,7 @@ test_schema_yin(void **state)
" <bit name=\"ten\"> <position value=\"11\"/> </bit> <bit name=\"two\"/>"
"</type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Invalid bits - position of the item \"ten\" has changed from 10 to 11 in the derived type.", "/TERR_0:port");
+ CHECK_LOG_CTX("Invalid bits - position of the item \"ten\" has changed from 10 to 11 in the derived type.", "Path \"/TERR_0:port\".");
/* add new bit */
schema = MODULE_CREATE_YIN("TERR_1",
@@ -431,7 +431,7 @@ test_schema_yin(void **state)
" <bit name=\"test\"/>"
"</type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Invalid bits - derived type adds new item \"test\".", "/TERR_1:port");
+ CHECK_LOG_CTX("Invalid bits - derived type adds new item \"test\".", "Path \"/TERR_1:port\".");
/* different max value => autoadd index */
schema = MODULE_CREATE_YIN("TERR_2",
@@ -440,8 +440,8 @@ test_schema_yin(void **state)
" <bit name=\"second\">"
"</type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_2\" failed.", NULL,
- "Invalid value \"-1\" of \"value\" attribute in \"position\" element.", "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_2\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid value \"-1\" of \"value\" attribute in \"position\" element.", "Line number 8.");
/* different max value => autoadd index */
schema = MODULE_CREATE_YIN("TERR_3",
@@ -451,7 +451,7 @@ test_schema_yin(void **state)
"</type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
CHECK_LOG_CTX("Invalid bits - it is not possible to auto-assign bit position for \"second\" since the highest value is already 4294967295.",
- "/TERR_3:port");
+ "Path \"/TERR_3:port\".");
schema = MODULE_CREATE_YIN("TERR_4",
"<leaf name=\"port\"> <type name=\"bits\">"
@@ -459,9 +459,8 @@ test_schema_yin(void **state)
" <bit name=\"second\"/>"
"</type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_4\" failed.", NULL,
- "Invalid identifier first character ' ' (0x0020).",
- "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_4\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid identifier first character ' ' (0x0020).", "Line number 8.");
schema = MODULE_CREATE_YIN("TERR_5",
"<leaf name=\"port\"> <type name=\"bits\">"
@@ -469,9 +468,8 @@ test_schema_yin(void **state)
" <bit name=\"second\"/>"
"</type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_5\" failed.", NULL,
- "Invalid identifier character ' ' (0x0020).",
- "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_5\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid identifier character ' ' (0x0020).", "Line number 8.");
schema = MODULE_CREATE_YIN("TERR_6",
"<leaf name=\"port\"> <type name=\"bits\">"
@@ -479,9 +477,8 @@ test_schema_yin(void **state)
" <bit name=\"hi\"/>"
"</type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_6\" failed.", NULL,
- "Duplicate identifier \"hi\" of bit statement.",
- "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_6\" failed.", NULL);
+ CHECK_LOG_CTX("Duplicate identifier \"hi\" of bit statement.", "Line number 8.");
schema = MODULE_CREATE_YIN("TERR_7",
"<leaf name=\"port\"> <type name=\"bits\">"
@@ -489,9 +486,8 @@ test_schema_yin(void **state)
" <bit name=\"second\"/>"
"</type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_7\" failed.", NULL,
- "Invalid identifier first character '4' (0x0034).",
- "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_7\" failed.", NULL);
+ CHECK_LOG_CTX("Invalid identifier first character '4' (0x0034).", "Line number 8.");
/* TEST EMPTY NAME*/
schema = MODULE_CREATE_YIN("TERR_8",
@@ -500,9 +496,8 @@ test_schema_yin(void **state)
" <bit name=\"second\"/>"
"</type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Parsing module \"TERR_8\" failed.", NULL,
- "Empty identifier is not allowed.",
- "Line number 8.");
+ CHECK_LOG_CTX("Parsing module \"TERR_8\" failed.", NULL);
+ CHECK_LOG_CTX("Empty identifier is not allowed.", "Line number 8.");
}
static void
@@ -954,30 +949,6 @@ test_plugin_compare(void **state)
assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
type->free(UTEST_LYCTX, &(diff_type_val));
- /*
- * derivated type add some limitations
- */
- diff_type_text = val_init[2];
- diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next->next)->type;
- ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
- 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &diff_type_val, NULL, &err);
- assert_int_equal(LY_SUCCESS, ly_ret);
- assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[2])));
- assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
- type->free(UTEST_LYCTX, &(diff_type_val));
-
- /*
- * different type (STRING)
- */
- diff_type_text = val_init[2];
- diff_type = ((struct lysc_node_leaf *)mod->compiled->data->next->next->next)->type;
- ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
- 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &diff_type_val, NULL, &err);
- assert_int_equal(LY_SUCCESS, ly_ret);
- assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[2])));
- assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[0])));
- type->free(UTEST_LYCTX, &(diff_type_val));
-
/* delete values */
for (unsigned int it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
type->free(UTEST_LYCTX, &(values[it]));
diff --git a/tests/utests/types/identityref.c b/tests/utests/types/identityref.c
index cdfe057..107164d 100644
--- a/tests/utests/types/identityref.c
+++ b/tests/utests/types/identityref.c
@@ -27,15 +27,6 @@
NODES \
"}\n"
-#define TEST_SUCCESS_XML(MOD_NAME, NAMESPACES, NODE_NAME, DATA, TYPE, ...) \
- { \
- struct lyd_node *tree; \
- const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \
- CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
- CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
- lyd_free_all(tree); \
- }
-
#define TEST_ERROR_XML(MOD_NAME, NAMESPACES, NODE_NAME, DATA) \
{\
struct lyd_node *tree; \
@@ -64,21 +55,44 @@ static void
test_data_xml(void **state)
{
const char *schema, *schema2;
+ struct lyd_node *tree;
+ const char *data;
/* xml test */
- schema = MODULE_CREATE_YANG("ident-base", "identity ident-base;"
- "identity ident-imp {base ident-base;}");
+ schema = "module ident-base {"
+ " yang-version 1.1;"
+ " namespace \"urn:tests:ident-base\";"
+ " prefix ib;"
+ " identity ident-base;"
+ " identity ident-imp {base ident-base;}"
+ "}";
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
- schema2 = MODULE_CREATE_YANG("defs", "import ident-base {prefix ib;}"
- "identity ident1 {base ib:ident-base;}"
- "leaf l1 {type identityref {base ib:ident-base;}}");
+ schema2 = "module defs {"
+ " yang-version 1.1;"
+ " namespace \"urn:tests:defs\";"
+ " prefix d;"
+ " import ident-base {prefix ib;}"
+ " identity ident1 {base ib:ident-base;}"
+ " leaf l1 {type identityref {base ib:ident-base;}}"
+ "}";
UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL);
- TEST_SUCCESS_XML("defs", "", "l1", "ident1", IDENT, "defs:ident1", "ident1");
-
- TEST_SUCCESS_XML("defs", "xmlns:i=\"urn:tests:ident-base\"", "l1", "i:ident-imp", IDENT, "ident-base:ident-imp",
- "ident-imp");
+ /* local ident, XML/JSON print */
+ data = "<l1 xmlns=\"urn:tests:defs\">ident1</l1>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, IDENT, "defs:ident1", "ident1");
+ CHECK_LYD_STRING_PARAM(tree, data, LYD_XML, LYD_PRINT_SHRINK);
+ CHECK_LYD_STRING_PARAM(tree, "{\"defs:l1\":\"ident1\"}", LYD_JSON, LYD_PRINT_SHRINK);
+ lyd_free_all(tree);
+
+ /* foreign ident, XML/JSON print */
+ data = "<l1 xmlns=\"urn:tests:defs\" xmlns:ib=\"urn:tests:ident-base\">ib:ident-imp</l1>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, IDENT, "ident-base:ident-imp", "ident-imp");
+ CHECK_LYD_STRING_PARAM(tree, data, LYD_XML, LYD_PRINT_SHRINK);
+ CHECK_LYD_STRING_PARAM(tree, "{\"defs:l1\":\"ident-base:ident-imp\"}", LYD_JSON, LYD_PRINT_SHRINK);
+ lyd_free_all(tree);
/* invalid value */
TEST_ERROR_XML("defs", "", "l1", "fast-ethernet");
diff --git a/tests/utests/types/instanceid.c b/tests/utests/types/instanceid.c
index 06c8622..3126f61 100644
--- a/tests/utests/types/instanceid.c
+++ b/tests/utests/types/instanceid.c
@@ -79,12 +79,12 @@ static void
test_data_xml(void **state)
{
const char *schema, *schema2;
- const enum ly_path_pred_type val1[] = {LY_PATH_PREDTYPE_NONE, LY_PATH_PREDTYPE_NONE};
- const enum ly_path_pred_type val2[] = {LY_PATH_PREDTYPE_LIST, LY_PATH_PREDTYPE_NONE};
+ const enum ly_path_pred_type val1[] = {0, 0};
+ const enum ly_path_pred_type val2[] = {LY_PATH_PREDTYPE_LIST, 0};
const enum ly_path_pred_type val3[] = {LY_PATH_PREDTYPE_LEAFLIST};
- const enum ly_path_pred_type val4[] = {LY_PATH_PREDTYPE_LIST, LY_PATH_PREDTYPE_NONE};
- const enum ly_path_pred_type val5[] = {LY_PATH_PREDTYPE_LIST, LY_PATH_PREDTYPE_NONE};
- const enum ly_path_pred_type val6[] = {LY_PATH_PREDTYPE_LIST, LY_PATH_PREDTYPE_NONE};
+ const enum ly_path_pred_type val4[] = {LY_PATH_PREDTYPE_LIST, 0};
+ const enum ly_path_pred_type val5[] = {LY_PATH_PREDTYPE_LIST, 0};
+ const enum ly_path_pred_type val6[] = {LY_PATH_PREDTYPE_LIST, 0};
/* xml test */
schema = MODULE_CREATE_YANG("mod", "container cont {leaf l2 {type empty;}}");
@@ -136,22 +136,23 @@ test_data_xml(void **state)
TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>a</id></list>"
"<list xmlns=\"urn:tests:defs\"><id>b</id><value>x</value></list>",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/xdf:list[2]/xdf:value", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[2]/xdf:value\" value - semantic error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[2]/xdf:value\" value - semantic error: "
+ "Positional predicate defined for configuration list \"list\" in path.",
"Schema location \"/defs:l1\", line number 1.");
TEST_ERROR_XML2("",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/t:cont/t:1l", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/t:cont/t:1l\" value - syntax error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:cont/t:1l\" value - syntax error: Invalid character 't'[9] of expression '/t:cont/t:1l'.",
"Schema location \"/defs:l1\", line number 1.");
TEST_ERROR_XML2("",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/t:cont:t:1l", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/t:cont:t:1l\" value - syntax error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:cont:t:1l\" value - syntax error: Invalid character ':'[8] of expression '/t:cont:t:1l'.",
"Schema location \"/defs:l1\", line number 1.");
TEST_ERROR_XML2("",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l1", "/xdf:cont/xdf:invalid/xdf:path", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:cont/xdf:invalid/xdf:path\" value - semantic error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:cont/xdf:invalid/xdf:path\" value - semantic error: Not found node \"invalid\" in path.",
"Schema location \"/defs:l1\", line number 1.");
/* non-existing instances, instance-identifier is here in JSON format because it is already in internal
@@ -190,72 +191,72 @@ test_data_xml(void **state)
/* more errors */
TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">x</llist>",
"defs", "xmlns:t=\"urn:tests:defs\"", "t:l1", "/t:llist[1", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[1\" value - syntax error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[1\" value - syntax error: Unexpected XPath expression end.",
"Schema location \"/defs:l1\", line number 1.");
TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"/>",
"defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont[1]", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont[1]\" value - semantic error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont[1]\" value - semantic error: Positional predicate defined for container \"cont\" in path.",
"Schema location \"/defs:l1\", line number 1.");
TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"/>",
"defs", "xmlns:m=\"urn:tests:mod\"", "l1", "[1]", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"[1]\" value - syntax error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"[1]\" value - syntax error: Unexpected XPath token \"[\" (\"[1]\"), expected \"Operator(Path)\".",
"Schema location \"/defs:l1\", line number 1.");
TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"><l2/></cont>",
"defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont/m:l2[l2='1']", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont/m:l2[l2='1']\" value - syntax error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont/m:l2[l2='1']\" value - syntax error: Prefix missing for \"l2\" in path.",
"Schema location \"/defs:l1\", line number 1.");
TEST_ERROR_XML2("<cont xmlns=\"urn:tests:mod\"><l2/></cont>",
"defs", "xmlns:m=\"urn:tests:mod\"", "l1", "/m:cont/m:l2[m:l2='1']", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont/m:l2[m:l2='1']\" value - semantic error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/m:cont/m:l2[m:l2='1']\" value - semantic error: List predicate defined for leaf \"l2\" in path.",
"Schema location \"/defs:l1\", line number 1.");
TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">1</llist><llist xmlns=\"urn:tests:defs\">2</llist>",
"defs", "xmlns:t=\"urn:tests:defs\"", "t:l1", "/t:llist[4]", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[4]\" value - semantic error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[4]\" value - semantic error: Positional predicate defined for configuration leaf-list \"llist\" in path.",
"Schema location \"/defs:l1\", line number 1.");
TEST_ERROR_XML2("",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/t:llist[6]", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[6]\" value - semantic error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[6]\" value - semantic error: No module connected with the prefix \"t\" found (prefix format XML prefixes).",
"Schema location \"/defs:l2\", line number 1.");
TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>1</id><value>x</value></list>",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list[xdf:value='x']", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[xdf:value='x']\" value - semantic error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[xdf:value='x']\" value - semantic error: Key expected instead of leaf \"value\" in path.",
"Schema location \"/defs:l2\", line number 1.");
TEST_ERROR_XML2("",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list[.='x']", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[.='x']\" value - semantic error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[.='x']\" value - semantic error: Leaf-list predicate defined for list \"list\" in path.",
"Schema location \"/defs:l2\", line number 1.");
TEST_ERROR_XML2("<llist xmlns=\"urn:tests:defs\">1</llist>",
"defs", "xmlns:t=\"urn:tests:defs\"", "t:l1", "/t:llist[.='x']", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[.='x']\" value - semantic error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[.='x']\" value - semantic error: Invalid type uint32 value \"x\".",
"Schema location \"/defs:l1\", line number 1.");
TEST_ERROR_XML2("",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/t:llist[1][2]", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[1][2]\" value - syntax error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[1][2]\" value - syntax error: Unparsed characters \"[2]\" left at the end of path.",
"Schema location \"/defs:l2\", line number 1.");
TEST_ERROR_XML2("",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/t:llist[.='a'][.='b']", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[.='a'][.='b']\" value - syntax error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/t:llist[.='a'][.='b']\" value - syntax error: Unparsed characters \"[.='b']\" left at the end of path.",
"Schema location \"/defs:l2\", line number 1.");
TEST_ERROR_XML2("<list xmlns=\"urn:tests:defs\"><id>1</id><value>x</value></list>",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list[xdf:id='1'][xdf:id='2']/xdf:value", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[xdf:id='1'][xdf:id='2']/xdf:value\" value - syntax error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list[xdf:id='1'][xdf:id='2']/xdf:value\" value - syntax error: Duplicate predicate key \"id\" in path.",
"Schema location \"/defs:l2\", line number 1.");
TEST_ERROR_XML2("",
"defs", "xmlns:xdf=\"urn:tests:defs\"", "xdf:l2", "/xdf:list2[xdf:id='1']/xdf:value", LY_EVALID);
- CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list2[xdf:id='1']/xdf:value\" value - semantic error.",
+ CHECK_LOG_CTX("Invalid instance-identifier \"/xdf:list2[xdf:id='1']/xdf:value\" value - semantic error: Predicate missing for a key of list \"list2\" in path.",
"Schema location \"/defs:l2\", line number 1.");
}
diff --git a/tests/utests/types/int8.c b/tests/utests/types/int8.c
index 7d0b9ad..198d1f7 100644
--- a/tests/utests/types/int8.c
+++ b/tests/utests/types/int8.c
@@ -240,22 +240,22 @@ test_schema_yang(void **state)
/* TEST ERROR -60 .. 0 | 0 .. 127 */
schema = MODULE_CREATE_YANG("ERR0", "leaf port {type int8 {range \"-60 .. 0 | 0 .. 127\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EEXIST);
- CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (0).", "/ERR0:port");
+ CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (0).", "Path \"/ERR0:port\".");
/* TEST ERROR 0 .. 128 */
schema = MODULE_CREATE_YANG("ERR1", "leaf port {type int8 {range \"0 .. 128\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
- CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.", "/ERR1:port");
+ CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.", "Path \"/ERR1:port\".");
/* TEST ERROR -129 .. 126 */
schema = MODULE_CREATE_YANG("ERR2", "leaf port {type int8 {range \"-129 .. 0\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
- CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "/ERR2:port");
+ CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "Path \"/ERR2:port\".");
/* TEST ERROR 0 */
schema = MODULE_CREATE_YANG("ERR3", "leaf port {type int8 {range \"-129\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
- CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "/ERR3:port");
+ CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "Path \"/ERR3:port\".");
/*
* TEST MODULE SUBTYPE
@@ -374,7 +374,7 @@ test_schema_yang(void **state)
"leaf my_leaf {type my_int_type {range \"min .. max\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Invalid range restriction - the derived restriction (min .. max) is not equally or more limiting.",
- "/TS_ERR0:my_leaf");
+ "Path \"/TS_ERR0:my_leaf\".");
/* TEST SUBTYPE ERROR -80 .. 80 */
schema = MODULE_CREATE_YANG("TS_ERR1",
@@ -382,7 +382,7 @@ test_schema_yang(void **state)
" leaf my_leaf {type my_int_type {range \"-80 .. 80\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Invalid range restriction - the derived restriction (-80 .. 80) is not equally or more limiting.",
- "/TS_ERR1:my_leaf");
+ "Path \"/TS_ERR1:my_leaf\".");
/* TEST SUBTYPE ERROR 0 .. max */
schema = MODULE_CREATE_YANG("TS_ERR2",
@@ -390,7 +390,7 @@ test_schema_yang(void **state)
"leaf my_leaf {type my_int_type {range \"0 .. max\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Invalid range restriction - the derived restriction (0 .. max) is not equally or more limiting.",
- "/TS_ERR2:my_leaf");
+ "Path \"/TS_ERR2:my_leaf\".");
/* TEST SUBTYPE ERROR -2 .. 2 */
schema = MODULE_CREATE_YANG("TS_ERR3",
@@ -398,7 +398,7 @@ test_schema_yang(void **state)
"leaf my_leaf {type my_int_type {range \"-2 .. 2\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Invalid range restriction - the derived restriction (-2 .. 2) is not equally or more limiting.",
- "/TS_ERR3:my_leaf");
+ "Path \"/TS_ERR3:my_leaf\".");
/* TEST SUBTYPE ERROR -2 .. 2 */
schema = MODULE_CREATE_YANG("TS_ERR4",
@@ -406,7 +406,7 @@ test_schema_yang(void **state)
"leaf my_leaf {type my_int_type {range \"-100 .. -90 | 100 .. 128\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.",
- "/TS_ERR4:my_leaf");
+ "Path \"/TS_ERR4:my_leaf\".");
/*
* TEST DEFAULT VALUE
@@ -745,7 +745,7 @@ test_schema_yin(void **state)
" <type name=\"int8\"> <range value = \"min .. 0 | 0 .. 12\"/> </type>"
"</leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EEXIST);
- CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (0).", "/TE0:port");
+ CHECK_LOG_CTX("Invalid range restriction - values are not in ascending order (0).", "Path \"/TE0:port\".");
/* TEST ERROR 0 .. 128 */
schema = MODULE_CREATE_YIN("TE1",
@@ -753,7 +753,7 @@ test_schema_yin(void **state)
" <type name=\"int8\"> <range value = \"0 .. 128\"/> </type>"
"</leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED);
- CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.", "/TE1:port");
+ CHECK_LOG_CTX("Invalid range restriction - value \"128\" does not fit the type limitations.", "Path \"/TE1:port\".");
/* TEST ERROR -129 .. 126 */
schema = MODULE_CREATE_YIN("TE2",
@@ -761,7 +761,7 @@ test_schema_yin(void **state)
" <type name=\"int8\"> <range value =\"-129 .. 126\"/> </type>"
"</leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED);
- CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "/TE2:port");
+ CHECK_LOG_CTX("Invalid range restriction - value \"-129\" does not fit the type limitations.", "Path \"/TE2:port\".");
/* TEST YIN */
schema = MODULE_CREATE_YIN("TS0",
@@ -817,7 +817,7 @@ test_schema_yin(void **state)
"</leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
CHECK_LOG_CTX("Invalid range restriction - the derived restriction (min .. max) is not equally or more limiting.",
- "/TS_ERR1:port");
+ "Path \"/TS_ERR1:port\".");
/* TEST ERROR */
schema = MODULE_CREATE_YIN("TS_ERR2",
@@ -829,7 +829,7 @@ test_schema_yin(void **state)
"</leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
CHECK_LOG_CTX("Invalid range restriction - the derived restriction (5 .. 11) is not equally or more limiting.",
- "/TS_ERR2:port");
+ "Path \"/TS_ERR2:port\".");
/* TEST DEFAULT VALUE */
schema = MODULE_CREATE_YIN("DF0",
@@ -1243,8 +1243,14 @@ test_data_json(void **state)
TEST_SUCCESS_JSON("T0", "127", INT8, "127", 127);
/* leading zeros */
TEST_ERROR_JSON("T0", "015");
+ CHECK_LOG_CTX("Invalid character sequence \"15}\", expected a JSON object-end or next item.",
+ "Line number 1.");
TEST_ERROR_JSON("T0", "-015");
+ CHECK_LOG_CTX("Invalid character sequence \"15}\", expected a JSON object-end or next item.",
+ "Line number 1.");
TEST_ERROR_JSON("defs", "+50");
+ CHECK_LOG_CTX("Invalid character sequence \"+50}\", expected a JSON value.",
+ "Line number 1.");
TEST_ERROR_JSON("T0", "-129");
CHECK_LOG_CTX("Value \"-129\" is out of type int8 min/max bounds.",
"Schema location \"/T0:port\", line number 1.");
@@ -1510,6 +1516,7 @@ test_plugin_store(void **state)
ly_ret = type->store(UTEST_LYCTX, &lysc_type_test, val_text, strlen(val_text),
0, LY_VALUE_XML, NULL, LYD_VALHINT_HEXNUM, NULL, &value, NULL, &err);
assert_int_equal(LY_EINT, ly_ret);
+ UTEST_LOG_CTX_CLEAN;
/*
* ERROR TESTS
@@ -1541,6 +1548,8 @@ test_plugin_store(void **state)
0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &value, NULL, &err);
assert_int_equal(LY_EVALID, ly_ret);
ly_err_free(err);
+
+ UTEST_LOG_CTX_CLEAN;
}
static void
diff --git a/tests/utests/types/leafref.c b/tests/utests/types/leafref.c
index c8d0cb6..21e91cd 100644
--- a/tests/utests/types/leafref.c
+++ b/tests/utests/types/leafref.c
@@ -209,6 +209,70 @@ test_plugin_lyb(void **state)
TEST_SUCCESS_LYB("lyb", "lst", "key_str", "lref", "key_str");
}
+static void
+test_data_xpath_json(void **state)
+{
+ const char *schema, *data;
+ struct lyd_node *tree;
+
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED);
+
+ /* json xpath test */
+ schema = MODULE_CREATE_YANG("xp_test",
+ "list l1 {key t1;"
+ "leaf t1 {type uint8;}"
+ "list l2 {key t2;"
+ "leaf t2 {type uint8;}"
+ "leaf-list l3 {type uint8;}"
+ "}}"
+ "leaf r1 {type leafref {path \"../l1/t1\";}}"
+ "leaf r2 {type leafref {path \"deref(../r1)/../l2/t2\";}}"
+ "leaf r3 {type leafref {path \"deref(../r2)/../l3\";}}");
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ data = "{"
+ " \"xp_test:l1\":[{\"t1\": 1,\"l2\":[{\"t2\": 2,\"l3\":[3]}]}],"
+ " \"xp_test:r1\": 1,"
+ " \"xp_test:r2\": 2,"
+ " \"xp_test:r3\": 3"
+ "}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_all(tree);
+}
+
+static void
+test_xpath_invalid_schema(void **state)
+{
+ const char *schema1, *schema2;
+
+ ly_ctx_set_options(UTEST_LYCTX, LY_CTX_LEAFREF_EXTENDED);
+ schema1 = MODULE_CREATE_YANG("xp_test",
+ "list l1 {key t1;"
+ "leaf t1 {type uint8;}"
+ "list l2 {key t2;"
+ "leaf t2 {type uint8;}"
+ "leaf-list l3 {type uint8;}"
+ "}}"
+ "leaf r1 {type leafref {path \"deref(../l1)/../l2/t2\";}}");
+
+ UTEST_INVALID_MODULE(schema1, LYS_IN_YANG, NULL, LY_EVALID)
+ CHECK_LOG_CTX("The deref function target node \"l1\" is not leaf nor leaflist", "Schema location \"/xp_test:r1\".");
+
+ schema2 = MODULE_CREATE_YANG("xp_test",
+ "list l1 {key t1;"
+ "leaf t1 {type uint8;}"
+ "list l2 {key t2;"
+ "leaf t2 {type uint8;}"
+ "leaf-list l3 {type uint8;}"
+ "}}"
+ "leaf r1 {type uint8;}"
+ "leaf r2 {type leafref {path \"deref(../r1)/../l2/t2\";}}");
+
+ UTEST_INVALID_MODULE(schema2, LYS_IN_YANG, NULL, LY_EVALID)
+ CHECK_LOG_CTX("The deref function target node \"r1\" is not leafref", "Schema location \"/xp_test:r2\".");
+}
+
int
main(void)
{
@@ -216,6 +280,8 @@ main(void)
UTEST(test_data_xml),
UTEST(test_data_json),
UTEST(test_plugin_lyb),
+ UTEST(test_data_xpath_json),
+ UTEST(test_xpath_invalid_schema)
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/types/string.c b/tests/utests/types/string.c
index d232e9d..ce5ae8d 100644
--- a/tests/utests/types/string.c
+++ b/tests/utests/types/string.c
@@ -200,15 +200,15 @@ test_schema_yang(void **state)
/* ERROR TESTS NEGATIVE VALUE */
schema = MODULE_CREATE_YANG("ERR0", "leaf port {type string {length \"-1 .. 20\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
- CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR0:port");
+ CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "Path \"/ERR0:port\".");
schema = MODULE_CREATE_YANG("ERR1", "leaf port {type string {length \"100 .. 18446744073709551616\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
- CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "/ERR1:port");
+ CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "Path \"/ERR1:port\".");
schema = MODULE_CREATE_YANG("ERR2", "leaf port {type string {length \"10 .. 20 | 20 .. 30\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EEXIST);
- CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (20).", "/ERR2:port");
+ CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (20).", "Path \"/ERR2:port\".");
schema = MODULE_CREATE_YANG("ERR3",
"typedef my_type {"
@@ -216,7 +216,7 @@ test_schema_yang(void **state)
"}"
"leaf port {type my_type {length \"-1 .. 15\";}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EDENIED);
- CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR3:port");
+ CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "Path \"/ERR3:port\".");
/*
* PATTERN
@@ -286,7 +286,7 @@ test_schema_yang(void **state)
"}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Regular expression \"[a-zA-Z_[a-zA-Z0-9\\-_.*\" is not valid (\"\": missing terminating ] for character class).",
- "/TPATTERN_ERR_0:port");
+ "Path \"/TPATTERN_ERR_0:port\".");
schema = MODULE_CREATE_YANG("TDEFAULT_0",
"typedef my_type {"
@@ -323,14 +323,14 @@ test_schema_yang(void **state)
"}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Regular expression \"\\[a]b\" is not valid (\"]b\": character group doesn't begin with '[').",
- "/TPATTERN_BC_ERR_1:port");
+ "Path \"/TPATTERN_BC_ERR_1:port\".");
schema = MODULE_CREATE_YANG("TPATTERN_BC_ERR_2", "leaf port {type string {"
"pattern \"\\\\[a]b\";" /* pattern "\\[a]b"; */
"}}");
UTEST_INVALID_MODULE(schema, LYS_IN_YANG, NULL, LY_EVALID);
CHECK_LOG_CTX("Regular expression \"\\[a]b\" is not valid (\"]b\": character group doesn't begin with '[').",
- "/TPATTERN_BC_ERR_2:port");
+ "Path \"/TPATTERN_BC_ERR_2:port\".");
/* PATTERN AND LENGTH */
schema = MODULE_CREATE_YANG("TPL_0",
@@ -467,26 +467,26 @@ test_schema_yin(void **state)
schema = MODULE_CREATE_YIN("ERR0", "<leaf name=\"port\"> <type name=\"string\">"
"<length value =\"-1 .. 20\"/> </type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED);
- CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR0:port");
+ CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "Path \"/ERR0:port\".");
schema = MODULE_CREATE_YIN("ERR1", "<leaf name=\"port\"> <type name=\"string\">"
"<length value=\"100 .. 18446744073709551616\"/>"
"</type> </leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
- CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "/ERR1:port");
+ CHECK_LOG_CTX("Invalid length restriction - invalid value \"18446744073709551616\".", "Path \"/ERR1:port\".");
schema = MODULE_CREATE_YIN("ERR2", "<leaf name=\"port\">"
"<type name=\"string\"> <length value=\"10 .. 20 | 20 .. 30\"/>"
"</type> </leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EEXIST);
- CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (20).", "/ERR2:port");
+ CHECK_LOG_CTX("Invalid length restriction - values are not in ascending order (20).", "Path \"/ERR2:port\".");
schema = MODULE_CREATE_YIN("ERR3",
"<typedef name=\"my_type\"> <type name=\"string\"/> </typedef>"
"<leaf name=\"port\"> <type name=\"my_type\"> <length value=\"-1 .. 15\"/>"
"</type> </leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EDENIED);
- CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "/ERR3:port");
+ CHECK_LOG_CTX("Invalid length restriction - value \"-1\" does not fit the type limitations.", "Path \"/ERR3:port\".");
/*
* PATTERN
@@ -557,7 +557,7 @@ test_schema_yin(void **state)
"</type> </leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
CHECK_LOG_CTX("Regular expression \"[a-zA-Z_][a-zA-Z0-9\\-_.*\" is not valid (\"\": missing terminating ] for character class).",
- "/TPATTERN_ERR_0:port");
+ "Path \"/TPATTERN_ERR_0:port\".");
/*
* DEFAUT VALUE
@@ -618,16 +618,8 @@ test_schema_yin(void **state)
"</typedef>"
"<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
-
- schema = MODULE_CREATE_YIN("TDEFAULT_2",
- "<typedef name=\"my_type\">"
- " <type name=\"string\">"
- " <length value=\"2\"/>"
- " </type>"
- " <default value=\"a1i-j&lt;\"/>"
- "</typedef>"
- "<leaf name=\"port\"> <type name=\"my_type\"/> </leaf>");
- UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied length - string \"a1i-j<\" length is not allowed.).",
+ "Schema location \"/TDEFAULT_2:port\".");
schema = MODULE_CREATE_YIN("TDEFAULT_3",
"<typedef name=\"my_type\">"
@@ -636,6 +628,8 @@ test_schema_yin(void **state)
"</type> </typedef>"
"<leaf name=\"port\"><type name=\"my_type\"> <pattern value=\"bcd.*\"/> </type></leaf>");
UTEST_INVALID_MODULE(schema, LYS_IN_YIN, NULL, LY_EVALID);
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Unsatisfied pattern - \"a1i-j<\" does not conform to \"bcd.*\".).",
+ "Schema location \"/TDEFAULT_3:port\".");
}
@@ -874,7 +868,7 @@ test_data_json(void **state)
CHECK_LOG_CTX("Invalid character reference \"\\f\" (0x0000000c).", "Line number 1.");
TEST_ERROR_JSON("T0", "\"");
- CHECK_LOG_CTX("Unexpected character \"\"\" after JSON string.", "Line number 1.");
+ CHECK_LOG_CTX("Invalid character sequence \"\"}\", expected a JSON object-end or next item.", "Line number 1.");
TEST_ERROR_JSON("T0", "aabb \\x");
CHECK_LOG_CTX("Invalid character escape sequence \\x.", "Line number 1.");
@@ -1258,26 +1252,6 @@ test_plugin_compare(void **state)
assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
type->free(UTEST_LYCTX, &(diff_type_val));
- /* original type */
- diff_type_text = "hi";
- diff_type = ((struct lysc_node_leaf *) mod->compiled->data->next->next)->type;
- ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
- 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &diff_type_val, NULL, &err);
- assert_int_equal(LY_SUCCESS, ly_ret);
- assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[0])));
- assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
- type->free(UTEST_LYCTX, &(diff_type_val));
-
- /* different type (UINT8) */
- diff_type_text = "20";
- diff_type = ((struct lysc_node_leaf *) mod->compiled->data->next->next->next)->type;
- ly_ret = diff_type->plugin->store(UTEST_LYCTX, diff_type, diff_type_text, strlen(diff_type_text),
- 0, LY_VALUE_XML, NULL, LYD_VALHINT_DECNUM, NULL, &diff_type_val, NULL, &err);
- assert_int_equal(LY_SUCCESS, ly_ret);
- assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[0])));
- assert_int_equal(LY_ENOT, type->compare(&diff_type_val, &(values[1])));
- type->free(UTEST_LYCTX, &(diff_type_val));
-
/* delete values */
for (int unsigned it = 0; it < sizeof(val_init) / sizeof(val_init[0]); it++) {
type->free(UTEST_LYCTX, &(values[it]));
diff --git a/tests/utests/types/union.c b/tests/utests/types/union.c
index 9a0705a..d23cbf1 100644
--- a/tests/utests/types/union.c
+++ b/tests/utests/types/union.c
@@ -104,11 +104,34 @@ test_data_xml(void **state)
/* invalid value */
TEST_ERROR_XML2("",
"defs", "", "un1", "123456789012345678901", LY_EVALID);
- CHECK_LOG_CTX("Invalid union value \"123456789012345678901\" - no matching subtype found.",
+ CHECK_LOG_CTX("Invalid union value \"123456789012345678901\" - no matching subtype found:\n"
+ " libyang 2 - leafref, version 1: Invalid type int8 value \"123456789012345678901\".\n"
+ " libyang 2 - leafref, version 1: Invalid type int64 value \"123456789012345678901\".\n"
+ " libyang 2 - identityref, version 1: Invalid identityref \"123456789012345678901\" value - identity not found in module \"defs\".\n"
+ " libyang 2 - instance-identifier, version 1: Invalid instance-identifier \"123456789012345678901\" value - syntax error.\n"
+ " libyang 2 - string, version 1: Unsatisfied length - string \"123456789012345678901\" length is not allowed.\n",
"Schema location \"/defs:un1\", line number 1.");
}
static void
+test_data_json(void **state)
+{
+ const char *schema, *data;
+ struct lyd_node *tree;
+
+ /* xml test */
+ schema = MODULE_CREATE_YANG("defs", "leaf un21 {type union {type uint8; type string;}}"
+ "leaf un22 {type union {type uint16; type string;}}"
+ "leaf un2 {type union {type leafref {path /un21; require-instance false;} type leafref {path /un22; require-instance false;}}}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ data = "{\"defs:un2\":\"str\"}";
+ CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ CHECK_LYD_STRING_PARAM(tree, data, LYD_JSON, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS);
+ lyd_free_all(tree);
+}
+
+static void
test_plugin_lyb(void **state)
{
const char *schema;
@@ -124,12 +147,60 @@ test_plugin_lyb(void **state)
TEST_SUCCESS_LYB("lyb", "un1", "");
}
+static void
+test_validation(void **state)
+{
+ const char *schema, *data;
+ struct lyd_node *tree;
+ char *out;
+
+ schema = MODULE_CREATE_YANG("val",
+ "leaf l1 {\n"
+ " type union {\n"
+ " type uint32 {\n"
+ " range \"0..1048575\";\n"
+ " }\n"
+ " type enumeration {\n"
+ " enum auto;\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "leaf int8 {type int8 {range 10..20;}}\n"
+ "leaf l2 {\n"
+ " type union {\n"
+ " type leafref {path /int8; require-instance true;}\n"
+ " type string;\n"
+ " }\n"
+ "}\n");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ /* parse from LYB */
+ data = "<l1 xmlns=\"urn:tests:val\">auto</l1><int8 xmlns=\"urn:tests:val\">15</int8><l2 xmlns=\"urn:tests:val\">15</l2>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_LYB, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS));
+ lyd_free_all(tree);
+ CHECK_PARSE_LYD_PARAM(out, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ free(out);
+
+ /* validate */
+ assert_int_equal(LY_SUCCESS, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
+
+ /* print and compare */
+ assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS));
+ assert_string_equal(out, data);
+
+ free(out);
+ lyd_free_all(tree);
+}
+
int
main(void)
{
const struct CMUnitTest tests[] = {
UTEST(test_data_xml),
+ UTEST(test_data_json),
UTEST(test_plugin_lyb),
+ UTEST(test_validation),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/types/yang_types.c b/tests/utests/types/yang_types.c
index 6ce7671..0f78455 100644
--- a/tests/utests/types/yang_types.c
+++ b/tests/utests/types/yang_types.c
@@ -3,7 +3,7 @@
* @author Michal Vaško <mvasko@cesnet.cz>
* @brief test for ietf-yang-types values
*
- * Copyright (c) 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2021 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -52,11 +52,11 @@
lyd_free_all(tree); \
}
-#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA) \
+#define TEST_ERROR_XML(MOD_NAME, NODE_NAME, DATA, RET) \
{\
struct lyd_node *tree; \
const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
- CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); \
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, RET, tree); \
assert_null(tree); \
}
@@ -84,14 +84,15 @@ test_data_xml(void **state)
/* xml test */
schema = MODULE_CREATE_YANG("a",
"leaf l {type yang:date-and-time;}"
- "leaf l2 {type yang:xpath1.0;}");
+ "leaf l21 {type yang:hex-string;}"
+ "leaf l22 {type yang:uuid;}"
+ "leaf l3 {type yang:xpath1.0;}");
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
schema = MODULE_CREATE_YANG("b",
"");
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
/* date-and-time */
-#if defined (HAVE_TM_GMTOFF) && defined (HAVE_TIME_H_TIMEZONE)
TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T21:15:15.88888-02:00");
TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-06-01T06:14:15-02:00");
TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-06-01T20:15:15-02:00");
@@ -105,51 +106,44 @@ test_data_xml(void **state)
/* fractional hours */
TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888+04:30", STRING, "2005-05-25T16:45:15.88888-02:00");
-#else
- /* Tests run with a TZ offset of +02:00, but this platform cannot represent that in time_t,
- * so libyang always returns unspecified TZ. */
- TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T23:15:15.88888-00:00");
- TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-06-01T08:14:15-00:00");
- TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-06-01T22:15:15-00:00");
-
- /* test 1 second before epoch (mktime returns -1, but it is a correct value), with and without DST */
- TEST_SUCCESS_XML("a", "l", "1970-01-01T00:59:59-02:00", STRING, "1970-01-01T02:59:59-00:00");
- TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59-02:00", STRING, "1970-01-01T01:59:59-00:00");
-
- /* canonize */
- TEST_SUCCESS_XML("a", "l", "2005-02-29T23:15:15-02:00", STRING, "2005-03-02T01:15:15-00:00");
-
- /* fractional hours */
- TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888+04:30", STRING, "2005-05-25T18:45:15.88888-00:00");
-#endif
/* unknown timezone -- timezone conversion MUST NOT happen */
TEST_SUCCESS_XML("a", "l", "2017-02-01T00:00:00-00:00", STRING, "2017-02-01T00:00:00-00:00");
TEST_SUCCESS_XML("a", "l", "2021-02-29T00:00:00-00:00", STRING, "2021-03-01T00:00:00-00:00");
- TEST_ERROR_XML("a", "l", "2005-05-31T23:15:15.-08:00");
+ TEST_ERROR_XML("a", "l", "2005-05-31T23:15:15.-08:00", LY_EVALID);
CHECK_LOG_CTX("Unsatisfied pattern - \"2005-05-31T23:15:15.-08:00\" does not conform to "
"\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[\\+\\-]\\d{2}:\\d{2})\".",
"Schema location \"/a:l\", line number 1.");
+ TEST_ERROR_XML("a", "l", "2023-16-15T20:13:01+01:00", LY_EINVAL);
+ CHECK_LOG_CTX("Invalid date-and-time month \"15\".", "Schema location \"/a:l\", line number 1.");
+
+ TEST_ERROR_XML("a", "l", "2023-10-15T20:13:01+95:00", LY_EINVAL);
+ CHECK_LOG_CTX("Invalid date-and-time timezone hour \"95\".", "Schema location \"/a:l\", line number 1.");
+
+ /* hex-string */
+ TEST_SUCCESS_XML("a", "l21", "DB:BA:12:54:fa", STRING, "db:ba:12:54:fa");
+ TEST_SUCCESS_XML("a", "l22", "f81D4fAE-7dec-11d0-A765-00a0c91E6BF6", STRING, "f81d4fae-7dec-11d0-a765-00a0c91e6bf6");
+
/* xpath1.0 */
- TEST_SUCCESS_XML("a\" xmlns:aa=\"urn:tests:a", "l2", "/aa:l2[. = '4']", STRING, "/a:l2[.='4']");
+ TEST_SUCCESS_XML("a\" xmlns:aa=\"urn:tests:a", "l3", "/aa:l3[. = '4']", STRING, "/a:l3[.='4']");
TEST_SUCCESS_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
- "xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores", "l2",
+ "xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores", "l3",
"/yl:yang-library/yl:datastore/yl:name = 'ds:running'", STRING,
"/ietf-yang-library:yang-library/datastore/name='ietf-datastores:running'");
- TEST_SUCCESS_XML("a\" xmlns:a1=\"urn:tests:a\" xmlns:a2=\"urn:tests:a\" xmlns:bb=\"urn:tests:b", "l2",
+ TEST_SUCCESS_XML("a\" xmlns:a1=\"urn:tests:a\" xmlns:a2=\"urn:tests:a\" xmlns:bb=\"urn:tests:b", "l3",
"/a1:node1/a2:node2[a1:node3/bb:node4]/bb:node5 | bb:node6 and (bb:node7)", STRING,
"/a:node1/node2[node3/b:node4]/b:node5 | b:node6 and (b:node7)");
- TEST_SUCCESS_XML("a", "l2", "/l2[. = '4']", STRING, "/l2[.='4']");
-
- TEST_ERROR_XML("a", "l2", "/a:l2[. = '4']");
- CHECK_LOG_CTX("Failed to resolve prefix \"a\".", "Schema location \"/a:l2\", line number 1.");
- TEST_ERROR_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library", "l2",
- "/yl:yang-library/yl:datastore/yl::name");
- CHECK_LOG_CTX("Storing value failed.", "Schema location \"/a:l2\", line number 1.",
- "Invalid character 'y'[31] of expression '/yl:yang-library/yl:datastore/yl::name'.",
- "Schema location \"/a:l2\", line number 1.");
+ TEST_SUCCESS_XML("a", "l3", "/l3[. = '4']", STRING, "/l3[.='4']");
+
+ TEST_ERROR_XML("a", "l3", "/a:l3[. = '4']", LY_EVALID);
+ CHECK_LOG_CTX("Failed to resolve prefix \"a\".", "Schema location \"/a:l3\", line number 1.");
+ TEST_ERROR_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library", "l3",
+ "/yl:yang-library/yl:datastore/yl::name", LY_EVALID);
+ CHECK_LOG_CTX("Storing value failed.", "Schema location \"/a:l3\", line number 1.");
+ CHECK_LOG_CTX("Invalid character 'y'[31] of expression '/yl:yang-library/yl:datastore/yl::name'.",
+ "Schema location \"/a:l3\", line number 1.");
}
static void
@@ -191,6 +185,31 @@ test_print(void **state)
}
static void
+test_duplicate(void **state)
+{
+ const char *schema = MODULE_CREATE_YANG("a", "leaf l1 {type yang:date-and-time;} leaf l2 {type yang:xpath1.0;}");
+ const char *data, *expected;
+ struct lyd_node *tree, *dup;
+
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+ data = "<l1 xmlns=\"urn:tests:a\">2005-05-25T23:15:15.88888+04:30</l1>"
+ "<l2 xmlns=\"urn:tests:a\" xmlns:aa=\"urn:tests:a\">/aa:l2[. = '/aa:l2']</l2>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+
+ /* duplicate */
+ assert_int_equal(LY_SUCCESS, lyd_dup_siblings(tree, NULL, 0, &dup));
+
+ /* print */
+ expected = "<l1 xmlns=\"urn:tests:a\">2005-05-25T16:45:15.88888-02:00</l1>"
+ "<l2 xmlns=\"urn:tests:a\" xmlns:pref=\"urn:tests:a\">/pref:l2[.='/pref:l2']</l2>";
+ CHECK_LYD_STRING_PARAM(dup, expected, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS);
+
+ lyd_free_siblings(tree);
+ lyd_free_siblings(dup);
+}
+
+static void
test_lyb(void **state)
{
const char *schema;
@@ -216,6 +235,7 @@ main(void)
const struct CMUnitTest tests[] = {
UTEST(test_data_xml),
UTEST(test_print),
+ UTEST(test_duplicate),
UTEST(test_lyb),
};
diff --git a/tests/utests/utests.h b/tests/utests/utests.h
index 877d048..0e0649d 100644
--- a/tests/utests/utests.h
+++ b/tests/utests/utests.h
@@ -1010,7 +1010,9 @@ struct utest_context {
LY_ARRAY_COUNT_TYPE arr_size = sizeof(VALUE) / sizeof(VALUE[0]); \
assert_int_equal(arr_size, LY_ARRAY_COUNT((NODE).target)); \
for (LY_ARRAY_COUNT_TYPE it = 0; it < arr_size; it++) { \
- assert_int_equal(VALUE[it], (NODE).target[it].pred_type); \
+ if ((NODE).target[it].predicates) { \
+ assert_int_equal(VALUE[it], (NODE).target[it].predicates[0].type); \
+ } \
} \
}
@@ -1224,97 +1226,32 @@ struct utest_context {
_UC->in = NULL
/**
- * @brief Internal macro to compare error info record with the expected error message and path.
- * If NULL is provided as MSG, no error info record (NULL) is expected.
+ * @brief Check expected last error message.
*
- * @param[in] ERR Error information record from libyang context.
* @param[in] MSG Expected error message.
- * @param[in] PATH Expected error path.
- */
-#define _CHECK_LOG_CTX(ERR, MSG, PATH) \
- if (!MSG) { \
- assert_null(ERR); \
- } else { \
- assert_non_null(ERR); \
- CHECK_STRING((ERR)->msg, MSG); \
- CHECK_STRING((ERR)->path, PATH); \
- }
-
-/**`
- * @brief Internal macro to check the last libyang's context error.
- */
-#define _CHECK_LOG_CTX1(MSG, PATH) \
- _CHECK_LOG_CTX(ly_err_last(_UC->ctx), MSG, PATH)
-
-/**
- * @brief Internal macro to check the last two libyang's context error.
- */
-#define _CHECK_LOG_CTX2(MSG1, PATH1, MSG2, PATH2) \
- _CHECK_LOG_CTX(ly_err_last(_UC->ctx), MSG1, PATH1); \
- _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev, MSG2, PATH2)
-
-/**
- * @brief Internal macro to check the last three libyang's context error.
- */
-#define _CHECK_LOG_CTX3(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3) \
- _CHECK_LOG_CTX2(MSG1, PATH1, MSG2, PATH2); \
- _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev, MSG3, PATH3)
-
-/**
- * @brief Internal macro to check the last three libyang's context error.
- */
-#define _CHECK_LOG_CTX4(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4) \
- _CHECK_LOG_CTX3(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3); \
- _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev->prev, MSG4, PATH4)
-
-/**
- * @brief Internal macro to check the last three libyang's context error.
- */
-#define _CHECK_LOG_CTX5(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5) \
- _CHECK_LOG_CTX4(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4); \
- _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev->prev->prev, MSG5, PATH5)
-
-/**
- * @brief Internal macro to check the last three libyang's context error.
- */
-#define _CHECK_LOG_CTX6(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5, MSG6, PATH6) \
- _CHECK_LOG_CTX5(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5); \
- _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev->prev->prev->prev, MSG6, PATH6)
-
-/**
- * @brief Internal macro to check the last three libyang's context error.
- */
-#define _CHECK_LOG_CTX7(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5, MSG6, PATH6, MSG7, PATH7) \
- _CHECK_LOG_CTX6(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5, MSG6, PATH6); \
- _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev->prev->prev->prev->prev, MSG7, PATH7)
-
-/**
- * @brief Internal macro to check the last three libyang's context error.
*/
-#define _CHECK_LOG_CTX8(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5, MSG6, PATH6, MSG7, PATH7, MSG8, PATH8) \
- _CHECK_LOG_CTX7(MSG1, PATH1, MSG2, PATH2, MSG3, PATH3, MSG4, PATH4, MSG5, PATH5, MSG6, PATH6, MSG7, PATH7); \
- _CHECK_LOG_CTX(ly_err_last(_UC->ctx)->prev->prev->prev->prev->prev->prev->prev, MSG8, PATH8)
+#define CHECK_LOG_LASTMSG(MSG) \
+ CHECK_STRING(ly_last_errmsg(), MSG)
/**
- * @brief Internal helper macro to select _CHECK_LOG_CTX* macro according to the provided parameters.
- */
-#define _GET_CHECK_LOG_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, NAME, ...) NAME
-
-/**
- * @brief Check expected error(s) in libyang context.
- *
- * Macro has variadic parameters expected to be provided in pairs of error message and error path starting
- * from the latest error. Current limit is checking at most 3 last errors. After checking, macro cleans up
- * all the errors from the libyang context.
+ * @brief Check expected last error in libyang context, which is then cleared. Can be called repeatedly to check
+ * several errors. If NULL is provided as MSG, no error info record (NULL) is expected.
*
* @param[in] MSG Expected error message.
* @param[in] PATH Expected error path.
*/
-#define CHECK_LOG_CTX(...) \
- _GET_CHECK_LOG_MACRO(__VA_ARGS__, _CHECK_LOG_CTX8, _INVAL, _CHECK_LOG_CTX7, _INVAL, \
- _CHECK_LOG_CTX6, _INVAL, _CHECK_LOG_CTX5, _INVAL, _CHECK_LOG_CTX4, _INVAL, \
- _CHECK_LOG_CTX3, _INVAL, _CHECK_LOG_CTX2, _INVAL, _CHECK_LOG_CTX1, DUMMY)(__VA_ARGS__); \
- ly_err_clean(_UC->ctx, NULL)
+#define CHECK_LOG_CTX(MSG, PATH) \
+ { \
+ struct ly_err_item *_e = ly_err_last(_UC->ctx); \
+ if (!MSG) { \
+ assert_null(_e); \
+ } else { \
+ assert_non_null(_e); \
+ CHECK_STRING(_e->msg, MSG); \
+ CHECK_STRING(_e->path, PATH); \
+ } \
+ ly_err_clean(_UC->ctx, _e); \
+ }
/**
* @brief Check expected error in libyang context including error-app-tag.
@@ -1324,14 +1261,23 @@ struct utest_context {
* @param[in] APPTAG Expected error-app-tag.
*/
#define CHECK_LOG_CTX_APPTAG(MSG, PATH, APPTAG) \
- if (!MSG) { \
- assert_null(ly_err_last(_UC->ctx)); \
- } else { \
- assert_non_null(ly_err_last(_UC->ctx)); \
- CHECK_STRING(ly_err_last(_UC->ctx)->msg, MSG); \
- CHECK_STRING(ly_err_last(_UC->ctx)->path, PATH); \
- CHECK_STRING(ly_err_last(_UC->ctx)->apptag, APPTAG); \
- } \
+ { \
+ struct ly_err_item *_e = ly_err_last(_UC->ctx); \
+ if (!MSG) { \
+ assert_null(_e); \
+ } else { \
+ assert_non_null(_e); \
+ CHECK_STRING(_e->msg, MSG); \
+ CHECK_STRING(_e->path, PATH); \
+ CHECK_STRING(_e->apptag, APPTAG); \
+ } \
+ ly_err_clean(_UC->ctx, _e); \
+ }
+
+/**
+ * @brief Clear all errors stored in the libyang context.
+ */
+#define UTEST_LOG_CTX_CLEAN \
ly_err_clean(_UC->ctx, NULL)
/**
@@ -1435,7 +1381,8 @@ utest_teardown(void **state)
{
*state = NULL;
- /* libyang context */
+ /* libyang context, no leftover messages */
+ assert_null(ly_err_last(current_utest_context->ctx));
ly_ctx_destroy(current_utest_context->ctx);
if (current_utest_context->orig_tz) {
diff --git a/tests/yanglint/CMakeLists.txt b/tests/yanglint/CMakeLists.txt
new file mode 100644
index 0000000..c1e081a
--- /dev/null
+++ b/tests/yanglint/CMakeLists.txt
@@ -0,0 +1,36 @@
+if(WIN32)
+ set(YANGLINT_INTERACTIVE OFF)
+else()
+ set(YANGLINT_INTERACTIVE ON)
+endif()
+
+function(add_yanglint_test)
+ cmake_parse_arguments(ADDTEST "" "NAME;VIA;SCRIPT" "" ${ARGN})
+ set(TEST_NAME yanglint_${ADDTEST_NAME})
+
+ if(${ADDTEST_VIA} STREQUAL "tclsh")
+ set(WRAPPER ${PATH_TCLSH})
+ else()
+ message(FATAL_ERROR "build: unexpected wrapper '${ADDTEST_VIA}'")
+ endif()
+
+ add_test(NAME ${TEST_NAME} COMMAND ${WRAPPER} ${CMAKE_CURRENT_SOURCE_DIR}/${ADDTEST_SCRIPT})
+ set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "TESTS_DIR=${CMAKE_CURRENT_SOURCE_DIR}")
+ set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "YANG_MODULES_DIR=${CMAKE_CURRENT_SOURCE_DIR}/modules")
+ set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "YANGLINT=${PROJECT_BINARY_DIR}")
+endfunction(add_yanglint_test)
+
+if(ENABLE_TESTS)
+ # tests of interactive mode using tclsh
+ find_program(PATH_TCLSH NAMES tclsh)
+ if(NOT PATH_TCLSH)
+ message(WARNING "'tclsh' not found! The yanglint(1) interactive tests will not be available.")
+ else()
+ if(YANGLINT_INTERACTIVE)
+ add_yanglint_test(NAME interactive VIA tclsh SCRIPT interactive/all.tcl)
+ add_yanglint_test(NAME non-interactive VIA tclsh SCRIPT non-interactive/all.tcl)
+ else()
+ add_yanglint_test(NAME non-interactive VIA tclsh SCRIPT non-interactive/all.tcl)
+ endif()
+ endif()
+endif()
diff --git a/tests/yanglint/README.md b/tests/yanglint/README.md
new file mode 100644
index 0000000..6c51d89
--- /dev/null
+++ b/tests/yanglint/README.md
@@ -0,0 +1,107 @@
+# yanglint testing
+
+Testing yanglint is divided into two ways.
+It is either tested in interactive mode using the tcl command 'expect' or non-interactively, classically from the command line.
+For both modes, unit testing was used using the tcl package tcltest.
+
+## How to
+
+The sample commands in this chapter using `tclsh` are called in the `interactive` or `non-interactive` directories.
+
+### How to run all yanglint tests?
+
+In the build directory designated for cmake, enter:
+
+```
+ctest -R yanglint
+```
+
+### How to run all yanglint tests that are in interactive mode?
+
+In the interactive directory, run:
+
+```
+tclsh all.tcl
+```
+
+### How to run all yanglint tests that are in non-interactive mode?
+
+In the non-interactive directory, run:
+
+```
+tclsh all.tcl
+```
+
+### How to run all unit-tests from .test file?
+
+```
+tclsh clear.test
+```
+
+or alternatively:
+
+```
+tclsh all.tcl -file clear.test
+```
+
+### How to run one unit-test?
+
+```
+tclsh clear.test -match clear_ietf_yang_library
+```
+
+or alternatively:
+
+```
+tclsh all.tcl -file clear.test -match clear_ietf_yang_library
+```
+
+### How to run unit-tests for a certain yanglint command?
+
+Test names are assumed to consist of the command name:
+
+```
+tclsh all.tcl -match clear*
+```
+
+### How do I get more detailed information about 'expect' for a certain test?
+
+In the interactive directory, run:
+
+```
+tclsh clear.test -match clear_ietf_yang_library -load "exp_internal 1"
+```
+
+### How do I get more detailed dialog between 'expect' and yanglint for a certain test?
+
+In the interactive directory, run:
+
+```
+tclsh clear.test -match clear_ietf_yang_library -load "log_user 1"
+```
+
+### How do I suppress error message from tcltest?
+
+Probably only possible to do via `-verbose ""`
+
+### How can I also debug?
+
+You can write commands `interact` and `interpreter` from 'Expect' package into some test.
+However, the most useful are the `exp_internal` and `log_user`, which can also be written directly into the test.
+See also the rlwrap tool.
+You can also use other debugging methods used in tcl programming.
+
+### Are the tests between interactive mode and non-interactive mode similar?
+
+Sort of...
+- regex \n must be changed to \r\n in the tests for interactive yanglint
+
+### I would like to add a new "ly_" function.
+
+Add it to the ly.tcl file.
+If you need to call other subfunctions in it, add them to namespace ly::private.
+
+### I would like to use function other than those prefixed with "ly_".
+
+Look in the common.tcl file in the "uti" namespace,
+which contains general tcl functions that can be used in both interactive and non-interactive tests.
diff --git a/tests/yanglint/common.tcl b/tests/yanglint/common.tcl
new file mode 100644
index 0000000..d186282
--- /dev/null
+++ b/tests/yanglint/common.tcl
@@ -0,0 +1,114 @@
+# @brief Common functions and variables for yanglint-interactive and yanglint-non-interactive.
+#
+# The script requires variables:
+# ::env(TESTS_DIR) - Main test directory. Must be set if the script is run via ctest.
+#
+# The script sets the variables:
+# ::env(TESTS_DIR) - Main test directory. It is set by default if not defined.
+# ::env(YANG_MODULES_DIR) - Directory of YANG modules.
+# TUT_PATH - Assumed absolute path to the directory in which the TUT is located.
+# TUT_NAME - TUT name (without path).
+# ::tcltest::testConstraint ctest - A tcltest variable that is set to true if the script is run via ctest. Causes tests
+# to be a skipped.
+
+package require tcltest
+namespace import ::tcltest::test ::tcltest::cleanupTests
+
+# Set directory paths for testing yanglint.
+if { ![info exists ::env(TESTS_DIR)] } {
+ # the script is not run via 'ctest' so paths must be set
+ set ::env(TESTS_DIR) "../"
+ set ::env(YANG_MODULES_DIR) "../modules"
+ set TUT_PATH "../../../build"
+ ::tcltest::testConstraint ctest false
+} else {
+ # cmake (ctest) already sets ::env variables
+ set TUT_PATH $::env(YANGLINT)
+ ::tcltest::testConstraint ctest true
+}
+
+set TUT_NAME "yanglint"
+
+# The script continues by defining functions specific to the yanglint tool.
+
+namespace eval uti {
+ namespace export *
+}
+
+# Iterate through the items in the list 'lst' and return a new list where
+# the items will have the form: <prefix><item><suffix>.
+# Parameter 'index' determines at which index it will start wrapping.
+# Parameter 'step' specifies how far the iterator must move to wrap the next item.
+proc uti::wrap_list_items {lst {prefix ""} {suffix ""} {index 0} {step 1}} {
+ # counter to track when to insert wrapper
+ set cnt $step
+ set len [llength $lst]
+
+ if {$index > 0} {
+ # copy list from interval <0;$index)
+ set ret [lrange $lst 0 [expr {$index - 1}]]
+ } else {
+ set ret {}
+ }
+
+ for {set i $index} {$i < $len} {incr i} {
+ incr cnt
+ set item [lindex $lst $i]
+ if {$cnt >= $step} {
+ # insert wrapper for item
+ set cnt 0
+ lappend ret [string cat $prefix $item $suffix]
+ } else {
+ # just copy item
+ lappend ret $item
+ }
+ }
+
+ return $ret
+}
+
+# Wrap list items with xml tags.
+# The element format is: <tag>value</tag>
+# Parameter 'values' is list of values.
+# Parameter 'tag' is the name of the searched tag.
+proc uti::wrap_to_xml {values tag {index 0} {step 1}} {
+ return [wrap_list_items $values "<$tag>" "</$tag>" $index $step]
+}
+
+# Wrap list items with json attributes.
+# The pair format is: "attribute": "value"
+# Parameter 'values' is list of values.
+# Parameter 'attribute' is the name of the searched attribute.
+proc uti::wrap_to_json {values attribute {index 0} {step 1}} {
+ return [wrap_list_items $values "\"$attribute\": \"" "\"" $index $step]
+}
+
+# Convert list to a regex (which is just a string) so that 'delim' is between items,
+# 'begin' is at the beginning of the expression and 'end' is at the end.
+proc uti::list_to_regex {lst {delim ".*"} {begin ".*"} {end ".*"}} {
+ return [string cat $begin [join $lst $delim] $end]
+}
+
+# Merge two lists into one such that the nth items are merged into one separated by a delimiter.
+# Returns a list that is the same length as 'lst1' and 'lst2'
+proc uti::blend_lists {lst1 lst2 {delim ".*"}} {
+ return [lmap a $lst1 b $lst2 {string cat $a $delim $b}]
+}
+
+# Create regex to find xml elements.
+# The element format is: <tag>value</tag>
+# Parameter 'values' is list of values.
+# Parameter 'tag' is the name of the searched tag.
+# The resulting expression looks like: ".*<tag>value1</tag>.*<tag>value2</tag>.*..."
+proc uti::regex_xml_elements {values tag} {
+ return [list_to_regex [wrap_to_xml $values $tag]]
+}
+
+# Create regex to find json pairs.
+# The pair format is: "attribute": "value"
+# Parameter 'values' is list of values.
+# Parameter 'attribute' is the name of the searched attribute.
+# The resulting expression looks like: ".*\"attribute\": \"value1\".*\"attribute\": \"value2\".*..."
+proc uti::regex_json_pairs {values attribute} {
+ return [list_to_regex [wrap_to_json $values $attribute]]
+}
diff --git a/tests/yanglint/data/modaction.xml b/tests/yanglint/data/modaction.xml
new file mode 100644
index 0000000..37faa2d
--- /dev/null
+++ b/tests/yanglint/data/modaction.xml
@@ -0,0 +1,8 @@
+<con xmlns="urn:yanglint:modaction">
+ <ls>
+ <lfkey>kv</lfkey>
+ <act>
+ <lfi>some_input</lfi>
+ </act>
+ </ls>
+</con>
diff --git a/tests/yanglint/data/modaction_ds.xml b/tests/yanglint/data/modaction_ds.xml
new file mode 100644
index 0000000..a5a1727
--- /dev/null
+++ b/tests/yanglint/data/modaction_ds.xml
@@ -0,0 +1,5 @@
+<con xmlns="urn:yanglint:modaction">
+ <ls>
+ <lfkey>kv</lfkey>
+ </ls>
+</con>
diff --git a/tests/yanglint/data/modaction_nc.xml b/tests/yanglint/data/modaction_nc.xml
new file mode 100644
index 0000000..a74b6bf
--- /dev/null
+++ b/tests/yanglint/data/modaction_nc.xml
@@ -0,0 +1,13 @@
+<rpc message-id="101"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <action xmlns="urn:ietf:params:xml:ns:yang:1">
+ <con xmlns="urn:yanglint:modaction">
+ <ls>
+ <lfkey>kv</lfkey>
+ <act>
+ <lfi>some_input</lfi>
+ </act>
+ </ls>
+ </con>
+ </action>
+</rpc>
diff --git a/tests/yanglint/data/modaction_reply.xml b/tests/yanglint/data/modaction_reply.xml
new file mode 100644
index 0000000..7d6532d
--- /dev/null
+++ b/tests/yanglint/data/modaction_reply.xml
@@ -0,0 +1,8 @@
+<con xmlns="urn:yanglint:modaction">
+ <ls>
+ <lfkey>kv</lfkey>
+ <act>
+ <lfo>-56</lfo>
+ </act>
+ </ls>
+</con>
diff --git a/tests/yanglint/data/modaction_reply_nc.xml b/tests/yanglint/data/modaction_reply_nc.xml
new file mode 100644
index 0000000..f7c3b8f
--- /dev/null
+++ b/tests/yanglint/data/modaction_reply_nc.xml
@@ -0,0 +1,4 @@
+<rpc-reply message-id="101"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <lfo xmlns="urn:yanglint:modaction">-56</lfo>
+</rpc-reply>
diff --git a/tests/yanglint/data/modconfig.xml b/tests/yanglint/data/modconfig.xml
new file mode 100644
index 0000000..f8a03a9
--- /dev/null
+++ b/tests/yanglint/data/modconfig.xml
@@ -0,0 +1,4 @@
+<mcc xmlns="urn:yanglint:modconfig">
+ <lft>rw</lft>
+ <lff>ro</lff>
+</mcc>
diff --git a/tests/yanglint/data/modconfig2.xml b/tests/yanglint/data/modconfig2.xml
new file mode 100644
index 0000000..c96e344
--- /dev/null
+++ b/tests/yanglint/data/modconfig2.xml
@@ -0,0 +1,3 @@
+<mcc xmlns="urn:yanglint:modconfig">
+ <lft>rw</lft>
+</mcc>
diff --git a/tests/yanglint/data/modconfig_ctx.xml b/tests/yanglint/data/modconfig_ctx.xml
new file mode 100644
index 0000000..124989c
--- /dev/null
+++ b/tests/yanglint/data/modconfig_ctx.xml
@@ -0,0 +1,13 @@
+<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+ <module-set>
+ <name>main-set</name>
+ <module>
+ <name>modconfig</name>
+ <namespace>urn:yanglint:modconfig</namespace>
+ </module>
+ </module-set>
+ <content-id>1</content-id>
+</yang-library>
+<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+ <module-set-id>1</module-set-id>
+</modules-state>
diff --git a/tests/yanglint/data/moddatanodes.xml b/tests/yanglint/data/moddatanodes.xml
new file mode 100644
index 0000000..8ae6e97
--- /dev/null
+++ b/tests/yanglint/data/moddatanodes.xml
@@ -0,0 +1,17 @@
+<dnc xmlns="urn:yanglint:moddatanodes">
+ <lf>x</lf>
+ <lfl>1</lfl>
+ <lfl>2</lfl>
+ <con>
+ <lt>
+ <kalf>ka1</kalf>
+ <kblf>kb1</kblf>
+ <vlf>v1</vlf>
+ </lt>
+ <lt>
+ <kalf>ka2</kalf>
+ <kblf>kb2</kblf>
+ <vlf>v2</vlf>
+ </lt>
+ </con>
+</dnc>
diff --git a/tests/yanglint/data/moddefault.xml b/tests/yanglint/data/moddefault.xml
new file mode 100644
index 0000000..00f3a9d
--- /dev/null
+++ b/tests/yanglint/data/moddefault.xml
@@ -0,0 +1,4 @@
+<mdc xmlns="urn:yanglint:moddefault">
+ <lf>0</lf>
+ <di>5</di>
+</mdc>
diff --git a/tests/yanglint/data/modimp_type_ctx.xml b/tests/yanglint/data/modimp_type_ctx.xml
new file mode 100644
index 0000000..e6d158a
--- /dev/null
+++ b/tests/yanglint/data/modimp_type_ctx.xml
@@ -0,0 +1,13 @@
+<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+ <module-set>
+ <name>main-set</name>
+ <module>
+ <name>modimp-type</name>
+ <namespace>urn:yanglint:modimp-type</namespace>
+ </module>
+ </module-set>
+ <content-id>1</content-id>
+</yang-library>
+<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+ <module-set-id>1</module-set-id>
+</modules-state>
diff --git a/tests/yanglint/data/modleaf.djson b/tests/yanglint/data/modleaf.djson
new file mode 100644
index 0000000..25af218
--- /dev/null
+++ b/tests/yanglint/data/modleaf.djson
@@ -0,0 +1,3 @@
+{
+ "modleaf:lfl": 7
+}
diff --git a/tests/yanglint/data/modleaf.dxml b/tests/yanglint/data/modleaf.dxml
new file mode 100644
index 0000000..408936a
--- /dev/null
+++ b/tests/yanglint/data/modleaf.dxml
@@ -0,0 +1 @@
+<lfl xmlns="urn:yanglint:modleaf">7</lfl>
diff --git a/tests/yanglint/data/modleaf.xml b/tests/yanglint/data/modleaf.xml
new file mode 100644
index 0000000..408936a
--- /dev/null
+++ b/tests/yanglint/data/modleaf.xml
@@ -0,0 +1 @@
+<lfl xmlns="urn:yanglint:modleaf">7</lfl>
diff --git a/tests/yanglint/data/modleafref.xml b/tests/yanglint/data/modleafref.xml
new file mode 100644
index 0000000..c9fb147
--- /dev/null
+++ b/tests/yanglint/data/modleafref.xml
@@ -0,0 +1,2 @@
+<lfl xmlns="urn:yanglint:modleaf">7</lfl>
+<lfr xmlns="urn:yanglint:modleafref">7</lfr>
diff --git a/tests/yanglint/data/modleafref2.xml b/tests/yanglint/data/modleafref2.xml
new file mode 100644
index 0000000..3946daf
--- /dev/null
+++ b/tests/yanglint/data/modleafref2.xml
@@ -0,0 +1,2 @@
+<lfl xmlns="urn:yanglint:modleaf">7</lfl>
+<lfr xmlns="urn:yanglint:modleafref">10</lfr>
diff --git a/tests/yanglint/data/modmandatory.xml b/tests/yanglint/data/modmandatory.xml
new file mode 100644
index 0000000..108cb2a
--- /dev/null
+++ b/tests/yanglint/data/modmandatory.xml
@@ -0,0 +1,3 @@
+<mmc xmlns="urn:yanglint:modmandatory">
+ <lft>9</lft>
+</mmc>
diff --git a/tests/yanglint/data/modmandatory_invalid.xml b/tests/yanglint/data/modmandatory_invalid.xml
new file mode 100644
index 0000000..de71895
--- /dev/null
+++ b/tests/yanglint/data/modmandatory_invalid.xml
@@ -0,0 +1,3 @@
+<mmc xmlns="urn:yanglint:modmandatory">
+ <lff>9</lff>
+</mmc>
diff --git a/tests/yanglint/data/modmerge.xml b/tests/yanglint/data/modmerge.xml
new file mode 100644
index 0000000..b52eff5
--- /dev/null
+++ b/tests/yanglint/data/modmerge.xml
@@ -0,0 +1,4 @@
+<mmc xmlns="urn:yanglint:modmerge">
+ <en>one</en>
+ <lm>4</lm>
+</mmc>
diff --git a/tests/yanglint/data/modmerge2.xml b/tests/yanglint/data/modmerge2.xml
new file mode 100644
index 0000000..e7f17c4
--- /dev/null
+++ b/tests/yanglint/data/modmerge2.xml
@@ -0,0 +1,3 @@
+<mmc xmlns="urn:yanglint:modmerge">
+ <en>zero</en>
+</mmc>
diff --git a/tests/yanglint/data/modmerge3.xml b/tests/yanglint/data/modmerge3.xml
new file mode 100644
index 0000000..6ef857e
--- /dev/null
+++ b/tests/yanglint/data/modmerge3.xml
@@ -0,0 +1,3 @@
+<mmc xmlns="urn:yanglint:modmerge">
+ <lf>str</lf>
+</mmc>
diff --git a/tests/yanglint/data/modnotif.xml b/tests/yanglint/data/modnotif.xml
new file mode 100644
index 0000000..81cab21
--- /dev/null
+++ b/tests/yanglint/data/modnotif.xml
@@ -0,0 +1,5 @@
+<con xmlns="urn:yanglint:modnotif">
+ <nfn>
+ <lf>nested</lf>
+ </nfn>
+</con>
diff --git a/tests/yanglint/data/modnotif2.xml b/tests/yanglint/data/modnotif2.xml
new file mode 100644
index 0000000..fc75b57
--- /dev/null
+++ b/tests/yanglint/data/modnotif2.xml
@@ -0,0 +1,3 @@
+<nfg xmlns="urn:yanglint:modnotif">
+ <lf>top</lf>
+</nfg>
diff --git a/tests/yanglint/data/modnotif2_nc.xml b/tests/yanglint/data/modnotif2_nc.xml
new file mode 100644
index 0000000..c87cfa0
--- /dev/null
+++ b/tests/yanglint/data/modnotif2_nc.xml
@@ -0,0 +1,6 @@
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+ <eventTime>2010-12-06T08:00:01Z</eventTime>
+ <nfg xmlns="urn:yanglint:modnotif">
+ <lf>top</lf>
+ </nfg>
+</notification>
diff --git a/tests/yanglint/data/modnotif_ds.xml b/tests/yanglint/data/modnotif_ds.xml
new file mode 100644
index 0000000..efd835b
--- /dev/null
+++ b/tests/yanglint/data/modnotif_ds.xml
@@ -0,0 +1 @@
+<con xmlns="urn:yanglint:modnotif"></con>
diff --git a/tests/yanglint/data/modnotif_nc.xml b/tests/yanglint/data/modnotif_nc.xml
new file mode 100644
index 0000000..39a3440
--- /dev/null
+++ b/tests/yanglint/data/modnotif_nc.xml
@@ -0,0 +1,8 @@
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+ <eventTime>2010-12-06T08:00:01Z</eventTime>
+ <con xmlns="urn:yanglint:modnotif">
+ <nfn>
+ <lf>nested</lf>
+ </nfn>
+ </con>
+</notification>
diff --git a/tests/yanglint/data/modoper_leafref_action.xml b/tests/yanglint/data/modoper_leafref_action.xml
new file mode 100644
index 0000000..7ccf29f
--- /dev/null
+++ b/tests/yanglint/data/modoper_leafref_action.xml
@@ -0,0 +1,8 @@
+<cond xmlns="urn:yanglint:modoper-leafref">
+ <list>
+ <klf>key_val</klf>
+ <act>
+ <lfi>rw</lfi>
+ </act>
+ </list>
+</cond>
diff --git a/tests/yanglint/data/modoper_leafref_action_reply.xml b/tests/yanglint/data/modoper_leafref_action_reply.xml
new file mode 100644
index 0000000..39ec672
--- /dev/null
+++ b/tests/yanglint/data/modoper_leafref_action_reply.xml
@@ -0,0 +1,8 @@
+<cond xmlns="urn:yanglint:modoper-leafref">
+ <list>
+ <klf>key_val</klf>
+ <act>
+ <lfo>rw</lfo>
+ </act>
+ </list>
+</cond>
diff --git a/tests/yanglint/data/modoper_leafref_ds.xml b/tests/yanglint/data/modoper_leafref_ds.xml
new file mode 100644
index 0000000..f934b9b
--- /dev/null
+++ b/tests/yanglint/data/modoper_leafref_ds.xml
@@ -0,0 +1,9 @@
+<mcc xmlns="urn:yanglint:modconfig">
+ <lft>rw</lft>
+ <lff>ro</lff>
+</mcc>
+<cond xmlns="urn:yanglint:modoper-leafref">
+ <list>
+ <klf>key_val</klf>
+ </list>
+</cond>
diff --git a/tests/yanglint/data/modoper_leafref_notif.xml b/tests/yanglint/data/modoper_leafref_notif.xml
new file mode 100644
index 0000000..2c56b67
--- /dev/null
+++ b/tests/yanglint/data/modoper_leafref_notif.xml
@@ -0,0 +1,3 @@
+<notifg xmlns="urn:yanglint:modoper-leafref">
+ <lfr>rw</lfr>
+</notifg>
diff --git a/tests/yanglint/data/modoper_leafref_notif2.xml b/tests/yanglint/data/modoper_leafref_notif2.xml
new file mode 100644
index 0000000..466697c
--- /dev/null
+++ b/tests/yanglint/data/modoper_leafref_notif2.xml
@@ -0,0 +1,8 @@
+<cond xmlns="urn:yanglint:modoper-leafref">
+ <list>
+ <klf>key_val</klf>
+ <notif>
+ <lfn>rw</lfn>
+ </notif>
+ </list>
+</cond>
diff --git a/tests/yanglint/data/modoper_leafref_notif_err.xml b/tests/yanglint/data/modoper_leafref_notif_err.xml
new file mode 100644
index 0000000..1622ded
--- /dev/null
+++ b/tests/yanglint/data/modoper_leafref_notif_err.xml
@@ -0,0 +1,7 @@
+<mcc xmlns="urn:yanglint:modconfig">
+ <lft>rw</lft>
+ <lff>ro</lff>
+</mcc>
+<notifg xmlns="urn:yanglint:modoper-leafref">
+ <lf>rw</lf>
+</notifg>
diff --git a/tests/yanglint/data/modoper_leafref_rpc.xml b/tests/yanglint/data/modoper_leafref_rpc.xml
new file mode 100644
index 0000000..b294544
--- /dev/null
+++ b/tests/yanglint/data/modoper_leafref_rpc.xml
@@ -0,0 +1,3 @@
+<rpcg xmlns="urn:yanglint:modoper-leafref">
+ <lfi>rw</lfi>
+</rpcg>
diff --git a/tests/yanglint/data/modoper_leafref_rpc_reply.xml b/tests/yanglint/data/modoper_leafref_rpc_reply.xml
new file mode 100644
index 0000000..e8f7af3
--- /dev/null
+++ b/tests/yanglint/data/modoper_leafref_rpc_reply.xml
@@ -0,0 +1,5 @@
+<rpcg xmlns="urn:yanglint:modoper-leafref">
+ <cono>
+ <lfo>rw</lfo>
+ </cono>
+</rpcg>
diff --git a/tests/yanglint/data/modrpc.xml b/tests/yanglint/data/modrpc.xml
new file mode 100644
index 0000000..a4f924d
--- /dev/null
+++ b/tests/yanglint/data/modrpc.xml
@@ -0,0 +1,3 @@
+<rpc xmlns="urn:yanglint:modrpc">
+ <lfi>some_input</lfi>
+</rpc>
diff --git a/tests/yanglint/data/modrpc_nc.xml b/tests/yanglint/data/modrpc_nc.xml
new file mode 100644
index 0000000..78d3149
--- /dev/null
+++ b/tests/yanglint/data/modrpc_nc.xml
@@ -0,0 +1,6 @@
+<rpc message-id="101"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <rpc xmlns="urn:yanglint:modrpc">
+ <lfi>some_input</lfi>
+ </rpc>
+</rpc>
diff --git a/tests/yanglint/data/modrpc_reply.xml b/tests/yanglint/data/modrpc_reply.xml
new file mode 100644
index 0000000..632971c
--- /dev/null
+++ b/tests/yanglint/data/modrpc_reply.xml
@@ -0,0 +1,5 @@
+<rpc xmlns="urn:yanglint:modrpc">
+ <con>
+ <lfo>-56</lfo>
+ </con>
+</rpc>
diff --git a/tests/yanglint/data/modrpc_reply_nc.xml b/tests/yanglint/data/modrpc_reply_nc.xml
new file mode 100644
index 0000000..da2a01c
--- /dev/null
+++ b/tests/yanglint/data/modrpc_reply_nc.xml
@@ -0,0 +1,6 @@
+<rpc-reply message-id="101"
+ xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+ <con xmlns="urn:yanglint:modrpc">
+ <lfo>-56</lfo>
+ </con>
+</rpc-reply>
diff --git a/tests/yanglint/data/modsm.xml b/tests/yanglint/data/modsm.xml
new file mode 100644
index 0000000..bb0793c
--- /dev/null
+++ b/tests/yanglint/data/modsm.xml
@@ -0,0 +1,3 @@
+<root xmlns="urn:yanglint:modsm">
+ <lfl xmlns="urn:yanglint:modleaf">7</lfl>
+</root>
diff --git a/tests/yanglint/data/modsm2.xml b/tests/yanglint/data/modsm2.xml
new file mode 100644
index 0000000..ff6f103
--- /dev/null
+++ b/tests/yanglint/data/modsm2.xml
@@ -0,0 +1,4 @@
+<root xmlns="urn:yanglint:modsm">
+ <lfl xmlns="urn:yanglint:modleaf">7</lfl>
+ <alf xmlns="urn:yanglint:modsm-augment">str</alf>
+</root>
diff --git a/tests/yanglint/data/modsm_ctx_ext.xml b/tests/yanglint/data/modsm_ctx_ext.xml
new file mode 100644
index 0000000..e80141a
--- /dev/null
+++ b/tests/yanglint/data/modsm_ctx_ext.xml
@@ -0,0 +1,20 @@
+<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+ <module-set>
+ <name>test-set</name>
+ <module>
+ <name>modleaf</name>
+ <namespace>urn:yanglint:modleaf</namespace>
+ </module>
+ </module-set>
+ <content-id>1</content-id>
+</yang-library>
+<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+ <module-set-id>1</module-set-id>
+</modules-state>
+<schema-mounts xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount">
+ <mount-point>
+ <module>modsm</module>
+ <label>root</label>
+ <inline></inline>
+ </mount-point>
+</schema-mounts>
diff --git a/tests/yanglint/data/modsm_ctx_main.xml b/tests/yanglint/data/modsm_ctx_main.xml
new file mode 100644
index 0000000..5405d4d
--- /dev/null
+++ b/tests/yanglint/data/modsm_ctx_main.xml
@@ -0,0 +1,17 @@
+<yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+ <module-set>
+ <name>main-set</name>
+ <module>
+ <name>modsm</name>
+ <namespace>urn:yanglint:modsm</namespace>
+ </module>
+ <module>
+ <name>modsm-augment</name>
+ <namespace>urn:yanglint:modsm-augment</namespace>
+ </module>
+ </module-set>
+ <content-id>1</content-id>
+</yang-library>
+<modules-state xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library">
+ <module-set-id>1</module-set-id>
+</modules-state>
diff --git a/tests/yanglint/interactive/add.test b/tests/yanglint/interactive/add.test
new file mode 100644
index 0000000..d1cacc1
--- /dev/null
+++ b/tests/yanglint/interactive/add.test
@@ -0,0 +1,59 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+
+test add_basic {} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "add $mdir/modleafref.yang"
+ ly_cmd "list" "I modleafref\r.*I modleaf"
+}}
+
+test add_disable_searchdir_once {add --disable-searchdir} {
+-setup $ly_setup -cleanup $ly_cleanup -constraints {!ctest} -body {
+ ly_cmd "add $mdir/modimp-cwd.yang"
+ ly_cmd "clear"
+ ly_cmd_err "add -D $mdir/modimp-cwd.yang" "not found in local searchdirs"
+}}
+
+test add_disable_searchdir_twice {add -D -D} {
+-setup $ly_setup -cleanup $ly_cleanup -constraints {!ctest} -body {
+ ly_cmd "add $mdir/ietf-ip.yang"
+ ly_cmd "clear"
+ ly_cmd_err "add -D -D $mdir/ietf-ip.yang" "Loading \"ietf-interfaces\" module failed."
+}}
+
+test add_with_feature {Add module with feature} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "add --feature modfeature:ftr2 $mdir/modfeature.yang"
+ ly_cmd "feature -a" "modfeature:\r\n\tftr1 \\(off\\)\r\n\tftr2 \\(on\\)"
+}}
+
+test add_make_implemented_once {add --make-implemented} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_ignore "add $mdir/modmust.yang"
+ ly_cmd "list" "I modmust\r.*i modleaf"
+ ly_cmd "clear"
+ ly_ignore "add -i $mdir/modmust.yang"
+ ly_cmd "list" "I modmust\r.*I modleaf"
+}}
+
+test add_make_implemented_twice {add -i -i} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "add $mdir/modimp-type.yang"
+ ly_cmd "list" "I modimp-type\r.*i modtypedef"
+ ly_cmd "clear"
+ ly_cmd "add -i -i $mdir/modimp-type.yang"
+ ly_cmd "list" "I modimp-type\r.*I modtypedef"
+}}
+
+test add_extended_leafref_enabled {Valid module with --extended-leafref option} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "add -X $mdir/modextleafref.yang"
+}}
+
+test add_extended_leafref_disabled {Expected error if --extended-leafref is not set} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd_err "add $mdir/modextleafref.yang" "Unexpected XPath token \"FunctionName\""
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/all.tcl b/tests/yanglint/interactive/all.tcl
new file mode 100644
index 0000000..b22a5ab
--- /dev/null
+++ b/tests/yanglint/interactive/all.tcl
@@ -0,0 +1,15 @@
+package require tcltest
+
+# Hook to determine if any of the tests failed.
+# Sets a global variable exitCode to 1 if any test fails otherwise it is set to 0.
+proc tcltest::cleanupTestsHook {} {
+ variable numTests
+ set ::exitCode [expr {$numTests(Failed) > 0}]
+}
+
+if {[info exists ::env(TESTS_DIR)]} {
+ tcltest::configure -testdir "$env(TESTS_DIR)/interactive"
+}
+
+tcltest::runAllTests
+exit $exitCode
diff --git a/tests/yanglint/interactive/clear.test b/tests/yanglint/interactive/clear.test
new file mode 100644
index 0000000..cac0810
--- /dev/null
+++ b/tests/yanglint/interactive/clear.test
@@ -0,0 +1,53 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+set ddir $::env(TESTS_DIR)/data
+
+test clear_searchpath {searchpath is also deleted} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "searchpath ./"
+ ly_cmd "clear"
+ ly_cmd "searchpath" "List of the searchpaths:" -ex
+}}
+
+test clear_make_implemented_once {clear --make-implemented} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -i"
+ ly_cmd "add $mdir/modmust.yang"
+ ly_cmd "list" "I modmust\r.*I modleaf"
+}}
+
+test clear_make_implemented_twice {clear -i -i} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -i -i"
+ ly_cmd "add $mdir/modmust.yang"
+ ly_cmd "list" "I modmust\r.*I modleaf"
+}}
+
+test clear_ietf_yang_library {clear --yang-library} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ # add models
+ ly_cmd "clear -y"
+ ly_cmd "list" "I ietf-yang-library"
+}}
+
+test clear_ylf_list {apply --yang-library-file and check result by --list} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -Y $ddir/modimp_type_ctx.xml"
+ ly_cmd "list" "I modimp-type.*i modtypedef"
+}}
+
+test clear_ylf_make_implemented {apply --yang-library-file and --make-implemented} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -Y $ddir/modimp_type_ctx.xml -i -i"
+ ly_cmd "list" "I modimp-type.*I modtypedef"
+}}
+
+test clear_ylf_augment_ctx {Setup context by yang-library-file and augment module} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -Y $ddir/modconfig_ctx.xml"
+ ly_cmd "add $mdir/modconfig-augment.yang"
+ ly_cmd "print -f tree modconfig" "mca:alf"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/completion.test b/tests/yanglint/interactive/completion.test
new file mode 100644
index 0000000..86ded1f
--- /dev/null
+++ b/tests/yanglint/interactive/completion.test
@@ -0,0 +1,69 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir "$::env(YANG_MODULES_DIR)"
+
+variable ly_cleanup {
+ ly_ignore
+ ly_exit
+}
+
+test completion_hints_ietf_ip {Completion and hints for ietf-ip.yang} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "add $mdir/ietf-ip.yang"
+
+ # completion and hint
+ ly_completion "print -f info -P " "print -f info -P /ietf-"
+
+ set hints {"/ietf-yang-schema-mount:schema-mounts" "/ietf-interfaces:interfaces" "/ietf-interfaces:interfaces-state"}
+ ly_hint "" "print -f info -P /ietf-" $hints
+
+ # double completion
+ ly_completion "i" "print -f info -P /ietf-interfaces:interfaces"
+ ly_completion "/" "print -f info -P /ietf-interfaces:interfaces/interface"
+
+ # a lot of hints
+ set hints {"/ietf-interfaces:interfaces/interface"
+ "/ietf-interfaces:interfaces/interface/name" "/ietf-interfaces:interfaces/interface/description"
+ "/ietf-interfaces:interfaces/interface/type" "/ietf-interfaces:interfaces/interface/enabled"
+ "/ietf-interfaces:interfaces/interface/link-up-down-trap-enable"
+ "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6"
+ }
+ ly_hint "" "print -f info -P /ietf-interfaces:interfaces/interface" $hints
+
+ # double tab
+ ly_completion "/i" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv"
+ ly_completion "4" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4"
+ set hints { "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled"
+ "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/forwarding" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/mtu"
+ "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/neighbor"
+ }
+ ly_hint "\t" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv" $hints
+
+ # no more completion
+ ly_completion "/e" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled "
+}}
+
+# Note that somehow a command is automatically sent again (\t\t replaced by \r) after the hints.
+# But that doesn't affect the test because the tests only focus on the word in the hint.
+
+test hint_data_file {Show file hints for command data} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_hint "data $mdir\t\t" "data $mdir" "modleaf.yang.*"
+}}
+
+test hint_data_format {Show print hints for command data --format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_hint "data -f \t\t" "data -f " "xml.*"
+}}
+
+test hint_data_file_after_opt {Show file hints after option with argument} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_hint "data -f xml $mdir\t\t" "data -f xml $mdir" "modleaf.yang.*"
+}}
+
+test hint_data_file_after_opt2 {Show file hints after option without argument} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_hint "data -m $mdir\t\t" "data -m $mdir" "modleaf.yang.*"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/data_default.test b/tests/yanglint/interactive/data_default.test
new file mode 100644
index 0000000..1953acc
--- /dev/null
+++ b/tests/yanglint/interactive/data_default.test
@@ -0,0 +1,41 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set mods "ietf-netconf-with-defaults moddefault"
+set data "$::env(TESTS_DIR)/data/moddefault.xml"
+
+test data_default_not_set {Print data without --default parameter} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load $mods"
+ ly_cmd "data -f xml $data" "</lf>.*</di>\r\n</mdc>"
+ ly_cmd "data -f json $data" "lf\".*di\"\[^\"]*"
+}}
+
+test data_default_all {data --default all} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load $mods"
+ ly_cmd "data -d all -f xml $data" "</lf>.*</di>.*</ds>\r\n</mdc>"
+ ly_cmd "data -d all -f json $data" "lf\".*di\".*ds\"\[^\"]*"
+}}
+
+test data_default_all_tagged {data --default all-tagged} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load $mods"
+ ly_cmd "data -d all-tagged -f xml $data" "</lf>.*<di.*default.*</di>.*<ds.*default.*</ds>\r\n</mdc>"
+ ly_cmd "data -d all-tagged -f json $data" "lf\".*di\".*ds\".*@ds\".*default\"\[^\"]*"
+}}
+
+test data_default_trim {data --default trim} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load $mods"
+ ly_cmd "data -d trim -f xml $data" "</lf>\r\n</mdc>"
+ ly_cmd "data -d trim -f json $data" "lf\"\[^\"]*"
+}}
+
+test data_default_implicit_tagged {data --default implicit-tagged} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load $mods"
+ ly_cmd "data -d implicit-tagged -f xml $data" "</lf>.*<di>5</di>.*<ds.*default.*</ds>\r\n</mdc>"
+ ly_cmd "data -d implicit-tagged -f json $data" "lf\".*di\"\[^@]*ds\".*default\"\[^\"]*"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/data_format.test b/tests/yanglint/interactive/data_format.test
new file mode 100644
index 0000000..dc4b7e0
--- /dev/null
+++ b/tests/yanglint/interactive/data_format.test
@@ -0,0 +1,23 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set ddir "$::env(TESTS_DIR)/data"
+
+test data_format_xml {Print data in xml format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd "data -f xml $ddir/modleaf.xml" "<lfl xmlns=\"urn:yanglint:modleaf\">7</lfl>"
+}}
+
+test data_format_json {Print data in json format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd "data -f json $ddir/modleaf.xml" "{\r\n \"modleaf:lfl\": 7\r\n}"
+}}
+
+test data_format_lyb_err {Print data in lyb format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd_err "data -f lyb $ddir/modleaf.xml" "The LYB format requires the -o"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/data_in_format.test b/tests/yanglint/interactive/data_in_format.test
new file mode 100644
index 0000000..cc5f37e
--- /dev/null
+++ b/tests/yanglint/interactive/data_in_format.test
@@ -0,0 +1,21 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set ddir "$::env(TESTS_DIR)/data"
+
+test data_in_format_xml {--in-format xml} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd "data -F xml $ddir/modleaf.dxml"
+ ly_cmd_err "data -F json $ddir/modleaf.dxml" "Failed to parse"
+ ly_cmd_err "data -F lyb $ddir/modleaf.dxml" "Failed to parse"
+}}
+
+test data_in_format_json {--in-format json} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd "data -F json $ddir/modleaf.djson"
+ ly_cmd_err "data -F xml $ddir/modleaf.djson" "Failed to parse"
+ ly_cmd_err "data -F lyb $ddir/modleaf.djson" "Failed to parse"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/data_merge.test b/tests/yanglint/interactive/data_merge.test
new file mode 100644
index 0000000..38754c7
--- /dev/null
+++ b/tests/yanglint/interactive/data_merge.test
@@ -0,0 +1,33 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set ddir "$::env(TESTS_DIR)/data"
+
+test data_merge_basic {Data is merged and the node is added} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modmerge"
+ ly_cmd "data -m -f xml $ddir/modmerge.xml $ddir/modmerge3.xml" "<en>.*<lm>.*<lf>"
+}}
+
+test data_merge_validation_failed {Data is merged but validation failed.} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modmerge"
+ ly_cmd "data $ddir/modmerge.xml"
+ ly_cmd "data $ddir/modmerge2.xml"
+ ly_cmd "data -m $ddir/modmerge2.xml $ddir/modmerge.xml"
+ ly_cmd_err "data -m $ddir/modmerge.xml $ddir/modmerge2.xml" "Merged data are not valid"
+}}
+
+test data_merge_dataconfig {The merge option has effect only for 'data' and 'config' TYPEs} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modrpc modnotif modconfig modleaf"
+ set wrn1 "option has effect only for"
+ ly_cmd_wrn "data -m -t rpc $ddir/modrpc.xml $ddir/modrpc.xml" $wrn1
+ ly_cmd_wrn "data -m -t notif $ddir/modnotif2.xml $ddir/modnotif2.xml" $wrn1
+ ly_cmd_wrn "data -m -t get $ddir/modleaf.xml $ddir/modconfig.xml" $wrn1
+ ly_cmd_wrn "data -m -t getconfig $ddir/modleaf.xml $ddir/modconfig2.xml" $wrn1
+ ly_cmd_wrn "data -m -t edit $ddir/modleaf.xml $ddir/modconfig2.xml" $wrn1
+ ly_cmd "data -m -t config $ddir/modleaf.xml $ddir/modconfig2.xml"
+ ly_cmd "data -m -t data $ddir/modleaf.xml $ddir/modconfig.xml"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/data_not_strict.test b/tests/yanglint/interactive/data_not_strict.test
new file mode 100644
index 0000000..201a5a9
--- /dev/null
+++ b/tests/yanglint/interactive/data_not_strict.test
@@ -0,0 +1,25 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set ddir $::env(TESTS_DIR)/data
+
+test data_no_strict_basic {} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd_err "data $ddir/modmandatory.xml" "No module with namespace \"urn:yanglint:modmandatory\" in the context."
+ ly_cmd "data -n $ddir/modmandatory.xml"
+}}
+
+test data_no_strict_invalid_data {validation with --no-strict but data are invalid} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ set errmsg "Mandatory node \"lft\" instance does not exist."
+ ly_cmd "load modmandatory"
+ ly_cmd_err "data -n $ddir/modmandatory_invalid.xml" $errmsg
+}}
+
+test data_no_strict_ignore_invalid_data {--no-strict ignore invalid data if no schema is provided} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd "data -f xml -n $ddir/modmandatory_invalid.xml $ddir/modleaf.xml" "modleaf.*</lfl>$"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/data_operational.test b/tests/yanglint/interactive/data_operational.test
new file mode 100644
index 0000000..c0c7b1c
--- /dev/null
+++ b/tests/yanglint/interactive/data_operational.test
@@ -0,0 +1,86 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set ddir "$::env(TESTS_DIR)/data"
+set err1 "Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types"
+
+test data_operational_twice {it is not allowed to specify more than one --operational parameter} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modoper-leafref"
+ ly_cmd "data -t notif -O $ddir/modconfig.xml -O $ddir/modleaf.xml" "cannot be set multiple times"
+}}
+
+test data_operational_no_type {--operational should be with parameter --type} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modoper-leafref"
+ ly_cmd_wrn "data -O $ddir/modconfig.xml $ddir/modoper_leafref_notif.xml" $err1
+}}
+
+test data_operational_missing {--operational is omitted and the datastore contents is in the data file} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modoper-leafref"
+ ly_cmd_err "data $ddir/modoper_leafref_notif_err.xml" "Failed to parse input data file"
+}}
+
+test data_operational_wrong_type {data are not defined as an operation} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd_wrn "data -t data -O $ddir/modconfig.xml $ddir/modleaf.xml" $err1
+}}
+
+test data_operational_datastore_with_unknown_data {unknown data are ignored} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modrpc"
+ ly_cmd "data -t rpc -O $ddir/modmandatory_invalid.xml $ddir/modrpc.xml"
+}}
+
+test data_operational_empty_datastore {datastore is considered empty because it contains unknown data} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modrpc modnotif"
+ ly_cmd "data -t rpc -O $ddir/modmandatory_invalid.xml $ddir/modrpc.xml"
+ set msg "parent \"/modnotif:con\" not found in the operational data"
+ ly_cmd_err "data -t notif -O $ddir/modmandatory_invalid.xml $ddir/modnotif.xml" $msg
+}}
+
+test data_operational_notif_leafref {--operational data is referenced from notification-leafref} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modoper-leafref"
+ ly_cmd "data -t notif -O $ddir/modconfig.xml $ddir/modoper_leafref_notif.xml"
+}}
+
+test data_operational_nested_notif_leafref {--operational data is referenced from nested-notification-leafref} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modoper-leafref"
+ ly_cmd "data -t notif -O $ddir/modoper_leafref_ds.xml $ddir/modoper_leafref_notif2.xml"
+}}
+
+test data_operational_nested_notif_parent_missing {--operational data are invalid due to missing parent node} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modoper-leafref"
+ set msg "klf='key_val']\" not found in the operational data"
+ ly_cmd_err "data -t notif -O $ddir/modconfig.xml $ddir/modoper_leafref_notif2.xml" $msg
+}}
+
+test data_operational_action_leafref {--operational data is referenced from action-leafref} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modoper-leafref"
+ ly_cmd "data -t rpc -O $ddir/modoper_leafref_ds.xml $ddir/modoper_leafref_action.xml"
+}}
+
+test data_operational_action_reply_leafref {--operational data is referenced from action-leafref output} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modoper-leafref"
+ ly_cmd "data -t reply -O $ddir/modoper_leafref_ds.xml $ddir/modoper_leafref_action_reply.xml"
+}}
+
+test data_operational_rpc_leafref {--operational data is referenced from rpc-leafref} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modoper-leafref"
+ ly_cmd "data -t rpc -O $ddir/modconfig.xml $ddir/modoper_leafref_rpc.xml"
+}}
+
+test data_operational_rpc_reply_leafref {--operational data is referenced from rpc-leafref output} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modoper-leafref"
+ ly_cmd "data -t reply -O $ddir/modconfig.xml $ddir/modoper_leafref_rpc_reply.xml"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/data_present.test b/tests/yanglint/interactive/data_present.test
new file mode 100644
index 0000000..4bba596
--- /dev/null
+++ b/tests/yanglint/interactive/data_present.test
@@ -0,0 +1,25 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set ddir "$::env(TESTS_DIR)/data"
+
+test data_present_via_mandatory {validation of mandatory-stmt will pass only with the --present} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf modmandatory"
+ ly_cmd_err "data $ddir/modleaf.xml" "Mandatory node \"lft\" instance does not exist."
+ ly_cmd "data -e $ddir/modleaf.xml"
+}}
+
+test data_present_merge {validation with --present and --merge} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf modmandatory moddefault"
+ ly_cmd_err "data -m $ddir/modleaf.xml $ddir/moddefault.xml" "Mandatory node \"lft\" instance does not exist."
+ ly_cmd "data -e -m $ddir/modleaf.xml $ddir/moddefault.xml"
+}}
+
+test data_present_merge_invalid {using --present and --merge but data are invalid} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf modmandatory"
+ ly_cmd_err "data -e -m $ddir/modleaf.xml $ddir/modmandatory_invalid.xml" "Mandatory node \"lft\" instance does not exist."
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/data_type.test b/tests/yanglint/interactive/data_type.test
new file mode 100644
index 0000000..a442813
--- /dev/null
+++ b/tests/yanglint/interactive/data_type.test
@@ -0,0 +1,140 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set ddir "$::env(TESTS_DIR)/data"
+
+test data_type_data {data --type data} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modconfig"
+ ly_cmd "data -t data $ddir/modconfig.xml"
+}}
+
+test data_type_config {data --type config} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modconfig"
+ ly_cmd_err "data -t config $ddir/modconfig.xml" "Unexpected data state node \"lff\""
+ ly_cmd "data -t config $ddir/modconfig2.xml"
+}}
+
+test data_type_get {data --type get} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleafref"
+ ly_cmd_err "data -t data $ddir/modleafref2.xml" "Invalid leafref value"
+ ly_cmd "data -t get $ddir/modleafref2.xml"
+}}
+
+test data_type_getconfig_no_state {No state node for data --type getconfig} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modconfig"
+ ly_cmd_err "data -t getconfig $ddir/modconfig.xml" "Unexpected data state node \"lff\""
+ ly_cmd "data -t getconfig $ddir/modconfig2.xml"
+}}
+
+test data_type_getconfig_parse_only {No validation performed for data --type getconfig} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleafref"
+ ly_cmd_err "data -t data $ddir/modleafref2.xml" "Invalid leafref value"
+ ly_cmd "data -t getconfig $ddir/modleafref2.xml"
+}}
+
+test data_type_edit_no_state {No state node for data --type edit} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modconfig"
+ ly_cmd_err "data -t edit $ddir/modconfig.xml" "Unexpected data state node \"lff\""
+ ly_cmd "data -t edit $ddir/modconfig2.xml"
+}}
+
+test data_type_edit_parse_only {No validation performed for data --type edit} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleafref"
+ ly_cmd_err "data -t data $ddir/modleafref2.xml" "Invalid leafref value"
+ ly_cmd "data -t edit $ddir/modleafref2.xml"
+}}
+
+test data_type_rpc {Validation of rpc-statement by data --type rpc} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modrpc modleaf"
+ ly_cmd_err "data -t rpc $ddir/modleaf.xml" "Missing the operation node."
+ ly_cmd "data -t rpc $ddir/modrpc.xml"
+}}
+
+test data_type_rpc_nc {Validation of rpc-statement by data --type nc-rpc} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modrpc modleaf ietf-netconf"
+ ly_cmd_err "data -t nc-rpc $ddir/modleaf.xml" "Missing NETCONF <rpc> envelope"
+ ly_cmd "data -t nc-rpc $ddir/modrpc_nc.xml"
+}}
+
+test data_type_rpc_reply {Validation of rpc-reply by data --type reply} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modrpc modleaf"
+ ly_cmd_err "data -t rpc $ddir/modleaf.xml" "Missing the operation node."
+ ly_cmd_wrn "data -t reply -R $ddir/modrpc.xml $ddir/modrpc_reply.xml" "needed only for NETCONF"
+ ly_cmd "data -t reply $ddir/modrpc_reply.xml"
+}}
+
+test data_type_rpc_reply_nc {Validation of rpc-reply by data --type nc-reply} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modrpc modleaf"
+ ly_cmd_err "data -t nc-reply -R $ddir/modrpc_nc.xml $ddir/modleaf.xml" "Missing NETCONF <rpc-reply> envelope"
+ ly_cmd_err "data -t nc-reply $ddir/modrpc_reply_nc.xml" "Missing source RPC"
+ ly_cmd "data -t nc-reply -R $ddir/modrpc_nc.xml $ddir/modrpc_reply_nc.xml"
+}}
+
+test data_type_rpc_action {Validation of action-statement by data --type rpc} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modaction modleaf"
+ ly_cmd_err "data -t rpc $ddir/modleaf.xml" "Missing the operation node."
+ ly_cmd "data -t rpc -O $ddir/modaction_ds.xml $ddir/modaction.xml"
+}}
+
+test data_type_rpc_action_nc {Validation of action-statement by data --type nc-rpc} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modaction modleaf"
+ ly_cmd_err "data -t nc-rpc $ddir/modleaf.xml" "Missing NETCONF <rpc> envelope"
+ ly_cmd "data -t nc-rpc -O $ddir/modaction_ds.xml $ddir/modaction_nc.xml"
+}}
+
+test data_type_rpc_action_reply {Validation of action-reply by data --type reply} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modaction modleaf"
+ ly_cmd_err "data -t rpc $ddir/modleaf.xml" "Missing the operation node."
+ ly_cmd "data -t reply -O $ddir/modaction_ds.xml $ddir/modaction_reply.xml"
+}}
+
+test data_type_rpc_action_reply_nc {Validation of action-reply by data --type nc-reply} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modaction modleaf"
+ ly_cmd_err "data -t nc-reply -R $ddir/modaction_nc.xml $ddir/modleaf.xml" "Missing NETCONF <rpc-reply> envelope"
+ ly_cmd_err "data -t nc-reply $ddir/modaction_reply_nc.xml" "Missing source RPC"
+ ly_cmd_err "data -t nc-reply -R $ddir/modaction_nc.xml $ddir/modaction_reply_nc.xml" "operational parameter needed"
+ ly_cmd "data -t nc-reply -O $ddir/modaction_ds.xml -R $ddir/modaction_nc.xml $ddir/modaction_reply_nc.xml"
+}}
+
+test data_type_notif {Validation of notification-statement by data --type notif} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modnotif modleaf"
+ ly_cmd_err "data -t notif $ddir/modleaf.xml" "Missing the operation node."
+ ly_cmd "data -t notif $ddir/modnotif2.xml"
+}}
+
+test data_type_notif_nc {Validation of notification-statement by data --type nc-notif} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modnotif modleaf ietf-netconf"
+ ly_cmd_err "data -t nc-notif $ddir/modleaf.xml" "Missing NETCONF <notification> envelope"
+ ly_cmd "data -t nc-notif $ddir/modnotif2_nc.xml"
+}}
+
+test data_type_notif_nested {Validation of nested-notification-statement by data --type notif} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modnotif modleaf"
+ ly_cmd "data -t notif -O $ddir/modnotif_ds.xml $ddir/modnotif.xml"
+}}
+
+test data_type_notif_nested_nc {Validation of nested-notification-statement by data --type nc-notif} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modnotif modleaf ietf-netconf"
+ ly_cmd_err "data -t nc-notif $ddir/modleaf.xml" "Missing NETCONF <notification> envelope"
+ ly_cmd "data -t nc-notif -O $ddir/modnotif_ds.xml $ddir/modnotif_nc.xml"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/data_xpath.test b/tests/yanglint/interactive/data_xpath.test
new file mode 100644
index 0000000..398cb9f
--- /dev/null
+++ b/tests/yanglint/interactive/data_xpath.test
@@ -0,0 +1,57 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set data "$::env(TESTS_DIR)/data/moddatanodes.xml"
+
+test data_xpath_empty {--xpath to missing node} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load moddatanodes"
+ ly_cmd "data -x /moddatanodes:dnc/mis $data" "Empty"
+}}
+
+test data_xpath_leaf {--xpath to leaf node} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load moddatanodes"
+ ly_cmd "data -x /moddatanodes:dnc/lf $data" "leaf \"lf\" \\(value: \"x\"\\)"
+}}
+
+test data_xpath_leaflist {--xpath to leaf-list node} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load moddatanodes"
+ set r1 "leaf-list \"lfl\" \\(value: \"1\"\\)"
+ set r2 "leaf-list \"lfl\" \\(value: \"2\"\\)"
+ ly_cmd "data -x /moddatanodes:dnc/lfl $data" "$r1\r\n $r2"
+}}
+
+test data_xpath_list {--xpath to list} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load moddatanodes"
+ set r1 "list \"lt\" \\(\"kalf\": \"ka1\"; \"kblf\": \"kb1\";\\)"
+ set r2 "list \"lt\" \\(\"kalf\": \"ka2\"; \"kblf\": \"kb2\";\\)"
+ ly_cmd "data -x /moddatanodes:dnc/con/lt $data" "$r1\r\n $r2"
+}}
+
+test data_xpath_container {--xpath to container} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load moddatanodes"
+ ly_cmd "data -x /moddatanodes:dnc/con $data" "container \"con\""
+}}
+
+test data_xpath_wrong_path {--xpath to a non-existent node} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load moddatanodes"
+ ly_cmd_err "data -x /moddatanodes:dnc/wrng $data" "xpath failed"
+}}
+
+test data_xpath_err_format {--xpath cannot be combined with --format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load moddatanodes"
+ ly_cmd_err "data -f xml -x /moddatanodes:dnc/lf $data" "option cannot be combined"
+}}
+
+test data_xpath_err_default {--xpath cannot be combined with --default} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load moddatanodes ietf-netconf-with-defaults"
+ ly_cmd_err "data -d all -x /moddatanodes:dnc/lf $data" "option cannot be combined"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/debug.test b/tests/yanglint/interactive/debug.test
new file mode 100644
index 0000000..8a64c92
--- /dev/null
+++ b/tests/yanglint/interactive/debug.test
@@ -0,0 +1,33 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+
+test debug_dict {Check debug message DICT} {
+-setup $ly_setup -cleanup $ly_cleanup -constraints {[yanglint_debug]} -body {
+ ly_cmd "verb debug"
+ ly_cmd "debug dict"
+ ly_cmd "load modleaf" "DICT"
+}}
+
+test debug_xpath {Check debug message XPATH} {
+-setup $ly_setup -cleanup $ly_cleanup -constraints {[yanglint_debug]} -body {
+ ly_cmd "verb debug"
+ ly_cmd "debug xpath"
+ ly_cmd "load modmust" "XPATH"
+}}
+
+test debug_dep_sets {Check debug message DEPSETS} {
+-setup $ly_setup -cleanup $ly_cleanup -constraints {[yanglint_debug]} -body {
+ ly_cmd "verb debug"
+ ly_cmd "debug dep-sets"
+ ly_cmd "load modleaf" "DEPSETS"
+}}
+
+test debug_depsets_xpath {Check debug message DEPSETS and XPATH} {
+-setup $ly_setup -cleanup $ly_cleanup -constraints {[yanglint_debug]} -body {
+ ly_cmd "verb debug"
+ ly_cmd "debug dep-sets xpath"
+ ly_cmd "load modmust" "DEPSETS.*XPATH"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/extdata.test b/tests/yanglint/interactive/extdata.test
new file mode 100644
index 0000000..e253d1a
--- /dev/null
+++ b/tests/yanglint/interactive/extdata.test
@@ -0,0 +1,63 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir "$::env(YANG_MODULES_DIR)"
+set ddir "$::env(TESTS_DIR)/data"
+
+test extdata_set_clear {Set and clear extdata file} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "extdata" "No file set"
+ ly_cmd "extdata $ddir/modsm_ctx_ext.xml"
+ ly_cmd "extdata" "$ddir/modsm_ctx_ext.xml"
+ ly_cmd "extdata -c"
+ ly_cmd "extdata" "No file set"
+}}
+
+test extdata_clear_cmd {Clear extdata file by 'clear' command} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "extdata $ddir/modsm_ctx_ext.xml"
+ ly_cmd "clear"
+ ly_cmd "extdata" "No file set"
+}}
+
+test extdata_one_only {Only one file for extdata} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd_err "extdata $ddir/modsm_ctx_ext.xml $ddir/modsm_ctx_ext.xml" "Only one file must be entered"
+}}
+
+test extdata_schema_mount_tree {Print tree output of a model with Schema Mount} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -y"
+ ly_cmd "searchpath $mdir"
+ ly_cmd "load modsm"
+ ly_cmd "extdata $ddir/modsm_ctx_ext.xml"
+ ly_cmd "print -f tree modsm" "--mp root.*--rw lfl/"
+}}
+
+test ext_data_schema_mount_tree_yanglibfile {Print tree output of a model with Schema Mount and --yang-library-file} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -Y $ddir/modsm_ctx_main.xml"
+ ly_cmd "searchpath $mdir"
+ ly_cmd "load modsm"
+ ly_cmd "extdata $ddir/modsm_ctx_ext.xml"
+ ly_cmd "print -f tree modsm" "--mp root.*--rw lfl/.*--rw msa:alf?"
+}}
+
+test ext_data_schema_mount_xml {Validating and printing mounted data} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -y"
+ ly_cmd "searchpath $mdir"
+ ly_cmd "load modsm"
+ ly_cmd "extdata $ddir/modsm_ctx_ext.xml"
+ ly_cmd "data -f xml -t config $ddir/modsm.xml" "</lfl>"
+}}
+
+test ext_data_schema_mount_xml_yanglibfile {Validating and printing mounted data with --yang-library-file} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -Y $ddir/modsm_ctx_main.xml"
+ ly_cmd "searchpath $mdir"
+ ly_cmd "load modsm"
+ ly_cmd "extdata $ddir/modsm_ctx_ext.xml"
+ ly_cmd "data -f xml -t config $ddir/modsm2.xml" "</lfl>.*</alf>"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/feature.test b/tests/yanglint/interactive/feature.test
new file mode 100644
index 0000000..84bfa8e
--- /dev/null
+++ b/tests/yanglint/interactive/feature.test
@@ -0,0 +1,37 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+test feature_all_default {Default output of feature --all} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "feature -a" "yang:\r\n\t(none)\r\n\r\nietf-yang-schema-mount:\r\n\t(none)\r\n" -ex
+}}
+
+test feature_all_add_module {Add module with only one feature and call feature --all} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load --feature modfeature:ftr1 modfeature"
+ ly_cmd "feature -a" "modfeature:\r\n\tftr1 \\(on\\)\r\n\tftr2 \\(off\\)"
+}}
+
+test feature_all_on {Add module with all enabled features and call feature --all} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load --feature modfeature:* modfeature"
+ ly_cmd "feature -a" "modfeature:\r\n\tftr1 \\(on\\)\r\n\tftr2 \\(on\\)"
+}}
+
+test feature_one_module {Show features for one module} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load ietf-ip"
+ ly_cmd "feature -f ietf-ip" " -F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf" -ex
+}}
+
+test feature_more_modules {Show a mix of modules with and without features} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+
+ set features " -F modfeature:ftr1,ftr2\
+-F modleaf:\
+-F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf"
+
+ ly_cmd "load ietf-ip modleaf modfeature"
+ ly_cmd "feature -f modfeature modleaf ietf-ip" $features -ex
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/list.test b/tests/yanglint/interactive/list.test
new file mode 100644
index 0000000..ab59a32
--- /dev/null
+++ b/tests/yanglint/interactive/list.test
@@ -0,0 +1,34 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+namespace import uti::regex_xml_elements uti::regex_json_pairs
+
+set modules {ietf-yang-library ietf-inet-types}
+
+test list_basic {basic test} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "list" "ietf-yang-types"
+}}
+
+test list_format_xml {list --format xml} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -y"
+ ly_cmd "list -f xml" [regex_xml_elements $modules "name"]
+}}
+
+test list_format_json {list --format json} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -y"
+ ly_cmd "list -f json" [regex_json_pairs $modules "name"]
+}}
+
+test list_ietf_yang_library {Error due to missing ietf-yang-library} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd_err "list -f xml" "Module \"ietf-yang-library\" is not implemented."
+}}
+
+test list_bad_format {Error due to bad format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "clear -y"
+ ly_cmd_err "list -f csv" "Unknown output format csv"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/load.test b/tests/yanglint/interactive/load.test
new file mode 100644
index 0000000..a95d044
--- /dev/null
+++ b/tests/yanglint/interactive/load.test
@@ -0,0 +1,45 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+test load_basic {} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleafref"
+ ly_cmd "list" "I modleafref\r.*I modleaf"
+}}
+
+test load_with_feature {Load module with feature} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load --feature modfeature:ftr2 modfeature"
+ ly_cmd "feature -a" "modfeature:\r\n\tftr1 \\(off\\)\r\n\tftr2 \\(on\\)"
+}}
+
+test load_make_implemented_once {load --make-implemented} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_ignore "load modmust"
+ ly_cmd "list" "I modmust\r.*i modleaf"
+ ly_cmd "clear"
+ ly_cmd "searchpath $::env(YANG_MODULES_DIR)"
+ ly_cmd "load -i modmust"
+ ly_cmd "list" "I modmust\r.*I modleaf"
+}}
+
+test load_make_implemented_twice {load -i -i} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modimp-type"
+ ly_cmd "list" "I modimp-type\r.*i modtypedef"
+ ly_cmd "clear"
+ ly_cmd "searchpath $::env(YANG_MODULES_DIR)"
+ ly_cmd "load -i -i modimp-type"
+ ly_cmd "list" "I modimp-type\r.*I modtypedef"
+}}
+
+test load_extended_leafref_enabled {Valid module with --extended-leafref option} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load -X modextleafref"
+}}
+
+test load_extended_leafref_disabled {Expected error if --extended-leafref is not set} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd_err "load modextleafref" "Unexpected XPath token \"FunctionName\""
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/ly.tcl b/tests/yanglint/interactive/ly.tcl
new file mode 100644
index 0000000..4c56be4
--- /dev/null
+++ b/tests/yanglint/interactive/ly.tcl
@@ -0,0 +1,81 @@
+# @brief The main source of functions and variables for testing yanglint in the interactive mode.
+
+# For testing yanglint.
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/common.tcl" : "../common.tcl"}]
+# For testing any interactive tool.
+source "$::env(TESTS_DIR)/../tool_i.tcl"
+
+# The script continues by defining variables and functions specific to the interactive yanglint tool.
+
+# set the timeout to 5 seconds
+set timeout 5
+# prompt of yanglint
+set prompt "> "
+# turn off dialog between expect and yanglint
+log_user 0
+# setting some large terminal width
+stty columns 720
+
+# default setup for every unit test
+variable ly_setup {
+ spawn $TUT
+ ly_skip_warnings
+ # Searchpath is set, so modules can be loaded via the 'load' command.
+ ly_cmd "searchpath $::env(YANG_MODULES_DIR)"
+}
+
+# default cleanup for every unit test
+variable ly_cleanup {
+ ly_exit
+}
+
+# Skip no dir and/or no history warnings and prompt.
+proc ly_skip_warnings {} {
+ global prompt
+ expect -re "(YANGLINT.*)*$prompt" {}
+}
+
+# Send command 'cmd' to the process, expect error header and then check output string by 'pattern'.
+# Parameter cmd is a string of arguments.
+# Parameter pattern is a regex. It must not contain a prompt.
+proc ly_cmd_err {cmd pattern} {
+ global prompt
+
+ send -- "${cmd}\r"
+ expect -- "${cmd}\r\n"
+
+ expect {
+ -re "YANGLINT\\\[E\\\]: .*${pattern}.*\r\n${prompt}$" {}
+ -re "libyang\\\[\[0-9]+\\\]: .*${pattern}.*\r\n${prompt}$" {}
+ -re "\r\n${prompt}$" {
+ error "unexpected output:\n$expect_out(buffer)"
+ }
+ }
+}
+
+# Send command 'cmd' to the process, expect warning header and then check output string by 'pattern'.
+# Parameter cmd is a string of arguments.
+# Parameter pattern is a regex. It must not contain a prompt.
+proc ly_cmd_wrn {cmd pattern} {
+ ly_cmd_header $cmd "YANGLINT\\\[W\\\]:" $pattern
+}
+
+# Send 'exit' and wait for eof.
+proc ly_exit {} {
+ send "exit\r"
+ expect eof
+}
+
+# Check if yanglint is configured as DEBUG.
+# Return 1 on success.
+proc yanglint_debug {} {
+ global TUT
+ # Call non-interactive yanglint with --help.
+ set output [exec -- $TUT "-h"]
+ # Find option --debug.
+ if { [regexp -- "--debug=GROUPS" $output] } {
+ return 1
+ } else {
+ return 0
+ }
+}
diff --git a/tests/yanglint/interactive/modcwd.yang b/tests/yanglint/interactive/modcwd.yang
new file mode 100644
index 0000000..db33e73
--- /dev/null
+++ b/tests/yanglint/interactive/modcwd.yang
@@ -0,0 +1,4 @@
+module modcwd {
+ namespace "urn:yanglint:modcwd";
+ prefix mc;
+}
diff --git a/tests/yanglint/interactive/print.test b/tests/yanglint/interactive/print.test
new file mode 100644
index 0000000..8b9d740
--- /dev/null
+++ b/tests/yanglint/interactive/print.test
@@ -0,0 +1,77 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set ipv6_path "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6/address"
+
+test print_yang {} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd "print -f yang modleaf" "leaf lfl"
+}}
+
+test print_yang_submodule {Print submodule in yang format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modinclude"
+ ly_cmd "print -f yang modsub" "submodule modsub"
+}}
+
+test print_yin {} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd "print -f yin modleaf" "<leaf name=\"lfl\">"
+}}
+
+test print_yin_submodule {Print submodule in yin format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modinclude"
+ ly_cmd "print -f yin modsub" "<submodule name=\"modsub\""
+}}
+
+test print_info {} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd "print -f info modleaf" "status current"
+}}
+
+test print_info_path {Print subtree in info format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load ietf-ip"
+ ly_cmd "print -f info -P $ipv6_path" "^list address .* leaf prefix-length"
+}}
+
+test print_info_path_single_node {Print node in info format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load ietf-ip"
+ ly_cmd "print -f info -q -P $ipv6_path" "^list address .* IPv6 addresses on the interface.\";\r\n\}$"
+}}
+
+test print_tree {} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modleaf"
+ ly_cmd "print -f tree modleaf" "\\+--rw lfl"
+}}
+
+test print_tree_submodule {Print submodule in tree format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load modinclude"
+ ly_cmd "print -f tree modsub" "submodule: modsub"
+}}
+
+test print_tree_path {Print subtree in tree format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load ietf-ip"
+ ly_cmd "print -f tree -P $ipv6_path" "\\+--rw address.*\\+--rw prefix-length"
+}}
+
+test print_tree_path_single_node {Print node in tree format} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load ietf-ip"
+ ly_cmd "print -f tree -q -P $ipv6_path" "\\+--rw address\\* \\\[ip\\\]$"
+}}
+
+test print_tree_path_single_node_line_length {Print node in the tree format and limit row size} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "load ietf-ip"
+ ly_cmd "print -f tree -L 20 -q -P $ipv6_path" "\\+--rw address\\*\r\n *\\\[ip\\\]$"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/interactive/searchpath.test b/tests/yanglint/interactive/searchpath.test
new file mode 100644
index 0000000..3bd6796
--- /dev/null
+++ b/tests/yanglint/interactive/searchpath.test
@@ -0,0 +1,24 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+
+variable ly_setup {
+ spawn $TUT
+ ly_skip_warnings
+}
+
+test searchpath_basic {} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "searchpath $mdir"
+ ly_cmd "searchpath" "$mdir"
+ ly_cmd "load modleaf"
+}}
+
+test searchpath_clear {searchpath --clear} {
+-setup $ly_setup -cleanup $ly_cleanup -body {
+ ly_cmd "searchpath $mdir"
+ ly_cmd "searchpath --clear"
+ ly_cmd_err "load modleaf" "Data model \"modleaf\" not found in local searchdirs"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/modules/ietf-interfaces.yang b/tests/yanglint/modules/ietf-interfaces.yang
new file mode 100644
index 0000000..ad64425
--- /dev/null
+++ b/tests/yanglint/modules/ietf-interfaces.yang
@@ -0,0 +1,725 @@
+module ietf-interfaces {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces";
+ prefix if;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: Thomas Nadeau
+ <mailto:tnadeau@lucidvision.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+ description
+ "This module contains a collection of YANG definitions for
+ managing network interfaces.
+
+ Copyright (c) 2014 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 7223; see
+ the RFC itself for full legal notices.";
+
+ revision 2014-05-08 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 7223: A YANG Data Model for Interface Management";
+ }
+
+ /*
+ * Typedefs
+ */
+
+ typedef interface-ref {
+ type leafref {
+ path "/if:interfaces/if:interface/if:name";
+ }
+ description
+ "This type is used by data models that need to reference
+ configured interfaces.";
+ }
+
+ typedef interface-state-ref {
+ type leafref {
+ path "/if:interfaces-state/if:interface/if:name";
+ }
+ description
+ "This type is used by data models that need to reference
+ the operationally present interfaces.";
+ }
+
+ /*
+ * Identities
+ */
+
+ identity interface-type {
+ description
+ "Base identity from which specific interface types are
+ derived.";
+ }
+
+ /*
+ * Features
+ */
+
+ feature arbitrary-names {
+ description
+ "This feature indicates that the device allows user-controlled
+ interfaces to be named arbitrarily.";
+ }
+ feature pre-provisioning {
+ description
+ "This feature indicates that the device supports
+ pre-provisioning of interface configuration, i.e., it is
+ possible to configure an interface whose physical interface
+ hardware is not present on the device.";
+ }
+
+ feature if-mib {
+ description
+ "This feature indicates that the device implements
+ the IF-MIB.";
+ reference
+ "RFC 2863: The Interfaces Group MIB";
+ }
+
+ /*
+ * Configuration data nodes
+ */
+
+ container interfaces {
+ description
+ "Interface configuration parameters.";
+
+ list interface {
+ key "name";
+
+ description
+ "The list of configured interfaces on the device.
+
+ The operational state of an interface is available in the
+ /interfaces-state/interface list. If the configuration of a
+ system-controlled interface cannot be used by the system
+ (e.g., the interface hardware present does not match the
+ interface type), then the configuration is not applied to
+ the system-controlled interface shown in the
+ /interfaces-state/interface list. If the configuration
+ of a user-controlled interface cannot be used by the system,
+ the configured interface is not instantiated in the
+ /interfaces-state/interface list.";
+
+ leaf name {
+ type string;
+ description
+ "The name of the interface.
+
+ A device MAY restrict the allowed values for this leaf,
+ possibly depending on the type of the interface.
+ For system-controlled interfaces, this leaf is the
+ device-specific name of the interface. The 'config false'
+ list /interfaces-state/interface contains the currently
+ existing interfaces on the device.
+
+ If a client tries to create configuration for a
+ system-controlled interface that is not present in the
+ /interfaces-state/interface list, the server MAY reject
+ the request if the implementation does not support
+ pre-provisioning of interfaces or if the name refers to
+ an interface that can never exist in the system. A
+ NETCONF server MUST reply with an rpc-error with the
+ error-tag 'invalid-value' in this case.
+
+ If the device supports pre-provisioning of interface
+ configuration, the 'pre-provisioning' feature is
+ advertised.
+
+ If the device allows arbitrarily named user-controlled
+ interfaces, the 'arbitrary-names' feature is advertised.
+
+ When a configured user-controlled interface is created by
+ the system, it is instantiated with the same name in the
+ /interface-state/interface list.";
+ }
+
+ leaf description {
+ type string;
+ description
+ "A textual description of the interface.
+
+ A server implementation MAY map this leaf to the ifAlias
+ MIB object. Such an implementation needs to use some
+ mechanism to handle the differences in size and characters
+ allowed between this leaf and ifAlias. The definition of
+ such a mechanism is outside the scope of this document.
+
+ Since ifAlias is defined to be stored in non-volatile
+ storage, the MIB implementation MUST map ifAlias to the
+ value of 'description' in the persistently stored
+ datastore.
+
+ Specifically, if the device supports ':startup', when
+ ifAlias is read the device MUST return the value of
+ 'description' in the 'startup' datastore, and when it is
+ written, it MUST be written to the 'running' and 'startup'
+ datastores. Note that it is up to the implementation to
+
+ decide whether to modify this single leaf in 'startup' or
+ perform an implicit copy-config from 'running' to
+ 'startup'.
+
+ If the device does not support ':startup', ifAlias MUST
+ be mapped to the 'description' leaf in the 'running'
+ datastore.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAlias";
+ }
+
+ leaf type {
+ type identityref {
+ base interface-type;
+ }
+ mandatory true;
+ description
+ "The type of the interface.
+
+ When an interface entry is created, a server MAY
+ initialize the type leaf with a valid value, e.g., if it
+ is possible to derive the type from the name of the
+ interface.
+
+ If a client tries to set the type of an interface to a
+ value that can never be used by the system, e.g., if the
+ type is not supported or if the type does not match the
+ name of the interface, the server MUST reject the request.
+ A NETCONF server MUST reply with an rpc-error with the
+ error-tag 'invalid-value' in this case.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifType";
+ }
+
+ leaf enabled {
+ type boolean;
+ default "true";
+ description
+ "This leaf contains the configured, desired state of the
+ interface.
+
+ Systems that implement the IF-MIB use the value of this
+ leaf in the 'running' datastore to set
+ IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry
+ has been initialized, as described in RFC 2863.
+
+
+
+ Changes in this leaf in the 'running' datastore are
+ reflected in ifAdminStatus, but if ifAdminStatus is
+ changed over SNMP, this leaf is not affected.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+ }
+
+ leaf link-up-down-trap-enable {
+ if-feature if-mib;
+ type enumeration {
+ enum enabled {
+ value 1;
+ }
+ enum disabled {
+ value 2;
+ }
+ }
+ description
+ "Controls whether linkUp/linkDown SNMP notifications
+ should be generated for this interface.
+
+ If this node is not configured, the value 'enabled' is
+ operationally used by the server for interfaces that do
+ not operate on top of any other interface (i.e., there are
+ no 'lower-layer-if' entries), and 'disabled' otherwise.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifLinkUpDownTrapEnable";
+ }
+ }
+ }
+
+ /*
+ * Operational state data nodes
+ */
+
+ container interfaces-state {
+ config false;
+ description
+ "Data nodes for the operational state of interfaces.";
+
+ list interface {
+ key "name";
+
+
+
+
+
+ description
+ "The list of interfaces on the device.
+
+ System-controlled interfaces created by the system are
+ always present in this list, whether they are configured or
+ not.";
+
+ leaf name {
+ type string;
+ description
+ "The name of the interface.
+
+ A server implementation MAY map this leaf to the ifName
+ MIB object. Such an implementation needs to use some
+ mechanism to handle the differences in size and characters
+ allowed between this leaf and ifName. The definition of
+ such a mechanism is outside the scope of this document.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifName";
+ }
+
+ leaf type {
+ type identityref {
+ base interface-type;
+ }
+ mandatory true;
+ description
+ "The type of the interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifType";
+ }
+
+ leaf admin-status {
+ if-feature if-mib;
+ type enumeration {
+ enum up {
+ value 1;
+ description
+ "Ready to pass packets.";
+ }
+ enum down {
+ value 2;
+ description
+ "Not ready to pass packets and not in some test mode.";
+ }
+
+
+
+ enum testing {
+ value 3;
+ description
+ "In some test mode.";
+ }
+ }
+ mandatory true;
+ description
+ "The desired state of the interface.
+
+ This leaf has the same read semantics as ifAdminStatus.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifAdminStatus";
+ }
+
+ leaf oper-status {
+ type enumeration {
+ enum up {
+ value 1;
+ description
+ "Ready to pass packets.";
+ }
+ enum down {
+ value 2;
+ description
+ "The interface does not pass any packets.";
+ }
+ enum testing {
+ value 3;
+ description
+ "In some test mode. No operational packets can
+ be passed.";
+ }
+ enum unknown {
+ value 4;
+ description
+ "Status cannot be determined for some reason.";
+ }
+ enum dormant {
+ value 5;
+ description
+ "Waiting for some external event.";
+ }
+ enum not-present {
+ value 6;
+ description
+ "Some component (typically hardware) is missing.";
+ }
+ enum lower-layer-down {
+ value 7;
+ description
+ "Down due to state of lower-layer interface(s).";
+ }
+ }
+ mandatory true;
+ description
+ "The current operational state of the interface.
+
+ This leaf has the same semantics as ifOperStatus.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOperStatus";
+ }
+
+ leaf last-change {
+ type yang:date-and-time;
+ description
+ "The time the interface entered its current operational
+ state. If the current state was entered prior to the
+ last re-initialization of the local network management
+ subsystem, then this node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifLastChange";
+ }
+
+ leaf if-index {
+ if-feature if-mib;
+ type int32 {
+ range "1..2147483647";
+ }
+ mandatory true;
+ description
+ "The ifIndex value for the ifEntry represented by this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifIndex";
+ }
+
+ leaf phys-address {
+ type yang:phys-address;
+ description
+ "The interface's address at its protocol sub-layer. For
+ example, for an 802.x interface, this object normally
+ contains a Media Access Control (MAC) address. The
+ interface's media-specific modules must define the bit
+
+
+ and byte ordering and the format of the value of this
+ object. For interfaces that do not have such an address
+ (e.g., a serial line), this node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifPhysAddress";
+ }
+
+ leaf-list higher-layer-if {
+ type interface-state-ref;
+ description
+ "A list of references to interfaces layered on top of this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifStackTable";
+ }
+
+ leaf-list lower-layer-if {
+ type interface-state-ref;
+ description
+ "A list of references to interfaces layered underneath this
+ interface.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifStackTable";
+ }
+
+ leaf speed {
+ type yang:gauge64;
+ units "bits/second";
+ description
+ "An estimate of the interface's current bandwidth in bits
+ per second. For interfaces that do not vary in
+ bandwidth or for those where no accurate estimation can
+ be made, this node should contain the nominal bandwidth.
+ For interfaces that have no concept of bandwidth, this
+ node is not present.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifSpeed, ifHighSpeed";
+ }
+
+
+
+
+
+
+
+
+
+ container statistics {
+ description
+ "A collection of interface-related statistics objects.";
+
+ leaf discontinuity-time {
+ type yang:date-and-time;
+ mandatory true;
+ description
+ "The time on the most recent occasion at which any one or
+ more of this interface's counters suffered a
+ discontinuity. If no such discontinuities have occurred
+ since the last re-initialization of the local management
+ subsystem, then this node contains the time the local
+ management subsystem re-initialized itself.";
+ }
+
+ leaf in-octets {
+ type yang:counter64;
+ description
+ "The total number of octets received on the interface,
+ including framing characters.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCInOctets";
+ }
+
+ leaf in-unicast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were not addressed to a
+ multicast or broadcast address at this sub-layer.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts";
+ }
+
+
+
+
+ leaf in-broadcast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were addressed to a broadcast
+ address at this sub-layer.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCInBroadcastPkts";
+ }
+
+ leaf in-multicast-pkts {
+ type yang:counter64;
+ description
+ "The number of packets, delivered by this sub-layer to a
+ higher (sub-)layer, that were addressed to a multicast
+ address at this sub-layer. For a MAC-layer protocol,
+ this includes both Group and Functional addresses.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCInMulticastPkts";
+ }
+
+ leaf in-discards {
+ type yang:counter32;
+ description
+ "The number of inbound packets that were chosen to be
+ discarded even though no errors had been detected to
+ prevent their being deliverable to a higher-layer
+ protocol. One possible reason for discarding such a
+ packet could be to free up buffer space.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+
+
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInDiscards";
+ }
+
+ leaf in-errors {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of inbound
+ packets that contained errors preventing them from being
+ deliverable to a higher-layer protocol. For character-
+ oriented or fixed-length interfaces, the number of
+ inbound transmission units that contained errors
+ preventing them from being deliverable to a higher-layer
+ protocol.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInErrors";
+ }
+
+ leaf in-unknown-protos {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of packets
+ received via the interface that were discarded because
+ of an unknown or unsupported protocol. For
+ character-oriented or fixed-length interfaces that
+ support protocol multiplexing, the number of
+ transmission units received via the interface that were
+ discarded because of an unknown or unsupported protocol.
+ For any interface that does not support protocol
+ multiplexing, this counter is not present.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos";
+ }
+
+
+
+
+
+ leaf out-octets {
+ type yang:counter64;
+ description
+ "The total number of octets transmitted out of the
+ interface, including framing characters.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCOutOctets";
+ }
+
+ leaf out-unicast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were not addressed
+ to a multicast or broadcast address at this sub-layer,
+ including those that were discarded or not sent.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts";
+ }
+
+ leaf out-broadcast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were addressed to a
+ broadcast address at this sub-layer, including those
+ that were discarded or not sent.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCOutBroadcastPkts";
+ }
+
+
+ leaf out-multicast-pkts {
+ type yang:counter64;
+ description
+ "The total number of packets that higher-level protocols
+ requested be transmitted, and that were addressed to a
+ multicast address at this sub-layer, including those
+ that were discarded or not sent. For a MAC-layer
+ protocol, this includes both Group and Functional
+ addresses.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB -
+ ifHCOutMulticastPkts";
+ }
+
+ leaf out-discards {
+ type yang:counter32;
+ description
+ "The number of outbound packets that were chosen to be
+ discarded even though no errors had been detected to
+ prevent their being transmitted. One possible reason
+ for discarding such a packet could be to free up buffer
+ space.
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOutDiscards";
+ }
+
+ leaf out-errors {
+ type yang:counter32;
+ description
+ "For packet-oriented interfaces, the number of outbound
+ packets that could not be transmitted because of errors.
+ For character-oriented or fixed-length interfaces, the
+ number of outbound transmission units that could not be
+ transmitted because of errors.
+
+
+
+
+ Discontinuities in the value of this counter can occur
+ at re-initialization of the management system, and at
+ other times as indicated by the value of
+ 'discontinuity-time'.";
+ reference
+ "RFC 2863: The Interfaces Group MIB - ifOutErrors";
+ }
+ }
+ }
+ }
+}
diff --git a/tests/yanglint/modules/ietf-ip.yang b/tests/yanglint/modules/ietf-ip.yang
new file mode 100644
index 0000000..1499120
--- /dev/null
+++ b/tests/yanglint/modules/ietf-ip.yang
@@ -0,0 +1,758 @@
+module ietf-ip {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-ip";
+ prefix ip;
+
+ import ietf-interfaces {
+ prefix if;
+ }
+ import ietf-inet-types {
+ prefix inet;
+ }
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETMOD (NETCONF Data Modeling Language) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netmod/>
+ WG List: <mailto:netmod@ietf.org>
+
+ WG Chair: Thomas Nadeau
+ <mailto:tnadeau@lucidvision.com>
+
+ WG Chair: Juergen Schoenwaelder
+ <mailto:j.schoenwaelder@jacobs-university.de>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+
+
+
+
+
+
+
+
+
+ description
+ "This module contains a collection of YANG definitions for
+ configuring IP implementations.
+
+ Copyright (c) 2014 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 7277; see
+ the RFC itself for full legal notices.";
+
+ revision 2014-06-16 {
+ description
+ "Initial revision.";
+ reference
+ "RFC 7277: A YANG Data Model for IP Management";
+ }
+
+ /*
+
+ * Features
+ */
+
+ feature ipv4-non-contiguous-netmasks {
+ description
+ "Indicates support for configuring non-contiguous
+ subnet masks.";
+ }
+
+ feature ipv6-privacy-autoconf {
+ description
+ "Indicates support for Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6";
+ }
+
+
+
+
+
+ /*
+ * Typedefs
+ */
+
+ typedef ip-address-origin {
+ type enumeration {
+ enum other {
+ description
+ "None of the following.";
+ }
+ enum static {
+ description
+ "Indicates that the address has been statically
+ configured - for example, using NETCONF or a Command Line
+ Interface.";
+ }
+ enum dhcp {
+ description
+ "Indicates an address that has been assigned to this
+ system by a DHCP server.";
+ }
+ enum link-layer {
+ description
+ "Indicates an address created by IPv6 stateless
+ autoconfiguration that embeds a link-layer address in its
+ interface identifier.";
+ }
+ enum random {
+ description
+ "Indicates an address chosen by the system at
+
+ random, e.g., an IPv4 address within 169.254/16, an
+ RFC 4941 temporary address, or an RFC 7217 semantically
+ opaque address.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ RFC 7217: A Method for Generating Semantically Opaque
+ Interface Identifiers with IPv6 Stateless
+ Address Autoconfiguration (SLAAC)";
+ }
+ }
+ description
+ "The origin of an address.";
+ }
+
+
+
+ typedef neighbor-origin {
+ type enumeration {
+ enum other {
+ description
+ "None of the following.";
+ }
+ enum static {
+ description
+ "Indicates that the mapping has been statically
+ configured - for example, using NETCONF or a Command Line
+ Interface.";
+ }
+ enum dynamic {
+ description
+ "Indicates that the mapping has been dynamically resolved
+ using, e.g., IPv4 ARP or the IPv6 Neighbor Discovery
+ protocol.";
+ }
+ }
+ description
+ "The origin of a neighbor entry.";
+ }
+
+ /*
+ * Configuration data nodes
+ */
+
+ augment "/if:interfaces/if:interface" {
+ description
+ "Parameters for configuring IP on interfaces.
+
+ If an interface is not capable of running IP, the server
+ must not allow the client to configure these parameters.";
+
+ container ipv4 {
+ presence
+ "Enables IPv4 unless the 'enabled' leaf
+ (which defaults to 'true') is set to 'false'";
+ description
+ "Parameters for the IPv4 address family.";
+
+
+
+
+
+
+
+
+ leaf enabled {
+ type boolean;
+ default true;
+ description
+ "Controls whether IPv4 is enabled or disabled on this
+ interface. When IPv4 is enabled, this interface is
+ connected to an IPv4 stack, and the interface can send
+ and receive IPv4 packets.";
+ }
+ leaf forwarding {
+ type boolean;
+ default false;
+ description
+ "Controls IPv4 packet forwarding of datagrams received by,
+ but not addressed to, this interface. IPv4 routers
+ forward datagrams. IPv4 hosts do not (except those
+ source-routed via the host).";
+ }
+ leaf mtu {
+ type uint16 {
+ range "68..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv4 packet that the
+ interface will send and receive.
+
+ The server may restrict the allowed values for this leaf,
+ depending on the interface's type.
+
+ If this leaf is not configured, the operationally used MTU
+ depends on the interface's type.";
+ reference
+ "RFC 791: Internet Protocol";
+ }
+ list address {
+ key "ip";
+ description
+ "The list of configured IPv4 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address on the interface.";
+ }
+
+
+
+ choice subnet {
+ mandatory true;
+ description
+ "The subnet can be specified as a prefix-length, or,
+ if the server supports non-contiguous netmasks, as
+ a netmask.";
+ leaf prefix-length {
+ type uint8 {
+ range "0..32";
+ }
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf netmask {
+ if-feature ipv4-non-contiguous-netmasks;
+ type yang:dotted-quad;
+ description
+ "The subnet specified as a netmask.";
+ }
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv4 addresses to
+ link-layer addresses.
+
+ Entries in this list are used as static entries in the
+ ARP Cache.";
+ reference
+ "RFC 826: An Ethernet Address Resolution Protocol";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ mandatory true;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ }
+
+ }
+
+
+ container ipv6 {
+ presence
+ "Enables IPv6 unless the 'enabled' leaf
+ (which defaults to 'true') is set to 'false'";
+ description
+ "Parameters for the IPv6 address family.";
+
+ leaf enabled {
+ type boolean;
+ default true;
+ description
+ "Controls whether IPv6 is enabled or disabled on this
+ interface. When IPv6 is enabled, this interface is
+ connected to an IPv6 stack, and the interface can send
+ and receive IPv6 packets.";
+ }
+ leaf forwarding {
+ type boolean;
+ default false;
+ description
+ "Controls IPv6 packet forwarding of datagrams received by,
+ but not addressed to, this interface. IPv6 routers
+ forward datagrams. IPv6 hosts do not (except those
+ source-routed via the host).";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 6.2.1, IsRouter";
+ }
+ leaf mtu {
+ type uint32 {
+ range "1280..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv6 packet that the
+ interface will send and receive.
+
+ The server may restrict the allowed values for this leaf,
+ depending on the interface's type.
+
+ If this leaf is not configured, the operationally used MTU
+ depends on the interface's type.";
+ reference
+ "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ Section 5";
+ }
+
+
+ list address {
+ key "ip";
+ description
+ "The list of configured IPv6 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address on the interface.";
+ }
+ leaf prefix-length {
+ type uint8 {
+ range "0..128";
+ }
+ mandatory true;
+ description
+ "The length of the subnet prefix.";
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv6 addresses to
+ link-layer addresses.
+
+ Entries in this list are used as static entries in the
+ Neighbor Cache.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ mandatory true;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ }
+
+
+
+
+
+
+ leaf dup-addr-detect-transmits {
+ type uint32;
+ default 1;
+ description
+ "The number of consecutive Neighbor Solicitation messages
+ sent while performing Duplicate Address Detection on a
+ tentative address. A value of zero indicates that
+ Duplicate Address Detection is not performed on
+ tentative addresses. A value of one indicates a single
+ transmission with no follow-up retransmissions.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration";
+ }
+ container autoconf {
+ description
+ "Parameters to control the autoconfiguration of IPv6
+ addresses, as described in RFC 4862.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration";
+
+ leaf create-global-addresses {
+ type boolean;
+ default true;
+ description
+ "If enabled, the host creates global addresses as
+ described in RFC 4862.";
+ reference
+ "RFC 4862: IPv6 Stateless Address Autoconfiguration
+ Section 5.5";
+ }
+ leaf create-temporary-addresses {
+ if-feature ipv6-privacy-autoconf;
+ type boolean;
+ default false;
+ description
+ "If enabled, the host creates temporary addresses as
+ described in RFC 4941.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6";
+ }
+
+
+
+
+
+
+
+ leaf temporary-valid-lifetime {
+ if-feature ipv6-privacy-autoconf;
+ type uint32;
+ units "seconds";
+ default 604800;
+ description
+ "The time period during which the temporary address
+ is valid.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ - TEMP_VALID_LIFETIME";
+ }
+ leaf temporary-preferred-lifetime {
+ if-feature ipv6-privacy-autoconf;
+ type uint32;
+ units "seconds";
+ default 86400;
+ description
+ "The time period during which the temporary address is
+ preferred.";
+ reference
+ "RFC 4941: Privacy Extensions for Stateless Address
+ Autoconfiguration in IPv6
+ - TEMP_PREFERRED_LIFETIME";
+ }
+ }
+ }
+ }
+
+ /*
+ * Operational state data nodes
+ */
+
+ augment "/if:interfaces-state/if:interface" {
+ description
+ "Data nodes for the operational state of IP on interfaces.";
+
+ container ipv4 {
+ presence "Present if IPv4 is enabled on this interface";
+ config false;
+ description
+ "Interface-specific parameters for the IPv4 address family.";
+
+
+
+
+
+ leaf forwarding {
+ type boolean;
+ description
+ "Indicates whether IPv4 packet forwarding is enabled or
+ disabled on this interface.";
+ }
+ leaf mtu {
+ type uint16 {
+ range "68..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv4 packet that the
+ interface will send and receive.";
+ reference
+ "RFC 791: Internet Protocol";
+ }
+ list address {
+ key "ip";
+ description
+ "The list of IPv4 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address on the interface.";
+ }
+ choice subnet {
+ description
+ "The subnet can be specified as a prefix-length, or,
+ if the server supports non-contiguous netmasks, as
+ a netmask.";
+ leaf prefix-length {
+ type uint8 {
+ range "0..32";
+ }
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf netmask {
+ if-feature ipv4-non-contiguous-netmasks;
+ type yang:dotted-quad;
+ description
+ "The subnet specified as a netmask.";
+ }
+ }
+
+
+ leaf origin {
+ type ip-address-origin;
+ description
+ "The origin of this address.";
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv4 addresses to
+ link-layer addresses.
+
+ This list represents the ARP Cache.";
+ reference
+ "RFC 826: An Ethernet Address Resolution Protocol";
+
+ leaf ip {
+ type inet:ipv4-address-no-zone;
+ description
+ "The IPv4 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ leaf origin {
+ type neighbor-origin;
+ description
+ "The origin of this neighbor entry.";
+ }
+ }
+
+ }
+
+ container ipv6 {
+ presence "Present if IPv6 is enabled on this interface";
+ config false;
+ description
+ "Parameters for the IPv6 address family.";
+
+
+
+
+
+
+
+
+ leaf forwarding {
+ type boolean;
+ default false;
+ description
+ "Indicates whether IPv6 packet forwarding is enabled or
+ disabled on this interface.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 6.2.1, IsRouter";
+ }
+ leaf mtu {
+ type uint32 {
+ range "1280..max";
+ }
+ units octets;
+ description
+ "The size, in octets, of the largest IPv6 packet that the
+ interface will send and receive.";
+ reference
+ "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
+ Section 5";
+ }
+ list address {
+ key "ip";
+ description
+ "The list of IPv6 addresses on the interface.";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address on the interface.";
+ }
+ leaf prefix-length {
+ type uint8 {
+ range "0..128";
+ }
+ mandatory true;
+ description
+ "The length of the subnet prefix.";
+ }
+ leaf origin {
+ type ip-address-origin;
+ description
+ "The origin of this address.";
+ }
+
+
+
+ leaf status {
+ type enumeration {
+ enum preferred {
+ description
+ "This is a valid address that can appear as the
+ destination or source address of a packet.";
+ }
+ enum deprecated {
+ description
+ "This is a valid but deprecated address that should
+ no longer be used as a source address in new
+ communications, but packets addressed to such an
+ address are processed as expected.";
+ }
+ enum invalid {
+ description
+ "This isn't a valid address, and it shouldn't appear
+ as the destination or source address of a packet.";
+ }
+ enum inaccessible {
+ description
+ "The address is not accessible because the interface
+ to which this address is assigned is not
+ operational.";
+ }
+ enum unknown {
+ description
+ "The status cannot be determined for some reason.";
+ }
+ enum tentative {
+ description
+ "The uniqueness of the address on the link is being
+ verified. Addresses in this state should not be
+ used for general communication and should only be
+ used to determine the uniqueness of the address.";
+ }
+ enum duplicate {
+ description
+ "The address has been determined to be non-unique on
+ the link and so must not be used.";
+ }
+
+
+
+
+
+
+
+ enum optimistic {
+ description
+ "The address is available for use, subject to
+ restrictions, while its uniqueness on a link is
+ being verified.";
+ }
+ }
+ description
+ "The status of an address. Most of the states correspond
+ to states from the IPv6 Stateless Address
+ Autoconfiguration protocol.";
+ reference
+ "RFC 4293: Management Information Base for the
+ Internet Protocol (IP)
+ - IpAddressStatusTC
+ RFC 4862: IPv6 Stateless Address Autoconfiguration";
+ }
+ }
+ list neighbor {
+ key "ip";
+ description
+ "A list of mappings from IPv6 addresses to
+ link-layer addresses.
+
+ This list represents the Neighbor Cache.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
+
+ leaf ip {
+ type inet:ipv6-address-no-zone;
+ description
+ "The IPv6 address of the neighbor node.";
+ }
+ leaf link-layer-address {
+ type yang:phys-address;
+ description
+ "The link-layer address of the neighbor node.";
+ }
+ leaf origin {
+ type neighbor-origin;
+ description
+ "The origin of this neighbor entry.";
+ }
+ leaf is-router {
+ type empty;
+ description
+ "Indicates that the neighbor node acts as a router.";
+ }
+ leaf state {
+ type enumeration {
+ enum incomplete {
+ description
+ "Address resolution is in progress, and the link-layer
+ address of the neighbor has not yet been
+ determined.";
+ }
+ enum reachable {
+ description
+ "Roughly speaking, the neighbor is known to have been
+ reachable recently (within tens of seconds ago).";
+ }
+ enum stale {
+ description
+ "The neighbor is no longer known to be reachable, but
+ until traffic is sent to the neighbor no attempt
+ should be made to verify its reachability.";
+ }
+ enum delay {
+ description
+ "The neighbor is no longer known to be reachable, and
+ traffic has recently been sent to the neighbor.
+ Rather than probe the neighbor immediately, however,
+ delay sending probes for a short while in order to
+ give upper-layer protocols a chance to provide
+ reachability confirmation.";
+ }
+ enum probe {
+ description
+ "The neighbor is no longer known to be reachable, and
+ unicast Neighbor Solicitation probes are being sent
+ to verify reachability.";
+ }
+ }
+ description
+ "The Neighbor Unreachability Detection state of this
+ entry.";
+ reference
+ "RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
+ Section 7.3.2";
+ }
+ }
+ }
+ }
+}
diff --git a/tests/yanglint/modules/ietf-netconf-acm.yang b/tests/yanglint/modules/ietf-netconf-acm.yang
new file mode 100644
index 0000000..d372fa0
--- /dev/null
+++ b/tests/yanglint/modules/ietf-netconf-acm.yang
@@ -0,0 +1,411 @@
+module ietf-netconf-acm {
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
+ prefix nacm;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+
+ WG Chair: Mehmet Ersue
+ <mailto:mehmet.ersue@nsn.com>
+
+ WG Chair: Bert Wijnen
+ <mailto:bertietf@bwijnen.net>
+
+ Editor: Andy Bierman
+ <mailto:andy@yumaworks.com>
+
+ Editor: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+ description
+ "NETCONF Access Control Model.
+
+ Copyright (c) 2012 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD
+ License set forth in Section 4.c of the IETF Trust's
+ Legal Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6536; see
+ the RFC itself for full legal notices.";
+
+ revision 2012-02-22 {
+ description
+ "Initial version";
+ reference
+ "RFC 6536: Network Configuration Protocol (NETCONF)
+ Access Control Model";
+ }
+
+ extension default-deny-write {
+ description
+ "Used to indicate that the data model node
+ represents a sensitive security system parameter.
+
+ If present, and the NACM module is enabled (i.e.,
+ /nacm/enable-nacm object equals 'true'), the NETCONF server
+ will only allow the designated 'recovery session' to have
+ write access to the node. An explicit access control rule is
+ required for all other users.
+
+ The 'default-deny-write' extension MAY appear within a data
+ definition statement. It is ignored otherwise.";
+ }
+
+ extension default-deny-all {
+ description
+ "Used to indicate that the data model node
+ controls a very sensitive security system parameter.
+
+ If present, and the NACM module is enabled (i.e.,
+ /nacm/enable-nacm object equals 'true'), the NETCONF server
+ will only allow the designated 'recovery session' to have
+ read, write, or execute access to the node. An explicit
+ access control rule is required for all other users.
+
+ The 'default-deny-all' extension MAY appear within a data
+ definition statement, 'rpc' statement, or 'notification'
+ statement. It is ignored otherwise.";
+ }
+
+ typedef user-name-type {
+ type string {
+ length "1..max";
+ }
+ description
+ "General Purpose Username string.";
+ }
+
+ typedef matchall-string-type {
+ type string {
+ pattern "\\*";
+ }
+ description
+ "The string containing a single asterisk '*' is used
+ to conceptually represent all possible values
+ for the particular leaf using this data type.";
+ }
+
+ typedef access-operations-type {
+ type bits {
+ bit create {
+ description
+ "Any protocol operation that creates a
+ new data node.";
+ }
+ bit read {
+ description
+ "Any protocol operation or notification that
+ returns the value of a data node.";
+ }
+ bit update {
+ description
+ "Any protocol operation that alters an existing
+ data node.";
+ }
+ bit delete {
+ description
+ "Any protocol operation that removes a data node.";
+ }
+ bit exec {
+ description
+ "Execution access to the specified protocol operation.";
+ }
+ }
+ description
+ "NETCONF Access Operation.";
+ }
+
+ typedef group-name-type {
+ type string {
+ length "1..max";
+ pattern "[^\\*].*";
+ }
+ description
+ "Name of administrative group to which
+ users can be assigned.";
+ }
+
+ typedef action-type {
+ type enumeration {
+ enum "permit" {
+ description
+ "Requested action is permitted.";
+ }
+ enum "deny" {
+ description
+ "Requested action is denied.";
+ }
+ }
+ description
+ "Action taken by the server when a particular
+ rule matches.";
+ }
+
+ typedef node-instance-identifier {
+ type yang:xpath1.0;
+ description
+ "Path expression used to represent a special
+ data node instance identifier string.
+
+ A node-instance-identifier value is an
+ unrestricted YANG instance-identifier expression.
+ All the same rules as an instance-identifier apply
+ except predicates for keys are optional. If a key
+ predicate is missing, then the node-instance-identifier
+ represents all possible server instances for that key.
+
+ This XPath expression is evaluated in the following context:
+
+ o The set of namespace declarations are those in scope on
+ the leaf element where this type is used.
+
+ o The set of variable bindings contains one variable,
+ 'USER', which contains the name of the user of the current
+ session.
+
+ o The function library is the core function library, but
+ note that due to the syntax restrictions of an
+ instance-identifier, no functions are allowed.
+
+ o The context node is the root node in the data tree.";
+ }
+
+ container nacm {
+ nacm:default-deny-all;
+ description
+ "Parameters for NETCONF Access Control Model.";
+ leaf enable-nacm {
+ type boolean;
+ default "true";
+ description
+ "Enables or disables all NETCONF access control
+ enforcement. If 'true', then enforcement
+ is enabled. If 'false', then enforcement
+ is disabled.";
+ }
+ leaf read-default {
+ type action-type;
+ default "permit";
+ description
+ "Controls whether read access is granted if
+ no appropriate rule is found for a
+ particular read request.";
+ }
+ leaf write-default {
+ type action-type;
+ default "deny";
+ description
+ "Controls whether create, update, or delete access
+ is granted if no appropriate rule is found for a
+ particular write request.";
+ }
+ leaf exec-default {
+ type action-type;
+ default "permit";
+ description
+ "Controls whether exec access is granted if no appropriate
+ rule is found for a particular protocol operation request.";
+ }
+ leaf enable-external-groups {
+ type boolean;
+ default "true";
+ description
+ "Controls whether the server uses the groups reported by the
+ NETCONF transport layer when it assigns the user to a set of
+ NACM groups. If this leaf has the value 'false', any group
+ names reported by the transport layer are ignored by the
+ server.";
+ }
+ leaf denied-operations {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that a
+ protocol operation request was denied.";
+ }
+ leaf denied-data-writes {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that a
+ protocol operation request to alter
+ a configuration datastore was denied.";
+ }
+ leaf denied-notifications {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that
+ a notification was dropped for a subscription because
+ access to the event type was denied.";
+ }
+ container groups {
+ description
+ "NETCONF Access Control Groups.";
+ list group {
+ key "name";
+ description
+ "One NACM Group Entry. This list will only contain
+ configured entries, not any entries learned from
+ any transport protocols.";
+ leaf name {
+ type group-name-type;
+ description
+ "Group name associated with this entry.";
+ }
+ leaf-list user-name {
+ type user-name-type;
+ description
+ "Each entry identifies the username of
+ a member of the group associated with
+ this entry.";
+ }
+ }
+ }
+ list rule-list {
+ key "name";
+ ordered-by user;
+ description
+ "An ordered collection of access control rules.";
+ leaf name {
+ type string {
+ length "1..max";
+ }
+ description
+ "Arbitrary name assigned to the rule-list.";
+ }
+ leaf-list group {
+ type union {
+ type matchall-string-type;
+ type group-name-type;
+ }
+ description
+ "List of administrative groups that will be
+ assigned the associated access rights
+ defined by the 'rule' list.
+
+ The string '*' indicates that all groups apply to the
+ entry.";
+ }
+ list rule {
+ key "name";
+ ordered-by user;
+ description
+ "One access control rule.
+
+ Rules are processed in user-defined order until a match is
+ found. A rule matches if 'module-name', 'rule-type', and
+ 'access-operations' match the request. If a rule
+ matches, the 'action' leaf determines if access is granted
+ or not.";
+ leaf name {
+ type string {
+ length "1..max";
+ }
+ description
+ "Arbitrary name assigned to the rule.";
+ }
+ leaf module-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ default "*";
+ description
+ "Name of the module associated with this rule.
+
+ This leaf matches if it has the value '*' or if the
+ object being accessed is defined in the module with the
+ specified module name.";
+ }
+ choice rule-type {
+ description
+ "This choice matches if all leafs present in the rule
+ match the request. If no leafs are present, the
+ choice matches all requests.";
+ case protocol-operation {
+ leaf rpc-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ description
+ "This leaf matches if it has the value '*' or if
+ its value equals the requested protocol operation
+ name.";
+ }
+ }
+ case notification {
+ leaf notification-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ description
+ "This leaf matches if it has the value '*' or if its
+ value equals the requested notification name.";
+ }
+ }
+ case data-node {
+ leaf path {
+ type node-instance-identifier;
+ mandatory true;
+ description
+ "Data Node Instance Identifier associated with the
+ data node controlled by this rule.
+
+ Configuration data or state data instance
+ identifiers start with a top-level data node. A
+ complete instance identifier is required for this
+ type of path value.
+
+ The special value '/' refers to all possible
+ datastore contents.";
+ }
+ }
+ }
+ leaf access-operations {
+ type union {
+ type matchall-string-type;
+ type access-operations-type;
+ }
+ default "*";
+ description
+ "Access operations associated with this rule.
+
+ This leaf matches if it has the value '*' or if the
+ bit corresponding to the requested operation is set.";
+ }
+ leaf action {
+ type action-type;
+ mandatory true;
+ description
+ "The access control action associated with the
+ rule. If a rule is determined to match a
+ particular request, then this object is used
+ to determine whether to permit or deny the
+ request.";
+ }
+ leaf comment {
+ type string;
+ description
+ "A textual description of the access rule.";
+ }
+ }
+ }
+ }
+}
diff --git a/tests/yanglint/modules/ietf-netconf-with-defaults@2011-06-01.yang b/tests/yanglint/modules/ietf-netconf-with-defaults@2011-06-01.yang
new file mode 100644
index 0000000..e19d2b3
--- /dev/null
+++ b/tests/yanglint/modules/ietf-netconf-with-defaults@2011-06-01.yang
@@ -0,0 +1,140 @@
+module ietf-netconf-with-defaults {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-with-defaults";
+
+ prefix ncwd;
+
+ import ietf-netconf { prefix nc; }
+
+ organization
+ "IETF NETCONF (Network Configuration Protocol) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+
+ WG List: <netconf@ietf.org>
+
+ WG Chair: Bert Wijnen
+ <bertietf@bwijnen.net>
+
+ WG Chair: Mehmet Ersue
+ <mehmet.ersue@nsn.com>
+
+ Editor: Andy Bierman
+ <andy.bierman@brocade.com>
+
+ Editor: Balazs Lengyel
+ <balazs.lengyel@ericsson.com>";
+
+ description
+ "This module defines an extension to the NETCONF protocol
+ that allows the NETCONF client to control how default
+ values are handled by the server in particular NETCONF
+ operations.
+
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ the document authors. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6243; see
+ the RFC itself for full legal notices.";
+
+ revision 2011-06-01 {
+ description
+ "Initial version.";
+ reference
+ "RFC 6243: With-defaults Capability for NETCONF";
+ }
+
+ typedef with-defaults-mode {
+ description
+ "Possible modes to report default data.";
+ reference
+ "RFC 6243; Section 3.";
+ type enumeration {
+ enum report-all {
+ description
+ "All default data is reported.";
+ reference
+ "RFC 6243; Section 3.1";
+ }
+ enum report-all-tagged {
+ description
+ "All default data is reported.
+ Any nodes considered to be default data
+ will contain a 'default' XML attribute,
+ set to 'true' or '1'.";
+ reference
+ "RFC 6243; Section 3.4";
+ }
+ enum trim {
+ description
+ "Values are not reported if they contain the default.";
+ reference
+ "RFC 6243; Section 3.2";
+ }
+ enum explicit {
+ description
+ "Report values that contain the definition of
+ explicitly set data.";
+ reference
+ "RFC 6243; Section 3.3";
+ }
+ }
+ }
+
+ grouping with-defaults-parameters {
+ description
+ "Contains the <with-defaults> parameter for control
+ of defaults in NETCONF retrieval operations.";
+
+ leaf with-defaults {
+ description
+ "The explicit defaults processing mode requested.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ type with-defaults-mode;
+ }
+ }
+
+ // extending the get-config operation
+ augment /nc:get-config/nc:input {
+ description
+ "Adds the <with-defaults> parameter to the
+ input of the NETCONF <get-config> operation.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ uses with-defaults-parameters;
+ }
+
+ // extending the get operation
+ augment /nc:get/nc:input {
+ description
+ "Adds the <with-defaults> parameter to
+ the input of the NETCONF <get> operation.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ uses with-defaults-parameters;
+ }
+
+ // extending the copy-config operation
+ augment /nc:copy-config/nc:input {
+ description
+ "Adds the <with-defaults> parameter to
+ the input of the NETCONF <copy-config> operation.";
+ reference
+ "RFC 6243; Section 4.5.1";
+
+ uses with-defaults-parameters;
+ }
+
+}
diff --git a/tests/yanglint/modules/ietf-netconf@2011-06-01.yang b/tests/yanglint/modules/ietf-netconf@2011-06-01.yang
new file mode 100644
index 0000000..3053db2
--- /dev/null
+++ b/tests/yanglint/modules/ietf-netconf@2011-06-01.yang
@@ -0,0 +1,934 @@
+module ietf-netconf {
+
+ // the namespace for NETCONF XML definitions is unchanged
+ // from RFC 4741, which this document replaces
+ namespace "urn:ietf:params:xml:ns:netconf:base:1.0";
+
+ prefix nc;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+
+ import ietf-netconf-acm { prefix nacm; }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <http://tools.ietf.org/wg/netconf/>
+ WG List: <netconf@ietf.org>
+
+ WG Chair: Bert Wijnen
+ <bertietf@bwijnen.net>
+
+ WG Chair: Mehmet Ersue
+ <mehmet.ersue@nsn.com>
+
+ Editor: Martin Bjorklund
+ <mbj@tail-f.com>
+
+ Editor: Juergen Schoenwaelder
+ <j.schoenwaelder@jacobs-university.de>
+
+ Editor: Andy Bierman
+ <andy.bierman@brocade.com>";
+ description
+ "NETCONF Protocol Data Types and Protocol Operations.
+
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ the document authors. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD License
+ set forth in Section 4.c of the IETF Trust's Legal Provisions
+ Relating to IETF Documents
+ (http://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 6241; see
+ the RFC itself for full legal notices.";
+
+ revision 2011-06-01 {
+ description
+ "Initial revision;
+ 2013-09-29: Updated to include NACM attributes,
+ as specified in RFC 6536: sec 3.2.5 and 3.2.8";
+ reference
+ "RFC 6241: Network Configuration Protocol";
+ }
+
+ extension get-filter-element-attributes {
+ description
+ "If this extension is present within an 'anyxml'
+ statement named 'filter', which must be conceptually
+ defined within the RPC input section for the <get>
+ and <get-config> protocol operations, then the
+ following unqualified XML attribute is supported
+ within the <filter> element, within a <get> or
+ <get-config> protocol operation:
+
+ type : optional attribute with allowed
+ value strings 'subtree' and 'xpath'.
+ If missing, the default value is 'subtree'.
+
+ If the 'xpath' feature is supported, then the
+ following unqualified XML attribute is
+ also supported:
+
+ select: optional attribute containing a
+ string representing an XPath expression.
+ The 'type' attribute must be equal to 'xpath'
+ if this attribute is present.";
+ }
+
+ // NETCONF capabilities defined as features
+ feature writable-running {
+ description
+ "NETCONF :writable-running capability;
+ If the server advertises the :writable-running
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.2";
+ }
+
+ feature candidate {
+ description
+ "NETCONF :candidate capability;
+ If the server advertises the :candidate
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.3";
+ }
+
+ feature confirmed-commit {
+ if-feature candidate;
+ description
+ "NETCONF :confirmed-commit:1.1 capability;
+ If the server advertises the :confirmed-commit:1.1
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+
+ reference "RFC 6241, Section 8.4";
+ }
+
+ feature rollback-on-error {
+ description
+ "NETCONF :rollback-on-error capability;
+ If the server advertises the :rollback-on-error
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.5";
+ }
+
+ feature validate {
+ description
+ "NETCONF :validate:1.1 capability;
+ If the server advertises the :validate:1.1
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.6";
+ }
+
+ feature startup {
+ description
+ "NETCONF :startup capability;
+ If the server advertises the :startup
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.7";
+ }
+
+ feature url {
+ description
+ "NETCONF :url capability;
+ If the server advertises the :url
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.8";
+ }
+
+ feature xpath {
+ description
+ "NETCONF :xpath capability;
+ If the server advertises the :xpath
+ capability for a session, then this feature must
+ also be enabled for that session. Otherwise,
+ this feature must not be enabled.";
+ reference "RFC 6241, Section 8.9";
+ }
+
+ // NETCONF Simple Types
+
+ typedef session-id-type {
+ type uint32 {
+ range "1..max";
+ }
+ description
+ "NETCONF Session Id";
+ }
+
+ typedef session-id-or-zero-type {
+ type uint32;
+ description
+ "NETCONF Session Id or Zero to indicate none";
+ }
+ typedef error-tag-type {
+ type enumeration {
+ enum in-use {
+ description
+ "The request requires a resource that
+ already is in use.";
+ }
+ enum invalid-value {
+ description
+ "The request specifies an unacceptable value for one
+ or more parameters.";
+ }
+ enum too-big {
+ description
+ "The request or response (that would be generated) is
+ too large for the implementation to handle.";
+ }
+ enum missing-attribute {
+ description
+ "An expected attribute is missing.";
+ }
+ enum bad-attribute {
+ description
+ "An attribute value is not correct; e.g., wrong type,
+ out of range, pattern mismatch.";
+ }
+ enum unknown-attribute {
+ description
+ "An unexpected attribute is present.";
+ }
+ enum missing-element {
+ description
+ "An expected element is missing.";
+ }
+ enum bad-element {
+ description
+ "An element value is not correct; e.g., wrong type,
+ out of range, pattern mismatch.";
+ }
+ enum unknown-element {
+ description
+ "An unexpected element is present.";
+ }
+ enum unknown-namespace {
+ description
+ "An unexpected namespace is present.";
+ }
+ enum access-denied {
+ description
+ "Access to the requested protocol operation or
+ data model is denied because authorization failed.";
+ }
+ enum lock-denied {
+ description
+ "Access to the requested lock is denied because the
+ lock is currently held by another entity.";
+ }
+ enum resource-denied {
+ description
+ "Request could not be completed because of
+ insufficient resources.";
+ }
+ enum rollback-failed {
+ description
+ "Request to roll back some configuration change (via
+ rollback-on-error or <discard-changes> operations)
+ was not completed for some reason.";
+
+ }
+ enum data-exists {
+ description
+ "Request could not be completed because the relevant
+ data model content already exists. For example,
+ a 'create' operation was attempted on data that
+ already exists.";
+ }
+ enum data-missing {
+ description
+ "Request could not be completed because the relevant
+ data model content does not exist. For example,
+ a 'delete' operation was attempted on
+ data that does not exist.";
+ }
+ enum operation-not-supported {
+ description
+ "Request could not be completed because the requested
+ operation is not supported by this implementation.";
+ }
+ enum operation-failed {
+ description
+ "Request could not be completed because the requested
+ operation failed for some reason not covered by
+ any other error condition.";
+ }
+ enum partial-operation {
+ description
+ "This error-tag is obsolete, and SHOULD NOT be sent
+ by servers conforming to this document.";
+ }
+ enum malformed-message {
+ description
+ "A message could not be handled because it failed to
+ be parsed correctly. For example, the message is not
+ well-formed XML or it uses an invalid character set.";
+ }
+ }
+ description "NETCONF Error Tag";
+ reference "RFC 6241, Appendix A";
+ }
+
+ typedef error-severity-type {
+ type enumeration {
+ enum error {
+ description "Error severity";
+ }
+ enum warning {
+ description "Warning severity";
+ }
+ }
+ description "NETCONF Error Severity";
+ reference "RFC 6241, Section 4.3";
+ }
+
+ typedef edit-operation-type {
+ type enumeration {
+ enum merge {
+ description
+ "The configuration data identified by the
+ element containing this attribute is merged
+ with the configuration at the corresponding
+ level in the configuration datastore identified
+ by the target parameter.";
+ }
+ enum replace {
+ description
+ "The configuration data identified by the element
+ containing this attribute replaces any related
+ configuration in the configuration datastore
+ identified by the target parameter. If no such
+ configuration data exists in the configuration
+ datastore, it is created. Unlike a
+ <copy-config> operation, which replaces the
+ entire target configuration, only the configuration
+ actually present in the config parameter is affected.";
+ }
+ enum create {
+ description
+ "The configuration data identified by the element
+ containing this attribute is added to the
+ configuration if and only if the configuration
+ data does not already exist in the configuration
+ datastore. If the configuration data exists, an
+ <rpc-error> element is returned with an
+ <error-tag> value of 'data-exists'.";
+ }
+ enum delete {
+ description
+ "The configuration data identified by the element
+ containing this attribute is deleted from the
+ configuration if and only if the configuration
+ data currently exists in the configuration
+ datastore. If the configuration data does not
+ exist, an <rpc-error> element is returned with
+ an <error-tag> value of 'data-missing'.";
+ }
+ enum remove {
+ description
+ "The configuration data identified by the element
+ containing this attribute is deleted from the
+ configuration if the configuration
+ data currently exists in the configuration
+ datastore. If the configuration data does not
+ exist, the 'remove' operation is silently ignored
+ by the server.";
+ }
+ }
+ default "merge";
+ description "NETCONF 'operation' attribute values";
+ reference "RFC 6241, Section 7.2";
+ }
+
+ // NETCONF Standard Protocol Operations
+
+ rpc get-config {
+ description
+ "Retrieve all or part of a specified configuration.";
+
+ reference "RFC 6241, Section 7.1";
+
+ input {
+ container source {
+ description
+ "Particular configuration to retrieve.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration to retrieve.";
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.
+ This is optional-to-implement on the server because
+ not all servers will support filtering for this
+ datastore.";
+ }
+ }
+ }
+
+ anyxml filter {
+ description
+ "Subtree or XPath filter to use.";
+ nc:get-filter-element-attributes;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the source datastore subset that matched
+ the filter criteria (if any). An empty data container
+ indicates that the request did not produce any results.";
+ }
+ }
+ }
+
+ rpc edit-config {
+ description
+ "The <edit-config> operation loads all or part of a specified
+ configuration to the specified target configuration.";
+
+ reference "RFC 6241, Section 7.2";
+
+ input {
+ container target {
+ description
+ "Particular configuration to edit.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ }
+ }
+
+ leaf default-operation {
+ type enumeration {
+ enum merge {
+ description
+ "The default operation is merge.";
+ }
+ enum replace {
+ description
+ "The default operation is replace.";
+ }
+ enum none {
+ description
+ "There is no default operation.";
+ }
+ }
+ default "merge";
+ description
+ "The default operation to use.";
+ }
+
+ leaf test-option {
+ if-feature validate;
+ type enumeration {
+ enum test-then-set {
+ description
+ "The server will test and then set if no errors.";
+ }
+ enum set {
+ description
+ "The server will set without a test first.";
+ }
+
+ enum test-only {
+ description
+ "The server will only test and not set, even
+ if there are no errors.";
+ }
+ }
+ default "test-then-set";
+ description
+ "The test option to use.";
+ }
+
+ leaf error-option {
+ type enumeration {
+ enum stop-on-error {
+ description
+ "The server will stop on errors.";
+ }
+ enum continue-on-error {
+ description
+ "The server may continue on errors.";
+ }
+ enum rollback-on-error {
+ description
+ "The server will roll back on errors.
+ This value can only be used if the 'rollback-on-error'
+ feature is supported.";
+ }
+ }
+ default "stop-on-error";
+ description
+ "The error option to use.";
+ }
+
+ choice edit-content {
+ mandatory true;
+ description
+ "The content for the edit operation.";
+
+ anyxml config {
+ description
+ "Inline Config content.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "URL-based config content.";
+ }
+ }
+ }
+ }
+
+ rpc copy-config {
+ description
+ "Create or replace an entire configuration datastore with the
+ contents of another complete configuration datastore.";
+
+ reference "RFC 6241, Section 7.3";
+
+ input {
+ container target {
+ description
+ "Particular configuration to copy to.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target of the copy operation.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ if-feature writable-running;
+ type empty;
+ description
+ "The running configuration is the config target.
+ This is optional-to-implement on the server.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config target.";
+ }
+ }
+ }
+
+ container source {
+ description
+ "Particular configuration to copy from.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration source for the copy operation.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config source.";
+ }
+ anyxml config {
+ description
+ "Inline Config content: <config> element. Represents
+ an entire configuration datastore, not
+ a subset of the running datastore.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc delete-config {
+ nacm:default-deny-all;
+ description
+ "Delete a configuration datastore.";
+
+ reference "RFC 6241, Section 7.4";
+
+ input {
+ container target {
+ description
+ "Particular configuration to delete.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to delete.";
+
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc lock {
+ description
+ "The lock operation allows the client to lock the configuration
+ system of a device.";
+
+ reference "RFC 6241, Section 7.5";
+
+ input {
+ container target {
+ description
+ "Particular configuration to lock.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to lock.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config target.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc unlock {
+ description
+ "The unlock operation is used to release a configuration lock,
+ previously obtained with the 'lock' operation.";
+
+ reference "RFC 6241, Section 7.6";
+
+ input {
+ container target {
+ description
+ "Particular configuration to unlock.";
+
+ choice config-target {
+ mandatory true;
+ description
+ "The configuration target to unlock.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config target.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config target.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config target.";
+ }
+ }
+ }
+ }
+ }
+
+ rpc get {
+ description
+ "Retrieve running configuration and device state information.";
+
+ reference "RFC 6241, Section 7.7";
+
+ input {
+ anyxml filter {
+ description
+ "This parameter specifies the portion of the system
+ configuration and state data to retrieve.";
+ nc:get-filter-element-attributes;
+ }
+ }
+
+ output {
+ anyxml data {
+ description
+ "Copy of the running datastore subset and/or state
+ data that matched the filter criteria (if any).
+ An empty data container indicates that the request did not
+ produce any results.";
+ }
+ }
+ }
+
+ rpc close-session {
+ description
+ "Request graceful termination of a NETCONF session.";
+
+ reference "RFC 6241, Section 7.8";
+ }
+
+ rpc kill-session {
+ nacm:default-deny-all;
+ description
+ "Force the termination of a NETCONF session.";
+
+ reference "RFC 6241, Section 7.9";
+
+ input {
+ leaf session-id {
+ type session-id-type;
+ mandatory true;
+ description
+ "Particular session to kill.";
+ }
+ }
+ }
+
+ rpc commit {
+ if-feature candidate;
+
+ description
+ "Commit the candidate configuration as the device's new
+ current configuration.";
+
+ reference "RFC 6241, Section 8.3.4.1";
+
+ input {
+ leaf confirmed {
+ if-feature confirmed-commit;
+ type empty;
+ description
+ "Requests a confirmed commit.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf confirm-timeout {
+ if-feature confirmed-commit;
+ type uint32 {
+ range "1..max";
+ }
+ units "seconds";
+ default "600"; // 10 minutes
+ description
+ "The timeout interval for a confirmed commit.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf persist {
+ if-feature confirmed-commit;
+ type string;
+ description
+ "This parameter is used to make a confirmed commit
+ persistent. A persistent confirmed commit is not aborted
+ if the NETCONF session terminates. The only way to abort
+ a persistent confirmed commit is to let the timer expire,
+ or to use the <cancel-commit> operation.
+
+ The value of this parameter is a token that must be given
+ in the 'persist-id' parameter of <commit> or
+ <cancel-commit> operations in order to confirm or cancel
+ the persistent confirmed commit.
+
+ The token should be a random string.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ leaf persist-id {
+ if-feature confirmed-commit;
+ type string;
+ description
+ "This parameter is given in order to commit a persistent
+ confirmed commit. The value must be equal to the value
+ given in the 'persist' parameter to the <commit> operation.
+ If it does not match, the operation fails with an
+ 'invalid-value' error.";
+ reference "RFC 6241, Section 8.3.4.1";
+ }
+
+ }
+ }
+
+ rpc discard-changes {
+ if-feature candidate;
+
+ description
+ "Revert the candidate configuration to the current
+ running configuration.";
+ reference "RFC 6241, Section 8.3.4.2";
+ }
+
+ rpc cancel-commit {
+ if-feature confirmed-commit;
+ description
+ "This operation is used to cancel an ongoing confirmed commit.
+ If the confirmed commit is persistent, the parameter
+ 'persist-id' must be given, and it must match the value of the
+ 'persist' parameter.";
+ reference "RFC 6241, Section 8.4.4.1";
+
+ input {
+ leaf persist-id {
+ type string;
+ description
+ "This parameter is given in order to cancel a persistent
+ confirmed commit. The value must be equal to the value
+ given in the 'persist' parameter to the <commit> operation.
+ If it does not match, the operation fails with an
+ 'invalid-value' error.";
+ }
+ }
+ }
+
+ rpc validate {
+ if-feature validate;
+
+ description
+ "Validates the contents of the specified configuration.";
+
+ reference "RFC 6241, Section 8.6.4.1";
+
+ input {
+ container source {
+ description
+ "Particular configuration to validate.";
+
+ choice config-source {
+ mandatory true;
+ description
+ "The configuration source to validate.";
+
+ leaf candidate {
+ if-feature candidate;
+ type empty;
+ description
+ "The candidate configuration is the config source.";
+ }
+ leaf running {
+ type empty;
+ description
+ "The running configuration is the config source.";
+ }
+ leaf startup {
+ if-feature startup;
+ type empty;
+ description
+ "The startup configuration is the config source.";
+ }
+ leaf url {
+ if-feature url;
+ type inet:uri;
+ description
+ "The URL-based configuration is the config source.";
+ }
+ anyxml config {
+ description
+ "Inline Config content: <config> element. Represents
+ an entire configuration datastore, not
+ a subset of the running datastore.";
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/tests/yanglint/modules/modaction.yang b/tests/yanglint/modules/modaction.yang
new file mode 100644
index 0000000..5a3f92f
--- /dev/null
+++ b/tests/yanglint/modules/modaction.yang
@@ -0,0 +1,26 @@
+module modaction {
+ yang-version 1.1;
+ namespace "urn:yanglint:modaction";
+ prefix ma;
+
+ container con {
+ list ls {
+ key "lfkey";
+ leaf lfkey {
+ type string;
+ }
+ action act {
+ input {
+ leaf lfi {
+ type string;
+ }
+ }
+ output {
+ leaf lfo {
+ type int16;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/yanglint/modules/modconfig-augment.yang b/tests/yanglint/modules/modconfig-augment.yang
new file mode 100644
index 0000000..d94b366
--- /dev/null
+++ b/tests/yanglint/modules/modconfig-augment.yang
@@ -0,0 +1,15 @@
+module modconfig-augment {
+ yang-version 1.1;
+ namespace "urn:yanglint:modconfig-augment";
+ prefix "mca";
+
+ import modconfig {
+ prefix mc;
+ }
+
+ augment "/mc:mcc" {
+ leaf alf {
+ type string;
+ }
+ }
+}
diff --git a/tests/yanglint/modules/modconfig.yang b/tests/yanglint/modules/modconfig.yang
new file mode 100644
index 0000000..1d12ca6
--- /dev/null
+++ b/tests/yanglint/modules/modconfig.yang
@@ -0,0 +1,17 @@
+module modconfig {
+ namespace "urn:yanglint:modconfig";
+ prefix mc;
+
+ container mcc {
+ leaf lft {
+ type string;
+ config true;
+ mandatory true;
+ }
+ leaf lff {
+ type string;
+ config false;
+ mandatory true;
+ }
+ }
+}
diff --git a/tests/yanglint/modules/moddatanodes.yang b/tests/yanglint/modules/moddatanodes.yang
new file mode 100644
index 0000000..ae4ab20
--- /dev/null
+++ b/tests/yanglint/modules/moddatanodes.yang
@@ -0,0 +1,31 @@
+module moddatanodes {
+ yang-version 1.1;
+ namespace "urn:yanglint:moddatanodes";
+ prefix mdn;
+
+ container dnc {
+ leaf lf {
+ type string;
+ }
+ leaf-list lfl {
+ type string;
+ }
+ leaf mis {
+ type string;
+ }
+ container con {
+ list lt {
+ key "kalf kblf";
+ leaf kalf {
+ type string;
+ }
+ leaf kblf {
+ type string;
+ }
+ leaf vlf {
+ type string;
+ }
+ }
+ }
+ }
+}
diff --git a/tests/yanglint/modules/moddefault.yang b/tests/yanglint/modules/moddefault.yang
new file mode 100644
index 0000000..26570c3
--- /dev/null
+++ b/tests/yanglint/modules/moddefault.yang
@@ -0,0 +1,19 @@
+module moddefault {
+ namespace "urn:yanglint:moddefault";
+ prefix md;
+
+ container mdc {
+ leaf lf {
+ type uint16;
+ }
+ leaf di {
+ type int16;
+ default "5";
+ }
+ leaf ds {
+ type string;
+ default "str";
+ }
+ }
+
+}
diff --git a/tests/yanglint/modules/modextleafref.yang b/tests/yanglint/modules/modextleafref.yang
new file mode 100644
index 0000000..d45ec71
--- /dev/null
+++ b/tests/yanglint/modules/modextleafref.yang
@@ -0,0 +1,24 @@
+module modextleafref {
+ namespace "urn:yanglint:modextleafref";
+ prefix mel;
+
+ list ls {
+ key k;
+ leaf k {
+ type string;
+ }
+ leaf lf {
+ type uint8;
+ }
+ }
+ leaf lfr {
+ type leafref {
+ path "../ls/k";
+ }
+ }
+ leaf lfrderef {
+ type leafref {
+ path "deref(../lfr)/../lf";
+ }
+ }
+}
diff --git a/tests/yanglint/modules/modfeature.yang b/tests/yanglint/modules/modfeature.yang
new file mode 100644
index 0000000..f59d4c8
--- /dev/null
+++ b/tests/yanglint/modules/modfeature.yang
@@ -0,0 +1,7 @@
+module modfeature {
+ namespace "urn:yanglint:modfeature";
+ prefix l;
+
+ feature ftr1;
+ feature ftr2;
+}
diff --git a/tests/yanglint/modules/modimp-cwd.yang b/tests/yanglint/modules/modimp-cwd.yang
new file mode 100644
index 0000000..3249462
--- /dev/null
+++ b/tests/yanglint/modules/modimp-cwd.yang
@@ -0,0 +1,8 @@
+module modimp-cwd {
+ namespace "urn:yanglint:modimp-cwd";
+ prefix ic;
+
+ import modcwd {
+ prefix mc;
+ }
+}
diff --git a/tests/yanglint/modules/modimp-path.yang b/tests/yanglint/modules/modimp-path.yang
new file mode 100644
index 0000000..d9dbb9b
--- /dev/null
+++ b/tests/yanglint/modules/modimp-path.yang
@@ -0,0 +1,8 @@
+module modimp-path {
+ namespace "urn:yanglint:modimp-path";
+ prefix ip;
+
+ import modpath {
+ prefix mp;
+ }
+}
diff --git a/tests/yanglint/modules/modimp-type.yang b/tests/yanglint/modules/modimp-type.yang
new file mode 100644
index 0000000..ec21d31
--- /dev/null
+++ b/tests/yanglint/modules/modimp-type.yang
@@ -0,0 +1,12 @@
+module modimp-type {
+ namespace "urn:yanglint:modimp-type";
+ prefix mit;
+
+ import modtypedef {
+ prefix mtd;
+ }
+
+ leaf lf {
+ type mtd:mui8;
+ }
+}
diff --git a/tests/yanglint/modules/modinclude.yang b/tests/yanglint/modules/modinclude.yang
new file mode 100644
index 0000000..849d43f
--- /dev/null
+++ b/tests/yanglint/modules/modinclude.yang
@@ -0,0 +1,9 @@
+module modinclude {
+ yang-version 1.1;
+ namespace "urn:yanglint:modinclude";
+ prefix mi;
+
+ include "modsub";
+
+ container mic;
+}
diff --git a/tests/yanglint/modules/modleaf.yang b/tests/yanglint/modules/modleaf.yang
new file mode 100644
index 0000000..48ce786
--- /dev/null
+++ b/tests/yanglint/modules/modleaf.yang
@@ -0,0 +1,8 @@
+module modleaf {
+ namespace "urn:yanglint:modleaf";
+ prefix l;
+
+ leaf lfl {
+ type uint16;
+ }
+}
diff --git a/tests/yanglint/modules/modleafref.yang b/tests/yanglint/modules/modleafref.yang
new file mode 100644
index 0000000..f86fb3f
--- /dev/null
+++ b/tests/yanglint/modules/modleafref.yang
@@ -0,0 +1,14 @@
+module modleafref {
+ namespace "urn:yanglint:modleafref";
+ prefix m;
+
+ import modleaf {
+ prefix ml;
+ }
+
+ leaf lfr {
+ type leafref {
+ path "/ml:lfl";
+ }
+ }
+}
diff --git a/tests/yanglint/modules/modmandatory.yang b/tests/yanglint/modules/modmandatory.yang
new file mode 100644
index 0000000..4d48540
--- /dev/null
+++ b/tests/yanglint/modules/modmandatory.yang
@@ -0,0 +1,14 @@
+module modmandatory {
+ namespace "urn:yanglint:modmandatory";
+ prefix mm;
+
+ container mmc {
+ leaf lft {
+ type int16;
+ mandatory true;
+ }
+ leaf lff {
+ type int16;
+ }
+ }
+}
diff --git a/tests/yanglint/modules/modmerge.yang b/tests/yanglint/modules/modmerge.yang
new file mode 100644
index 0000000..60fd75c
--- /dev/null
+++ b/tests/yanglint/modules/modmerge.yang
@@ -0,0 +1,21 @@
+module modmerge {
+ namespace "urn:yanglint:modmerge";
+ prefix mm;
+
+ container mmc {
+ leaf en {
+ type enumeration {
+ enum zero;
+ enum one;
+ }
+ }
+ leaf lm {
+ type int16;
+ must "../en != 'zero'";
+ }
+ leaf lf {
+ type string;
+ }
+ }
+
+}
diff --git a/tests/yanglint/modules/modmust.yang b/tests/yanglint/modules/modmust.yang
new file mode 100644
index 0000000..99971bd
--- /dev/null
+++ b/tests/yanglint/modules/modmust.yang
@@ -0,0 +1,13 @@
+module modmust {
+ namespace "urn:yanglint:modmust";
+ prefix m;
+
+ import modleaf {
+ prefix ml;
+ }
+
+ leaf lfm {
+ type string;
+ must "/ml:lfl > 0";
+ }
+}
diff --git a/tests/yanglint/modules/modnotif.yang b/tests/yanglint/modules/modnotif.yang
new file mode 100644
index 0000000..a2155a0
--- /dev/null
+++ b/tests/yanglint/modules/modnotif.yang
@@ -0,0 +1,19 @@
+module modnotif {
+ yang-version 1.1;
+ namespace "urn:yanglint:modnotif";
+ prefix mn;
+
+ container con {
+ notification nfn {
+ leaf lf {
+ type string;
+ }
+ }
+ }
+
+ notification nfg {
+ leaf lf {
+ type string;
+ }
+ }
+}
diff --git a/tests/yanglint/modules/modoper-leafref.yang b/tests/yanglint/modules/modoper-leafref.yang
new file mode 100644
index 0000000..36a1124
--- /dev/null
+++ b/tests/yanglint/modules/modoper-leafref.yang
@@ -0,0 +1,68 @@
+module modoper-leafref {
+ yang-version 1.1;
+ namespace "urn:yanglint:modoper-leafref";
+ prefix mol;
+
+ import modconfig {
+ prefix mc;
+ }
+
+ container cond {
+ list list {
+ key "klf";
+ leaf klf {
+ type string;
+ }
+ action act {
+ input {
+ leaf lfi {
+ type leafref {
+ path "/mc:mcc/mc:lft";
+ }
+ }
+ }
+ output {
+ leaf lfo {
+ type leafref {
+ path "/mc:mcc/mc:lft";
+ }
+ }
+ }
+ }
+ notification notif {
+ leaf lfn {
+ type leafref {
+ path "/mc:mcc/mc:lft";
+ }
+ }
+ }
+ }
+ }
+
+ rpc rpcg {
+ input {
+ leaf lfi {
+ type leafref {
+ path "/mc:mcc/mc:lft";
+ }
+ }
+ }
+ output {
+ container cono {
+ leaf lfo {
+ type leafref {
+ path "/mc:mcc/mc:lft";
+ }
+ }
+ }
+ }
+ }
+
+ notification notifg {
+ leaf lfr {
+ type leafref {
+ path "/mc:mcc/mc:lft";
+ }
+ }
+ }
+}
diff --git a/tests/yanglint/modules/modpath.yang b/tests/yanglint/modules/modpath.yang
new file mode 100644
index 0000000..da099a2
--- /dev/null
+++ b/tests/yanglint/modules/modpath.yang
@@ -0,0 +1,4 @@
+module modpath {
+ namespace "urn:yanglint:modpath";
+ prefix mp;
+}
diff --git a/tests/yanglint/modules/modrpc.yang b/tests/yanglint/modules/modrpc.yang
new file mode 100644
index 0000000..dc0cced
--- /dev/null
+++ b/tests/yanglint/modules/modrpc.yang
@@ -0,0 +1,19 @@
+module modrpc {
+ namespace "urn:yanglint:modrpc";
+ prefix mr;
+
+ rpc rpc {
+ input {
+ leaf lfi {
+ type string;
+ }
+ }
+ output {
+ container con {
+ leaf lfo {
+ type int16;
+ }
+ }
+ }
+ }
+}
diff --git a/tests/yanglint/modules/modsm-augment.yang b/tests/yanglint/modules/modsm-augment.yang
new file mode 100644
index 0000000..5d16fbd
--- /dev/null
+++ b/tests/yanglint/modules/modsm-augment.yang
@@ -0,0 +1,15 @@
+module modsm-augment {
+ yang-version 1.1;
+ namespace "urn:yanglint:modsm-augment";
+ prefix "msa";
+
+ import modsm {
+ prefix msm;
+ }
+
+ augment "/msm:root" {
+ leaf alf {
+ type string;
+ }
+ }
+}
diff --git a/tests/yanglint/modules/modsm.yang b/tests/yanglint/modules/modsm.yang
new file mode 100644
index 0000000..dfe8830
--- /dev/null
+++ b/tests/yanglint/modules/modsm.yang
@@ -0,0 +1,13 @@
+module modsm {
+ yang-version 1.1;
+ namespace "urn:yanglint:modsm";
+ prefix "msm";
+
+ import ietf-yang-schema-mount {
+ prefix sm;
+ }
+
+ container root {
+ sm:mount-point "root";
+ }
+}
diff --git a/tests/yanglint/modules/modsub.yang b/tests/yanglint/modules/modsub.yang
new file mode 100644
index 0000000..79d9286
--- /dev/null
+++ b/tests/yanglint/modules/modsub.yang
@@ -0,0 +1,8 @@
+submodule modsub {
+ yang-version 1.1;
+ belongs-to modinclude {
+ prefix mi;
+ }
+
+ container msc;
+}
diff --git a/tests/yanglint/modules/modtypedef.yang b/tests/yanglint/modules/modtypedef.yang
new file mode 100644
index 0000000..ea09c95
--- /dev/null
+++ b/tests/yanglint/modules/modtypedef.yang
@@ -0,0 +1,8 @@
+module modtypedef {
+ namespace "urn:yanglint:typedef";
+ prefix mt;
+
+ typedef mui8 {
+ type uint8;
+ }
+}
diff --git a/tests/yanglint/non-interactive/all.tcl b/tests/yanglint/non-interactive/all.tcl
new file mode 100644
index 0000000..998c03a
--- /dev/null
+++ b/tests/yanglint/non-interactive/all.tcl
@@ -0,0 +1,15 @@
+package require tcltest
+
+# Hook to determine if any of the tests failed.
+# Sets a global variable exitCode to 1 if any test fails otherwise it is set to 0.
+proc tcltest::cleanupTestsHook {} {
+ variable numTests
+ set ::exitCode [expr {$numTests(Failed) > 0}]
+}
+
+if {[info exists ::env(TESTS_DIR)]} {
+ tcltest::configure -testdir "$env(TESTS_DIR)/non-interactive"
+}
+
+tcltest::runAllTests
+exit $exitCode
diff --git a/tests/yanglint/non-interactive/data_default.test b/tests/yanglint/non-interactive/data_default.test
new file mode 100644
index 0000000..be19d72
--- /dev/null
+++ b/tests/yanglint/non-interactive/data_default.test
@@ -0,0 +1,31 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mods "$::env(YANG_MODULES_DIR)/ietf-netconf-with-defaults@2011-06-01.yang $::env(YANG_MODULES_DIR)/moddefault.yang"
+set data "$::env(TESTS_DIR)/data/moddefault.xml"
+
+test data_default_not_set {Print data without --default parameter} {
+ ly_cmd "-f xml $mods $data" "</lf>.*</di>\n</mdc>"
+ ly_cmd "-f json $mods $data" "lf\".*di\"\[^\"]*"
+} {}
+
+test data_default_all {data --default all} {
+ ly_cmd "-d all -f xml $mods $data" "</lf>.*</di>.*</ds>\n</mdc>"
+ ly_cmd "-d all -f json $mods $data" "lf\".*di\".*ds\"\[^\"]*"
+} {}
+
+test data_default_all_tagged {data --default all-tagged} {
+ ly_cmd "-d all-tagged -f xml $mods $data" "</lf>.*<di.*default.*</di>.*<ds.*default.*</ds>\n</mdc>"
+ ly_cmd "-d all-tagged -f json $mods $data" "lf\".*di\".*ds\".*@ds\".*default\"\[^\"]*"
+} {}
+
+test data_default_trim {data --default trim} {
+ ly_cmd "-d trim -f xml $mods $data" "</lf>\n</mdc>"
+ ly_cmd "-d trim -f json $mods $data" "lf\"\[^\"]*"
+} {}
+
+test data_default_implicit_tagged {data --default implicit-tagged} {
+ ly_cmd "-d implicit-tagged -f xml $mods $data" "</lf>.*<di>5</di>.*<ds.*default.*</ds>\n</mdc>"
+ ly_cmd "-d implicit-tagged -f json $mods $data" "lf\".*di\"\[^@]*ds\".*default\"\[^\"]*"
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/data_in_format.test b/tests/yanglint/non-interactive/data_in_format.test
new file mode 100644
index 0000000..f1336dd
--- /dev/null
+++ b/tests/yanglint/non-interactive/data_in_format.test
@@ -0,0 +1,18 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+set ddir $::env(TESTS_DIR)/data
+
+test data_in_format_xml {--in-format xml} {
+ ly_cmd "-I xml $mdir/modleaf.yang $ddir/modleaf.dxml"
+ ly_cmd_err "-I json $mdir/modleaf.yang $ddir/modleaf.dxml" "Failed to parse"
+ ly_cmd_err "-I lyb $mdir/modleaf.yang $ddir/modleaf.dxml" "Failed to parse"
+} {}
+
+test data_in_format_json {--in-format json} {
+ ly_cmd "-I json $mdir/modleaf.yang $ddir/modleaf.djson"
+ ly_cmd_err "-I xml $mdir/modleaf.yang $ddir/modleaf.djson" "Failed to parse"
+ ly_cmd_err "-I lyb $mdir/modleaf.yang $ddir/modleaf.djson" "Failed to parse"
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/data_merge.test b/tests/yanglint/non-interactive/data_merge.test
new file mode 100644
index 0000000..4ecfcee
--- /dev/null
+++ b/tests/yanglint/non-interactive/data_merge.test
@@ -0,0 +1,28 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+set ddir $::env(TESTS_DIR)/data
+
+test data_merge_basic {Data is merged and the node is added} {
+ ly_cmd "-m -f xml $mdir/modmerge.yang $ddir/modmerge.xml $ddir/modmerge3.xml" "<en>.*<lm>.*<lf>"
+} {}
+
+test data_merge_validation_failed {Data is merged but validation failed.} {
+ ly_cmd "$mdir/modmerge.yang $ddir/modmerge.xml"
+ ly_cmd "$mdir/modmerge.yang $ddir/modmerge2.xml"
+ ly_cmd "-m $mdir/modmerge.yang $ddir/modmerge2.xml $ddir/modmerge.xml"
+ ly_cmd_err "-m $mdir/modmerge.yang $ddir/modmerge.xml $ddir/modmerge2.xml" "Merged data are not valid"
+} {}
+
+test data_merge_dataconfig {The merge option has effect only for 'data' and 'config' TYPEs} {
+ set wrn1 "option has effect only for"
+ ly_cmd_wrn "-m -t rpc $mdir/modrpc.yang $ddir/modrpc.xml $ddir/modrpc.xml" $wrn1
+ ly_cmd_wrn "-m -t notif $mdir/modnotif.yang $ddir/modnotif2.xml $ddir/modnotif2.xml" $wrn1
+ ly_cmd_wrn "-m -t get $mdir/modconfig.yang $mdir/modleaf.yang $ddir/modleaf.xml $ddir/modconfig.xml" $wrn1
+ ly_cmd_wrn "-m -t getconfig $mdir/modconfig.yang $mdir/modleaf.yang $ddir/modleaf.xml $ddir/modconfig2.xml" $wrn1
+ ly_cmd_wrn "-m -t edit $mdir/modconfig.yang $mdir/modleaf.yang $ddir/modleaf.xml $ddir/modconfig2.xml" $wrn1
+ ly_cmd "-m -t config $mdir/modconfig.yang $mdir/modleaf.yang $ddir/modleaf.xml $ddir/modconfig2.xml"
+ ly_cmd "-m -t data $mdir/modconfig.yang $mdir/modleaf.yang $ddir/modleaf.xml $ddir/modconfig.xml"
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/data_not_strict.test b/tests/yanglint/non-interactive/data_not_strict.test
new file mode 100644
index 0000000..b91eed8
--- /dev/null
+++ b/tests/yanglint/non-interactive/data_not_strict.test
@@ -0,0 +1,20 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+set ddir $::env(TESTS_DIR)/data
+
+test data_no_strict_basic {} {
+ ly_cmd_err "$ddir/modmandatory.xml $mdir/modleaf.yang" "No module with namespace \"urn:yanglint:modmandatory\" in the context."
+ ly_cmd "-n $ddir/modmandatory.xml $mdir/modleaf.yang"
+} {}
+
+test data_no_strict_invalid_data {validation with --no-strict but data are invalid} {
+ set errmsg "Mandatory node \"lft\" instance does not exist."
+ ly_cmd_err "-n $ddir/modmandatory_invalid.xml $mdir/modmandatory.yang" $errmsg
+} {}
+
+test data_no_strict_ignore_invalid_data {--no-strict ignore invalid data if no schema is provided} {
+ ly_cmd "-f xml -n $ddir/modmandatory_invalid.xml $ddir/modleaf.xml $mdir/modleaf.yang" "modleaf.*</lfl>$"
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/data_operational.test b/tests/yanglint/non-interactive/data_operational.test
new file mode 100644
index 0000000..82e861e
--- /dev/null
+++ b/tests/yanglint/non-interactive/data_operational.test
@@ -0,0 +1,62 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir "$::env(YANG_MODULES_DIR)"
+set ddir "$::env(TESTS_DIR)/data"
+set err1 "Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types"
+
+test data_operational_twice {it is not allowed to specify more than one --operational parameter} {
+ ly_cmd_err "-t notif -O $ddir/modconfig.xml -O $ddir/modleaf.xml" "cannot be set multiple times"
+} {}
+
+test data_operational_no_type {--operational should be with parameter --type} {
+ ly_cmd_err "-O $ddir/modconfig.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_notif.xml" $err1
+} {}
+
+test data_operational_missing {--operational is omitted and the datastore contents is in the data file} {
+ ly_cmd_err "$mdir/modoper-leafref.yang $ddir/modoper_leafref_notif_err.xml" "Failed to parse input data file"
+} {}
+
+test data_operational_wrong_type {data are not defined as an operation} {
+ ly_cmd_wrn "-t data -O $ddir/modconfig.xml $mdir/modleaf.yang $ddir/modleaf.xml" $err1
+} {}
+
+test data_operational_datastore_with_unknown_data {unknown data are ignored} {
+ ly_cmd "-t rpc -O $ddir/modmandatory_invalid.xml $mdir/modrpc.yang $ddir/modrpc.xml"
+} {}
+
+test data_operational_empty_datastore {datastore is considered empty because it contains unknown data} {
+ ly_cmd "-t rpc -O $ddir/modmandatory_invalid.xml $mdir/modrpc.yang $ddir/modrpc.xml"
+ set msg "parent \"/modnotif:con\" not found in the operational data"
+ ly_cmd_err "-t notif -O $ddir/modmandatory_invalid.xml $mdir/modnotif.yang $ddir/modnotif.xml" $msg
+} {}
+
+test data_operational_notif_leafref {--operational data is referenced from notification-leafref} {
+ ly_cmd "-t notif -O $ddir/modconfig.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_notif.xml"
+} {}
+
+test data_operational_nested_notif_leafref {--operational data is referenced from nested-notification-leafref} {
+ ly_cmd "-t notif -O $ddir/modoper_leafref_ds.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_notif2.xml"
+} {}
+
+test data_operational_nested_notif_parent_missing {--operational data are invalid due to missing parent node} {
+ set msg "klf='key_val']\" not found in the operational data"
+ ly_cmd_err "-t notif -O $ddir/modconfig.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_notif2.xml" $msg
+} {}
+
+test data_operational_action_leafref {--operational data is referenced from action-leafref} {
+ ly_cmd "-t rpc -O $ddir/modoper_leafref_ds.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_action.xml"
+} {}
+
+test data_operational_action_reply_leafref {--operational data is referenced from action-leafref output} {
+ ly_cmd "-t reply -O $ddir/modoper_leafref_ds.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_action_reply.xml"
+} {}
+
+test data_operational_rpc_leafref {--operational data is referenced from rpc-leafref} {
+ ly_cmd "-t rpc -O $ddir/modconfig.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_rpc.xml"
+} {}
+
+test data_operational_rpc_reply_leafref {--operational data is referenced from rpc-leafref output} {
+ ly_cmd "-t reply -O $ddir/modconfig.xml $mdir/modoper-leafref.yang $ddir/modoper_leafref_rpc_reply.xml"
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/data_present.test b/tests/yanglint/non-interactive/data_present.test
new file mode 100644
index 0000000..81aac14
--- /dev/null
+++ b/tests/yanglint/non-interactive/data_present.test
@@ -0,0 +1,25 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+set ddir $::env(TESTS_DIR)/data
+
+test data_present_via_mandatory {validation of mandatory-stmt will pass only with the --present} {
+ set mods "$mdir/modleaf.yang $mdir/modmandatory.yang"
+ ly_cmd_err "$ddir/modleaf.xml $mods" "Mandatory node \"lft\" instance does not exist."
+ ly_cmd "-e $ddir/modleaf.xml $mods"
+} {}
+
+test data_present_merge {validation with --present and --merge} {
+ set mods "$mdir/modleaf.yang $mdir/modmandatory.yang $mdir/moddefault.yang"
+ set data "$ddir/modleaf.xml $ddir/moddefault.xml"
+ ly_cmd_err "-m $data $mods" "Mandatory node \"lft\" instance does not exist."
+ ly_cmd "-m -e $data $mods"
+} {}
+
+test data_present_merge_invalid {using --present and --merge but data are invalid} {
+ set mods "$mdir/modleaf.yang $mdir/modmandatory.yang"
+ set data "$ddir/modleaf.xml $ddir/modmandatory_invalid.xml"
+ ly_cmd_err "-e -m $data $mods" "Mandatory node \"lft\" instance does not exist."
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/data_type.test b/tests/yanglint/non-interactive/data_type.test
new file mode 100644
index 0000000..e3691d7
--- /dev/null
+++ b/tests/yanglint/non-interactive/data_type.test
@@ -0,0 +1,107 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir "$::env(YANG_MODULES_DIR)"
+set ddir "$::env(TESTS_DIR)/data"
+set modnc "$mdir/ietf-netconf@2011-06-01.yang"
+
+test data_type_data {data --type data} {
+ ly_cmd "-t data $mdir/modconfig.yang $ddir/modconfig.xml"
+} {}
+
+test data_type_config {data --type config} {
+ ly_cmd_err "-t config $mdir/modconfig.yang $ddir/modconfig.xml" "Unexpected data state node \"lff\""
+ ly_cmd "-t config $mdir/modconfig.yang $ddir/modconfig2.xml"
+} {}
+
+test data_type_get {data --type get} {
+ ly_cmd_err "-t data $mdir/modleafref.yang $ddir/modleafref2.xml" "Invalid leafref value"
+ ly_cmd "-t get $mdir/modleafref.yang $ddir/modleafref2.xml"
+} {}
+
+test data_type_getconfig_no_state {No state node for data --type getconfig} {
+ ly_cmd_err "-t getconfig $mdir/modconfig.yang $ddir/modconfig.xml" "Unexpected data state node \"lff\""
+ ly_cmd "-t getconfig $mdir/modconfig.yang $ddir/modconfig2.xml"
+} {}
+
+test data_type_getconfig_parse_only {No validation performed for data --type getconfig} {
+ ly_cmd_err "-t data $mdir/modleafref.yang $ddir/modleafref2.xml" "Invalid leafref value"
+ ly_cmd "-t getconfig $mdir/modleafref.yang $ddir/modleafref2.xml"
+} {}
+
+test data_type_edit_no_state {No state node for data --type edit} {
+ ly_cmd_err "-t edit $mdir/modconfig.yang $ddir/modconfig.xml" "Unexpected data state node \"lff\""
+ ly_cmd "-t edit $mdir/modconfig.yang $ddir/modconfig2.xml"
+} {}
+
+test data_type_edit_parse_only {No validation performed for data --type edit} {
+ ly_cmd_err "-t data $mdir/modleafref.yang $ddir/modleafref2.xml" "Invalid leafref value"
+ ly_cmd "-t edit $mdir/modleafref.yang $ddir/modleafref2.xml"
+} {}
+
+test data_type_rpc {Validation of rpc-statement by data --type rpc} {
+ ly_cmd_err "-t rpc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing the operation node."
+ ly_cmd "-t rpc $mdir/modrpc.yang $ddir/modrpc.xml"
+} {}
+
+test data_type_rpc_nc {Validation of rpc-statement by data --type nc-rpc} {
+ ly_cmd_err "-t nc-rpc $modnc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing NETCONF <rpc> envelope"
+ ly_cmd "-t nc-rpc $modnc $mdir/modrpc.yang $ddir/modrpc_nc.xml"
+} {}
+
+test data_type_rpc_reply {Validation of rpc-reply by data --type reply} {
+ ly_cmd_err "-t rpc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing the operation node."
+ ly_cmd_wrn "-t reply -R $ddir/modrpc.xml $mdir/modrpc.yang $ddir/modrpc_reply.xml" "needed only for NETCONF"
+ ly_cmd "-t reply $mdir/modrpc.yang $ddir/modrpc_reply.xml"
+} {}
+
+test data_type_rpc_reply_nc {Validation of rpc-reply by data --type nc-reply} {
+ set err1 "Missing NETCONF <rpc-reply> envelope"
+ ly_cmd_err "-t nc-reply -R $ddir/modrpc_nc.xml $mdir/modrpc.yang $mdir/modleaf.yang $ddir/modleaf.xml" $err1
+ ly_cmd_err "-t nc-reply $mdir/modrpc.yang $ddir/modrpc_reply_nc.xml" "Missing source RPC"
+ ly_cmd "-t nc-reply -R $ddir/modrpc_nc.xml $mdir/modrpc.yang $ddir/modrpc_reply_nc.xml"
+} {}
+
+test data_type_rpc_action {Validation of action-statement by data --type rpc} {
+ ly_cmd_err "-t rpc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing the operation node."
+ ly_cmd "-t rpc -O $ddir/modaction_ds.xml $mdir/modaction.yang $ddir/modaction.xml"
+} {}
+
+test data_type_rpc_action_nc {Validation of action-statement by data --type nc-rpc} {
+ ly_cmd_err "-t nc-rpc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing NETCONF <rpc> envelope"
+ ly_cmd "-t nc-rpc -O $ddir/modaction_ds.xml $mdir/modaction.yang $ddir/modaction_nc.xml"
+} {}
+
+test data_type_rpc_action_reply {Validation of action-reply by data --type reply} {
+ ly_cmd_err "-t rpc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing the operation node."
+ ly_cmd "-t reply -O $ddir/modaction_ds.xml $mdir/modaction.yang $ddir/modaction_reply.xml"
+} {}
+
+test data_type_rpc_action_reply_nc {Validation of action-reply by data --type nc-reply} {
+ set err1 "Missing NETCONF <rpc-reply> envelope"
+ set err2 "operational parameter needed"
+ ly_cmd_err "-t nc-reply -R $ddir/modaction_nc.xml $mdir/modaction.yang $mdir/modleaf.yang $ddir/modleaf.xml" $err1
+ ly_cmd_err "-t nc-reply $mdir/modaction.yang $ddir/modaction_reply_nc.xml" "Missing source RPC"
+ ly_cmd_err "-t nc-reply -R $ddir/modaction_nc.xml $mdir/modaction.yang $ddir/modaction_reply_nc.xml" $err2
+ ly_cmd "-t nc-reply -O $ddir/modaction_ds.xml -R $ddir/modaction_nc.xml $mdir/modaction.yang $ddir/modaction_reply_nc.xml"
+} {}
+
+test data_type_notif {Validation of notification-statement by data --type notif} {
+ ly_cmd_err "-t notif $mdir/modleaf.yang $ddir/modleaf.xml" "Missing the operation node."
+ ly_cmd "-t notif $mdir/modnotif.yang $ddir/modnotif2.xml"
+} {}
+
+test data_type_notif_nc {Validation of notification-statement by data --type nc-notif} {
+ ly_cmd_err "-t nc-notif $modnc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing NETCONF <notification> envelope"
+ ly_cmd "-t nc-notif $modnc $mdir/modnotif.yang $ddir/modnotif2_nc.xml"
+} {}
+
+test data_type_notif_nested {Validation of nested-notification-statement by data --type notif} {
+ ly_cmd "-t notif -O $ddir/modnotif_ds.xml $mdir/modnotif.yang $ddir/modnotif.xml"
+} {}
+
+test data_type_notif_nested_nc {Validation of nested-notification-statement by data --type nc-notif} {
+ ly_cmd_err "-t nc-notif $modnc $mdir/modleaf.yang $ddir/modleaf.xml" "Missing NETCONF <notification> envelope"
+ ly_cmd "-t nc-notif -O $ddir/modnotif_ds.xml $modnc $mdir/modnotif.yang $ddir/modnotif_nc.xml"
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/data_xpath.test b/tests/yanglint/non-interactive/data_xpath.test
new file mode 100644
index 0000000..1d96106
--- /dev/null
+++ b/tests/yanglint/non-interactive/data_xpath.test
@@ -0,0 +1,42 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mod "$::env(YANG_MODULES_DIR)/moddatanodes.yang"
+set data "$::env(TESTS_DIR)/data/moddatanodes.xml"
+
+test data_xpath_empty {--data-path to missing node} {
+ ly_cmd "-E /moddatanodes:dnc/mis $mod $data" "Empty"
+} {}
+
+test data_xpath_leaf {--xpath to leaf node} {
+ ly_cmd "-E /moddatanodes:dnc/lf $mod $data" "leaf \"lf\" \\(value: \"x\"\\)"
+} {}
+
+test data_xpath_leaflist {--xpath to leaf-list node} {
+ set r1 "leaf-list \"lfl\" \\(value: \"1\"\\)"
+ set r2 "leaf-list \"lfl\" \\(value: \"2\"\\)"
+ ly_cmd "-E /moddatanodes:dnc/lfl $mod $data" "$r1\n $r2"
+} {}
+
+test data_xpath_list {--xpath to list} {
+ set r1 "list \"lt\" \\(\"kalf\": \"ka1\"; \"kblf\": \"kb1\";\\)"
+ set r2 "list \"lt\" \\(\"kalf\": \"ka2\"; \"kblf\": \"kb2\";\\)"
+ ly_cmd "-E /moddatanodes:dnc/con/lt $mod $data" "$r1\n $r2"
+} {}
+
+test data_xpath_container {--xpath to container} {
+ ly_cmd "-E /moddatanodes:dnc/con $mod $data" "container \"con\""
+} {}
+
+test data_xpath_wrong_path {--xpath to a non-existent node} {
+ ly_cmd_err "-E /moddatanodes:dnc/wrng $mod $data" "xpath failed"
+} {}
+
+test data_xpath_err_format {--xpath cannot be combined with --format} {
+ ly_cmd_err "-f xml -E /moddatanodes:dnc/lf $mod $data" "option cannot be combined"
+} {}
+
+test data_xpath_err_default {--xpath cannot be combined with --default} {
+ ly_cmd_err "-d all -E /moddatanodes:dnc/lf $mod $data" "option cannot be combined"
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/debug.test b/tests/yanglint/non-interactive/debug.test
new file mode 100644
index 0000000..4543acb
--- /dev/null
+++ b/tests/yanglint/non-interactive/debug.test
@@ -0,0 +1,25 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+
+test debug_dict {Check debug message DICT} {
+-constraints {[ly_opt_exists "-G"]} -body {
+ ly_cmd_wrn "-V -V -G dict $mdir/modleaf.yang" "DICT"
+}}
+
+test debug_xpath {Check debug message XPATH} {
+-constraints {[ly_opt_exists "-G"]} -body {
+ ly_cmd_wrn "-V -V -G xpath $mdir/modmust.yang" "XPATH"
+}}
+
+test debug_dep_sets {Check debug message DEPSETS} {
+-constraints {[ly_opt_exists "-G"]} -body {
+ ly_cmd_wrn "-V -V -G dep-sets $mdir/modleaf.yang" "DEPSETS"
+}}
+
+test debug_depsets_xpath {Check debug message DEPSETS and XPATH} {
+-constraints {[ly_opt_exists "-G"]} -body {
+ ly_cmd_wrn "-V -V -G dep-sets,xpath $mdir/modmust.yang" "DEPSETS.*XPATH"
+}}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/disabled_searchdir.test b/tests/yanglint/non-interactive/disabled_searchdir.test
new file mode 100644
index 0000000..49fe13e
--- /dev/null
+++ b/tests/yanglint/non-interactive/disabled_searchdir.test
@@ -0,0 +1,18 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $env(YANG_MODULES_DIR)
+
+# Test should be skipped if called by ctest.
+test disable_searchdir_once {Unsuccessfully imports module due to disabled cwd searching} {
+-constraints {!ctest} -body {
+ ly_cmd "$mdir/modimp-cwd.yang"
+ ly_cmd_err "-D $mdir/modimp-cwd.yang" "not found in local searchdirs"
+}}
+
+test disable_searchdir_twice {Unsuccessfully imports module due to -D -D} {
+ ly_cmd "$mdir/ietf-ip.yang"
+ ly_cmd_err "-D -D $mdir/ietf-ip.yang" "Loading \"ietf-interfaces\" module failed."
+} {}
+
+cleanupTests
+
diff --git a/tests/yanglint/non-interactive/ext_data.test b/tests/yanglint/non-interactive/ext_data.test
new file mode 100644
index 0000000..d4e3c44
--- /dev/null
+++ b/tests/yanglint/non-interactive/ext_data.test
@@ -0,0 +1,29 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir "$::env(YANG_MODULES_DIR)"
+set ddir "$::env(TESTS_DIR)/data"
+
+test ext_data_schema_mount_tree {Print tree output of a model with Schema Mount} {
+ # mounting node lfl from modleaf.yang into modsm.yang
+ set out1 "--mp root.*--rw lfl/"
+ ly_cmd "-f tree -p $mdir -y -x $ddir/modsm_ctx_ext.xml $mdir/modsm.yang" $out1
+} {}
+
+test ext_data_schema_mount_tree_yanglibfile {Print tree output of a model with Schema Mount and --yang-library-file} {
+ # yang-library-file context contains an augment node 'alf' for modsm
+ set out1 "--mp root.*--rw lfl/.*--rw msa:alf?"
+ ly_cmd "-f tree -p $mdir -Y $ddir/modsm_ctx_main.xml -x $ddir/modsm_ctx_ext.xml $mdir/modsm.yang" $out1
+} {}
+
+test ext_data_schema_mount_xml {Validating and printing mounted data} {
+ ly_cmd "-f xml -t config -p $mdir -y -x $ddir/modsm_ctx_ext.xml $mdir/modsm.yang $ddir/modsm.xml" "</lfl>"
+} {}
+
+test ext_data_schema_mount_xml_yanglibfile {Validating and printing mounted data with --yang-library-file} {
+ set yanglibfile "$ddir/modsm_ctx_main.xml"
+ set extdata "$ddir/modsm_ctx_ext.xml"
+ set out1 "</lfl>.*</alf>"
+ ly_cmd "-f xml -t config -p $mdir -Y $yanglibfile -x $extdata $mdir/modsm.yang $ddir/modsm2.xml" $out1
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/extended_leafref.test b/tests/yanglint/non-interactive/extended_leafref.test
new file mode 100644
index 0000000..5e1a90e
--- /dev/null
+++ b/tests/yanglint/non-interactive/extended_leafref.test
@@ -0,0 +1,13 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+
+test extended_leafref_enabled {Valid module with --extended-leafref option} {
+ ly_cmd "-X $mdir/modextleafref.yang"
+} {}
+
+test extended_leafref_disabled {Expected error if --extended-leafref is not set} {
+ ly_cmd_err "$mdir/modextleafref.yang" "Unexpected XPath token \"FunctionName\""
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/format.test b/tests/yanglint/non-interactive/format.test
new file mode 100644
index 0000000..8df5544
--- /dev/null
+++ b/tests/yanglint/non-interactive/format.test
@@ -0,0 +1,72 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+set ddir $::env(TESTS_DIR)/data
+set ipv6_path "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6/address"
+
+test format_yang {} {
+ ly_cmd "-f yang $mdir/modleaf.yang" "leaf lfl"
+} {}
+
+test format_yang_submodule {Print submodule in yang format} {
+ ly_cmd "-s modsub -f yang $mdir/modinclude.yang" "submodule modsub"
+} {}
+
+test format_yin {} {
+ ly_cmd "-f yin $mdir/modleaf.yang" "<leaf name=\"lfl\">"
+} {}
+
+test format_yin_submodule {Print submodule in yin format} {
+ ly_cmd "-s modsub -f yin $mdir/modinclude.yang" "<submodule name=\"modsub\""
+} {}
+
+test format_info {} {
+ ly_cmd "-f info $mdir/modleaf.yang" "status current"
+} {}
+
+test format_tree {} {
+ ly_cmd "-f tree $mdir/modleaf.yang" "\\+--rw lfl"
+} {}
+
+test format_data_xml {Print data in xml format} {
+ ly_cmd "-f xml $mdir/modleaf.yang $ddir/modleaf.xml" "<lfl xmlns=\"urn:yanglint:modleaf\">7</lfl>"
+} {}
+
+test format_data_json {Print data in json format} {
+ ly_cmd "-f json $mdir/modleaf.yang $ddir/modleaf.xml" "{\n \"modleaf:lfl\": 7\n}"
+} {}
+
+test format_data_lyb_err {Printing in LYB format: expect error due to missing parameter} {
+ ly_cmd_err "-f lyb $mdir/modleaf.yang $ddir/modleaf.xml" "The LYB format requires the -o"
+} {}
+
+test format_tree_submodule {Print submodule in tree format} {
+ ly_cmd "-s modsub -f tree $mdir/modinclude.yang" "submodule: modsub"
+} {}
+
+test format_tree_path {Print subtree in tree format} {
+ ly_cmd "-f tree -P $ipv6_path $mdir/ietf-ip.yang" "\\+--rw address.*\\+--rw prefix-length"
+} {}
+
+test format_tree_path_single_node {Print node in tree format} {
+ ly_cmd "-f tree -q -P $ipv6_path $mdir/ietf-ip.yang" "\\+--rw address\\* \\\[ip\\\]$"
+} {}
+
+test format_tree_path_single_node_line_length {Print node in the tree format and limit row size} {
+ ly_cmd "-f tree -L 20 -q -P $ipv6_path $mdir/ietf-ip.yang" "\\+--rw address\\*\n *\\\[ip\\\]$"
+} {}
+
+test format_feature_param_one_module {Show features for one module} {
+ ly_cmd "-f feature-param $mdir/ietf-ip.yang" " -F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf" -ex
+} {}
+
+test format_feature_param_more_modules {Show a mix of modules with and without features} {
+
+ set features " -F modfeature:ftr1,ftr2\
+-F modleaf:\
+-F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf"
+
+ ly_cmd "-f feature-param $mdir/modfeature.yang $mdir/modleaf.yang $mdir/ietf-ip.yang" $features -ex
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/list.test b/tests/yanglint/non-interactive/list.test
new file mode 100644
index 0000000..626d9a1
--- /dev/null
+++ b/tests/yanglint/non-interactive/list.test
@@ -0,0 +1,26 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+namespace import uti::regex_xml_elements uti::regex_json_pairs
+
+set modules {ietf-yang-library ietf-inet-types}
+
+test list_basic {} {
+ ly_cmd "-l" "ietf-yang-types"
+} {}
+
+test list_format_xml {list --format xml} {
+ ly_cmd "-y -f xml -l" [regex_xml_elements $modules "name"]
+} {}
+
+test list_format_json {list --format json} {
+ ly_cmd "-y -f json -l" [regex_json_pairs $modules "name"]
+} {}
+
+test list_ietf_yang_library {Error due to missing ietf-yang-library} {
+ ly_cmd_err "-f xml -l" "Module \"ietf-yang-library\" is not implemented."
+} {}
+
+test list_bad_format {Error due to bad format} {
+ ly_cmd_err "-f csv -l" "Unknown output format csv"
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/ly.tcl b/tests/yanglint/non-interactive/ly.tcl
new file mode 100644
index 0000000..f6bb2c7
--- /dev/null
+++ b/tests/yanglint/non-interactive/ly.tcl
@@ -0,0 +1,8 @@
+# @brief The main source of functions and variables for testing yanglint in the non-interactive mode.
+
+# For testing yanglint.
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/common.tcl" : "../common.tcl"}]
+# For testing any non-interactive tool.
+source "$::env(TESTS_DIR)/../tool_ni.tcl"
+
+# The script continues by defining variables and functions specific to the non-interactive yanglint tool.
diff --git a/tests/yanglint/non-interactive/make_implemented.test b/tests/yanglint/non-interactive/make_implemented.test
new file mode 100644
index 0000000..40cead9
--- /dev/null
+++ b/tests/yanglint/non-interactive/make_implemented.test
@@ -0,0 +1,17 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $::env(YANG_MODULES_DIR)
+
+test make_impl_no_set {Import while --make-implemented is not set} {
+ ly_cmd "-l $mdir/modleafref.yang" "I modleafref\n.*I modleaf"
+} {}
+
+test make_impl_set_once {--make-implemented} {
+ ly_cmd "-l -i $mdir/modmust.yang" "I modmust\n.*I modleaf"
+} {}
+
+test make_impl_set_twice {-i -i} {
+ ly_cmd "-l -i -i $mdir/modimp-type.yang" "I modimp-type\n.*I modtypedef"
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/modcwd.yang b/tests/yanglint/non-interactive/modcwd.yang
new file mode 100644
index 0000000..db33e73
--- /dev/null
+++ b/tests/yanglint/non-interactive/modcwd.yang
@@ -0,0 +1,4 @@
+module modcwd {
+ namespace "urn:yanglint:modcwd";
+ prefix mc;
+}
diff --git a/tests/yanglint/non-interactive/path.test b/tests/yanglint/non-interactive/path.test
new file mode 100644
index 0000000..bf915ff
--- /dev/null
+++ b/tests/yanglint/non-interactive/path.test
@@ -0,0 +1,9 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir $env(YANG_MODULES_DIR)
+
+test path_basic {} {
+ ly_cmd "-p $::env(TESTS_DIR)/data $::env(YANG_MODULES_DIR)/modimp-path.yang"
+} {}
+
+cleanupTests
diff --git a/tests/yanglint/non-interactive/yang_library_file.test b/tests/yanglint/non-interactive/yang_library_file.test
new file mode 100644
index 0000000..bd95978
--- /dev/null
+++ b/tests/yanglint/non-interactive/yang_library_file.test
@@ -0,0 +1,18 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/ly.tcl" : "ly.tcl"}]
+
+set mdir "$::env(YANG_MODULES_DIR)"
+set ddir "$::env(TESTS_DIR)/data"
+
+test ylf_list {apply --yang-library-file and check result by --list} {
+ ly_cmd "-Y $ddir/modimp_type_ctx.xml -p $mdir -l" "I modimp-type.*i modtypedef"
+} {}
+
+test ylf_make_implemented {apply --yang-library-file and --make-implemented} {
+ ly_cmd "-Y $ddir/modimp_type_ctx.xml -p $mdir -i -i -l" "I modimp-type.*I modtypedef"
+} {}
+
+test ylf_augment_ctx {Setup context by yang-library-file and augment module} {
+ ly_cmd "-Y $ddir/modconfig_ctx.xml -p $mdir -f tree $mdir/modconfig.yang $mdir/modconfig-augment.yang" "mca:alf"
+} {}
+
+cleanupTests
diff --git a/tests/yangre/CMakeLists.txt b/tests/yangre/CMakeLists.txt
new file mode 100644
index 0000000..ce5b39b
--- /dev/null
+++ b/tests/yangre/CMakeLists.txt
@@ -0,0 +1,8 @@
+find_program(PATH_TCLSH NAMES tclsh)
+if(NOT PATH_TCLSH)
+ message(WARNING "'tclsh' not found! The yangre test will not be available.")
+else()
+ add_test(NAME "yangre" COMMAND "tclsh" "${CMAKE_CURRENT_SOURCE_DIR}/all.tcl")
+ set_property(TEST "yangre" APPEND PROPERTY ENVIRONMENT "TESTS_DIR=${CMAKE_CURRENT_SOURCE_DIR}")
+ set_property(TEST "yangre" APPEND PROPERTY ENVIRONMENT "YANGRE=${PROJECT_BINARY_DIR}")
+endif()
diff --git a/tests/yangre/all.tcl b/tests/yangre/all.tcl
new file mode 100644
index 0000000..f00563f
--- /dev/null
+++ b/tests/yangre/all.tcl
@@ -0,0 +1,15 @@
+package require tcltest
+
+# Hook to determine if any of the tests failed.
+# Sets a global variable exitCode to 1 if any test fails otherwise it is set to 0.
+proc tcltest::cleanupTestsHook {} {
+ variable numTests
+ set ::exitCode [expr {$numTests(Failed) > 0}]
+}
+
+if {[info exists ::env(TESTS_DIR)]} {
+ tcltest::configure -testdir "$env(TESTS_DIR)"
+}
+
+tcltest::runAllTests
+exit $exitCode
diff --git a/tests/yangre/arg.test b/tests/yangre/arg.test
new file mode 100644
index 0000000..821aad1
--- /dev/null
+++ b/tests/yangre/arg.test
@@ -0,0 +1,19 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/ly.tcl" : "ly.tcl"}]
+
+test arg_empty {Missing arguments} {
+ ly_cmd_err "" "missing <string> parameter to process"
+} {}
+
+test arg_wrong {Wrong argument} {
+ ly_cmd_err "-j" "invalid option"
+} {}
+
+test arg_help {Print help} {
+ ly_cmd "-h" "Usage:"
+} {}
+
+test arg_version {Print version} {
+ ly_cmd "-v" "yangre"
+} {}
+
+cleanupTests
diff --git a/tests/yangre/file.test b/tests/yangre/file.test
new file mode 100644
index 0000000..80ea3f6
--- /dev/null
+++ b/tests/yangre/file.test
@@ -0,0 +1,37 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/ly.tcl" : "ly.tcl"}]
+
+set fdir "$::env(TESTS_DIR)/files"
+
+test file_empty {file is empty} {
+ ly_cmd "-f $fdir/empty.txt"
+} {}
+
+test file_empty_str {<string> is empty} {
+ ly_cmd "-f $fdir/empty_str.txt"
+} {}
+
+test file_empty_str_err {empty <string> is not allowed} {
+ ly_cmd_err "-f $fdir/empty_str_err.txt" "does not conform"
+} {}
+
+test file_one_pattern {one pattern in the file} {
+ ly_cmd "-f $fdir/one_pattern.txt"
+} {}
+
+test file_two_patterns {two patterns in the file} {
+ ly_cmd "-f $fdir/two_patterns.txt"
+} {}
+
+test file_two_patterns_err {two patterns and the <string> is wrong} {
+ ly_cmd_err "-f $fdir/two_patterns_err.txt" "does not conform"
+} {}
+
+test file_two_patterns_invert_match {one pattern is inverted} {
+ ly_cmd "-f $fdir/two_patterns_invert_match.txt"
+} {}
+
+test file_two_patterns_invert_match_err {one pattern is inverted and the <string> is wrong} {
+ ly_cmd_err "-f $fdir/two_patterns_invert_match_err.txt" "does not conform to inverted"
+} {}
+
+cleanupTests
diff --git a/tests/yangre/files/empty.txt b/tests/yangre/files/empty.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/yangre/files/empty.txt
diff --git a/tests/yangre/files/empty_str.txt b/tests/yangre/files/empty_str.txt
new file mode 100644
index 0000000..bf9b1b5
--- /dev/null
+++ b/tests/yangre/files/empty_str.txt
@@ -0,0 +1,2 @@
+"[0-9a-fA-F]*"
+
diff --git a/tests/yangre/files/empty_str_err.txt b/tests/yangre/files/empty_str_err.txt
new file mode 100644
index 0000000..f48a15d
--- /dev/null
+++ b/tests/yangre/files/empty_str_err.txt
@@ -0,0 +1,2 @@
+"[0-9a-fA-F]+"
+
diff --git a/tests/yangre/files/one_pattern.txt b/tests/yangre/files/one_pattern.txt
new file mode 100644
index 0000000..cf9acc5
--- /dev/null
+++ b/tests/yangre/files/one_pattern.txt
@@ -0,0 +1,3 @@
+"[0-9a-fA-F]*"
+
+1F
diff --git a/tests/yangre/files/two_patterns.txt b/tests/yangre/files/two_patterns.txt
new file mode 100644
index 0000000..7d04b2c
--- /dev/null
+++ b/tests/yangre/files/two_patterns.txt
@@ -0,0 +1,4 @@
+"[0-9a-fA-F]*"
+'[a-zA-Z0-9\-_.]*'
+
+1F
diff --git a/tests/yangre/files/two_patterns_err.txt b/tests/yangre/files/two_patterns_err.txt
new file mode 100644
index 0000000..78f9878
--- /dev/null
+++ b/tests/yangre/files/two_patterns_err.txt
@@ -0,0 +1,4 @@
+"[0-9a-fA-F]*"
+'[a-zA-Z0-9\-_.]*'
+
+@!@
diff --git a/tests/yangre/files/two_patterns_invert_match.txt b/tests/yangre/files/two_patterns_invert_match.txt
new file mode 100644
index 0000000..ffbd835
--- /dev/null
+++ b/tests/yangre/files/two_patterns_invert_match.txt
@@ -0,0 +1,4 @@
+"[a-z]*"
+ '[a-f]*'
+
+gh
diff --git a/tests/yangre/files/two_patterns_invert_match_err.txt b/tests/yangre/files/two_patterns_invert_match_err.txt
new file mode 100644
index 0000000..f182aab
--- /dev/null
+++ b/tests/yangre/files/two_patterns_invert_match_err.txt
@@ -0,0 +1,4 @@
+"[a-z]*"
+ '[a-f]*'
+
+ab
diff --git a/tests/yangre/invert_match.test b/tests/yangre/invert_match.test
new file mode 100644
index 0000000..707ca9d
--- /dev/null
+++ b/tests/yangre/invert_match.test
@@ -0,0 +1,28 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/ly.tcl" : "ly.tcl"}]
+
+test invert_match_from_help1 {Test the first pattern from help via invert match} {
+ ly_cmd_err {-p {"[0-9a-fA-F]*"} -i {1F}} "not conform to inverted"
+ ly_cmd {-p {"[0-9a-fA-F]*"} -i {GUN}}
+} {}
+
+test invert_match_from_help2 {Test the second pattern from help via invert match} {
+ ly_cmd_err {-p {'[a-zA-Z0-9\-_.]*'} -i {a-b}} "not conform to inverted"
+ ly_cmd {-p {'[a-zA-Z0-9\-_.]*'} -i {%@}}
+} {}
+
+test invert_match_from_help3 {Test the third pattern from help via invert match} {
+ ly_cmd_err {-p {[xX][mM][lL].*} -i {xml-encoding}} "not conform to inverted"
+ ly_cmd {-p {[xX][mM][lL].*} -i {json}}
+} {}
+
+test invert_match_three_at_once {Test three inverted patterns and once} {
+ ly_cmd_err {-p {"[0-9a-zA-Z]*"} -i -p {'[a-zA-Z0-9\-_.]*'} -i -p {[xX][mM][lL].*} -i {xml}} "not conform to inverted"
+ ly_cmd {-p {"[0-9a-zA-Z]*"} -i -p {'[a-zA-Z0-9\-_.]*'} -i -p {[xX][mM][lL].*} -i {%@}}
+} {}
+
+test invert_match_second_is_not {Test three patterns but the second one is not inverted} {
+ ly_cmd_err {-p {"[0-9a-zA-Z]*"} -i -p {'[a-zA-Z0-9\-_.]*'} -i -p {[xX][mM][lL].*} -i {o_O}} "not conform to inverted"
+ ly_cmd {-p {"[0-9a-zA-Z]*"} -i -p {'[a-zA-Z0-9\-_.]*'} -p {[xX][mM][lL].*} -i {o_O}}
+} {}
+
+cleanupTests
diff --git a/tests/yangre/ly.tcl b/tests/yangre/ly.tcl
new file mode 100644
index 0000000..3bb62b5
--- /dev/null
+++ b/tests/yangre/ly.tcl
@@ -0,0 +1,17 @@
+# @brief The main source of functions and variables for testing yangre.
+
+package require tcltest
+namespace import ::tcltest::test ::tcltest::cleanupTests
+
+if { ![info exists ::env(TESTS_DIR)] } {
+ # the script is not run via 'ctest' so paths must be set
+ set ::env(TESTS_DIR) "./"
+ set TUT_PATH "../../build"
+} else {
+ # cmake (ctest) already sets ::env variables
+ set TUT_PATH $::env(YANGRE)
+}
+set TUT_NAME "yangre"
+source "$::env(TESTS_DIR)/../tool_ni.tcl"
+
+# The script continues by defining variables and functions specific to the yangre tool.
diff --git a/tests/yangre/pattern.test b/tests/yangre/pattern.test
new file mode 100644
index 0000000..45b7e3b
--- /dev/null
+++ b/tests/yangre/pattern.test
@@ -0,0 +1,19 @@
+source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/ly.tcl" : "ly.tcl"}]
+
+test pattern_from_help1 {Test the first pattern from help} {
+ ly_cmd {-p {"[0-9a-fA-F]*"} {1F}}
+} {}
+
+test pattern_from_help2 {Test the second pattern from help} {
+ ly_cmd {-p {'[a-zA-Z0-9\-_.]*'} {a-b}}
+} {}
+
+test pattern_from_help3 {Test the third pattern from help} {
+ ly_cmd {-p {[xX][mM][lL].*} {xml-encoding}}
+} {}
+
+test pattern_three_at_once {Test three patterns and once} {
+ ly_cmd {-p {"[0-9a-zA-Z]*"} -p {'[a-zA-Z0-9\-_.]*'} -p {[xX][mM][lL].*} {xml}}
+} {}
+
+cleanupTests
diff --git a/tools/lint/CMakeLists.txt b/tools/lint/CMakeLists.txt
index 32cdcbf..14f8b76 100644
--- a/tools/lint/CMakeLists.txt
+++ b/tools/lint/CMakeLists.txt
@@ -17,6 +17,12 @@ set(lintsrc
cmd_load.c
cmd_print.c
cmd_searchpath.c
+ cmd_extdata.c
+ cmd_help.c
+ cmd_verb.c
+ cmd_debug.c
+ yl_opt.c
+ yl_schema_features.c
common.c
)
if(YANGLINT_INTERACTIVE)
@@ -46,45 +52,3 @@ if(WIN32)
target_include_directories(yanglint PRIVATE ${GETOPT_INCLUDE_DIR})
target_link_libraries(yanglint ${GETOPT_LIBRARY})
endif()
-
-#
-# tests
-#
-function(add_yanglint_test)
- cmake_parse_arguments(ADDTEST "" "NAME;VIA;SCRIPT" "" ${ARGN})
- set(TEST_NAME yanglint_${ADDTEST_NAME})
-
- if(${ADDTEST_VIA} STREQUAL "bash")
- set(WRAPPER /usr/bin/env bash)
- elseif(${ADDTEST_VIA} STREQUAL "expect")
- set(WRAPPER ${PATH_EXPECT})
- else()
- message(FATAL_ERROR "build: unexpected wrapper '${ADDTEST_VIA}'")
- endif()
-
- add_test(NAME ${TEST_NAME} COMMAND ${WRAPPER} ${CMAKE_CURRENT_SOURCE_DIR}/tests/${ADDTEST_SCRIPT})
- set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "YANGLINT=${PROJECT_BINARY_DIR}/yanglint")
- set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "YANG_MODULES_DIR=${PROJECT_SOURCE_DIR}/tests/modules/yang")
- set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}")
-endfunction(add_yanglint_test)
-
-if(ENABLE_TESTS)
- # tests of non-interactive mode using shunit2
- find_program(PATH_SHUNIT NAMES shunit2)
- if(NOT PATH_SHUNIT)
- message(WARNING "'shunit2' not found! The yanglint(1) non-interactive tests will not be available.")
- else()
- add_yanglint_test(NAME ni_list VIA bash SCRIPT shunit2/list.sh)
- add_yanglint_test(NAME ni_feature VIA bash SCRIPT shunit2/feature.sh)
- endif()
-
- # tests of interactive mode using expect
- find_program(PATH_EXPECT NAMES expect)
- if(NOT PATH_EXPECT)
- message(WARNING "'expect' not found! The yanglint(1) interactive tests will not be available.")
- elseif(YANGLINT_INTERACTIVE)
- add_yanglint_test(NAME in_list VIA expect SCRIPT expect/list.exp)
- add_yanglint_test(NAME in_feature VIA expect SCRIPT expect/feature.exp)
- add_yanglint_test(NAME in_completion VIA expect SCRIPT expect/completion.exp)
- endif()
-endif()
diff --git a/tools/lint/cmd.c b/tools/lint/cmd.c
index 10e7446..344900d 100644
--- a/tools/lint/cmd.c
+++ b/tools/lint/cmd.c
@@ -2,9 +2,10 @@
* @file cmd.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool general commands
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -30,230 +31,80 @@
COMMAND commands[];
extern int done;
-#ifndef NDEBUG
-
-void
-cmd_debug_help(void)
-{
- printf("Usage: debug (dict | xpath)+\n");
-}
-
void
-cmd_debug(struct ly_ctx **UNUSED(ctx), const char *cmdline)
+cmd_free(void)
{
- int argc = 0;
- char **argv = NULL;
- int opt, opt_index;
- struct option options[] = {
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
- uint32_t dbg_groups = 0;
-
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
- }
+ uint16_t i;
- while ((opt = getopt_long(argc, argv, "h", options, &opt_index)) != -1) {
- switch (opt) {
- case 'h':
- cmd_debug_help();
- goto cleanup;
- default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ for (i = 0; commands[i].name; i++) {
+ if (commands[i].free_func) {
+ commands[i].free_func();
}
}
- if (argc == optind) {
- /* no argument */
- cmd_debug_help();
- goto cleanup;
- }
-
- for (int i = 0; i < argc - optind; i++) {
- if (!strcasecmp("dict", argv[optind + i])) {
- dbg_groups |= LY_LDGDICT;
- } else if (!strcasecmp("xpath", argv[optind + i])) {
- dbg_groups |= LY_LDGXPATH;
- } else {
- YLMSG_E("Unknown debug group \"%s\"\n", argv[optind + 1]);
- goto cleanup;
- }
- }
-
- ly_log_dbg_groups(dbg_groups);
-
-cleanup:
- free_cmdline(argv);
}
-#endif
-
-void
-cmd_verb_help(void)
-{
- printf("Usage: verb (error | warning | verbose | debug)\n");
-}
-
-void
-cmd_verb(struct ly_ctx **UNUSED(ctx), const char *cmdline)
-{
- int argc = 0;
- char **argv = NULL;
- int opt, opt_index;
- struct option options[] = {
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
-
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
- }
-
- while ((opt = getopt_long(argc, argv, "h", options, &opt_index)) != -1) {
- switch (opt) {
- case 'h':
- cmd_verb_help();
- goto cleanup;
- default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
- }
- }
-
- if (argc - optind > 1) {
- YLMSG_E("Only a single verbosity level can be set.\n");
- cmd_verb_help();
- goto cleanup;
- } else if (argc == optind) {
- /* no argument - print current value */
- LY_LOG_LEVEL level = ly_log_level(LY_LLERR);
-
- ly_log_level(level);
- printf("Current verbosity level: ");
- if (level == LY_LLERR) {
- printf("error\n");
- } else if (level == LY_LLWRN) {
- printf("warning\n");
- } else if (level == LY_LLVRB) {
- printf("verbose\n");
- } else if (level == LY_LLDBG) {
- printf("debug\n");
- }
- goto cleanup;
- }
-
- if (!strcasecmp("error", argv[optind]) || !strcmp("0", argv[optind])) {
- ly_log_level(LY_LLERR);
- } else if (!strcasecmp("warning", argv[optind]) || !strcmp("1", argv[optind])) {
- ly_log_level(LY_LLWRN);
- } else if (!strcasecmp("verbose", argv[optind]) || !strcmp("2", argv[optind])) {
- ly_log_level(LY_LLVRB);
- } else if (!strcasecmp("debug", argv[optind]) || !strcmp("3", argv[optind])) {
- ly_log_level(LY_LLDBG);
- } else {
- YLMSG_E("Unknown verbosity \"%s\"\n", argv[optind]);
- goto cleanup;
- }
-
-cleanup:
- free_cmdline(argv);
-}
-
-void
-cmd_quit(struct ly_ctx **UNUSED(ctx), const char *UNUSED(cmdline))
+int
+cmd_quit_exec(struct ly_ctx **UNUSED(ctx), struct yl_opt *UNUSED(yo), const char *UNUSED(posv))
{
done = 1;
- return;
-}
-
-void
-cmd_help_help(void)
-{
- printf("Usage: help [cmd ...]\n");
-}
-
-void
-cmd_help(struct ly_ctx **UNUSED(ctx), const char *cmdline)
-{
- int argc = 0;
- char **argv = NULL;
- int opt, opt_index;
- struct option options[] = {
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0}
- };
-
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
- }
-
- while ((opt = getopt_long(argc, argv, "h", options, &opt_index)) != -1) {
- switch (opt) {
- case 'h':
- cmd_help_help();
- goto cleanup;
- default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
- }
- }
-
- if (argc == optind) {
-generic_help:
- printf("Available commands:\n");
- for (uint16_t i = 0; commands[i].name; i++) {
- if (commands[i].helpstring != NULL) {
- printf(" %-15s %s\n", commands[i].name, commands[i].helpstring);
- }
- }
- } else {
- /* print specific help for the selected command(s) */
-
- for (int c = 0; c < argc - optind; ++c) {
- int8_t match = 0;
-
- /* get the command of the specified name */
- for (uint16_t i = 0; commands[i].name; i++) {
- if (strcmp(argv[optind + c], commands[i].name) == 0) {
- match = 1;
- if (commands[i].help_func != NULL) {
- commands[i].help_func();
- } else {
- printf("%s: %s\n", argv[optind + c], commands[i].helpstring);
- }
- break;
- }
- }
- if (!match) {
- /* if unknown command specified, print the list of commands */
- printf("Unknown command \'%s\'\n", argv[optind + c]);
- goto generic_help;
- }
- }
- }
-
-cleanup:
- free_cmdline(argv);
+ return 0;
}
+/* Also keep enum COMMAND_INDEX updated. */
COMMAND commands[] = {
- {"help", cmd_help, cmd_help_help, "Display commands description"},
- {"add", cmd_add, cmd_add_help, "Add a new module from a specific file"},
- {"load", cmd_load, cmd_load_help, "Load a new schema from the searchdirs"},
- {"print", cmd_print, cmd_print_help, "Print a module"},
- {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
- {"list", cmd_list, cmd_list_help, "List all the loaded modules"},
- {"feature", cmd_feature, cmd_feature_help, "Print all features of module(s) with their state"},
- {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for schemas"},
- {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded modules"},
- {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
+ {
+ "help", cmd_help_opt, NULL, cmd_help_exec, NULL, cmd_help_help, NULL,
+ "Display commands description", "h"
+ },
+ {
+ "add", cmd_add_opt, cmd_add_dep, cmd_add_exec, NULL, cmd_add_help, NULL,
+ "Add a new module from a specific file", "DF:hiX"
+ },
+ {
+ "load", cmd_load_opt, cmd_load_dep, cmd_load_exec, NULL, cmd_load_help, NULL,
+ "Load a new schema from the searchdirs", "F:hiX"
+ },
+ {
+ "print", cmd_print_opt, cmd_print_dep, cmd_print_exec, NULL, cmd_print_help, NULL,
+ "Print a module", "f:hL:o:P:q"
+ },
+ {
+ "data", cmd_data_opt, cmd_data_dep, cmd_data_store, cmd_data_process, cmd_data_help, NULL,
+ "Load, validate and optionally print instance data", "d:ef:F:hmo:O:R:r:nt:x:"
+ },
+ {
+ "list", cmd_list_opt, cmd_list_dep, cmd_list_exec, NULL, cmd_list_help, NULL,
+ "List all the loaded modules", "f:h"
+ },
+ {
+ "feature", cmd_feature_opt, cmd_feature_dep, cmd_feature_exec, cmd_feature_fin, cmd_feature_help, NULL,
+ "Print all features of module(s) with their state", "haf"
+ },
+ {
+ "searchpath", cmd_searchpath_opt, NULL, cmd_searchpath_exec, NULL, cmd_searchpath_help, NULL,
+ "Print/set the search path(s) for schemas", "ch"
+ },
+ {
+ "extdata", cmd_extdata_opt, cmd_extdata_dep, cmd_extdata_exec, NULL, cmd_extdata_help, cmd_extdata_free,
+ "Set the specific data required by an extension", "ch"
+ },
+ {
+ "clear", cmd_clear_opt, cmd_clear_dep, cmd_clear_exec, NULL, cmd_clear_help, NULL,
+ "Clear the context - remove all the loaded modules", "iyhY:"
+ },
+ {
+ "verb", cmd_verb_opt, cmd_verb_dep, cmd_verb_exec, NULL, cmd_verb_help, NULL,
+ "Change verbosity", "h"
+ },
#ifndef NDEBUG
- {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups"},
+ {
+ "debug", cmd_debug_opt, cmd_debug_dep, cmd_debug_store, cmd_debug_setlog, cmd_debug_help, NULL,
+ "Display specific debug message groups", "h"
+ },
#endif
- {"quit", cmd_quit, NULL, "Quit the program"},
+ {"quit", NULL, NULL, cmd_quit_exec, NULL, NULL, NULL, "Quit the program", "h"},
/* synonyms for previous commands */
- {"?", cmd_help, NULL, "Display commands description"},
- {"exit", cmd_quit, NULL, "Quit the program"},
- {NULL, NULL, NULL, NULL}
+ {"?", NULL, NULL, cmd_help_exec, NULL, NULL, NULL, "Display commands description", "h"},
+ {"exit", NULL, NULL, cmd_quit_exec, NULL, NULL, NULL, "Quit the program", "h"},
+ {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};
diff --git a/tools/lint/cmd.h b/tools/lint/cmd.h
index 9f6f88d..bd2f2f2 100644
--- a/tools/lint/cmd.h
+++ b/tools/lint/cmd.h
@@ -2,9 +2,10 @@
* @file cmd.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool commands header
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -18,15 +19,44 @@
#include "libyang.h"
+struct yl_opt;
+
/**
* @brief command information
+ *
+ * Callback functions are in the order they should be called.
+ * First, the 'opt_func' should be called, which parses arguments from the command line and sets flags or pointers in
+ * the struct yl_opt. This type of function is for interactive mode and is optional.
+ * Then the 'dep_func' callback can check the struct yl_opt settings. Other items that depend on them can also be
+ * set. There is also an possibility for controlling the number of positional arguments and its implications.
+ * The most important callback is 'exec_func' where the command itself is executed. This function can even replace the
+ * entire libyang context. The function parameters are mainly found in the yl_opt structure. Optionally, the function
+ * can be called with a positional argument obtained from the command line. Some 'exec_func' are adapted to be called
+ * from non-interactive yanglint mode.
+ * The 'fun_func' complements the 'exec_func'. In some cases, the command execution must be divided into two stages.
+ * For example, the 'exec_func' is used to fill some items in the yl_opt structure from the positional
+ * arguments and then the 'fin_func' is used to perform the final action.
*/
typedef struct {
- char *name; /* User printable name of the function. */
+ /* User printable name of the function. */
+ char *name;
- void (*func)(struct ly_ctx **ctx, const char *); /* Function to call to do the command. */
- void (*help_func)(void); /* Display command help. */
- char *helpstring; /* Documentation for this function. */
+ /* Convert command line options to the data struct yl_opt. */
+ int (*opt_func)(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+ /* Additionally set dependent items and perform error checking. */
+ int (*dep_func)(struct yl_opt *yo, int posc);
+ /* Execute command. */
+ int (*exec_func)(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+ /* Finish execution of command. */
+ int (*fin_func)(struct ly_ctx *ctx, struct yl_opt *yo);
+ /* Display command help. */
+ void (*help_func)(void);
+ /* Freeing global variables allocated by the command. */
+ void (*free_func)(void);
+ /* Documentation for this function. */
+ char *helpstring;
+ /* Option characters used in function getopt_long. */
+ char *optstring;
} COMMAND;
/**
@@ -34,36 +64,333 @@ typedef struct {
*/
extern COMMAND commands[];
+/**
+ * @brief Index for global variable ::commands.
+ */
+enum COMMAND_INDEX {
+ CMD_HELP = 0,
+ CMD_ADD,
+ CMD_LOAD,
+ CMD_PRINT,
+ CMD_DATA,
+ CMD_LIST,
+ CMD_FEATURE,
+ CMD_SEARCHPATH,
+ CMD_EXTDATA,
+ CMD_CLEAR,
+ CMD_VERB,
+#ifndef NDEBUG
+ CMD_DEBUG,
+#endif
+};
+
+/**
+ * @brief For each cmd, call the COMMAND.free_func in the variable 'commands'.
+ */
+void cmd_free(void);
+
/* cmd_add.c */
-void cmd_add(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @brief Parse the arguments of an interactive command.
+ *
+ * @param[out] yo Context for yanglint.
+ * @param[in] cmdline String containing command line arguments.
+ * @param[out] posv Pointer to argv to a section of positional arguments.
+ * @param[out] posc Number of positional arguments.
+ * @return 0 on success.
+ */
+int cmd_add_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @brief Check the options and set dependent items in @p yo.
+ *
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posc number of positional arguments.
+ * @return 0 on success.
+ */
+int cmd_add_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Parse and compile a new module using filepath.
+ *
+ * @param[in,out] ctx Context for libyang.
+ * @param[in,out] yo Context for yanglint.
+ * @param[in] posv Path to the file where the new module is located.
+ * @return 0 on success.
+ */
+int cmd_add_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_add_help(void);
/* cmd_clear.c */
-void cmd_clear(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_clear_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_clear_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Clear libyang context.
+ *
+ * @param[in,out] ctx context for libyang that will be replaced with an empty one.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv not used.
+ * @return 0 on success.
+ */
+int cmd_clear_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_clear_help(void);
/* cmd_data.c */
-void cmd_data(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_data_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_data_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Store data file for later processing.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Path to the file where the data is located.
+ * @return 0 on success.
+ */
+int cmd_data_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+
+/**
+ * @brief Parse, validate and optionally print data instances.
+ *
+ * @param[in] ctx Context for libyang.
+ * @param[in] yo Context of yanglint. All necessary parameters should already be set.
+ * @return 0 on success.
+ */
+int cmd_data_process(struct ly_ctx *ctx, struct yl_opt *yo);
void cmd_data_help(void);
/* cmd_list.c */
-void cmd_list(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_list_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_list_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Print the list of modules in the current context.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Not used.
+ * @return 0 on success.
+ */
+int cmd_list_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_list_help(void);
/* cmd_feature.c */
-void cmd_feature(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_feature_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_feature_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Print the features the modules.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the module which features are to be printed.
+ * @return 0 on success.
+ */
+int cmd_feature_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+
+/**
+ * @brief Printing of features ends.
+ *
+ * @param[in] ctx context for libyang. Not used.
+ * @param[in] yo context for yanglint.
+ * @return 0 on success.
+ */
+int cmd_feature_fin(struct ly_ctx *ctx, struct yl_opt *yo);
void cmd_feature_help(void);
/* cmd_load.c */
-void cmd_load(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_load_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_load_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Parse and compile a new module using module name.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the module to be loaded into the context.
+ * @return 0 on success.
+ */
+int cmd_load_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_load_help(void);
/* cmd_print.c */
-void cmd_print(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_print_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_print_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Print a schema module.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the module to be printed. Can be NULL in the case of printing a node.
+ * @return 0 on success.
+ */
+int cmd_print_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_print_help(void);
/* cmd_searchpath.c */
-void cmd_searchpath(struct ly_ctx **ctx, const char *cmdline);
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_searchpath_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @brief Set the paths of directories where to search schema modules.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Path to the directory. Can be NULL in the case of printing a current searchdirs.
+ * @return 0 on success.
+ */
+int cmd_searchpath_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
void cmd_searchpath_help(void);
+/* cmd_extdata.c */
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_extdata_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_extdata_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Set path to the file required by the extension.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Path to the directory. Can be NULL in the case of printing a current path.
+ * @return 0 on success.
+ */
+int cmd_extdata_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+void cmd_extdata_help(void);
+void cmd_extdata_free(void);
+
+/* cmd_help.c */
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_help_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @brief Print help.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the command which help message is to be printed. Can be NULL.
+ * @return 0 on success.
+ */
+int cmd_help_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+void cmd_help_help(void);
+
+/* cmd_verb.c */
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_verb_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_verb_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Set the verbose level.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the verbose level to be set.
+ * @return 0 on success.
+ */
+int cmd_verb_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+void cmd_verb_help(void);
+
+/* cmd_debug.c */
+
+/**
+ * @copydoc cmd_add_opt
+ */
+int cmd_debug_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc);
+
+/**
+ * @copydoc cmd_add_dep
+ */
+int cmd_debug_dep(struct yl_opt *yo, int posc);
+
+/**
+ * @brief Store the type of debug messages for later processing.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint.
+ * @param[in] posv Name of the debug type to be set.
+ * @return 0 on success.
+ */
+int cmd_debug_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv);
+
+/**
+ * @brief Set debug logging.
+ *
+ * @param[in,out] ctx context for libyang.
+ * @param[in,out] yo context for yanglint. All necessary parameters should already be set.
+ * @return 0 on success.
+ */
+int cmd_debug_setlog(struct ly_ctx *ctx, struct yl_opt *yo);
+void cmd_debug_help(void);
+
#endif /* COMMANDS_H_ */
diff --git a/tools/lint/cmd_add.c b/tools/lint/cmd_add.c
index bbfdfd6..9f10711 100644
--- a/tools/lint/cmd_add.c
+++ b/tools/lint/cmd_add.c
@@ -2,9 +2,10 @@
* @file cmd_add.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'add' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -17,8 +18,8 @@
#include "cmd.h"
+#include <assert.h>
#include <getopt.h>
-#include <libgen.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -26,6 +27,8 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
+#include "yl_schema_features.h"
void
cmd_add_help(void)
@@ -44,133 +47,164 @@ cmd_add_help(void)
" -i, --make-implemented\n"
" Make the imported modules \"referenced\" from any loaded\n"
" <schema> module also implemented. If specified a second time,\n"
- " all the modules are set implemented.\n");
+ " all the modules are set implemented.\n"
+ " -X, --extended-leafref\n"
+ " Allow usage of deref() XPath function within leafref.\n");
}
-void
-cmd_add(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_add_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"disable-searchdir", no_argument, NULL, 'D'},
{"features", required_argument, NULL, 'F'},
{"help", no_argument, NULL, 'h'},
{"make-implemented", no_argument, NULL, 'i'},
+ {"extended-leafref", no_argument, NULL, 'X'},
{NULL, 0, NULL, 0}
};
- uint16_t options_ctx = 0;
- const char *all_features[] = {"*", NULL};
- struct ly_set fset = {0};
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "D:F:hi", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_ADD].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'D': /* --disable--search */
- if (options_ctx & LY_CTX_DISABLE_SEARCHDIRS) {
- YLMSG_W("The -D option specified too many times.\n");
- }
- if (options_ctx & LY_CTX_DISABLE_SEARCHDIR_CWD) {
- options_ctx &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
- options_ctx |= LY_CTX_DISABLE_SEARCHDIRS;
- } else {
- options_ctx |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+ if (yo->ctx_options & LY_CTX_DISABLE_SEARCHDIRS) {
+ YLMSG_W("The -D option specified too many times.");
}
+ yo_opt_update_disable_searchdir(yo);
break;
case 'F': /* --features */
- if (parse_features(optarg, &fset)) {
- goto cleanup;
+ if (parse_features(optarg, &yo->schema_features)) {
+ return 1;
}
break;
case 'h':
cmd_add_help();
- goto cleanup;
+ return 1;
case 'i': /* --make-implemented */
- if (options_ctx & LY_CTX_REF_IMPLEMENTED) {
- options_ctx &= ~LY_CTX_REF_IMPLEMENTED;
- options_ctx |= LY_CTX_ALL_IMPLEMENTED;
- } else {
- options_ctx |= LY_CTX_REF_IMPLEMENTED;
- }
+ yo_opt_update_make_implemented(yo);
+ break;
+
+ case 'X': /* --extended-leafref */
+ yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
break;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
- if (argc == optind) {
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_add_dep(struct yl_opt *yo, int posc)
+{
+ if (yo->interactive && !posc) {
/* no argument */
cmd_add_help();
- goto cleanup;
+ return 1;
}
-
- if (!fset.count) {
+ if (!yo->schema_features.count) {
/* no features, enable all of them */
- options_ctx |= LY_CTX_ENABLE_IMP_FEATURES;
+ yo->ctx_options |= LY_CTX_ENABLE_IMP_FEATURES;
}
- if (options_ctx) {
- ly_ctx_set_options(*ctx, options_ctx);
- }
+ return 0;
+}
- for (int i = 0; i < argc - optind; i++) {
- /* process the schema module files */
- LY_ERR ret;
- uint8_t path_unset = 1; /* flag to unset the path from the searchpaths list (if not already present) */
- char *dir, *module;
- const char **features = NULL;
- struct ly_in *in = NULL;
+static int
+store_parsed_module(const char *filepath, struct lys_module *mod, struct yl_opt *yo)
+{
+ assert(!yo->interactive);
- if (parse_schema_path(argv[optind + i], &dir, &module)) {
- goto cleanup;
+ if (yo->schema_out_format || yo->feature_param_format) {
+ if (ly_set_add(&yo->schema_modules, (void *)mod, 1, NULL)) {
+ YLMSG_E("Storing parsed schema module (%s) for print failed.", filepath);
+ return 1;
}
+ }
- /* add temporarily also the path of the module itself */
- if (ly_ctx_set_searchdir(*ctx, dirname(dir)) == LY_EEXIST) {
- path_unset = 0;
- }
+ return 0;
+}
- /* get features list for this module */
- if (!fset.count) {
- features = all_features;
- } else {
- get_features(&fset, module, &features);
- }
+int
+cmd_add_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ const char *all_features[] = {"*", NULL};
+ LY_ERR ret;
+ uint8_t path_unset = 1; /* flag to unset the path from the searchpaths list (if not already present) */
+ char *dir, *modname = NULL;
+ const char **features = NULL;
+ struct ly_in *in = NULL;
+ struct lys_module *mod;
+
+ assert(posv);
- /* temporary cleanup */
- free(dir);
- free(module);
+ if (yo->ctx_options) {
+ ly_ctx_set_options(*ctx, yo->ctx_options);
+ }
+
+ if (parse_schema_path(posv, &dir, &modname)) {
+ return 1;
+ }
- /* prepare input handler */
- ret = ly_in_new_filepath(argv[optind + i], 0, &in);
- if (ret) {
+ if (!yo->interactive) {
+ /* The module should already be parsed if yang_lib_file was set. */
+ if (yo->yang_lib_file && (mod = ly_ctx_get_module_implemented(*ctx, modname))) {
+ ret = store_parsed_module(posv, mod, yo);
goto cleanup;
}
+ /* parse module */
+ }
+
+ /* add temporarily also the path of the module itself */
+ if (ly_ctx_set_searchdir(*ctx, dir) == LY_EEXIST) {
+ path_unset = 0;
+ }
+
+ /* get features list for this module */
+ if (!yo->schema_features.count) {
+ features = all_features;
+ } else {
+ get_features(&yo->schema_features, modname, &features);
+ }
+
+ /* prepare input handler */
+ ret = ly_in_new_filepath(posv, 0, &in);
+ if (ret) {
+ goto cleanup;
+ }
- /* parse the file */
- ret = lys_parse(*ctx, in, LYS_IN_UNKNOWN, features, NULL);
- ly_in_free(in, 1);
- ly_ctx_unset_searchdir_last(*ctx, path_unset);
+ /* parse the file */
+ ret = lys_parse(*ctx, in, LYS_IN_UNKNOWN, features, &mod);
+ ly_in_free(in, 1);
+ ly_ctx_unset_searchdir_last(*ctx, path_unset);
+ if (ret) {
+ goto cleanup;
+ }
- if (ret) {
- /* libyang printed the error messages */
+ if (!yo->interactive) {
+ if ((ret = store_parsed_module(posv, mod, yo))) {
goto cleanup;
}
}
cleanup:
- if (options_ctx) {
- ly_ctx_unset_options(*ctx, options_ctx);
- }
- ly_set_erase(&fset, free_features);
- free_cmdline(argv);
+ free(dir);
+ free(modname);
+
+ return ret;
}
diff --git a/tools/lint/cmd_clear.c b/tools/lint/cmd_clear.c
index 5eed6ff..4a869af 100644
--- a/tools/lint/cmd_clear.c
+++ b/tools/lint/cmd_clear.c
@@ -2,9 +2,10 @@
* @file cmd_clear.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'clear' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -24,6 +25,7 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
void
cmd_clear_help(void)
@@ -32,68 +34,139 @@ cmd_clear_help(void)
" Replace the current context with an empty one, searchpaths\n"
" are not kept.\n\n"
" -i, --make-implemented\n"
- " Make the imported modules \"referenced\" from any loaded\n"
- " module also implemented. If specified a second time, all the\n"
- " modules are set implemented.\n"
+ " When loading a module into the context, the imported 'referenced'\n"
+ " modules will also be implemented. If specified a second time,\n"
+ " all the modules will be implemented.\n"
" -y, --yang-library\n"
" Load and implement internal \"ietf-yang-library\" YANG module.\n"
" Note that this module includes definitions of mandatory state\n"
- " data that can result in unexpected data validation errors.\n");
-#if 0
- " If <yang-library-data> path specified, load the modules\n"
- " according to the provided yang library data.\n"
-#endif
+ " data that can result in unexpected data validation errors.\n"
+ " -Y FILE, --yang-library-file=FILE\n"
+ " Parse FILE with \"ietf-yang-library\" data and use them to\n"
+ " create an exact YANG schema context. Searchpaths defined so far\n"
+ " are used, but then deleted.\n");
}
-void
-cmd_clear(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_clear_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
- {"make-implemented", no_argument, NULL, 'i'},
- {"yang-library", no_argument, NULL, 'y'},
+ {"make-implemented", no_argument, NULL, 'i'},
+ {"yang-library", no_argument, NULL, 'y'},
+ {"yang-library-file", no_argument, NULL, 'Y'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
- uint16_t options_ctx = LY_CTX_NO_YANGLIBRARY;
- struct ly_ctx *ctx_new;
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ yo->ctx_options = LY_CTX_NO_YANGLIBRARY;
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "iyh", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_CLEAR].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'i':
- if (options_ctx & LY_CTX_REF_IMPLEMENTED) {
- options_ctx &= ~LY_CTX_REF_IMPLEMENTED;
- options_ctx |= LY_CTX_ALL_IMPLEMENTED;
+ if (yo->ctx_options & LY_CTX_REF_IMPLEMENTED) {
+ yo->ctx_options &= ~LY_CTX_REF_IMPLEMENTED;
+ yo->ctx_options |= LY_CTX_ALL_IMPLEMENTED;
} else {
- options_ctx |= LY_CTX_REF_IMPLEMENTED;
+ yo->ctx_options |= LY_CTX_REF_IMPLEMENTED;
}
break;
case 'y':
- options_ctx &= ~LY_CTX_NO_YANGLIBRARY;
+ yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
+ break;
+ case 'Y':
+ yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
+ yo->yang_lib_file = optarg;
+ if (!yo->yang_lib_file) {
+ YLMSG_E("Memory allocation error.");
+ return 1;
+ }
break;
case 'h':
cmd_clear_help();
- goto cleanup;
+ return 1;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return rc;
+}
+
+int
+cmd_clear_dep(struct yl_opt *yo, int posc)
+{
+ (void) yo;
+
+ if (posc) {
+ YLMSG_E("No positional arguments are allowed.");
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Convert searchpaths into single string.
+ *
+ * @param[in] ctx Context with searchpaths.
+ * @param[out] searchpaths Collection of paths in the single string. Paths are delimited by colon ":"
+ * (on Windows, used semicolon ";" instead).
+ * @return LY_ERR value.
+ */
+static LY_ERR
+searchpaths_to_str(const struct ly_ctx *ctx, char **searchpaths)
+{
+ uint32_t i;
+ int rc = 0;
+ const char * const *dirs = ly_ctx_get_searchdirs(ctx);
+
+ for (i = 0; dirs[i]; ++i) {
+ rc = searchpath_strcat(searchpaths, dirs[i]);
+ if (!rc) {
+ break;
}
}
- if (ly_ctx_new(NULL, options_ctx, &ctx_new)) {
- YLMSG_W("Failed to create context.\n");
- goto cleanup;
+ return rc;
+}
+
+int
+cmd_clear_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void) posv;
+ struct ly_ctx *ctx_new = NULL;
+
+ if (yo->yang_lib_file) {
+ if (searchpaths_to_str(*ctx, &yo->searchpaths)) {
+ YLMSG_E("Storing searchpaths failed.");
+ return 1;
+ }
+ if (ly_ctx_new_ylpath(yo->searchpaths, yo->yang_lib_file, LYD_UNKNOWN, yo->ctx_options, &ctx_new)) {
+ YLMSG_E("Unable to create libyang context with yang-library data.");
+ return 1;
+ }
+ } else {
+ if (ly_ctx_new(NULL, yo->ctx_options, &ctx_new)) {
+ YLMSG_W("Failed to create context.");
+ return 1;
+ }
}
+ /* Global variables in commands are also deleted. */
+ cmd_free();
+
ly_ctx_destroy(*ctx);
*ctx = ctx_new;
-cleanup:
- free_cmdline(argv);
+ return 0;
}
diff --git a/tools/lint/cmd_data.c b/tools/lint/cmd_data.c
index 25449f5..44fb237 100644
--- a/tools/lint/cmd_data.c
+++ b/tools/lint/cmd_data.c
@@ -2,9 +2,10 @@
* @file cmd_data.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'data' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -17,6 +18,7 @@
#include "cmd.h"
+#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <stdint.h>
@@ -27,18 +29,23 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
-void
-cmd_data_help(void)
+static void
+cmd_data_help_header(void)
{
printf("Usage: data [-emn] [-t TYPE]\n"
" [-F FORMAT] [-f FORMAT] [-d DEFAULTS] [-o OUTFILE] <data1> ...\n"
" data [-n] -t (rpc | notif | reply) [-O FILE]\n"
" [-F FORMAT] [-f FORMAT] [-d DEFAULTS] [-o OUTFILE] <data1> ...\n"
" data [-en] [-t TYPE] [-F FORMAT] -x XPATH [-o OUTFILE] <data1> ...\n"
- " Parse, validate and optionally print data instances\n\n"
+ " Parse, validate and optionally print data instances\n");
+}
- " -t TYPE, --type=TYPE\n"
+static void
+cmd_data_help_type(void)
+{
+ printf(" -t TYPE, --type=TYPE\n"
" Specify data tree type in the input data file(s):\n"
" data - Complete datastore with status data (default type).\n"
" config - Configuration datastore (without status data).\n"
@@ -47,39 +54,45 @@ cmd_data_help(void)
" edit - Content of the NETCONF <edit-config> operation.\n"
" rpc - Content of the NETCONF <rpc> message, defined as YANG's\n"
" RPC/Action input statement.\n"
+ " nc-rpc - Similar to 'rpc' but expect and check also the NETCONF\n"
+ " envelopes <rpc> or <action>.\n"
" reply - Reply to the RPC/Action. Note that the reply data are\n"
" expected inside a container representing the original\n"
" RPC/Action. This is necessary to identify appropriate\n"
" data definitions in the schema module.\n"
+ " nc-reply - Similar to 'reply' but expect and check also the NETCONF\n"
+ " envelope <rpc-reply> with output data nodes as direct\n"
+ " descendants. The original RPC/action invocation is expected\n"
+ " in a separate parameter '-R' and is parsed as 'nc-rpc'.\n"
" notif - Notification instance (content of the <notification>\n"
- " element without <eventTime>).\n\n"
-
- " -e, --present Validate only with the schema modules whose data actually\n"
- " exist in the provided input data files. Takes effect only\n"
- " with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
- " mandatory nodes from modules which data are not present in the\n"
- " provided input data files.\n"
- " -m, --merge Merge input data files into a single tree and validate at\n"
- " once.The option has effect only for 'data' and 'config' TYPEs.\n"
- " In case of using -x option, the data are always merged.\n"
- " -n, --not-strict\n"
- " Do not require strict data parsing (silently skip unknown data),\n"
- " has no effect for schemas.\n\n"
- " -O FILE, --operational=FILE\n"
- " Provide optional data to extend validation of the 'rpc',\n"
- " 'reply' or 'notif' TYPEs. The FILE is supposed to contain\n"
- " the :running configuration datastore and state data\n"
- " (operational datastore) referenced from the RPC/Notification.\n\n"
+ " element without <eventTime>).\n"
+ " nc-notif - Similar to 'notif' but expect and check also the NETCONF\n"
+ " envelope <notification> with element <eventTime> and its\n"
+ " sibling as the actual notification.\n");
+}
- " -f FORMAT, --format=FORMAT\n"
+static void
+cmd_data_help_format(void)
+{
+ printf(" -f FORMAT, --format=FORMAT\n"
" Print the data in one of the following formats:\n"
" xml, json, lyb\n"
- " Note that the LYB format requires the -o option specified.\n"
- " -F FORMAT, --in-format=FORMAT\n"
+ " Note that the LYB format requires the -o option specified.\n");
+}
+
+static void
+cmd_data_help_in_format(void)
+{
+ printf(" -F FORMAT, --in-format=FORMAT\n"
" Load the data in one of the following formats:\n"
" xml, json, lyb\n"
- " If input format not specified, it is detected from the file extension.\n"
- " -d MODE, --default=MODE\n"
+ " If input format not specified, it is detected from the file extension.\n");
+}
+
+static void
+cmd_data_help_default(void)
+{
+ printf(" -d MODE, --default=MODE\n"
" Print data with default values, according to the MODE\n"
" (to print attributes, ietf-netconf-with-defaults model\n"
" must be loaded):\n"
@@ -87,24 +100,58 @@ cmd_data_help(void)
" all-tagged - Add missing default nodes and mark all the default\n"
" nodes with the attribute.\n"
" trim - Remove all nodes with a default value.\n"
- " implicit-tagged - Add missing nodes and mark them with the attribute.\n"
- " -o OUTFILE, --output=OUTFILE\n"
- " Write the output to OUTFILE instead of stdout.\n\n"
+ " implicit-tagged - Add missing nodes and mark them with the attribute.\n");
+}
- " -x XPATH, --xpath=XPATH\n"
- " Evaluate XPATH expression and print the nodes satysfying the.\n"
+static void
+cmd_data_help_xpath(void)
+{
+ printf(" -x XPATH, --xpath=XPATH\n"
+ " Evaluate XPATH expression and print the nodes satisfying the\n"
" expression. The output format is specific and the option cannot\n"
" be combined with the -f and -d options. Also all the data\n"
" inputs are merged into a single data tree where the expression\n"
- " is evaluated, so the -m option is always set implicitly.\n\n");
-
+ " is evaluated, so the -m option is always set implicitly.\n");
}
void
-cmd_data(struct ly_ctx **ctx, const char *cmdline)
+cmd_data_help(void)
+{
+ cmd_data_help_header();
+ printf("\n");
+ cmd_data_help_type();
+ printf(" -e, --present Validate only with the schema modules whose data actually\n"
+ " exist in the provided input data files. Takes effect only\n"
+ " with the 'data' or 'config' TYPEs. Used to avoid requiring\n"
+ " mandatory nodes from modules which data are not present in the\n"
+ " provided input data files.\n"
+ " -m, --merge Merge input data files into a single tree and validate at\n"
+ " once.The option has effect only for 'data' and 'config' TYPEs.\n"
+ " In case of using -x option, the data are always merged.\n"
+ " -n, --not-strict\n"
+ " Do not require strict data parsing (silently skip unknown data),\n"
+ " has no effect for schemas.\n"
+ " -O FILE, --operational=FILE\n"
+ " Provide optional data to extend validation of the 'rpc',\n"
+ " 'reply' or 'notif' TYPEs. The FILE is supposed to contain\n"
+ " the operational datastore referenced from the operation.\n"
+ " In case of a nested notification or action, its parent\n"
+ " existence is also checked in these operational data.\n"
+ " -R FILE, --reply-rpc=FILE\n"
+ " Provide source RPC for parsing of the 'nc-reply' TYPE. The FILE\n"
+ " is supposed to contain the source 'nc-rpc' operation of the reply.\n");
+ cmd_data_help_format();
+ cmd_data_help_in_format();
+ printf(" -o OUTFILE, --output=OUTFILE\n"
+ " Write the output to OUTFILE instead of stdout.\n");
+ cmd_data_help_xpath();
+ printf("\n");
+}
+
+int
+cmd_data_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"defaults", required_argument, NULL, 'd'},
@@ -115,214 +162,525 @@ cmd_data(struct ly_ctx **ctx, const char *cmdline)
{"merge", no_argument, NULL, 'm'},
{"output", required_argument, NULL, 'o'},
{"operational", required_argument, NULL, 'O'},
+ {"reply-rpc", required_argument, NULL, 'R'},
{"not-strict", no_argument, NULL, 'n'},
{"type", required_argument, NULL, 't'},
{"xpath", required_argument, NULL, 'x'},
{NULL, 0, NULL, 0}
};
- uint8_t data_merge = 0;
- uint32_t options_print = 0;
- uint32_t options_parse = YL_DEFAULT_DATA_PARSE_OPTIONS;
- uint32_t options_validate = 0;
- enum lyd_type data_type = 0;
uint8_t data_type_set = 0;
- LYD_FORMAT outformat = LYD_UNKNOWN;
- LYD_FORMAT informat = LYD_UNKNOWN;
- struct ly_out *out = NULL;
- struct cmdline_file *operational = NULL;
- struct ly_set inputs = {0};
- struct ly_set xpaths = {0};
-
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+
+ yo->data_parse_options = YL_DEFAULT_DATA_PARSE_OPTIONS;
+ yo->data_validate_options = YL_DEFAULT_DATA_VALIDATE_OPTIONS;
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "d:ef:F:hmo:O:r:nt:x:", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_DATA].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'd': /* --default */
- if (!strcasecmp(optarg, "all")) {
- options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
- } else if (!strcasecmp(optarg, "all-tagged")) {
- options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
- } else if (!strcasecmp(optarg, "trim")) {
- options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
- } else if (!strcasecmp(optarg, "implicit-tagged")) {
- options_print = (options_print & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
- } else {
- YLMSG_E("Unknown default mode %s\n", optarg);
- cmd_data_help();
- goto cleanup;
+ if (yo_opt_update_data_default(optarg, yo)) {
+ YLMSG_E("Unknown default mode %s.", optarg);
+ cmd_data_help_default();
+ return 1;
}
break;
case 'f': /* --format */
- if (!strcasecmp(optarg, "xml")) {
- outformat = LYD_XML;
- } else if (!strcasecmp(optarg, "json")) {
- outformat = LYD_JSON;
- } else if (!strcasecmp(optarg, "lyb")) {
- outformat = LYD_LYB;
- } else {
- YLMSG_E("Unknown output format %s\n", optarg);
- cmd_data_help();
- goto cleanup;
+ if (yl_opt_update_data_out_format(optarg, yo)) {
+ cmd_data_help_format();
+ return 1;
}
break;
case 'F': /* --in-format */
- if (!strcasecmp(optarg, "xml")) {
- informat = LYD_XML;
- } else if (!strcasecmp(optarg, "json")) {
- informat = LYD_JSON;
- } else if (!strcasecmp(optarg, "lyb")) {
- informat = LYD_LYB;
- } else {
- YLMSG_E("Unknown input format %s\n", optarg);
- cmd_data_help();
- goto cleanup;
+ if (yo_opt_update_data_in_format(optarg, yo)) {
+ YLMSG_E("Unknown input format %s.", optarg);
+ cmd_data_help_in_format();
+ return 1;
}
break;
case 'o': /* --output */
- if (out) {
- YLMSG_E("Only a single output can be specified.\n");
- goto cleanup;
+ if (yo->out) {
+ YLMSG_E("Only a single output can be specified.");
+ return 1;
} else {
- if (ly_out_new_filepath(optarg, &out)) {
- YLMSG_E("Unable open output file %s (%s)\n", optarg, strerror(errno));
- goto cleanup;
+ if (ly_out_new_filepath(optarg, &yo->out)) {
+ YLMSG_E("Unable open output file %s (%s).", optarg, strerror(errno));
+ return 1;
}
}
break;
- case 'O': { /* --operational */
- struct ly_in *in;
- LYD_FORMAT f;
-
- if (operational) {
- YLMSG_E("The operational datastore (-O) cannot be set multiple times.\n");
- goto cleanup;
+ case 'O': /* --operational */
+ if (yo->data_operational.path) {
+ YLMSG_E("The operational datastore (-O) cannot be set multiple times.");
+ return 1;
}
- if (get_input(optarg, NULL, &f, &in)) {
- goto cleanup;
+ yo->data_operational.path = optarg;
+ break;
+ case 'R': /* --reply-rpc */
+ if (yo->reply_rpc.path) {
+ YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.");
+ return 1;
}
- operational = fill_cmdline_file(NULL, in, optarg, f);
+ yo->reply_rpc.path = optarg;
break;
- } /* case 'O' */
-
case 'e': /* --present */
- options_validate |= LYD_VALIDATE_PRESENT;
+ yo->data_validate_options |= LYD_VALIDATE_PRESENT;
break;
case 'm': /* --merge */
- data_merge = 1;
+ yo->data_merge = 1;
break;
case 'n': /* --not-strict */
- options_parse &= ~LYD_PARSE_STRICT;
+ yo->data_parse_options &= ~LYD_PARSE_STRICT;
break;
case 't': /* --type */
if (data_type_set) {
- YLMSG_E("The data type (-t) cannot be set multiple times.\n");
- goto cleanup;
+ YLMSG_E("The data type (-t) cannot be set multiple times.");
+ return 1;
}
- if (!strcasecmp(optarg, "config")) {
- options_parse |= LYD_PARSE_NO_STATE;
- options_validate |= LYD_VALIDATE_NO_STATE;
- } else if (!strcasecmp(optarg, "get")) {
- options_parse |= LYD_PARSE_ONLY;
- } else if (!strcasecmp(optarg, "getconfig") || !strcasecmp(optarg, "get-config") || !strcasecmp(optarg, "edit")) {
- options_parse |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
- } else if (!strcasecmp(optarg, "rpc") || !strcasecmp(optarg, "action")) {
- data_type = LYD_TYPE_RPC_YANG;
- } else if (!strcasecmp(optarg, "reply") || !strcasecmp(optarg, "rpcreply")) {
- data_type = LYD_TYPE_REPLY_YANG;
- } else if (!strcasecmp(optarg, "notif") || !strcasecmp(optarg, "notification")) {
- data_type = LYD_TYPE_NOTIF_YANG;
- } else if (!strcasecmp(optarg, "data")) {
- /* default option */
- } else {
- YLMSG_E("Unknown data tree type %s.\n", optarg);
- cmd_data_help();
- goto cleanup;
+ if (yl_opt_update_data_type(optarg, yo)) {
+ YLMSG_E("Unknown data tree type %s.", optarg);
+ cmd_data_help_type();
+ return 1;
}
data_type_set = 1;
break;
case 'x': /* --xpath */
- if (ly_set_add(&xpaths, optarg, 0, NULL)) {
- YLMSG_E("Storing XPath \"%s\" failed.\n", optarg);
- goto cleanup;
+ if (ly_set_add(&yo->data_xpath, optarg, 0, NULL)) {
+ YLMSG_E("Storing XPath \"%s\" failed.", optarg);
+ return 1;
}
break;
case 'h': /* --help */
cmd_data_help();
- goto cleanup;
+ return 1;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
- if (optind == argc) {
- YLMSG_E("Missing the data file to process.\n");
- goto cleanup;
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return rc;
+}
+
+int
+cmd_data_dep(struct yl_opt *yo, int posc)
+{
+ if (yo->interactive && !posc) {
+ YLMSG_E("Missing the data file to process.");
+ return 1;
}
- if (data_merge) {
- if (data_type || (options_parse & LYD_PARSE_ONLY)) {
+ if (yo->data_merge) {
+ if (yo->data_type || (yo->data_parse_options & LYD_PARSE_ONLY)) {
/* switch off the option, incompatible input data type */
- data_merge = 0;
+ YLMSG_W("The --merge option has effect only for 'data' and 'config' TYPEs.");
+ yo->data_merge = 0;
} else {
/* postpone validation after the merge of all the input data */
- options_parse |= LYD_PARSE_ONLY;
+ yo->data_parse_options |= LYD_PARSE_ONLY;
}
- } else if (xpaths.count) {
- data_merge = 1;
+ } else if (yo->data_xpath.count) {
+ yo->data_merge = 1;
}
- if (xpaths.count && outformat) {
- YLMSG_E("The --format option cannot be combined with --xpath option.\n");
- cmd_data_help();
- goto cleanup;
+ if (yo->data_xpath.count && (yo->schema_out_format || yo->data_out_format)) {
+ YLMSG_E("The --format option cannot be combined with --xpath option.");
+ if (yo->interactive) {
+ cmd_data_help_xpath();
+ }
+ return 1;
+ }
+ if (yo->data_xpath.count && (yo->data_print_options & LYD_PRINT_WD_MASK)) {
+ YLMSG_E("The --default option cannot be combined with --xpath option.");
+ if (yo->interactive) {
+ cmd_data_help_xpath();
+ }
+ return 1;
+ }
+
+ if (yo->data_operational.path && !yo->data_type) {
+ YLMSG_W("Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types.");
+ yo->data_operational.path = NULL;
+ }
+
+ if (yo->reply_rpc.path && (yo->data_type != LYD_TYPE_REPLY_NETCONF)) {
+ YLMSG_W("Source RPC is needed only for NETCONF Reply input data type.");
+ yo->data_operational.path = NULL;
+ } else if (!yo->reply_rpc.path && (yo->data_type == LYD_TYPE_REPLY_NETCONF)) {
+ YLMSG_E("Missing source RPC (-R) for NETCONF Reply input data type.");
+ return 1;
+ }
+
+ if (!yo->out && (yo->data_out_format == LYD_LYB)) {
+ YLMSG_E("The LYB format requires the -o option specified.");
+ return 1;
+ }
+
+ /* default output stream */
+ if (!yo->out) {
+ if (ly_out_new_file(stdout, &yo->out)) {
+ YLMSG_E("Unable to set stdout as output.");
+ return 1;
+ }
+ yo->out_stdout = 1;
}
- if (operational && !data_type) {
- YLMSG_W("Operational datastore takes effect only with RPCs/Actions/Replies/Notifications input data types.\n");
- free_cmdline_file(operational);
- operational = NULL;
+ /* process the operational and/or reply RPC content if any */
+ if (yo->data_operational.path) {
+ if (get_input(yo->data_operational.path, NULL, &yo->data_operational.format, &yo->data_operational.in)) {
+ return -1;
+ }
+ }
+ if (yo->reply_rpc.path) {
+ if (get_input(yo->reply_rpc.path, NULL, &yo->reply_rpc.format, &yo->reply_rpc.in)) {
+ return -1;
+ }
}
+ return 0;
+}
+
+int
+cmd_data_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void) ctx;
+ struct ly_in *in;
+ LYD_FORMAT format_data;
+
+ assert(posv);
+
+ format_data = yo->data_in_format;
+
/* process input data files provided as standalone command line arguments */
- for (int i = 0; i < argc - optind; i++) {
- struct ly_in *in;
+ if (get_input(posv, NULL, &format_data, &in)) {
+ return 1;
+ }
+
+ if (!fill_cmdline_file(&yo->data_inputs, in, posv, format_data)) {
+ ly_in_free(in, 1);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Evaluate xpath adn print result.
+ *
+ * @param[in] tree Data tree.
+ * @param[in] xpath Xpath to evaluate.
+ * @return 0 on success.
+ */
+static int
+evaluate_xpath(const struct lyd_node *tree, const char *xpath)
+{
+ struct ly_set *set = NULL;
+
+ if (lyd_find_xpath(tree, xpath, &set)) {
+ return -1;
+ }
+
+ /* print result */
+ printf("XPath \"%s\" evaluation result:\n", xpath);
+ if (!set->count) {
+ printf("\tEmpty\n");
+ } else {
+ for (uint32_t u = 0; u < set->count; ++u) {
+ struct lyd_node *node = (struct lyd_node *)set->objs[u];
+
+ printf(" %s \"%s\"", lys_nodetype2str(node->schema->nodetype), node->schema->name);
+ if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+ printf(" (value: \"%s\")\n", lyd_get_value(node));
+ } else if (node->schema->nodetype == LYS_LIST) {
+ printf(" (");
+ for (struct lyd_node *key = ((struct lyd_node_inner *)node)->child; key && lysc_is_key(key->schema); key = key->next) {
+ printf("%s\"%s\": \"%s\";", (key != ((struct lyd_node_inner *)node)->child) ? " " : "",
+ key->schema->name, lyd_get_value(key));
+ }
+ printf(")\n");
+ } else {
+ printf("\n");
+ }
+ }
+ }
+
+ ly_set_free(set, NULL);
+ return 0;
+}
+
+/**
+ * @brief Checking that a parent data node exists in the datastore for the nested-notification and action.
+ *
+ * @param[in] op Operation to check.
+ * @param[in] oper_tree Data from datastore.
+ * @param[in] operational_f Operational datastore file information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+check_operation_parent(struct lyd_node *op, struct lyd_node *oper_tree, struct cmdline_file *operational_f)
+{
+ LY_ERR ret;
+ struct ly_set *set = NULL;
+ char *path = NULL;
+
+ if (!op || !lyd_parent(op)) {
+ /* The function is defined only for nested-notification and action. */
+ return LY_SUCCESS;
+ }
+
+ if (!operational_f || (operational_f && !operational_f->in)) {
+ YLMSG_E("The --operational parameter needed to validate operation \"%s\" is missing.", LYD_NAME(op));
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+ path = lyd_path(lyd_parent(op), LYD_PATH_STD, NULL, 0);
+ if (!path) {
+ ret = LY_EMEM;
+ goto cleanup;
+ }
+
+ if (!oper_tree) {
+ YLMSG_W("Operational datastore is empty or contains unknown data.");
+ YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.", LYD_NAME(op), path);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ if ((ret = lyd_find_xpath(oper_tree, path, &set))) {
+ goto cleanup;
+ }
+ if (!set->count) {
+ YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.", LYD_NAME(op), path);
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+
+cleanup:
+ ly_set_free(set, NULL);
+ free(path);
+
+ return ret;
+}
+
+/**
+ * @brief Process the input data files - parse, validate and print according to provided options.
+ *
+ * @param[in] ctx libyang context with schema.
+ * @param[in] type The type of data in the input files.
+ * @param[in] merge Flag if the data should be merged before validation.
+ * @param[in] out_format Data format for printing.
+ * @param[in] out The output handler for printing.
+ * @param[in] parse_options Parser options.
+ * @param[in] validate_options Validation options.
+ * @param[in] print_options Printer options.
+ * @param[in] operational Optional operational datastore file information for the case of an extended validation of
+ * operation(s).
+ * @param[in] reply_rpc Source RPC operation file information for parsing NETCONF rpc-reply.
+ * @param[in] inputs Set of file informations of input data files.
+ * @param[in] xpaths The set of XPaths to be evaluated on the processed data tree, basic information about the resulting set
+ * is printed. Alternative to data printing.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+process_data(struct ly_ctx *ctx, enum lyd_type type, uint8_t merge, LYD_FORMAT out_format,
+ struct ly_out *out, uint32_t parse_options, uint32_t validate_options, uint32_t print_options,
+ struct cmdline_file *operational, struct cmdline_file *reply_rpc, struct ly_set *inputs,
+ struct ly_set *xpaths)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_node *tree = NULL, *op = NULL, *envp = NULL, *merged_tree = NULL, *oper_tree = NULL;
+ const char *xpath;
+ struct ly_set *set = NULL;
- if (get_input(argv[optind + i], NULL, &informat, &in)) {
+ /* additional operational datastore */
+ if (operational && operational->in) {
+ ret = lyd_parse_data(ctx, NULL, operational->in, operational->format, LYD_PARSE_ONLY, 0, &oper_tree);
+ if (ret) {
+ YLMSG_E("Failed to parse operational datastore file \"%s\".", operational->path);
goto cleanup;
}
+ }
+
+ for (uint32_t u = 0; u < inputs->count; ++u) {
+ struct cmdline_file *input_f = (struct cmdline_file *)inputs->objs[u];
+
+ switch (type) {
+ case LYD_TYPE_DATA_YANG:
+ ret = lyd_parse_data(ctx, NULL, input_f->in, input_f->format, parse_options, validate_options, &tree);
+ break;
+ case LYD_TYPE_RPC_YANG:
+ case LYD_TYPE_REPLY_YANG:
+ case LYD_TYPE_NOTIF_YANG:
+ ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, type, &tree, &op);
+ break;
+ case LYD_TYPE_RPC_NETCONF:
+ case LYD_TYPE_NOTIF_NETCONF:
+ ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, type, &envp, &op);
+
+ /* adjust pointers */
+ for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
+ break;
+ case LYD_TYPE_REPLY_NETCONF:
+ /* parse source RPC operation */
+ assert(reply_rpc && reply_rpc->in);
+ ret = lyd_parse_op(ctx, NULL, reply_rpc->in, reply_rpc->format, LYD_TYPE_RPC_NETCONF, &envp, &op);
+ if (ret) {
+ YLMSG_E("Failed to parse source NETCONF RPC operation file \"%s\".", reply_rpc->path);
+ goto cleanup;
+ }
+
+ /* adjust pointers */
+ for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
+
+ /* free input */
+ lyd_free_siblings(lyd_child(op));
+
+ /* we do not care */
+ lyd_free_all(envp);
+ envp = NULL;
- if (!fill_cmdline_file(&inputs, in, argv[optind + i], informat)) {
- ly_in_free(in, 1);
+ ret = lyd_parse_op(ctx, op, input_f->in, input_f->format, type, &envp, NULL);
+ break;
+ default:
+ YLMSG_E("Internal error (%s:%d).", __FILE__, __LINE__);
+ goto cleanup;
+ }
+
+ if (ret) {
+ YLMSG_E("Failed to parse input data file \"%s\".", input_f->path);
goto cleanup;
}
+
+ if (merge) {
+ /* merge the data so far parsed for later validation and print */
+ if (!merged_tree) {
+ merged_tree = tree;
+ } else {
+ ret = lyd_merge_siblings(&merged_tree, tree, LYD_MERGE_DESTRUCT);
+ if (ret) {
+ YLMSG_E("Merging %s with previous data failed.", input_f->path);
+ goto cleanup;
+ }
+ }
+ tree = NULL;
+ } else if (out_format) {
+ /* print */
+ switch (type) {
+ case LYD_TYPE_DATA_YANG:
+ lyd_print_all(out, tree, out_format, print_options);
+ break;
+ case LYD_TYPE_RPC_YANG:
+ case LYD_TYPE_REPLY_YANG:
+ case LYD_TYPE_NOTIF_YANG:
+ case LYD_TYPE_RPC_NETCONF:
+ case LYD_TYPE_NOTIF_NETCONF:
+ lyd_print_tree(out, tree, out_format, print_options);
+ break;
+ case LYD_TYPE_REPLY_NETCONF:
+ /* just the output */
+ lyd_print_tree(out, lyd_child(tree), out_format, print_options);
+ break;
+ default:
+ assert(0);
+ }
+ } else {
+ /* validation of the RPC/Action/reply/Notification with the operational datastore, if any */
+ switch (type) {
+ case LYD_TYPE_DATA_YANG:
+ /* already validated */
+ break;
+ case LYD_TYPE_RPC_YANG:
+ case LYD_TYPE_RPC_NETCONF:
+ ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_RPC_YANG, NULL);
+ break;
+ case LYD_TYPE_REPLY_YANG:
+ case LYD_TYPE_REPLY_NETCONF:
+ ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_REPLY_YANG, NULL);
+ break;
+ case LYD_TYPE_NOTIF_YANG:
+ case LYD_TYPE_NOTIF_NETCONF:
+ ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_NOTIF_YANG, NULL);
+ break;
+ default:
+ assert(0);
+ }
+ if (ret) {
+ if (operational->path) {
+ YLMSG_E("Failed to validate input data file \"%s\" with operational datastore \"%s\".",
+ input_f->path, operational->path);
+ } else {
+ YLMSG_E("Failed to validate input data file \"%s\".", input_f->path);
+ }
+ goto cleanup;
+ }
+
+ if ((ret = check_operation_parent(op, oper_tree, operational))) {
+ goto cleanup;
+ }
+ }
+
+ /* next iter */
+ lyd_free_all(tree);
+ tree = NULL;
+ lyd_free_all(envp);
+ envp = NULL;
}
- /* default output stream */
- if (!out) {
- if (ly_out_new_file(stdout, &out)) {
- YLMSG_E("Unable to set stdout as output.\n");
+ if (merge) {
+ /* validate the merged result */
+ ret = lyd_validate_all(&merged_tree, ctx, validate_options, NULL);
+ if (ret) {
+ YLMSG_E("Merged data are not valid.");
goto cleanup;
}
+
+ if (out_format) {
+ /* and print it */
+ lyd_print_all(out, merged_tree, out_format, print_options);
+ }
+
+ for (uint32_t u = 0; xpaths && (u < xpaths->count); ++u) {
+ xpath = (const char *)xpaths->objs[u];
+ ly_set_free(set, NULL);
+ ret = lys_find_xpath(ctx, NULL, xpath, LYS_FIND_NO_MATCH_ERROR, &set);
+ if (ret || !set->count) {
+ ret = (ret == LY_SUCCESS) ? LY_EINVAL : ret;
+ YLMSG_E("The requested xpath failed.");
+ goto cleanup;
+ }
+ if (evaluate_xpath(merged_tree, xpath)) {
+ goto cleanup;
+ }
+ }
}
+cleanup:
+ lyd_free_all(tree);
+ lyd_free_all(envp);
+ lyd_free_all(merged_tree);
+ lyd_free_all(oper_tree);
+ ly_set_free(set, NULL);
+ return ret;
+}
+
+int
+cmd_data_process(struct ly_ctx *ctx, struct yl_opt *yo)
+{
/* parse, validate and print data */
- if (process_data(*ctx, data_type, data_merge, outformat, out, options_parse, options_validate, options_print,
- operational, NULL, &inputs, &xpaths)) {
- goto cleanup;
+ if (process_data(ctx, yo->data_type, yo->data_merge, yo->data_out_format, yo->out, yo->data_parse_options,
+ yo->data_validate_options, yo->data_print_options, &yo->data_operational, &yo->reply_rpc,
+ &yo->data_inputs, &yo->data_xpath)) {
+ return 1;
}
-cleanup:
- ly_out_free(out, NULL, 0);
- ly_set_erase(&inputs, free_cmdline_file);
- ly_set_erase(&xpaths, NULL);
- free_cmdline_file(operational);
- free_cmdline(argv);
+ return 0;
}
diff --git a/tools/lint/cmd_debug.c b/tools/lint/cmd_debug.c
new file mode 100644
index 0000000..3661bfa
--- /dev/null
+++ b/tools/lint/cmd_debug.c
@@ -0,0 +1,134 @@
+/**
+ * @file cmd_debug.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief 'verb' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2023-2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef NDEBUG
+
+#include "cmd.h"
+
+#include <assert.h>
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <strings.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "yl_opt.h"
+
+struct debug_groups {
+ char *name;
+ uint32_t flag;
+} const dg [] = {
+ {"dict", LY_LDGDICT},
+ {"xpath", LY_LDGXPATH},
+ {"dep-sets", LY_LDGDEPSETS},
+};
+#define DG_LENGTH (sizeof dg / sizeof *dg)
+
+void
+cmd_debug_help(void)
+{
+ uint32_t i;
+
+ printf("Usage: debug (");
+ for (i = 0; i < DG_LENGTH; i++) {
+ if ((i + 1) == DG_LENGTH) {
+ printf("%s", dg[i].name);
+ } else {
+ printf("%s | ", dg[i].name);
+ }
+ }
+ printf(")+\n");
+}
+
+int
+cmd_debug_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
+{
+ int rc = 0, argc = 0;
+ int opt, opt_index;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
+ }
+
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_DEBUG].optstring, options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'h':
+ cmd_debug_help();
+ return 1;
+ default:
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_debug_dep(struct yl_opt *yo, int posc)
+{
+ (void) yo;
+
+ if (yo->interactive && !posc) {
+ /* no argument */
+ cmd_debug_help();
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cmd_debug_store(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void) ctx;
+ uint32_t i;
+ ly_bool set;
+
+ assert(posv);
+
+ set = 0;
+ for (i = 0; i < DG_LENGTH; i++) {
+ if (!strcasecmp(posv, dg[i].name)) {
+ yo->dbg_groups |= dg[i].flag;
+ set = 1;
+ break;
+ }
+ }
+
+ if (!set) {
+ YLMSG_E("Unknown debug group \"%s\".", posv);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cmd_debug_setlog(struct ly_ctx *ctx, struct yl_opt *yo)
+{
+ (void) ctx;
+ return ly_log_dbg_groups(yo->dbg_groups);
+}
+
+#endif
diff --git a/tools/lint/cmd_extdata.c b/tools/lint/cmd_extdata.c
new file mode 100644
index 0000000..fc7ac7b
--- /dev/null
+++ b/tools/lint/cmd_extdata.c
@@ -0,0 +1,115 @@
+/**
+ * @file cmd_extdata.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief 'extdata' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+#define _POSIX_C_SOURCE 200809L /* strdup */
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "yl_opt.h"
+
+char *filename;
+
+void
+cmd_extdata_free(void)
+{
+ free(filename);
+ filename = NULL;
+}
+
+void
+cmd_extdata_help(void)
+{
+ printf("Usage: extdata [--clear] [<extdata-file-path>]\n"
+ " File containing the specific data required by an extension. Required by\n"
+ " the schema-mount extension, for example, when the operational data are\n"
+ " expected in the file. File format is guessed.\n");
+}
+
+int
+cmd_extdata_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
+{
+ int rc = 0, argc = 0;
+ int opt, opt_index;
+ struct option options[] = {
+ {"clear", no_argument, NULL, 'c'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
+ }
+
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_EXTDATA].optstring, options, &opt_index)) != -1) {
+ switch (opt) {
+ case 'c':
+ yo->extdata_unset = 1;
+ free(filename);
+ filename = NULL;
+ break;
+ case 'h':
+ cmd_extdata_help();
+ return 1;
+ default:
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_extdata_dep(struct yl_opt *yo, int posc)
+{
+ if (!yo->extdata_unset && (posc > 1)) {
+ YLMSG_E("Only one file must be entered.");
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cmd_extdata_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ if (yo->extdata_unset) {
+ ly_ctx_set_ext_data_clb(*ctx, NULL, NULL);
+ } else if (!yo->extdata_unset && !posv) {
+ /* no argument - print the current file */
+ printf("%s\n", filename ? filename : "No file set.");
+ } else if (posv) {
+ /* set callback providing run-time extension instance data */
+ free(filename);
+ filename = strdup(posv);
+ if (!filename) {
+ YLMSG_E("Memory allocation error.");
+ return 1;
+ }
+ ly_ctx_set_ext_data_clb(*ctx, ext_data_clb, filename);
+ }
+
+ return 0;
+}
diff --git a/tools/lint/cmd_feature.c b/tools/lint/cmd_feature.c
index 6b332ab..96d55c1 100644
--- a/tools/lint/cmd_feature.c
+++ b/tools/lint/cmd_feature.c
@@ -1,9 +1,10 @@
/**
* @file cmd_feature.c
* @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'feature' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2021 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -23,6 +24,8 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
+#include "yl_schema_features.h"
void
cmd_feature_help(void)
@@ -37,17 +40,11 @@ cmd_feature_help(void)
" Print features of all implemented modules.\n");
}
-void
-cmd_feature(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_feature_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
- char *features_output = NULL;
- int opt, opt_index, i;
- ly_bool generate_features = 0, print_all = 0;
- struct ly_set set = {0};
- const struct lys_module *mod;
- struct ly_out *out = NULL;
+ int rc = 0, argc = 0;
+ int opt, opt_index;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
{"all", no_argument, NULL, 'a'},
@@ -55,81 +52,80 @@ cmd_feature(struct ly_ctx **ctx, const char *cmdline)
{NULL, 0, NULL, 0}
};
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "haf", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_FEATURE].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'h':
cmd_feature_help();
- goto cleanup;
+ return 1;
case 'a':
- print_all = 1;
+ yo->feature_print_all = 1;
break;
case 'f':
- generate_features = 1;
+ yo->feature_param_format = 1;
break;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
- if (ly_out_new_file(stdout, &out)) {
- YLMSG_E("Unable to print to the standard output.\n");
- goto cleanup;
- }
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
- if (print_all) {
- if (print_all_features(out, *ctx, generate_features, &features_output)) {
- YLMSG_E("Printing all features failed.\n");
- goto cleanup;
- }
- if (generate_features) {
- printf("%s\n", features_output);
- }
- goto cleanup;
- }
+ return 0;
+}
- if (argc == optind) {
- YLMSG_E("Missing modules to print.\n");
- goto cleanup;
+int
+cmd_feature_dep(struct yl_opt *yo, int posc)
+{
+ if (ly_out_new_file(stdout, &yo->out)) {
+ YLMSG_E("Unable to print to the standard output.");
+ return 1;
}
+ yo->out_stdout = 1;
- for (i = 0; i < argc - optind; i++) {
- /* always erase the set, so the previous module's features don't carry over to the next module's features */
- ly_set_erase(&set, NULL);
+ if (yo->interactive && !yo->feature_print_all && !posc) {
+ YLMSG_E("Missing modules to print.");
+ return 1;
+ }
- mod = ly_ctx_get_module_latest(*ctx, argv[optind + i]);
- if (!mod) {
- YLMSG_E("Module \"%s\" not found.\n", argv[optind + i]);
- goto cleanup;
- }
+ return 0;
+}
- /* collect features of the module */
- if (collect_features(mod, &set)) {
- goto cleanup;
- }
+int
+cmd_feature_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ const struct lys_module *mod;
- if (generate_features) {
- if (generate_features_output(mod, &set, &features_output)) {
- goto cleanup;
- }
- /* don't print features and their state of each module if generating features parameter */
- continue;
- }
+ if (yo->feature_print_all) {
+ print_all_features(yo->out, *ctx, yo->feature_param_format);
+ return 0;
+ }
- print_features(out, mod, &set);
+ mod = ly_ctx_get_module_latest(*ctx, posv);
+ if (!mod) {
+ YLMSG_E("Module \"%s\" not found.", posv);
+ return 1;
}
- if (generate_features) {
- printf("%s\n", features_output);
+ if (yo->feature_param_format) {
+ print_feature_param(yo->out, mod);
+ } else {
+ print_features(yo->out, mod);
}
-cleanup:
- free_cmdline(argv);
- ly_out_free(out, NULL, 0);
- ly_set_erase(&set, NULL);
- free(features_output);
+ return 0;
+}
+
+int
+cmd_feature_fin(struct ly_ctx *ctx, struct yl_opt *yo)
+{
+ (void) ctx;
+
+ ly_print(yo->out, "\n");
+ return 0;
}
diff --git a/tools/lint/cmd_help.c b/tools/lint/cmd_help.c
new file mode 100644
index 0000000..a1ee3f6
--- /dev/null
+++ b/tools/lint/cmd_help.c
@@ -0,0 +1,107 @@
+/**
+ * @file cmd_help.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief 'help' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2023-2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "yl_opt.h"
+
+void
+cmd_help_help(void)
+{
+ printf("Usage: help [cmd ...]\n");
+}
+
+int
+cmd_help_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
+{
+ int rc = 0, argc = 0;
+ int opt, opt_index;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
+ }
+
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_HELP].optstring, options, &opt_index)) != -1) {
+ if (opt == 'h') {
+ cmd_help_help();
+ return 1;
+ } else {
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return rc;
+}
+
+static void
+print_generic_help(void)
+{
+ printf("Available commands:\n");
+ for (uint16_t i = 0; commands[i].name; i++) {
+ if (commands[i].helpstring != NULL) {
+ printf(" %-15s %s\n", commands[i].name, commands[i].helpstring);
+ }
+ }
+}
+
+int
+cmd_help_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void)ctx, (void)yo;
+
+ if (!posv) {
+ print_generic_help();
+ } else {
+ /* print specific help for the selected command(s) */
+
+ int8_t match = 0;
+
+ /* get the command of the specified name */
+ for (uint16_t i = 0; commands[i].name; i++) {
+ if (strcmp(posv, commands[i].name) == 0) {
+ match = 1;
+ if (commands[i].help_func != NULL) {
+ commands[i].help_func();
+ } else {
+ printf("%s: %s\n", posv, commands[i].helpstring);
+ }
+ break;
+ }
+ }
+ if (!match) {
+ /* if unknown command specified, print the list of commands */
+ printf("Unknown command \'%s\'\n", posv);
+ print_generic_help();
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/lint/cmd_list.c b/tools/lint/cmd_list.c
index ec7a021..166fbfa 100644
--- a/tools/lint/cmd_list.c
+++ b/tools/lint/cmd_list.c
@@ -2,9 +2,10 @@
* @file cmd_list.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'list' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -24,6 +25,7 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
void
cmd_list_help(void)
@@ -37,53 +39,148 @@ cmd_list_help(void)
" modules.\n");
}
-void
-cmd_list(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_list_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"format", required_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
- LYD_FORMAT format = LYD_UNKNOWN;
- struct ly_out *out = NULL;
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ yo->data_out_format = LYD_UNKNOWN;
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "f:h", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_LIST].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'f': /* --format */
if (!strcasecmp(optarg, "xml")) {
- format = LYD_XML;
+ yo->data_out_format = LYD_XML;
} else if (!strcasecmp(optarg, "json")) {
- format = LYD_JSON;
+ yo->data_out_format = LYD_JSON;
} else {
- YLMSG_E("Unknown output format %s\n", optarg);
+ YLMSG_E("Unknown output format %s.", optarg);
cmd_list_help();
- goto cleanup;
+ return 1;
}
break;
case 'h':
cmd_list_help();
- goto cleanup;
+ return 1;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_list_dep(struct yl_opt *yo, int posc)
+{
+ if (posc) {
+ YLMSG_E("No positional arguments are allowed.");
+ return 1;
+ }
+ if (ly_out_new_file(stdout, &yo->out)) {
+ YLMSG_E("Unable to print to the standard output.");
+ return 1;
+ }
+ yo->out_stdout = 1;
+
+ return 0;
+}
+
+/**
+ * @brief Print yang library data.
+ *
+ * @param[in] ctx Context for libyang.
+ * @param[in] data_out_format Output format of printed data.
+ * @param[in] out Output handler.
+ * @return 0 on success.
+ */
+static int
+print_yang_lib_data(struct ly_ctx *ctx, LYD_FORMAT data_out_format, struct ly_out *out)
+{
+ struct lyd_node *ylib;
+
+ if (ly_ctx_get_yanglib_data(ctx, &ylib, "%u", ly_ctx_get_change_count(ctx))) {
+ YLMSG_E("Getting context info (ietf-yang-library data) failed. If the YANG module is missing or not implemented, "
+ "use an option to add it internally.");
+ return 1;
+ }
+
+ lyd_print_all(out, ylib, data_out_format, 0);
+ lyd_free_all(ylib);
+
+ return 0;
+}
+
+int
+cmd_list_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void) posv;
+ uint32_t idx = 0, has_modules = 0;
+ const struct lys_module *mod;
+
+ if (yo->data_out_format != LYD_UNKNOWN) {
+ /* ietf-yang-library data are printed in the specified format */
+ if (print_yang_lib_data(*ctx, yo->data_out_format, yo->out)) {
+ return 1;
}
+ return 0;
}
- if (!ly_out_new_file(stdout, &out)) {
- print_list(out, *ctx, format);
- ly_out_free(out, NULL, 0);
- } else {
- YLMSG_E("Unable to print to the standard output.\n");
+ /* iterate schemas in context and provide just the basic info */
+ ly_print(yo->out, "List of the loaded models:\n");
+ while ((mod = ly_ctx_get_module_iter(*ctx, &idx))) {
+ has_modules++;
+
+ /* conformance print */
+ if (mod->implemented) {
+ ly_print(yo->out, " I");
+ } else {
+ ly_print(yo->out, " i");
+ }
+
+ /* module print */
+ ly_print(yo->out, " %s", mod->name);
+ if (mod->revision) {
+ ly_print(yo->out, "@%s", mod->revision);
+ }
+
+ /* submodules print */
+ if (mod->parsed && mod->parsed->includes) {
+ uint64_t u = 0;
+
+ ly_print(yo->out, " (");
+ LY_ARRAY_FOR(mod->parsed->includes, u) {
+ ly_print(yo->out, "%s%s", !u ? "" : ",", mod->parsed->includes[u].name);
+ if (mod->parsed->includes[u].rev[0]) {
+ ly_print(yo->out, "@%s", mod->parsed->includes[u].rev);
+ }
+ }
+ ly_print(yo->out, ")");
+ }
+
+ /* finish the line */
+ ly_print(yo->out, "\n");
+ }
+
+ if (!has_modules) {
+ ly_print(yo->out, "\t(none)\n");
}
-cleanup:
- free_cmdline(argv);
+ ly_print_flush(yo->out);
+
+ return 0;
}
diff --git a/tools/lint/cmd_load.c b/tools/lint/cmd_load.c
index f5883e9..808c125 100644
--- a/tools/lint/cmd_load.c
+++ b/tools/lint/cmd_load.c
@@ -2,9 +2,10 @@
* @file cmd_load.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'load' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -17,6 +18,7 @@
#include "cmd.h"
+#include <assert.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
@@ -25,13 +27,15 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
+#include "yl_schema_features.h"
void
cmd_load_help(void)
{
printf("Usage: load [-i] <module-name1>[@<revision>] [<module-name2>[@revision] ...]\n"
" Add a new module of the specified name, yanglint will find\n"
- " them in searchpaths. if the <revision> of the module not\n"
+ " them in searchpaths. If the <revision> of the module not\n"
" specified, the latest revision available is loaded.\n\n"
" -F FEATURES, --features=FEATURES\n"
" Features to support, default all in all implemented modules.\n"
@@ -39,101 +43,110 @@ cmd_load_help(void)
" -i, --make-implemented\n"
" Make the imported modules \"referenced\" from any loaded\n"
" <schema> module also implemented. If specified a second time,\n"
- " all the modules are set implemented.\n");
+ " all the modules are set implemented.\n"
+ " -X, --extended-leafref\n"
+ " Allow usage of deref() XPath function within leafref.\n");
}
-void
-cmd_load(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_load_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc, argc = 0;
int opt, opt_index;
struct option options[] = {
{"features", required_argument, NULL, 'F'},
{"help", no_argument, NULL, 'h'},
{"make-implemented", no_argument, NULL, 'i'},
+ {"extended-leafref", no_argument, NULL, 'X'},
{NULL, 0, NULL, 0}
};
- uint16_t options_ctx = 0;
- const char *all_features[] = {"*", NULL};
- struct ly_set fset = {0};
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "F:hi", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_LOAD].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'F': /* --features */
- if (parse_features(optarg, &fset)) {
- goto cleanup;
+ if (parse_features(optarg, &yo->schema_features)) {
+ return 1;
}
break;
case 'h':
cmd_load_help();
- goto cleanup;
+ return 1;
case 'i': /* --make-implemented */
- if (options_ctx & LY_CTX_REF_IMPLEMENTED) {
- options_ctx &= ~LY_CTX_REF_IMPLEMENTED;
- options_ctx |= LY_CTX_ALL_IMPLEMENTED;
- } else {
- options_ctx |= LY_CTX_REF_IMPLEMENTED;
- }
+ yo_opt_update_make_implemented(yo);
+ break;
+
+ case 'X': /* --extended-leafref */
+ yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
break;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
- if (argc == optind) {
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_load_dep(struct yl_opt *yo, int posc)
+{
+ if (yo->interactive && !posc) {
/* no argument */
- cmd_add_help();
- goto cleanup;
+ cmd_load_help();
+ return 1;
}
- if (!fset.count) {
+ if (!yo->schema_features.count) {
/* no features, enable all of them */
- options_ctx |= LY_CTX_ENABLE_IMP_FEATURES;
+ yo->ctx_options |= LY_CTX_ENABLE_IMP_FEATURES;
}
- if (options_ctx) {
- ly_ctx_set_options(*ctx, options_ctx);
- }
+ return 0;
+}
- for (int i = 0; i < argc - optind; i++) {
- /* process the schema module files */
- char *revision;
- const char **features = NULL;
+int
+cmd_load_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ const char *all_features[] = {"*", NULL};
+ char *revision;
+ const char **features = NULL;
- /* get revision */
- revision = strchr(argv[optind + i], '@');
- if (revision) {
- revision[0] = '\0';
- ++revision;
- }
+ assert(posv);
- /* get features list for this module */
- if (!fset.count) {
- features = all_features;
- } else {
- get_features(&fset, argv[optind + i], &features);
- }
+ if (yo->ctx_options) {
+ ly_ctx_set_options(*ctx, yo->ctx_options);
+ yo->ctx_options = 0;
+ }
- /* load the module */
- if (!ly_ctx_load_module(*ctx, argv[optind + i], revision, features)) {
- /* libyang printed the error messages */
- goto cleanup;
- }
+ /* get revision */
+ revision = strchr(posv, '@');
+ if (revision) {
+ revision[0] = '\0';
+ ++revision;
}
-cleanup:
- if (options_ctx) {
- ly_ctx_unset_options(*ctx, options_ctx);
+ /* get features list for this module */
+ if (!yo->schema_features.count) {
+ features = all_features;
+ } else {
+ get_features(&yo->schema_features, posv, &features);
}
- ly_set_erase(&fset, free_features);
- free_cmdline(argv);
+
+ /* load the module */
+ if (!ly_ctx_load_module(*ctx, posv, revision, features)) {
+ /* libyang printed the error messages */
+ return 1;
+ }
+
+ return 0;
}
diff --git a/tools/lint/cmd_print.c b/tools/lint/cmd_print.c
index c1a5359..ff5fb90 100644
--- a/tools/lint/cmd_print.c
+++ b/tools/lint/cmd_print.c
@@ -2,9 +2,10 @@
* @file cmd_print.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'print' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -27,6 +28,7 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
void
cmd_print_help(void)
@@ -34,7 +36,8 @@ cmd_print_help(void)
printf("Usage: print [-f (yang | yin | tree [-q -P PATH -L LINE_LENGTH ] | info [-q -P PATH])]\n"
" [-o OUTFILE] [<module-name1>[@revision]] ...\n"
" Print a schema module. The <module-name> is not required\n"
- " only in case the -P option is specified.\n\n"
+ " only in case the -P option is specified. For yang, yin and tree\n"
+ " formats, a submodule can also be printed.\n\n"
" -f FORMAT, --format=FORMAT\n"
" Print the module in the specified FORMAT. If format not\n"
" specified, the 'tree' format is used.\n"
@@ -53,95 +56,10 @@ cmd_print_help(void)
" Write the output to OUTFILE instead of stdout.\n");
}
-static LY_ERR
-cmd_print_submodule(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
-{
- LY_ERR erc;
- const struct lysp_submodule *submodule;
-
- submodule = revision ?
- ly_ctx_get_submodule(*ctx, name, revision) :
- ly_ctx_get_submodule_latest(*ctx, name);
-
- erc = submodule ?
- lys_print_submodule(out, submodule, format, line_length, options) :
- LY_ENOTFOUND;
-
- return erc;
-}
-
-static LY_ERR
-cmd_print_module(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
-{
- LY_ERR erc;
- struct lys_module *module;
-
- module = revision ?
- ly_ctx_get_module(*ctx, name, revision) :
- ly_ctx_get_module_latest(*ctx, name);
-
- erc = module ?
- lys_print_module(out, module, format, line_length, options) :
- LY_ENOTFOUND;
-
- return erc;
-}
-
-static void
-cmd_print_modules(int argc, char **argv, struct ly_out *out, struct ly_ctx **ctx, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
-{
- LY_ERR erc;
- char *name, *revision;
- ly_bool search_submodul;
- const int stop = argc - optind;
-
- for (int i = 0; i < stop; i++) {
- name = argv[optind + i];
- /* get revision */
- revision = strchr(name, '@');
- if (revision) {
- revision[0] = '\0';
- ++revision;
- }
-
- erc = cmd_print_module(out, ctx, name, revision, format, line_length, options);
-
- if (erc == LY_ENOTFOUND) {
- search_submodul = 1;
- erc = cmd_print_submodule(out, ctx, name, revision, format, line_length, options);
- } else {
- search_submodul = 0;
- }
-
- if (erc == LY_SUCCESS) {
- /* for YANG Tree Diagrams printing it's more readable to print a blank line between modules. */
- if ((format == LYS_OUT_TREE) && (i + 1 < stop)) {
- ly_print(out, "\n");
- }
- continue;
- } else if (erc == LY_ENOTFOUND) {
- if (revision) {
- YLMSG_E("No (sub)module \"%s\" in revision %s found.\n", name, revision);
- } else {
- YLMSG_E("No (sub)module \"%s\" found.\n", name);
- }
- break;
- } else {
- if (search_submodul) {
- YLMSG_E("Unable to print submodule %s.\n", name);
- } else {
- YLMSG_E("Unable to print module %s.\n", name);
- }
- break;
- }
- }
-}
-
-void
-cmd_print(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_print_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"format", required_argument, NULL, 'f'},
@@ -152,113 +70,230 @@ cmd_print(struct ly_ctx **ctx, const char *cmdline)
{"single-node", no_argument, NULL, 'q'},
{NULL, 0, NULL, 0}
};
- uint16_t options_print = 0;
- const char *node_path = NULL;
- LYS_OUTFORMAT format = LYS_OUT_TREE;
- struct ly_out *out = NULL;
- ly_bool out_stdout = 0;
- size_t line_length = 0;
-
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+
+ yo->schema_out_format = LYS_OUT_TREE;
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "f:hL:o:P:q", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_PRINT].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'o': /* --output */
- if (out) {
- if (ly_out_filepath(out, optarg) != NULL) {
- YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
- goto cleanup;
- }
+ if (yo->out) {
+ YLMSG_E("Only a single output can be specified.");
+ return 1;
} else {
- if (ly_out_new_filepath(optarg, &out)) {
- YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
- goto cleanup;
+ if (ly_out_new_filepath(optarg, &yo->out)) {
+ YLMSG_E("Unable open output file %s (%s).", optarg, strerror(errno));
+ return 1;
}
}
break;
case 'f': /* --format */
- if (!strcasecmp(optarg, "yang")) {
- format = LYS_OUT_YANG;
- } else if (!strcasecmp(optarg, "yin")) {
- format = LYS_OUT_YIN;
- } else if (!strcasecmp(optarg, "info")) {
- format = LYS_OUT_YANG_COMPILED;
- } else if (!strcasecmp(optarg, "tree")) {
- format = LYS_OUT_TREE;
- } else {
- YLMSG_E("Unknown output format %s\n", optarg);
+ if (yl_opt_update_schema_out_format(optarg, yo)) {
cmd_print_help();
- goto cleanup;
+ return 1;
}
break;
case 'L': /* --tree-line-length */
- line_length = atoi(optarg);
+ yo->line_length = atoi(optarg);
break;
case 'P': /* --schema-node */
- node_path = optarg;
+ yo->schema_node_path = optarg;
break;
case 'q': /* --single-node */
- options_print |= LYS_PRINT_NO_SUBSTMT;
+ yo->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
break;
case 'h':
cmd_print_help();
- goto cleanup;
+ return 1;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_print_dep(struct yl_opt *yo, int posc)
+{
/* file name */
- if ((argc == optind) && !node_path) {
- YLMSG_E("Missing the name of the module to print.\n");
- goto cleanup;
+ if (yo->interactive && !posc && !yo->schema_node_path) {
+ YLMSG_E("Missing the name of the module to print.");
+ return 1;
}
- if ((format != LYS_OUT_TREE) && line_length) {
- YLMSG_E("--tree-line-length take effect only in case of the tree output format.\n");
- goto cleanup;
+ if ((yo->schema_out_format != LYS_OUT_TREE) && yo->line_length) {
+ YLMSG_W("--tree-line-length take effect only in case of the tree output format.");
}
- if (!out) {
- if (ly_out_new_file(stdout, &out)) {
- YLMSG_E("Could not use stdout to print output.\n");
- goto cleanup;
+ if (!yo->out) {
+ if (ly_out_new_file(stdout, &yo->out)) {
+ YLMSG_E("Could not use stdout to print output.");
}
- out_stdout = 1;
+ yo->out_stdout = 1;
}
- if (format == LYS_OUT_TREE) {
+ if (yo->schema_out_format == LYS_OUT_TREE) {
/* print tree from lysc_nodes */
- ly_ctx_set_options(*ctx, LY_CTX_SET_PRIV_PARSED);
+ yo->ctx_options |= LY_CTX_SET_PRIV_PARSED;
}
- if (node_path) {
- const struct lysc_node *node;
+ return 0;
+}
+
+static LY_ERR
+print_submodule(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
+{
+ LY_ERR erc;
+ const struct lysp_submodule *submodule;
- node = find_schema_path(*ctx, node_path);
+ submodule = revision ?
+ ly_ctx_get_submodule(*ctx, name, revision) :
+ ly_ctx_get_submodule_latest(*ctx, name);
+
+ erc = submodule ?
+ lys_print_submodule(out, submodule, format, line_length, options) :
+ LY_ENOTFOUND;
+
+ if (!erc) {
+ return 0;
+ } else if ((erc == LY_ENOTFOUND) && revision) {
+ YLMSG_E("No submodule \"%s\" found.", name);
+ } else {
+ YLMSG_E("Unable to print submodule %s.", name);
+ }
+
+ return erc;
+}
+
+static LY_ERR
+print_module(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
+{
+ LY_ERR erc;
+ struct lys_module *module;
+
+ module = revision ?
+ ly_ctx_get_module(*ctx, name, revision) :
+ ly_ctx_get_module_latest(*ctx, name);
+
+ erc = module ?
+ lys_print_module(out, module, format, line_length, options) :
+ LY_ENOTFOUND;
+
+ if (!erc) {
+ return 0;
+ } else if ((erc == LY_ENOTFOUND) && revision) {
+ YLMSG_E("No module \"%s\" found.", name);
+ } else {
+ YLMSG_E("Unable to print module %s.", name);
+ }
+
+ return erc;
+}
+
+static int
+cmd_print_module(const char *posv, struct ly_out *out, struct ly_ctx **ctx, LYS_OUTFORMAT format,
+ size_t line_length, uint32_t options)
+{
+ LY_ERR erc;
+ char *name = NULL, *revision;
+
+ name = strdup(posv);
+ /* get revision */
+ revision = strchr(name, '@');
+ if (revision) {
+ revision[0] = '\0';
+ ++revision;
+ }
+
+ erc = print_module(out, ctx, name, revision, format, line_length, options);
+
+ if (erc == LY_ENOTFOUND) {
+ erc = print_submodule(out, ctx, name, revision, format, line_length, options);
+ }
+
+ free(name);
+ return erc;
+}
+
+/**
+ * @brief Print schema node path.
+ *
+ * @param[in] ctx Context for libyang.
+ * @param[in] yo Context for yanglint.
+ * @return 0 on success.
+ */
+static int
+print_node(struct ly_ctx *ctx, struct yl_opt *yo)
+{
+ const struct lysc_node *node;
+ uint32_t temp_lo = 0;
+
+ if (yo->interactive) {
+ /* Use the same approach as for completion. */
+ node = find_schema_path(ctx, yo->schema_node_path);
if (!node) {
- YLMSG_E("The requested schema node \"%s\" does not exists.\n", node_path);
- goto cleanup;
+ YLMSG_E("The requested schema node \"%s\" does not exists.", yo->schema_node_path);
+ return 1;
}
-
- if (lys_print_node(out, node, format, 0, options_print)) {
- YLMSG_E("Unable to print schema node %s.\n", node_path);
- goto cleanup;
+ } else {
+ /* turn off logging so that the message is not repeated */
+ ly_temp_log_options(&temp_lo);
+ /* search operation input */
+ node = lys_find_path(ctx, NULL, yo->schema_node_path, 0);
+ if (!node) {
+ /* restore logging so an error may be displayed */
+ ly_temp_log_options(NULL);
+ /* search operation output */
+ node = lys_find_path(ctx, NULL, yo->schema_node_path, 1);
+ if (!node) {
+ YLMSG_E("Invalid schema path.");
+ return 1;
+ }
}
+ }
+
+ if (lys_print_node(yo->out, node, yo->schema_out_format, yo->line_length, yo->schema_print_options)) {
+ YLMSG_E("Unable to print schema node %s.", yo->schema_node_path);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cmd_print_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ int rc = 0;
+
+ if (yo->ctx_options & LY_CTX_SET_PRIV_PARSED) {
+ /* print tree from lysc_nodes */
+ ly_ctx_set_options(*ctx, LY_CTX_SET_PRIV_PARSED);
+ }
+
+ if (yo->schema_node_path) {
+ rc = print_node(*ctx, yo);
+ } else if (!yo->interactive && yo->submodule) {
+ rc = print_submodule(yo->out, ctx, yo->submodule, NULL, yo->schema_out_format, yo->line_length,
+ yo->schema_print_options);
} else {
- cmd_print_modules(argc, argv, out, ctx, format, line_length, options_print);
- goto cleanup;
+ rc = cmd_print_module(posv, yo->out, ctx, yo->schema_out_format, yo->line_length, yo->schema_print_options);
+ if (!yo->last_one && (yo->schema_out_format == LYS_OUT_TREE)) {
+ ly_print(yo->out, "\n");
+ }
}
-cleanup:
- free_cmdline(argv);
- ly_out_free(out, NULL, out_stdout ? 0 : 1);
+ return rc;
}
diff --git a/tools/lint/cmd_searchpath.c b/tools/lint/cmd_searchpath.c
index 529e05d..a6aeacf 100644
--- a/tools/lint/cmd_searchpath.c
+++ b/tools/lint/cmd_searchpath.c
@@ -2,9 +2,10 @@
* @file cmd_searchpath.c
* @author Michal Vasko <mvasko@cesnet.cz>
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief 'searchpath' command of the libyang's yanglint tool.
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -24,51 +25,62 @@
#include "libyang.h"
#include "common.h"
+#include "yl_opt.h"
void
cmd_searchpath_help(void)
{
printf("Usage: searchpath [--clear] [<modules-dir-path> ...]\n"
- " Set paths of directories where to search for imports and\n"
- " includes of the schema modules. The current working directory\n"
- " and the path of the module being added is used implicitly.\n"
- " The 'load' command uses these paths to search even for the\n"
- " schema modules to be loaded.\n");
+ " Set paths of directories where to search for imports and includes\n"
+ " of the schema modules. Subdirectories are also searched. The current\n"
+ " working directory and the path of the module being added is used implicitly.\n"
+ " The 'load' command uses these paths to search even for the schema modules\n"
+ " to be loaded.\n");
}
-void
-cmd_searchpath(struct ly_ctx **ctx, const char *cmdline)
+int
+cmd_searchpath_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
{
- int argc = 0;
- char **argv = NULL;
+ int rc = 0, argc = 0;
int opt, opt_index;
struct option options[] = {
{"clear", no_argument, NULL, 'c'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
- int8_t cleared = 0;
- if (parse_cmdline(cmdline, &argc, &argv)) {
- goto cleanup;
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
}
- while ((opt = getopt_long(argc, argv, "ch", options, &opt_index)) != -1) {
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_SEARCHPATH].optstring, options, &opt_index)) != -1) {
switch (opt) {
case 'c':
- ly_ctx_unset_searchdir(*ctx, NULL);
- cleared = 1;
+ yo->searchdir_unset = 1;
break;
case 'h':
cmd_searchpath_help();
- goto cleanup;
+ return 1;
default:
- YLMSG_E("Unknown option.\n");
- goto cleanup;
+ YLMSG_E("Unknown option.");
+ return 1;
}
}
- if (!cleared && (argc == optind)) {
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_searchpath_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ int rc = 0;
+
+ if (yo->searchdir_unset) {
+ ly_ctx_unset_searchdir(*ctx, NULL);
+ } else if (!yo->searchdir_unset && !posv) {
/* no argument - print the paths */
const char * const *dirs = ly_ctx_get_searchdirs(*ctx);
@@ -76,15 +88,9 @@ cmd_searchpath(struct ly_ctx **ctx, const char *cmdline)
for (uint32_t i = 0; dirs[i]; ++i) {
printf(" %s\n", dirs[i]);
}
- goto cleanup;
- }
-
- for (int i = 0; i < argc - optind; i++) {
- if (ly_ctx_set_searchdir(*ctx, argv[optind + i])) {
- goto cleanup;
- }
+ } else {
+ rc = ly_ctx_set_searchdir(*ctx, posv);
}
-cleanup:
- free_cmdline(argv);
+ return rc;
}
diff --git a/tools/lint/cmd_verb.c b/tools/lint/cmd_verb.c
new file mode 100644
index 0000000..33c8d1e
--- /dev/null
+++ b/tools/lint/cmd_verb.c
@@ -0,0 +1,114 @@
+/**
+ * @file cmd_verb.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief 'verb' command of the libyang's yanglint tool.
+ *
+ * Copyright (c) 2023-2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "cmd.h"
+
+#include <getopt.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <strings.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "yl_opt.h"
+
+void
+cmd_verb_help(void)
+{
+ printf("Usage: verb (error | warning | verbose | debug)\n");
+}
+
+int
+cmd_verb_opt(struct yl_opt *yo, const char *cmdline, char ***posv, int *posc)
+{
+ int rc = 0, argc = 0;
+ int opt, opt_index;
+ struct option options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+ };
+
+ if ((rc = parse_cmdline(cmdline, &argc, &yo->argv))) {
+ return rc;
+ }
+
+ while ((opt = getopt_long(argc, yo->argv, commands[CMD_VERB].optstring, options, &opt_index)) != -1) {
+ if (opt == 'h') {
+ cmd_verb_help();
+ return 1;
+ } else {
+ YLMSG_E("Unknown option.");
+ return 1;
+ }
+ }
+
+ *posv = &yo->argv[optind];
+ *posc = argc - optind;
+
+ return 0;
+}
+
+int
+cmd_verb_dep(struct yl_opt *yo, int posc)
+{
+ (void) yo;
+
+ if (posc > 1) {
+ YLMSG_E("Only a single verbosity level can be set.");
+ cmd_verb_help();
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+cmd_verb_exec(struct ly_ctx **ctx, struct yl_opt *yo, const char *posv)
+{
+ (void) ctx, (void) yo;
+
+ if (!posv) {
+ /* no argument - print current value */
+ LY_LOG_LEVEL level = ly_log_level(LY_LLERR);
+
+ ly_log_level(level);
+ printf("Current verbosity level: ");
+ if (level == LY_LLERR) {
+ printf("error\n");
+ } else if (level == LY_LLWRN) {
+ printf("warning\n");
+ } else if (level == LY_LLVRB) {
+ printf("verbose\n");
+ } else if (level == LY_LLDBG) {
+ printf("debug\n");
+ }
+ return 0;
+ } else {
+ if (!strcasecmp("error", posv) || !strcmp("0", posv)) {
+ ly_log_level(LY_LLERR);
+ } else if (!strcasecmp("warning", posv) || !strcmp("1", posv)) {
+ ly_log_level(LY_LLWRN);
+ } else if (!strcasecmp("verbose", posv) || !strcmp("2", posv)) {
+ ly_log_level(LY_LLVRB);
+ } else if (!strcasecmp("debug", posv) || !strcmp("3", posv)) {
+ ly_log_level(LY_LLDBG);
+ } else {
+ YLMSG_E("Unknown verbosity \"%s\".", posv);
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/lint/common.c b/tools/lint/common.c
index fc9b1cd..d86c54f 100644
--- a/tools/lint/common.c
+++ b/tools/lint/common.c
@@ -1,9 +1,10 @@
/**
* @file common.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool - common functions for both interactive and non-interactive mode.
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -19,7 +20,7 @@
#include <assert.h>
#include <errno.h>
-#include <getopt.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -27,6 +28,21 @@
#include "compat.h"
#include "libyang.h"
+#include "plugins_exts.h"
+#include "yl_opt.h"
+
+void
+yl_log(ly_bool err, const char *format, ...)
+{
+ char msg[256];
+ va_list ap;
+
+ va_start(ap, format);
+ vsnprintf(msg, 256, format, ap);
+ va_end(ap);
+
+ fprintf(stderr, "YANGLINT[%c]: %s\n", err ? 'E' : 'W', msg);
+}
int
parse_schema_path(const char *path, char **dir, char **module)
@@ -38,6 +54,7 @@ parse_schema_path(const char *path, char **dir, char **module)
/* split the path to dirname and basename for further work */
*dir = strdup(path);
+ /* FIXME: this is broken on Windows */
*module = strrchr(*dir, '/');
if (!(*module)) {
*module = *dir;
@@ -65,722 +82,99 @@ get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_
/* check that the filepath exists and is a regular file */
if (stat(filepath, &st) == -1) {
- YLMSG_E("Unable to use input filepath (%s) - %s.\n", filepath, strerror(errno));
+ YLMSG_E("Unable to use input filepath (%s) - %s.", filepath, strerror(errno));
return -1;
}
if (!S_ISREG(st.st_mode)) {
- YLMSG_E("Provided input file (%s) is not a regular file.\n", filepath);
+ YLMSG_E("Provided input file (%s) is not a regular file.", filepath);
return -1;
}
- if ((format_schema && !*format_schema) || (format_data && !*format_data)) {
- /* get the file format */
- if (get_format(filepath, format_schema, format_data)) {
- return -1;
- }
- }
-
- if (ly_in_new_filepath(filepath, 0, in)) {
- YLMSG_E("Unable to process input file.\n");
+ if (get_format(filepath, format_schema, format_data)) {
return -1;
}
- return 0;
-}
-
-void
-free_features(void *flist)
-{
- struct schema_features *rec = (struct schema_features *)flist;
-
- if (rec) {
- free(rec->mod_name);
- if (rec->features) {
- for (uint32_t u = 0; rec->features[u]; ++u) {
- free(rec->features[u]);
- }
- free(rec->features);
- }
- free(rec);
- }
-}
-
-void
-get_features(struct ly_set *fset, const char *module, const char ***features)
-{
- /* get features list for this module */
- for (uint32_t u = 0; u < fset->count; ++u) {
- struct schema_features *sf = (struct schema_features *)fset->objs[u];
-
- if (!strcmp(module, sf->mod_name)) {
- /* matched module - explicitly set features */
- *features = (const char **)sf->features;
- sf->applied = 1;
- return;
- }
- }
-
- /* features not set so disable all */
- *features = NULL;
-}
-
-int
-parse_features(const char *fstring, struct ly_set *fset)
-{
- struct schema_features *rec;
- uint32_t count;
- char *p, **fp;
-
- rec = calloc(1, sizeof *rec);
- if (!rec) {
- YLMSG_E("Unable to allocate features information record (%s).\n", strerror(errno));
+ if (in && ly_in_new_filepath(filepath, 0, in)) {
+ YLMSG_E("Unable to process input file.");
return -1;
}
- if (ly_set_add(fset, rec, 1, NULL)) {
- YLMSG_E("Unable to store features information (%s).\n", strerror(errno));
- free(rec);
- return -1;
- }
-
- /* fill the record */
- p = strchr(fstring, ':');
- if (!p) {
- YLMSG_E("Invalid format of the features specification (%s).\n", fstring);
- return -1;
- }
- rec->mod_name = strndup(fstring, p - fstring);
-
- count = 0;
- while (p) {
- size_t len = 0;
- char *token = p + 1;
-
- p = strchr(token, ',');
- if (!p) {
- /* the last item, if any */
- len = strlen(token);
- } else {
- len = p - token;
- }
-
- if (len) {
- fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
- if (!fp) {
- YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
- return -1;
- }
- rec->features = fp;
- fp = &rec->features[count++]; /* array item to set */
- (*fp) = strndup(token, len);
- }
- }
-
- /* terminating NULL */
- fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
- if (!fp) {
- YLMSG_E("Unable to store features list information (%s).\n", strerror(errno));
- return -1;
- }
- rec->features = fp;
- rec->features[count++] = NULL;
return 0;
}
-struct cmdline_file *
-fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
+LYS_INFORMAT
+get_schema_format(const char *filename)
{
- struct cmdline_file *rec;
-
- rec = malloc(sizeof *rec);
- if (!rec) {
- YLMSG_E("Allocating memory for data file information failed.\n");
- return NULL;
- }
- rec->in = in;
- rec->path = path;
- rec->format = format;
-
- if (set && ly_set_add(set, rec, 1, NULL)) {
- free(rec);
- YLMSG_E("Storing data file information failed.\n");
- return NULL;
- }
-
- return rec;
-}
-
-int
-collect_features(const struct lys_module *mod, struct ly_set *set)
-{
- struct lysp_feature *f = NULL;
- uint32_t idx = 0;
-
- while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
- if (ly_set_add(set, (void *)f->name, 1, NULL)) {
- YLMSG_E("Memory allocation failed.\n");
- ly_set_erase(set, NULL);
- return 1;
- }
- }
-
- return 0;
-}
-
-void
-print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set)
-{
- size_t max_len;
- uint32_t j;
- const char *name;
-
- /* header */
- ly_print(out, "%s:\n", mod->name);
-
- /* no features */
- if (!set->count) {
- ly_print(out, "\t(none)\n\n");
- return;
- }
-
- /* get max len, so the statuses of all the features will be aligned */
- max_len = 0;
- for (j = 0; j < set->count; ++j) {
- name = set->objs[j];
- if (strlen(name) > max_len) {
- max_len = strlen(name);
- }
- }
-
- /* print features */
- for (j = 0; j < set->count; ++j) {
- name = set->objs[j];
- ly_print(out, "\t%-*s (%s)\n", (int)max_len, name, lys_feature_value(mod, name) ? "off" : "on");
- }
-
- ly_print(out, "\n");
-}
-
-int
-generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param)
-{
- uint32_t j;
- /*
- * features_len - length of all the features in the current module
- * added_len - length of a string to be added, = features_len + extra necessary length
- * param_len - length of the parameter before appending new string
- */
- size_t features_len, added_len, param_len;
- char *tmp;
-
- features_len = 0;
- for (j = 0; j < set->count; j++) {
- features_len += strlen(set->objs[j]);
- }
-
- if (j == 0) {
- /* no features */
- added_len = strlen("-F ") + strlen(mod->name) + strlen(":");
- } else {
- /* j = comma count, -1 because of trailing comma */
- added_len = strlen("-F ") + strlen(mod->name) + strlen(":") + features_len + j - 1;
- }
-
- /* to avoid strlen(NULL) if this is the first call */
- param_len = 0;
- if (*features_param) {
- param_len = strlen(*features_param);
- }
-
- /* +1 because of white space at the beginning */
- tmp = realloc(*features_param, param_len + added_len + 1 + 1);
- if (!tmp) {
- goto error;
- } else {
- *features_param = tmp;
- }
- sprintf(*features_param + param_len, " -F %s:", mod->name);
-
- for (j = 0; j < set->count; j++) {
- strcat(*features_param, set->objs[j]);
- /* no trailing comma */
- if (j != (set->count - 1)) {
- strcat(*features_param, ",");
- }
- }
-
- return 0;
-
-error:
- YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
- return 1;
-}
-
-int
-print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool generate_features, char **features_param)
-{
- int ret = 0;
- uint32_t i = 0;
- struct lys_module *mod;
- struct ly_set set = {0};
-
- while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
- /* only care about implemented modules */
- if (!mod->implemented) {
- continue;
- }
-
- /* always erase the set, so the previous module's features don't carry over to the next module's features */
- ly_set_erase(&set, NULL);
-
- if (collect_features(mod, &set)) {
- ret = 1;
- goto cleanup;
- }
-
- if (generate_features && generate_features_output(mod, &set, features_param)) {
- ret = 1;
- goto cleanup;
- }
- print_features(out, mod, &set);
- }
-
-cleanup:
- ly_set_erase(&set, NULL);
- return ret;
-}
-
-void
-free_cmdline_file(void *cmdline_file)
-{
- struct cmdline_file *rec = (struct cmdline_file *)cmdline_file;
-
- if (rec) {
- ly_in_free(rec->in, 1);
- free(rec);
- }
-}
-
-void
-free_cmdline(char *argv[])
-{
- if (argv) {
- free(argv[0]);
- free(argv);
- }
-}
-
-int
-parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[])
-{
- int count;
- char **vector;
char *ptr;
- char qmark = 0;
-
- assert(cmdline);
- assert(argc_p);
- assert(argv_p);
-
- /* init */
- optind = 0; /* reinitialize getopt() */
- count = 1;
- vector = malloc((count + 1) * sizeof *vector);
- vector[0] = strdup(cmdline);
-
- /* command name */
- strtok(vector[0], " ");
-
- /* arguments */
- while ((ptr = strtok(NULL, " "))) {
- size_t len;
- void *r;
-
- len = strlen(ptr);
-
- if (qmark) {
- /* still in quotated text */
- /* remove NULL termination of the previous token since it is not a token,
- * but a part of the quotation string */
- ptr[-1] = ' ';
-
- if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
- /* end of quotation */
- qmark = 0;
- /* shorten the argument by the terminating quotation mark */
- ptr[len - 1] = '\0';
- }
- continue;
- }
- /* another token in cmdline */
- ++count;
- r = realloc(vector, (count + 1) * sizeof *vector);
- if (!r) {
- YLMSG_E("Memory allocation failed (%s:%d, %s).\n", __FILE__, __LINE__, strerror(errno));
- free(vector);
- return -1;
- }
- vector = r;
- vector[count - 1] = ptr;
-
- if ((ptr[0] == '"') || (ptr[0] == '\'')) {
- /* remember the quotation mark to identify end of quotation */
- qmark = ptr[0];
-
- /* move the remembered argument after the quotation mark */
- ++vector[count - 1];
-
- /* check if the quotation is terminated within this token */
- if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
- /* end of quotation */
- qmark = 0;
- /* shorten the argument by the terminating quotation mark */
- ptr[len - 1] = '\0';
- }
+ if ((ptr = strrchr(filename, '.')) != NULL) {
+ ++ptr;
+ if (!strcmp(ptr, "yang")) {
+ return LYS_IN_YANG;
+ } else if (!strcmp(ptr, "yin")) {
+ return LYS_IN_YIN;
+ } else {
+ return LYS_IN_UNKNOWN;
}
+ } else {
+ return LYS_IN_UNKNOWN;
}
- vector[count] = NULL;
-
- *argc_p = count;
- *argv_p = vector;
-
- return 0;
}
-int
-get_format(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
+LYD_FORMAT
+get_data_format(const char *filename)
{
char *ptr;
- LYS_INFORMAT informat_s;
- LYD_FORMAT informat_d;
- /* get the file format */
if ((ptr = strrchr(filename, '.')) != NULL) {
++ptr;
- if (!strcmp(ptr, "yang")) {
- informat_s = LYS_IN_YANG;
- informat_d = 0;
- } else if (!strcmp(ptr, "yin")) {
- informat_s = LYS_IN_YIN;
- informat_d = 0;
- } else if (!strcmp(ptr, "xml")) {
- informat_s = 0;
- informat_d = LYD_XML;
+ if (!strcmp(ptr, "xml")) {
+ return LYD_XML;
} else if (!strcmp(ptr, "json")) {
- informat_s = 0;
- informat_d = LYD_JSON;
+ return LYD_JSON;
} else if (!strcmp(ptr, "lyb")) {
- informat_s = 0;
- informat_d = LYD_LYB;
+ return LYD_LYB;
} else {
- YLMSG_E("Input file \"%s\" in an unknown format \"%s\".\n", filename, ptr);
- return 0;
+ return LYD_UNKNOWN;
}
} else {
- YLMSG_E("Input file \"%s\" without file extension - unknown format.\n", filename);
- return 1;
+ return LYD_UNKNOWN;
}
-
- if (informat_d) {
- if (!data) {
- YLMSG_E("Input file \"%s\" not expected to contain data instances (unexpected format).\n", filename);
- return 2;
- }
- (*data) = informat_d;
- } else if (informat_s) {
- if (!schema) {
- YLMSG_E("Input file \"%s\" not expected to contain schema definition (unexpected format).\n", filename);
- return 3;
- }
- (*schema) = informat_s;
- }
-
- return 0;
}
int
-print_list(struct ly_out *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
+get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form)
{
- struct lyd_node *ylib;
- uint32_t idx = 0, has_modules = 0;
- const struct lys_module *mod;
-
- if (outformat != LYD_UNKNOWN) {
- if (ly_ctx_get_yanglib_data(ctx, &ylib, "%u", ly_ctx_get_change_count(ctx))) {
- YLMSG_E("Getting context info (ietf-yang-library data) failed. If the YANG module is missing or not implemented, use an option to add it internally.\n");
- return 1;
- }
-
- lyd_print_all(out, ylib, outformat, 0);
- lyd_free_all(ylib);
- return 0;
- }
-
- /* iterate schemas in context and provide just the basic info */
- ly_print(out, "List of the loaded models:\n");
- while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
- has_modules++;
-
- /* conformance print */
- if (mod->implemented) {
- ly_print(out, " I");
- } else {
- ly_print(out, " i");
- }
-
- /* module print */
- ly_print(out, " %s", mod->name);
- if (mod->revision) {
- ly_print(out, "@%s", mod->revision);
- }
+ LYS_INFORMAT schema;
+ LYD_FORMAT data;
- /* submodules print */
- if (mod->parsed && mod->parsed->includes) {
- uint64_t u = 0;
+ schema = !schema_form || !*schema_form ? LYS_IN_UNKNOWN : *schema_form;
+ data = !data_form || !*data_form ? LYD_UNKNOWN : *data_form;
- ly_print(out, " (");
- LY_ARRAY_FOR(mod->parsed->includes, u) {
- ly_print(out, "%s%s", !u ? "" : ",", mod->parsed->includes[u].name);
- if (mod->parsed->includes[u].rev[0]) {
- ly_print(out, "@%s", mod->parsed->includes[u].rev);
- }
- }
- ly_print(out, ")");
- }
-
- /* finish the line */
- ly_print(out, "\n");
+ if (!schema) {
+ schema = get_schema_format(filepath);
}
-
- if (!has_modules) {
- ly_print(out, "\t(none)\n");
+ if (!data) {
+ data = get_data_format(filepath);
}
- ly_print_flush(out);
- return 0;
-}
-
-int
-evaluate_xpath(const struct lyd_node *tree, const char *xpath)
-{
- struct ly_set *set = NULL;
-
- if (lyd_find_xpath(tree, xpath, &set)) {
+ if (!schema && !data) {
+ YLMSG_E("Input schema format for %s file not recognized.", filepath);
+ return -1;
+ } else if (!data && !schema) {
+ YLMSG_E("Input data format for %s file not recognized.", filepath);
return -1;
}
+ assert(schema || data);
- /* print result */
- printf("XPath \"%s\" evaluation result:\n", xpath);
- if (!set->count) {
- printf("\tEmpty\n");
- } else {
- for (uint32_t u = 0; u < set->count; ++u) {
- struct lyd_node *node = (struct lyd_node *)set->objs[u];
-
- printf(" %s \"%s\"", lys_nodetype2str(node->schema->nodetype), node->schema->name);
- if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
- printf(" (value: \"%s\")\n", lyd_get_value(node));
- } else if (node->schema->nodetype == LYS_LIST) {
- printf(" (");
- for (struct lyd_node *key = ((struct lyd_node_inner *)node)->child; key && lysc_is_key(key->schema); key = key->next) {
- printf("%s\"%s\": \"%s\";", (key != ((struct lyd_node_inner *)node)->child) ? " " : "",
- key->schema->name, lyd_get_value(key));
- }
- printf(")\n");
- }
- }
- }
-
- ly_set_free(set, NULL);
- return 0;
-}
-
-LY_ERR
-process_data(struct ly_ctx *ctx, enum lyd_type data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
- uint32_t options_parse, uint32_t options_validate, uint32_t options_print, struct cmdline_file *operational_f,
- struct cmdline_file *rpc_f, struct ly_set *inputs, struct ly_set *xpaths)
-{
- LY_ERR ret = LY_SUCCESS;
- struct lyd_node *tree = NULL, *op = NULL, *envp = NULL, *merged_tree = NULL, *oper_tree = NULL;
- char *path = NULL;
- struct ly_set *set = NULL;
-
- /* additional operational datastore */
- if (operational_f && operational_f->in) {
- ret = lyd_parse_data(ctx, NULL, operational_f->in, operational_f->format, LYD_PARSE_ONLY, 0, &oper_tree);
- if (ret) {
- YLMSG_E("Failed to parse operational datastore file \"%s\".\n", operational_f->path);
- goto cleanup;
- }
- }
-
- for (uint32_t u = 0; u < inputs->count; ++u) {
- struct cmdline_file *input_f = (struct cmdline_file *)inputs->objs[u];
-
- switch (data_type) {
- case LYD_TYPE_DATA_YANG:
- ret = lyd_parse_data(ctx, NULL, input_f->in, input_f->format, options_parse, options_validate, &tree);
- break;
- case LYD_TYPE_RPC_YANG:
- case LYD_TYPE_REPLY_YANG:
- case LYD_TYPE_NOTIF_YANG:
- ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &tree, &op);
- break;
- case LYD_TYPE_RPC_NETCONF:
- case LYD_TYPE_NOTIF_NETCONF:
- ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &envp, &op);
-
- /* adjust pointers */
- for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
- break;
- case LYD_TYPE_REPLY_NETCONF:
- /* parse source RPC operation */
- assert(rpc_f && rpc_f->in);
- ret = lyd_parse_op(ctx, NULL, rpc_f->in, rpc_f->format, LYD_TYPE_RPC_NETCONF, &envp, &op);
- if (ret) {
- YLMSG_E("Failed to parse source NETCONF RPC operation file \"%s\".\n", rpc_f->path);
- goto cleanup;
- }
-
- /* adjust pointers */
- for (tree = op; lyd_parent(tree); tree = lyd_parent(tree)) {}
-
- /* free input */
- lyd_free_siblings(lyd_child(op));
-
- /* we do not care */
- lyd_free_all(envp);
- envp = NULL;
-
- ret = lyd_parse_op(ctx, op, input_f->in, input_f->format, data_type, &envp, NULL);
- break;
- default:
- YLMSG_E("Internal error (%s:%d).\n", __FILE__, __LINE__);
- goto cleanup;
- }
-
- if (ret) {
- YLMSG_E("Failed to parse input data file \"%s\".\n", input_f->path);
- goto cleanup;
- }
-
- if (merge) {
- /* merge the data so far parsed for later validation and print */
- if (!merged_tree) {
- merged_tree = tree;
- } else {
- ret = lyd_merge_siblings(&merged_tree, tree, LYD_MERGE_DESTRUCT);
- if (ret) {
- YLMSG_E("Merging %s with previous data failed.\n", input_f->path);
- goto cleanup;
- }
- }
- tree = NULL;
- } else if (format) {
- /* print */
- switch (data_type) {
- case LYD_TYPE_DATA_YANG:
- lyd_print_all(out, tree, format, options_print);
- break;
- case LYD_TYPE_RPC_YANG:
- case LYD_TYPE_REPLY_YANG:
- case LYD_TYPE_NOTIF_YANG:
- case LYD_TYPE_RPC_NETCONF:
- case LYD_TYPE_NOTIF_NETCONF:
- lyd_print_tree(out, tree, format, options_print);
- break;
- case LYD_TYPE_REPLY_NETCONF:
- /* just the output */
- lyd_print_tree(out, lyd_child(tree), format, options_print);
- break;
- default:
- assert(0);
- }
- } else {
- /* validation of the RPC/Action/reply/Notification with the operational datastore, if any */
- switch (data_type) {
- case LYD_TYPE_DATA_YANG:
- /* already validated */
- break;
- case LYD_TYPE_RPC_YANG:
- case LYD_TYPE_RPC_NETCONF:
- ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_RPC_YANG, NULL);
- break;
- case LYD_TYPE_REPLY_YANG:
- case LYD_TYPE_REPLY_NETCONF:
- ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_REPLY_YANG, NULL);
- break;
- case LYD_TYPE_NOTIF_YANG:
- case LYD_TYPE_NOTIF_NETCONF:
- ret = lyd_validate_op(tree, oper_tree, LYD_TYPE_NOTIF_YANG, NULL);
- break;
- default:
- assert(0);
- }
- if (ret) {
- if (operational_f->path) {
- YLMSG_E("Failed to validate input data file \"%s\" with operational datastore \"%s\".\n",
- input_f->path, operational_f->path);
- } else {
- YLMSG_E("Failed to validate input data file \"%s\".\n", input_f->path);
- }
- goto cleanup;
- }
-
- if (op && oper_tree && lyd_parent(op)) {
- /* check operation parent existence */
- path = lyd_path(lyd_parent(op), LYD_PATH_STD, NULL, 0);
- if (!path) {
- ret = LY_EMEM;
- goto cleanup;
- }
- if ((ret = lyd_find_xpath(oper_tree, path, &set))) {
- goto cleanup;
- }
- if (!set->count) {
- YLMSG_E("Operation \"%s\" parent \"%s\" not found in the operational data.\n", LYD_NAME(op), path);
- ret = LY_EVALID;
- goto cleanup;
- }
- }
- }
-
- /* next iter */
- lyd_free_all(tree);
- tree = NULL;
- lyd_free_all(envp);
- envp = NULL;
+ if (schema_form) {
+ *schema_form = schema;
}
-
- if (merge) {
- /* validate the merged result */
- ret = lyd_validate_all(&merged_tree, ctx, LYD_VALIDATE_PRESENT, NULL);
- if (ret) {
- YLMSG_E("Merged data are not valid.\n");
- goto cleanup;
- }
-
- if (format) {
- /* and print it */
- lyd_print_all(out, merged_tree, format, options_print);
- }
-
- for (uint32_t u = 0; xpaths && (u < xpaths->count); ++u) {
- if (evaluate_xpath(merged_tree, (const char *)xpaths->objs[u])) {
- goto cleanup;
- }
- }
+ if (data_form) {
+ *data_form = data;
}
-cleanup:
- lyd_free_all(tree);
- lyd_free_all(envp);
- lyd_free_all(merged_tree);
- lyd_free_all(oper_tree);
- free(path);
- ly_set_free(set, NULL);
- return ret;
+ return 0;
}
const struct lysc_node *
@@ -812,7 +206,7 @@ find_schema_path(const struct ly_ctx *ctx, const char *schema_path)
/* - 1 because module_name_end points to ':' */
module_name = strndup(schema_path + 1, module_name_end - schema_path - 1);
if (!module_name) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
parent_node = NULL;
goto cleanup;
}
@@ -866,3 +260,42 @@ cleanup:
free(module_name);
return parent_node;
}
+
+LY_ERR
+ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
+{
+ struct ly_ctx *ctx;
+ struct lyd_node *data = NULL;
+
+ ctx = ext->module->ctx;
+ if (user_data) {
+ lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
+ }
+
+ *ext_data = data;
+ *ext_data_free = 1;
+ return LY_SUCCESS;
+}
+
+LY_ERR
+searchpath_strcat(char **searchpaths, const char *path)
+{
+ uint64_t len;
+ char *new;
+
+ if (!(*searchpaths)) {
+ *searchpaths = strdup(path);
+ return LY_SUCCESS;
+ }
+
+ len = strlen(*searchpaths) + strlen(path) + strlen(PATH_SEPARATOR);
+ new = realloc(*searchpaths, sizeof(char) * len + 1);
+ if (!new) {
+ return LY_EMEM;
+ }
+ strcat(new, PATH_SEPARATOR);
+ strcat(new, path);
+ *searchpaths = new;
+
+ return LY_SUCCESS;
+}
diff --git a/tools/lint/common.h b/tools/lint/common.h
index 7c6a8ad..7c50e72 100644
--- a/tools/lint/common.h
+++ b/tools/lint/common.h
@@ -1,9 +1,10 @@
/**
* @file common.h
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool - common functions and definitions for both interactive and non-interactive mode.
*
- * Copyright (c) 2020 CESNET, z.s.p.o.
+ * Copyright (c) 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -33,16 +34,21 @@
#define YL_DEFAULT_DATA_PARSE_OPTIONS LYD_PARSE_STRICT
/**
+ * @brief Default data validation flags.
+ */
+#define YL_DEFAULT_DATA_VALIDATE_OPTIONS LYD_VALIDATE_MULTI_ERROR
+
+/**
* @brief log error message
*/
#define YLMSG_E(...) \
- fprintf(stderr, "YANGLINT[E]: " __VA_ARGS__)
+ yl_log(1, __VA_ARGS__);
/**
* @brief log warning message
*/
#define YLMSG_W(...) \
- fprintf(stderr, "YANGLINT[W]: " __VA_ARGS__)
+ yl_log(0, __VA_ARGS__);
#ifndef _WIN32
# define PATH_SEPARATOR ":"
@@ -50,90 +56,16 @@
# define PATH_SEPARATOR ";"
#endif
-/**
- * @brief Storage for the list of the features (their names) in a specific YANG module.
- */
-struct schema_features {
- char *mod_name;
- char **features;
- ly_bool applied;
-};
-
-/**
- * @brief Data connected with a file provided on a command line as a file path.
- */
-struct cmdline_file {
- struct ly_in *in;
- const char *path;
- LYD_FORMAT format;
-};
-
-/**
- * @brief Free the schema features list (struct schema_features *)
- * @param[in,out] flist The (struct schema_features *) to free.
- */
-void free_features(void *flist);
-
-/**
- * @brief Get the list of features connected with the specific YANG module.
- *
- * @param[in] fset The set of features information (struct schema_features *).
- * @param[in] module Name of the YANG module which features should be found.
- * @param[out] features Pointer to the list of features being returned.
- */
-void get_features(struct ly_set *fset, const char *module, const char ***features);
-
-/**
- * @brief Parse features being specified for the specific YANG module.
- *
- * Format of the input @p fstring is as follows: <module_name>:[<feature>,]*
- *
- * @param[in] fstring Input string to be parsed.
- * @param[in, out] fset Features information set (of struct schema_features *). The set is being filled.
- */
-int parse_features(const char *fstring, struct ly_set *fset);
-
-/**
- * @brief Collect all features of a module.
- *
- * @param[in] mod Module to be searched for features.
- * @param[out] set Set in which the features will be stored.
- * @return 0 on success.
- * @return 1 on error.
- */
-int collect_features(const struct lys_module *mod, struct ly_set *set);
-
-/**
- * @brief Print all features of a single module.
- *
- * @param[in] out The output handler for printing.
- * @param[in] mod Module which contains the features.
- * @param[in] set Set which holds the features.
- */
-void print_features(struct ly_out *out, const struct lys_module *mod, const struct ly_set *set);
-
-/**
- * @brief Generate a string, which will contain features paramater.
- *
- * @param[in] mod Module, for which the string will be generated.
- * @param[in] set Set containing the features.
- * @param[out] features_param String which will contain the output.
- * @return 0 on success.
- * @return 1 on error.
- */
-int generate_features_output(const struct lys_module *mod, const struct ly_set *set, char **features_param);
+struct cmdline_file;
/**
- * @brief Print all features of all implemented modules.
+ * @brief Log a yanglint message.
*
- * @param[in] out The output handler for printing.
- * @param[in] ctx Libyang context.
- * @param[in] generate_features Flag expressing whether to generate features parameter.
- * @param[out] features_param String, which will contain the output if the above flag is set.
- * @return 0 on success.
- * @return 1 on error.
+ * @param[in] err Whether the message is an error or a warning.
+ * @param[in] format Message format.
+ * @param[in] ... Format arguments.
*/
-int print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool generate_features, char **features_param);
+void yl_log(ly_bool err, const char *format, ...);
/**
* @brief Parse path of a schema module file into the directory and module name.
@@ -141,7 +73,7 @@ int print_all_features(struct ly_out *out, const struct ly_ctx *ctx, ly_bool gen
* @param[in] path Schema module file path to be parsed.
* @param[out] dir Pointer to the directory path where the file resides. Caller is expected to free the returned string.
* @param[out] module Pointer to the name of the module (without file suffixes or revision information) specified by the
- * @path. Caller is expected to free the returned string.
+ * @p path. Caller is expected to free the returned string.
* @return 0 on success
* @return -1 on error
*/
@@ -158,100 +90,69 @@ int parse_schema_path(const char *path, char **dir, char **module);
* prohibited and such files are refused.
* @param[out] format_data Format of the data detected from the file name. If NULL specified, the data formats are
* prohibited and such files are refused.
- * @param[out] in Created input handler referring the file behind the @p filepath.
+ * @param[out] in Created input handler referring the file behind the @p filepath. Can be NULL.
* @return 0 on success.
* @return -1 on failure.
*/
int get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in);
/**
- * @brief Free the command line file data (struct cmdline_file *)
- * @param[in,out] cmdline_file The (struct cmdline_file *) to free.
- */
-void free_cmdline_file(void *cmdline_file);
-
-/**
- * @brief Create and fill the command line file data (struct cmdline_file *).
- * @param[in] set Optional parameter in case the record is supposed to be added into a set.
- * @param[in] in Input file handler.
- * @param[in] path Filepath of the file.
- * @param[in] format Format of the data file.
- * @return The created command line file structure.
- * @return NULL on failure
- */
-struct cmdline_file *fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format);
-
-/**
- * @brief Helper function to prepare argc, argv pair from a command line string.
+ * @brief Get schema format of the @p filename's content according to the @p filename's suffix.
*
- * @param[in] cmdline Complete command line string.
- * @param[out] argc_p Pointer to store argc value.
- * @param[out] argv_p Pointer to store argv vector.
- * @return 0 on success, non-zero on failure.
+ * @param[in] filename Name of the file to examine.
+ * @return Detected schema input format.
*/
-int parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[]);
+LYS_INFORMAT get_schema_format(const char *filename);
/**
- * @brief Destructor for the argument vector prepared by ::parse_cmdline().
+ * @brief Get data format of the @p filename's content according to the @p filename's suffix.
*
- * @param[in,out] argv Argument vector to destroy.
+ * @param[in] filename Name of the file to examine.
+ * @return Detected data input format.
*/
-void free_cmdline(char *argv[]);
+LYD_FORMAT get_data_format(const char *filename);
/**
- * @brief Get expected format of the @p filename's content according to the @p filename's suffix.
- * @param[in] filename Name of the file to examine.
- * @param[out] schema Pointer to a variable to store the expected input schema format. Do not provide the pointer in case a
- * schema format is not expected.
- * @param[out] data Pointer to a variable to store the expected input data format. Do not provide the pointer in case a data
- * format is not expected.
+ * @brief Get format of the @p filename's content according to the @p filename's suffix.
+ *
+ * Either the @p schema or @p data parameter is set.
+ *
+ * @param[in] filepath Name of the file to examine.
+ * @param[out] schema_form Pointer to a variable to store the input schema format.
+ * @param[out] data_form Pointer to a variable to store the expected input data format.
* @return zero in case a format was successfully detected.
* @return nonzero in case it is not possible to get valid format from the @p filename.
*/
-int get_format(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data);
+int get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form);
/**
- * @brief Print list of schemas in the context.
+ * @brief Get the node specified by the path.
*
- * @param[in] out Output handler where to print.
- * @param[in] ctx Context to print.
- * @param[in] outformat Optional output format. If not specified (:LYD_UNKNOWN), a simple list with single module per line
- * is printed. Otherwise, the ietf-yang-library data are printed in the specified format.
- * @return zero in case the data successfully printed.
- * @return nonzero in case of error.
+ * @param[in] ctx libyang context with schema.
+ * @param[in] schema_path Path to the wanted node.
+ * @return Pointer to the schema node specified by the path on success, NULL otherwise.
*/
-int print_list(struct ly_out *out, struct ly_ctx *ctx, LYD_FORMAT outformat);
+const struct lysc_node *find_schema_path(const struct ly_ctx *ctx, const char *schema_path);
/**
- * @brief Process the input data files - parse, validate and print according to provided options.
+ * @brief General callback providing run-time extension instance data.
*
- * @param[in] ctx libyang context with schema.
- * @param[in] data_type The type of data in the input files.
- * @param[in] merge Flag if the data should be merged before validation.
- * @param[in] format Data format for printing.
- * @param[in] out The output handler for printing.
- * @param[in] options_parse Parser options.
- * @param[in] options_validate Validation options.
- * @param[in] options_print Printer options.
- * @param[in] operational_f Optional operational datastore file information for the case of an extended validation of
- * operation(s).
- * @param[in] rpc_f Source RPC operation file information for parsing NETCONF rpc-reply.
- * @param[in] inputs Set of file informations of input data files.
- * @param[in] xpath The set of XPaths to be evaluated on the processed data tree, basic information about the resulting set
- * is printed. Alternative to data printing.
+ * @param[in] ext Compiled extension instance.
+ * @param[in] user_data User-supplied callback data.
+ * @param[out] ext_data Provided extension instance data.
+ * @param[out] ext_data_free Whether the extension instance should free @p ext_data or not.
* @return LY_ERR value.
*/
-LY_ERR process_data(struct ly_ctx *ctx, enum lyd_type data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
- uint32_t options_parse, uint32_t options_validate, uint32_t options_print, struct cmdline_file *operational_f,
- struct cmdline_file *rpc_f, struct ly_set *inputs, struct ly_set *xpaths);
+LY_ERR ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free);
/**
- * @brief Get the node specified by the path.
+ * @brief Concatenation of paths into one string.
*
- * @param[in] ctx libyang context with schema.
- * @param[in] schema_path Path to the wanted node.
- * @return Pointer to the schema node specified by the path on success, NULL otherwise.
+ * @param[in,out] searchpaths Collection of paths in the single string. Paths are delimited by colon ":"
+ * (on Windows, used semicolon ";" instead).
+ * @param[in] path Path to add.
+ * @return LY_ERR value.
*/
-const struct lysc_node * find_schema_path(const struct ly_ctx *ctx, const char *schema_path);
+LY_ERR searchpath_strcat(char **searchpaths, const char *path);
#endif /* COMMON_H_ */
diff --git a/tools/lint/completion.c b/tools/lint/completion.c
index 9843816..67c6b68 100644
--- a/tools/lint/completion.c
+++ b/tools/lint/completion.c
@@ -43,14 +43,18 @@ cmd_completion_add_match(const char *match, char ***matches, unsigned int *match
{
void *p;
- ++(*match_count);
- p = realloc(*matches, *match_count * sizeof **matches);
+ p = realloc(*matches, (*match_count + 1) * sizeof **matches);
if (!p) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
return;
}
*matches = p;
- (*matches)[*match_count - 1] = strdup(match);
+ (*matches)[*match_count] = strdup(match);
+ if (!((*matches)[*match_count])) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
+ return;
+ }
+ ++(*match_count);
}
/**
@@ -76,6 +80,34 @@ get_cmd_completion(const char *hint, char ***matches, unsigned int *match_count)
}
/**
+ * @brief Provides completion for arguments.
+ *
+ * @param[in] hint User input.
+ * @param[in] args Array of all possible arguments. The last element must be NULL.
+ * @param[out] matches Matches provided to the user as a completion hint.
+ * @param[out] match_count Number of matches.
+ */
+static void
+get_arg_completion(const char *hint, const char **args, char ***matches, unsigned int *match_count)
+{
+ int i;
+
+ *match_count = 0;
+ *matches = NULL;
+
+ for (i = 0; args[i]; i++) {
+ if (!strncmp(hint, args[i], strlen(hint))) {
+ cmd_completion_add_match(args[i], matches, match_count);
+ }
+ }
+ if (*match_count == 0) {
+ for (i = 0; args[i]; i++) {
+ cmd_completion_add_match(args[i], matches, match_count);
+ }
+ }
+}
+
+/**
* @brief Provides completion for module names.
*
* @param[in] hint User input.
@@ -108,21 +140,21 @@ get_model_completion(const char *hint, char ***matches, unsigned int *match_coun
/**
* @brief Add all child nodes of a single node to the completion hint.
*
- * @param[in] last_node Node of which children will be added to the hint.
- * @param matches[out] Matches provided to the user as a completion hint.
- * @param match_count[out] Number of matches.
+ * @param[in] parent Node of which children will be added to the hint.
+ * @param[out] matches Matches provided to the user as a completion hint.
+ * @param[out] match_count Number of matches.
*/
static void
-single_hint_add_children(const struct lysc_node *last_node, char ***matches, unsigned int *match_count)
+single_hint_add_children(const struct lysc_node *parent, char ***matches, unsigned int *match_count)
{
const struct lysc_node *node = NULL;
char *match;
- if (!last_node) {
+ if (!parent) {
return;
}
- while ((node = lys_getnext(node, last_node, NULL, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
+ while ((node = lys_getnext(node, parent, NULL, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
match = lysc_path(node, LYSC_PATH_LOG, NULL, 0);
cmd_completion_add_match(match, matches, match_count);
free(match);
@@ -157,13 +189,13 @@ add_all_children_nodes(const struct lysc_module *module, const struct lysc_node
if (parent && (node->module != parent->module)) {
/* augmented node */
if (asprintf(&node_name, "%s:%s", node->module->name, node->name) == -1) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
break;
}
} else {
node_name = strdup(node->name);
if (!node_name) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
break;
}
}
@@ -229,7 +261,7 @@ get_schema_completion(const char *hint, char ***matches, unsigned int *match_cou
/* module name known */
module_name = strndup(start, end - start);
if (!module_name) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
rc = 1;
goto cleanup;
}
@@ -255,7 +287,7 @@ get_schema_completion(const char *hint, char ***matches, unsigned int *match_cou
/* get rid of stuff after the last '/' to obtain the parent node */
path = strndup(hint, start - hint);
if (!path) {
- YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
rc = 1;
goto cleanup;
}
@@ -277,6 +309,90 @@ cleanup:
}
/**
+ * @brief Get all possible argument hints for option.
+ *
+ * @param[in] hint User input.
+ * @param[out] matches Matches provided to the user as a completion hint.
+ * @param[out] match_count Number of matches.
+ */
+static void
+get_print_format_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"yang", "yin", "tree", "info", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_data_type_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"data", "config", "get", "getconfig", "edit", "rpc", "reply", "notif", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_data_in_format_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"xml", "json", "lyb", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_data_default_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"all", "all-tagged", "trim", "implicit-tagged", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_list_format_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"xml", "json", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_verb_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"error", "warning", "verbose", "debug", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+#ifndef NDEBUG
+/**
+ * @copydoc get_print_format_arg
+ */
+static void
+get_debug_arg(const char *hint, char ***matches, unsigned int *match_count)
+{
+ const char *args[] = {"dict", "xpath", "dep-sets", NULL};
+
+ get_arg_completion(hint, args, matches, match_count);
+}
+
+#endif
+
+/**
* @brief Get the string before the hint, which autocompletion is for.
*
* @param[in] buf Complete user input.
@@ -315,22 +431,37 @@ void
complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
{
struct autocomplete {
- const char *cmd; /**< command */
- const char *opt; /**< optional option */
- int last_opt; /**< whether to autocomplete even if an option is last in the hint */
-
+ enum COMMAND_INDEX ci; /**< command index to global variable 'commands' */
+ const char *opt; /**< optional option */
void (*ln_cb)(const char *, const char *, linenoiseCompletions *); /**< linenoise callback to call */
void (*yl_cb)(const char *, char ***, unsigned int *); /**< yanglint callback to call */
} ac[] = {
- {"add", NULL, 1, linenoisePathCompletion, NULL},
- {"searchpath", NULL, 0, linenoisePathCompletion, NULL},
- {"data", NULL, 0, linenoisePathCompletion, NULL},
- {"print", NULL, 0, NULL, get_model_completion},
- {"feature", NULL, 0, NULL, get_model_completion},
- {"print", "-P", 1, NULL, get_schema_completion},
+ {CMD_ADD, NULL, linenoisePathCompletion, NULL},
+ {CMD_PRINT, "-f", NULL, get_print_format_arg},
+ {CMD_PRINT, "-P", NULL, get_schema_completion},
+ {CMD_PRINT, "-o", linenoisePathCompletion, NULL},
+ {CMD_PRINT, NULL, NULL, get_model_completion},
+ {CMD_SEARCHPATH, NULL, linenoisePathCompletion, NULL},
+ {CMD_EXTDATA, NULL, linenoisePathCompletion, NULL},
+ {CMD_CLEAR, "-Y", linenoisePathCompletion, NULL},
+ {CMD_DATA, "-t", NULL, get_data_type_arg},
+ {CMD_DATA, "-O", linenoisePathCompletion, NULL},
+ {CMD_DATA, "-R", linenoisePathCompletion, NULL},
+ {CMD_DATA, "-f", NULL, get_data_in_format_arg},
+ {CMD_DATA, "-F", NULL, get_data_in_format_arg},
+ {CMD_DATA, "-d", NULL, get_data_default_arg},
+ {CMD_DATA, "-o", linenoisePathCompletion, NULL},
+ {CMD_DATA, NULL, linenoisePathCompletion, NULL},
+ {CMD_LIST, NULL, NULL, get_list_format_arg},
+ {CMD_FEATURE, NULL, NULL, get_model_completion},
+ {CMD_VERB, NULL, NULL, get_verb_arg},
+#ifndef NDEBUG
+ {CMD_DEBUG, NULL, NULL, get_debug_arg},
+#endif
};
- size_t cmd_len;
- const char *last;
+ size_t name_len;
+ const char *last, *name, *getoptstr;
+ char opt[3] = {'\0', ':', '\0'};
char **matches = NULL;
unsigned int match_count = 0, i;
@@ -340,24 +471,27 @@ complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
} else {
for (i = 0; i < (sizeof ac / sizeof *ac); ++i) {
- cmd_len = strlen(ac[i].cmd);
- if (strncmp(buf, ac[i].cmd, cmd_len) || (buf[cmd_len] != ' ')) {
+ /* Find the right command. */
+ name = commands[ac[i].ci].name;
+ name_len = strlen(name);
+ if (strncmp(buf, name, name_len) || (buf[name_len] != ' ')) {
/* not this command */
continue;
}
+ /* Select based on the right option. */
last = get_last_str(buf, hint);
- if (ac[i].opt && strncmp(ac[i].opt, last, strlen(ac[i].opt))) {
- /* autocompletion for (another) option */
+ opt[0] = (last[0] == '-') && last[1] ? last[1] : '\0';
+ getoptstr = commands[ac[i].ci].optstring;
+ if (!ac[i].opt && opt[0] && strstr(getoptstr, opt)) {
+ /* completion for the argument must be defined */
continue;
- }
- if (!ac[i].last_opt && (last[0] == '-')) {
- /* autocompletion for the command, not an option */
+ } else if (ac[i].opt && opt[0] && strncmp(ac[i].opt, last, strlen(ac[i].opt))) {
+ /* completion for (another) option */
+ continue;
+ } else if (ac[i].opt && !opt[0]) {
+ /* completion is defined for option */
continue;
- }
- if ((last != buf) && (last[0] != '-')) {
- /* autocompleted */
- return;
}
/* callback */
diff --git a/tools/lint/configuration.c b/tools/lint/configuration.c
index 86179fa..e3db668 100644
--- a/tools/lint/configuration.c
+++ b/tools/lint/configuration.c
@@ -37,14 +37,14 @@ get_yanglint_dir(void)
char *user_home, *yl_dir;
if (!(pw = getpwuid(getuid()))) {
- YLMSG_E("Determining home directory failed (%s).\n", strerror(errno));
+ YLMSG_E("Determining home directory failed (%s).", strerror(errno));
return NULL;
}
user_home = pw->pw_dir;
yl_dir = malloc(strlen(user_home) + 1 + strlen(YL_DIR) + 1);
if (!yl_dir) {
- YLMSG_E("Memory allocation failed (%s).\n", strerror(errno));
+ YLMSG_E("Memory allocation failed (%s).", strerror(errno));
return NULL;
}
sprintf(yl_dir, "%s/%s", user_home, YL_DIR);
@@ -53,17 +53,17 @@ get_yanglint_dir(void)
if (ret == -1) {
if (errno == ENOENT) {
/* directory does not exist */
- YLMSG_W("Configuration directory \"%s\" does not exist, creating it.\n", yl_dir);
+ YLMSG_W("Configuration directory \"%s\" does not exist, creating it.", yl_dir);
if (mkdir(yl_dir, 00700)) {
if (errno != EEXIST) {
/* parallel execution, yay */
- YLMSG_E("Configuration directory \"%s\" cannot be created (%s).\n", yl_dir, strerror(errno));
+ YLMSG_E("Configuration directory \"%s\" cannot be created (%s).", yl_dir, strerror(errno));
free(yl_dir);
return NULL;
}
}
} else {
- YLMSG_E("Configuration directory \"%s\" exists but cannot be accessed (%s).\n", yl_dir, strerror(errno));
+ YLMSG_E("Configuration directory \"%s\" exists but cannot be accessed (%s).", yl_dir, strerror(errno));
free(yl_dir);
return NULL;
}
@@ -83,16 +83,16 @@ load_config(void)
history_file = malloc(strlen(yl_dir) + 9);
if (!history_file) {
- YLMSG_E("Memory allocation failed (%s).\n", strerror(errno));
+ YLMSG_E("Memory allocation failed (%s).", strerror(errno));
free(yl_dir);
return;
}
sprintf(history_file, "%s/history", yl_dir);
if (access(history_file, F_OK) && (errno == ENOENT)) {
- YLMSG_W("No saved history.\n");
+ YLMSG_W("No saved history.");
} else if (linenoiseHistoryLoad(history_file)) {
- YLMSG_E("Failed to load history.\n");
+ YLMSG_E("Failed to load history.");
}
free(history_file);
@@ -110,14 +110,14 @@ store_config(void)
history_file = malloc(strlen(yl_dir) + 9);
if (!history_file) {
- YLMSG_E("Memory allocation failed (%s).\n", strerror(errno));
+ YLMSG_E("Memory allocation failed (%s).", strerror(errno));
free(yl_dir);
return;
}
sprintf(history_file, "%s/history", yl_dir);
if (linenoiseHistorySave(history_file)) {
- YLMSG_E("Failed to save history.\n");
+ YLMSG_E("Failed to save history.");
}
free(history_file);
diff --git a/tools/lint/examples/README.md b/tools/lint/examples/README.md
index 604591c..93d3c2a 100644
--- a/tools/lint/examples/README.md
+++ b/tools/lint/examples/README.md
@@ -33,11 +33,11 @@ combination with the command name you are interested in:
```
> help searchpath
Usage: searchpath [--clear] [<modules-dir-path> ...]
- Set paths of directories where to search for imports and
- includes of the schema modules. The current working directory
- and the path of the module being added is used implicitly.
- The 'load' command uses these paths to search even for the
- schema modules to be loaded.
+ Set paths of directories where to search for imports and includes
+ of the schema modules. Subdirectories are also searched. The current
+ working directory and the path of the module being added is used implicitly.
+ The 'load' command uses these paths to search even for the schema modules
+ to be loaded.
```
The input files referred in this document are available together with this
@@ -469,3 +469,68 @@ ietf-ip features:
}
}
```
+
+## YANG modules with the Schema Mount extension
+
+In these examples the non-interactive `yanglint` is used to simplify creating the context, a `yang-library` data file is
+used. The working directory is `libyang/tools/lint/examples` and *libyang* must be installed.
+
+**Print tree output of a model with Schema Mount**
+
+Command and its output:
+
+```
+$ yanglint -f tree -p . -Y sm-context-main.xml -x sm-context-extension.xml sm-main.yang
+module: sm-main
+ +--mp root* [node]
+ | +--rw node string
+ +--mp root2
+ +--rw root3
+ +--mp my-list* [name]
+ +--rw things/* [name]
+ | +--rw name -> /if:interfaces/if:interface/if:name
+ | +--rw attribute? uint32
+ +--rw not-compiled/
+ | +--rw first? string
+ | +--rw second? string
+ +--rw interfaces@
+ | +--rw interface* [name]
+ | +--rw name string
+ | +--rw type identityref
+ +--rw name string
+```
+
+**Validating and printing mounted data**
+
+Command and its output:
+
+```
+$ yanglint -f json -t config -p . -Y sm-context-main.xml -x sm-context-extension.xml sm-data.xml
+{
+ "ietf-interfaces:interfaces": {
+ "interface": [
+ {
+ "name": "eth0",
+ "type": "iana-if-type:ethernetCsmacd"
+ },
+ {
+ "name": "eth1",
+ "type": "iana-if-type:ethernetCsmacd"
+ }
+ ]
+ },
+ "sm-main:root3": {
+ "my-list": [
+ {
+ "name": "list item 1",
+ "sm-extension:things": [
+ {
+ "name": "eth0",
+ "attribute": 1
+ }
+ ]
+ }
+ ]
+ }
+}
+```
diff --git a/tools/lint/main.c b/tools/lint/main.c
index 9f0d027..43b90c8 100644
--- a/tools/lint/main.c
+++ b/tools/lint/main.c
@@ -1,9 +1,10 @@
/**
* @file main.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool
*
- * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ * Copyright (c) 2015-2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -27,6 +28,7 @@
#include "completion.h"
#include "configuration.h"
#include "linenoise/linenoise.h"
+#include "yl_opt.h"
int done;
struct ly_ctx *ctx = NULL;
@@ -37,25 +39,32 @@ int main_ni(int argc, char *argv[]);
int
main(int argc, char *argv[])
{
- char *cmdline;
- int cmdlen;
+ int cmdlen, posc, i, j;
+ struct yl_opt yo = {0};
+ char *empty = NULL, *cmdline;
+ char **posv;
+ uint8_t cmd_found;
if (argc > 1) {
/* run in non-interactive mode */
return main_ni(argc, argv);
}
+ yo.interactive = 1;
/* continue in interactive mode */
linenoiseSetCompletionCallback(complete_cmd);
load_config();
if (ly_ctx_new(NULL, YL_DEFAULT_CTX_OPTIONS, &ctx)) {
- YLMSG_E("Failed to create context.\n");
+ YLMSG_E("Failed to create context.");
return 1;
}
while (!done) {
- uint8_t executed = 0;
+ cmd_found = 0;
+
+ posv = &empty;
+ posc = 0;
/* get the command from user */
cmdline = linenoise(PROMPT);
@@ -76,25 +85,48 @@ main(int argc, char *argv[])
for (cmdlen = 0; cmdline[cmdlen] && (cmdline[cmdlen] != ' '); cmdlen++) {}
/* execute the command if any valid specified */
- for (uint16_t i = 0; commands[i].name; i++) {
+ for (i = 0; commands[i].name; i++) {
if (strncmp(cmdline, commands[i].name, (size_t)cmdlen) || (commands[i].name[cmdlen] != '\0')) {
continue;
}
- commands[i].func(&ctx, cmdline);
- executed = 1;
+ cmd_found = 1;
+ if (commands[i].opt_func && commands[i].opt_func(&yo, cmdline, &posv, &posc)) {
+ break;
+ }
+ if (commands[i].dep_func && commands[i].dep_func(&yo, posc)) {
+ break;
+ }
+ if (posc) {
+ for (j = 0; j < posc; j++) {
+ yo.last_one = (j + 1) == posc;
+ if (commands[i].exec_func(&ctx, &yo, posv[j])) {
+ break;
+ }
+ }
+ } else {
+ commands[i].exec_func(&ctx, &yo, NULL);
+ }
+ if (commands[i].fin_func) {
+ commands[i].fin_func(ctx, &yo);
+ }
+
break;
}
- if (!executed) {
+ if (!cmd_found) {
/* if unknown command specified, tell it to user */
- YLMSG_E("Unknown command \"%.*s\", type 'help' for more information.\n", cmdlen, cmdline);
+ YLMSG_E("Unknown command \"%.*s\", type 'help' for more information.", cmdlen, cmdline);
}
linenoiseHistoryAdd(cmdline);
free(cmdline);
+ yl_opt_erase(&yo);
}
+ /* Global variables in commands are freed. */
+ cmd_free();
+
store_config();
ly_ctx_destroy(ctx);
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index 04c2340..c08acc3 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -2,9 +2,10 @@
* @file main_ni.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's yanglint tool - non-interactive code
*
- * Copyright (c) 2020 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -25,107 +26,13 @@
#include <sys/stat.h>
#include "libyang.h"
-#include "plugins_exts.h"
+#include "cmd.h"
#include "common.h"
#include "out.h"
#include "tools/config.h"
-
-/**
- * @brief Context structure to hold and pass variables in a structured form.
- */
-struct context {
- /* libyang context for the run */
- const char *yang_lib_file;
- uint16_t ctx_options;
- struct ly_ctx *ctx;
-
- /* prepared output (--output option or stdout by default) */
- struct ly_out *out;
-
- char *searchpaths;
-
- /* options flags */
- uint8_t list; /* -l option to print list of schemas */
-
- /* line length for 'tree' format */
- size_t line_length; /* --tree-line-length */
-
- /*
- * schema
- */
- /* set schema modules' features via --features option (struct schema_features *) */
- struct ly_set schema_features;
-
- /* set of loaded schema modules (struct lys_module *) */
- struct ly_set schema_modules;
-
- /* options to parse and print schema modules */
- uint32_t schema_parse_options;
- uint32_t schema_print_options;
-
- /* specification of printing schema node subtree, option --schema-node */
- const char *schema_node_path;
- const struct lysc_node *schema_node;
- const char *submodule;
-
- /* name of file containing explicit context passed to callback
- * for schema-mount extension. This also causes a callback to
- * be registered.
- */
- char *schema_context_filename;
-
- /* value of --format in case of schema format */
- LYS_OUTFORMAT schema_out_format;
- ly_bool feature_param_format;
-
- /*
- * data
- */
- /* various options based on --type option */
- enum lyd_type data_type;
- uint32_t data_parse_options;
- uint32_t data_validate_options;
- uint32_t data_print_options;
-
- /* flag for --merge option */
- uint8_t data_merge;
-
- /* value of --format in case of data format */
- LYD_FORMAT data_out_format;
-
- /* input data files (struct cmdline_file *) */
- struct ly_set data_inputs;
-
- /* storage for --operational */
- struct cmdline_file data_operational;
-
- /* storage for --reply-rpc */
- struct cmdline_file reply_rpc;
-};
-
-static void
-erase_context(struct context *c)
-{
- /* data */
- ly_set_erase(&c->data_inputs, free_cmdline_file);
- ly_in_free(c->data_operational.in, 1);
-
- /* schema */
- ly_set_erase(&c->schema_features, free_features);
- ly_set_erase(&c->schema_modules, NULL);
-
- /* context */
- free(c->searchpaths);
- c->searchpaths = NULL;
-
- ly_out_free(c->out, NULL, 0);
- ly_ctx_destroy(c->ctx);
-
- if (c->schema_context_filename) {
- free(c->schema_context_filename);
- }
-}
+#include "yl_opt.h"
+#include "yl_schema_features.h"
static void
version(void)
@@ -146,7 +53,8 @@ help(int shortout)
" printing them in the specified format.\n\n"
" yanglint -t (nc-)rpc/notif [-O <operational-file>] <schema>... <file>\n"
" Validates the YANG/NETCONF RPC/notification <file> according to the <schema>(s) using\n"
- " <operational-file> with possible references to the operational datastore data.\n\n"
+ " <operational-file> with possible references to the operational datastore data.\n"
+ " To validate nested-notification or action, the <operational-file> is required.\n\n"
" yanglint -t nc-reply -R <rpc-file> [-O <operational-file>] <schema>... <file>\n"
" Validates the NETCONF rpc-reply <file> of RPC <rpc-file> according to the <schema>(s)\n"
" using <operational-file> with possible references to the operational datastore data.\n\n"
@@ -169,10 +77,16 @@ help(int shortout)
" yang, yin, tree, info and feature-param for schemas,\n"
" xml, json, and lyb for data.\n\n");
+ printf(" -I FORMAT, --in-format=FORMAT\n"
+ " Load the data in one of the following formats:\n"
+ " xml, json, lyb\n"
+ " If input format not specified, it is detected from the file extension.\n\n");
+
printf(" -p PATH, --path=PATH\n"
" Search path for schema (YANG/YIN) modules. The option can be\n"
" used multiple times. The current working directory and the\n"
- " path of the module being added is used implicitly.\n\n");
+ " path of the module being added is used implicitly. Subdirectories\n"
+ " are also searched\n\n");
printf(" -D, --disable-searchdir\n"
" Do not implicitly search in current working directory for\n"
@@ -251,6 +165,13 @@ help(int shortout)
" trim - Remove all nodes with a default value.\n"
" implicit-tagged - Add missing nodes and mark them with the attribute.\n\n");
+ printf(" -E XPATH, --data-xpath=XPATH\n"
+ " Evaluate XPATH expression over the data and print the nodes satisfying\n"
+ " the expression. The output format is specific and the option cannot\n"
+ " be combined with the -f and -d options. Also all the data\n"
+ " inputs are merged into a single data tree where the expression\n"
+ " is evaluated, so the -m option is always set implicitly.\n\n");
+
printf(" -l, --list Print info about the loaded schemas.\n"
" (i - imported module, I - implemented module)\n"
" In case the '-f' option with data encoding is specified,\n"
@@ -267,8 +188,8 @@ help(int shortout)
" Provide optional data to extend validation of the '(nc-)rpc',\n"
" '(nc-)reply' or '(nc-)notif' TYPEs. The FILE is supposed to contain\n"
" the operational datastore referenced from the operation.\n"
- " In case of a nested operation, its parent existence is also\n"
- " checked in these operational data.\n\n");
+ " In case of a nested notification or action, its parent existence\n"
+ " is also checked in these operational data.\n\n");
printf(" -R FILE, --reply-rpc=FILE\n"
" Provide source RPC for parsing of the 'nc-reply' TYPE. The FILE\n"
@@ -287,6 +208,9 @@ help(int shortout)
" create an exact YANG schema context. If specified, the '-F'\n"
" parameter (enabled features) is ignored.\n\n");
+ printf(" -X, --extended-leafref\n"
+ " Allow usage of deref() XPath function within leafref\n\n");
+
#ifndef NDEBUG
printf(" -G GROUPS, --debug=GROUPS\n"
" Enable printing of specific debugging message group\n"
@@ -321,11 +245,11 @@ libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *path)
}
}
-static struct schema_features *
+static struct yl_schema_features *
get_features_not_applied(const struct ly_set *fset)
{
for (uint32_t u = 0; u < fset->count; ++u) {
- struct schema_features *sf = fset->objs[u];
+ struct yl_schema_features *sf = fset->objs[u];
if (!sf->applied) {
return sf;
@@ -335,190 +259,166 @@ get_features_not_applied(const struct ly_set *fset)
return NULL;
}
-static LY_ERR
-ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
-{
- struct ly_ctx *ctx;
- struct lyd_node *data = NULL;
-
- ctx = ext->module->ctx;
- if (user_data) {
- lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
- }
-
- *ext_data = data;
- *ext_data_free = 1;
- return LY_SUCCESS;
-}
-
-static LY_ERR
-searchpath_strcat(char **searchpaths, const char *path)
-{
- uint64_t len;
- char *new;
-
- if (!(*searchpaths)) {
- *searchpaths = strdup(path);
- return LY_SUCCESS;
- }
-
- len = strlen(*searchpaths) + strlen(path) + strlen(PATH_SEPARATOR);
- new = realloc(*searchpaths, sizeof(char) * len + 1);
- if (!new) {
- return LY_EMEM;
- }
- strcat(new, PATH_SEPARATOR);
- strcat(new, path);
- *searchpaths = new;
-
- return LY_SUCCESS;
-}
-
+/**
+ * @brief Create the libyang context.
+ *
+ * @param[in] yang_lib_file Context can be defined in yang library file.
+ * @param[in] searchpaths Directories in which modules are searched.
+ * @param[in,out] schema_features Set of features.
+ * @param[in,out] ctx_options Options for libyang context.
+ * @param[out] ctx Context for libyang.
+ * @return 0 on success.
+ */
static int
-fill_context_inputs(int argc, char *argv[], struct context *c)
+create_ly_context(const char *yang_lib_file, const char *searchpaths, struct ly_set *schema_features,
+ uint16_t *ctx_options, struct ly_ctx **ctx)
{
- struct ly_in *in = NULL;
- struct schema_features *sf;
- struct lys_module *mod;
- const char *all_features[] = {"*", NULL};
- char *dir = NULL, *module = NULL;
-
- /* Create libyang context. */
- if (c->yang_lib_file) {
+ if (yang_lib_file) {
/* ignore features */
- ly_set_erase(&c->schema_features, free_features);
+ ly_set_erase(schema_features, yl_schema_features_free);
- if (ly_ctx_new_ylpath(c->searchpaths, c->yang_lib_file, LYD_UNKNOWN, c->ctx_options, &c->ctx)) {
- YLMSG_E("Unable to modify libyang context with yang-library data.\n");
+ if (ly_ctx_new_ylpath(searchpaths, yang_lib_file, LYD_UNKNOWN, *ctx_options, ctx)) {
+ YLMSG_E("Unable to modify libyang context with yang-library data.");
return -1;
}
} else {
/* set imp feature flag if all should be enabled */
- c->ctx_options |= !c->schema_features.count ? LY_CTX_ENABLE_IMP_FEATURES : 0;
+ (*ctx_options) |= !schema_features->count ? LY_CTX_ENABLE_IMP_FEATURES : 0;
- if (ly_ctx_new(c->searchpaths, c->ctx_options, &c->ctx)) {
- YLMSG_E("Unable to create libyang context\n");
+ if (ly_ctx_new(searchpaths, *ctx_options, ctx)) {
+ YLMSG_E("Unable to create libyang context.");
return -1;
}
}
- /* set callback providing run-time extension instance data */
- if (c->schema_context_filename) {
- ly_ctx_set_ext_data_clb(c->ctx, ext_data_clb, c->schema_context_filename);
- }
+ return 0;
+}
- /* process the operational and/or reply RPC content if any */
- if (c->data_operational.path) {
- if (get_input(c->data_operational.path, NULL, &c->data_operational.format, &c->data_operational.in)) {
- return -1;
+/**
+ * @brief Implement module if some feature has not been applied.
+ *
+ * @param[in] schema_features Set of features.
+ * @param[in,out] ctx Context for libyang.
+ * @return 0 on success.
+ */
+static int
+apply_features(struct ly_set *schema_features, struct ly_ctx *ctx)
+{
+ struct yl_schema_features *sf;
+ struct lys_module *mod;
+
+ /* check that all specified features were applied, apply now if possible */
+ while ((sf = get_features_not_applied(schema_features))) {
+ /* try to find implemented or the latest revision of this module */
+ mod = ly_ctx_get_module_implemented(ctx, sf->mod_name);
+ if (!mod) {
+ mod = ly_ctx_get_module_latest(ctx, sf->mod_name);
}
- }
- if (c->reply_rpc.path) {
- if (get_input(c->reply_rpc.path, NULL, &c->reply_rpc.format, &c->reply_rpc.in)) {
- return -1;
+ if (!mod) {
+ YLMSG_E("Specified features not applied, module \"%s\" not loaded.", sf->mod_name);
+ return 1;
}
- }
- for (int i = 0; i < argc - optind; i++) {
- LYS_INFORMAT format_schema = LYS_IN_UNKNOWN;
- LYD_FORMAT format_data = LYD_UNKNOWN;
-
- if (get_input(argv[optind + i], &format_schema, &format_data, &in)) {
- goto error;
+ /* we have the module, implement it if needed and enable the specific features */
+ if (lys_set_implemented(mod, (const char **)sf->features)) {
+ YLMSG_E("Implementing module \"%s\" failed.", mod->name);
+ return 1;
}
+ sf->applied = 1;
+ }
- if (format_schema) {
- LY_ERR ret;
- uint8_t path_unset = 1; /* flag to unset the path from the searchpaths list (if not already present) */
- const char **features;
+ return 0;
+}
- /* parse the input */
- if (parse_schema_path(argv[optind + i], &dir, &module)) {
- goto error;
- }
+/**
+ * @brief Parse and compile modules, data are only stored for later processing.
+ *
+ * @param[in] argc Number of strings in @p argv.
+ * @param[in] argv Strings from command line.
+ * @param[in] optind Index to the first input file in @p argv.
+ * @param[in] data_in_format Specified input data format.
+ * @param[in,out] ctx Context for libyang.
+ * @param[in,out] yo Options for yanglint.
+ * @return 0 on success.
+ */
+static int
+fill_context_inputs(int argc, char *argv[], int optind, LYD_FORMAT data_in_format, struct ly_ctx *ctx,
+ struct yl_opt *yo)
+{
+ char *filepath = NULL;
+ LYS_INFORMAT format_schema;
+ LYD_FORMAT format_data;
- if (c->yang_lib_file) {
- /* just get the module, it should already be parsed */
- mod = ly_ctx_get_module_implemented(c->ctx, module);
- if (!mod) {
- YLMSG_E("Schema module \"%s\" not implemented in yang-library data.\n", module);
- goto error;
- }
- } else {
- /* add temporarily also the path of the module itself */
- if (ly_ctx_set_searchdir(c->ctx, dir) == LY_EEXIST) {
- path_unset = 0;
- }
+ for (int i = 0; i < argc - optind; i++) {
+ format_schema = LYS_IN_UNKNOWN;
+ format_data = data_in_format;
- /* get features list for this module */
- if (!c->schema_features.count) {
- features = all_features;
- } else {
- get_features(&c->schema_features, module, &features);
- }
+ filepath = argv[optind + i];
- /* parse module */
- ret = lys_parse(c->ctx, in, format_schema, features, &mod);
- ly_ctx_unset_searchdir_last(c->ctx, path_unset);
- if (ret) {
- YLMSG_E("Parsing schema module \"%s\" failed.\n", argv[optind + i]);
- goto error;
- }
- }
+ if (!filepath) {
+ return -1;
+ }
+ if (get_format(filepath, &format_schema, &format_data)) {
+ return -1;
+ }
- /* temporary cleanup */
- free(dir);
- dir = NULL;
- free(module);
- module = NULL;
-
- if (c->schema_out_format || c->feature_param_format) {
- /* module will be printed */
- if (ly_set_add(&c->schema_modules, (void *)mod, 1, NULL)) {
- YLMSG_E("Storing parsed schema module (%s) for print failed.\n", argv[optind + i]);
- goto error;
- }
+ if (format_schema) {
+ if (cmd_add_exec(&ctx, yo, filepath)) {
+ return -1;
}
- } else if (format_data) {
- if (!fill_cmdline_file(&c->data_inputs, in, argv[optind + i], format_data)) {
- goto error;
+ } else {
+ if (cmd_data_store(&ctx, yo, filepath)) {
+ return -1;
}
- in = NULL;
}
+ }
- ly_in_free(in, 1);
- in = NULL;
+ /* Check that all specified features were applied, apply now if possible. */
+ if (apply_features(&yo->schema_features, ctx)) {
+ return -1;
}
- /* check that all specified features were applied, apply now if possible */
- while ((sf = get_features_not_applied(&c->schema_features))) {
- /* try to find implemented or the latest revision of this module */
- mod = ly_ctx_get_module_implemented(c->ctx, sf->mod_name);
- if (!mod) {
- mod = ly_ctx_get_module_latest(c->ctx, sf->mod_name);
- }
- if (!mod) {
- YLMSG_E("Specified features not applied, module \"%s\" not loaded.\n", sf->mod_name);
- goto error;
- }
+ return 0;
+}
- /* we have the module, implement it if needed and enable the specific features */
- if (lys_set_implemented(mod, (const char **)sf->features)) {
- YLMSG_E("Implementing module \"%s\" failed.\n", mod->name);
- goto error;
+#ifndef NDEBUG
+/**
+ * @brief Enable specific debugging messages.
+ *
+ * @param[in] groups String in the form "<group>[,group>]*".
+ * @param[in,out] yo Options for yanglint.
+ * return 0 on success.
+ */
+static int
+set_debug_groups(char *groups, struct yl_opt *yo)
+{
+ int rc;
+ char *str, *end;
+
+ /* Process all debug arguments except the last one. */
+ for (str = groups; (end = strchr(str, ',')); str = end + 1) {
+ /* Temporary modify input string. */
+ *end = '\0';
+ rc = cmd_debug_store(NULL, yo, str);
+ *end = ',';
+ if (rc) {
+ return -1;
}
- sf->applied = 1;
+ }
+ /* Process single/last debug argument. */
+ if (cmd_debug_store(NULL, yo, str)) {
+ return -1;
+ }
+ /* All debug arguments are valid, so they can apply. */
+ if (cmd_debug_setlog(NULL, yo)) {
+ return -1;
}
return 0;
-
-error:
- ly_in_free(in, 1);
- free(dir);
- free(module);
- return -1;
}
+#endif
+
/**
* @brief Process command line options and store the settings into the context.
*
@@ -527,10 +427,8 @@ error:
* return 1 in case of success, but expect to exit.
*/
static int
-fill_context(int argc, char *argv[], struct context *c)
+fill_context(int argc, char *argv[], struct yl_opt *yo, struct ly_ctx **ctx)
{
- int ret;
-
int opt, opt_index;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
@@ -542,6 +440,7 @@ fill_context(int argc, char *argv[], struct context *c)
{"disable-searchdir", no_argument, NULL, 'D'},
{"features", required_argument, NULL, 'F'},
{"make-implemented", no_argument, NULL, 'i'},
+ {"in-format", required_argument, NULL, 'I'},
{"schema-node", required_argument, NULL, 'P'},
{"single-node", no_argument, NULL, 'q'},
{"submodule", required_argument, NULL, 's'},
@@ -550,6 +449,7 @@ fill_context(int argc, char *argv[], struct context *c)
{"present", no_argument, NULL, 'e'},
{"type", required_argument, NULL, 't'},
{"default", required_argument, NULL, 'd'},
+ {"data-xpath", required_argument, NULL, 'E'},
{"list", no_argument, NULL, 'l'},
{"tree-line-length", required_argument, NULL, 'L'},
{"output", required_argument, NULL, 'o'},
@@ -558,6 +458,7 @@ fill_context(int argc, char *argv[], struct context *c)
{"merge", no_argument, NULL, 'm'},
{"yang-library", no_argument, NULL, 'y'},
{"yang-library-file", required_argument, NULL, 'Y'},
+ {"extended-leafref", no_argument, NULL, 'X'},
#ifndef NDEBUG
{"debug", required_argument, NULL, 'G'},
#endif
@@ -565,15 +466,16 @@ fill_context(int argc, char *argv[], struct context *c)
};
uint8_t data_type_set = 0;
- c->ctx_options = YL_DEFAULT_CTX_OPTIONS;
- c->data_parse_options = YL_DEFAULT_DATA_PARSE_OPTIONS;
- c->line_length = 0;
+ yo->ctx_options = YL_DEFAULT_CTX_OPTIONS;
+ yo->data_parse_options = YL_DEFAULT_DATA_PARSE_OPTIONS;
+ yo->data_validate_options = YL_DEFAULT_DATA_VALIDATE_OPTIONS;
+ yo->line_length = 0;
opterr = 0;
#ifndef NDEBUG
- while ((opt = getopt_long(argc, argv, "hvVQf:p:DF:iP:qs:net:d:lL:o:O:R:myY:x:G:", options, &opt_index)) != -1)
+ while ((opt = getopt_long(argc, argv, "hvVQf:I:p:DF:iP:qs:neE:t:d:lL:o:O:R:myY:Xx:G:", options, &opt_index)) != -1)
#else
- while ((opt = getopt_long(argc, argv, "hvVQf:p:DF:iP:qs:net:d:lL:o:O:R:myY:x:", options, &opt_index)) != -1)
+ while ((opt = getopt_long(argc, argv, "hvVQf:I:p:DF:iP:qs:neE:t:d:lL:o:O:R:myY:Xx:", options, &opt_index)) != -1)
#endif
{
switch (opt) {
@@ -609,138 +511,77 @@ fill_context(int argc, char *argv[], struct context *c)
} /* case 'Q' */
case 'f': /* --format */
- if (!strcasecmp(optarg, "yang")) {
- c->schema_out_format = LYS_OUT_YANG;
- c->data_out_format = 0;
- } else if (!strcasecmp(optarg, "yin")) {
- c->schema_out_format = LYS_OUT_YIN;
- c->data_out_format = 0;
- } else if (!strcasecmp(optarg, "info")) {
- c->schema_out_format = LYS_OUT_YANG_COMPILED;
- c->data_out_format = 0;
- } else if (!strcasecmp(optarg, "tree")) {
- c->schema_out_format = LYS_OUT_TREE;
- c->data_out_format = 0;
- } else if (!strcasecmp(optarg, "xml")) {
- c->schema_out_format = 0;
- c->data_out_format = LYD_XML;
- } else if (!strcasecmp(optarg, "json")) {
- c->schema_out_format = 0;
- c->data_out_format = LYD_JSON;
- } else if (!strcasecmp(optarg, "lyb")) {
- c->schema_out_format = 0;
- c->data_out_format = LYD_LYB;
- } else if (!strcasecmp(optarg, "feature-param")) {
- c->feature_param_format = 1;
- } else {
- YLMSG_E("Unknown output format %s\n", optarg);
+ if (yl_opt_update_out_format(optarg, yo)) {
help(1);
return -1;
}
break;
- case 'p': { /* --path */
- struct stat st;
-
- if (stat(optarg, &st) == -1) {
- YLMSG_E("Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
- return -1;
- }
- if (!S_ISDIR(st.st_mode)) {
- YLMSG_E("Provided search path is not a directory.\n");
+ case 'I': /* --in-format */
+ if (yo_opt_update_data_in_format(optarg, yo)) {
+ YLMSG_E("Unknown input format %s.", optarg);
+ help(1);
return -1;
}
+ break;
- if (searchpath_strcat(&c->searchpaths, optarg)) {
- YLMSG_E("Storing searchpath failed.\n");
+ case 'p': /* --path */
+ if (searchpath_strcat(&yo->searchpaths, optarg)) {
+ YLMSG_E("Storing searchpath failed.");
return -1;
}
-
break;
- } /* case 'p' */
+ /* case 'p' */
- case 'D': /* --disable-search */
- if (c->ctx_options & LY_CTX_DISABLE_SEARCHDIRS) {
- YLMSG_W("The -D option specified too many times.\n");
- }
- if (c->ctx_options & LY_CTX_DISABLE_SEARCHDIR_CWD) {
- c->ctx_options &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
- c->ctx_options |= LY_CTX_DISABLE_SEARCHDIRS;
- } else {
- c->ctx_options |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+ case 'D': /* --disable-searchdir */
+ if (yo->ctx_options & LY_CTX_DISABLE_SEARCHDIRS) {
+ YLMSG_W("The -D option specified too many times.");
}
+ yo_opt_update_disable_searchdir(yo);
break;
case 'F': /* --features */
- if (parse_features(optarg, &c->schema_features)) {
+ if (parse_features(optarg, &yo->schema_features)) {
return -1;
}
break;
case 'i': /* --make-implemented */
- if (c->ctx_options & LY_CTX_REF_IMPLEMENTED) {
- c->ctx_options &= ~LY_CTX_REF_IMPLEMENTED;
- c->ctx_options |= LY_CTX_ALL_IMPLEMENTED;
- } else {
- c->ctx_options |= LY_CTX_REF_IMPLEMENTED;
- }
+ yo_opt_update_make_implemented(yo);
break;
case 'P': /* --schema-node */
- c->schema_node_path = optarg;
+ yo->schema_node_path = optarg;
break;
case 'q': /* --single-node */
- c->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
+ yo->schema_print_options |= LYS_PRINT_NO_SUBSTMT;
break;
case 's': /* --submodule */
- c->submodule = optarg;
+ yo->submodule = optarg;
break;
case 'x': /* --ext-data */
- c->schema_context_filename = strdup(optarg);
+ yo->schema_context_filename = optarg;
break;
case 'n': /* --not-strict */
- c->data_parse_options &= ~LYD_PARSE_STRICT;
+ yo->data_parse_options &= ~LYD_PARSE_STRICT;
break;
case 'e': /* --present */
- c->data_validate_options |= LYD_VALIDATE_PRESENT;
+ yo->data_validate_options |= LYD_VALIDATE_PRESENT;
break;
case 't': /* --type */
if (data_type_set) {
- YLMSG_E("The data type (-t) cannot be set multiple times.\n");
+ YLMSG_E("The data type (-t) cannot be set multiple times.");
return -1;
}
- if (!strcasecmp(optarg, "config")) {
- c->data_parse_options |= LYD_PARSE_NO_STATE;
- c->data_validate_options |= LYD_VALIDATE_NO_STATE;
- } else if (!strcasecmp(optarg, "get")) {
- c->data_parse_options |= LYD_PARSE_ONLY;
- } else if (!strcasecmp(optarg, "getconfig") || !strcasecmp(optarg, "get-config")) {
- c->data_parse_options |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
- } else if (!strcasecmp(optarg, "edit")) {
- c->data_parse_options |= LYD_PARSE_ONLY;
- } else if (!strcasecmp(optarg, "rpc")) {
- c->data_type = LYD_TYPE_RPC_YANG;
- } else if (!strcasecmp(optarg, "nc-rpc")) {
- c->data_type = LYD_TYPE_RPC_NETCONF;
- } else if (!strcasecmp(optarg, "reply")) {
- c->data_type = LYD_TYPE_REPLY_YANG;
- } else if (!strcasecmp(optarg, "nc-reply")) {
- c->data_type = LYD_TYPE_REPLY_NETCONF;
- } else if (!strcasecmp(optarg, "notif")) {
- c->data_type = LYD_TYPE_NOTIF_YANG;
- } else if (!strcasecmp(optarg, "nc-notif")) {
- c->data_type = LYD_TYPE_NOTIF_NETCONF;
- } else if (!strcasecmp(optarg, "data")) {
- /* default option */
- } else {
- YLMSG_E("Unknown data tree type %s\n", optarg);
+ if (yl_opt_update_data_type(optarg, yo)) {
+ YLMSG_E("Unknown data tree type %s.", optarg);
help(1);
return -1;
}
@@ -749,184 +590,128 @@ fill_context(int argc, char *argv[], struct context *c)
break;
case 'd': /* --default */
- if (!strcasecmp(optarg, "all")) {
- c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
- } else if (!strcasecmp(optarg, "all-tagged")) {
- c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
- } else if (!strcasecmp(optarg, "trim")) {
- c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
- } else if (!strcasecmp(optarg, "implicit-tagged")) {
- c->data_print_options = (c->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
- } else {
- YLMSG_E("Unknown default mode %s\n", optarg);
+ if (yo_opt_update_data_default(optarg, yo)) {
+ YLMSG_E("Unknown default mode %s.", optarg);
help(1);
return -1;
}
break;
+ case 'E': /* --data-xpath */
+ if (ly_set_add(&yo->data_xpath, optarg, 0, NULL)) {
+ YLMSG_E("Storing XPath \"%s\" failed.", optarg);
+ return -1;
+ }
+ break;
+
case 'l': /* --list */
- c->list = 1;
+ yo->list = 1;
break;
case 'L': /* --tree-line-length */
- c->line_length = atoi(optarg);
+ yo->line_length = atoi(optarg);
break;
case 'o': /* --output */
- if (c->out) {
- YLMSG_E("Only a single output can be specified.\n");
+ if (yo->out) {
+ YLMSG_E("Only a single output can be specified.");
return -1;
} else {
- if (ly_out_new_filepath(optarg, &c->out)) {
- YLMSG_E("Unable open output file %s (%s)\n", optarg, strerror(errno));
+ if (ly_out_new_filepath(optarg, &yo->out)) {
+ YLMSG_E("Unable open output file %s (%s).", optarg, strerror(errno));
return -1;
}
}
break;
case 'O': /* --operational */
- if (c->data_operational.path) {
- YLMSG_E("The operational datastore (-O) cannot be set multiple times.\n");
+ if (yo->data_operational.path) {
+ YLMSG_E("The operational datastore (-O) cannot be set multiple times.");
return -1;
}
- c->data_operational.path = optarg;
+ yo->data_operational.path = optarg;
break;
case 'R': /* --reply-rpc */
- if (c->reply_rpc.path) {
- YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.\n");
+ if (yo->reply_rpc.path) {
+ YLMSG_E("The PRC of the reply (-R) cannot be set multiple times.");
return -1;
}
- c->reply_rpc.path = optarg;
+ yo->reply_rpc.path = optarg;
break;
case 'm': /* --merge */
- c->data_merge = 1;
+ yo->data_merge = 1;
break;
case 'y': /* --yang-library */
- c->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
+ yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
break;
case 'Y': /* --yang-library-file */
- c->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
- c->yang_lib_file = optarg;
+ yo->ctx_options &= ~LY_CTX_NO_YANGLIBRARY;
+ yo->yang_lib_file = optarg;
break;
-#ifndef NDEBUG
- case 'G': { /* --debug */
- uint32_t dbg_groups = 0;
- const char *ptr = optarg;
-
- while (ptr[0]) {
- if (!strncasecmp(ptr, "dict", sizeof "dict" - 1)) {
- dbg_groups |= LY_LDGDICT;
- ptr += sizeof "dict" - 1;
- } else if (!strncasecmp(ptr, "xpath", sizeof "xpath" - 1)) {
- dbg_groups |= LY_LDGXPATH;
- ptr += sizeof "xpath" - 1;
- } else if (!strncasecmp(ptr, "dep-sets", sizeof "dep-sets" - 1)) {
- dbg_groups |= LY_LDGDEPSETS;
- ptr += sizeof "dep-sets" - 1;
- }
+ case 'X': /* --extended-leafref */
+ yo->ctx_options |= LY_CTX_LEAFREF_EXTENDED;
+ break;
- if (ptr[0]) {
- if (ptr[0] != ',') {
- YLMSG_E("Unknown debug group string \"%s\"\n", optarg);
- return -1;
- }
- ++ptr;
- }
+#ifndef NDEBUG
+ case 'G': /* --debug */
+ if (set_debug_groups(optarg, yo)) {
+ return -1;
}
- ly_log_dbg_groups(dbg_groups);
break;
- } /* case 'G' */
+ /* case 'G' */
#endif
default:
- YLMSG_E("Invalid option or missing argument: -%c\n", optopt);
+ YLMSG_E("Invalid option or missing argument: -%c.", optopt);
return -1;
} /* switch */
}
/* additional checks for the options combinations */
- if (!c->list && (optind >= argc)) {
+ if (!yo->list && (optind >= argc)) {
help(1);
- YLMSG_E("Missing <schema> to process.\n");
+ YLMSG_E("Missing <schema> to process.");
return 1;
}
- if (c->data_merge) {
- if (c->data_type || (c->data_parse_options & LYD_PARSE_ONLY)) {
- /* switch off the option, incompatible input data type */
- c->data_merge = 0;
- } else {
- /* postpone validation after the merge of all the input data */
- c->data_parse_options |= LYD_PARSE_ONLY;
- }
- }
-
- if (c->data_operational.path && !c->data_type) {
- YLMSG_E("Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types.\n");
- c->data_operational.path = NULL;
- }
-
- if (c->reply_rpc.path && (c->data_type != LYD_TYPE_REPLY_NETCONF)) {
- YLMSG_E("Source RPC is needed only for NETCONF Reply input data type.\n");
- c->data_operational.path = NULL;
- } else if (!c->reply_rpc.path && (c->data_type == LYD_TYPE_REPLY_NETCONF)) {
- YLMSG_E("Missing source RPC (-R) for NETCONF Reply input data type.\n");
+ if (cmd_data_dep(yo, 0)) {
return -1;
}
-
- if ((c->schema_out_format != LYS_OUT_TREE) && c->line_length) {
- YLMSG_E("--tree-line-length take effect only in case of the tree output format.\n");
+ if (cmd_print_dep(yo, 0)) {
+ return -1;
}
- /* default output stream */
- if (!c->out) {
- if (ly_out_new_file(stdout, &c->out)) {
- YLMSG_E("Unable to set stdout as output.\n");
- return -1;
- }
+ /* Create the libyang context. */
+ if (create_ly_context(yo->yang_lib_file, yo->searchpaths, &yo->schema_features, &yo->ctx_options, ctx)) {
+ return -1;
}
- if (c->schema_out_format == LYS_OUT_TREE) {
- /* print tree from lysc_nodes */
- c->ctx_options |= LY_CTX_SET_PRIV_PARSED;
+ /* Set callback providing run-time extension instance data. */
+ if (yo->schema_context_filename) {
+ ly_ctx_set_ext_data_clb(*ctx, ext_data_clb, yo->schema_context_filename);
}
- /* process input files provided as standalone command line arguments,
- * schema modules are parsed and inserted into the context,
- * data files are just checked and prepared into internal structures for further processing */
- ret = fill_context_inputs(argc, argv, c);
- if (ret) {
- return ret;
+ /* Schema modules and data files are just checked and prepared into internal structures for further processing. */
+ if (fill_context_inputs(argc, argv, optind, yo->data_in_format, *ctx, yo)) {
+ return -1;
}
/* the second batch of checks */
- if (c->schema_print_options && !c->schema_out_format) {
- YLMSG_W("Schema printer options specified, but the schema output format is missing.\n");
+ if (yo->schema_print_options && !yo->schema_out_format) {
+ YLMSG_W("Schema printer options specified, but the schema output format is missing.");
}
- if (c->schema_parse_options && !c->schema_modules.count) {
- YLMSG_W("Schema parser options specified, but no schema input file provided.\n");
+ if (yo->schema_parse_options && !yo->schema_modules.count) {
+ YLMSG_W("Schema parser options specified, but no schema input file provided.");
}
- if (c->data_print_options && !c->data_out_format) {
- YLMSG_W("data printer options specified, but the data output format is missing.\n");
+ if (yo->data_print_options && !yo->data_out_format) {
+ YLMSG_W("data printer options specified, but the data output format is missing.");
}
- if (((c->data_parse_options != YL_DEFAULT_DATA_PARSE_OPTIONS) || c->data_type) && !c->data_inputs.count) {
- YLMSG_W("Data parser options specified, but no data input file provided.\n");
- }
-
- if (c->schema_node_path) {
- c->schema_node = lys_find_path(c->ctx, NULL, c->schema_node_path, 0);
- if (!c->schema_node) {
- c->schema_node = lys_find_path(c->ctx, NULL, c->schema_node_path, 1);
-
- if (!c->schema_node) {
- YLMSG_E("Invalid schema path.\n");
- return -1;
- }
- }
+ if (((yo->data_parse_options != YL_DEFAULT_DATA_PARSE_OPTIONS) || yo->data_type) && !yo->data_inputs.count) {
+ YLMSG_W("Data parser options specified, but no data input file provided.");
}
return 0;
@@ -936,7 +721,8 @@ int
main_ni(int argc, char *argv[])
{
int ret = EXIT_SUCCESS, r;
- struct context c = {0};
+ struct yl_opt yo = {0};
+ struct ly_ctx *ctx = NULL;
char *features_output = NULL;
struct ly_set set = {0};
uint32_t u;
@@ -944,7 +730,7 @@ main_ni(int argc, char *argv[])
/* set callback for printing libyang messages */
ly_set_log_clb(libyang_verbclb, 1);
- r = fill_context(argc, argv, &c);
+ r = fill_context(argc, argv, &yo, &ctx);
if (r < 0) {
ret = EXIT_FAILURE;
}
@@ -954,72 +740,46 @@ main_ni(int argc, char *argv[])
/* do the required job - parse, validate, print */
- if (c.list) {
+ if (yo.list) {
/* print the list of schemas */
- print_list(c.out, c.ctx, c.data_out_format);
- } else {
- if (c.feature_param_format) {
- for (u = 0; u < c.schema_modules.count; u++) {
- if (collect_features(c.schema_modules.objs[u], &set)) {
- YLMSG_E("Unable to read features from a module.\n");
- goto cleanup;
- }
- if (generate_features_output(c.schema_modules.objs[u], &set, &features_output)) {
- YLMSG_E("Unable to generate feature command output.\n");
- goto cleanup;
- }
- ly_set_erase(&set, NULL);
- }
- ly_print(c.out, "%s\n", features_output);
- } else if (c.schema_out_format) {
- if (c.schema_node) {
- ret = lys_print_node(c.out, c.schema_node, c.schema_out_format, 0, c.schema_print_options);
- if (ret) {
- YLMSG_E("Unable to print schema node %s.\n", c.schema_node_path);
- goto cleanup;
- }
- } else if (c.submodule) {
- const struct lysp_submodule *submod = ly_ctx_get_submodule_latest(c.ctx, c.submodule);
-
- if (!submod) {
- YLMSG_E("Unable to find submodule %s.\n", c.submodule);
- goto cleanup;
- }
-
- ret = lys_print_submodule(c.out, submod, c.schema_out_format, c.line_length, c.schema_print_options);
- if (ret) {
- YLMSG_E("Unable to print submodule %s.\n", submod->name);
- goto cleanup;
- }
- } else {
- for (u = 0; u < c.schema_modules.count; ++u) {
- ret = lys_print_module(c.out, (struct lys_module *)c.schema_modules.objs[u], c.schema_out_format,
- c.line_length, c.schema_print_options);
- /* for YANG Tree Diagrams printing it's more readable to print a blank line between modules. */
- if ((c.schema_out_format == LYS_OUT_TREE) && (u + 1 < c.schema_modules.count)) {
- ly_print(c.out, "\n");
- }
- if (ret) {
- YLMSG_E("Unable to print module %s.\n", ((struct lys_module *)c.schema_modules.objs[u])->name);
- goto cleanup;
- }
- }
+ ret = cmd_list_exec(&ctx, &yo, NULL);
+ goto cleanup;
+ }
+ if (yo.feature_param_format) {
+ for (u = 0; u < yo.schema_modules.count; u++) {
+ if ((ret = cmd_feature_exec(&ctx, &yo, ((struct lys_module *)yo.schema_modules.objs[u])->name))) {
+ goto cleanup;
}
}
-
- /* do the data validation despite the schema was printed */
- if (c.data_inputs.size) {
- ret = process_data(c.ctx, c.data_type, c.data_merge, c.data_out_format, c.out, c.data_parse_options,
- c.data_validate_options, c.data_print_options, &c.data_operational, &c.reply_rpc, &c.data_inputs, NULL);
- if (ret) {
+ cmd_feature_fin(ctx, &yo);
+ } else if (yo.schema_out_format && yo.schema_node_path) {
+ if ((ret = cmd_print_exec(&ctx, &yo, NULL))) {
+ goto cleanup;
+ }
+ } else if (yo.schema_out_format && yo.submodule) {
+ if ((ret = cmd_print_exec(&ctx, &yo, yo.submodule))) {
+ goto cleanup;
+ }
+ } else if (yo.schema_out_format) {
+ for (u = 0; u < yo.schema_modules.count; ++u) {
+ yo.last_one = (u + 1) == yo.schema_modules.count;
+ if ((ret = cmd_print_exec(&ctx, &yo, ((struct lys_module *)yo.schema_modules.objs[u])->name))) {
goto cleanup;
}
}
}
+ /* do the data validation despite the schema was printed */
+ if (yo.data_inputs.size) {
+ if ((ret = cmd_data_process(ctx, &yo))) {
+ goto cleanup;
+ }
+ }
+
cleanup:
/* cleanup */
- erase_context(&c);
+ yl_opt_erase(&yo);
+ ly_ctx_destroy(ctx);
free(features_output);
ly_set_erase(&set, NULL);
diff --git a/tools/lint/tests/expect/common.exp b/tools/lint/tests/expect/common.exp
deleted file mode 100644
index 0381e6c..0000000
--- a/tools/lint/tests/expect/common.exp
+++ /dev/null
@@ -1,68 +0,0 @@
-# detect the path to the yanglint binary
-if { [info exists ::env(YANGLINT)] } {
- set yanglint "$env(YANGLINT)"
-} else {
- set yanglint "../../../../build/yanglint"
-}
-
-# set the timeout to 1 second
-set timeout 1
-
-# expect a single line of anchored regex output
-proc expect_output {output} {
- expect {
- -re "^${output}$" {}
- timeout {exit 1}
- }
-}
-
-# send a command and either expect some anchored regex output if specified or just an empty line
-proc expect_command {command has_output output} {
- send -- "${command}\r"
-
- if ($has_output==1) {
- expect {
- -re "^${command}\r\n${output}$" {}
- timeout {exit 1}
- }
- } else {
- # input echoes
- expect {
- -re "^${command}\r\n$" {}
- timeout {exit 1}
- }
- expect {
- -re "^> $" {}
- timeout {exit 1}
- }
- }
-}
-
-# send a completion request and check if the anchored regex output matches
-proc expect_completion {input output} {
- send -- "${input}\t"
-
- expect {
- # expecting echoing input, output and 10 terminal control characters
- -re "^${input}\r> ${output}.*\r.*$" {}
- timeout {exit 1}
- }
-}
-
-# send a completion request and check if the anchored regex hint options match
-proc expect_hint {input prev_input hints} {
- set output {}
- foreach i $hints {
- # each element might have some number of spaces and CRLF around it
- append output "${i} *(?:\\r\\n)?"
- }
-
- send -- "${input}\t"
-
- expect {
- # expecting the hints, previous input from which the hints were generated
- # and some number of terminal control characters
- -re "^\r\n${output}\r> ${prev_input}.*\r.*$" {}
- timeout {exit 1}
- }
-}
diff --git a/tools/lint/tests/expect/completion.exp b/tools/lint/tests/expect/completion.exp
deleted file mode 100755
index ed4f6bd..0000000
--- a/tools/lint/tests/expect/completion.exp
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/expect -f
-
-if { [info exists ::env(CURRENT_SOURCE_DIR)] } {
- source "$env(CURRENT_SOURCE_DIR)/tests/expect/common.exp"
- set yang_dir "$env(CURRENT_SOURCE_DIR)/examples"
-} else {
- source "common.exp"
- set yang_dir "../../examples"
-}
-
-spawn $yanglint
-
-# skip no dir and/or no history warnings
-expect_output "(YANGLINT.*)*> "
-
-expect_command "clear -ii" 0 ""
-
-expect_command "add ${yang_dir}/ietf-ip.yang" 0 ""
-
-expect_completion "print -f info -P " "print -f info -P /ietf-"
-
-set hints {"/ietf-yang-schema-mount:schema-mounts" "/ietf-interfaces:interfaces" "/ietf-interfaces:interfaces-state"}
-
-expect_hint "" "print -f info -P /ietf-" $hints
-
-expect_completion "i" "print -f info -P /ietf-interfaces:interfaces"
-
-expect_completion "/" "print -f info -P /ietf-interfaces:interfaces/interface"
-
-set hints {"/ietf-interfaces:interfaces/interface"
-"/ietf-interfaces:interfaces/interface/name" "/ietf-interfaces:interfaces/interface/description"
-"/ietf-interfaces:interfaces/interface/type" "/ietf-interfaces:interfaces/interface/enabled"
-"/ietf-interfaces:interfaces/interface/link-up-down-trap-enable"
-"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv6"}
-
-expect_hint "" "print -f info -P /ietf-interfaces:interfaces/interface" $hints
-
-expect_completion "/i" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv"
-
-expect_completion "4" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4"
-
-set hints { "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled"
-"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/forwarding" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/mtu"
-"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/neighbor"
-}
-
-expect_hint "\t" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv" $hints
-
-expect_completion "/e" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled "
-
-send -- "\r"
-
-expect {
- -re ".*\r\n> " {}
- timeout {exit 1}
-}
-
-send -- "exit\r"
-
-expect eof
diff --git a/tools/lint/tests/expect/feature.exp b/tools/lint/tests/expect/feature.exp
deleted file mode 100755
index 37680b0..0000000
--- a/tools/lint/tests/expect/feature.exp
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/expect -f
-
-if { [info exists ::env(CURRENT_SOURCE_DIR)] } {
- source "$env(CURRENT_SOURCE_DIR)/tests/expect/common.exp"
- set yang_dir "$env(CURRENT_SOURCE_DIR)/examples"
-} else {
- source "common.exp"
- set yang_dir "../../examples"
-}
-
-spawn $yanglint
-
-# skip no dir and/or no history warnings
-expect_output "(YANGLINT.*)*> "
-
-expect_command "feature -a" 1 "yang:\r\n\t\\(none\\)\r\n\r\nietf-yang-schema-mount:\r\n\t\\(none\\)\r\n\r\n> "
-
-send -- "exit\r"
-
-expect eof
diff --git a/tools/lint/tests/expect/list.exp b/tools/lint/tests/expect/list.exp
deleted file mode 100755
index ec3cdba..0000000
--- a/tools/lint/tests/expect/list.exp
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/expect -f
-
-if { [info exists ::env(CURRENT_SOURCE_DIR)] } {
- source "$env(CURRENT_SOURCE_DIR)/tests/expect/common.exp"
- set yang_dir "$env(CURRENT_SOURCE_DIR)/examples"
-} else {
- source "common.exp"
- set yang_dir "../../examples"
-}
-
-spawn $yanglint
-
-# skip no dir and/or no history warnings
-expect_output "(YANGLINT.*)*> "
-
-expect_command "list" 1 "List of the loaded models:\r\n *i ietf-yang-metadata@2016-08-05\r\n *I yang@2022-06-16\r\n *i ietf-inet-types@2013-07-15\r\n *i ietf-yang-types@2013-07-15\r\n *I ietf-yang-schema-mount@2019-01-14\r\n *i ietf-yang-structure-ext@2020-06-17\r\n> "
-
-send -- "exit\r"
-
-expect eof
diff --git a/tools/lint/tests/shunit2/feature.sh b/tools/lint/tests/shunit2/feature.sh
deleted file mode 100755
index fb2ee88..0000000
--- a/tools/lint/tests/shunit2/feature.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-
-testFeature() {
- models=( "iana-if-type@2014-05-08.yang" "ietf-netconf@2011-06-01.yang" "ietf-netconf-with-defaults@2011-06-01.yang"
- "sm.yang" "ietf-interfaces@2014-05-08.yang" "ietf-netconf-acm@2018-02-14.yang" "ietf-origin@2018-02-14.yang"
- "ietf-ip@2014-06-16.yang" "ietf-restconf@2017-01-26.yang" )
- features=( " -F iana-if-type:"
- " -F ietf-netconf:writable-running,candidate,confirmed-commit,rollback-on-error,validate,startup,url,xpath"
- " -F ietf-netconf-with-defaults:" " -F sm:" " -F ietf-interfaces:arbitrary-names,pre-provisioning,if-mib"
- " -F ietf-netconf-acm:" " -F ietf-origin:" " -F ietf-ip:ipv4-non-contiguous-netmasks,ipv6-privacy-autoconf"
- " -F ietf-restconf:" )
-
- for i in ${!models[@]}; do
- output=`${YANGLINT} -f feature-param ${YANG_MODULES_DIR}/${models[$i]}`
- assertEquals "Unexpected features of module ${models[$i]}." "${features[$i]}" "${output}"
- done
-}
-
-. shunit2
diff --git a/tools/lint/tests/shunit2/list.sh b/tools/lint/tests/shunit2/list.sh
deleted file mode 100755
index d64503a..0000000
--- a/tools/lint/tests/shunit2/list.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-
-LIST_BASE="List of the loaded models:
- i ietf-yang-metadata@2016-08-05
- I yang@2022-06-16
- i ietf-inet-types@2013-07-15
- i ietf-yang-types@2013-07-15
- I ietf-yang-schema-mount@2019-01-14
- i ietf-yang-structure-ext@2020-06-17"
-
-testListEmptyContext() {
- output=`${YANGLINT} -l`
- assertEquals "Unexpected list of modules in empty context." "${LIST_BASE}" "${output}"
-}
-
-testListAllImplemented() {
- LIST_BASE_ALLIMPLEMENTED="List of the loaded models:
- I ietf-yang-metadata@2016-08-05
- I yang@2022-06-16
- I ietf-inet-types@2013-07-15
- I ietf-yang-types@2013-07-15
- I ietf-yang-schema-mount@2019-01-14
- I ietf-yang-structure-ext@2020-06-17"
- output=`${YANGLINT} -lii`
- assertEquals "Unexpected list of modules in empty context with -ii." "${LIST_BASE_ALLIMPLEMENTED}" "${output}"
-}
-
-. shunit2
diff --git a/tools/lint/yl_opt.c b/tools/lint/yl_opt.c
new file mode 100644
index 0000000..7cd855f
--- /dev/null
+++ b/tools/lint/yl_opt.c
@@ -0,0 +1,344 @@
+/**
+ * @file yl_opt.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief Settings options for the libyang context.
+ *
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <strings.h>
+
+#include "in.h" /* ly_in_free */
+
+#include "common.h"
+#include "yl_opt.h"
+#include "yl_schema_features.h"
+
+struct cmdline_file *
+fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format)
+{
+ struct cmdline_file *rec;
+
+ rec = malloc(sizeof *rec);
+ if (!rec) {
+ YLMSG_E("Allocating memory for data file information failed.");
+ return NULL;
+ }
+ rec->in = in;
+ rec->path = path;
+ rec->format = format;
+
+ if (set && ly_set_add(set, rec, 1, NULL)) {
+ free(rec);
+ YLMSG_E("Storing data file information failed.");
+ return NULL;
+ }
+
+ return rec;
+}
+
+void
+free_cmdline_file_items(struct cmdline_file *rec)
+{
+ if (rec && rec->in) {
+ ly_in_free(rec->in, 1);
+ }
+}
+
+void
+free_cmdline_file(void *cmdline_file)
+{
+ struct cmdline_file *rec = (struct cmdline_file *)cmdline_file;
+
+ if (rec) {
+ free_cmdline_file_items(rec);
+ free(rec);
+ }
+}
+
+void
+yl_opt_erase(struct yl_opt *yo)
+{
+ ly_bool interactive;
+
+ interactive = yo->interactive;
+
+ /* data */
+ ly_set_erase(&yo->data_inputs, free_cmdline_file);
+ ly_in_free(yo->data_operational.in, 1);
+ ly_set_erase(&yo->data_xpath, NULL);
+
+ /* schema */
+ ly_set_erase(&yo->schema_features, yl_schema_features_free);
+ ly_set_erase(&yo->schema_modules, NULL);
+
+ /* context */
+ free(yo->searchpaths);
+
+ /* --reply-rpc */
+ ly_in_free(yo->reply_rpc.in, 1);
+
+ ly_out_free(yo->out, NULL, yo->out_stdout ? 0 : 1);
+
+ free_cmdline(yo->argv);
+
+ *yo = (const struct yl_opt) {
+ 0
+ };
+ yo->interactive = interactive;
+}
+
+int
+yl_opt_update_schema_out_format(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "yang")) {
+ yo->schema_out_format = LYS_OUT_YANG;
+ yo->data_out_format = 0;
+ } else if (!strcasecmp(arg, "yin")) {
+ yo->schema_out_format = LYS_OUT_YIN;
+ yo->data_out_format = 0;
+ } else if (!strcasecmp(arg, "info")) {
+ yo->schema_out_format = LYS_OUT_YANG_COMPILED;
+ yo->data_out_format = 0;
+ } else if (!strcasecmp(arg, "tree")) {
+ yo->schema_out_format = LYS_OUT_TREE;
+ yo->data_out_format = 0;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+yl_opt_update_data_out_format(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "xml")) {
+ yo->schema_out_format = 0;
+ yo->data_out_format = LYD_XML;
+ } else if (!strcasecmp(arg, "json")) {
+ yo->schema_out_format = 0;
+ yo->data_out_format = LYD_JSON;
+ } else if (!strcasecmp(arg, "lyb")) {
+ yo->schema_out_format = 0;
+ yo->data_out_format = LYD_LYB;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+yl_opt_update_other_out_format(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "feature-param")) {
+ yo->feature_param_format = 1;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+yl_opt_update_out_format(const char *arg, struct yl_opt *yo)
+{
+ if (!yl_opt_update_schema_out_format(arg, yo)) {
+ return 0;
+ }
+ if (!yl_opt_update_data_out_format(arg, yo)) {
+ return 0;
+ }
+ if (!yl_opt_update_other_out_format(arg, yo)) {
+ return 0;
+ }
+
+ YLMSG_E("Unknown output format %s.", arg);
+ return 1;
+}
+
+int
+yl_opt_update_data_type(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "config")) {
+ yo->data_parse_options |= LYD_PARSE_NO_STATE;
+ yo->data_validate_options |= LYD_VALIDATE_NO_STATE;
+ } else if (!strcasecmp(arg, "get")) {
+ yo->data_parse_options |= LYD_PARSE_ONLY;
+ } else if (!strcasecmp(arg, "getconfig") || !strcasecmp(arg, "get-config") || !strcasecmp(arg, "edit")) {
+ yo->data_parse_options |= LYD_PARSE_ONLY | LYD_PARSE_NO_STATE;
+ } else if (!strcasecmp(arg, "rpc") || !strcasecmp(arg, "action")) {
+ yo->data_type = LYD_TYPE_RPC_YANG;
+ } else if (!strcasecmp(arg, "nc-rpc")) {
+ yo->data_type = LYD_TYPE_RPC_NETCONF;
+ } else if (!strcasecmp(arg, "reply") || !strcasecmp(arg, "rpcreply")) {
+ yo->data_type = LYD_TYPE_REPLY_YANG;
+ } else if (!strcasecmp(arg, "nc-reply")) {
+ yo->data_type = LYD_TYPE_REPLY_NETCONF;
+ } else if (!strcasecmp(arg, "notif") || !strcasecmp(arg, "notification")) {
+ yo->data_type = LYD_TYPE_NOTIF_YANG;
+ } else if (!strcasecmp(arg, "nc-notif")) {
+ yo->data_type = LYD_TYPE_NOTIF_NETCONF;
+ } else if (!strcasecmp(arg, "data")) {
+ /* default option */
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+yo_opt_update_data_default(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "all")) {
+ yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
+ } else if (!strcasecmp(arg, "all-tagged")) {
+ yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
+ } else if (!strcasecmp(arg, "trim")) {
+ yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
+ } else if (!strcasecmp(arg, "implicit-tagged")) {
+ yo->data_print_options = (yo->data_print_options & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+yo_opt_update_data_in_format(const char *arg, struct yl_opt *yo)
+{
+ if (!strcasecmp(arg, "xml")) {
+ yo->data_in_format = LYD_XML;
+ } else if (!strcasecmp(arg, "json")) {
+ yo->data_in_format = LYD_JSON;
+ } else if (!strcasecmp(arg, "lyb")) {
+ yo->data_in_format = LYD_LYB;
+ } else {
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+yo_opt_update_make_implemented(struct yl_opt *yo)
+{
+ if (yo->ctx_options & LY_CTX_REF_IMPLEMENTED) {
+ yo->ctx_options &= ~LY_CTX_REF_IMPLEMENTED;
+ yo->ctx_options |= LY_CTX_ALL_IMPLEMENTED;
+ } else {
+ yo->ctx_options |= LY_CTX_REF_IMPLEMENTED;
+ }
+}
+
+void
+yo_opt_update_disable_searchdir(struct yl_opt *yo)
+{
+ if (yo->ctx_options & LY_CTX_DISABLE_SEARCHDIR_CWD) {
+ yo->ctx_options &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
+ yo->ctx_options |= LY_CTX_DISABLE_SEARCHDIRS;
+ } else {
+ yo->ctx_options |= LY_CTX_DISABLE_SEARCHDIR_CWD;
+ }
+}
+
+void
+free_cmdline(char *argv[])
+{
+ if (argv) {
+ free(argv[0]);
+ free(argv);
+ }
+}
+
+int
+parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[])
+{
+ int count;
+ char **vector;
+ char *ptr;
+ char qmark = 0;
+
+ assert(cmdline);
+ assert(argc_p);
+ assert(argv_p);
+
+ /* init */
+ optind = 0; /* reinitialize getopt() */
+ count = 1;
+ vector = malloc((count + 1) * sizeof *vector);
+ vector[0] = strdup(cmdline);
+
+ /* command name */
+ strtok(vector[0], " ");
+
+ /* arguments */
+ while ((ptr = strtok(NULL, " "))) {
+ size_t len;
+ void *r;
+
+ len = strlen(ptr);
+
+ if (qmark) {
+ /* still in quotated text */
+ /* remove NULL termination of the previous token since it is not a token,
+ * but a part of the quotation string */
+ ptr[-1] = ' ';
+
+ if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
+ /* end of quotation */
+ qmark = 0;
+ /* shorten the argument by the terminating quotation mark */
+ ptr[len - 1] = '\0';
+ }
+ continue;
+ }
+
+ /* another token in cmdline */
+ ++count;
+ r = realloc(vector, (count + 1) * sizeof *vector);
+ if (!r) {
+ YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
+ free(vector);
+ return -1;
+ }
+ vector = r;
+ vector[count - 1] = ptr;
+
+ if ((ptr[0] == '"') || (ptr[0] == '\'')) {
+ /* remember the quotation mark to identify end of quotation */
+ qmark = ptr[0];
+
+ /* move the remembered argument after the quotation mark */
+ ++vector[count - 1];
+
+ /* check if the quotation is terminated within this token */
+ if ((ptr[len - 1] == qmark) && (ptr[len - 2] != '\\')) {
+ /* end of quotation */
+ qmark = 0;
+ /* shorten the argument by the terminating quotation mark */
+ ptr[len - 1] = '\0';
+ }
+ }
+ }
+ vector[count] = NULL;
+
+ *argc_p = count;
+ *argv_p = vector;
+
+ return 0;
+}
diff --git a/tools/lint/yl_opt.h b/tools/lint/yl_opt.h
new file mode 100644
index 0000000..d66ae4d
--- /dev/null
+++ b/tools/lint/yl_opt.h
@@ -0,0 +1,237 @@
+/**
+ * @file yl_opt.h
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief Settings options for the libyang context.
+ *
+ * Copyright (c) 2020 - 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef YL_OPT_H_
+#define YL_OPT_H_
+
+#include "parser_data.h" /* enum lyd_type */
+#include "printer_schema.h" /* LYS_OUTFORMAT */
+#include "set.h" /* ly_set */
+
+/**
+ * @brief Data connected with a file provided on a command line as a file path.
+ */
+struct cmdline_file {
+ struct ly_in *in;
+ const char *path;
+ LYD_FORMAT format;
+};
+
+/**
+ * @brief Create and fill the command line file data (struct cmdline_file *).
+ * @param[in] set Optional parameter in case the record is supposed to be added into a set.
+ * @param[in] in Input file handler.
+ * @param[in] path Filepath of the file.
+ * @param[in] format Format of the data file.
+ * @return The created command line file structure.
+ * @return NULL on failure
+ */
+struct cmdline_file *fill_cmdline_file(struct ly_set *set, struct ly_in *in, const char *path, LYD_FORMAT format);
+
+/**
+ * @brief Free the command line file data items.
+ * @param[in,out] rec record to free.
+ */
+void free_cmdline_file_items(struct cmdline_file *rec);
+
+/**
+ * @brief Free the command line file data (struct cmdline_file *).
+ * @param[in,out] cmdline_file The (struct cmdline_file *) to free.
+ */
+void free_cmdline_file(void *cmdline_file);
+
+/**
+ * @brief Context structure to hold and pass variables in a structured form.
+ */
+struct yl_opt {
+ /* Set to 1 if yanglint running in the interactive mode */
+ ly_bool interactive;
+ ly_bool last_one;
+
+ /* libyang context for the run */
+ char *yang_lib_file;
+ uint16_t ctx_options;
+
+ /* prepared output (--output option or stdout by default) */
+ ly_bool out_stdout;
+ struct ly_out *out;
+
+ char *searchpaths;
+ ly_bool searchdir_unset;
+
+ /* options flags */
+ uint8_t list; /* -l option to print list of schemas */
+
+ /* line length for 'tree' format */
+ size_t line_length; /* --tree-line-length */
+
+ uint32_t dbg_groups;
+
+ /*
+ * schema
+ */
+ /* set schema modules' features via --features option (struct schema_features *) */
+ struct ly_set schema_features;
+
+ /* set of loaded schema modules (struct lys_module *) */
+ struct ly_set schema_modules;
+
+ /* options to parse and print schema modules */
+ uint32_t schema_parse_options;
+ uint32_t schema_print_options;
+
+ /* specification of printing schema node subtree, option --schema-node */
+ char *schema_node_path;
+ char *submodule;
+
+ /* name of file containing explicit context passed to callback
+ * for schema-mount extension. This also causes a callback to
+ * be registered.
+ */
+ char *schema_context_filename;
+ ly_bool extdata_unset;
+
+ /* value of --format in case of schema format */
+ LYS_OUTFORMAT schema_out_format;
+ ly_bool feature_param_format;
+ ly_bool feature_print_all;
+
+ /*
+ * data
+ */
+ /* various options based on --type option */
+ enum lyd_type data_type;
+ uint32_t data_parse_options;
+ uint32_t data_validate_options;
+ uint32_t data_print_options;
+
+ /* flag for --merge option */
+ uint8_t data_merge;
+
+ /* value of --format in case of data format */
+ LYD_FORMAT data_out_format;
+
+ /* value of --in-format in case of data format */
+ LYD_FORMAT data_in_format;
+
+ /* input data files (struct cmdline_file *) */
+ struct ly_set data_inputs;
+
+ /* storage for --operational */
+ struct cmdline_file data_operational;
+
+ /* storage for --reply-rpc */
+ struct cmdline_file reply_rpc;
+
+ /* storage for --data-xpath */
+ struct ly_set data_xpath;
+
+ char **argv;
+};
+
+/**
+ * @brief Erase all values in @p opt.
+ *
+ * The yl_opt.interactive item is not deleted.
+ *
+ * @param[in,out] yo Option context to erase.
+ */
+void yl_opt_erase(struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the schema --format parameter.
+ *
+ * @param[in] arg Format parameter argument (for example yang, yin, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yl_opt_update_schema_out_format(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the data --format parameter.
+ *
+ * @param[in] arg Format parameter argument (for example xml, json, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yl_opt_update_data_out_format(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the general --format parameter.
+ *
+ * @param[in] arg Format parameter argument (for example yang, xml, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yl_opt_update_out_format(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the data --type parameter.
+ *
+ * @param[in] arg Format parameter argument (for example config, rpc, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yl_opt_update_data_type(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the data --default parameter.
+ *
+ * @param[in] arg Format parameter argument (for example all, trim, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yo_opt_update_data_default(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the @p arg of the data --in-format parameter.
+ *
+ * @param[in] arg Format parameter argument (for example xml, json, ...).
+ * @param[out] yo yanglint options used to update.
+ * @return 0 on success.
+ */
+int yo_opt_update_data_in_format(const char *arg, struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the --make-implemented parameter.
+ *
+ * @param[in,out] yo yanglint options used to update.
+ */
+void yo_opt_update_make_implemented(struct yl_opt *yo);
+
+/**
+ * @brief Update @p yo according to the --disable-searchdir parameter.
+ *
+ * @param[in,out] yo yanglint options used to update.
+ */
+void yo_opt_update_disable_searchdir(struct yl_opt *yo);
+
+/**
+ * @brief Helper function to prepare argc, argv pair from a command line string.
+ *
+ * @param[in] cmdline Complete command line string.
+ * @param[out] argc_p Pointer to store argc value.
+ * @param[out] argv_p Pointer to store argv vector.
+ * @return 0 on success, non-zero on failure.
+ */
+int parse_cmdline(const char *cmdline, int *argc_p, char **argv_p[]);
+
+/**
+ * @brief Destructor for the argument vector prepared by ::parse_cmdline().
+ *
+ * @param[in,out] argv Argument vector to destroy.
+ */
+void free_cmdline(char *argv[]);
+
+#endif /* YL_OPT_H_ */
diff --git a/tools/lint/yl_schema_features.c b/tools/lint/yl_schema_features.c
new file mode 100644
index 0000000..74f88b9
--- /dev/null
+++ b/tools/lint/yl_schema_features.c
@@ -0,0 +1,212 @@
+/**
+ * @file yl_schema_features.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief Control features for the schema.
+ *
+ * Copyright (c) 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <stdlib.h> /* calloc */
+#include <string.h> /* strcmp */
+
+#include "compat.h" /* strndup */
+#include "set.h" /* ly_set */
+
+#include "common.h"
+#include "yl_schema_features.h"
+
+void
+yl_schema_features_free(void *flist)
+{
+ struct yl_schema_features *rec = (struct yl_schema_features *)flist;
+
+ if (rec) {
+ free(rec->mod_name);
+ if (rec->features) {
+ for (uint32_t u = 0; rec->features[u]; ++u) {
+ free(rec->features[u]);
+ }
+ free(rec->features);
+ }
+ free(rec);
+ }
+}
+
+void
+get_features(const struct ly_set *fset, const char *module, const char ***features)
+{
+ /* get features list for this module */
+ for (uint32_t u = 0; u < fset->count; ++u) {
+ struct yl_schema_features *sf = (struct yl_schema_features *)fset->objs[u];
+
+ if (!strcmp(module, sf->mod_name)) {
+ /* matched module - explicitly set features */
+ *features = (const char **)sf->features;
+ sf->applied = 1;
+ return;
+ }
+ }
+
+ /* features not set so disable all */
+ *features = NULL;
+}
+
+int
+parse_features(const char *fstring, struct ly_set *fset)
+{
+ struct yl_schema_features *rec = NULL;
+ uint32_t count;
+ char *p, **fp;
+
+ rec = calloc(1, sizeof *rec);
+ if (!rec) {
+ YLMSG_E("Unable to allocate features information record (%s).", strerror(errno));
+ goto error;
+ }
+
+ /* fill the record */
+ p = strchr(fstring, ':');
+ if (!p) {
+ YLMSG_E("Invalid format of the features specification (%s).", fstring);
+ goto error;
+ }
+ rec->mod_name = strndup(fstring, p - fstring);
+
+ count = 0;
+ while (p) {
+ size_t len = 0;
+ char *token = p + 1;
+
+ p = strchr(token, ',');
+ if (!p) {
+ /* the last item, if any */
+ len = strlen(token);
+ } else {
+ len = p - token;
+ }
+
+ if (len) {
+ fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
+ if (!fp) {
+ YLMSG_E("Unable to store features list information (%s).", strerror(errno));
+ goto error;
+ }
+ rec->features = fp;
+ fp = &rec->features[count++]; /* array item to set */
+ (*fp) = strndup(token, len);
+ }
+ }
+
+ /* terminating NULL */
+ fp = realloc(rec->features, (count + 1) * sizeof *rec->features);
+ if (!fp) {
+ YLMSG_E("Unable to store features list information (%s).", strerror(errno));
+ goto error;
+ }
+ rec->features = fp;
+ rec->features[count++] = NULL;
+
+ /* Store record to the output set. */
+ if (ly_set_add(fset, rec, 1, NULL)) {
+ YLMSG_E("Unable to store features information (%s).", strerror(errno));
+ goto error;
+ }
+ rec = NULL;
+
+ return 0;
+
+error:
+ yl_schema_features_free(rec);
+ return -1;
+}
+
+void
+print_features(struct ly_out *out, const struct lys_module *mod)
+{
+ struct lysp_feature *f;
+ uint32_t idx;
+ size_t max_len, len;
+
+ ly_print(out, "%s:\n", mod->name);
+
+ /* get max len, so the statuses of all the features will be aligned */
+ max_len = 0, idx = 0, f = NULL;
+ while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
+ len = strlen(f->name);
+ max_len = (max_len > len) ? max_len : len;
+ }
+ if (!max_len) {
+ ly_print(out, "\t(none)\n");
+ return;
+ }
+
+ /* print features */
+ idx = 0, f = NULL;
+ while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
+ ly_print(out, "\t%-*s (%s)\n", (int)max_len, f->name, lys_feature_value(mod, f->name) ? "off" : "on");
+ }
+}
+
+void
+print_feature_param(struct ly_out *out, const struct lys_module *mod)
+{
+ struct lysp_feature *f = NULL;
+ uint32_t idx = 0;
+ uint8_t first = 1;
+
+ ly_print(out, " -F %s:", mod->name);
+ while ((f = lysp_feature_next(f, mod->parsed, &idx))) {
+ if (first) {
+ ly_print(out, "%s", f->name);
+ first = 0;
+ } else {
+ ly_print(out, ",%s", f->name);
+ }
+ }
+}
+
+void
+print_all_features(struct ly_out *out, const struct ly_ctx *ctx, uint8_t feature_param)
+{
+ uint32_t i;
+ struct lys_module *mod;
+ uint8_t first;
+
+ /* Print features for all implemented modules. */
+ first = 1;
+ i = 0;
+ while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
+ if (!mod->implemented) {
+ continue;
+ }
+ if (first) {
+ print_features(out, mod);
+ first = 0;
+ } else {
+ ly_print(out, "\n");
+ print_features(out, mod);
+ }
+ }
+
+ if (!feature_param) {
+ return;
+ }
+ ly_print(out, "\n");
+
+ /* Print features for all implemented modules in 'feature-param' format. */
+ i = 0;
+ while ((mod = ly_ctx_get_module_iter(ctx, &i)) != NULL) {
+ if (mod->implemented) {
+ print_feature_param(out, mod);
+ }
+ }
+}
diff --git a/tools/lint/yl_schema_features.h b/tools/lint/yl_schema_features.h
new file mode 100644
index 0000000..7bfe9fd
--- /dev/null
+++ b/tools/lint/yl_schema_features.h
@@ -0,0 +1,84 @@
+/**
+ * @file yl_schema_features.h
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief Control features for the schema.
+ *
+ * Copyright (c) 2023 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef YL_SCHEMA_FEATURES_H_
+#define YL_SCHEMA_FEATURES_H_
+
+#include <stdint.h>
+
+struct ly_set;
+struct lys_module;
+struct ly_out;
+struct ly_ctx;
+
+/**
+ * @brief Storage for the list of the features (their names) in a specific YANG module.
+ */
+struct yl_schema_features {
+ char *mod_name;
+ char **features;
+ uint8_t applied;
+};
+
+/**
+ * @brief Free the schema features list (struct schema_features *)
+ * @param[in,out] flist The (struct schema_features *) to free.
+ */
+void yl_schema_features_free(void *flist);
+
+/**
+ * @brief Get the list of features connected with the specific YANG module.
+ *
+ * @param[in] fset The set of features information (struct schema_features *).
+ * @param[in] module Name of the YANG module which features should be found.
+ * @param[out] features Pointer to the list of features being returned.
+ */
+void get_features(const struct ly_set *fset, const char *module, const char ***features);
+
+/**
+ * @brief Parse features being specified for the specific YANG module.
+ *
+ * Format of the input @p fstring is as follows: "<module_name>:[<feature>,]*"
+ *
+ * @param[in] fstring Input string to be parsed.
+ * @param[in, out] fset Features information set (of struct schema_features *). The set is being filled.
+ */
+int parse_features(const char *fstring, struct ly_set *fset);
+
+/**
+ * @brief Print all features of a single module.
+ *
+ * @param[in] out The output handler for printing.
+ * @param[in] mod Module which can contains the features.
+ */
+void print_features(struct ly_out *out, const struct lys_module *mod);
+
+/**
+ * @brief Print all features in the 'feature-param' format.
+ *
+ * @param[in] out The output handler for printing.
+ * @param[in] mod Module which can contains the features.
+ */
+void print_feature_param(struct ly_out *out, const struct lys_module *mod);
+
+/**
+ * @brief Print all features of all implemented modules.
+ *
+ * @param[in] out The output handler for printing.
+ * @param[in] ctx Libyang context.
+ * @param[in] feature_param Flag expressing whether to print features parameter.
+ */
+void print_all_features(struct ly_out *out, const struct ly_ctx *ctx, uint8_t feature_param);
+
+#endif /* YL_SCHEMA_FEATURES_H_ */
diff --git a/tools/re/main.c b/tools/re/main.c
index 2292b2a..5e33536 100644
--- a/tools/re/main.c
+++ b/tools/re/main.c
@@ -1,6 +1,7 @@
/**
* @file main.c
* @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Adam Piecek <piecek@cesnet.cz>
* @brief libyang's YANG Regular Expression tool
*
* Copyright (c) 2017 CESNET, z.s.p.o.
@@ -26,6 +27,11 @@
#include "compat.h"
#include "tools/config.h"
+struct yr_pattern {
+ char *expr;
+ ly_bool invert;
+};
+
void
help(void)
{
@@ -34,7 +40,9 @@ help(void)
fprintf(stdout, " yangre [-hv]\n");
fprintf(stdout, " yangre [-V] -p <regexp1> [-i] [-p <regexp2> [-i] ...] <string>\n");
fprintf(stdout, " yangre [-V] -f <file>\n");
- fprintf(stdout, "Returns 0 if string matches the pattern(s), 1 if not and -1 on error.\n\n");
+ fprintf(stdout, "Returns 0 if string matches the pattern(s) or if otherwise successful.\n");
+ fprintf(stdout, "Returns 1 on error.\n");
+ fprintf(stdout, "Returns 2 if string does not match the pattern(s).\n\n");
fprintf(stdout, "Options:\n"
" -h, --help Show this help message and exit.\n"
" -v, --version Show version number and exit.\n"
@@ -74,43 +82,248 @@ pattern_error(LY_LOG_LEVEL level, const char *msg, const char *path)
}
}
-static const char *module_start = "module yangre {"
- "yang-version 1.1;"
- "namespace urn:cesnet:libyang:yangre;"
- "prefix re;"
- "leaf pattern {"
- " type string {";
-static const char *module_invertmatch = " { modifier invert-match; }";
-static const char *module_match = ";";
-static const char *module_end = "}}}";
-
static int
-add_pattern(char ***patterns, int **inverts, int *counter, char *pattern)
+add_pattern(struct yr_pattern **patterns, int *counter, char *pattern)
{
- void *reallocated1, *reallocated2;
+ void *reallocated;
+ int orig_counter;
+ /* Store the original number of items. */
+ orig_counter = *counter;
+
+ /* Reallocate 'patterns' memory with additional space. */
+ reallocated = realloc(*patterns, (orig_counter + 1) * sizeof **patterns);
+ if (!reallocated) {
+ goto error;
+ }
+ (*patterns) = reallocated;
+ /* Allocated memory is now larger. */
(*counter)++;
- reallocated1 = realloc(*patterns, *counter * sizeof **patterns);
- reallocated2 = realloc(*inverts, *counter * sizeof **inverts);
- if (!reallocated1 || !reallocated2) {
- fprintf(stderr, "yangre error: memory allocation error.\n");
- free(reallocated1);
- free(reallocated2);
- return EXIT_FAILURE;
+ /* Copy the pattern and store it to the additonal space. */
+ (*patterns)[orig_counter].expr = strdup(pattern);
+ if (!(*patterns)[orig_counter].expr) {
+ goto error;
+ }
+ (*patterns)[orig_counter].invert = 0;
+
+ return 0;
+
+error:
+ fprintf(stderr, "yangre error: memory allocation error.\n");
+ return 1;
+}
+
+static int
+create_empty_string(char **str)
+{
+ free(*str);
+ *str = malloc(sizeof(char));
+ if (!(*str)) {
+ fprintf(stderr, "yangre error: memory allocation failed.\n");
+ return 1;
+ }
+ (*str)[0] = '\0';
+
+ return 0;
+}
+
+static ly_bool
+file_is_empty(FILE *fp)
+{
+ int c;
+
+ c = fgetc(fp);
+ if (c == EOF) {
+ return 1;
+ } else {
+ ungetc(c, fp);
+ return 0;
+ }
+}
+
+/**
+ * @brief Open the @p filepath, parse patterns and given string-argument.
+ *
+ * @param[in] filepath File to parse. Contains patterns and string.
+ * @param[out] infile The file descriptor of @p filepath.
+ * @param[out] patterns Storage of patterns.
+ * @param[out] patterns_count Number of items in @p patterns.
+ * @param[out] strarg The string-argument to check.
+ * @return 0 on success.
+ */
+static int
+parse_patterns_file(const char *filepath, FILE **infile, struct yr_pattern **patterns, int *patterns_count, char **strarg)
+{
+ int blankline = 0;
+ char *str = NULL;
+ size_t len = 0;
+ ssize_t l;
+
+ *infile = fopen(filepath, "rb");
+ if (!(*infile)) {
+ fprintf(stderr, "yangre error: unable to open input file %s (%s).\n", optarg, strerror(errno));
+ goto error;
+ }
+ if (file_is_empty(*infile)) {
+ if (create_empty_string(strarg)) {
+ goto error;
+ }
+ return 0;
+ }
+
+ while ((l = getline(&str, &len, *infile)) != -1) {
+ if (!blankline && ((str[0] == '\n') || ((str[0] == '\r') && (str[1] == '\n')))) {
+ /* blank line */
+ blankline = 1;
+ continue;
+ }
+ if ((str[0] != '\n') && (str[0] != '\r') && (str[l - 1] == '\n')) {
+ /* remove ending newline */
+ if ((l > 1) && (str[l - 2] == '\r') && (str[l - 1] == '\n')) {
+ str[l - 2] = '\0';
+ } else {
+ str[l - 1] = '\0';
+ }
+ }
+ if (blankline) {
+ /* done - str is now the string to check */
+ blankline = 0;
+ *strarg = str;
+ break;
+ /* else read the patterns */
+ } else if (add_pattern(patterns, patterns_count, (str[0] == ' ') ? &str[1] : str)) {
+ goto error;
+ }
+ if (str[0] == ' ') {
+ /* set invert-match */
+ (*patterns)[*patterns_count - 1].invert = 1;
+ }
+ }
+ if (!str || (blankline && (str[0] != '\0'))) {
+ /* corner case, no input after blankline meaning the pattern to check is empty */
+ if (create_empty_string(&str)) {
+ goto error;
+ }
+ }
+ *strarg = str;
+
+ return 0;
+
+error:
+ free(str);
+ if (*infile) {
+ fclose(*infile);
+ *infile = NULL;
+ }
+ *strarg = NULL;
+
+ return 1;
+}
+
+static char *
+modstr_init(void)
+{
+ const char *module_start = "module yangre {"
+ "yang-version 1.1;"
+ "namespace urn:cesnet:libyang:yangre;"
+ "prefix re;"
+ "leaf pattern {"
+ " type string {";
+
+ return strdup(module_start);
+}
+
+static char *
+modstr_add_pattern(char **modstr, const struct yr_pattern *pattern)
+{
+ char *new;
+ const char *module_invertmatch = " { modifier invert-match; }";
+ const char *module_match = ";";
+
+ if (asprintf(&new, "%s pattern %s%s", *modstr, pattern->expr,
+ pattern->invert ? module_invertmatch : module_match) == -1) {
+ fprintf(stderr, "yangre error: memory allocation failed.\n");
+ return NULL;
+ }
+ free(*modstr);
+ *modstr = NULL;
+
+ return new;
+}
+
+static char *
+modstr_add_ending(char **modstr)
+{
+ char *new;
+ static const char *module_end = "}}}";
+
+ if (asprintf(&new, "%s%s", *modstr, module_end) == -1) {
+ fprintf(stderr, "yangre error: memory allocation failed.\n");
+ return NULL;
}
- (*patterns) = reallocated1;
- (*patterns)[*counter - 1] = strdup(pattern);
- (*inverts) = reallocated2;
- (*inverts)[*counter - 1] = 0;
+ free(*modstr);
+ *modstr = NULL;
+
+ return new;
+}
+
+static int
+create_module(struct yr_pattern *patterns, int patterns_count, char **mod)
+{
+ int i;
+ char *new = NULL, *modstr;
+
+ if (!(modstr = modstr_init())) {
+ goto error;
+ }
+
+ for (i = 0; i < patterns_count; i++) {
+ if (!(new = modstr_add_pattern(&modstr, &patterns[i]))) {
+ goto error;
+ }
+ modstr = new;
+ }
+
+ if (!(new = modstr_add_ending(&modstr))) {
+ goto error;
+ }
+
+ *mod = new;
+
+ return 0;
+
+error:
+ *mod = NULL;
+ free(new);
+ free(modstr);
- return EXIT_SUCCESS;
+ return 1;
+}
+
+static void
+print_verbose(struct ly_ctx *ctx, struct yr_pattern *patterns, int patterns_count, char *str, LY_ERR match)
+{
+ int i;
+
+ for (i = 0; i < patterns_count; i++) {
+ fprintf(stdout, "pattern %d: %s\n", i + 1, patterns[i].expr);
+ fprintf(stdout, "matching %d: %s\n", i + 1, patterns[i].invert ? "inverted" : "regular");
+ }
+ fprintf(stdout, "string : %s\n", str);
+ if (match == LY_SUCCESS) {
+ fprintf(stdout, "result : matching\n");
+ } else if (match == LY_EVALID) {
+ fprintf(stdout, "result : not matching\n");
+ } else {
+ fprintf(stdout, "result : error (%s)\n", ly_errmsg(ctx));
+ }
}
int
main(int argc, char *argv[])
{
LY_ERR match;
- int i, opt_index = 0, ret = -1, verbose = 0, blankline = 0;
+ int i, opt_index = 0, ret = 1, verbose = 0;
struct option options[] = {
{"help", no_argument, NULL, 'h'},
{"file", required_argument, NULL, 'f'},
@@ -120,21 +333,20 @@ main(int argc, char *argv[])
{"verbose", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
- char **patterns = NULL, *str = NULL, *modstr = NULL, *s;
- int *invert_match = NULL;
+ struct yr_pattern *patterns = NULL;
+ char *str = NULL, *modstr = NULL;
int patterns_count = 0;
struct ly_ctx *ctx = NULL;
struct lys_module *mod;
FILE *infile = NULL;
- size_t len = 0;
- ssize_t l;
+ ly_bool info_printed = 0;
opterr = 0;
while ((i = getopt_long(argc, argv, "hf:ivVp:", options, &opt_index)) != -1) {
switch (i) {
case 'h':
help();
- ret = -2; /* continue to allow printing version and help at once */
+ info_printed = 1;
break;
case 'f':
if (infile) {
@@ -146,52 +358,17 @@ main(int argc, char *argv[])
fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
goto cleanup;
}
- infile = fopen(optarg, "rb");
- if (!infile) {
- fprintf(stderr, "yangre error: unable to open input file %s (%s).\n", optarg, strerror(errno));
+ if (parse_patterns_file(optarg, &infile, &patterns, &patterns_count, &str)) {
goto cleanup;
}
-
- while ((l = getline(&str, &len, infile)) != -1) {
- if (!blankline && (str[0] == '\n')) {
- /* blank line */
- blankline = 1;
- continue;
- }
- if ((str[0] != '\n') && (str[l - 1] == '\n')) {
- /* remove ending newline */
- str[l - 1] = '\0';
- }
- if (blankline) {
- /* done - str is now the string to check */
- blankline = 0;
- break;
- /* else read the patterns */
- } else if (add_pattern(&patterns, &invert_match, &patterns_count,
- (str[0] == ' ') ? &str[1] : str)) {
- goto cleanup;
- }
- if (str[0] == ' ') {
- /* set invert-match */
- invert_match[patterns_count - 1] = 1;
- }
- }
- if (blankline) {
- /* corner case, no input after blankline meaning the pattern to check is empty */
- if (str != NULL) {
- free(str);
- }
- str = malloc(sizeof(char));
- str[0] = '\0';
- }
break;
case 'i':
- if (!patterns_count || invert_match[patterns_count - 1]) {
+ if (!patterns_count || patterns[patterns_count - 1].invert) {
help();
fprintf(stderr, "yangre error: invert-match option must follow some pattern.\n");
goto cleanup;
}
- invert_match[patterns_count - 1] = 1;
+ patterns[patterns_count - 1].invert = 1;
break;
case 'p':
if (infile) {
@@ -199,13 +376,13 @@ main(int argc, char *argv[])
fprintf(stderr, "yangre error: command line patterns cannot be mixed with file input.\n");
goto cleanup;
}
- if (add_pattern(&patterns, &invert_match, &patterns_count, optarg)) {
+ if (add_pattern(&patterns, &patterns_count, optarg)) {
goto cleanup;
}
break;
case 'v':
version();
- ret = -2; /* continue to allow printing version and help at once */
+ info_printed = 1;
break;
case 'V':
verbose = 1;
@@ -221,7 +398,8 @@ main(int argc, char *argv[])
}
}
- if (ret == -2) {
+ if (info_printed) {
+ ret = 0;
goto cleanup;
}
@@ -239,24 +417,9 @@ main(int argc, char *argv[])
str = argv[optind];
}
- for (modstr = (char *)module_start, i = 0; i < patterns_count; i++) {
- if (asprintf(&s, "%s pattern %s%s", modstr, patterns[i], invert_match[i] ? module_invertmatch : module_match) == -1) {
- fprintf(stderr, "yangre error: memory allocation failed.\n");
- goto cleanup;
- }
- if (modstr != module_start) {
- free(modstr);
- }
- modstr = s;
- }
- if (asprintf(&s, "%s%s", modstr, module_end) == -1) {
- fprintf(stderr, "yangre error: memory allocation failed.\n");
+ if (create_module(patterns, patterns_count, &modstr)) {
goto cleanup;
}
- if (modstr != module_start) {
- free(modstr);
- }
- modstr = s;
if (ly_ctx_new(NULL, 0, &ctx)) {
goto cleanup;
@@ -271,34 +434,24 @@ main(int argc, char *argv[])
match = lyd_value_validate(ctx, mod->compiled->data, str, strlen(str), NULL, NULL, NULL);
if (verbose) {
- for (i = 0; i < patterns_count; i++) {
- fprintf(stdout, "pattern %d: %s\n", i + 1, patterns[i]);
- fprintf(stdout, "matching %d: %s\n", i + 1, invert_match[i] ? "inverted" : "regular");
- }
- fprintf(stdout, "string : %s\n", str);
- if (match == LY_SUCCESS) {
- fprintf(stdout, "result : matching\n");
- } else if (match == LY_EVALID) {
- fprintf(stdout, "result : not matching\n");
- } else {
- fprintf(stdout, "result : error (%s)\n", ly_errmsg(ctx));
- }
+ print_verbose(ctx, patterns, patterns_count, str, match);
}
if (match == LY_SUCCESS) {
ret = 0;
} else if (match == LY_EVALID) {
- ret = 1;
+ ret = 2;
} else {
- ret = -1;
+ ret = 1;
}
cleanup:
ly_ctx_destroy(ctx);
for (i = 0; i < patterns_count; i++) {
- free(patterns[i]);
+ free(patterns[i].expr);
+ }
+ if (patterns_count) {
+ free(patterns);
}
- free(patterns);
- free(invert_match);
free(modstr);
if (infile) {
fclose(infile);
diff --git a/uncrustify.cfg b/uncrustify.cfg
index ec52e71..80d95e9 100644
--- a/uncrustify.cfg
+++ b/uncrustify.cfg
@@ -1,4 +1,4 @@
-# Uncrustify-0.76.0_f
+# Uncrustify-0.77.1_f
#
# General options
@@ -210,6 +210,11 @@ sp_before_ptr_star = force # ignore/add/remove/force
# variable name. If set to ignore, sp_before_ptr_star is used instead.
sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force
+# Add or remove space between a qualifier and a pointer star '*' that isn't
+# followed by a variable name, as in '(char const *)'. If set to ignore,
+# sp_before_ptr_star is used instead.
+sp_qualifier_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined
+
# Add or remove space between pointer stars '*'.
sp_between_ptr_star = remove # ignore/add/remove/force
@@ -250,10 +255,20 @@ sp_ptr_star_paren = remove # ignore/add/remove/force
# prototype or function definition.
sp_before_ptr_star_func = force # ignore/add/remove/force
+# Add or remove space between a qualifier and a pointer star '*' followed by
+# the name of the function in a function prototype or definition, as in
+# 'char const *foo()`. If set to ignore, sp_before_ptr_star is used instead.
+sp_qualifier_ptr_star_func = ignore # ignore/add/remove/force/not_defined
+
# Add or remove space before a pointer star '*' in the trailing return of a
# function prototype or function definition.
sp_before_ptr_star_trailing = ignore # ignore/add/remove/force/not_defined
+# Add or remove space between a qualifier and a pointer star '*' in the
+# trailing return of a function prototype or function definition, as in
+# 'auto foo() -> char const *'.
+sp_qualifier_ptr_star_trailing = force # ignore/add/remove/force/not_defined
+
# Add or remove space before a reference sign '&'.
sp_before_byref = ignore # ignore/add/remove/force
@@ -611,6 +626,16 @@ sp_inside_fparens = remove # ignore/add/remove/force
# Add or remove space inside function '(' and ')'.
sp_inside_fparen = remove # ignore/add/remove/force
+# Add or remove space inside user functor '(' and ')'.
+sp_func_call_user_inside_rparen = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space inside empty functor '()'.
+# Overrides sp_after_angle unless use_sp_after_angle_always is set to true.
+sp_inside_rparens = ignore # ignore/add/remove/force/not_defined
+
+# Add or remove space inside functor '(' and ')'.
+sp_inside_rparen = ignore # ignore/add/remove/force/not_defined
+
# Add or remove space inside the first parentheses in a function type, as in
# 'void (*x)(...)'.
sp_inside_tparen = remove # ignore/add/remove/force
@@ -951,6 +976,14 @@ sp_extern_paren = ignore # ignore/add/remove/force
# i.e. '// A' vs. '//A'.
sp_cmt_cpp_start = force # ignore/add/remove/force
+# remove space after the '//' and the pvs command '-V1234',
+# only works with sp_cmt_cpp_start set to add or force.
+sp_cmt_cpp_pvs = false # true/false
+
+# remove space after the '//' and the command 'lint',
+# only works with sp_cmt_cpp_start set to add or force.
+sp_cmt_cpp_lint = false # true/false
+
# Add or remove space in a C++ region marker comment, as in '// <here> BEGIN'.
# A region marker is defined as a comment which is not preceded by other text
# (i.e. the comment is the first non-whitespace on the line), and which starts
@@ -2804,9 +2837,19 @@ align_single_line_brace_gap = 1 # unsigned number
# 0: Don't align (default).
align_oc_msg_spec_span = 0 # unsigned number
-# Whether to align macros wrapped with a backslash and a newline. This will
-# not work right if the macro contains a multi-line comment.
-align_nl_cont = false # true/false
+# 0: Do nothing (default)
+# 1: Align the backslashes in the column at the end of the longest line
+# 2: Align with the backslash that is farthest to the left, or, if that
+# backslash is farther left than the end of the longest line, at the end of
+# the longest line
+# 3: Align with the backslash that is farthest to the right
+align_nl_cont = 0 # unsigned number
+
+# The minimum number of spaces between the end of a line and its continuation
+# backslash. Requires align_nl_cont.
+#
+# Default: 1
+align_nl_cont_spaces = 1 # unsigned number
# Whether to align macro functions and variables together.
align_pp_define_together = false # true/false
@@ -3117,6 +3160,12 @@ mod_remove_extra_semicolon = true # true/false
# Whether to remove duplicate include.
mod_remove_duplicate_include = true # true/false
+# the following options (mod_XX_closebrace_comment) use different comment,
+# depending of the setting of the next option.
+# false: Use the c comment (default)
+# true : Use the cpp comment
+mod_add_force_c_closebrace_comment = false # true/false
+
# If a function body exceeds the specified number of newlines and doesn't have
# a comment after the close brace, a comment will be added.
mod_add_long_function_closebrace_comment = 0 # unsigned number