diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ab2938..96a4533 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: options: "-DENABLE_TESTS=ON", packager: "sudo apt-get", # no expect because stdout seems to be redirected - packages: "libcmocka-dev shunit2", + packages: "libcmocka-dev libxxhash-dev shunit2", snaps: "", build-cmd: "make" } @@ -36,7 +36,7 @@ jobs: cc: "clang", options: "-DENABLE_TESTS=ON", packager: "sudo apt-get", - packages: "libcmocka-dev shunit2", + packages: "libcmocka-dev libxxhash-dev shunit2", snaps: "", build-cmd: "make" } @@ -47,7 +47,7 @@ jobs: cc: "gcc", options: "", packager: "sudo apt-get", - packages: "libcmocka-dev valgrind shunit2", + packages: "libcmocka-dev libxxhash-dev valgrind shunit2", snaps: "", build-cmd: "make" } @@ -59,7 +59,7 @@ jobs: options: "", packager: "sudo apt-get", # no valgrind because it does not support DWARF5 yet generated by clang 14 - packages: "libcmocka-dev shunit2", + packages: "libcmocka-dev libxxhash-dev shunit2", snaps: "", build-cmd: "make" } @@ -70,7 +70,7 @@ jobs: cc: "clang", options: "-DENABLE_TESTS=ON -DPATH_EXPECT=", packager: "brew", - packages: "cmocka shunit2 tcl-tk", + packages: "cmocka xxhash shunit2 tcl-tk", snaps: "", build-cmd: "make" } @@ -81,7 +81,7 @@ jobs: cc: "clang", options: "-DCMAKE_C_FLAGS=-fsanitize=address,undefined -DENABLE_TESTS=ON -DENABLE_VALGRIND_TESTS=OFF", packager: "sudo apt-get", - packages: "libcmocka-dev", + packages: "libcmocka-dev libxxhash-dev", snaps: "", build-cmd: "make" } @@ -92,7 +92,7 @@ jobs: cc: "gcc", options: "", packager: "sudo apt-get", - packages: "libcmocka-dev abi-dumper abi-compliance-checker", + packages: "libcmocka-dev libxxhash-dev abi-dumper abi-compliance-checker", snaps: "core universal-ctags", build-cmd: "make abi-check" } @@ -103,7 +103,7 @@ jobs: cc: "gcc", options: "", packager: "sudo apt-get", - packages: "cmake debhelper libcmocka-dev python3-pip", + packages: "cmake debhelper libcmocka-dev libxxhash-dev python3-pip", snaps: "", build-cmd: "" } @@ -132,7 +132,7 @@ jobs: cd uncrustify mkdir build cd build - CC=${{ matrix.config.cc }} cmake .. + CC=${{ matrix.config.cc }} cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 .. make sudo make install if: ${{ matrix.config.name == 'Debug, gcc' }} @@ -209,8 +209,13 @@ jobs: id: cpu-cores uses: SimenB/github-actions-cpu-cores@v1 + - name: pin CMake to the latest 3.x series + uses: jwlawson/actions-setup-cmake@09fd9b0fb3b239b4b68d9256cd65adf8d6b91da0 + with: + cmake-version: '3.31.6' + - name: Install Windows dependencies - run: vcpkg install --triplet=${{ matrix.triplet }} pcre2 pthreads dirent dlfcn-win32 cmocka getopt + run: vcpkg install --triplet=${{ matrix.triplet }} pcre2 pthreads dirent dlfcn-win32 cmocka getopt xxhash - name: Configure shell: bash diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 89b7bbd..1330c78 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -17,7 +17,7 @@ jobs: fuzz-seconds: 300 dry-run: false - name: Upload Crash - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@main if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/.github/workflows/devel-push.yml b/.github/workflows/devel-push.yml index 5d01a0c..1aa97ff 100644 --- a/.github/workflows/devel-push.yml +++ b/.github/workflows/devel-push.yml @@ -34,7 +34,7 @@ jobs: cc: "gcc", options: "-DENABLE_COVERAGE=ON", packager: "sudo apt-get", - packages: "libcmocka-dev lcov", + packages: "libcmocka-dev libxxhash-dev lcov", snaps: "", make-prepend: "", make-target: "" diff --git a/CMakeLists.txt b/CMakeLists.txt index 609bedb..1d6d487 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,14 +59,14 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) # set version of the project set(LIBYANG_MAJOR_VERSION 3) -set(LIBYANG_MINOR_VERSION 7) -set(LIBYANG_MICRO_VERSION 8) +set(LIBYANG_MINOR_VERSION 12) +set(LIBYANG_MICRO_VERSION 2) set(LIBYANG_VERSION ${LIBYANG_MAJOR_VERSION}.${LIBYANG_MINOR_VERSION}.${LIBYANG_MICRO_VERSION}) # set version of the library set(LIBYANG_MAJOR_SOVERSION 3) -set(LIBYANG_MINOR_SOVERSION 6) -set(LIBYANG_MICRO_SOVERSION 10) +set(LIBYANG_MINOR_SOVERSION 9) +set(LIBYANG_MICRO_SOVERSION 1) set(LIBYANG_SOVERSION_FULL ${LIBYANG_MAJOR_SOVERSION}.${LIBYANG_MINOR_SOVERSION}.${LIBYANG_MICRO_SOVERSION}) set(LIBYANG_SOVERSION ${LIBYANG_MAJOR_SOVERSION}) @@ -411,6 +411,17 @@ find_package(PCRE2 10.21 REQUIRED) include_directories(${PCRE2_INCLUDE_DIRS}) target_link_libraries(yang ${PCRE2_LIBRARIES}) +# XXHash include and library +find_package(XXHash) +if(XXHASH_FOUND) + add_definitions(-DLY_XXHASH_SUPPORT) + include_directories(${XXHASH_INCLUDE_DIR}) + target_link_libraries(yang ${XXHASH_LIBRARY}) + message(STATUS "Hash algorithm: xxhash") +else() + message(STATUS "Hash algorithm: internal Jenkin's one-at-a-time") +endif() + # generated header list foreach(h IN LISTS gen_headers) list(APPEND g_headers ${PROJECT_BINARY_DIR}/libyang/${h}) diff --git a/CMakeModules/FindXXHash.cmake b/CMakeModules/FindXXHash.cmake new file mode 100644 index 0000000..3ce77f7 --- /dev/null +++ b/CMakeModules/FindXXHash.cmake @@ -0,0 +1,38 @@ +# Try to find XXHash +# Once done this will define +# +# Read-Only variables: +# XXHASH_FOUND - system has XXHash +# XXHASH_INCLUDE_DIR - the XXHash include directory +# XXHASH_LIBRARY - Link these to use XXHash + +find_path(XXHASH_INCLUDE_DIR + NAMES + xxhash.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include +) + +find_library(XXHASH_LIBRARY + NAMES + xxhash + libxxhash + PATHS + PATHS + /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(XXHash FOUND_VAR XXHASH_FOUND REQUIRED_VARS XXHASH_INCLUDE_DIR XXHASH_LIBRARY) diff --git a/README.md b/README.md index cdfc1e0..d70afd7 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ the `distro` directory. #### Optional +* xxhash (for faster hashing) * doxygen (for generating documentation) * cmocka >= 1.0.1 (for [tests](#Tests)) * valgrind (for enhanced testing) diff --git a/models/yang@2022-06-16.h b/models/yang@2022-06-16.h deleted file mode 100644 index 2ca2ef0..0000000 --- a/models/yang@2022-06-16.h +++ /dev/null @@ -1,485 +0,0 @@ -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, 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, 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, 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, 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, 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, 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, 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, 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@2025-01-29.h b/models/yang@2025-01-29.h new file mode 100644 index 0000000..bf88f88 --- /dev/null +++ b/models/yang@2025-01-29.h @@ -0,0 +1,565 @@ +char yang_2025_01_29_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, 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, 0x35, 0x2d, 0x30, 0x31, 0x2d, + 0x32, 0x39, 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, 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, 0x7d, 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, 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, 0x74, 0x79, + 0x70, 0x65, 0x64, 0x65, 0x66, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 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, 0x52, 0x65, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, + 0x74, 0x73, 0x20, 0x61, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x73, + 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x6e, + 0x61, 0x6d, 0x65, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, + 0x61, 0x6d, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x27, 0x3c, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x2d, 0x6e, 0x61, 0x6d, 0x65, 0x3e, 0x3a, 0x3c, 0x61, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x6e, 0x61, + 0x6d, 0x65, 0x3e, 0x3d, 0x3c, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3e, 0x27, + 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, 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, 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, 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, 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, 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, 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, 0x0a, 0x20, + 0x20, 0x6d, 0x64, 0x3a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x2d, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 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, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x69, 0x74, 0x73, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x76, 0x61, 0x6c, + 0x75, 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, 0x6d, 0x65, 0x74, 0x61, 0x2d, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 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, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x64, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x69, 0x74, 0x73, 0x20, 0x6f, 0x6c, 0x64, 0x20, 0x76, 0x61, 0x6c, + 0x75, 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, 0x6d, 0x65, 0x74, 0x61, 0x2d, 0x72, 0x65, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, + 0x70, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2d, + 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 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, 0x43, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x64, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x20, 0x69, 0x74, 0x73, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6f, 0x6c, 0x64, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x27, 0x6d, 0x65, 0x74, + 0x61, 0x2d, 0x6f, 0x72, 0x69, 0x67, 0x27, 0x20, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 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, 0x6d, 0x65, 0x74, 0x61, 0x2d, 0x6f, 0x72, + 0x69, 0x67, 0x20, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2d, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 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, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x64, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x69, 0x74, 0x73, 0x20, 0x6f, 0x6c, 0x64, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x69, 0x73, 0x20, 0x73, 0x74, 0x6f, + 0x72, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x27, 0x6d, 0x65, 0x74, 0x61, + 0x2d, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x27, 0x20, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x22, 0x3b, 0x0a, 0x20, 0x20, + 0x7d, 0x0a, 0x7d, 0x0a, 0x00 +}; diff --git a/models/yang@2022-06-16.yang b/models/yang@2025-01-29.yang similarity index 85% rename from models/yang@2022-06-16.yang rename to models/yang@2025-01-29.yang index e506127..cb7623b 100644 --- a/models/yang@2022-06-16.yang +++ b/models/yang@2025-01-29.yang @@ -21,6 +21,11 @@ module yang { of various metadata defined in RFC 6020 and RFC 7950. There are additional metadata used in libyang diff data format."; + revision 2025-01-29 { + description + "Added metadata for diff of metadata."; + } + revision 2022-06-16 { description "Added typedef for key metadata type."; @@ -59,6 +64,13 @@ module yang { "The key predicates of the full instance-identifier built-in type."; } + typedef metadata-change { + type string; + description + "Represents a metadata instance value change and consists of module name, + annotation name, and the value in the form ':='."; + } + md:annotation insert { type enumeration { enum first; @@ -192,4 +204,28 @@ module yang { "Its meaning is the same as the \"position\" attribute but identifies the original list instance rather than the new one."; } + + md:annotation meta-create { + type metadata-change; + description + "Created metadata instance with its new value."; + } + + md:annotation meta-delete { + type metadata-change; + description + "Deleted metadata instance with its old value."; + } + + md:annotation meta-replace { + type metadata-change; + description + "Changed metadata instance with its new value. The old value is stored in 'meta-orig' metadata."; + } + + md:annotation meta-orig { + type metadata-change; + description + "Changed metadata instance with its old value. The new value is stored in 'meta-replace' metadata."; + } } diff --git a/src/context.c b/src/context.c index 2345cf2..4d817a0 100644 --- a/src/context.c +++ b/src/context.c @@ -55,7 +55,7 @@ #include "../models/ietf-yang-schema-mount@2019-01-14.h" #include "../models/ietf-yang-structure-ext@2020-06-17.h" #include "../models/ietf-yang-types@2013-07-15.h" -#include "../models/yang@2022-06-16.h" +#include "../models/yang@2025-01-29.h" #define IETF_YANG_LIB_REV "2019-01-04" static struct internal_modules_s { @@ -66,7 +66,7 @@ static struct internal_modules_s { LYS_INFORMAT format; } internal_modules[] = { {"ietf-yang-metadata", "2016-08-05", (const char *)ietf_yang_metadata_2016_08_05_yang, 0, LYS_IN_YANG}, - {"yang", "2022-06-16", (const char *)yang_2022_06_16_yang, 1, LYS_IN_YANG}, + {"yang", "2025-01-29", (const char *)yang_2025_01_29_yang, 1, LYS_IN_YANG}, {"ietf-inet-types", "2013-07-15", (const char *)ietf_inet_types_2013_07_15_yang, 0, LYS_IN_YANG}, {"ietf-yang-types", "2013-07-15", (const char *)ietf_yang_types_2013_07_15_yang, 0, LYS_IN_YANG}, {"ietf-yang-schema-mount", "2019-01-14", (const char *)ietf_yang_schema_mount_2019_01_14_yang, 1, LYS_IN_YANG}, @@ -272,9 +272,9 @@ ly_ctx_ht_err_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UN static ly_bool ly_ctx_ht_leafref_links_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data)) { - struct lyd_leafref_links_rec *rec1 = val1_p, *rec2 = val2_p; + struct lyd_leafref_links_rec **rec1 = val1_p, **rec2 = val2_p; - return rec1->node == rec2->node; + return (*rec1)->node == (*rec2)->node; } /** @@ -285,9 +285,10 @@ ly_ctx_ht_leafref_links_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod) static void ly_ctx_ht_leafref_links_rec_free(void *val_p) { - struct lyd_leafref_links_rec *rec = val_p; + struct lyd_leafref_links_rec **rec = val_p; - lyd_free_leafref_links_rec(rec); + lyd_free_leafref_links_rec(*rec); + free(*rec); } LIBYANG_API_DEF LY_ERR @@ -316,7 +317,12 @@ ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx) LY_CHECK_ERR_GOTO(lyplg_init(builtin_plugins_only), LOGINT(NULL); rc = LY_EINT, cleanup); if (options & LY_CTX_LEAFREF_LINKING) { - ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec), ly_ctx_ht_leafref_links_equal_cb, NULL, 1); + /** + * storing the pointer instead of record itself is needed to avoid invalid memory reads. Hash table can reallocate + * its memory completely during various manipulation function (e.g. remove, insert). In case of using pointers, the + * pointer can be reallocated safely, while record itself remains untouched and can be accessed/modified freely + * */ + ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec *), ly_ctx_ht_leafref_links_equal_cb, NULL, 1); LY_CHECK_ERR_GOTO(!ctx->leafref_links_ht, rc = LY_EMEM, cleanup); } @@ -367,7 +373,7 @@ ly_ctx_new(const char *search_dir, uint16_t options, struct ly_ctx **new_ctx) /* load internal modules */ for (i = 0; i < ((options & LY_CTX_NO_YANGLIBRARY) ? (LY_INTERNAL_MODS_COUNT - 2) : LY_INTERNAL_MODS_COUNT); i++) { ly_in_memory(in, internal_modules[i].data); - LY_CHECK_GOTO(rc = lys_parse_in(ctx, in, internal_modules[i].format, NULL, NULL, &unres.creating, &module), cleanup); + LY_CHECK_GOTO(rc = lys_parse_in(ctx, in, internal_modules[i].format, NULL, &unres.creating, &module), cleanup); if (internal_modules[i].implemented || (ctx->flags & LY_CTX_ALL_IMPLEMENTED)) { imp_f = (ctx->flags & LY_CTX_ENABLE_IMP_FEATURES) ? all_f : NULL; LY_CHECK_GOTO(rc = lys_implement(module, imp_f, &unres), cleanup); @@ -654,7 +660,7 @@ ly_ctx_set_options(struct ly_ctx *ctx, uint16_t option) } if (!(ctx->flags & LY_CTX_LEAFREF_LINKING) && (option & LY_CTX_LEAFREF_LINKING)) { - ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec), ly_ctx_ht_leafref_links_equal_cb, NULL, 1); + ctx->leafref_links_ht = lyht_new(1, sizeof(struct lyd_leafref_links_rec *), ly_ctx_ht_leafref_links_equal_cb, NULL, 1); LY_CHECK_ERR_RET(!ctx->leafref_links_ht, LOGARG(ctx, option), LY_EMEM); } diff --git a/src/diff.c b/src/diff.c index e041c15..00e4d79 100644 --- a/src/diff.c +++ b/src/diff.c @@ -3,7 +3,7 @@ * @author Michal Vasko * @brief diff functions * - * Copyright (c) 2020 - 2021 CESNET, z.s.p.o. + * Copyright (c) 2020 - 2025 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 +24,7 @@ #include "compat.h" #include "context.h" +#include "dict.h" #include "log.h" #include "ly_common.h" #include "plugins_exts.h" @@ -232,10 +233,11 @@ lyd_diff_find_meta(const struct lyd_node *node, const char *name, struct lyd_met * * @param[in] diff_node Diff node. * @param[out] op Operation. + * @param[out] found Whether any @p op was found. If not set, no found operation is an error. * @return LY_ERR value. */ static LY_ERR -lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op) +lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op, ly_bool *found) { struct lyd_meta *meta = NULL; struct lyd_attr *attr = NULL; @@ -255,14 +257,22 @@ lyd_diff_get_op(const struct lyd_node *diff_node, enum lyd_diff_op *op) continue; } *op = lyd_diff_str2op(str); + if (found) { + *found = 1; + } 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; + if (found) { + *found = 0; + return LY_SUCCESS; + } else { + 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; + } } /** @@ -310,13 +320,14 @@ lyd_diff_insert_sibling(struct lyd_node *sibling, struct lyd_node *node, struct 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, - struct lyd_node **diff) + struct lyd_node **diff, struct lyd_node **diff_node) { 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; + ly_bool found; assert(diff); @@ -336,6 +347,10 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_ (op != LYD_DIFF_OP_REPLACE) || (lysc_is_dup_inst_list(node->schema) && position && orig_position) || (value && orig_value)); + if (diff_node) { + *diff_node = NULL; + } + /* find the first existing parent */ siblings = *diff; do { @@ -365,8 +380,7 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_ 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; + assert(!lyd_diff_get_op(diff_parent, &cur_op, NULL)); /* move it to the end where it is expected (matters for user-ordered lists) */ if (lysc_is_userordered(diff_parent->schema)) { @@ -398,12 +412,7 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_ } /* 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)); - } + LY_CHECK_RET(lyd_dup_single(node, (struct lyd_node_inner *)diff_parent, diff_opts, &dup)); /* find the first duplicated parent */ if (!diff_parent) { @@ -432,8 +441,11 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_ } } - /* add subtree operation */ - LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), LYD_NEW_VAL_STORE_ONLY, NULL)); + /* add subtree operation if needed */ + LY_CHECK_RET(lyd_diff_get_op(dup, &cur_op, &found)); + if (!found || (cur_op != op)) { + LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:operation", lyd_diff_op2str(op), LYD_NEW_VAL_STORE_ONLY, NULL)); + } if (op == LYD_DIFF_OP_CREATE) { /* all nested user-ordered (leaf-)lists need special metadata for create op */ @@ -480,6 +492,9 @@ lyd_diff_add(const struct lyd_node *node, enum lyd_diff_op op, const char *orig_ LY_CHECK_RET(lyd_new_meta(NULL, dup, NULL, "yang:orig-position", orig_position, LYD_NEW_VAL_STORE_ONLY, NULL)); } + if (diff_node) { + *diff_node = dup; + } return LY_SUCCESS; } @@ -523,6 +538,91 @@ lyd_diff_userord_get(const struct lyd_node *first, const struct lysc_node *schem return item; } +/** + * @brief Check whether there are any metadata differences on 2 nodes. + * + * @param[in] first First node. + * @param[in] second Second node. + * @return 1 if there are some differences; + * @return 0 otherwise. + */ +static ly_bool +lyd_diff_node_metadata_check(const struct lyd_node *first, const struct lyd_node *second) +{ + ly_bool rc = 0; + const struct lys_module *mod; + const struct lyd_meta *m, **meta_second = NULL; + uint32_t i, m_second_count = 0; + const struct lyd_node *first_ch, *second_ch; + + assert(first && second); + + mod = ly_ctx_get_module_implemented(LYD_CTX(first), "yang"); + assert(mod); + + /* collect second node metadata that we can delete from */ + LY_LIST_FOR(second->meta, m) { + if (m->annotation->module == mod) { + continue; + } + + meta_second = ly_realloc(meta_second, (m_second_count + 1) * sizeof *meta_second); + LY_CHECK_ERR_GOTO(!meta_second, LOGMEM(LYD_CTX(first)), cleanup); + meta_second[m_second_count] = m; + ++m_second_count; + } + + /* go through first metadata and search for a match in second */ + LY_LIST_FOR(first->meta, m) { + if (m->annotation->module == mod) { + continue; + } + + for (i = 0; i < m_second_count; ++i) { + if (!lyd_compare_meta(m, meta_second[i])) { + break; + } + } + + if (i == m_second_count) { + /* not found */ + rc = 1; + goto cleanup; + } + + /* found, remove from the second metadata to consider */ + --m_second_count; + if (i < m_second_count) { + memcpy(&meta_second[i], &meta_second[i + 1], (m_second_count - i) * sizeof *meta_second); + } + } + + if (m_second_count) { + /* not found */ + rc = 1; + goto cleanup; + } + + /* for lists, we also need to check their keys */ + if (first->schema->nodetype == LYS_LIST) { + first_ch = lyd_child(first); + second_ch = lyd_child(second); + while (first_ch && lysc_is_key(first_ch->schema)) { + /* check every key */ + assert(second_ch && (first_ch->schema == second_ch->schema)); + rc = lyd_diff_node_metadata_check(first_ch, second_ch); + LY_CHECK_GOTO(rc, cleanup); + + first_ch = first_ch->next; + second_ch = second_ch->next; + } + } + +cleanup: + free(meta_second); + return rc; +} + /** * @brief Get all the metadata to be stored in a diff for the 2 nodes. Can be used only for user-ordered * lists/leaf-lists. @@ -594,6 +694,9 @@ lyd_diff_userord_attrs(const struct lyd_node *first, const struct lyd_node *seco } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) { /* default flag change */ *op = LYD_DIFF_OP_NONE; + } else if ((options & LYD_DIFF_META) && lyd_diff_node_metadata_check(first, second)) { + /* metadata changes */ + *op = LYD_DIFF_OP_NONE; } else { /* no changes */ return LY_ENOT; @@ -775,13 +878,22 @@ lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, uint case LYS_RPC: case LYS_ACTION: case LYS_NOTIF: - /* no changes */ - return LY_ENOT; + if ((options & LYD_DIFF_META) && lyd_diff_node_metadata_check(first, second)) { + /* metadata changes */ + *op = LYD_DIFF_OP_NONE; + } else { + /* no changes */ + return LY_ENOT; + } + break; case LYS_LIST: case LYS_LEAFLIST: if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) { /* default flag change */ *op = LYD_DIFF_OP_NONE; + } else if ((options & LYD_DIFF_META) && lyd_diff_node_metadata_check(first, second)) { + /* metadata changes */ + *op = LYD_DIFF_OP_NONE; } else { /* no changes */ return LY_ENOT; @@ -796,6 +908,9 @@ lyd_diff_attrs(const struct lyd_node *first, const struct lyd_node *second, uint } else if ((options & LYD_DIFF_DEFAULTS) && ((first->flags & LYD_DEFAULT) != (second->flags & LYD_DEFAULT))) { /* default flag change */ *op = LYD_DIFF_OP_NONE; + } else if ((options & LYD_DIFF_META) && lyd_diff_node_metadata_check(first, second)) { + /* metadata changes */ + *op = LYD_DIFF_OP_NONE; } else { /* no changes */ return LY_ENOT; @@ -873,6 +988,168 @@ lyd_diff_find_match(const struct lyd_node *siblings, const struct lyd_node *targ return LY_SUCCESS; } +/** + * @brief Create a diff metadata instance. + * + * @param[in,out] parent Parent node of the diff metadata. + * @param[in] diff_meta_name Diff metadata name with a mdoule name. + * @param[in] meta_module Changed metadata module name. + * @param[in] meta_name Changed metadata name. + * @param[in] meta_value Changed metadata value. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_node_metadata_add(struct lyd_node *parent, const char *diff_meta_name, const char *meta_module, + const char *meta_name, const char *meta_value) +{ + LY_ERR rc = LY_SUCCESS; + char *val = NULL; + + /* prepare the value */ + if (asprintf(&val, "%s:%s=%s", meta_module, meta_name, meta_value) == -1) { + LOGMEM(LYD_CTX(parent)); + rc = LY_EMEM; + goto cleanup; + } + + /* create the metadata */ + LY_CHECK_GOTO(rc = lyd_new_meta(NULL, parent, NULL, diff_meta_name, val, 0, NULL), cleanup); + +cleanup: + free(val); + return rc; +} + +/** + * @brief Add metadata differences of 2 nodes into a diff node. + * + * @param[in] first First node. + * @param[in] second Second node. + * @param[in,out] diff_node Diff node to add to. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_node_metadata(const struct lyd_node *first, const struct lyd_node *second, struct lyd_node *diff_node) +{ + LY_ERR rc = LY_SUCCESS; + const struct lys_module *mod; + const struct lyd_meta *m, **meta_second = NULL; + uint32_t i, m_second_count = 0, match_ann_idx; + + mod = ly_ctx_get_module_implemented(LYD_CTX(diff_node), "yang"); + assert(mod); + + /* collect second node metadata that we can delete from */ + if (second) { + LY_LIST_FOR(second->meta, m) { + if (m->annotation->module == mod) { + continue; + } + + meta_second = ly_realloc(meta_second, (m_second_count + 1) * sizeof *meta_second); + LY_CHECK_ERR_GOTO(!meta_second, LOGMEM(LYD_CTX(diff_node)); rc = LY_EMEM, cleanup); + meta_second[m_second_count] = m; + ++m_second_count; + } + } + + /* go through first metadata and search for match in second */ + if (first) { + LY_LIST_FOR(first->meta, m) { + if (m->annotation->module == mod) { + continue; + } + + match_ann_idx = m_second_count; + for (i = 0; i < m_second_count; ++i) { + /* annotation match */ + if (m->annotation != meta_second[i]->annotation) { + continue; + } + if (match_ann_idx == m_second_count) { + match_ann_idx = i; + } + + /* value match */ + if (m->value.realtype->plugin->compare(LYD_CTX(diff_node), &m->value, &meta_second[i]->value)) { + continue; + } + break; + } + + if (i < m_second_count) { + /* found, no change */ + --m_second_count; + if (i < m_second_count) { + memcpy(&meta_second[i], &meta_second[i + 1], (m_second_count - i) * sizeof *meta_second); + } + } else if (match_ann_idx < m_second_count) { + /* found with a different value, replace */ + i = match_ann_idx; + rc = lyd_diff_node_metadata_add(diff_node, "yang:meta-replace", meta_second[i]->annotation->module->name, + meta_second[i]->name, lyd_get_meta_value(meta_second[i])); + LY_CHECK_GOTO(rc, cleanup); + rc = lyd_diff_node_metadata_add(diff_node, "yang:meta-orig", m->annotation->module->name, m->name, + lyd_get_meta_value(m)); + LY_CHECK_GOTO(rc, cleanup); + + --m_second_count; + if (i < m_second_count) { + memcpy(&meta_second[i], &meta_second[i + 1], (m_second_count - i) * sizeof *meta_second); + } + } else { + /* not found, delete */ + rc = lyd_diff_node_metadata_add(diff_node, "yang:meta-delete", m->annotation->module->name, m->name, + lyd_get_meta_value(m)); + LY_CHECK_GOTO(rc, cleanup); + } + } + } + + for (i = 0; i < m_second_count; ++i) { + /* not processed, create */ + rc = lyd_diff_node_metadata_add(diff_node, "yang:meta-create", meta_second[i]->annotation->module->name, + meta_second[i]->name, lyd_get_meta_value(meta_second[i])); + LY_CHECK_GOTO(rc, cleanup); + } + +cleanup: + free(meta_second); + return rc; +} + +/** + * @brief Add metadata differences of 2 subtrees into a diff subtree. + * + * @param[in] first_subtree First subtree. + * @param[in] second_subtree Second subtree. + * @param[in] keys_only Whether to check all the subtrees recursively or only the direct key children. + * @param[in,out] diff_subtree Diff subtree to add to. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_node_metadata_r(const struct lyd_node *first_subtree, const struct lyd_node *second_subtree, ly_bool keys_only, + struct lyd_node *diff_subtree) +{ + struct lyd_node *diff_node, *first, *second; + + /* metadata diff on the node itself */ + LY_CHECK_RET(lyd_diff_node_metadata(first_subtree, second_subtree, diff_subtree)); + + /* metadata diff on all the children, recursively */ + LY_LIST_FOR(lyd_child(diff_subtree), diff_node) { + if (keys_only && !lysc_is_key(diff_node->schema)) { + break; + } + + lyd_find_sibling_first(lyd_child(first_subtree), diff_node, &first); + lyd_find_sibling_first(lyd_child(second_subtree), diff_node, &second); + LY_CHECK_RET(lyd_diff_node_metadata_r(first, second, keys_only, diff_node)); + } + + return LY_SUCCESS; +} + /** * @brief Perform diff for all siblings at certain depth, recursively. * @@ -918,9 +1195,9 @@ static LY_ERR lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, uint16_t options, ly_bool nosiblings, struct lyd_node **diff) { - LY_ERR ret = LY_SUCCESS; + LY_ERR rc = LY_SUCCESS, r; const struct lyd_node *iter_first, *iter_second; - struct lyd_node *match_second, *match_first; + struct lyd_node *match_second, *match_first, *diff_node; struct lyd_diff_userord *userord = NULL, *userord_item; struct ly_ht *dup_inst_first = NULL, *dup_inst_second = NULL; LY_ARRAY_COUNT_TYPE u; @@ -940,60 +1217,69 @@ lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, continue; } + diff_node = NULL; + /* find a match in the second tree */ - LY_CHECK_GOTO(ret = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second, + LY_CHECK_GOTO(rc = lyd_diff_find_match(second, iter_first, options & LYD_DIFF_DEFAULTS, &dup_inst_second, &match_second), cleanup); if (lysc_is_userordered(iter_first->schema)) { /* get (create) userord entry */ userord_item = lyd_diff_userord_get(iter_first, iter_first->schema, &userord); - LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); ret = LY_EMEM, cleanup); + LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_first)); rc = LY_EMEM, cleanup); /* we are handling only user-ordered node delete now */ if (!match_second) { /* get all the attributes */ - LY_CHECK_GOTO(ret = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op, + LY_CHECK_GOTO(rc = lyd_diff_userord_attrs(iter_first, match_second, options, userord_item, &op, &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position), cleanup); /* there must be changes, it is deleted */ assert(op == LYD_DIFF_OP_DELETE); - ret = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key, - orig_position, diff); - + rc = lyd_diff_add(iter_first, op, orig_default, orig_value, key, value, position, orig_key, + orig_position, diff, &diff_node); free(orig_value); free(key); free(value); free(position); free(orig_key); free(orig_position); - LY_CHECK_GOTO(ret, cleanup); + LY_CHECK_GOTO(rc, cleanup); } } else { /* get all the attributes */ - ret = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value); + r = lyd_diff_attrs(iter_first, match_second, options, &op, &orig_default, &orig_value); + if (r && (r != LY_ENOT)) { + goto cleanup; + } /* add into diff if there are any changes */ - if (!ret) { + if (!r) { if (op == LYD_DIFF_OP_DELETE) { - ret = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff); + rc = lyd_diff_add(iter_first, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff, &diff_node); } else { assert(match_second); - ret = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff); + rc = lyd_diff_add(match_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff, &diff_node); } - free(orig_value); - LY_CHECK_GOTO(ret, cleanup); - } else if (ret == LY_ENOT) { - ret = LY_SUCCESS; - } else { - goto cleanup; + LY_CHECK_GOTO(rc, cleanup); } } - /* check descendants, if any, recursively */ if (match_second) { - LY_CHECK_GOTO(ret = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second), + if ((options & LYD_DIFF_META) && diff_node) { + /* create metadata diff for the node (and list keys, if relevant) */ + LY_CHECK_GOTO(rc = lyd_diff_node_metadata_r(iter_first, match_second, 1, diff_node), cleanup); + } + + /* check descendants, if any, recursively */ + LY_CHECK_GOTO(rc = lyd_diff_siblings_r(lyd_child_no_keys(iter_first), lyd_child_no_keys(match_second), options, 0, diff), cleanup); + } else { + if ((options & LYD_DIFF_META) && diff_node) { + /* create metadata diff for the node and all its descendants */ + LY_CHECK_GOTO(rc = lyd_diff_node_metadata_r(iter_first, NULL, 0, diff_node), cleanup); + } } if (nosiblings) { @@ -1018,48 +1304,52 @@ lyd_diff_siblings_r(const struct lyd_node *first, const struct lyd_node *second, continue; } + diff_node = NULL; + /* find a match in the first tree */ - LY_CHECK_GOTO(ret = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first, + LY_CHECK_GOTO(rc = lyd_diff_find_match(first, iter_second, options & LYD_DIFF_DEFAULTS, &dup_inst_first, &match_first), cleanup); if (lysc_is_userordered(iter_second->schema)) { /* get userord entry */ userord_item = lyd_diff_userord_get(match_first, iter_second->schema, &userord); - LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); ret = LY_EMEM, cleanup); + LY_CHECK_ERR_GOTO(!userord_item, LOGMEM(LYD_CTX(iter_second)); rc = LY_EMEM, cleanup); /* get all the attributes */ - ret = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default, + r = lyd_diff_userord_attrs(match_first, iter_second, options, userord_item, &op, &orig_default, &value, &orig_value, &key, &orig_key, &position, &orig_position); + if (r && (r != LY_ENOT)) { + goto cleanup; + } /* add into diff if there are any changes */ - if (!ret) { - ret = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key, - orig_position, diff); - + if (!r) { + rc = lyd_diff_add(iter_second, op, orig_default, orig_value, key, value, position, orig_key, + orig_position, diff, &diff_node); free(orig_value); free(key); free(value); free(position); free(orig_key); free(orig_position); - LY_CHECK_GOTO(ret, cleanup); - } else if (ret == LY_ENOT) { - ret = LY_SUCCESS; - } else { - goto cleanup; + LY_CHECK_GOTO(rc, cleanup); } } else if (!match_first) { /* get all the attributes */ - LY_CHECK_GOTO(ret = lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_attrs(match_first, iter_second, options, &op, &orig_default, &orig_value), cleanup); /* there must be changes, it is created */ assert(op == LYD_DIFF_OP_CREATE); - ret = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff); - + rc = lyd_diff_add(iter_second, op, orig_default, orig_value, NULL, NULL, NULL, NULL, NULL, diff, &diff_node); free(orig_value); - LY_CHECK_GOTO(ret, cleanup); + LY_CHECK_GOTO(rc, cleanup); } /* else was handled */ + if ((options & LYD_DIFF_META) && diff_node) { + /* create metadata diff for the node and all its descendants */ + LY_CHECK_GOTO(rc = lyd_diff_node_metadata_r(match_first, iter_second, 0, diff_node), cleanup); + } + if (nosiblings) { break; } @@ -1072,7 +1362,7 @@ cleanup: LY_ARRAY_FREE(userord[u].inst); } LY_ARRAY_FREE(userord); - return ret; + return rc; } static LY_ERR @@ -1218,6 +1508,248 @@ lyd_diff_insert(struct lyd_node **first_node, struct lyd_node *parent_node, stru return LY_SUCCESS; } +/** + * @brief Parse a diff metadata value into the changed metadata instance name (with module name) and value. + * + * @param[in] meta Diff metadata instance. + * @param[out] name Optional changed metadata name. + * @param[out] value Optional changed metadata value. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_apply_metadata_parse(const struct lyd_meta *meta, char **name, const char **value) +{ + LY_ERR rc = LY_SUCCESS; + const char *v, *ptr; + + v = lyd_get_meta_value(meta); + ptr = strchr(v, '='); + LY_CHECK_ERR_GOTO(!ptr, LOGINT(meta->annotation->module->ctx); rc = LY_EINT, cleanup); + + if (name) { + *name = strndup(v, ptr - v); + LY_CHECK_ERR_GOTO(!*name, LOGMEM(meta->annotation->module->ctx); rc = LY_EMEM, cleanup); + } + + if (value) { + *value = ptr + 1; + } + +cleanup: + return rc; +} + +/** + * @brief Find a metadata instance with a specific value. + * + * @param[in] meta First metadata to consider. + * @param[in] name Metadata name with module name. + * @param[in] value Metadata value. + * @param[in] log Whether to log an error if not found. + * @param[out] match Found metadata. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_metadata_find(const struct lyd_meta *meta, const char *name, const char *value, ly_bool log, + struct lyd_meta **match) +{ + const struct lyd_meta *m; + + for (m = meta; (m = lyd_find_meta(m, NULL, name)); m = m->next) { + if (!strcmp(lyd_get_meta_value(m), value)) { + *match = (struct lyd_meta *)m; + return LY_SUCCESS; + } + } + + *match = NULL; + if (log) { + LOGINT(meta->annotation->module->ctx); + return LY_EINT; + } else { + return LY_SUCCESS; + } +} + +/** + * @brief Add a metadata into an array. + * + * @param[in] meta Metadata to add. + * @param[in,out] meta_a Metadata array. + * @param[in,out] meta_a_count Count of @p meta_a items. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_meta_store(struct lyd_meta *meta, struct lyd_meta ***meta_a, uint32_t *meta_a_count) +{ + *meta_a = ly_realloc(*meta_a, (*meta_a_count + 1) * sizeof **meta_a); + LY_CHECK_ERR_RET(!*meta_a, LOGMEM(meta->annotation->module->ctx), LY_EMEM); + (*meta_a)[*meta_a_count] = meta; + ++(*meta_a_count); + + return LY_SUCCESS; +} + +/** + * @brief Align all meta-replace metadata with its matching meta-orig metadata. + * + * @param[in] meta_replace Meta-replace metadata. + * @param[in] mr_count Count of @p meta_replace. + * @param[in,out] meta_orig Meta-orig metadata to align and reorder. + * @param[in] mo_count Count of @p meta_orig. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_metadata_replace_orig_align(struct lyd_meta **meta_replace, uint32_t mr_count, struct lyd_meta **meta_orig, + uint32_t mo_count) +{ + const struct ly_ctx *ctx; + struct lyd_meta *m; + const char *val1, *val2, *ptr; + uint32_t i, j; + + if (!mr_count) { + return LY_SUCCESS; + } + + ctx = meta_replace[0]->annotation->module->ctx; + + LY_CHECK_ERR_RET(mr_count != mo_count, LOGINT(ctx), LY_EINT); + + for (i = 0; i < mr_count; ++i) { + /* meta-replace value */ + val1 = lyd_get_meta_value(meta_replace[i]); + ptr = strchr(val1, '='); + LY_CHECK_ERR_RET(!ptr, LOGINT(ctx), LY_EINT); + ++ptr; + + /* find matching meta-orig value */ + j = i; + while (j < mo_count) { + val2 = lyd_get_meta_value(meta_orig[j]); + if (!strncmp(val1, val2, ptr - val1)) { + break; + } + + ++j; + } + LY_CHECK_ERR_RET(j == mo_count, LOGINT(ctx), LY_EINT); + + if (j != i) { + /* non-matching index, move it */ + m = meta_orig[i]; + meta_orig[i] = meta_orig[j]; + meta_orig[j] = m; + } + } + + return LY_SUCCESS; +} + +/** + * @brief Apply any metadata changes in the diff. + * + * @param[in,out] node Node to change. + * @param[in] diff_node Diff node to read the metadata changes from. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_apply_metadata(struct lyd_node *node, const struct lyd_node *diff_node) +{ + LY_ERR rc = LY_SUCCESS; + struct lyd_meta *m, *m2, **meta_replace = NULL, **meta_orig = NULL; + uint32_t i, mr_count = 0, mo_count = 0; + const struct lys_module *mod; + const char *meta_value, *old_meta_value; + char *meta_name = NULL; + const struct lyd_node *diff_ch; + struct lyd_node *node_ch; + + mod = ly_ctx_get_module_implemented(LYD_CTX(node), "yang"); + assert(mod); + + /* go through all the metadata */ + LY_LIST_FOR(diff_node->meta, m) { + if (m->annotation->module != mod) { + continue; + } + + if (!strcmp(m->name, "meta-create")) { + /* parse the value */ + LY_CHECK_GOTO(rc = lyd_diff_apply_metadata_parse(m, &meta_name, &meta_value), cleanup); + + /* create the metadata instance */ + LY_CHECK_GOTO(rc = lyd_new_meta(NULL, node, NULL, meta_name, meta_value, 0, NULL), cleanup); + + free(meta_name); + meta_name = NULL; + } else if (!strcmp(m->name, "meta-delete")) { + /* parse the value */ + LY_CHECK_GOTO(rc = lyd_diff_apply_metadata_parse(m, &meta_name, &meta_value), cleanup); + + /* find the metadata instance and free it */ + LY_CHECK_GOTO(rc = lyd_diff_metadata_find(node->meta, meta_name, meta_value, 1, &m2), cleanup); + lyd_free_meta_single(m2); + + free(meta_name); + meta_name = NULL; + } else if (!strcmp(m->name, "meta-replace")) { + /* just store it, to be able to correctly match to 'meta-orig' */ + LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_replace, &mr_count), cleanup); + } else if (!strcmp(m->name, "meta-orig")) { + /* just store it */ + LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_orig, &mo_count), cleanup); + } + } + + /* make sure meta_replace and meta_orig arrays are aligned */ + LY_CHECK_GOTO(rc = lyd_diff_metadata_replace_orig_align(meta_replace, mr_count, meta_orig, mo_count), cleanup); + + /* process replaced metadata */ + LY_CHECK_ERR_GOTO(mr_count != mo_count, LOGINT(LYD_CTX(node)); rc = LY_EINT, cleanup); + for (i = 0; i < mr_count; ++i) { + /* get the changed meta name with '=' */ + LY_CHECK_GOTO(rc = lyd_diff_apply_metadata_parse(meta_replace[i], &meta_name, &meta_value), cleanup); + + /* parse the orig value */ + LY_CHECK_GOTO(rc = lyd_diff_apply_metadata_parse(meta_orig[i], NULL, &old_meta_value), cleanup); + + /* find the metadata instance */ + LY_CHECK_GOTO(rc = lyd_diff_metadata_find(node->meta, meta_name, old_meta_value, 0, &m2), cleanup); + LY_CHECK_ERR_GOTO(!m2, LOGINT(LYD_CTX(node)); rc = LY_EINT, cleanup); + + /* change its value */ + LY_CHECK_GOTO(rc = lyd_change_meta(m2, meta_value), cleanup); + + /* meta-orig spent */ + meta_orig[i] = NULL; + + free(meta_name); + meta_name = NULL; + } + + /* for lists, we also need to process their keys */ + if (diff_node->schema->nodetype == LYS_LIST) { + diff_ch = lyd_child(diff_node); + node_ch = lyd_child(node); + while (diff_ch && lysc_is_key(diff_ch->schema)) { + /* process every key */ + assert(node_ch && (diff_ch->schema == node_ch->schema)); + rc = lyd_diff_apply_metadata(node_ch, diff_ch); + LY_CHECK_GOTO(rc, cleanup); + + diff_ch = diff_ch->next; + node_ch = node_ch->next; + } + } + +cleanup: + free(meta_name); + free(meta_replace); + free(meta_orig); + return rc; +} + /** * @brief Apply diff subtree on data tree nodes, recursively. * @@ -1233,7 +1765,7 @@ 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 ly_ht **dup_inst) { - LY_ERR ret; + LY_ERR rc = LY_SUCCESS, r; struct lyd_node *match, *diff_child; const char *str_val, *meta_str; enum lyd_diff_op op; @@ -1242,7 +1774,7 @@ lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, con const struct ly_ctx *ctx = LYD_CTX(diff_node); /* read all the valid attributes */ - LY_CHECK_RET(lyd_diff_get_op(diff_node, &op)); + LY_CHECK_RET(lyd_diff_get_op(diff_node, &op, NULL)); /* handle specific user-ordered (leaf-)lists operations separately */ if (lysc_is_userordered(diff_node->schema) && ((op == LYD_DIFF_OP_CREATE) || (op == LYD_DIFF_OP_REPLACE))) { @@ -1269,124 +1801,115 @@ lyd_diff_apply_r(struct lyd_node **first_node, struct lyd_node *parent_node, con /* insert/move the node */ if (str_val[0]) { - ret = lyd_diff_insert(first_node, parent_node, match, str_val); + r = lyd_diff_insert(first_node, parent_node, match, str_val); } else { - ret = lyd_diff_insert(first_node, parent_node, match, NULL); + r = lyd_diff_insert(first_node, parent_node, match, NULL); } - if (ret) { + if (r) { if (op == LYD_DIFF_OP_CREATE) { lyd_free_tree(match); } - return ret; + return r; } + } else { + /* apply operation */ + switch (op) { + case LYD_DIFF_OP_NONE: + /* find the node */ + LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); + LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL); - goto next_iter_r; - } - - /* apply operation */ - switch (op) { - case LYD_DIFF_OP_NONE: - /* find the node */ - LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); - LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL); - - if (match->schema->nodetype & LYD_NODE_TERM) { - /* special case of only dflt flag change */ - if (diff_node->flags & LYD_DEFAULT) { - match->flags |= LYD_DEFAULT; - } else { - match->flags &= ~LYD_DEFAULT; + if (match->schema->nodetype & LYD_NODE_TERM) { + /* special case of only dflt flag change */ + if (diff_node->flags & LYD_DEFAULT) { + match->flags |= LYD_DEFAULT; + } else { + match->flags &= ~LYD_DEFAULT; + } } - } else { - /* none operation on nodes without children is redundant and hence forbidden */ - if (!lyd_child_no_keys(diff_node)) { - LOGERR(ctx, LY_EINVAL, "Operation \"none\" is invalid for node \"%s\" without children.", - LYD_NAME(diff_node)); + break; + case LYD_DIFF_OP_CREATE: + /* duplicate the node */ + LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match)); + + /* insert it at the end */ + if (parent_node) { + if (match->flags & LYD_EXT) { + r = lyplg_ext_insert(parent_node, match); + } else { + r = lyd_insert_child(parent_node, match); + } + } else { + r = lyd_insert_sibling(*first_node, match, first_node); + } + if (r) { + lyd_free_tree(match); + return r; + } + + break; + case LYD_DIFF_OP_DELETE: + /* find the node */ + LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); + LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL); + + /* remove it */ + if ((match == *first_node) && !match->parent) { + assert(!parent_node); + /* we have removed the top-level node */ + *first_node = (*first_node)->next; + } + lyd_free_tree(match); + + /* we are not going recursively in this case, the whole subtree was already deleted */ + return LY_SUCCESS; + case LYD_DIFF_OP_REPLACE: + if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) { + LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".", + lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node)); return LY_EINVAL; } - } - break; - case LYD_DIFF_OP_CREATE: - /* duplicate the node */ - LY_CHECK_RET(lyd_dup_single(diff_node, NULL, LYD_DUP_NO_META, &match)); - /* insert it at the end */ - ret = 0; - if (parent_node) { - if (match->flags & LYD_EXT) { - ret = lyplg_ext_insert(parent_node, match); + /* find the node */ + LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); + LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL); + + /* update the value */ + if (diff_node->schema->nodetype == LYS_LEAF) { + r = lyd_change_term(match, lyd_get_value(diff_node)); + LY_CHECK_ERR_RET(r && (r != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL); } else { - ret = lyd_insert_child(parent_node, match); + struct lyd_node_any *any = (struct lyd_node_any *)diff_node; + + LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type)); } - } else { - ret = lyd_insert_sibling(*first_node, match, first_node); + + /* with flags */ + match->flags = diff_node->flags; + break; + default: + LOGINT_RET(ctx); } - if (ret) { - lyd_free_tree(match); - return ret; - } - - break; - case LYD_DIFF_OP_DELETE: - /* find the node */ - LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); - LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL); - - /* remove it */ - if ((match == *first_node) && !match->parent) { - assert(!parent_node); - /* we have removed the top-level node */ - *first_node = (*first_node)->next; - } - lyd_free_tree(match); - - /* we are not going recursively in this case, the whole subtree was already deleted */ - return LY_SUCCESS; - case LYD_DIFF_OP_REPLACE: - if (!(diff_node->schema->nodetype & (LYS_LEAF | LYS_ANYDATA))) { - LOGERR(ctx, LY_EINVAL, "Operation \"replace\" is invalid for %s node \"%s\".", - lys_nodetype2str(diff_node->schema->nodetype), LYD_NAME(diff_node)); - return LY_EINVAL; - } - - /* find the node */ - LY_CHECK_RET(lyd_diff_find_match(*first_node, diff_node, 1, dup_inst, &match)); - LY_CHECK_ERR_RET(!match, LOGERR_NOINST(ctx, diff_node), LY_EINVAL); - - /* update the value */ - if (diff_node->schema->nodetype == LYS_LEAF) { - ret = lyd_change_term(match, lyd_get_value(diff_node)); - LY_CHECK_ERR_RET(ret && (ret != LY_EEXIST), LOGERR_UNEXPVAL(ctx, match, "data"), LY_EINVAL); - } else { - struct lyd_node_any *any = (struct lyd_node_any *)diff_node; - - LY_CHECK_RET(lyd_any_copy_value(match, &any->value, any->value_type)); - } - - /* with flags */ - match->flags = diff_node->flags; - break; - default: - LOGINT_RET(ctx); } -next_iter_r: + /* apply any metadata changes */ + LY_CHECK_RET(lyd_diff_apply_metadata(match, diff_node)); + if (diff_cb) { /* call callback */ LY_CHECK_RET(diff_cb(diff_node, match, cb_data)); } /* apply diff recursively */ - ret = LY_SUCCESS; LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) { - ret = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst); - if (ret) { + rc = lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data, &child_dup_inst); + if (rc) { break; } } lyd_dup_inst_free(child_dup_inst); - return ret; + return rc; } LIBYANG_API_DEF LY_ERR @@ -1850,13 +2373,49 @@ lyd_diff_merge_delete(struct lyd_node *diff_match, enum lyd_diff_op cur_op, cons return LY_SUCCESS; } +/** + * @brief Check a node is redundant based on having any diff metadata. + * + * @param[in] diff Diff node to check. + * @return 1 if the node is redundant; + * @return 0 otherwise. + */ +static ly_bool +lyd_diff_is_redundant_meta(const struct lyd_node *diff) +{ + const struct lyd_meta *m; + const struct lyd_node *child; + + /* diff metadata on the node */ + LY_LIST_FOR(diff->meta, m) { + if (!strncmp(m->name, "meta-", 5)) { + return 0; + } + } + + /* diff metadata on keys */ + LY_LIST_FOR(lyd_child(diff), child) { + if (!lysc_is_key(child->schema)) { + break; + } + + LY_LIST_FOR(child->meta, m) { + if (!strncmp(m->name, "meta-", 5)) { + return 0; + } + } + } + + return 1; +} + /** * @brief Check whether this diff node is redundant (does not change data). * * @param[in] diff Diff node. * @return 0 if not, non-zero if it is. */ -static int +static ly_bool lyd_diff_is_redundant(struct lyd_node *diff) { enum lyd_diff_op op; @@ -1873,11 +2432,11 @@ lyd_diff_is_redundant(struct lyd_node *diff) } else { child = lyd_child_no_keys(diff); } - mod = ly_ctx_get_module_latest(LYD_CTX(diff), "yang"); + mod = ly_ctx_get_module_implemented(LYD_CTX(diff), "yang"); assert(mod); /* get node operation */ - LY_CHECK_RET(lyd_diff_get_op(diff, &op), 0); + LY_CHECK_RET(lyd_diff_get_op(diff, &op, NULL), 0); if ((op == LYD_DIFF_OP_REPLACE) && lysc_is_userordered(diff->schema)) { /* get metadata names */ @@ -1922,6 +2481,11 @@ lyd_diff_is_redundant(struct lyd_node *diff) return 1; } + /* check for diff metadata */ + if (!lyd_diff_is_redundant_meta(diff)) { + 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"); @@ -1943,6 +2507,175 @@ lyd_diff_is_redundant(struct lyd_node *diff) return 0; } +/** + * @brief Merge all diff metadata found on a source diff node. + * + * @param[in] src_diff Source node. + * @param[in,out] trg_diff Target node to update. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_merge_metadata(const struct lyd_node *src_diff, struct lyd_node *trg_diff) +{ + LY_ERR rc = LY_SUCCESS; + const struct lys_module *mod; + struct lyd_meta *m, **src_meta_replace = NULL, **src_meta_orig = NULL; + struct lyd_meta **trg_meta_replace = NULL, **trg_meta_orig = NULL, *m1, *m2; + uint32_t i, j, src_mr_count = 0, src_mo_count = 0, trg_mr_count = 0, trg_mo_count = 0; + + assert(src_diff->schema == trg_diff->schema); + + mod = ly_ctx_get_module_implemented(LYD_CTX(src_diff), "yang"); + assert(mod); + + /* collect all the metadata so we can safely modify them */ + LY_LIST_FOR(trg_diff->meta, m) { + if (m->annotation->module != mod) { + continue; + } + + if (!strcmp(m->name, "meta-replace")) { + LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &trg_meta_replace, &trg_mr_count), cleanup); + } else if (!strcmp(m->name, "meta-orig")) { + LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &trg_meta_orig, &trg_mo_count), cleanup); + } + } + + /* make sure meta_replace and meta_orig arrays are aligned */ + rc = lyd_diff_metadata_replace_orig_align(trg_meta_replace, trg_mr_count, trg_meta_orig, trg_mo_count); + LY_CHECK_GOTO(rc, cleanup); + + LY_LIST_FOR(src_diff->meta, m) { + if (m->annotation->module != mod) { + continue; + } + + if (!strcmp(m->name, "meta-create")) { + /* find relevant metadata in the target */ + rc = lyd_diff_metadata_find(trg_diff->meta, "yang:meta-delete", lyd_get_meta_value(m), 0, &m1); + LY_CHECK_GOTO(rc, cleanup); + m2 = NULL; + for (i = 0; i < trg_mo_count; ++i) { + if (lyd_get_meta_value(m) == lyd_get_meta_value(trg_meta_orig[i])) { + m2 = trg_meta_orig[i]; + break; + } + } + + if (m1) { + /* create + delete -> no change */ + lyd_free_meta_single(m1); + } else if (m2) { + /* create + replace -> create with updated value */ + rc = lyd_new_meta(NULL, trg_diff, mod, "meta-create", lyd_get_meta_value(trg_meta_replace[i]), 0, NULL); + LY_CHECK_GOTO(rc, cleanup); + + /* remove meta-replace and meta-orig */ + lyd_free_meta_single(trg_meta_replace[i]); + --trg_mr_count; + if (i < trg_mr_count) { + memmove(&trg_meta_replace[i], &trg_meta_replace[i + 1], (trg_mr_count - i) * sizeof *trg_meta_replace); + } + + lyd_free_meta_single(trg_meta_orig[i]); + --trg_mo_count; + if (i < trg_mo_count) { + memmove(&trg_meta_orig[i], &trg_meta_orig[i + 1], (trg_mo_count - i) * sizeof *trg_meta_orig); + } + } else { + /* copy to the target */ + LY_CHECK_GOTO(rc = lyd_dup_meta_single(m, trg_diff, NULL), cleanup); + } + } else if (!strcmp(m->name, "meta-delete")) { + /* find relevant metadata in the target */ + rc = lyd_diff_metadata_find(trg_diff->meta, "yang:meta-create", lyd_get_meta_value(m), 0, &m1); + LY_CHECK_GOTO(rc, cleanup); + + if (m1) { + /* delete + create -> no change */ + lyd_free_meta_single(m1); + } else { + /* copy to the target */ + LY_CHECK_GOTO(rc = lyd_dup_meta_single(m, trg_diff, NULL), cleanup); + } + } else if (!strcmp(m->name, "meta-replace")) { + /* collect all meta-replace metadata */ + LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &src_meta_replace, &src_mr_count), cleanup); + } else if (!strcmp(m->name, "meta-orig")) { + /* collect all meta-orig metadata */ + LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &src_meta_orig, &src_mo_count), cleanup); + } + } + + /* make sure meta_replace and meta_orig arrays are aligned */ + rc = lyd_diff_metadata_replace_orig_align(src_meta_replace, src_mr_count, src_meta_orig, src_mo_count); + LY_CHECK_GOTO(rc, cleanup); + + for (i = 0; i < src_mr_count; ++i) { + /* find relevant metadata in the target */ + rc = lyd_diff_metadata_find(trg_diff->meta, "yang:meta-delete", lyd_get_meta_value(src_meta_replace[i]), 0, &m1); + LY_CHECK_GOTO(rc, cleanup); + m2 = NULL; + for (j = 0; j < trg_mo_count; ++j) { + if (lyd_get_meta_value(trg_meta_orig[j]) == lyd_get_meta_value(src_meta_replace[i])) { + m2 = trg_meta_orig[j]; + break; + } + } + + if (m1) { + /* replace + delete -> delete with updated value */ + LY_CHECK_GOTO(rc = lyd_change_meta(m1, lyd_get_meta_value(src_meta_orig[i])), cleanup); + } else if (m2) { + /* replace + replace -> replace (orig) with updated value */ + LY_CHECK_GOTO(rc = lyd_change_meta(m2, lyd_get_meta_value(src_meta_orig[i])), cleanup); + } else { + /* copy to the target */ + LY_CHECK_GOTO(rc = lyd_dup_meta_single(src_meta_replace[i], trg_diff, NULL), cleanup); + LY_CHECK_GOTO(rc = lyd_dup_meta_single(src_meta_orig[i], trg_diff, NULL), cleanup); + } + } + +cleanup: + free(src_meta_replace); + free(src_meta_orig); + free(trg_meta_replace); + free(trg_meta_orig); + return rc; +} + +/** + * @brief Merge all diff metadata found on a source diff subtree, recursively. + * + * @param[in] src_diff Source subtree. + * @param[in,out] trg_diff Target subtree to update. + * @param[in] keys_only Whether to process the node with keys only or with all the descendants. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_merge_metadata_r(const struct lyd_node *src_diff, struct lyd_node *trg_diff, ly_bool keys_only) +{ + const struct lyd_node *src_child; + struct lyd_node *trg_child; + + /* merge metadata on the node itself */ + LY_CHECK_RET(lyd_diff_merge_metadata(src_diff, trg_diff)); + + /* merge descendants recursively */ + trg_child = lyd_child(trg_diff); + LY_LIST_FOR(lyd_child(src_diff), src_child) { + if (keys_only && !lysc_is_key(src_child->schema)) { + break; + } + + LY_CHECK_RET(lyd_diff_merge_metadata(src_child, trg_child)); + + trg_child = trg_child->next; + } + + return LY_SUCCESS; +} + /** * @brief Merge sysrepo diff subtree with another diff, recursively. * @@ -1965,14 +2698,14 @@ lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, struct ly_ht *child_dup_inst = NULL; /* get source node operation */ - LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op)); + LY_CHECK_RET(lyd_diff_get_op(src_diff, &src_op, NULL)); /* find an equal node in the current diff */ LY_CHECK_RET(lyd_diff_find_match(diff_parent ? lyd_child_no_keys(diff_parent) : *diff, src_diff, 1, dup_inst, &diff_node)); if (diff_node) { /* get target (current) operation */ - LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op)); + LY_CHECK_RET(lyd_diff_get_op(diff_node, &cur_op, NULL)); /* merge operations */ switch (src_op) { @@ -2003,6 +2736,9 @@ lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, return ret; } + /* merge any metadata */ + LY_CHECK_RET(lyd_diff_merge_metadata_r(src_diff, diff_node, lysc_is_dup_inst_list(src_diff->schema) ? 0 : 1)); + if (diff_cb) { /* call callback */ LY_CHECK_RET(diff_cb(src_diff, diff_node, cb_data)); @@ -2027,13 +2763,8 @@ lyd_diff_merge_r(const struct lyd_node *src_diff, struct lyd_node *diff_parent, } else { add_diff: /* add new diff node with all descendants */ - if ((src_diff->flags & LYD_EXT) && diff_parent) { - LY_CHECK_RET(lyd_dup_single_to_ctx(src_diff, LYD_CTX(diff_parent), (struct lyd_node_inner *)diff_parent, - LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_LYDS, &diff_node)); - } else { - LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, - LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_LYDS, &diff_node)); - } + LY_CHECK_RET(lyd_dup_single(src_diff, (struct lyd_node_inner *)diff_parent, + LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS | LYD_DUP_NO_LYDS, &diff_node)); /* insert node into diff if not already */ if (!diff_parent) { @@ -2108,6 +2839,13 @@ lyd_diff_merge_all(struct lyd_node **diff, const struct lyd_node *src_diff, uint return lyd_diff_merge_module(diff, src_diff, NULL, NULL, NULL, options); } +/** + * @brief Reverse diff value meta by switching it for the node value. + * + * @param[in] node Parent meta node. + * @param[in] mod Meta module. + * @return LY_ERR value. + */ static LY_ERR lyd_diff_reverse_value(struct lyd_node *node, const struct lys_module *mod) { @@ -2149,6 +2887,13 @@ cleanup: return ret; } +/** + * @brief Reverse diff default meta by switching it for the node dflt flag. + * + * @param[in] node Parent meta node. + * @param[in] mod Meta module. + * @return LY_ERR value. + */ static LY_ERR lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod) { @@ -2181,6 +2926,15 @@ lyd_diff_reverse_default(struct lyd_node *node, const struct lys_module *mod) return LY_SUCCESS; } +/** + * @brief Reverse diff meta by switching their values. + * + * @param[in] node Parent meta node. + * @param[in] mod Meta module. + * @param[in] name1 First meta name. + * @param[in] name2 Second meta name. + * @return LY_ERR value. + */ static LY_ERR lyd_diff_reverse_meta(struct lyd_node *node, const struct lys_module *mod, const char *name1, const char *name2) { @@ -2236,11 +2990,86 @@ lyd_diff_reverse_remove_op_r(struct lyd_node *diff, enum lyd_diff_op op) return LY_SUCCESS; } +/** + * @brief Reverse all metadata diff meta. + * + * @param[in,out] diff Diff node with metadata diff to reverse. + * @return LY_ERR value. + */ +static LY_ERR +lyd_diff_reverse_metadata_diff(struct lyd_node *node) +{ + LY_ERR rc = LY_SUCCESS; + struct lyd_meta *m, **meta_create = NULL, **meta_delete = NULL, **meta_replace = NULL, **meta_orig = NULL; + uint32_t i, mc_count = 0, md_count = 0, mr_count = 0, mo_count = 0; + const struct lys_module *mod; + const char *val1; + + mod = ly_ctx_get_module_implemented(LYD_CTX(node), "yang"); + assert(mod); + + /* collect all the metadata so we can safely modify them */ + LY_LIST_FOR(node->meta, m) { + if (m->annotation->module != mod) { + continue; + } + + if (!strcmp(m->name, "meta-create")) { + LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_create, &mc_count), cleanup); + } else if (!strcmp(m->name, "meta-delete")) { + LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_delete, &md_count), cleanup); + } else if (!strcmp(m->name, "meta-replace")) { + LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_replace, &mr_count), cleanup); + } else if (!strcmp(m->name, "meta-orig")) { + LY_CHECK_GOTO(rc = lyd_diff_meta_store(m, &meta_orig, &mo_count), cleanup); + } + } + + /* make sure meta_replace and meta_orig arrays are aligned */ + LY_CHECK_GOTO(rc = lyd_diff_metadata_replace_orig_align(meta_replace, mr_count, meta_orig, mo_count), cleanup); + + /* reverse all the meta-create metadata */ + for (i = 0; i < mc_count; ++i) { + rc = lyd_new_meta(NULL, node, mod, "meta-delete", lyd_get_meta_value(meta_create[i]), 0, NULL); + LY_CHECK_GOTO(rc, cleanup); + lyd_free_meta_single(meta_create[i]); + } + + /* reverse all the meta-replace and meta-orig metadata */ + for (i = 0; i < mr_count; ++i) { + LY_CHECK_GOTO(rc = lydict_dup(LYD_CTX(node), lyd_get_meta_value(meta_replace[i]), &val1), cleanup); + + rc = lyd_change_meta(meta_replace[i], lyd_get_meta_value(meta_orig[i])); + if (rc) { + lydict_remove(LYD_CTX(node), val1); + goto cleanup; + } + + rc = lyd_change_meta(meta_orig[i], val1); + lydict_remove(LYD_CTX(node), val1); + LY_CHECK_GOTO(rc, cleanup); + } + + /* reverse all the meta-delete metadata */ + for (i = 0; i < md_count; ++i) { + rc = lyd_new_meta(NULL, node, mod, "meta-create", lyd_get_meta_value(meta_delete[i]), 0, NULL); + LY_CHECK_GOTO(rc, cleanup); + lyd_free_meta_single(meta_delete[i]); + } + +cleanup: + free(meta_create); + free(meta_delete); + free(meta_replace); + free(meta_orig); + return rc; +} + LIBYANG_API_DEF LY_ERR lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff) { - LY_ERR ret = LY_SUCCESS; - const struct lys_module *mod; + LY_ERR rc = LY_SUCCESS; + const struct lys_module *mod = NULL; struct lyd_node *root, *elem, *iter; enum lyd_diff_op op; @@ -2254,21 +3083,23 @@ lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff) /* duplicate diff */ LY_CHECK_RET(lyd_dup_siblings(src_diff, NULL, LYD_DUP_RECURSIVE | LYD_DUP_NO_LYDS, diff)); - /* find module with metadata needed for later */ - mod = ly_ctx_get_module_latest(LYD_CTX(src_diff), "yang"); - LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); ret = LY_EINT, cleanup); - LY_LIST_FOR(*diff, root) { LYD_TREE_DFS_BEGIN(root, elem) { /* skip all keys */ if (!lysc_is_key(elem->schema)) { + /* find module with metadata needed for later in the current node context */ + if (!mod || (mod->ctx != LYD_CTX(elem))) { + mod = ly_ctx_get_module_latest(LYD_CTX(elem), "yang"); + LY_CHECK_ERR_GOTO(!mod, LOGINT(LYD_CTX(src_diff)); rc = LY_EINT, cleanup); + } + /* find operation attribute, if any */ - LY_CHECK_GOTO(ret = lyd_diff_get_op(elem, &op), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_get_op(elem, &op, NULL), cleanup); switch (op) { case LYD_DIFF_OP_CREATE: /* reverse create to delete */ - LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_change_op(elem, LYD_DIFF_OP_DELETE), cleanup); /* check all the children for the same operation, nothing else is expected */ LY_LIST_FOR(lyd_child(elem), iter) { @@ -2279,7 +3110,7 @@ lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff) break; case LYD_DIFF_OP_DELETE: /* reverse delete to create */ - LY_CHECK_GOTO(ret = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_change_op(elem, LYD_DIFF_OP_CREATE), cleanup); /* check all the children for the same operation, nothing else is expected */ LY_LIST_FOR(lyd_child(elem), iter) { @@ -2292,34 +3123,34 @@ lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff) switch (elem->schema->nodetype) { case LYS_LEAF: /* leaf value change */ - LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup); - LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_reverse_value(elem, mod), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_reverse_default(elem, mod), cleanup); break; case LYS_ANYXML: case LYS_ANYDATA: /* any value change */ - LY_CHECK_GOTO(ret = lyd_diff_reverse_value(elem, mod), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_reverse_value(elem, mod), cleanup); break; case LYS_LEAFLIST: /* leaf-list move */ - LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_reverse_default(elem, mod), cleanup); if (lysc_is_dup_inst_list(elem->schema)) { - LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup); } else { - LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(elem, mod, "orig-value", "value"), cleanup); } break; case LYS_LIST: /* list move */ if (lysc_is_dup_inst_list(elem->schema)) { - LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(elem, mod, "orig-position", "position"), cleanup); } else { - LY_CHECK_GOTO(ret = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_reverse_meta(elem, mod, "orig-key", "key"), cleanup); } break; default: LOGINT(LYD_CTX(src_diff)); - ret = LY_EINT; + rc = LY_EINT; goto cleanup; } break; @@ -2328,7 +3159,7 @@ lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff) case LYS_LEAF: case LYS_LEAFLIST: /* default flag change */ - LY_CHECK_GOTO(ret = lyd_diff_reverse_default(elem, mod), cleanup); + LY_CHECK_GOTO(rc = lyd_diff_reverse_default(elem, mod), cleanup); break; default: /* nothing to do */ @@ -2338,14 +3169,17 @@ lyd_diff_reverse_all(const struct lyd_node *src_diff, struct lyd_node **diff) } } + /* reverse any metadata diff */ + LY_CHECK_GOTO(rc = lyd_diff_reverse_metadata_diff(elem), cleanup); + LYD_TREE_DFS_END(root, elem); } } cleanup: - if (ret) { + if (rc) { lyd_free_siblings(*diff); *diff = NULL; } - return ret; + return rc; } diff --git a/src/diff.h b/src/diff.h index dab1614..b7f3604 100644 --- a/src/diff.h +++ b/src/diff.h @@ -3,7 +3,7 @@ * @author Michal Vasko * @brief internal diff header * - * Copyright (c) 2020 CESNET, z.s.p.o. + * Copyright (c) 2020 - 2025 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,10 +53,11 @@ enum lyd_diff_op { * @param[in] orig_key Original key metadata to set. * @param[in] orig_position Original position metadata to set. * @param[in,out] diff Diff to append to. + * @param[out] diff_node Optional created diff node. * @return LY_ERR value. */ -LIBYANG_API_DECL 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, - struct lyd_node **diff); +LIBYANG_API_DECL 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, struct lyd_node **diff, struct lyd_node **diff_node); #endif /* LY_DIFF_H_ */ diff --git a/src/hash_table.c b/src/hash_table.c index 6da79f2..b5f5f77 100644 --- a/src/hash_table.c +++ b/src/hash_table.c @@ -4,7 +4,7 @@ * @author Michal Vasko * @brief libyang generic hash table implementation * - * Copyright (c) 2015 - 2023 CESNET, z.s.p.o. + * Copyright (c) 2015 - 2025 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. @@ -26,9 +26,20 @@ #include "log.h" #include "ly_common.h" +#ifdef LY_XXHASH_SUPPORT +# include +#endif + LIBYANG_API_DEF uint32_t lyht_hash_multi(uint32_t hash, const char *key_part, size_t len) { +#ifdef LY_XXHASH_SUPPORT + if (key_part && len) { + return XXH3_64bits_withSeed(key_part, len, hash); + } + + return XXH3_64bits_withSeed(NULL, 0, hash); +#else uint32_t i; if (key_part && len) { @@ -44,15 +55,20 @@ lyht_hash_multi(uint32_t hash, const char *key_part, size_t len) } return hash; +#endif } LIBYANG_API_DEF uint32_t lyht_hash(const char *key, size_t len) { +#ifdef LY_XXHASH_SUPPORT + return XXH3_64bits(key, len); +#else uint32_t hash; hash = lyht_hash_multi(0, key, len); return lyht_hash_multi(hash, NULL, len); +#endif } static LY_ERR diff --git a/src/hash_table.h b/src/hash_table.h index a0cd7b6..e318393 100644 --- a/src/hash_table.h +++ b/src/hash_table.h @@ -54,6 +54,8 @@ LIBYANG_API_DECL uint32_t lyht_hash_multi(uint32_t hash, const char *key_part, s * * Spooky hash is faster, but it works only for little endian architectures. * + * Uses XXH3_64bits internally if xxhash is available. See https://xxhash.com + * * @param[in] key Key to hash. * @param[in] len Length of @p key. * @return Hash of the key. diff --git a/src/log.c b/src/log.c index 12d5753..13da500 100644 --- a/src/log.c +++ b/src/log.c @@ -579,6 +579,38 @@ log_stderr_path_line(const char *data_path, const char *schema_path, uint64_t li fprintf(stderr, par ? ")\n" : "\n"); } +/** + * @brief Learn whether a log is a no-operation or must be produced, based on current ly_log_opts. + * + * @param[in] level Message log level to compare to enabled logging level. + * @param[out] will_log Optionally learn whether the log will be printed. + * @param[out] will_store Optionally learn whether the log will be stored. + * @return 1 if the log is a no-operation, 0 otherwise. + */ +static ly_bool +log_is_noop(LY_LOG_LEVEL level, ly_bool *will_log, ly_bool *will_store) +{ + ly_bool lolog, lostore; + + /* learn effective logger options */ + if (temp_ly_log_opts) { + lolog = *temp_ly_log_opts & LY_LOLOG; + lostore = *temp_ly_log_opts & LY_LOSTORE; + } else { + lolog = ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOLOG; + lostore = ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE; + } + + if (will_log) { + *will_log = lolog; + } + if (will_store) { + *will_store = lostore; + } + + return (level > ATOMIC_LOAD_RELAXED(ly_ll)) || (!lolog && !lostore); +} + /** * @brief Log a message. * @@ -601,16 +633,7 @@ log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR err, LY_VECODE const char *msg; ly_bool free_strs = 1, lolog, lostore; - /* learn effective logger options */ - if (temp_ly_log_opts) { - lolog = *temp_ly_log_opts & LY_LOLOG; - lostore = *temp_ly_log_opts & LY_LOSTORE; - } else { - lolog = ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOLOG; - lostore = ATOMIC_LOAD_RELAXED(ly_log_opts) & LY_LOSTORE; - } - - if (level > ATOMIC_LOAD_RELAXED(ly_ll)) { + if (log_is_noop(level, &lolog, &lostore)) { /* do not print or store the message */ goto cleanup; } @@ -858,6 +881,10 @@ ly_vlog(const struct ly_ctx *ctx, const char *apptag, LY_VECODE code, const char char *data_path = NULL, *schema_path = NULL; uint64_t line = 0; + if (log_is_noop(LY_LLERR, NULL, NULL)) { + return; + } + if (ctx) { ly_vlog_build_path_line(ctx, &data_path, &schema_path, &line); } @@ -887,9 +914,10 @@ ly_ext_log(const struct ly_ctx *ctx, const char *plugin_name, LY_LOG_LEVEL level { char *plugin_msg; - if (ATOMIC_LOAD_RELAXED(ly_ll) < level) { + if (log_is_noop(level, NULL, NULL)) { return; } + if (asprintf(&plugin_msg, "Ext plugin \"%s\": %s", plugin_name, format) == -1) { LOGMEM(ctx); return; @@ -987,6 +1015,10 @@ _ly_err_print(const struct ly_ctx *ctx, const struct ly_err_item *eitem, const c LY_CHECK_ARG_RET(ctx, eitem, ); + if (log_is_noop(eitem->level, NULL, NULL)) { + return; + } + if (eitem->data_path) { data_path = strdup(eitem->data_path); } diff --git a/src/ly_common.h b/src/ly_common.h index 51aba57..79dc4df 100644 --- a/src/ly_common.h +++ b/src/ly_common.h @@ -154,7 +154,7 @@ const struct lyd_node *ly_log_location_dnode(uint32_t idx); uint32_t ly_log_location_dnode_count(void); /** - * @brief Update location schema/data nodes for logger, not provided arguments (NULLs) are kept (does not override). + * @brief Update location schema/data nodes for logger. If both NULL, root data node is added. * * @param[in] SCNODE Compiled schema node. * @param[in] DNODE Data node. @@ -232,12 +232,12 @@ void ly_log_dbg(uint32_t group, const char *format, ...); #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;} -#define LY_CHECK_CTX_EQUAL_RET3(CTX1, CTX2, CTX3, RETVAL) LY_CHECK_CTX_EQUAL_RET2(CTX1, CTX2, RETVAL); \ - LY_CHECK_CTX_EQUAL_RET2(CTX2, CTX3, RETVAL); LY_CHECK_CTX_EQUAL_RET2(CTX1, CTX3, RETVAL) -#define LY_CHECK_CTX_EQUAL_RET(CTX, ...) GETMACRO3(__VA_ARGS__, LY_CHECK_CTX_EQUAL_RET3, LY_CHECK_CTX_EQUAL_RET2, \ - DUMMY) (CTX, __VA_ARGS__) +#define LY_CHECK_CTX_EQUAL_RET2(FUNC, CTX1, CTX2, RETVAL) if ((CTX1) && (CTX2) && ((CTX1) != (CTX2))) \ + {LOGERR(CTX1, LY_EINVAL, "Different contexts mixed in a \"%s\" function call.", FUNC); return RETVAL;} +#define LY_CHECK_CTX_EQUAL_RET3(FUNC, CTX1, CTX2, CTX3, RETVAL) LY_CHECK_CTX_EQUAL_RET2(FUNC, CTX1, CTX2, RETVAL); \ + LY_CHECK_CTX_EQUAL_RET2(FUNC, CTX2, CTX3, RETVAL); LY_CHECK_CTX_EQUAL_RET2(FUNC, CTX1, CTX3, RETVAL) +#define LY_CHECK_CTX_EQUAL_RET(FUNC, CTX, ...) GETMACRO3(__VA_ARGS__, LY_CHECK_CTX_EQUAL_RET3, LY_CHECK_CTX_EQUAL_RET2, \ + DUMMY) (FUNC, CTX, __VA_ARGS__) /* count sequence size for LY_VCODE_INCHILDSTMT validation error code */ int LY_VCODE_INSTREXP_len(const char *str); diff --git a/src/parser_data.h b/src/parser_data.h index d7fbe18..ddb2278 100644 --- a/src/parser_data.h +++ b/src/parser_data.h @@ -179,7 +179,10 @@ struct ly_in; #define LYD_PARSE_JSON_NULL 0x4000000 /**< Allow using JSON empty value 'null' within JSON input, such nodes are silently skipped and treated as non-existent. By default, such values are invalid. */ - +#define LYD_PARSE_JSON_STRING_DATATYPES 0x8000000 /**< By default, JSON data values are expected to be in the correct + format according to RFC 7951 based on their type. Using this + option the validation can be softened to accept boolean and + number type values enclosed in quotes. */ #define LYD_PARSE_OPTS_MASK 0xFFFF0000 /**< Mask for all the LYD_PARSE_ options. */ /** @} dataparseroptions */ diff --git a/src/parser_json.c b/src/parser_json.c index 5c31712..b4d5487 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -340,7 +340,7 @@ 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] lydctx JSON data parser context. * @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. @@ -348,8 +348,10 @@ cleanup: * @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) +lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p) { + struct lyjson_ctx *jsonctx = lydctx->jsonctx; + *type_hint_p = 0; if (*status_p == LYJSON_ARRAY) { @@ -383,6 +385,10 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s return LY_EINVAL; } + if (lydctx->parse_opts & LYD_PARSE_JSON_STRING_DATATYPES) { + *type_hint_p |= LYD_VALHINT_STRING_DATATYPES; + } + return LY_SUCCESS; } @@ -391,15 +397,16 @@ lydjson_value_type_hint(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *s * * Checks for all the list's keys. Function does not revert the context state. * - * @param[in] jsonctx JSON parser context. + * @param[in] lydctx JSON data parser context. * @param[in] list List schema node corresponding to the input data object. * @return LY_SUCCESS in case the data are ok for the @p list * @return LY_ENOT in case the input data are not sufficient to fully parse the list instance. */ static LY_ERR -lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list) +lydjson_check_list(struct lyd_json_ctx *lydctx, const struct lysc_node *list) { LY_ERR rc = LY_SUCCESS; + struct lyjson_ctx *jsonctx = lydctx->jsonctx; enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(jsonctx); struct ly_set key_set = {0}; const struct lysc_node *snode; @@ -451,7 +458,7 @@ lydjson_check_list(struct lyjson_ctx *jsonctx, const struct lysc_node *list) goto cleanup; } - rc = lydjson_value_type_hint(jsonctx, &status, &hints); + rc = lydjson_value_type_hint(lydctx, &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); @@ -521,7 +528,7 @@ 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 */ - if ((ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p))) { + if ((ret = lydjson_value_type_hint(lydctx, &status, type_hint_p))) { break; } @@ -533,14 +540,14 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno break; case LYS_LIST: /* lists may not have all its keys */ - if (lydjson_check_list(jsonctx, snode)) { + if (lydjson_check_list(lydctx, snode)) { /* invalid list, parse as opaque if it misses/has invalid some keys */ ret = LY_ENOT; } break; } } else if (snode->nodetype & LYD_NODE_TERM) { - ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p); + ret = lydjson_value_type_hint(lydctx, &status, type_hint_p); } /* restore parser */ @@ -852,7 +859,7 @@ next_entry: LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup); /* get value hints */ - LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx->jsonctx, &status, &val_hints), cleanup); + LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx, &status, &val_hints), cleanup); if (node->schema) { /* create metadata */ @@ -981,7 +988,7 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l dynamic = lydctx->jsonctx->dynamic; lydctx->jsonctx->dynamic = 0; - LY_CHECK_RET(lydjson_value_type_hint(lydctx->jsonctx, status_inner_p, &type_hint)); + LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint)); } /* get the module name */ diff --git a/src/parser_xml.c b/src/parser_xml.c index 6a17c7d..03d5f5b 100644 --- a/src/parser_xml.c +++ b/src/parser_xml.c @@ -4,7 +4,7 @@ * @author Michal Vasko * @brief XML data parser for libyang * - * Copyright (c) 2019 - 2022 CESNET, z.s.p.o. + * Copyright (c) 2019 - 2025 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. @@ -1392,396 +1392,10 @@ cleanup: return rc; } -/** - * @brief Parse an XML element as an opaque node subtree. - * - * @param[in] xmlctx XML parser context. - * @param[in] parent Parent to append nodes to. - * @return LY_ERR value. - */ -static LY_ERR -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 *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); - - name = xmlctx->name; - name_len = xmlctx->name_len; - prefix = xmlctx->prefix; - prefix_len = xmlctx->prefix_len; - ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len); - if (!ns) { - lydxml_log_namespace_err(xmlctx, prefix, prefix_len, NULL, 0); - return LY_EVALID; - } - - LY_CHECK_RET(lyxml_ctx_next(xmlctx)); - - /* create attributes */ - if (xmlctx->status == LYXML_ATTRIBUTE) { - LY_CHECK_RET(lydxml_attrs(xmlctx, &attr)); - } - - /* remember the value */ - assert(xmlctx->status == LYXML_ELEM_CONTENT); - 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 *)node)->attr = attr; - attr = NULL; - - /* parser next element */ - LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup); - - /* parse all the descendants */ - while (xmlctx->status == LYXML_ELEMENT) { - rc = lydxml_opaq_r(xmlctx, node); - LY_CHECK_GOTO(rc, cleanup); - } - - /* insert */ - lyd_insert_node(parent, NULL, node, LYD_INSERT_NODE_LAST); - - /* 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(node); - } - return rc; -} - -/** - * @brief Parse all expected non-data XML elements of the error-info element in NETCONF rpc-reply message. - * - * @param[in] xmlctx XML parser context. - * @param[in] parent Parent to append nodes to. - * @return LY_ERR value. - */ -static LY_ERR -lydxml_env_netconf_rpc_reply_error_info(struct lyxml_ctx *xmlctx, struct lyd_node *parent) -{ - LY_ERR r; - struct lyd_node *child, *iter; - ly_bool no_dup; - - /* there must be some child */ - if (xmlctx->status == LYXML_ELEM_CLOSE) { - LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Missing child elements of \"error-info\"."); - return LY_EVALID; - } - - while (xmlctx->status == LYXML_ELEMENT) { - child = NULL; - - /* - * session-id - */ - r = lydxml_envelope(xmlctx, "session-id", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child); - if (r == LY_SUCCESS) { - no_dup = 1; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - /* - * bad-attribute - */ - r = lydxml_envelope(xmlctx, "bad-attribute", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child); - if (r == LY_SUCCESS) { - no_dup = 1; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - /* - * bad-element - */ - r = lydxml_envelope(xmlctx, "bad-element", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child); - if (r == LY_SUCCESS) { - no_dup = 1; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - /* - * bad-namespace - */ - r = lydxml_envelope(xmlctx, "bad-namespace", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child); - if (r == LY_SUCCESS) { - no_dup = 1; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - if (r == LY_ENOT) { - assert(xmlctx->status == LYXML_ELEMENT); - - /* custom elements, parse all the siblings */ - while (xmlctx->status == LYXML_ELEMENT) { - LY_CHECK_GOTO(r = lydxml_opaq_r(xmlctx, parent), error); - LY_CHECK_GOTO(r = lyxml_ctx_next(xmlctx), error); - } - continue; - } - -check_child: - /* check for duplicates */ - if (no_dup) { - LY_LIST_FOR(lyd_child(parent), iter) { - if ((((struct lyd_node_opaq *)iter)->name.name == ((struct lyd_node_opaq *)child)->name.name) && - (((struct lyd_node_opaq *)iter)->name.module_ns == ((struct lyd_node_opaq *)child)->name.module_ns)) { - LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Duplicate element \"%s\" in \"error-info\".", - ((struct lyd_node_opaq *)child)->name.name); - r = LY_EVALID; - goto error; - } - } - } - - /* finish child parsing */ - if (xmlctx->status != LYXML_ELEM_CLOSE) { - assert(xmlctx->status == LYXML_ELEMENT); - LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"error-info\".", - (int)xmlctx->name_len, xmlctx->name); - r = LY_EVALID; - goto error; - } - LY_CHECK_GOTO(r = lyxml_ctx_next(xmlctx), error); - - /* insert */ - lyd_insert_node(parent, NULL, child, LYD_INSERT_NODE_LAST); - } - - return LY_SUCCESS; - -error: - lyd_free_tree(child); - return r; -} - -/** - * @brief Parse all expected non-data XML elements of the rpc-error element in NETCONF rpc-reply message. - * - * @param[in] xmlctx XML parser context. - * @param[in] parent Parent to append nodes to. - * @return LY_ERR value. - */ -static LY_ERR -lydxml_env_netconf_rpc_reply_error(struct lyxml_ctx *xmlctx, struct lyd_node *parent) -{ - LY_ERR r; - struct lyd_node *child, *iter; - const char *val; - ly_bool no_dup; - - /* there must be some child */ - if (xmlctx->status == LYXML_ELEM_CLOSE) { - LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Missing child elements of \"rpc-error\"."); - return LY_EVALID; - } - - while (xmlctx->status == LYXML_ELEMENT) { - child = NULL; - - /* - * error-type - */ - r = lydxml_envelope(xmlctx, "error-type", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child); - if (r == LY_SUCCESS) { - val = ((struct lyd_node_opaq *)child)->value; - if (strcmp(val, "transport") && strcmp(val, "rpc") && strcmp(val, "protocol") && strcmp(val, "application")) { - LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Invalid value \"%s\" of element \"%s\".", val, - ((struct lyd_node_opaq *)child)->name.name); - r = LY_EVALID; - goto error; - } - - no_dup = 1; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - /* - * error-tag - */ - r = lydxml_envelope(xmlctx, "error-tag", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child); - if (r == LY_SUCCESS) { - val = ((struct lyd_node_opaq *)child)->value; - if (strcmp(val, "in-use") && strcmp(val, "invalid-value") && strcmp(val, "too-big") && - strcmp(val, "missing-attribute") && strcmp(val, "bad-attribute") && - strcmp(val, "unknown-attribute") && strcmp(val, "missing-element") && strcmp(val, "bad-element") && - strcmp(val, "unknown-element") && strcmp(val, "unknown-namespace") && strcmp(val, "access-denied") && - strcmp(val, "lock-denied") && strcmp(val, "resource-denied") && strcmp(val, "rollback-failed") && - strcmp(val, "data-exists") && strcmp(val, "data-missing") && strcmp(val, "operation-not-supported") && - strcmp(val, "operation-failed") && strcmp(val, "malformed-message")) { - LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Invalid value \"%s\" of element \"%s\".", val, - ((struct lyd_node_opaq *)child)->name.name); - r = LY_EVALID; - goto error; - } - - no_dup = 1; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - /* - * error-severity - */ - r = lydxml_envelope(xmlctx, "error-severity", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child); - if (r == LY_SUCCESS) { - val = ((struct lyd_node_opaq *)child)->value; - if (strcmp(val, "error") && strcmp(val, "warning")) { - LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Invalid value \"%s\" of element \"%s\".", val, - ((struct lyd_node_opaq *)child)->name.name); - r = LY_EVALID; - goto error; - } - - no_dup = 1; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - /* - * error-app-tag - */ - r = lydxml_envelope(xmlctx, "error-app-tag", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child); - if (r == LY_SUCCESS) { - no_dup = 1; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - /* - * error-path - */ - r = lydxml_envelope(xmlctx, "error-path", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child); - if (r == LY_SUCCESS) { - no_dup = 1; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - /* - * error-message - */ - r = lydxml_envelope(xmlctx, "error-message", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child); - if (r == LY_SUCCESS) { - no_dup = 1; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - /* error-info */ - r = lydxml_envelope(xmlctx, "error-info", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, &child); - if (r == LY_SUCCESS) { - /* parse all the descendants */ - LY_CHECK_GOTO(r = lydxml_env_netconf_rpc_reply_error_info(xmlctx, child), error); - - no_dup = 0; - goto check_child; - } else if (r != LY_ENOT) { - goto error; - } - - if (r == LY_ENOT) { - assert(xmlctx->status == LYXML_ELEMENT); - LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"rpc-error\".", - (int)xmlctx->name_len, xmlctx->name); - r = LY_EVALID; - goto error; - } - -check_child: - /* check for duplicates */ - if (no_dup) { - LY_LIST_FOR(lyd_child(parent), iter) { - if ((((struct lyd_node_opaq *)iter)->name.name == ((struct lyd_node_opaq *)child)->name.name) && - (((struct lyd_node_opaq *)iter)->name.module_ns == ((struct lyd_node_opaq *)child)->name.module_ns)) { - LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Duplicate element \"%s\" in \"rpc-error\".", - ((struct lyd_node_opaq *)child)->name.name); - r = LY_EVALID; - goto error; - } - } - } - - /* finish child parsing */ - if (xmlctx->status != LYXML_ELEM_CLOSE) { - assert(xmlctx->status == LYXML_ELEMENT); - LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"rpc-error\".", - (int)xmlctx->name_len, xmlctx->name); - r = LY_EVALID; - goto error; - } - LY_CHECK_GOTO(r = lyxml_ctx_next(xmlctx), error); - - /* insert */ - lyd_insert_node(parent, NULL, child, LYD_INSERT_NODE_LAST); - } - - return LY_SUCCESS; - -error: - lyd_free_tree(child); - return r; -} - /** * @brief Parse all expected non-data XML elements of a NETCONF rpc-reply message. * - * @param[in] xmlctx XML parser context. + * @param[in] lydctx XML YANG data 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. @@ -1789,14 +1403,19 @@ error: * @return LY_ERR value on error. */ static LY_ERR -lydxml_env_netconf_reply(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem) +lydxml_env_netconf_reply(struct lyd_xml_ctx *lydctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem) { LY_ERR rc = LY_SUCCESS, r; + struct lyxml_ctx *xmlctx = lydctx->xmlctx; struct lyd_node *child = NULL; const char *parsed_elem = NULL; assert(envp && !*envp); + /* not all nodes are represented as internal schema nodes and there may even be custom additional nodes */ + lydctx->parse_opts &= ~LYD_PARSE_STRICT; + lydctx->parse_opts |= LYD_PARSE_OPAQ; + /* 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); @@ -1833,24 +1452,8 @@ lydxml_env_netconf_reply(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint3 } /* try to parse all "rpc-error" elements */ - while (xmlctx->status == LYXML_ELEMENT) { - r = lydxml_envelope(xmlctx, "rpc-error", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, &child); - if (r == LY_ENOT) { - break; - } else if (r) { - rc = r; - goto cleanup; - } - - /* insert */ - lyd_insert_node(*envp, NULL, child, LYD_INSERT_NODE_LAST); - - /* parse all children of "rpc-error" */ - LY_CHECK_GOTO(rc = lydxml_env_netconf_rpc_reply_error(xmlctx, child), cleanup); - - /* finish child parsing */ - assert(xmlctx->status == LYXML_ELEM_CLOSE); - LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup); + while ((xmlctx->status == LYXML_ELEMENT) && !ly_strncmp("rpc-error", xmlctx->name, xmlctx->name_len)) { + LY_CHECK_GOTO(rc = lydxml_subtree_r(lydctx, *envp, lyd_node_child_p(*envp), NULL), cleanup); parsed_elem = "rpc-error"; } @@ -1929,7 +1532,7 @@ lyd_parse_xml_netconf(const struct ly_ctx *ctx, const struct lysc_ext_instance * break; case LYD_TYPE_REPLY_NETCONF: assert(parent); - rc = lydxml_env_netconf_reply(lydctx->xmlctx, envp, &int_opts, &close_elem); + rc = lydxml_env_netconf_reply(lydctx, envp, &int_opts, &close_elem); if (rc == LY_ENOT) { LOGVAL(ctx, LYVE_DATA, "Missing NETCONF envelope or in incorrect namespace."); } diff --git a/src/parser_yang.c b/src/parser_yang.c index 57b5f9b..fcf055d 100644 --- a/src/parser_yang.c +++ b/src/parser_yang.c @@ -100,7 +100,7 @@ struct lys_glob_unres; goto ERR_LABEL; \ } \ if (KW == LY_STMT_SYNTAX_RIGHT_BRACE) { \ - if (EXTS && (RET = ly_set_add(&(CTX)->main_ctx->ext_inst, (EXTS), 1, NULL))) { \ + if (EXTS && (RET = ly_set_add(&(CTX)->ext_inst, (EXTS), 1, NULL))) { \ goto ERR_LABEL; \ } \ __loop_end = 1; \ @@ -417,13 +417,11 @@ read_qstring(struct lysp_yang_ctx *ctx, enum yang_arg arg, char **word_p, char * break; case '\r': /* 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; - } + if ((ctx->in->current[1] == '\n') || !strncmp(&ctx->in->current[1], "\\n", 2)) { + /* skip this character, do not store it */ + ++ctx->in->current; + } /* else just store '\n' instead */ - /* skip this character, do not store it */ - ++ctx->in->current; /* fallthrough */ case '\n': if (block_indent) { @@ -437,9 +435,20 @@ read_qstring(struct lysp_yang_ctx *ctx, enum yang_arg arg, char **word_p, char * current_indent = 0; } + c = NULL; + if (ctx->in->current[0] != '\n') { + /* storing '\r' as '\n' */ + c = ctx->in->current; + ctx->in->current = "\n"; + } + /* check and store character */ LY_CHECK_RET(buf_store_char(ctx, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix)); + if (c) { + ctx->in->current = c + 1; + } + /* reset context indentation counter for possible string after this one */ ctx->indent = 0; trailing_ws = 0; @@ -2833,8 +2842,7 @@ parse_typedef(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_t /* store data for collision check */ if (parent) { - assert(ctx->main_ctx); - LY_CHECK_RET(ly_set_add(&ctx->main_ctx->tpdfs_nodes, parent, 0, NULL)); + LY_CHECK_RET(ly_set_add(&ctx->tpdfs_nodes, parent, 0, NULL)); } cleanup: @@ -3175,8 +3183,7 @@ parse_grouping(struct lysp_yang_ctx *ctx, struct lysp_node *parent, struct lysp_ /* store data for collision check */ if (parent) { - assert(ctx->main_ctx); - LY_CHECK_RET(ly_set_add(&ctx->main_ctx->grps_nodes, parent, 0, NULL)); + LY_CHECK_RET(ly_set_add(&ctx->grps_nodes, parent, 0, NULL)); } cleanup: diff --git a/src/parser_yin.c b/src/parser_yin.c index eab9c37..a280423 100644 --- a/src/parser_yin.c +++ b/src/parser_yin.c @@ -646,7 +646,7 @@ yin_unres_exts_add(struct lysp_yin_ctx *ctx, struct lysp_ext_instance *exts) return LY_SUCCESS; } - return ly_set_add(&ctx->main_ctx->ext_inst, exts, 1, NULL); + return ly_set_add(&ctx->ext_inst, exts, 1, NULL); } /** @@ -1713,8 +1713,7 @@ yin_parse_typedef(struct lysp_yin_ctx *ctx, struct tree_node_meta *typedef_meta) /* store data for collision check */ if (typedef_meta->parent) { - assert(ctx->main_ctx); - LY_CHECK_RET(ly_set_add(&ctx->main_ctx->tpdfs_nodes, typedef_meta->parent, 0, NULL)); + LY_CHECK_RET(ly_set_add(&ctx->tpdfs_nodes, typedef_meta->parent, 0, NULL)); } return LY_SUCCESS; @@ -2472,8 +2471,7 @@ yin_parse_grouping(struct lysp_yin_ctx *ctx, struct tree_node_meta *gr_meta) /* store data for collision check */ if (!ret && grp->parent) { - assert(ctx->main_ctx); - LY_CHECK_RET(ly_set_add(&ctx->main_ctx->grps_nodes, grp->parent, 0, NULL)); + LY_CHECK_RET(ly_set_add(&ctx->grps_nodes, grp->parent, 0, NULL)); } return ret; diff --git a/src/path.c b/src/path.c index f173b17..d3cea0d 100644 --- a/src/path.c +++ b/src/path.c @@ -3,7 +3,7 @@ * @author Michal Vasko * @brief Path functions * - * Copyright (c) 2020 - 2023 CESNET, z.s.p.o. + * Copyright (c) 2020 - 2025 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. @@ -339,6 +339,17 @@ ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const LOG_LOCSET(ctx_node, NULL); } + if (!path_len) { + path_len = strlen(str_path); + } + + /* check if path begins with '/' if expected to and fail early if not */ + if ((begin == LY_PATH_BEGIN_ABSOLUTE) && (str_path[0] != '/')) { + LOGVAL(ctx, LYVE_XPATH, "XPath \"%.*s\" was expected to be absolute.", (int)path_len, str_path); + ret = LY_EVALID; + goto 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; @@ -499,7 +510,7 @@ error: * @brief Parse NameTest and get the corresponding schema node. * * @param[in] ctx libyang context. - * @param[in] cur_node Optional current (original context) node. + * @param[in] cur_node Current (original context) node. * @param[in] cur_mod Current module of the path (where the path is "instantiated"). Needed for ::LY_VALUE_SCHEMA * and ::LY_VALUE_SCHEMA_RESOLVED. * @param[in] prev_ctx_node Previous context node. @@ -519,7 +530,7 @@ ly_path_compile_snode(const struct ly_ctx *ctx, const struct lysc_node *cur_node void *prefix_data, const struct lysc_ext_instance *top_ext, uint32_t getnext_opts, const struct lysc_node **snode, struct lysc_ext_instance **ext) { - LY_ERR ret; + LY_ERR rc = LY_SUCCESS, r; const struct lys_module *mod = NULL; struct lysc_ext_instance *e = NULL; const char *pref, *name; @@ -551,39 +562,37 @@ ly_path_compile_snode(const struct ly_ctx *ctx, const struct lysc_node *cur_node /* find node module */ if (pref) { - if (cur_node) { - LOG_LOCSET(cur_node, NULL); - } - mod = ly_resolve_prefix(prev_ctx_node ? prev_ctx_node->module->ctx : ctx, pref, len, format, prefix_data); if ((!mod || !mod->implemented) && prev_ctx_node) { /* check for nested ext data */ - ret = ly_nested_ext_schema(NULL, prev_ctx_node, pref, len, format, prefix_data, name, name_len, snode, &e); - if (!ret) { - goto success; - } else if (ret != LY_ENOT) { - goto error; + r = ly_nested_ext_schema(NULL, prev_ctx_node, pref, len, format, prefix_data, name, name_len, snode, &e); + if (!r) { + goto cleanup; + } else if (r != LY_ENOT) { + rc = r; + goto cleanup; } } if (!mod) { - LOGVAL(ctx, LYVE_XPATH, "No module connected with the prefix \"%.*s\" found (prefix format %s).", - (int)len, pref, ly_format2str(format)); - ret = LY_EVALID; - goto error; + LOGVAL_PATH(ctx, cur_node, prev_ctx_node, LYVE_XPATH, + "No module connected with the prefix \"%.*s\" found (prefix format %s).", (int)len, pref, + ly_format2str(format)); + rc = LY_EVALID; + goto cleanup; } else if (!mod->implemented) { - LOGVAL(ctx, LYVE_XPATH, "Not implemented module \"%s\" in path.", mod->name); - ret = LY_EVALID; - goto error; + LOGVAL_PATH(ctx, cur_node, prev_ctx_node, LYVE_XPATH, "Not implemented module \"%s\" in path.", mod->name); + rc = LY_EVALID; + goto cleanup; } - - LOG_LOCBACK(cur_node ? 1 : 0, 0); } else { switch (format) { case LY_VALUE_SCHEMA: case LY_VALUE_SCHEMA_RESOLVED: if (!cur_mod) { - LOGINT_RET(ctx); + LOGINT(ctx); + rc = LY_EINT; + goto cleanup; } /* use current module */ mod = cur_mod; @@ -591,7 +600,9 @@ ly_path_compile_snode(const struct ly_ctx *ctx, const struct lysc_node *cur_node case LY_VALUE_JSON: case LY_VALUE_LYB: if (!prev_ctx_node) { - LOGINT_RET(ctx); + LOGINT(ctx); + rc = LY_EINT; + goto cleanup; } /* inherit module of the previous node */ mod = prev_ctx_node->module; @@ -600,7 +611,9 @@ ly_path_compile_snode(const struct ly_ctx *ctx, const struct lysc_node *cur_node case LY_VALUE_XML: case LY_VALUE_STR_NS: /* not really defined or accepted */ - LOGINT_RET(ctx); + LOGINT(ctx); + rc = LY_EINT; + goto cleanup; } } @@ -610,24 +623,21 @@ ly_path_compile_snode(const struct ly_ctx *ctx, const struct lysc_node *cur_node } else { *snode = lys_find_child(prev_ctx_node, mod, name, name_len, 0, getnext_opts); if (!(*snode) && prev_ctx_node) { - ret = ly_nested_ext_schema(NULL, prev_ctx_node, pref, len, format, prefix_data, name, name_len, snode, &e); - LY_CHECK_RET(ret && (ret != LY_ENOT), ret); + r = ly_nested_ext_schema(NULL, prev_ctx_node, pref, len, format, prefix_data, name, name_len, snode, &e); + LY_CHECK_ERR_GOTO(r && (r != LY_ENOT), rc = r, cleanup); } } if (!(*snode)) { - LOGVAL(ctx, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name); - return LY_ENOTFOUND; + LOGVAL_PATH(ctx, cur_node, prev_ctx_node, LYVE_XPATH, "Not found node \"%.*s\" in path.", (int)name_len, name); + rc = LY_ENOTFOUND; + goto cleanup; } -success: - if (ext) { +cleanup: + if (!rc && ext) { *ext = e; } - return LY_SUCCESS; - -error: - LOG_LOCBACK(cur_node ? 1 : 0, 0); - return ret; + return rc; } LY_ERR @@ -635,7 +645,7 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_ 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) { - LY_ERR ret = LY_SUCCESS; + LY_ERR rc = LY_SUCCESS; struct ly_path_predicate *p; const struct lysc_node *key; const char *val; @@ -643,10 +653,6 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_ assert(ctx && ctx_node); - if (cur_node) { - LOG_LOCSET(cur_node, NULL); - } - *predicates = NULL; if (lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)) { @@ -656,31 +662,32 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_ if (expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST) { if (ctx_node->nodetype != LYS_LIST) { - LOGVAL(ctx, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } else if (ctx_node->flags & LYS_KEYLESS) { - LOGVAL(ctx, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } do { /* NameTest, find the key */ - LY_CHECK_RET(ly_path_compile_snode(ctx, cur_node, cur_mod, ctx_node, expr, *tok_idx, format, prefix_data, - NULL, 0, &key, NULL)); + rc = ly_path_compile_snode(ctx, cur_node, cur_mod, ctx_node, expr, *tok_idx, format, prefix_data, NULL, 0, + &key, NULL); + LY_CHECK_GOTO(rc, cleanup); if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) { - LOGVAL(ctx, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.", lys_nodetype2str(key->nodetype), - key->name); - ret = LY_EVALID; + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.", + lys_nodetype2str(key->nodetype), key->name); + rc = LY_EVALID; goto cleanup; } ++(*tok_idx); /* new predicate */ - LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup); + LY_ARRAY_NEW_GOTO(ctx, *predicates, p, rc, cleanup); p->key = key; /* '=' */ @@ -691,7 +698,7 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_ 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); + LY_CHECK_ERR_GOTO(!p->variable, LOGMEM(ctx); rc = LY_EMEM, cleanup); p->type = LY_PATH_PREDTYPE_LIST_VAR; ++(*tok_idx); @@ -708,10 +715,10 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_ /* store the value */ LOG_LOCSET(key, NULL); - ret = lyd_value_store(ctx_node->module->ctx, &p->value, ((struct lysc_node_leaf *)key)->type, val, val_len, 0, 0, + rc = lyd_value_store(ctx_node->module->ctx, &p->value, ((struct lysc_node_leaf *)key)->type, val, val_len, 0, 0, NULL, format, prefix_data, LYD_HINT_DATA, key, NULL); LOG_LOCBACK(1, 0); - LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup); + LY_CHECK_ERR_GOTO(rc, 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); @@ -734,23 +741,23 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_ } if (LY_ARRAY_COUNT(*predicates) != key_count) { /* names (keys) are unique - it was checked when parsing */ - LOGVAL(ctx, LYVE_XPATH, "Predicate missing for a key of %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "Predicate missing for a key of %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } } else if (expr->tokens[*tok_idx] == LYXP_TOKEN_DOT) { if (ctx_node->nodetype != LYS_LEAFLIST) { - LOGVAL(ctx, LYVE_XPATH, "Leaf-list predicate defined for %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "Leaf-list predicate defined for %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } ++(*tok_idx); /* new predicate */ - LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup); + LY_ARRAY_NEW_GOTO(ctx, *predicates, p, rc, cleanup); p->type = LY_PATH_PREDTYPE_LEAFLIST; /* '=' */ @@ -769,11 +776,9 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_ } /* store the value */ - LOG_LOCSET(ctx_node, NULL); - ret = lyd_value_store(ctx_node->module->ctx, &p->value, ((struct lysc_node_leaflist *)ctx_node)->type, val, val_len, 0, 0, + rc = lyd_value_store(ctx_node->module->ctx, &p->value, ((struct lysc_node_leaflist *)ctx_node)->type, val, val_len, 0, 0, NULL, format, prefix_data, LYD_HINT_DATA, ctx_node, NULL); - LOG_LOCBACK(1, 0); - LY_CHECK_ERR_GOTO(ret, p->value.realtype = NULL, cleanup); + LY_CHECK_ERR_GOTO(rc, p->value.realtype = NULL, cleanup); ++(*tok_idx); /* "allocate" the type to avoid problems when freeing the value after the type was freed */ @@ -785,19 +790,19 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_ } else { assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NUMBER); if (!(ctx_node->nodetype & (LYS_LEAFLIST | LYS_LIST))) { - ret = LY_EVALID; - LOGVAL(ctx, LYVE_XPATH, "Positional predicate defined for %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "Positional predicate defined for %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name); + rc = LY_EVALID; goto cleanup; } else if (ctx_node->flags & LYS_CONFIG_W) { - ret = LY_EVALID; - LOGVAL(ctx, LYVE_XPATH, "Positional predicate defined for configuration %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "Positional predicate defined for configuration %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name); + rc = LY_EVALID; goto cleanup; } /* new predicate */ - LY_ARRAY_NEW_GOTO(ctx, *predicates, p, ret, cleanup); + LY_ARRAY_NEW_GOTO(ctx, *predicates, p, rc, cleanup); p->type = LY_PATH_PREDTYPE_POSITION; /* syntax was already checked */ @@ -810,12 +815,11 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_ } cleanup: - LOG_LOCBACK(cur_node ? 1 : 0, 0); - if (ret) { + if (rc) { ly_path_predicates_free(ctx_node->module->ctx, *predicates); *predicates = NULL; } - return ret; + return rc; } /** @@ -833,7 +837,7 @@ static LY_ERR ly_path_compile_predicate_leafref(const struct lysc_node *ctx_node, const struct lysc_node *cur_node, const struct lyxp_expr *expr, uint32_t *tok_idx, LY_VALUE_FORMAT format, void *prefix_data) { - LY_ERR ret = LY_SUCCESS; + LY_ERR rc = LY_SUCCESS; const struct lysc_node *key, *node, *node2; struct ly_ctx *ctx = cur_node->module->ctx; @@ -843,26 +847,26 @@ ly_path_compile_predicate_leafref(const struct lysc_node *ctx_node, const struct } if (ctx_node->nodetype != LYS_LIST) { - LOGVAL(ctx, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "List predicate defined for %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } else if (ctx_node->flags & LYS_KEYLESS) { - LOGVAL(ctx, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "List predicate defined for keyless %s \"%s\" in path.", lys_nodetype2str(ctx_node->nodetype), ctx_node->name); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } do { /* NameTest, find the key */ - ret = ly_path_compile_snode(ctx, cur_node, cur_node->module, ctx_node, expr, *tok_idx, format, prefix_data, + rc = ly_path_compile_snode(ctx, cur_node, cur_node->module, ctx_node, expr, *tok_idx, format, prefix_data, NULL, 0, &key, NULL); - LY_CHECK_GOTO(ret, cleanup); + LY_CHECK_GOTO(rc, cleanup); if ((key->nodetype != LYS_LEAF) || !(key->flags & LYS_KEY)) { - LOGVAL(ctx, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "Key expected instead of %s \"%s\" in path.", lys_nodetype2str(key->nodetype), key->name); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } ++(*tok_idx); @@ -896,8 +900,8 @@ ly_path_compile_predicate_leafref(const struct lysc_node *ctx_node, const struct /* go to parent */ if (!node) { - LOGVAL(ctx, LYVE_XPATH, "Too many parent references in path."); - ret = LY_EVALID; + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "Too many parent references in path."); + rc = LY_EVALID; goto cleanup; } node = lysc_data_parent(node); @@ -914,17 +918,18 @@ ly_path_compile_predicate_leafref(const struct lysc_node *ctx_node, const struct /* NameTest */ assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST); - LY_CHECK_RET(ly_path_compile_snode(ctx, cur_node, cur_node->module, node, expr, *tok_idx, format, - prefix_data, NULL, 0, &node2, NULL)); + rc = ly_path_compile_snode(ctx, cur_node, cur_node->module, node, expr, *tok_idx, format, prefix_data, NULL, + 0, &node2, NULL); + LY_CHECK_GOTO(rc, cleanup); node = node2; ++(*tok_idx); } while ((*tok_idx + 1 < expr->used) && (expr->tokens[*tok_idx + 1] == LYXP_TOKEN_NAMETEST)); /* check the last target node */ if (node->nodetype != LYS_LEAF) { - LOGVAL(ctx, LYVE_XPATH, "Leaf expected instead of %s \"%s\" in leafref predicate in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "Leaf expected instead of %s \"%s\" in leafref predicate in path.", lys_nodetype2str(node->nodetype), node->name); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } @@ -939,7 +944,7 @@ ly_path_compile_predicate_leafref(const struct lysc_node *ctx_node, const struct } while (!lyxp_next_token(NULL, expr, tok_idx, LYXP_TOKEN_BRACK1)); cleanup: - return (ret == LY_ENOTFOUND) ? LY_EVALID : ret; + return (rc == LY_ENOTFOUND) ? LY_EVALID : rc; } /** @@ -1022,6 +1027,7 @@ cleanup: * @brief Compile deref XPath function into ly_path structure. * * @param[in] ctx libyang context. + * @param[in] cur_node Current (original context) node. * @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 @@ -1037,7 +1043,7 @@ cleanup: * @return LY_ERR value. */ static LY_ERR -ly_path_compile_deref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, +ly_path_compile_deref(const struct ly_ctx *ctx, const struct lysc_node *cur_node, 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) { @@ -1081,13 +1087,14 @@ ly_path_compile_deref(const struct ly_ctx *ctx, const struct lysc_node *ctx_node &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); + LOGVAL_PATH(ctx, cur_node, node2, LYVE_XPATH, "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); + LOGVAL_PATH(ctx, cur_node, node2, LYVE_XPATH, "Deref function target node \"%s\" is not leafref.", node2->name); ret = LY_EVALID; goto cleanup; } @@ -1160,9 +1167,9 @@ _ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, con 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; + LY_ERR rc = LY_SUCCESS; uint32_t tok_idx = 0, getnext_opts; - const struct lysc_node *node2, *cur_node, *op; + const struct lysc_node *node2, *cur_node, *op, *prev_ctx_node = NULL; struct ly_path *p = NULL; struct lysc_ext_instance *ext = NULL; @@ -1182,9 +1189,6 @@ _ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, con /* remember original context node */ cur_node = ctx_node; - if (cur_node) { - LOG_LOCSET(cur_node, NULL); - } if (oper == LY_PATH_OPER_OUTPUT) { getnext_opts = LYS_GETNEXT_OUTPUT; @@ -1195,7 +1199,7 @@ _ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, con 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); + rc = ly_path_compile_deref(ctx, cur_node, 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 */ @@ -1206,19 +1210,20 @@ _ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, con /* relative path */ if (!ctx_node) { LOGVAL(ctx, LYVE_XPATH, "No initial schema parent for a relative path."); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } /* go up the parents for leafref */ while (lref && (expr->tokens[tok_idx] == LYXP_TOKEN_DDOT)) { if (!ctx_node) { - LOGVAL(ctx, LYVE_XPATH, "Too many parent references in path."); - ret = LY_EVALID; + LOGVAL_PATH(ctx, cur_node, prev_ctx_node, LYVE_XPATH, "Too many parent references in path."); + rc = LY_EVALID; goto cleanup; } /* get parent */ + prev_ctx_node = ctx_node; ctx_node = lysc_data_parent(ctx_node); ++tok_idx; @@ -1231,63 +1236,66 @@ _ly_path_compile(const struct ly_ctx *ctx, const struct lys_module *cur_mod, con do { /* check last compiled inner node, whether it is uniquely identified (even key-less list) */ if (p && !lref && (target == LY_PATH_TARGET_SINGLE) && (p->node->nodetype == LYS_LIST) && !p->predicates) { - LOGVAL(ctx, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, prev_ctx_node, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.", lys_nodetype2str(p->node->nodetype), p->node->name); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } /* NameTest */ - LY_CHECK_ERR_GOTO(lyxp_check_token(ctx, expr, tok_idx, LYXP_TOKEN_NAMETEST), ret = LY_EVALID, cleanup); + LY_CHECK_ERR_GOTO(lyxp_check_token(ctx, expr, tok_idx, LYXP_TOKEN_NAMETEST), rc = LY_EVALID, cleanup); /* get schema node */ - LY_CHECK_GOTO(ret = ly_path_compile_snode(ctx, cur_node, cur_mod, ctx_node, expr, tok_idx, format, prefix_data, + LY_CHECK_GOTO(rc = ly_path_compile_snode(ctx, cur_node, cur_mod, ctx_node, expr, tok_idx, format, prefix_data, top_ext, getnext_opts, &node2, &ext), cleanup); ++tok_idx; if ((op && (node2->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && (node2 != op))) { - LOGVAL(ctx, LYVE_XPATH, "Not found node \"%s\" in path.", node2->name); - ret = LY_EVALID; + LOGVAL_PATH(ctx, cur_node, prev_ctx_node, LYVE_XPATH, "Not found node \"%s\" in path.", node2->name); + rc = LY_EVALID; goto cleanup; } + + /* next node */ + prev_ctx_node = ctx_node; ctx_node = node2; /* new path segment */ - LY_ARRAY_NEW_GOTO(ctx, *path, p, ret, cleanup); + LY_ARRAY_NEW_GOTO(ctx, *path, p, rc, cleanup); p->node = ctx_node; p->ext = ext; /* compile any predicates */ if (lref) { - ret = ly_path_compile_predicate_leafref(ctx_node, cur_node, expr, &tok_idx, format, prefix_data); + rc = 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, + rc = ly_path_compile_predicate(ctx, cur_node, cur_mod, ctx_node, expr, &tok_idx, format, prefix_data, &p->predicates); } - LY_CHECK_GOTO(ret, cleanup); + LY_CHECK_GOTO(rc, cleanup); } while (!lyxp_next_token(NULL, expr, &tok_idx, LYXP_TOKEN_OPER_PATH)); /* check leftover tokens */ if (tok_idx < expr->used) { - LOGVAL(ctx, LY_VCODE_XP_INTOK, lyxp_token2str(expr->tokens[tok_idx]), &expr->expr[expr->tok_pos[tok_idx]]); - ret = LY_EVALID; + LOGVAL_PATH(ctx, cur_node, ctx_node, LY_VCODE_XP_INTOK, lyxp_token2str(expr->tokens[tok_idx]), + &expr->expr[expr->tok_pos[tok_idx]]); + rc = LY_EVALID; goto cleanup; } /* check last compiled node */ if (!lref && (target == LY_PATH_TARGET_SINGLE) && (p->node->nodetype & (LYS_LIST | LYS_LEAFLIST)) && !p->predicates) { - LOGVAL(ctx, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.", + LOGVAL_PATH(ctx, cur_node, ctx_node, LYVE_XPATH, "Predicate missing for %s \"%s\" in path.", lys_nodetype2str(p->node->nodetype), p->node->name); - ret = LY_EVALID; + rc = LY_EVALID; goto cleanup; } cleanup: - if (ret) { + if (rc) { ly_path_free(*path); *path = NULL; } - LOG_LOCBACK(cur_node ? 1 : 0, 0); - return (ret == LY_ENOTFOUND) ? LY_EVALID : ret; + return (rc == LY_ENOTFOUND) ? LY_EVALID : rc; } LY_ERR @@ -1304,6 +1312,8 @@ ly_path_compile_leafref(const struct ly_ctx *ctx, const struct lysc_node *ctx_no const struct lyxp_expr *expr, uint16_t oper, uint16_t target, LY_VALUE_FORMAT format, void *prefix_data, struct ly_path **path) { + assert(ctx_node); + return _ly_path_compile(ctx, ctx_node->module, ctx_node, top_ext, expr, 1, oper, target, 1, format, prefix_data, path); } diff --git a/src/path.h b/src/path.h index 68cf76e..494a881 100644 --- a/src/path.h +++ b/src/path.h @@ -3,7 +3,7 @@ * @author Michal Vasko * @brief Path structure and manipulation routines. * - * Copyright (c) 2020 - 2023 CESNET, z.s.p.o. + * Copyright (c) 2020 - 2025 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. @@ -28,6 +28,25 @@ struct lysc_ext_instance; struct lysc_node; struct lyxp_expr; +/** + * @brief Similar to LOGVAL, but also logs path of CUR_SCNODE and if not set, CTX_SCNODE, in this order. + * + * @param[in] CTX Context to use. + * @param[in] CUR_SCNODE Current (original context) node. + * @param[in] CTX_SCNODE Context node. + */ +#define LOGVAL_PATH(CTX, CUR_SCNODE, CTX_SCNODE, ...) \ + if ((CUR_SCNODE) || (CTX_SCNODE)) { \ + LOG_LOCSET((CUR_SCNODE) ? (CUR_SCNODE) : (CTX_SCNODE), NULL); \ + } \ + ly_vlog(CTX, NULL, __VA_ARGS__); \ + if ((CUR_SCNODE) || (CTX_SCNODE)) { \ + LOG_LOCBACK(1, 0); \ + } + +/** + * @brief Common types of predicates. + */ enum ly_path_pred_type { LY_PATH_PREDTYPE_POSITION, /**< position predicate - [2] */ LY_PATH_PREDTYPE_LIST, /**< keys predicate - [key1='val1'][key2='val2']... */ @@ -99,7 +118,7 @@ struct ly_path { * @param[in] ctx libyang context. * @param[in] ctx_node Optional context node, used for logging. * @param[in] str_path Path to parse. - * @param[in] path_len Length of @p str_path. + * @param[in] path_len Length of @p str_path, may be 0 if @p str_path is 0-terminated. * @param[in] lref Whether leafref is being parsed or not. * @param[in] begin Begin option (@ref path_begin_options). * @param[in] prefix Prefix option (@ref path_prefix_options). @@ -116,7 +135,7 @@ LY_ERR ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, * @param[in] ctx libyang context. * @param[in] cur_node Optional current (original context) node, used for logging. * @param[in] str_path Path to parse. - * @param[in] path_len Length of @p str_path. + * @param[in] path_len Length of @p str_path, may be 0 if @p str_path is 0-terminated. * @param[in] prefix Prefix option (@ref path_prefix_options). * @param[in] pred Predicate option (@ref path_pred_options). * @param[out] expr Parsed path. diff --git a/src/plugins_exts/structure.c b/src/plugins_exts/structure.c index 6aa902e..f05e635 100644 --- a/src/plugins_exts/structure.c +++ b/src/plugins_exts/structure.c @@ -318,7 +318,8 @@ structure_aug_parse(struct lysp_ctx *pctx, struct lysp_ext_instance *ext) /* augment-structure must define some data-def-stmt */ LY_LIST_FOR(ext->child, stmt) { - if (stmt->kw & LY_STMT_DATA_NODE_MASK) { + if (stmt->kw & (LY_STMT_CONTAINER | LY_STMT_LEAF | LY_STMT_LEAF_LIST | LY_STMT_LIST | LY_STMT_CHOICE | + LY_STMT_ANYDATA | LY_STMT_ANYXML | LY_STMT_USES)) { break; } } diff --git a/src/plugins_types.c b/src/plugins_types.c index d773a8a..fb6f75a 100644 --- a/src/plugins_types.c +++ b/src/plugins_types.c @@ -685,7 +685,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D case LY_TYPE_INT32: LY_CHECK_ARG_RET(NULL, base, LY_EINVAL); - if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM))) { + if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) && + !(hints & LYD_VALHINT_STRING_DATATYPES)) { return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-number-encoded %s value \"%.*s\".", lys_datatype2str(type), (int)value_len, value); } @@ -695,7 +696,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D case LY_TYPE_INT64: LY_CHECK_ARG_RET(NULL, base, LY_EINVAL); - if (!(hints & LYD_VALHINT_NUM64)) { + if (!(hints & LYD_VALHINT_NUM64) && + !(hints & LYD_VALHINT_STRING_DATATYPES)) { return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-num64-encoded %s value \"%.*s\".", lys_datatype2str(type), (int)value_len, value); } @@ -714,7 +716,8 @@ lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_D } break; case LY_TYPE_BOOL: - if (!(hints & LYD_VALHINT_BOOLEAN)) { + if (!(hints & LYD_VALHINT_BOOLEAN) && + !(hints & LYD_VALHINT_STRING_DATATYPES)) { return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-boolean-encoded %s value \"%.*s\".", lys_datatype2str(type), (int)value_len, value); } diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c index 64281c6..47aea31 100644 --- a/src/plugins_types/union.c +++ b/src/plugins_types/union.c @@ -150,6 +150,48 @@ lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, c } } +/** + * @brief For leafref failures, ensure the appropriate error is propagated, not a type validation failure. + * + * RFC7950 Section 15.5 defines the appropriate error app tag of "require-instance". + * + * @param[in,out] err Error record to be updated. + * @param[in] type Leafref type used to extract target path. + * @param[in] value Value attempted to be stored. + * @param[in] value_len Length of @p value. + * @return LY_ERR value. Only possible errors are LY_SUCCESS and LY_EMEM. + */ +static LY_ERR +union_update_lref_err(struct ly_err_item *err, const struct lysc_type *type, const void *value, size_t value_len) +{ + const struct lysc_type_leafref *lref; + char *valstr = NULL; + int r; + + if (!err || (type->basetype != LY_TYPE_LEAFREF)) { + /* nothing to do */ + return LY_SUCCESS; + } + + lref = (const struct lysc_type_leafref *)type; + + /* update error-app-tag */ + free(err->apptag); + err->apptag = strdup("instance-required"); + LY_CHECK_ERR_RET(!err->apptag, LOGMEM(NULL), LY_EMEM); + + valstr = strndup((const char *)value, value_len); + LY_CHECK_ERR_RET(!valstr, LOGMEM(NULL), LY_EMEM); + + /* update error-message */ + free(err->msg); + r = asprintf(&err->msg, LY_ERRMSG_NOLREF_VAL, valstr, lyxp_get_expr(lref->path)); + free(valstr); + LY_CHECK_ERR_RET(r == -1, LOGMEM(NULL), LY_EMEM); + + return LY_SUCCESS; +} + /** * @brief Store (and validate) subvalue as a specific type. * @@ -187,10 +229,13 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3 /* value of another type, first store the value properly and then use its JSON value for parsing */ rc = type_u->types[ti]->plugin->store(ctx, type_u->types[ti], value, value_len, LYPLG_TYPE_STORE_ONLY, subvalue->format, subvalue->prefix_data, subvalue->hints, subvalue->ctx_node, &subvalue->value, unres, err); - if ((rc != LY_SUCCESS) && (rc != LY_EINCOMPLETE)) { + if (rc && (rc != LY_EINCOMPLETE)) { /* clear any leftover/freed garbage */ memset(&subvalue->value, 0, sizeof subvalue->value); - return rc; + + /* if this is a leafref, lets make sure we propagate the appropriate error, and not a type validation failure */ + union_update_lref_err(*err, type_u->types[ti], value, value_len); + goto cleanup; } assert(subvalue->value.realtype); @@ -219,16 +264,16 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3 if (options & LYPLG_TYPE_STORE_ONLY) { opts |= LYPLG_TYPE_STORE_ONLY; } - if (dynamic) { - opts |= LYPLG_TYPE_STORE_DYNAMIC; - } rc = type->plugin->store(ctx, type, value, value_len, opts, format, prefix_data, subvalue->hints, subvalue->ctx_node, &subvalue->value, unres, err); - if ((rc != LY_SUCCESS) && (rc != LY_EINCOMPLETE)) { + if (rc && (rc != LY_EINCOMPLETE)) { /* clear any leftover/freed garbage */ memset(&subvalue->value, 0, sizeof subvalue->value); - return rc; + + /* if this is a leafref, lets make sure we propagate the appropriate error, and not a type validation failure */ + union_update_lref_err(*err, type, value, value_len); + goto cleanup; } if (validate && (rc == LY_EINCOMPLETE)) { @@ -237,9 +282,14 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint3 if (rc) { /* validate failed, we need to free the stored value */ type->plugin->free(ctx, &subvalue->value); + goto cleanup; } } +cleanup: + if (dynamic) { + free((void *)value); + } return rc; } @@ -267,8 +317,9 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct LY_ARRAY_COUNT_TYPE u; struct ly_err_item **errs = NULL, *e; uint32_t *prev_lo, temp_lo = 0; - char *msg = NULL; + char *msg = NULL, *err_app_tag = NULL; int msg_len = 0; + ly_bool use_err_app_tag = 0; *err = NULL; @@ -299,6 +350,8 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct } if (msg_len == -1) { LY_CHECK_ERR_GOTO(!errs, ret = LY_EMEM, cleanup); + /* for further actions in function msg_len is just 0 */ + msg_len = 0; } for (u = 0; u < LY_ARRAY_COUNT(type_u->types); ++u) { if (!errs[u]) { @@ -306,12 +359,27 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct continue; } + /* use an app-tag if all the types set it or set none */ + if (errs[u]->apptag) { + if (!err_app_tag) { + err_app_tag = strdup(errs[u]->apptag); + LY_CHECK_ERR_GOTO(!err_app_tag, ret = LY_EMEM, cleanup); + use_err_app_tag = 1; + } else if (strcmp(errs[u]->apptag, err_app_tag)) { + use_err_app_tag = 0; + } + } + msg = ly_realloc(msg, msg_len + 4 + strlen(type_u->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", type_u->types[u]->plugin->id, errs[u]->msg); } - ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "%s", msg); + if (!use_err_app_tag) { + free(err_app_tag); + err_app_tag = NULL; + } + ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, err_app_tag, "%s", msg); } else if (type_idx) { *type_idx = u; } @@ -496,26 +564,33 @@ lyplg_type_compare_union(const struct ly_ctx *ctx, const struct lyd_value *val1, LIBYANG_API_DEF int lyplg_type_sort_union(const struct ly_ctx *ctx, const struct lyd_value *val1, const struct lyd_value *val2) { - int rc = LY_SUCCESS; + int rc; LY_ARRAY_COUNT_TYPE u; - struct lysc_type **types; + struct lysc_type **types, *type; if (val1->subvalue->value.realtype == val2->subvalue->value.realtype) { return val1->subvalue->value.realtype->plugin->sort(ctx, &val1->subvalue->value, &val2->subvalue->value); } /* compare according to the order of types */ + rc = 0; types = ((struct lysc_type_union *)val1->realtype)->types; LY_ARRAY_FOR(types, u) { - if (types[u] == val1->subvalue->value.realtype) { + if (types[u]->basetype == LY_TYPE_LEAFREF) { + type = ((struct lysc_type_leafref *)types[u])->realtype; + } else { + type = types[u]; + } + + if (type == val1->subvalue->value.realtype) { rc = 1; break; - } else if (types[u] == val2->subvalue->value.realtype) { + } else if (type == val2->subvalue->value.realtype) { rc = -1; break; } } - assert(rc != 0); + assert(rc); return rc; } diff --git a/src/printer_tree.c b/src/printer_tree.c index 44b0f53..f58ded5 100644 --- a/src/printer_tree.c +++ b/src/printer_tree.c @@ -590,7 +590,7 @@ struct trt_fp_read { .module_name = tro_read_module_name, \ .node = troc_read_node, \ .if_sibling_exists = troc_read_if_sibling_exists, \ - .if_parent_exists = tro_read_if_sibling_exists \ + .if_parent_exists = tro_read_if_parent_exists \ } /** @@ -601,7 +601,7 @@ struct trt_fp_read { .module_name = tro_read_module_name, \ .node = trop_read_node, \ .if_sibling_exists = trop_read_if_sibling_exists, \ - .if_parent_exists = tro_read_if_sibling_exists \ + .if_parent_exists = tro_read_if_parent_exists \ } /********************************************************************** @@ -781,6 +781,7 @@ typedef const char *(*trt_get_charptr_func)(const struct lysp_node *pn); */ struct tro_getters { uint16_t (*nodetype)(const void *); /**< Get nodetype. */ + uint16_t (*lysp_flags)(const void *); /**< Get flags from lysp_node. */ const void *(*next)(const void *); /**< Get sibling. */ const void *(*parent)(const void *); /**< Get parent. */ const void *(*child)(const void *); /**< Get child. */ @@ -1947,6 +1948,16 @@ trop_nodetype(const void *node) return ((const struct lysp_node *)node)->nodetype; } +/** + * @brief Get lysp_node flags. + * @param[in] node is any lysp_node. + */ +static uint16_t +trop_flags(const void *node) +{ + return ((const struct lysp_node *)node)->flags; +} + /** * @brief Get sibling. * @param[in] node is any lysp_node. @@ -2026,6 +2037,7 @@ trop_init_getters(void) { return (struct tro_getters) { .nodetype = trop_nodetype, + .lysp_flags = trop_flags, .next = trop_next, .parent = trop_parent, .child = trop_child, @@ -2046,6 +2058,23 @@ troc_nodetype(const void *node) return ((const struct lysc_node *)node)->nodetype; } +/** + * @brief Get lysp_node flags. + * @param[in] node is any lysc_node. + */ +static uint16_t +troc_lysp_flags(const void *node) +{ + const struct lysc_node *cn; + + cn = (const struct lysc_node *)node; + if (TRP_TREE_CTX_LYSP_NODE_PRESENT(cn)) { + return TRP_TREE_CTX_GET_LYSP_NODE(cn)->flags; + } else { + return 0; + } +} + /** * @brief Get sibling. * @param[in] node is any lysc_node. @@ -2125,6 +2154,7 @@ troc_init_getters(void) { return (struct tro_getters) { .nodetype = troc_nodetype, + .lysp_flags = troc_lysp_flags, .next = troc_next, .parent = troc_parent, .child = troc_child, @@ -2272,6 +2302,8 @@ tro_next_sibling(const void *node, const struct trt_tree_ctx *tc) plugin_ctx = tc->plugin_ctx; if (sibl && tro_set_node_overr(tc->lysc_tree, sibl, 1, &plugin_ctx) && plugin_ctx.filtered) { return tro_next_sibling(sibl, tc); + } else if (sibl && (get.lysp_flags(node) & LYS_INTERNAL)) { + return tro_next_sibling(sibl, tc); } return sibl; @@ -2328,6 +2360,8 @@ tro_next_child(const void *node, const struct trt_tree_ctx *tc) plugin_ctx = tc->plugin_ctx; if (child && tro_set_node_overr(tc->lysc_tree, child, 1, &plugin_ctx) && plugin_ctx.filtered) { return tro_next_sibling(child, tc); + } else if (child && (get.lysp_flags(node) & LYS_INTERNAL)) { + return tro_next_sibling(child, tc); } return child; @@ -2605,7 +2639,7 @@ tro_read_module_name(const struct trt_tree_ctx *tc) } static ly_bool -tro_read_if_sibling_exists(const struct trt_tree_ctx *tc) +tro_read_if_parent_exists(const struct trt_tree_ctx *tc) { const void *parent; @@ -3118,7 +3152,11 @@ trop_modi_first_sibling(struct trt_parent_cache ca, struct trt_tree_ctx *tc) default: assert(0); } - node = trop_read_node(ca, tc); + if (tc->pn && (trop_flags(tc->pn) & LYS_INTERNAL)) { + node = trop_modi_next_sibling(ca, tc); + } else { + node = trop_read_node(ca, tc); + } } if (tc->plugin_ctx.filtered) { @@ -3440,7 +3478,11 @@ troc_modi_first_sibling(struct trt_parent_cache ca, struct trt_tree_ctx *tc) default: assert(0); } - node = troc_read_node(ca, tc); + if (tc->cn && (troc_lysp_flags(tc->cn) & LYS_INTERNAL)) { + node = troc_modi_next_sibling(ca, tc); + } else { + node = troc_read_node(ca, tc); + } } if (tc->plugin_ctx.filtered) { diff --git a/src/printer_xml.c b/src/printer_xml.c index 31c5ad4..815dae2 100644 --- a/src/printer_xml.c +++ b/src/printer_xml.c @@ -463,6 +463,7 @@ no_content: pctx->options = prev_opts; break; case LYD_ANYDATA_STRING: + case LYD_ANYDATA_JSON: /* escape XML-sensitive characters */ if (!any->value.str[0]) { goto no_content; @@ -478,9 +479,8 @@ no_content: } ly_print_(pctx->out, ">%s", any->value.str); break; - case LYD_ANYDATA_JSON: case LYD_ANYDATA_LYB: - /* JSON and LYB format is not supported */ + /* LYB format is not supported */ LOGWRN(pctx->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type); goto no_content; } diff --git a/src/printer_yang.c b/src/printer_yang.c index 3adcc2a..1ed081e 100644 --- a/src/printer_yang.c +++ b/src/printer_yang.c @@ -2269,6 +2269,10 @@ yang_print_parsed_body(struct lys_ypr_ctx *pctx, const struct lysp_module *modp) YPR_EXTRA_LINE(modp->groupings, pctx); LY_LIST_FOR(modp->data, data) { + if (data->flags & LYS_INTERNAL) { + continue; + } + YPR_EXTRA_LINE_PRINT(pctx); yprp_node(pctx, data); } diff --git a/src/printer_yin.c b/src/printer_yin.c index c3a31a6..a460373 100644 --- a/src/printer_yin.c +++ b/src/printer_yin.c @@ -1367,6 +1367,10 @@ yin_print_parsed_body(struct lys_ypr_ctx *pctx, const struct lysp_module *modp) } LY_LIST_FOR(modp->data, data) { + if (data->flags & LYS_INTERNAL) { + continue; + } + yprp_node(pctx, data); } diff --git a/src/schema_compile.c b/src/schema_compile.c index 5adfc01..98231ed 100644 --- a/src/schema_compile.c +++ b/src/schema_compile.c @@ -937,6 +937,7 @@ lys_compile_unres_dflt(struct lysc_ctx *ctx, struct lysc_node *node, struct lysc { LY_ERR ret; uint32_t options; + struct lyd_value *val; struct ly_err_item *err = NULL; options = (ctx->ctx->flags & LY_CTX_REF_IMPLEMENTED) ? LYPLG_TYPE_STORE_IMPLEMENT : 0; @@ -963,11 +964,16 @@ lys_compile_unres_dflt(struct lysc_ctx *ctx, struct lysc_node *node, struct lysc } LY_ATOMIC_INC_BARRIER(((struct lysc_type *)storage->realtype)->refcount); - if (storage->realtype->basetype == LY_TYPE_INST) { + if (storage->realtype->basetype == LY_TYPE_UNION) { + val = &storage->subvalue->value; + } else { + val = storage; + } + if (val->realtype->basetype == LY_TYPE_INST) { /* ly_path includes references to other nodes, in case they are in foreign modules, the context would * need to be freed in specific order to avoid accessing freed memory, so just avoid storing it */ - ly_path_free(storage->target); - storage->target = NULL; + ly_path_free(val->target); + val->target = NULL; } return LY_SUCCESS; } @@ -1537,11 +1543,13 @@ lys_compile_depset_r(struct ly_ctx *ctx, struct ly_set *dep_set, struct lys_glob LY_CHECK_GOTO(ret = lys_compile(mod, &unres->ds_unres), cleanup); } +resolve_unres: /* resolve dep set unres */ ret = lys_compile_unres_depset(ctx, unres); + lys_compile_unres_depset_erase(ctx, unres); + if (ret == LY_ERECOMPILE) { - /* new module is implemented, discard current dep set unres and recompile the whole dep set */ - lys_compile_unres_depset_erase(ctx, unres); + /* new module is implemented referencing previously compiled modules, recompile the whole dep set */ return lys_compile_depset_r(ctx, dep_set, unres); } else if (ret) { /* error */ @@ -1551,6 +1559,13 @@ lys_compile_depset_r(struct ly_ctx *ctx, struct ly_set *dep_set, struct lys_glob /* success, unset the flags of all the modules in the dep set */ for (i = 0; i < dep_set->count; ++i) { mod = dep_set->objs[i]; + + if (mod->to_compile && !mod->compiled) { + /* new module is implemented but does not require recompilation of the whole dep set */ + LY_CHECK_GOTO(ret = lys_compile(mod, &unres->ds_unres), cleanup); + goto resolve_unres; + } + mod->to_compile = 0; } diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c index 8807b0f..9c230cd 100644 --- a/src/schema_compile_node.c +++ b/src/schema_compile_node.c @@ -365,12 +365,12 @@ lysc_range_dup(struct lysc_ctx *ctx, const struct lysc_range *orig, struct ly_se dup = calloc(1, sizeof *dup); LY_CHECK_ERR_RET(!dup, LOGMEM(ctx->ctx), NULL); if (orig->parts) { - LY_ARRAY_CREATE_GOTO(ctx->ctx, dup->parts, LY_ARRAY_COUNT(orig->parts), ret, cleanup); + LY_ARRAY_CREATE_GOTO(ctx->ctx, dup->parts, LY_ARRAY_COUNT(orig->parts), ret, error); (*((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->ctx, orig->eapptag, dup->eapptag, ret, cleanup); - DUP_STRING_GOTO(ctx->ctx, orig->emsg, dup->emsg, ret, cleanup); + DUP_STRING_GOTO(ctx->ctx, orig->eapptag, dup->eapptag, ret, error); + DUP_STRING_GOTO(ctx->ctx, orig->emsg, dup->emsg, ret, error); /* collect all range extensions */ if (tpdf_chain->count > tpdf_chain_last) { @@ -381,15 +381,17 @@ lysc_range_dup(struct lysc_ctx *ctx, const struct lysc_range *orig, struct ly_se if (!tpdf_item->tpdf->type.range) { continue; } - COMPILE_EXTS_GOTO(ctx, tpdf_item->tpdf->type.range->exts, dup->exts, dup, ret, cleanup); + COMPILE_EXTS_GOTO(ctx, tpdf_item->tpdf->type.range->exts, dup->exts, dup, ret, error); } while (i > tpdf_chain_last); } return dup; -cleanup: - free(dup); - (void) ret; /* set but not used due to the return type */ +error: + if (dup) { + lysc_range_free(&ctx->free_ctx, dup); + free(dup); + } return NULL; } @@ -1518,7 +1520,7 @@ lys_compile_type_enums(struct lysc_ctx *ctx, const struct lysp_type_enum *enums_ } } - /* save highest value for auto assing */ + /* save highest value for auto assign */ if (highest_value < cur_val) { highest_value = cur_val; } @@ -1553,7 +1555,7 @@ lys_compile_type_enums(struct lysc_ctx *ctx, const struct lysp_type_enum *enums_ } } - /* save highest position for auto assing */ + /* save highest position for auto assign */ if (highest_position < cur_pos) { highest_position = cur_pos; } @@ -1583,7 +1585,17 @@ lys_compile_type_enums(struct lysc_ctx *ctx, const struct lysp_type_enum *enums_ DUP_STRING_GOTO(ctx->ctx, enums_p[u].name, e->name, ret, done); DUP_STRING_GOTO(ctx->ctx, enums_p[u].dsc, e->dsc, ret, done); DUP_STRING_GOTO(ctx->ctx, enums_p[u].ref, e->ref, ret, done); - e->flags = (enums_p[u].flags & LYS_FLAGS_COMPILED_MASK) | (basetype == LY_TYPE_ENUM ? LYS_IS_ENUM : 0); + + /* copy flags except for status */ + e->flags = (enums_p[u].flags & LYS_FLAGS_COMPILED_MASK) & ~LYS_STATUS_MASK; + + /* compile status */ + LY_CHECK_RET(lys_compile_status(ctx, enums_p[u].flags, 0, 0, NULL, (basetype == LY_TYPE_ENUM) ? "enum" : "bit", + &e->flags)); + + /* enum/bit flag */ + e->flags |= (basetype == LY_TYPE_ENUM) ? LYS_IS_ENUM : 0; + if (basetype == LY_TYPE_ENUM) { e->value = cur_val; } else { @@ -4127,49 +4139,82 @@ cleanup: return rc; } +/** + * @brief Generate path of a grouping for logging. + * + * @param[in] ctx Compile context. + * @param[in] node Grouping node. + * @param[out] path Generated path. + * @return Length of the generated @p path; + * @return -1 on error. + */ static int lys_compile_grouping_pathlog(struct lysc_ctx *ctx, struct lysp_node *node, char **path) { struct lysp_node *iter; - int len = 0; + int len = 0, r; + char *s, *id; *path = NULL; - for (iter = node; iter && len >= 0; iter = iter->parent) { - char *s = *path; - char *id; + for (iter = node; iter && (len >= 0); iter = iter->parent) { + s = *path; + + /* next node segment */ switch (iter->nodetype) { case LYS_USES: - LY_CHECK_RET(asprintf(&id, "{uses='%s'}", iter->name) == -1, -1); + r = asprintf(&id, "{uses='%s'}", iter->name); break; case LYS_GROUPING: - LY_CHECK_RET(asprintf(&id, "{grouping='%s'}", iter->name) == -1, -1); + r = asprintf(&id, "{grouping='%s'}", iter->name); break; case LYS_AUGMENT: - LY_CHECK_RET(asprintf(&id, "{augment='%s'}", iter->name) == -1, -1); + r = asprintf(&id, "{augment='%s'}", iter->name); break; default: id = strdup(iter->name); + r = id ? 1 : -1; break; } + if (r == -1) { + len = -1; + goto cleanup; + } + /* append the segment to the path */ if (!iter->parent) { /* print prefix */ - len = asprintf(path, "/%s:%s%s", ctx->cur_mod->name, id, s ? s : ""); + r = asprintf(path, "/%s:%s%s", ctx->cur_mod->name, id, s ? s : ""); } else { /* prefix is the same as in parent */ - len = asprintf(path, "/%s%s", id, s ? s : ""); + r = asprintf(path, "/%s%s", id, s ? s : ""); } free(s); free(id); + if (r == -1) { + len = -1; + goto cleanup; + } + + /* remember the length of the full path */ + len = r; } + if (!len) { + /* root node path */ + *path = strdup("/"); + if (!*path) { + len = -1; + goto cleanup; + } + + len = 1; + } + +cleanup: if (len < 0) { free(*path); *path = NULL; - } else if (len == 0) { - *path = strdup("/"); - len = 1; } return len; } diff --git a/src/tree_data.c b/src/tree_data.c index 71c0b78..5a9fc7b 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -222,10 +222,14 @@ LIBYANG_API_DEF 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) { - LY_CHECK_ARG_RET(ctx, ctx, in, parent || tree, LY_EINVAL); + LY_CHECK_ARG_RET(ctx, ctx || parent, in, parent || tree, LY_EINVAL); LY_CHECK_ARG_RET(ctx, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL); LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL); + if (!ctx) { + ctx = LYD_CTX(parent); + } + return lyd_parse(ctx, NULL, parent, tree, in, format, parse_options, validate_options, NULL); } @@ -1035,7 +1039,7 @@ LIBYANG_API_DEF LY_ERR lyd_insert_child(struct lyd_node *parent, struct lyd_node *node) { LY_CHECK_ARG_RET(NULL, parent, node, !parent->schema || (parent->schema->nodetype & LYD_NODE_INNER), LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(LYD_CTX(parent), LYD_CTX(node), LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, LYD_CTX(parent), LYD_CTX(node), LY_EINVAL); LY_CHECK_RET(lyd_insert_check_schema(parent->schema, NULL, node->schema)); @@ -1101,7 +1105,7 @@ LIBYANG_API_DEF LY_ERR lyd_insert_before(struct lyd_node *sibling, struct lyd_node *node) { LY_CHECK_ARG_RET(NULL, sibling, node, sibling != node, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(LYD_CTX(sibling), LYD_CTX(node), LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, LYD_CTX(sibling), LYD_CTX(node), LY_EINVAL); LY_CHECK_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema)); @@ -1125,7 +1129,7 @@ LIBYANG_API_DEF LY_ERR lyd_insert_after(struct lyd_node *sibling, struct lyd_node *node) { LY_CHECK_ARG_RET(NULL, sibling, node, sibling != node, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(LYD_CTX(sibling), LYD_CTX(node), LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, LYD_CTX(sibling), LYD_CTX(node), LY_EINVAL); LY_CHECK_RET(lyd_insert_check_schema(NULL, sibling->schema, node->schema)); @@ -2002,6 +2006,121 @@ lyd_find_schema_ctx(const struct lysc_node *schema, const struct ly_ctx *trg_ctx return LY_SUCCESS; } +/** + * @brief Return the top-level context of a subtree of node. Handles extension data nodes. + * + * @param[in] node Node to use. + * @return + */ +static const struct ly_ctx * +lyd_dup_get_top_ctx(const struct lyd_node *node) +{ + const struct lyd_node *par; + + par = node; + while (par && !(par->flags & LYD_EXT)) { + par = lyd_parent(par); + } + + if (par && lyd_parent(par)) { + /* context of the first non-extension parent */ + return LYD_CTX(lyd_parent(par)); + } + + /* context of the node, all the parents have it */ + return LYD_CTX(node); +} + +/** + * @brief Find (update) the target context for the next node, if needed. + * + * @param[in] orig_node First extension data node being processed from the original tree. + * @param[in] dup_parent Duplicated parent of @p orig_node, set if @p dup_sparent is NULL. + * @param[in] dup_sparent Schema node of the duplicated parent of @p orig_node, set if @p dup_parent is NULL. + * @param[in,out] trg_ctx Target context, may be updated. + * @return LY_ERR value. + */ +static LY_ERR +lyd_find_ext_ctx(const struct lyd_node *orig_node, const struct lyd_node *dup_parent, + const struct lysc_node *dup_sparent, const struct ly_ctx **trg_ctx) +{ + LY_ERR r; + const struct lysc_node *snode; + char *path; + + assert(orig_node && (orig_node->flags & LYD_EXT) && *trg_ctx); + + if (!lyd_parent(orig_node)) { + /* treat as a non-extension node */ + return LY_SUCCESS; + } + assert(dup_parent || dup_sparent); + + if (LYD_CTX(lyd_parent(orig_node)) == *trg_ctx) { + /* same contexts, just extension data */ + *trg_ctx = LYD_CTX(orig_node); + return LY_SUCCESS; + } + + /* find the extension context to use from the target context */ + r = ly_nested_ext_schema(dup_parent, dup_sparent, orig_node->schema->module->name, strlen(orig_node->schema->module->name), + LY_VALUE_JSON, NULL, LYD_NAME(orig_node), strlen(LYD_NAME(orig_node)), &snode, NULL); + if (r == LY_ENOT) { + path = lyd_path(orig_node, LYD_PATH_STD, NULL, 0); + LOGERR(*trg_ctx, LY_ENOTFOUND, "Schema node of an extension node \"%s\" not found in the target context.", path); + free(path); + return LY_ENOTFOUND; + } else if (r) { + return r; + } + + /* update the context */ + *trg_ctx = snode->module->ctx; + return LY_SUCCESS; +} + +/** + * @brief Find (update) the target context for the specific nested node, if needed. + * + * @param[in] orig_node Nested data node being processed from the original tree. + * @param[in,out] trg_ctx Target context, may be updated. + * @return LY_ERR value. + */ +static LY_ERR +lyd_find_ext_ctx_nested(const struct lyd_node *orig_node, const struct ly_ctx **trg_ctx) +{ + const struct lyd_node *parent; + const struct lysc_node *sparent; + char *path; + + if (lyd_dup_get_top_ctx(orig_node) == *trg_ctx) { + /* it is the same context, use the same one for extension data nodes as well (if node is nested in such data) */ + *trg_ctx = LYD_CTX(orig_node); + } else { + /* not the same context, need to find the right one */ + parent = orig_node; + while (parent && !(parent->flags & LYD_EXT)) { + parent = lyd_parent(parent); + } + + if (parent && lyd_parent(parent)) { + /* find the parent schema node in the target context */ + path = lysc_path(lyd_parent(parent)->schema, LYSC_PATH_DATA, NULL, 0); + sparent = lys_find_path(*trg_ctx, NULL, path, 0); + if (!sparent) { + LOGERR(*trg_ctx, LY_ENOTFOUND, "Node \"%s\" was not found in the target context.", path); + free(path); + return LY_ENOTFOUND; + } + free(path); + + LY_CHECK_RET(lyd_find_ext_ctx(parent, NULL, sparent, trg_ctx)); + } + } + + return LY_SUCCESS; +} + /** * @brief Duplicate a single node and connect it into @p parent (if present) or last of @p first siblings. * @@ -2020,7 +2139,7 @@ static LY_ERR lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *parent, uint32_t insert_order, struct lyd_node **first, uint32_t options, struct lyd_node **dup_p) { - LY_ERR ret; + LY_ERR rc = LY_SUCCESS; struct lyd_node *dup = NULL; struct lyd_meta *meta; struct lyd_attr *attr; @@ -2036,8 +2155,10 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_ return LY_SUCCESS; } - /* we need to use the same context */ - trg_ctx = LYD_CTX(node); + if (parent) { + /* update the context */ + LY_CHECK_GOTO(rc = lyd_find_ext_ctx(node, parent, NULL, &trg_ctx), cleanup); + } /* else called from lyd_dup_get_local_parent() and the context is correct */ } if (!node->schema) { @@ -2062,11 +2183,11 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_ break; default: LOGINT(trg_ctx); - ret = LY_EINT; - goto error; + rc = LY_EINT; + goto cleanup; } } - LY_CHECK_ERR_GOTO(!dup, LOGMEM(trg_ctx); ret = LY_EMEM, error); + LY_CHECK_ERR_GOTO(!dup, LOGMEM(trg_ctx); rc = LY_EMEM, cleanup); if (options & LYD_DUP_WITH_FLAGS) { dup->flags = node->flags; @@ -2076,15 +2197,17 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_ if (options & LYD_DUP_WITH_PRIV) { dup->priv = node->priv; } + + /* find schema node */ if (trg_ctx == LYD_CTX(node)) { dup->schema = node->schema; } else { - ret = lyd_find_schema_ctx(node->schema, trg_ctx, parent, 1, &dup->schema); - if (ret) { + rc = lyd_find_schema_ctx(node->schema, trg_ctx, parent, 1, &dup->schema); + if (rc) { /* has no schema but is not an opaque node */ free(dup); dup = NULL; - goto error; + goto cleanup; } } dup->prev = dup; @@ -2093,11 +2216,11 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_ if (!(options & LYD_DUP_NO_META)) { if (!node->schema) { LY_LIST_FOR(((struct lyd_node_opaq *)node)->attr, attr) { - LY_CHECK_GOTO(ret = lyd_dup_attr_single(attr, dup, NULL), error); + LY_CHECK_GOTO(rc = lyd_dup_attr_single(attr, dup, NULL), cleanup); } } else { LY_LIST_FOR(node->meta, meta) { - LY_CHECK_GOTO(ret = lyd_dup_meta_single_to_ctx(trg_ctx, meta, dup, NULL), error); + LY_CHECK_GOTO(rc = lyd_dup_meta_single_to_ctx(trg_ctx, meta, dup, NULL), cleanup); } } } @@ -2111,18 +2234,18 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_ if (options & LYD_DUP_RECURSIVE) { /* duplicate all the children */ LY_LIST_FOR(orig->child, child) { - LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, LYD_INSERT_NODE_LAST, NULL, options, NULL), error); + LY_CHECK_GOTO(rc = lyd_dup_r(child, trg_ctx, dup, LYD_INSERT_NODE_LAST, NULL, options, NULL), cleanup); } } - LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->name.name, 0, &opaq->name.name), error); - LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->name.prefix, 0, &opaq->name.prefix), error); - LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->name.module_ns, 0, &opaq->name.module_ns), error); - LY_CHECK_GOTO(ret = lydict_insert(trg_ctx, orig->value, 0, &opaq->value), error); + LY_CHECK_GOTO(rc = lydict_insert(trg_ctx, orig->name.name, 0, &opaq->name.name), cleanup); + LY_CHECK_GOTO(rc = lydict_insert(trg_ctx, orig->name.prefix, 0, &opaq->name.prefix), cleanup); + LY_CHECK_GOTO(rc = lydict_insert(trg_ctx, orig->name.module_ns, 0, &opaq->name.module_ns), cleanup); + LY_CHECK_GOTO(rc = lydict_insert(trg_ctx, orig->value, 0, &opaq->value), cleanup); opaq->hints = orig->hints; opaq->format = orig->format; if (orig->val_prefix_data) { - ret = ly_dup_prefix_data(trg_ctx, opaq->format, orig->val_prefix_data, &opaq->val_prefix_data); - LY_CHECK_GOTO(ret, error); + rc = ly_dup_prefix_data(trg_ctx, opaq->format, orig->val_prefix_data, &opaq->val_prefix_data); + LY_CHECK_GOTO(rc, cleanup); } } else if (dup->schema->nodetype & LYD_NODE_TERM) { struct lyd_node_term *term = (struct lyd_node_term *)dup; @@ -2130,15 +2253,15 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_ term->hash = orig->hash; if (trg_ctx == LYD_CTX(node)) { - ret = orig->value.realtype->plugin->duplicate(trg_ctx, &orig->value, &term->value); - LY_CHECK_ERR_GOTO(ret, LOGERR(trg_ctx, ret, "Value duplication failed."), error); + rc = orig->value.realtype->plugin->duplicate(trg_ctx, &orig->value, &term->value); + LY_CHECK_ERR_GOTO(rc, LOGERR(trg_ctx, rc, "Value duplication failed."), cleanup); } else { /* 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), 1, 1, NULL, LY_VALUE_CANON, NULL, + rc = lyd_value_store(trg_ctx, &term->value, type, val_can, strlen(val_can), 1, 1, NULL, LY_VALUE_CANON, NULL, LYD_HINT_DATA, term->schema, NULL); - LY_CHECK_GOTO(ret, error); + LY_CHECK_GOTO(rc, cleanup); } } else if (dup->schema->nodetype & LYD_NODE_INNER) { struct lyd_node_inner *orig = (struct lyd_node_inner *)node; @@ -2147,44 +2270,44 @@ lyd_dup_r(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_ if (options & LYD_DUP_RECURSIVE) { /* create a hash table with the size of the previous hash table (duplicate) */ if (orig->children_ht) { - ((struct lyd_node_inner *)dup)->children_ht = lyht_new(orig->children_ht->size, sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1); + ((struct lyd_node_inner *)dup)->children_ht = lyht_new(orig->children_ht->size, + sizeof(struct lyd_node *), lyd_hash_table_val_equal, NULL, 1); } /* duplicate all the children */ LY_LIST_FOR(orig->child, child) { - LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, LYD_INSERT_NODE_LAST, NULL, options, NULL), error); + LY_CHECK_GOTO(rc = lyd_dup_r(child, trg_ctx, dup, LYD_INSERT_NODE_LAST, NULL, options, NULL), cleanup); } } else if ((dup->schema->nodetype == LYS_LIST) && !(dup->schema->flags & LYS_KEYLESS)) { /* always duplicate keys of a list */ for (child = orig->child; child && lysc_is_key(child->schema); child = child->next) { - LY_CHECK_GOTO(ret = lyd_dup_r(child, trg_ctx, dup, LYD_INSERT_NODE_LAST, NULL, options, NULL), error); + LY_CHECK_GOTO(rc = lyd_dup_r(child, trg_ctx, dup, LYD_INSERT_NODE_LAST, NULL, options, NULL), cleanup); } } lyd_hash(dup); } else if (dup->schema->nodetype & LYD_NODE_ANY) { dup->hash = node->hash; any = (struct lyd_node_any *)node; - LY_CHECK_GOTO(ret = lyd_any_copy_value(dup, &any->value, any->value_type), error); + LY_CHECK_GOTO(rc = lyd_any_copy_value(dup, &any->value, any->value_type), cleanup); } /* insert */ lyd_insert_node(parent, first, dup, insert_order); - if (dup_p) { +cleanup: + if (rc) { + lyd_free_tree(dup); + } else if (dup_p) { *dup_p = dup; } - return LY_SUCCESS; - -error: - lyd_free_tree(dup); - return ret; + return rc; } /** * @brief Get a parent node to connect duplicated subtree to. * * @param[in] node Node (subtree) to duplicate. - * @param[in] trg_ctx Target context for duplicated nodes. + * @param[in,out] trg_ctx Target context for duplicated nodes, may be updated for @p node. * @param[in] parent Initial parent to connect to. * @param[in] options Bitmask of options flags, see @ref dupoptions. * @param[out] dup_parent First duplicated parent node, if any. @@ -2192,24 +2315,30 @@ error: * @return LY_ERR value. */ static LY_ERR -lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_node *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 *orig_parent; + const struct ly_ctx *ctx, *top_ctx; struct lyd_node *iter = NULL; - ly_bool repeat = 1, ext_parent = 0; + ly_bool repeat = 1; + + assert(node && *trg_ctx); *dup_parent = NULL; *local_parent = NULL; - if (node->flags & LYD_EXT) { - ext_parent = 1; + if (!lyd_parent(node)) { + /* no parents */ + return LY_SUCCESS; } + + /* adjust the context for node parent correctly */ + top_ctx = *trg_ctx; + LY_CHECK_RET(lyd_find_ext_ctx_nested(lyd_parent(node), trg_ctx)); + ctx = *trg_ctx; + 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 && (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; @@ -2221,7 +2350,7 @@ lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_c repeat = 0; } else { iter = NULL; - LY_CHECK_RET(lyd_dup_r(orig_parent, trg_ctx, NULL, LYD_INSERT_NODE_DEFAULT, &iter, options, &iter)); + LY_CHECK_RET(lyd_dup_r(orig_parent, ctx, NULL, LYD_INSERT_NODE_DEFAULT, &iter, options, &iter)); /* insert into the previous duplicated parent */ if (*dup_parent) { @@ -2238,13 +2367,14 @@ lyd_dup_get_local_parent(const struct lyd_node *node, const struct ly_ctx *trg_c } if (orig_parent->flags & LYD_EXT) { - ext_parent = 1; + /* parents of the nested extension data, use the original context */ + ctx = top_ctx; } } if (repeat && parent) { /* given parent and created parents chain actually do not interconnect */ - LOGERR(trg_ctx, LY_EINVAL, "None of the duplicated node \"%s\" schema parents match the provided parent \"%s\".", + 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; } @@ -2272,11 +2402,13 @@ lyd_dup(const struct lyd_node *node, const struct ly_ctx *trg_ctx, struct lyd_no assert(node && trg_ctx); + /* create/find parents, adjusts the context as well */ if (options & LYD_DUP_WITH_PARENTS) { - LY_CHECK_GOTO(rc = lyd_dup_get_local_parent(node, trg_ctx, parent, options & (LYD_DUP_WITH_FLAGS | LYD_DUP_NO_META), + LY_CHECK_GOTO(rc = lyd_dup_get_local_parent(node, &trg_ctx, parent, options & (LYD_DUP_WITH_FLAGS | LYD_DUP_NO_META), &top, &local_parent), error); } else { local_parent = parent; + LY_CHECK_GOTO(rc = lyd_find_ext_ctx_nested(node, &trg_ctx), error); } LY_LIST_FOR(node, orig) { @@ -2340,42 +2472,16 @@ error: return rc; } -/** - * @brief Check the context of node and parent when duplicating nodes. - * - * @param[in] node Node to duplicate. - * @param[in] parent Parent of the duplicated node(s). - * @return LY_ERR value. - */ -static LY_ERR -lyd_dup_ctx_check(const struct lyd_node *node, const struct lyd_node_inner *parent) -{ - const struct lyd_node *iter; - - if (!node || !parent) { - return LY_SUCCESS; - } - - if ((LYD_CTX(node) != LYD_CTX(parent))) { - /* try to find top-level ext data parent */ - 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(LYD_CTX(node), LY_EINVAL, "Different contexts used in node duplication."); - return LY_EINVAL; - } - } - - return LY_SUCCESS; -} - LIBYANG_API_DEF LY_ERR lyd_dup_single(const struct lyd_node *node, struct lyd_node_inner *parent, uint32_t options, struct lyd_node **dup) { LY_CHECK_ARG_RET(NULL, node, LY_EINVAL); - LY_CHECK_RET(lyd_dup_ctx_check(node, parent)); + if (parent && (lyd_dup_get_top_ctx(node) != lyd_dup_get_top_ctx(&parent->node))) { + LOGERR(LYD_CTX(node), LY_EINVAL, "Different \"node\" and \"parent\" contexts used in node duplication."); + return LY_EINVAL; + } - return lyd_dup(node, LYD_CTX(node), (struct lyd_node *)parent, options, 1, dup); + return lyd_dup(node, lyd_dup_get_top_ctx(node), (struct lyd_node *)parent, options, 1, dup); } LIBYANG_API_DEF LY_ERR @@ -2383,6 +2489,10 @@ lyd_dup_single_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ctx, uint32_t options, struct lyd_node **dup) { LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL); + if (parent && (trg_ctx != lyd_dup_get_top_ctx(&parent->node))) { + LOGERR(LYD_CTX(node), LY_EINVAL, "Different \"trg_ctx\" and \"parent\" contexts used in node duplication."); + return LY_EINVAL; + } return lyd_dup(node, trg_ctx, (struct lyd_node *)parent, options, 1, dup); } @@ -2391,9 +2501,12 @@ LIBYANG_API_DEF LY_ERR lyd_dup_siblings(const struct lyd_node *node, struct lyd_node_inner *parent, uint32_t options, struct lyd_node **dup) { LY_CHECK_ARG_RET(NULL, node, LY_EINVAL); - LY_CHECK_RET(lyd_dup_ctx_check(node, parent)); + if (parent && (lyd_dup_get_top_ctx(node) != lyd_dup_get_top_ctx(&parent->node))) { + LOGERR(LYD_CTX(node), LY_EINVAL, "Different \"node\" and \"parent\" contexts used in node duplication."); + return LY_EINVAL; + } - return lyd_dup(node, LYD_CTX(node), (struct lyd_node *)parent, options, 0, dup); + return lyd_dup(node, lyd_dup_get_top_ctx(node), (struct lyd_node *)parent, options, 0, dup); } LIBYANG_API_DEF LY_ERR @@ -2401,6 +2514,10 @@ lyd_dup_siblings_to_ctx(const struct lyd_node *node, const struct ly_ctx *trg_ct uint32_t options, struct lyd_node **dup) { LY_CHECK_ARG_RET(trg_ctx, node, trg_ctx, LY_EINVAL); + if (parent && (trg_ctx != lyd_dup_get_top_ctx(&parent->node))) { + LOGERR(LYD_CTX(node), LY_EINVAL, "Different \"trg_ctx\" and \"parent\" contexts used in node duplication."); + return LY_EINVAL; + } return lyd_dup(node, trg_ctx, (struct lyd_node *)parent, options, 0, dup); } @@ -2635,8 +2752,8 @@ lyd_merge(struct lyd_node **target, const struct lyd_node *source, const struct struct lyds_pool lyds = {0}; LY_CHECK_ARG_RET(NULL, target, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(*target ? LYD_CTX(*target) : NULL, source ? LYD_CTX(source) : NULL, mod ? mod->ctx : NULL, - LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, *target ? LYD_CTX(*target) : NULL, source ? LYD_CTX(source) : NULL, + mod ? mod->ctx : NULL, LY_EINVAL); if (!source) { /* nothing to merge */ @@ -2993,7 +3110,7 @@ lyd_find_meta(const struct lyd_meta *first, const struct lys_module *module, con size_t pref_len, name_len; LY_CHECK_ARG_RET(NULL, module || strchr(name, ':'), name, NULL); - LY_CHECK_CTX_EQUAL_RET(first ? first->annotation->module->ctx : NULL, module ? module->ctx : NULL, NULL); + LY_CHECK_CTX_EQUAL_RET(__func__, first ? first->annotation->module->ctx : NULL, module ? module->ctx : NULL, NULL); if (!first) { return NULL; @@ -3188,7 +3305,7 @@ lyd_find_sibling_dup_inst_set(const struct lyd_node *siblings, const struct lyd_ 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); + LY_CHECK_CTX_EQUAL_RET(__func__, siblings ? LYD_CTX(siblings) : NULL, LYD_CTX(target), LY_EINVAL); LY_CHECK_RET(ly_set_new(set)); @@ -3262,6 +3379,11 @@ 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) { + /* find the actual first node */ + while (first->prev->next) { + first = first->prev; + } + first = first->prev; if (first->schema) { /* no opaque nodes */ @@ -3573,8 +3695,8 @@ lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output, LY_CHECK_ARG_RET(NULL, ctx_node, ctx_node->schema, path, LY_EINVAL); /* 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_FIRST, LY_PATH_PRED_SIMPLE, &expr); + ret = ly_path_parse(LYD_CTX(ctx_node), ctx_node->schema, path, 0, 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST, + LY_PATH_PRED_SIMPLE, &expr); LY_CHECK_GOTO(ret, cleanup); /* compile the path */ @@ -3617,8 +3739,11 @@ LY_ERR lyd_get_or_create_leafref_links_record(const struct lyd_node_term *node, struct lyd_leafref_links_rec **record, ly_bool create) { struct ly_ht *ht; + LY_ERR ret = LY_SUCCESS; uint32_t hash; struct lyd_leafref_links_rec rec = {0}; + struct lyd_leafref_links_rec *rec_p = &rec; + struct lyd_leafref_links_rec **rec_p2; assert(node); assert(record); @@ -3633,15 +3758,23 @@ lyd_get_or_create_leafref_links_record(const struct lyd_node_term *node, struct ht = LYD_CTX(node)->leafref_links_ht; hash = lyht_hash((const char *)&node, sizeof node); - if (lyht_find(ht, &rec, hash, (void **)record) == LY_ENOTFOUND) { + if (lyht_find(ht, &rec_p, hash, (void **)&rec_p2) == LY_ENOTFOUND) { if (create) { - LY_CHECK_RET(lyht_insert_no_check(ht, &rec, hash, (void **)record)); + rec_p = calloc(1, sizeof rec); + rec_p->node = node; + LY_CHECK_ERR_RET(!rec_p, LOGMEM(LYD_CTX(node)), LY_EMEM); + ret = lyht_insert_no_check(ht, &rec_p, hash, (void **)&rec_p2); + LY_CHECK_ERR_GOTO(ret, free(rec_p), cleanup); } else { return LY_ENOTFOUND; } } - return LY_SUCCESS; +cleanup: + if (!ret) { + *record = *rec_p2; + } + return ret; } LIBYANG_API_DEF LY_ERR diff --git a/src/tree_data.h b/src/tree_data.h index 18bc679..0aa00cf 100644 --- a/src/tree_data.h +++ b/src/tree_data.h @@ -4,7 +4,7 @@ * @author Michal Vasko * @brief libyang representation of YANG data trees. * - * Copyright (c) 2015 - 2024 CESNET, z.s.p.o. + * Copyright (c) 2015 - 2025 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. @@ -556,15 +556,16 @@ typedef enum { * @brief List of possible value types stored in ::lyd_node_any. */ typedef enum { - LYD_ANYDATA_DATATREE, /**< Value is a pointer to ::lyd_node structure (first sibling). When provided as input parameter, the pointer - is directly connected into the anydata node without duplication, caller is supposed to not manipulate - with the data after a successful call (including calling ::lyd_free_all() on the provided data) */ - LYD_ANYDATA_STRING, /**< Value is a generic string without any knowledge about its format (e.g. anyxml value in JSON encoded - as string). XML sensitive characters (such as & or \>) are automatically escaped when the anydata - is printed in XML format. */ - LYD_ANYDATA_XML, /**< Value is a string containing the serialized XML data. */ - LYD_ANYDATA_JSON, /**< Value is a string containing the data modeled by YANG and encoded as I-JSON. */ - LYD_ANYDATA_LYB /**< Value is a memory chunk with the serialized data tree in LYB format. */ + LYD_ANYDATA_DATATREE, /**< Value is a pointer to ::lyd_node structure (first sibling). When provided as input + parameter, the pointer is directly connected into the anydata node without duplication, + caller is supposed to not manipulate with the data after a successful call (including + calling ::lyd_free_all() on the provided data) */ + LYD_ANYDATA_STRING, /**< Value is a generic string without any knowledge about its format (e.g. anyxml value in + JSON encoded as string). XML sensitive characters (such as & or \>) are automatically + escaped when the anydata is printed in XML format. */ + LYD_ANYDATA_XML, /**< Value is a string containing the serialized XML data. */ + LYD_ANYDATA_JSON, /**< Value is a string containing the data modeled by YANG and encoded as I-JSON. */ + LYD_ANYDATA_LYB /**< Value is a memory chunk with the serialized data tree in LYB format. */ } LYD_ANYDATA_VALUETYPE; /** @} */ @@ -944,6 +945,7 @@ struct lyd_node_any { #define LYD_VALHINT_NUM64 0x0010 /**< value is allowed to be an int64 or uint64 */ #define LYD_VALHINT_BOOLEAN 0x0020 /**< value is allowed to be a boolean */ #define LYD_VALHINT_EMPTY 0x0040 /**< value is allowed to be empty */ +#define LYD_VALHINT_STRING_DATATYPES 0x0080 /**< boolean and numeric fields are allowed to be quoted */ /** * @} lydvalhints */ @@ -1554,8 +1556,8 @@ LIBYANG_API_DECL LY_ERR lyd_new_attr2(struct lyd_node *parent, const char *modul * and @p value is set, the predicate is preferred. * * 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. + * (or lists) either positional predicate can be used or leaf-list (or key) 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, * it may no longer be first if @p path is absolute and starts with a non-existing top-level node inserted @@ -1591,7 +1593,7 @@ LIBYANG_API_DECL LY_ERR lyd_new_path(struct lyd_node *parent, const struct ly_ct * @param[in] value_type Anyxml/anydata node @p value type. * @param[in] options Bitmask of options, see @ref newvaloptions. * @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. + * @param[out] new_node Optional target node of @p path (the last created node, the list instance in case of a list). * @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. @@ -2005,7 +2007,8 @@ LIBYANG_API_DECL LY_ERR lyd_dup_single(const struct lyd_node *node, struct lyd_n * @brief Create a copy of the specified data tree @p node. Schema references are assigned from @p trg_ctx. * * @param[in] node Data tree node to be duplicated. - * @param[in] trg_ctx Target context for duplicated nodes. + * @param[in] trg_ctx Target context for duplicated nodes. In case of mixed contexts in @p node subtree or parents + * (schema mount data), this is the context of top-level nodes. * @param[in] parent Optional parent node where to connect the duplicated node(s). If set in combination with * ::LYD_DUP_WITH_PARENTS, the missing parents' chain is duplicated and connected with @p parent. * @param[in] options Bitmask of options flags, see @ref dupoptions. @@ -2035,7 +2038,8 @@ LIBYANG_API_DECL LY_ERR lyd_dup_siblings(const struct lyd_node *node, struct lyd * from @p trg_ctx. * * @param[in] node Data tree node to be duplicated. - * @param[in] trg_ctx Target context for duplicated nodes. + * @param[in] trg_ctx Target context for duplicated nodes. In case of mixed contexts in @p node subtree or parents + * (schema mount data), this is the context of top-level nodes. * @param[in] parent Optional parent node where to connect the duplicated node(s). If set in combination with * ::LYD_DUP_WITH_PARENTS, the missing parents' chain is duplicated and connected with @p parent. * @param[in] options Bitmask of options flags, see @ref dupoptions. @@ -2158,12 +2162,18 @@ LIBYANG_API_DECL LY_ERR lyd_merge_module(struct lyd_node **target, const struct * * Default behavior: * - any default nodes are treated as non-existent and ignored. + * - nodes with 'none' operation can appear only in case a leaf node has not changed its value and only its + * default flag. + * - metadata differences are not included in the diff. * @{ */ #define LYD_DIFF_DEFAULTS 0x01 /**< Default nodes in the trees are not ignored but treated similarly to explicit nodes. Also, leaves and leaf-lists are added into diff even in case only their default flag (state) was changed. */ +#define LYD_DIFF_META 0x02 /**< All metadata are compared and the full difference reported in the diff always in + the form of 'yang:meta-' metadata. Also, equal nodes with only changes + in their metadata will be present in the diff with the 'none' operation. */ /** @} diffoptions */ @@ -2661,7 +2671,7 @@ 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. + * @param[in] value Valid string date-and-time value, the string may continue after the value (be longer). * @param[out] time UNIX timestamp. * @param[out] fractions_s Optional fractions of a second, set to NULL if none. * @return LY_ERR value. @@ -2681,7 +2691,7 @@ LIBYANG_API_DECL LY_ERR ly_time_time2str(time_t time, const char *fractions_s, c /** * @brief Convert date-and-time from string to timespec. * - * @param[in] value Valid string date-and-time value. + * @param[in] value Valid string date-and-time value, the string may continue after the value (be longer). * @param[out] ts Timespec. * @return LY_ERR value. */ diff --git a/src/tree_data_common.c b/src/tree_data_common.c index ce9479d..1be6acf 100644 --- a/src/tree_data_common.c +++ b/src/tree_data_common.c @@ -516,6 +516,7 @@ lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct ly if (!value) { value = ""; + value_len = 0; } if (incomplete) { *incomplete = 0; @@ -1626,9 +1627,6 @@ 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); @@ -1670,7 +1668,7 @@ ly_time_str2time(const char *value, time_t *time, char **fractions_s) int64_t shift, shift_m; time_t t; - LY_CHECK_ARG_RET(NULL, value, strlen(value) > 17, time, LY_EINVAL); + LY_CHECK_ARG_RET(NULL, value, strnlen(value, 18) > 17, time, LY_EINVAL); tm.tm_year = atoi(&value[0]) - 1900; tm.tm_mon = atoi(&value[5]) - 1; @@ -1776,9 +1774,6 @@ ly_time_time2str(time_t time, const char *fractions_s, char **str) LY_CHECK_ARG_RET(NULL, str, LY_EINVAL); - /* init timezone */ - tzset(); - /* convert */ if (!localtime_r(&time, &tm)) { return LY_ESYS; diff --git a/src/tree_data_free.c b/src/tree_data_free.c index 32d32cc..893dfec 100644 --- a/src/tree_data_free.c +++ b/src/tree_data_free.c @@ -191,7 +191,8 @@ lyd_free_leafref_nodes(const struct lyd_node_term *node) /* free entry itself from hash table */ ht = LYD_CTX(node)->leafref_links_ht; hash = lyht_hash((const char *)&node, sizeof node); - lyht_remove(ht, rec, hash); + lyht_remove(ht, &rec, hash); + free(rec); } /** diff --git a/src/tree_data_new.c b/src/tree_data_new.c index 76f9a62..0efa761 100644 --- a/src/tree_data_new.c +++ b/src/tree_data_new.c @@ -505,7 +505,7 @@ lyd_new_inner(struct lyd_node *parent, const struct lys_module *module, const ch 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_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL); if (!module) { module = parent->schema->module; @@ -621,7 +621,7 @@ lyd_new_list(struct lyd_node *parent, const struct lys_module *module, const cha va_list ap; 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_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL); LY_CHECK_RET(lyd_new_val_get_format(options, &format)); LY_CHECK_ARG_RET(ctx, !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)), LY_EINVAL); @@ -726,7 +726,7 @@ lyd_new_list2(struct lyd_node *parent, const struct lys_module *module, const ch uint32_t getnext_opts = (options & LYD_NEW_VAL_OUTPUT) ? LYS_GETNEXT_OUTPUT : 0; 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_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL); if (!module) { module = parent->schema->module; @@ -781,7 +781,7 @@ lyd_new_list3(struct lyd_node *parent, const struct lys_module *module, const ch LY_CHECK_RET(lyd_new_val_get_format(options, &format)); LY_CHECK_ARG_RET(ctx, parent || module, parent || node, name, (format != LY_VALUE_LYB) || value_lengths, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL); LY_CHECK_ARG_RET(ctx, !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)), LY_EINVAL); /* create the list node */ @@ -845,7 +845,7 @@ _lyd_new_term(struct lyd_node *parent, const struct lys_module *module, const ch LY_VALUE_FORMAT format; 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_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL); LY_CHECK_RET(lyd_new_val_get_format(options, &format)); LY_CHECK_ARG_RET(ctx, !(store_only && (format == LY_VALUE_CANON || format == LY_VALUE_LYB)), LY_EINVAL); @@ -941,7 +941,7 @@ lyd_new_any(struct lyd_node *parent, const struct lys_module *module, const char 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); + LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL); if (!module) { module = parent->schema->module; @@ -1007,7 +1007,7 @@ lyd_new_meta(const struct ly_ctx *ctx, struct lyd_node *parent, const struct lys ly_bool store_only = options & LYD_NEW_VAL_STORE_ONLY; LY_CHECK_ARG_RET(ctx, ctx || parent, name, module || strchr(name, ':'), parent || meta, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(ctx, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, ctx, parent ? LYD_CTX(parent) : NULL, module ? module->ctx : NULL, LY_EINVAL); if (!ctx) { ctx = module ? module->ctx : LYD_CTX(parent); } @@ -1050,7 +1050,7 @@ lyd_new_meta2(const struct ly_ctx *ctx, struct lyd_node *parent, uint32_t option ly_bool store_only = options & LYD_NEW_VAL_STORE_ONLY; LY_CHECK_ARG_RET(NULL, ctx, attr, parent || meta, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL); if (parent && !parent->schema) { LOGERR(ctx, LY_EINVAL, "Cannot add metadata to an opaque node \"%s\".", @@ -1092,7 +1092,7 @@ lyd_new_opaq(struct lyd_node *parent, const struct ly_ctx *ctx, const char *name uint32_t hints = 0; LY_CHECK_ARG_RET(ctx, parent || ctx, parent || node, name, module_name, !prefix || !strcmp(prefix, module_name), LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL); if (!ctx) { ctx = LYD_CTX(parent); @@ -1122,7 +1122,7 @@ lyd_new_opaq2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *nam struct lyd_node *ret = NULL; LY_CHECK_ARG_RET(ctx, parent || ctx, parent || node, name, module_ns, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, ctx, parent ? LYD_CTX(parent) : NULL, LY_EINVAL); if (!ctx) { ctx = LYD_CTX(parent); @@ -1479,13 +1479,14 @@ cleanup: * @param[in] value_len Length of @p value. * @param[in] value_type Type of @p value for anydata/anyxml node. * @param[in] format Format of @p value. + * @param[in] any_use_value Whether to spend @p value when updating an anydata/anyxml node or not. * @param[out] new_parent Set to @p node if the value was updated, otherwise set to NULL. * @param[out] new_node Set to @p node if the value was updated, otherwise set to NULL. * @return LY_ERR value. */ static LY_ERR lyd_new_path_update(struct lyd_node *node, const void *value, size_t value_len, LYD_ANYDATA_VALUETYPE value_type, - LY_VALUE_FORMAT format, struct lyd_node **new_parent, struct lyd_node **new_node) + LY_VALUE_FORMAT format, ly_bool any_use_value, struct lyd_node **new_parent, struct lyd_node **new_node) { LY_ERR ret = LY_SUCCESS; struct lyd_node *new_any; @@ -1525,12 +1526,14 @@ lyd_new_path_update(struct lyd_node *node, const void *value, size_t value_len, case LYS_ANYDATA: case LYS_ANYXML: /* create a new any node */ - LY_CHECK_RET(lyd_create_any(node->schema, value, value_type, 0, &new_any)); + LY_CHECK_RET(lyd_create_any(node->schema, value, value_type, any_use_value, &new_any)); /* compare with the existing one */ if (lyd_compare_single(node, new_any, 0)) { /* not equal, switch values (so that we can use generic node free) */ - ((struct lyd_node_any *)new_any)->value = ((struct lyd_node_any *)node)->value; + value = ((struct lyd_node_any *)new_any)->value.str; + value_type = ((struct lyd_node_any *)new_any)->value_type; + ((struct lyd_node_any *)new_any)->value.str = ((struct lyd_node_any *)node)->value.str; ((struct lyd_node_any *)new_any)->value_type = ((struct lyd_node_any *)node)->value_type; ((struct lyd_node_any *)node)->value.str = value; ((struct lyd_node_any *)node)->value_type = value_type; @@ -1679,7 +1682,7 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly LY_CHECK_GOTO(ret = lyd_new_val_get_format(options, &format), cleanup); /* parse path */ - LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST, + LY_CHECK_GOTO(ret = ly_path_parse(ctx, NULL, path, 0, 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST, LY_PATH_PRED_SIMPLE, &exp), cleanup); /* compile path */ @@ -1708,7 +1711,7 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly } /* update the existing node */ - ret = lyd_new_path_update(node, value, value_len, value_type, format, &nparent, &nnode); + ret = lyd_new_path_update(node, value, value_len, value_type, format, any_use_value, &nparent, &nnode); goto cleanup; } /* else we were not searching for the whole path */ } else if (r == LY_EINCOMPLETE) { @@ -1811,7 +1814,8 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly if (val) { LY_CHECK_GOTO(ret = lyd_create_term2(schema, val, &node), cleanup); } else { - LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, 0, store_only, NULL, format, NULL, LYD_HINT_DATA, NULL, &node), cleanup); + LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, 0, store_only, NULL, format, NULL, + LYD_HINT_DATA, NULL, &node), cleanup); } break; case LYS_LEAF: @@ -1836,15 +1840,16 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly if (value && (format == LY_VALUE_JSON) && !ly_strncmp("[null]", value, value_len)) { hints |= LYD_VALHINT_EMPTY; } - LY_CHECK_GOTO(ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, - schema->module->name, strlen(schema->module->name), value, value_len, NULL, format, NULL, hints, &node), - cleanup); + ret = lyd_create_opaq(ctx, schema->name, strlen(schema->name), NULL, 0, schema->module->name, + strlen(schema->module->name), value, value_len, NULL, format, NULL, hints, &node); + LY_CHECK_GOTO(ret, cleanup); break; } } /* create a leaf instance */ - LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, 0, store_only, NULL, format, NULL, LYD_HINT_DATA, NULL, &node), cleanup); + LY_CHECK_GOTO(ret = lyd_create_term(schema, value, value_len, 0, store_only, NULL, format, NULL, + LYD_HINT_DATA, NULL, &node), cleanup); break; case LYS_ANYDATA: case LYS_ANYXML: @@ -1903,7 +1908,7 @@ lyd_new_path(struct lyd_node *parent, const struct ly_ctx *ctx, const char *path { LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, !(options & LYD_NEW_VAL_BIN) || !(options & LYD_NEW_VAL_CANON), LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, ctx, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, ctx, LY_EINVAL); return lyd_new_path_(parent, ctx, NULL, path, value, 0, LYD_ANYDATA_STRING, options, node, NULL); } @@ -1915,7 +1920,7 @@ lyd_new_path2(struct lyd_node *parent, const struct ly_ctx *ctx, const char *pat { LY_CHECK_ARG_RET(ctx, parent || ctx, path, (path[0] == '/') || parent, !(options & LYD_NEW_VAL_BIN) || !(options & LYD_NEW_VAL_CANON), LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, ctx, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, ctx, LY_EINVAL); return lyd_new_path_(parent, ctx, NULL, path, value, value_len, value_type, options, new_parent, new_node); } @@ -1928,7 +1933,7 @@ lyd_new_ext_path(struct lyd_node *parent, const struct lysc_ext_instance *ext, c LY_CHECK_ARG_RET(ctx, ext, path, (path[0] == '/') || parent, !(options & LYD_NEW_VAL_BIN) || !(options & LYD_NEW_VAL_CANON), LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(parent ? LYD_CTX(parent) : NULL, ctx, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, parent ? LYD_CTX(parent) : NULL, ctx, LY_EINVAL); return lyd_new_path_(parent, ctx, ext, path, value, 0, LYD_ANYDATA_STRING, options, node, NULL); } @@ -2150,7 +2155,7 @@ lyd_new_implicit_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t LY_ERR rc = LY_SUCCESS; LY_CHECK_ARG_RET(ctx, tree, *tree || ctx, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, ctx, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, *tree ? LYD_CTX(*tree) : NULL, ctx, LY_EINVAL); if (diff) { *diff = NULL; } @@ -2191,7 +2196,7 @@ lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module, struct ly_ht *getnext_ht = NULL; LY_CHECK_ARG_RET(NULL, tree, module, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, module ? module->ctx : NULL, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, *tree ? LYD_CTX(*tree) : NULL, module ? module->ctx : NULL, LY_EINVAL); if (diff) { *diff = NULL; } diff --git a/src/tree_edit.h b/src/tree_edit.h index af13fd4..e3c1192 100644 --- a/src/tree_edit.h +++ b/src/tree_edit.h @@ -148,7 +148,7 @@ void *ly_realloc(void *ptr, size_t size); } \ p__ = (char *)((LY_ARRAY_COUNT_TYPE*)(p__) + 1); \ memcpy(&(ARRAY), &p__, sizeof p__); \ - if (ARRAY) { \ + if ((ARRAY) && (SIZE > 0)) { \ memset(&(ARRAY)[*((LY_ARRAY_COUNT_TYPE*)(p__) - 1)], 0, (SIZE) * sizeof *(ARRAY)); \ } \ } diff --git a/src/tree_schema.c b/src/tree_schema.c index 21e2413..e1c979b 100644 --- a/src/tree_schema.c +++ b/src/tree_schema.c @@ -1,9 +1,10 @@ /** * @file tree_schema.c * @author Radek Krejci + * @author Michal Vasko * @brief Schema tree implementation * - * Copyright (c) 2015 - 2018 CESNET, z.s.p.o. + * Copyright (c) 2015 - 2025 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. @@ -410,7 +411,7 @@ lys_find_child(const struct lysc_node *parent, const struct lys_module *module, const struct lysc_node *node = NULL; LY_CHECK_ARG_RET(NULL, module, name, NULL); - LY_CHECK_CTX_EQUAL_RET(parent ? parent->module->ctx : NULL, module->ctx, NULL); + LY_CHECK_CTX_EQUAL_RET(__func__, parent ? parent->module->ctx : NULL, module->ctx, NULL); if (!nodetype) { nodetype = LYS_NODETYPE_MASK; } @@ -447,7 +448,7 @@ lys_find_xpath_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, uint32_t i; LY_CHECK_ARG_RET(NULL, ctx || ctx_node, xpath, set, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(ctx, ctx_node ? ctx_node->module->ctx : NULL, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, ctx, ctx_node ? ctx_node->module->ctx : NULL, LY_EINVAL); if (!(options & LYXP_SCNODE_ALL)) { options |= LYXP_SCNODE; } @@ -494,7 +495,7 @@ lys_find_expr_atoms(const struct lysc_node *ctx_node, const struct lys_module *c uint32_t i; LY_CHECK_ARG_RET(NULL, cur_mod, expr, prefixes, set, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(ctx_node ? ctx_node->module->ctx : NULL, cur_mod->ctx, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, ctx_node ? ctx_node->module->ctx : NULL, cur_mod->ctx, LY_EINVAL); if (!(options & LYXP_SCNODE_ALL)) { options = LYXP_SCNODE; } @@ -542,9 +543,9 @@ lys_find_xpath(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const uint32_t i; LY_CHECK_ARG_RET(NULL, ctx || ctx_node, xpath, set, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(ctx, ctx_node ? ctx_node->module->ctx : NULL, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, ctx, ctx_node ? ctx_node->module->ctx : NULL, LY_EINVAL); if (!(options & LYXP_SCNODE_ALL)) { - options = LYXP_SCNODE; + options |= LYXP_SCNODE; } if (!ctx) { ctx = ctx_node->module->ctx; @@ -624,14 +625,14 @@ lys_find_path_atoms(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, struct ly_path *p = NULL; LY_CHECK_ARG_RET(ctx, ctx || ctx_node, path, set, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(ctx, ctx_node ? ctx_node->module->ctx : NULL, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, ctx, ctx_node ? ctx_node->module->ctx : NULL, LY_EINVAL); if (!ctx) { ctx = ctx_node->module->ctx; } /* parse */ - ret = ly_path_parse(ctx, ctx_node, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST, + ret = ly_path_parse(ctx, ctx_node, path, 0, 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST, LY_PATH_PRED_SIMPLE, &expr); LY_CHECK_GOTO(ret, cleanup); @@ -658,15 +659,15 @@ lys_find_path(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const LY_ERR ret; uint8_t oper; - LY_CHECK_ARG_RET(ctx, ctx || ctx_node, NULL); - LY_CHECK_CTX_EQUAL_RET(ctx, ctx_node ? ctx_node->module->ctx : NULL, NULL); + LY_CHECK_ARG_RET(ctx, ctx || ctx_node, path, NULL); + LY_CHECK_CTX_EQUAL_RET(__func__, ctx, ctx_node ? ctx_node->module->ctx : NULL, NULL); if (!ctx) { ctx = ctx_node->module->ctx; } /* parse */ - ret = ly_path_parse(ctx, ctx_node, path, strlen(path), 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST, + ret = ly_path_parse(ctx, ctx_node, path, 0, 0, LY_PATH_BEGIN_EITHER, LY_PATH_PREFIX_FIRST, LY_PATH_PRED_SIMPLE, &expr); LY_CHECK_GOTO(ret, cleanup); @@ -787,6 +788,7 @@ lysc_path_until(const struct lysc_node *node, const struct lysc_node *parent, LY if (buffer) { strcpy(buffer, "/"); } else { + free(path); path = strdup("/"); } } @@ -1420,35 +1422,147 @@ next_iter: return LY_SUCCESS; } +/** + * @brief Generate a 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 + */ +static 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"); + } + } +} + +/** + * @brief Check created (sub)module that it matches the expected module. + * + * @param[in] ctx Context to use. + * @param[in] mod Parsed module. + * @param[in] submod Parsed submodule. + * @param[in] mod_data Expected module data. + * @return LY_SUCCESS on success; + * @return LY_EEXIST if the same module already exists; + * @return LY_ERR on error. + */ +static LY_ERR +lysp_load_module_data_check(const struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, + const struct lysp_load_module_data *mod_data) +{ + const char *name; + uint8_t latest_revision; + struct lysp_revision *revs; + + name = mod ? mod->mod->name : submod->name; + revs = mod ? mod->revs : submod->revs; + latest_revision = mod ? mod->mod->latest_revision : submod->latest_revision; + + if (mod_data->name) { + /* check name of the parsed model */ + if (strcmp(mod_data->name, name)) { + LOGERR(ctx, LY_EINVAL, "Unexpected module \"%s\" parsed instead of \"%s\".", name, mod_data->name); + return LY_EINVAL; + } + } + + if (mod_data->revision) { + /* check revision of the parsed model */ + if (!revs || strcmp(mod_data->revision, revs[0].date)) { + LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").", name, + revs ? revs[0].date : "none", mod_data->revision); + return LY_EINVAL; + } + } else if (!latest_revision) { + /* do not log, we just need to drop the schema and use the latest revision from the context */ + return LY_EEXIST; + } + + if (submod) { + assert(mod_data->submoduleof); + + /* check that the submodule belongs-to our module */ + if (strcmp(mod_data->submoduleof, submod->mod->name)) { + LOGVAL(ctx, LYVE_REFERENCE, "Included \"%s\" submodule from \"%s\" belongs-to a different module \"%s\".", + submod->name, mod_data->submoduleof, submod->mod->name); + return LY_EVALID; + } + /* check circular dependency */ + if (submod->parsing) { + LOGVAL(ctx, LYVE_REFERENCE, "A circular dependency (include) for module \"%s\".", submod->name); + return LY_EVALID; + } + } + + if (mod_data->path) { + ly_check_module_filename(ctx, name, revs ? revs[0].date : NULL, mod_data->path); + } + + return LY_SUCCESS; +} + LY_ERR lys_parse_submodule(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, struct lysp_ctx *main_ctx, - LY_ERR (*custom_check)(const struct ly_ctx *, struct lysp_module *, struct lysp_submodule *, void *), - void *check_data, struct ly_set *new_mods, struct lysp_submodule **submodule) + const struct lysp_load_module_data *mod_data, struct ly_set *new_mods, struct lysp_submodule **submodule) { - LY_ERR ret; + LY_ERR rc = LY_SUCCESS, r; struct lysp_submodule *submod = NULL, *latest_sp; struct lysp_yang_ctx *yangctx = NULL; struct lysp_yin_ctx *yinctx = NULL; - struct lysp_ctx *pctx; + struct lysp_ctx *pctx = NULL; struct lysf_ctx fctx = {.ctx = ctx}; + const char *submod_name; LY_CHECK_ARG_RET(ctx, ctx, in, LY_EINVAL); switch (format) { case LYS_IN_YIN: - ret = yin_parse_submodule(&yinctx, ctx, main_ctx, in, &submod); + rc = yin_parse_submodule(&yinctx, ctx, main_ctx, in, &submod); pctx = (struct lysp_ctx *)yinctx; break; case LYS_IN_YANG: - ret = yang_parse_submodule(&yangctx, ctx, main_ctx, in, &submod); + rc = yang_parse_submodule(&yangctx, ctx, main_ctx, in, &submod); pctx = (struct lysp_ctx *)yangctx; break; default: LOGERR(ctx, LY_EINVAL, "Invalid schema input format."); - ret = LY_EINVAL; + rc = LY_EINVAL; break; } - LY_CHECK_GOTO(ret, error); + LY_CHECK_GOTO(rc, cleanup); assert(submod); /* make sure that the newest revision is at position 0 */ @@ -1475,8 +1589,21 @@ lys_parse_submodule(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, s submod->latest_revision = 1; } - if (custom_check) { - LY_CHECK_GOTO(ret = custom_check(ctx, NULL, submod, check_data), error); + if (mod_data) { + /* check the parsed submodule it is as expected */ + r = lysp_load_module_data_check(ctx, NULL, submod, mod_data); + if (r == LY_EEXIST) { + /* not an error, the submodule already exists so free this one */ + ly_set_erase(&pctx->tpdfs_nodes, NULL); + ly_set_erase(&pctx->grps_nodes, NULL); + ly_set_erase(&pctx->ext_inst, NULL); + lysp_module_free(&fctx, (struct lysp_module *)submod); + submod = NULL; + goto cleanup; + } else if (r) { + rc = r; + goto cleanup; + } } if (latest_sp) { @@ -1486,29 +1613,47 @@ lys_parse_submodule(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, s lys_parser_fill_filepath(ctx, in, &submod->filepath); /* resolve imports and includes */ - LY_CHECK_GOTO(ret = lysp_resolve_import_include(pctx, (struct lysp_module *)submod, new_mods), error); + LY_CHECK_GOTO(rc = lysp_resolve_import_include(pctx, (struct lysp_module *)submod, new_mods), cleanup); + +cleanup: + if (rc) { + if (submod && submod->name) { + submod_name = submod->name; + } else if (mod_data) { + submod_name = mod_data->name; + } else { + submod_name = NULL; + } + if (submod_name) { + LOGERR(ctx, rc, "Parsing submodule \"%s\" failed.", submod_name); + } else { + LOGERR(ctx, rc, "Parsing submodule failed."); + } + + if (pctx) { + ly_set_erase(&pctx->tpdfs_nodes, NULL); + ly_set_erase(&pctx->grps_nodes, NULL); + ly_set_erase(&pctx->ext_inst, NULL); + } + lysp_module_free(&fctx, (struct lysp_module *)submod); + } else if (submod) { + *submodule = submod; + + /* merge submod unres into main_ctx unres */ + ly_set_merge(&pctx->main_ctx->tpdfs_nodes, &pctx->tpdfs_nodes, 1, NULL); + ly_set_erase(&pctx->tpdfs_nodes, NULL); + ly_set_merge(&pctx->main_ctx->grps_nodes, &pctx->grps_nodes, 1, NULL); + ly_set_erase(&pctx->grps_nodes, NULL); + ly_set_merge(&pctx->main_ctx->ext_inst, &pctx->ext_inst, 1, NULL); + ly_set_erase(&pctx->ext_inst, NULL); + } if (format == LYS_IN_YANG) { lysp_yang_ctx_free(yangctx); } else { lysp_yin_ctx_free(yinctx); } - *submodule = submod; - return LY_SUCCESS; - -error: - if (!submod || !submod->name) { - LOGERR(ctx, ret, "Parsing submodule failed."); - } else { - LOGERR(ctx, ret, "Parsing submodule \"%s\" failed.", submod->name); - } - lysp_module_free(&fctx, (struct lysp_module *)submod); - if (format == LYS_IN_YANG) { - lysp_yang_ctx_free(yangctx); - } else { - lysp_yin_ctx_free(yinctx); - } - return ret; + return rc; } /** @@ -1524,6 +1669,9 @@ lysp_add_internal_ietf_netconf(struct lysp_ctx *pctx, struct lysp_module *mod) { struct lysp_ext_instance *extp, *prev_exts = mod->exts; struct lysp_stmt *stmt; + struct lysp_node_leaf *leaf; + struct lysp_node_container *cont; + struct lysp_type_enum *enm; struct lysp_import *imp; uint32_t idx; @@ -1677,6 +1825,107 @@ lysp_add_internal_ietf_netconf(struct lysp_ctx *pctx, struct lysp_module *mod) pctx->ext_inst.objs[idx] = mod->exts; } + /* + * 4) rpc-error + */ + LY_LIST_NEW_RET(mod->mod->ctx, &mod->data, cont, next, LY_EMEM); + cont->nodetype = LYS_CONTAINER; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "rpc-error", 0, &cont->name)); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "presence", 0, &cont->presence)); + cont->flags = LYS_INTERNAL; + + LY_LIST_NEW_RET(mod->mod->ctx, &cont->child, leaf, next, LY_EMEM); + leaf->nodetype = LYS_LEAF; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "error-type", 0, &leaf->name)); + leaf->flags = LYS_INTERNAL; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enumeration", 0, &leaf->type.name)); + leaf->type.pmod = mod; + leaf->type.flags = LYS_SET_ENUM; + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "transport", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "rpc", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "protocol", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "application", 0, &enm->name)); + + LY_LIST_NEW_RET(mod->mod->ctx, &cont->child, leaf, next, LY_EMEM); + leaf->nodetype = LYS_LEAF; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "error-tag", 0, &leaf->name)); + leaf->flags = LYS_INTERNAL; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enumeration", 0, &leaf->type.name)); + leaf->type.pmod = mod; + leaf->type.flags = LYS_SET_ENUM; + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "in-use", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "invalid-value", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "too-big", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "missing-attribute", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "bad-attribute", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "unknown-attribute", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "missing-element", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "bad-element", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "unknown-element", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "unknown-namespace", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "access-denied", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "lock-denied", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "resource-denied", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "rollback-failed", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "data-exists", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "data-missing", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "operation-not-supported", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "operation-failed", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "partial-operation", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "malformed-message", 0, &enm->name)); + + LY_LIST_NEW_RET(mod->mod->ctx, &cont->child, leaf, next, LY_EMEM); + leaf->nodetype = LYS_LEAF; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "error-severity", 0, &leaf->name)); + leaf->flags = LYS_INTERNAL; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "enumeration", 0, &leaf->type.name)); + leaf->type.pmod = mod; + leaf->type.flags = LYS_SET_ENUM; + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "error", 0, &enm->name)); + LY_ARRAY_NEW_RET(mod->mod->ctx, leaf->type.enums, enm, LY_EMEM); + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "warning", 0, &enm->name)); + + LY_LIST_NEW_RET(mod->mod->ctx, &cont->child, leaf, next, LY_EMEM); + leaf->nodetype = LYS_LEAF; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "error-app-tag", 0, &leaf->name)); + leaf->flags = LYS_INTERNAL; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "string", 0, &leaf->type.name)); + leaf->type.pmod = mod; + + LY_LIST_NEW_RET(mod->mod->ctx, &cont->child, leaf, next, LY_EMEM); + leaf->nodetype = LYS_LEAF; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "error-path", 0, &leaf->name)); + leaf->flags = LYS_INTERNAL; + LY_CHECK_RET(lydict_insert(mod->mod->ctx, "yang_:xpath1.0", 0, &leaf->type.name)); + leaf->type.pmod = mod; + + /* the rest are opaque nodes, error-message (because of 'xml:lang' attribute) and error-info (because can be any nodes) */ + /* create new imports for the used prefixes */ LY_ARRAY_NEW_RET(mod->mod->ctx, mod->imports, imp, LY_EMEM); LY_CHECK_RET(lydict_insert(mod->mod->ctx, "ietf-yang-metadata", 0, &imp->name)); @@ -1810,17 +2059,16 @@ lysp_add_internal_yang(struct lysp_ctx *pctx, struct lysp_module *mod) } LY_ERR -lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, - LY_ERR (*custom_check)(const struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *data), - void *check_data, struct ly_set *new_mods, struct lys_module **module) +lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const struct lysp_load_module_data *mod_data, + struct ly_set *new_mods, struct lys_module **module) { + LY_ERR rc = LY_SUCCESS, r; struct lys_module *mod = NULL, *latest, *mod_dup = NULL; - LY_ERR ret; struct lysp_yang_ctx *yangctx = NULL; struct lysp_yin_ctx *yinctx = NULL; struct lysp_ctx *pctx = NULL; struct lysf_ctx fctx = {.ctx = ctx}; - ly_bool module_created = 0; + ly_bool mod_created = 0, mod_exists = 0; assert(ctx && in && new_mods); @@ -1835,24 +2083,24 @@ lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, /* parse */ switch (format) { case LYS_IN_YIN: - ret = yin_parse_module(&yinctx, in, mod); + rc = yin_parse_module(&yinctx, in, mod); pctx = (struct lysp_ctx *)yinctx; break; case LYS_IN_YANG: - ret = yang_parse_module(&yangctx, in, mod); + rc = yang_parse_module(&yangctx, in, mod); pctx = (struct lysp_ctx *)yangctx; break; default: LOGERR(ctx, LY_EINVAL, "Invalid schema input format."); - ret = LY_EINVAL; + rc = LY_EINVAL; break; } - LY_CHECK_GOTO(ret, cleanup); + LY_CHECK_GOTO(rc, cleanup); /* make sure that the newest revision is at position 0 */ lysp_sort_revisions(mod->parsed->revs); if (mod->parsed->revs) { - LY_CHECK_GOTO(ret = lydict_insert(ctx, mod->parsed->revs[0].date, 0, &mod->revision), cleanup); + LY_CHECK_GOTO(rc = lydict_insert(ctx, mod->parsed->revs[0].date, 0, &mod->revision), cleanup); } /* decide the latest revision */ @@ -1876,8 +2124,16 @@ lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, mod->latest_revision = LYS_MOD_LATEST_REV; } - if (custom_check) { - LY_CHECK_GOTO(ret = custom_check(ctx, mod->parsed, NULL, check_data), cleanup); + if (mod_data) { + /* check the parsed module it is as expected */ + r = lysp_load_module_data_check(ctx, mod->parsed, NULL, mod_data); + if (r == LY_EEXIST) { + mod_exists = 1; + goto cleanup; + } else if (r) { + rc = r; + goto cleanup; + } } /* check whether it is not already in the context in the same revision */ @@ -1894,7 +2150,7 @@ lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, if (mod_dup && (mod_dup->revision == mod->revision)) { LOGERR(ctx, LY_EINVAL, "Two different modules (\"%s\" and \"%s\") have the same namespace \"%s\".", mod_dup->name, mod->name, mod->ns); - ret = LY_EINVAL; + rc = LY_EINVAL; goto cleanup; } @@ -1909,7 +2165,7 @@ lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, break; case LY_IN_ERROR: LOGINT(ctx); - ret = LY_EINT; + rc = LY_EINT; goto cleanup; } lys_parser_fill_filepath(ctx, in, &mod->filepath); @@ -1920,53 +2176,51 @@ lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, /* add internal data in case specific modules were parsed */ if (!strcmp(mod->name, "ietf-netconf")) { - LY_CHECK_GOTO(ret = lysp_add_internal_ietf_netconf(pctx, mod->parsed), cleanup); + LY_CHECK_GOTO(rc = lysp_add_internal_ietf_netconf(pctx, mod->parsed), cleanup); } else if (!strcmp(mod->name, "ietf-netconf-with-defaults")) { - LY_CHECK_GOTO(ret = lysp_add_internal_ietf_netconf_with_defaults(pctx, mod->parsed), cleanup); + LY_CHECK_GOTO(rc = lysp_add_internal_ietf_netconf_with_defaults(pctx, mod->parsed), cleanup); } else if (!strcmp(mod->name, "yang")) { - LY_CHECK_GOTO(ret = lysp_add_internal_yang(pctx, mod->parsed), cleanup); + LY_CHECK_GOTO(rc = lysp_add_internal_yang(pctx, mod->parsed), cleanup); } /* add the module into newly created module set, will also be freed from there on any error */ - LY_CHECK_GOTO(ret = ly_set_add(new_mods, mod, 1, NULL), cleanup); - module_created = 1; + LY_CHECK_GOTO(rc = ly_set_add(new_mods, mod, 1, NULL), cleanup); + mod_created = 1; /* add into context */ - ret = ly_set_add(&ctx->list, mod, 1, NULL); - LY_CHECK_GOTO(ret, cleanup); + rc = ly_set_add(&ctx->list, mod, 1, NULL); + LY_CHECK_GOTO(rc, cleanup); ctx->change_count++; /* resolve includes and all imports */ - LY_CHECK_GOTO(ret = lysp_resolve_import_include(pctx, mod->parsed, new_mods), cleanup); + LY_CHECK_GOTO(rc = lysp_resolve_import_include(pctx, mod->parsed, new_mods), cleanup); /* resolve extension instance plugin records */ - LY_CHECK_GOTO(ret = lysp_resolve_ext_instance_records(pctx), cleanup); + LY_CHECK_GOTO(rc = lysp_resolve_ext_instance_records(pctx), cleanup); /* check name collisions */ - LY_CHECK_GOTO(ret = lysp_check_dup_typedefs(pctx, mod->parsed), cleanup); - LY_CHECK_GOTO(ret = lysp_check_dup_groupings(pctx, mod->parsed), cleanup); - LY_CHECK_GOTO(ret = lysp_check_dup_features(pctx, mod->parsed), cleanup); - LY_CHECK_GOTO(ret = lysp_check_dup_identities(pctx, mod->parsed), cleanup); + LY_CHECK_GOTO(rc = lysp_check_dup_typedefs(pctx, mod->parsed), cleanup); + LY_CHECK_GOTO(rc = lysp_check_dup_groupings(pctx, mod->parsed), cleanup); + LY_CHECK_GOTO(rc = lysp_check_dup_features(pctx, mod->parsed), cleanup); + LY_CHECK_GOTO(rc = lysp_check_dup_identities(pctx, mod->parsed), cleanup); /* compile features */ - LY_CHECK_GOTO(ret = lys_compile_feature_iffeatures(mod->parsed), cleanup); + LY_CHECK_GOTO(rc = lys_compile_feature_iffeatures(mod->parsed), cleanup); /* compile identities */ - LY_CHECK_GOTO(ret = lys_compile_identities(mod), cleanup); + LY_CHECK_GOTO(rc = lys_compile_identities(mod), cleanup); cleanup: - if (ret && (ret != LY_EEXIST)) { - if (mod && mod->name) { - /* there are cases when path is not available for parsing error, so this additional - * message tries to add information about the module where the error occurred */ - const struct ly_err_item *e = ly_err_last(ctx); + if (rc && mod && mod->name) { + /* there are cases when path is not available for parsing error, so this additional + * message tries to add information about the module where the error occurred */ + const struct ly_err_item *e = ly_err_last(ctx); - if (e && (!e->schema_path || e->line)) { - LOGERR(ctx, LY_EOTHER, "Parsing module \"%s\" failed.", mod->name); - } + if (e && (!e->schema_path || e->line)) { + LOGERR(ctx, LY_EOTHER, "Parsing module \"%s\" failed.", mod->name); } } - if (!module_created) { + if (!mod_created) { fctx.mod = mod; lys_module_free(&fctx, mod, 0); lysf_ctx_erase(&fctx); @@ -1980,10 +2234,10 @@ cleanup: lysp_yin_ctx_free(yinctx); } - if (!ret && module) { + if (!rc && !mod_exists && module) { *module = mod; } - return ret; + return rc; } static LYS_INFORMAT @@ -2027,7 +2281,7 @@ lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const char in->func_start = in->current; /* parse */ - ret = lys_parse_in(ctx, in, format, NULL, NULL, &ctx->unres.creating, &mod); + ret = lys_parse_in(ctx, in, format, NULL, &ctx->unres.creating, &mod); LY_CHECK_GOTO(ret, cleanup); /* implement */ diff --git a/src/tree_schema.h b/src/tree_schema.h index 0181bc6..baeb261 100644 --- a/src/tree_schema.h +++ b/src/tree_schema.h @@ -4,7 +4,7 @@ * @author Michal Vasko * @brief libyang representation of YANG schema trees. * - * Copyright (c) 2015 - 2022 CESNET, z.s.p.o. + * Copyright (c) 2015 - 2025 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. @@ -1908,13 +1908,39 @@ 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. + * @brief Get the target node of a leafref node. Function ::lysc_node_lref_targets() should be used instead + * to get all the leafref targets even for a union 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 Get the target node(s) of a leafref node or union node with leafrefs. + * + * @param[in] node Term node to use. + * @param[out] set Set with all the leafref targets, may be empty if the node is a different type or the targets + * are not found. + * @return LY_SUCCESS on success. + * @return LY_ERR value on error. + */ +LIBYANG_API_DECL LY_ERR lysc_node_lref_targets(const struct lysc_node *node, struct ly_set **set); + +/** + * @brief Get all the leafref (or union with leafrefs) nodes that target a specific node. + * + * @param[in] ctx Context to use, may not be set if @p node is. + * @param[in] node Leafref target node to use for matching. If not set, all the leafref nodes are just collected. + * @param[in] match_ancestors If set, @p node is considered a match not only when a leafref targets it directly but + * even when an ancestor (parent) node of @p node is a target of the leafref. + * @param[out] set Set of matching leafref nodes. + * @return LY_SUCCESS on success. + * @return LY_ERR value on error. + */ +LIBYANG_API_DECL LY_ERR lysc_node_lref_backlinks(const struct ly_ctx *ctx, const struct lysc_node *node, + ly_bool match_ancestors, struct ly_set **set); + /** * @brief Callback to be called for every schema node in a DFS traversal. * diff --git a/src/tree_schema_common.c b/src/tree_schema_common.c index e6af579..cd3f07a 100644 --- a/src/tree_schema_common.c +++ b/src/tree_schema_common.c @@ -686,64 +686,6 @@ cleanup: return ret; } -struct lysp_load_module_check_data { - const char *name; - const char *revision; - const char *path; - const char *submoduleof; -}; - -static LY_ERR -lysp_load_module_check(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 *name; - uint8_t latest_revision; - struct lysp_revision *revs; - - name = mod ? mod->mod->name : submod->name; - revs = mod ? mod->revs : submod->revs; - latest_revision = mod ? mod->mod->latest_revision : submod->latest_revision; - - if (info->name) { - /* check name of the parsed model */ - if (strcmp(info->name, name)) { - LOGERR(ctx, LY_EINVAL, "Unexpected module \"%s\" parsed instead of \"%s\").", name, info->name); - return LY_EINVAL; - } - } - if (info->revision) { - /* check revision of the parsed model */ - if (!revs || strcmp(info->revision, revs[0].date)) { - LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").", name, - revs ? revs[0].date : "none", info->revision); - return LY_EINVAL; - } - } else if (!latest_revision) { - /* do not log, we just need to drop the schema and use the latest revision from the context */ - return LY_EEXIST; - } - if (submod) { - assert(info->submoduleof); - - /* check that the submodule belongs-to our module */ - if (strcmp(info->submoduleof, submod->mod->name)) { - LOGVAL(ctx, LYVE_REFERENCE, "Included \"%s\" submodule from \"%s\" belongs-to a different module \"%s\".", - submod->name, info->submoduleof, submod->mod->name); - return LY_EVALID; - } - /* check circular dependency */ - if (submod->parsing) { - LOGVAL(ctx, LYVE_REFERENCE, "A circular dependency (include) for module \"%s\".", submod->name); - return LY_EVALID; - } - } - if (info->path) { - ly_check_module_filename(ctx, name, revs ? revs[0].date : NULL, info->path); - } - return LY_SUCCESS; -} - /** * @brief Parse a (sub)module from a local file and add into the context. * @@ -771,7 +713,7 @@ lys_parse_localfile(struct ly_ctx *ctx, const char *name, const char *revision, LYS_INFORMAT format = 0; void *mod = NULL; LY_ERR ret = LY_SUCCESS; - struct lysp_load_module_check_data check_data = {0}; + struct lysp_load_module_data mod_data = {0}; LY_CHECK_RET(lys_search_localfile(ly_ctx_get_searchdirs(ctx), !(ctx->flags & LY_CTX_DISABLE_SEARCHDIR_CWD), name, revision, &filepath, &format)); @@ -779,8 +721,9 @@ lys_parse_localfile(struct ly_ctx *ctx, const char *name, const char *revision, if (required) { LOGERR(ctx, LY_ENOTFOUND, "Data model \"%s%s%s\" not found in local searchdirs.", name, revision ? "@" : "", revision ? revision : ""); + ret = LY_ENOTFOUND; } - return LY_ENOTFOUND; + goto cleanup; } LOGVRB("Loading schema from \"%s\" file.", filepath); @@ -788,15 +731,14 @@ lys_parse_localfile(struct ly_ctx *ctx, const char *name, const char *revision, /* get the (sub)module */ LY_CHECK_ERR_GOTO(ret = ly_in_new_filepath(filepath, 0, &in), LOGERR(ctx, ret, "Unable to create input handler for filepath %s.", filepath), cleanup); - check_data.name = name; - check_data.revision = revision; - check_data.path = filepath; - check_data.submoduleof = main_name; + mod_data.name = name; + mod_data.revision = revision; + mod_data.path = filepath; + mod_data.submoduleof = main_name; if (main_ctx) { - ret = lys_parse_submodule(ctx, in, format, main_ctx, lysp_load_module_check, &check_data, new_mods, - (struct lysp_submodule **)&mod); + ret = lys_parse_submodule(ctx, in, format, main_ctx, &mod_data, new_mods, (struct lysp_submodule **)&mod); } else { - ret = lys_parse_in(ctx, in, format, lysp_load_module_check, &check_data, new_mods, (struct lys_module **)&mod); + ret = lys_parse_in(ctx, in, format, &mod_data, new_mods, (struct lys_module **)&mod); } ly_in_free(in, 1); @@ -804,8 +746,6 @@ lys_parse_localfile(struct ly_ctx *ctx, const char *name, const char *revision, *result = mod; - /* success */ - cleanup: free(filepath); return ret; @@ -827,11 +767,12 @@ static LY_ERR lys_parse_load_from_clb_or_file(struct ly_ctx *ctx, const char *name, const char *revision, struct lys_module *mod_latest, struct ly_set *new_mods, struct lys_module **mod) { + LY_ERR r; const char *module_data = NULL; LYS_INFORMAT format = LYS_IN_UNKNOWN; void (*module_data_free)(void *module_data, void *user_data) = NULL; - struct lysp_load_module_check_data check_data = {0}; + struct lysp_load_module_data mod_data = {0}; struct ly_in *in; *mod = NULL; @@ -848,13 +789,14 @@ search_clb: if (ctx->imp_clb && (!mod_latest || !(mod_latest->latest_revision & LYS_MOD_LATEST_IMPCLB))) { if (!ctx->imp_clb(name, revision, NULL, NULL, ctx->imp_clb_data, &format, &module_data, &module_data_free)) { LY_CHECK_RET(ly_in_new_memory(module_data, &in)); - check_data.name = name; - check_data.revision = revision; - lys_parse_in(ctx, in, format, lysp_load_module_check, &check_data, new_mods, mod); + mod_data.name = name; + mod_data.revision = revision; + r = lys_parse_in(ctx, in, format, &mod_data, new_mods, mod); ly_in_free(in, 0); if (module_data_free) { module_data_free((void *)module_data, ctx->imp_clb_data); } + LY_CHECK_RET(r); } } if (*mod && !revision) { @@ -868,7 +810,7 @@ search_file: /* check we can use searchdirs and that we should */ if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS) && (!mod_latest || !(mod_latest->latest_revision & LYS_MOD_LATEST_SEARCHDIRS))) { - lys_parse_localfile(ctx, name, revision, NULL, NULL, mod_latest ? 0 : 1, new_mods, (void **)mod); + LY_CHECK_RET(lys_parse_localfile(ctx, name, revision, NULL, NULL, mod_latest ? 0 : 1, new_mods, (void **)mod)); } if (*mod && !revision) { /* we got the latest revision module in the searchdirs */ @@ -878,6 +820,11 @@ search_file: } } + if (!*mod && !mod_latest) { + LOGVAL(ctx, LYVE_REFERENCE, "Loading \"%s\" module failed, not found.", name); + return LY_ENOTFOUND; + } + return LY_SUCCESS; } @@ -972,10 +919,6 @@ lys_parse_load(struct ly_ctx *ctx, const char *name, const char *revision, struc if (!*mod) { /* No suitable module in the context, try to load it. */ LY_CHECK_RET(lys_parse_load_from_clb_or_file(ctx, name, revision, mod_latest, new_mods, mod)); - if (!*mod && !mod_latest) { - LOGVAL(ctx, LYVE_REFERENCE, "Loading \"%s\" module failed.", name); - return LY_EVALID; - } /* Update the latest_revision flag - here we have selected the latest available schema, * consider that even the callback provides correct latest revision. @@ -1171,22 +1114,29 @@ lysp_inject_submodule(struct lysp_ctx *pctx, struct lysp_include *inc) LY_ERR lysp_load_submodules(struct lysp_ctx *pctx, struct lysp_module *pmod, struct ly_set *new_mods) { - LY_ARRAY_COUNT_TYPE u; + LY_ERR r; struct ly_ctx *ctx = PARSER_CTX(pctx); + struct lysp_submodule *submod; + struct lysp_include *inc; + LY_ARRAY_COUNT_TYPE u; + ly_bool submod_included; LY_ARRAY_FOR(pmod->includes, u) { - LY_ERR ret = LY_SUCCESS, r; - struct lysp_submodule *submod = NULL; - struct lysp_include *inc = &pmod->includes[u]; - + inc = &pmod->includes[u]; if (inc->submodule) { continue; } + submod = NULL; + submod_included = 1; if (pmod->is_submod) { /* try to find the submodule in the main module or its submodules */ - ret = lysp_main_pmod_get_submodule(pctx, inc); - LY_CHECK_RET(ret != LY_ENOT, ret); + r = lysp_main_pmod_get_submodule(pctx, inc); + if (r == LY_ENOT) { + submod_included = 0; + } else if (r) { + return r; + } } /* try to use currently parsed submodule */ @@ -1201,27 +1151,25 @@ search_clb: LYS_INFORMAT format = LYS_IN_UNKNOWN; void (*submodule_data_free)(void *module_data, void *user_data) = NULL; - struct lysp_load_module_check_data check_data = {0}; + struct lysp_load_module_data mod_data = {0}; struct ly_in *in; - if (ctx->imp_clb(PARSER_CUR_PMOD(pctx)->mod->name, NULL, inc->name, - inc->rev[0] ? inc->rev : NULL, ctx->imp_clb_data, - &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) { + if (ctx->imp_clb(PARSER_CUR_PMOD(pctx)->mod->name, NULL, inc->name, inc->rev[0] ? inc->rev : NULL, + ctx->imp_clb_data, &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) { LY_CHECK_RET(ly_in_new_memory(submodule_data, &in)); - check_data.name = inc->name; - check_data.revision = inc->rev[0] ? inc->rev : NULL; - check_data.submoduleof = PARSER_CUR_PMOD(pctx)->mod->name; - lys_parse_submodule(ctx, in, format, pctx->main_ctx, lysp_load_module_check, &check_data, new_mods, - &submod); - - /* update inc pointer - parsing another (YANG 1.0) submodule can cause injecting - * submodule's include into main module, where it is missing */ - inc = &pmod->includes[u]; - + mod_data.name = inc->name; + mod_data.revision = inc->rev[0] ? inc->rev : NULL; + mod_data.submoduleof = PARSER_CUR_PMOD(pctx)->mod->name; + r = lys_parse_submodule(ctx, in, format, pctx->main_ctx, &mod_data, new_mods, &submod); ly_in_free(in, 0); if (submodule_data_free) { submodule_data_free((void *)submodule_data, ctx->imp_clb_data); } + LY_CHECK_RET(r); + + /* update inc pointer - parsing another (YANG 1.0) submodule can cause injecting + * submodule's include into main module, where it is missing */ + inc = &pmod->includes[u]; } } if (!submod && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) { @@ -1250,7 +1198,7 @@ search_file: } inc->submodule = submod; - if (ret == LY_ENOT) { + if (!submod_included) { /* the submodule include is not present in YANG 1.0 main module - add it there */ LY_CHECK_RET(lysp_inject_submodule(pctx, &pmod->includes[u])); } @@ -1380,6 +1328,10 @@ lys_datatype2str(LY_DATA_TYPE basetype) LIBYANG_API_DEF const struct lysp_tpdf * lysp_node_typedefs(const struct lysp_node *node) { + if (!node) { + return NULL; + } + switch (node->nodetype) { case LYS_CONTAINER: return ((struct lysp_node_container *)node)->typedefs; @@ -1403,6 +1355,10 @@ lysp_node_typedefs(const struct lysp_node *node) LIBYANG_API_DEF const struct lysp_node_grp * lysp_node_groupings(const struct lysp_node *node) { + if (!node) { + return NULL; + } + switch (node->nodetype) { case LYS_CONTAINER: return ((struct lysp_node_container *)node)->groupings; @@ -1447,6 +1403,10 @@ lysp_node_actions(const struct lysp_node *node) { struct lysp_node_action **actions; + if (!node) { + return NULL; + } + actions = lysp_node_actions_p((struct lysp_node *)node); if (actions) { return *actions; @@ -1478,6 +1438,10 @@ lysp_node_notifs(const struct lysp_node *node) { struct lysp_node_notif **notifs; + if (!node) { + return NULL; + } + notifs = lysp_node_notifs_p((struct lysp_node *)node); if (notifs) { return *notifs; @@ -1621,6 +1585,7 @@ struct lysc_node_action ** lysc_node_actions_p(struct lysc_node *node) { assert(node); + switch (node->nodetype) { case LYS_CONTAINER: return &((struct lysc_node_container *)node)->actions; @@ -1636,6 +1601,10 @@ lysc_node_actions(const struct lysc_node *node) { struct lysc_node_action **actions; + if (!node) { + return NULL; + } + actions = lysc_node_actions_p((struct lysc_node *)node); if (actions) { return *actions; @@ -1648,6 +1617,7 @@ struct lysc_node_notif ** lysc_node_notifs_p(struct lysc_node *node) { assert(node); + switch (node->nodetype) { case LYS_CONTAINER: return &((struct lysc_node_container *)node)->notifs; @@ -1663,6 +1633,10 @@ lysc_node_notifs(const struct lysc_node *node) { struct lysc_node_notif **notifs; + if (!node) { + return NULL; + } + notifs = lysc_node_notifs_p((struct lysc_node *)node); if (notifs) { return *notifs; @@ -1750,6 +1724,10 @@ lysc_node_musts(const struct lysc_node *node) { struct lysc_must **must_p; + if (!node) { + return NULL; + } + must_p = lysc_node_musts_p(node); if (must_p) { return *must_p; @@ -1796,6 +1774,10 @@ lysc_node_when(const struct lysc_node *node) { struct lysc_when ***when_p; + if (!node) { + return NULL; + } + when_p = lysc_node_when_p(node); if (when_p) { return *when_p; @@ -1804,21 +1786,24 @@ lysc_node_when(const struct lysc_node *node) } } -LIBYANG_API_DEF const struct lysc_node * -lysc_node_lref_target(const struct lysc_node *node) +/** + * @brief Get the target node of a leafref. + * + * @param[in] node Context node for the leafref. + * @param[in] type Leafref type to resolve. + * @return Target schema node; + * @return NULL if the tearget is not found. + */ +static const struct lysc_node * +lysc_type_lref_target(const struct lysc_node *node, const struct lysc_type *type) { struct lysc_type_leafref *lref; struct ly_path *p; const struct lysc_node *target; - if (!node || !(node->nodetype & LYD_NODE_TERM)) { - return NULL; - } + assert(type->basetype == LY_TYPE_LEAFREF); - lref = (struct lysc_type_leafref *)((struct lysc_node_leaf *)node)->type; - if (lref->basetype != LY_TYPE_LEAFREF) { - return NULL; - } + lref = (struct lysc_type_leafref *)type; /* compile the path */ if (ly_path_compile_leafref(node->module->ctx, node, NULL, lref->path, @@ -1834,6 +1819,164 @@ lysc_node_lref_target(const struct lysc_node *node) return target; } +LIBYANG_API_DEF const struct lysc_node * +lysc_node_lref_target(const struct lysc_node *node) +{ + if (!node || !(node->nodetype & LYD_NODE_TERM) || (((struct lysc_node_leaf *)node)->type->basetype != LY_TYPE_LEAFREF)) { + return NULL; + } + + return lysc_type_lref_target(node, ((struct lysc_node_leaf *)node)->type); +} + +LIBYANG_API_DEF LY_ERR +lysc_node_lref_targets(const struct lysc_node *node, struct ly_set **set) +{ + LY_ERR rc = LY_SUCCESS; + struct lysc_type *type; + struct lysc_type_union *type_un; + const struct lysc_node *target; + LY_ARRAY_COUNT_TYPE u; + + LY_CHECK_ARG_RET(NULL, node, (node->nodetype & LYD_NODE_TERM), LY_EINVAL); + + /* allocate return set */ + LY_CHECK_RET(ly_set_new(set)); + + type = ((struct lysc_node_leaf *)node)->type; + if (type->basetype == LY_TYPE_UNION) { + /* union with possible leafrefs */ + type_un = (struct lysc_type_union *)type; + + LY_ARRAY_FOR(type_un->types, u) { + if (type_un->types[u]->basetype != LY_TYPE_LEAFREF) { + continue; + } + + target = lysc_type_lref_target(node, type_un->types[u]); + if (target) { + LY_CHECK_GOTO(rc = ly_set_add(*set, target, 1, NULL), cleanup); + } + } + } else if (type->basetype == LY_TYPE_LEAFREF) { + /* leafref */ + target = lysc_type_lref_target(node, type); + if (target) { + LY_CHECK_GOTO(rc = ly_set_add(*set, target, 1, NULL), cleanup); + } + } + +cleanup: + if (rc) { + ly_set_free(*set, NULL); + *set = NULL; + } + return rc; +} + +struct lysc_node_lref_backlings_arg { + const struct lysc_node *node; + ly_bool match_ancestors; + struct ly_set *set; +}; + +static LY_ERR +lysc_node_lref_backlinks_clb(struct lysc_node *node, void *data, ly_bool *dfs_continue) +{ + LY_ERR rc = LY_SUCCESS; + struct lysc_node_lref_backlings_arg *arg = data; + struct ly_set *set = NULL; + const struct lysc_node *par; + uint32_t i; + + (void)dfs_continue; + + if (!(node->nodetype & LYD_NODE_TERM)) { + /* skip */ + goto cleanup; + } + + /* get all the leafref targets */ + LY_CHECK_GOTO(rc = lysc_node_lref_targets(node, &set), cleanup); + + /* ignore node if has no leafref targets */ + if (!set->count) { + goto cleanup; + } + + /* if just collecting leafrefs, we are done */ + if (!arg->node) { + rc = ly_set_add(arg->set, node, 1, NULL); + goto cleanup; + } + + /* check that the node (or the ancestor of) is the target of this leafref */ + for (i = 0; i < set->count; ++i) { + for (par = set->snodes[i]; par; par = par->parent) { + if (par == arg->node) { + /* match */ + break; + } + + if (!arg->match_ancestors) { + /* not a match */ + par = NULL; + break; + } + } + + if (par) { + /* add into the set, matches */ + LY_CHECK_GOTO(rc = ly_set_add(arg->set, node, 1, NULL), cleanup); + break; + } + } + +cleanup: + ly_set_free(set, NULL); + return rc; +} + +LIBYANG_API_DEF LY_ERR +lysc_node_lref_backlinks(const struct ly_ctx *ctx, const struct lysc_node *node, ly_bool match_ancestors, + struct ly_set **set) +{ + LY_ERR rc = LY_SUCCESS; + struct lysc_node_lref_backlings_arg arg = {0}; + uint32_t idx = 0; + const struct lys_module *mod; + + LY_CHECK_ARG_RET(NULL, ctx || node, set, LY_EINVAL); + + if (!ctx) { + ctx = node->module->ctx; + } + + /* allocate return set */ + LY_CHECK_RET(ly_set_new(set)); + + /* prepare the arg */ + arg.node = node; + arg.match_ancestors = match_ancestors; + arg.set = *set; + + /* iterate across all loaded modules */ + while ((mod = ly_ctx_get_module_iter(ctx, &idx))) { + if (!mod->compiled) { + continue; + } + + LY_CHECK_GOTO(rc = lysc_module_dfs_full(mod, lysc_node_lref_backlinks_clb, &arg), cleanup); + } + +cleanup: + if (rc) { + ly_set_free(*set, NULL); + *set = NULL; + } + return rc; +} + enum ly_stmt lysp_match_kw(struct ly_in *in, uint64_t *indent) { @@ -2619,41 +2762,3 @@ 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_free.c b/src/tree_schema_free.c index 040872b..21e7ed7 100644 --- a/src/tree_schema_free.c +++ b/src/tree_schema_free.c @@ -825,14 +825,7 @@ lysc_ident_free(struct lysf_ctx *ctx, struct lysc_ident *ident) FREE_ARRAY(ctx, ident->exts, lysc_ext_instance_free); } -/** - * @brief Free the compiled range structure. - * - * @param[in] ctx Free context. - * @param[in,out] range Compiled range structure to be freed. - * Since the structure is typically part of the sized array, the structure itself is not freed. - */ -static void +void lysc_range_free(struct lysf_ctx *ctx, struct lysc_range *range) { LY_ARRAY_FREE(range->parts); diff --git a/src/tree_schema_free.h b/src/tree_schema_free.h index d79164b..f4141de 100644 --- a/src/tree_schema_free.h +++ b/src/tree_schema_free.h @@ -134,6 +134,15 @@ void lysc_ext_instance_free(struct lysf_ctx *ctx, struct lysc_ext_instance *ext) */ void lysc_iffeature_free(struct lysf_ctx *ctx, struct lysc_iffeature *iff); +/** + * @brief Free the compiled range structure. + * + * @param[in] ctx Free context. + * @param[in,out] range Compiled range structure to be freed. + * Since the structure is typically part of the sized array, the structure itself is not freed. + */ +void lysc_range_free(struct lysf_ctx *ctx, struct lysc_range *range); + /** * @brief Free a compiled pattern. * diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h index ee40f58..f4adabd 100644 --- a/src/tree_schema_internal.h +++ b/src/tree_schema_internal.h @@ -130,11 +130,12 @@ enum yang_arg { struct lysp_ctx { LYS_INFORMAT format; /**< parser format */ - struct ly_set tpdfs_nodes; /**< Set of nodes that contain typedef(s). Invalid in case of - submodule, use ::lysp_ctx.main_ctx instead. */ - struct ly_set grps_nodes; /**< Set of nodes that contain grouping(s). Invalid in case of - submodule, use ::lysp_ctx.main_ctx instead. */ - struct ly_set ext_inst; /**< parsed extension instances to finish parsing */ + struct ly_set tpdfs_nodes; /**< Set of nodes that contain typedef(s). Used only temporarily in case of + submodule, ::lysp_ctx.main_ctx used instead. */ + struct ly_set grps_nodes; /**< Set of nodes that contain grouping(s). Used only temporarily in case of + submodule, ::lysp_ctx.main_ctx used instead. */ + struct ly_set ext_inst; /**< Set of parsed extension instances to finish parsing. Used only temporarily + in case of submodule, ::lysp_ctx.main_ctx used instead. */ struct ly_set *parsed_mods; /**< (sub)modules being parsed, the last one is the current */ struct lysp_ctx *main_ctx; /**< This pointer must not be NULL. If this context deals with the submodule, @@ -528,8 +529,12 @@ void lys_unres_glob_revert(struct ly_ctx *ctx, struct lys_glob_unres *unres); */ void lys_unres_glob_erase(struct lys_glob_unres *unres); -typedef LY_ERR (*lys_custom_check)(const struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, - void *check_data); +struct lysp_load_module_data { + const char *name; /**< expected module name */ + const char *revision; /**< expected module revision */ + const char *path; /**< module file name to check */ + const char *submoduleof; /**< expected submodule main module */ +}; /** * @brief Parse a module and add it into the context. @@ -537,15 +542,14 @@ typedef LY_ERR (*lys_custom_check)(const struct ly_ctx *ctx, struct lysp_module * @param[in] ctx libyang context where to process the data model. * @param[in] in Input structure. * @param[in] format Format of the input data (YANG or YIN). - * @param[in] custom_check Callback to check the parsed schema before it is accepted. - * @param[in] check_data Caller's data to pass to the custom_check callback. + * @param[in] mod_data Optional expected module data to check. * @param[in,out] new_mods Set of all the new mods added to the context. Includes this module and all of its imports. * @param[out] module Created module. * @return LY_SUCCESS on success. * @return LY_ERR on error, @p new_mods may be modified. */ -LY_ERR lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, lys_custom_check custom_check, - void *check_data, struct ly_set *new_mods, struct lys_module **module); +LY_ERR lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, + const struct lysp_load_module_data *mod_data, struct ly_set *new_mods, struct lys_module **module); /** * @brief Parse submodule. @@ -556,14 +560,13 @@ LY_ERR lys_parse_in(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, l * @param[in] in Input structure. * @param[in] format Format of the input data (YANG or YIN). * @param[in] main_ctx Parser context of the main module. - * @param[in] custom_check Callback to check the parsed schema before it is accepted. - * @param[in] check_data Caller's data to pass to the custom_check callback. + * @param[in] mod_data Optional expected module data to check. * @param[in] new_mods Set of all the new mods added to the context. Includes this module and all of its imports. * @param[out] submodule Parsed submodule. * @return LY_ERR value. */ LY_ERR lys_parse_submodule(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, struct lysp_ctx *main_ctx, - lys_custom_check custom_check, void *check_data, struct ly_set *new_mods, struct lysp_submodule **submodule); + const struct lysp_load_module_data *mod_data, struct ly_set *new_mods, struct lysp_submodule **submodule); /** * @brief Fill filepath value if available in input handler @p in @@ -732,14 +735,4 @@ uint8_t lys_stmt_flags(enum ly_stmt stmt); */ LY_ERR lyplg_ext_get_storage_p(const struct lysc_ext_instance *ext, int stmt, void ***storage_pp); -/** - * @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 18ec15c..25be0fe 100644 --- a/src/validation.c +++ b/src/validation.c @@ -216,7 +216,7 @@ lyd_val_diff_add(const struct lyd_node *node, enum lyd_diff_op op, struct lyd_no } /* create new diff tree */ - LY_CHECK_GOTO(ret = lyd_diff_add(node, op, NULL, NULL, key, value, position, NULL, NULL, &new_diff), cleanup); + LY_CHECK_GOTO(ret = lyd_diff_add(node, op, NULL, NULL, key, value, position, NULL, NULL, &new_diff, NULL), cleanup); /* merge into existing diff */ ret = lyd_diff_merge_all(diff, new_diff, 0); @@ -336,7 +336,8 @@ lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *del, con /* remove nested from node_when set */ LYD_TREE_DFS_BEGIN(del, iter) { if ((del != iter) && ly_set_contains(node_when, iter, &idx)) { - ly_set_rm_index(node_when, idx, NULL); + assert(0 && "Please contact libyang support with the use-case that triggered this assert."); + // ly_set_rm_index(node_when, idx, NULL); } LYD_TREE_DFS_END(del, iter); } @@ -345,7 +346,9 @@ lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *del, con 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)) { + if ((iter->schema->nodetype & LYD_NODE_TERM) && + ((struct lysc_node_leaf *)iter->schema)->type->plugin->validate && + ly_set_contains(node_types, iter, &idx)) { ly_set_rm_index(node_types, idx, NULL); } LYD_TREE_DFS_END(del, iter); @@ -490,8 +493,14 @@ lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod, enum ly /* 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 || ((rc == LY_EVALID) && (val_opts & LYD_VALIDATE_MULTI_ERROR))); + if (node_when->count) { + /* there could have been no cyclic when dependencies, checked during compilation */ + assert((rc == LY_EVALID) && (val_opts & LYD_VALIDATE_MULTI_ERROR)); + + /* when condition was validated and it is not satisfied, error printed, if kept in the set the following + * unres (for the next module) can fail this assert */ + ly_set_erase(node_when, NULL); + } } if (node_types && node_types->count) { @@ -1140,13 +1149,17 @@ 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 val_opts) { + LY_ERR rc = LY_SUCCESS; uint32_t count = 0; - struct lyd_node *iter; + struct lyd_node *iter, *last_iter = NULL; const struct lysc_when *disabled; + char *log_path; + int r; assert(min || max); LYD_LIST_FOR_INST(first, snode, iter) { + last_iter = iter; ++count; if (min && (count == min)) { @@ -1182,32 +1195,52 @@ lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent, max = 0; } - if (min) { - if (val_opts & LYD_VALIDATE_OPERATIONAL) { - /* only a warning */ - LOG_LOCSET(snode, NULL); - LOGWRN(snode->module->ctx, "Too few \"%s\" instances.", snode->name); - LOG_LOCBACK(1, 0); + if (min || max) { + /* set log path */ + if (last_iter) { + /* standard data path */ + LOG_LOCSET(NULL, last_iter); } else { - LOG_LOCSET(snode, NULL); - LOGVAL_APPTAG(snode->module->ctx, "too-few-elements", LY_VCODE_NOMIN, snode->name); - LOG_LOCBACK(1, 0); - return LY_EVALID; + /* data path with last schema node name or only the schema node if !parent */ + if (lyd_node_module(parent) != snode->module) { + r = asprintf(&log_path, "/%s:%s", snode->module->name, snode->name); + } else { + r = asprintf(&log_path, "/%s", snode->name); + } + if (r == -1) { + LOGMEM_RET(snode->module->ctx); + } + ly_log_location(NULL, parent, log_path, NULL); + free(log_path); } - } else if (max) { - if (val_opts & LYD_VALIDATE_OPERATIONAL) { - /* only a warning */ - LOG_LOCSET(NULL, iter); - LOGWRN(snode->module->ctx, "Too many \"%s\" instances.", snode->name); + + if (min) { + if (val_opts & LYD_VALIDATE_OPERATIONAL) { + /* only a warning */ + LOGWRN(snode->module->ctx, "Too few \"%s\" instances.", snode->name); + } else { + LOGVAL_APPTAG(snode->module->ctx, "too-few-elements", LY_VCODE_NOMIN, snode->name); + rc = LY_EVALID; + } + } else if (max) { + if (val_opts & LYD_VALIDATE_OPERATIONAL) { + /* only a warning */ + LOGWRN(snode->module->ctx, "Too many \"%s\" instances.", snode->name); + } else { + LOGVAL_APPTAG(snode->module->ctx, "too-many-elements", LY_VCODE_NOMAX, snode->name); + rc = LY_EVALID; + } + } + + /* revert log path */ + if (last_iter) { LOG_LOCBACK(0, 1); } else { - LOG_LOCSET(NULL, iter); - LOGVAL_APPTAG(snode->module->ctx, "too-many-elements", LY_VCODE_NOMAX, snode->name); - LOG_LOCBACK(0, 1); - return LY_EVALID; + ly_log_location_revert(0, parent ? 1 : 0, 1, 0); } } - return LY_SUCCESS; + + return rc; } /** @@ -1588,7 +1621,8 @@ lyd_validate_obsolete(const struct lyd_node *node) snode = node->schema; do { - if (snode->flags & LYS_STATUS_OBSLT) { + if (snode->flags & LYS_STATUS_OBSLT && + (!(snode->nodetype & LYD_NODE_INNER) || lyd_child(node))) { LOG_LOCSET(NULL, node); LOGWRN(snode->module->ctx, "Obsolete schema node \"%s\" instantiated in data.", snode->name); LOG_LOCBACK(0, 1); @@ -2126,7 +2160,7 @@ LIBYANG_API_DEF LY_ERR lyd_validate_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t val_opts, struct lyd_node **diff) { LY_CHECK_ARG_RET(NULL, tree, *tree || ctx, LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, ctx, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, *tree ? LYD_CTX(*tree) : NULL, ctx, LY_EINVAL); if (!ctx) { ctx = LYD_CTX(*tree); } @@ -2141,7 +2175,7 @@ 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, module, !(val_opts & LYD_VALIDATE_PRESENT), LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, module->ctx, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, *tree ? LYD_CTX(*tree) : NULL, module->ctx, LY_EINVAL); if (diff) { *diff = NULL; } @@ -2159,7 +2193,7 @@ lyd_validate_module_final(struct lyd_node *tree, const struct lys_module *module struct ly_ht *getnext_ht = NULL; LY_CHECK_ARG_RET(NULL, module, !(val_opts & (LYD_VALIDATE_PRESENT | LYD_VALIDATE_NOT_FINAL)), LY_EINVAL); - LY_CHECK_CTX_EQUAL_RET(tree ? LYD_CTX(tree) : NULL, module->ctx, LY_EINVAL); + LY_CHECK_CTX_EQUAL_RET(__func__, tree ? LYD_CTX(tree) : NULL, 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); diff --git a/src/xpath.c b/src/xpath.c index 9594c48..5429eca 100644 --- a/src/xpath.c +++ b/src/xpath.c @@ -3690,6 +3690,7 @@ xpath_bit_is_set(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp struct lyd_node_term *leaf; struct lysc_node_leaf *sleaf; struct lyd_value_bits *bits; + struct lyd_value *val; LY_ERR rc = LY_SUCCESS; LY_ARRAY_COUNT_TYPE u; @@ -3725,10 +3726,14 @@ xpath_bit_is_set(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp LY_CHECK_RET(rc); set_fill_boolean(set, 0); - if (args[0]->used) { + if (args[0]->used && (args[0]->val.nodes[0].node->schema->nodetype & LYD_NODE_TERM)) { leaf = (struct lyd_node_term *)args[0]->val.nodes[0].node; - if ((leaf->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) && (leaf->value.realtype->basetype == LY_TYPE_BITS)) { - LYD_VALUE_GET(&leaf->value, bits); + val = &leaf->value; + if (val->realtype->basetype == LY_TYPE_UNION) { + val = &val->subvalue->value; + } + if (val->realtype->basetype == LY_TYPE_BITS) { + LYD_VALUE_GET(val, bits); LY_ARRAY_FOR(bits->items, u) { if (!strcmp(bits->items[u]->name, args[1]->val.str)) { set_fill_boolean(set, 1); @@ -4200,17 +4205,21 @@ xpath_derived_(struct lyxp_set **args, struct lyxp_set *set, uint32_t options, l leaf = (struct lyd_node_term *)args[0]->val.nodes[i].node; sleaf = (struct lysc_node_leaf *)leaf->schema; val = &leaf->value; - if (!sleaf || !(sleaf->nodetype & LYD_NODE_TERM) || (leaf->value.realtype->basetype != LY_TYPE_IDENT)) { + if (!sleaf || !(sleaf->nodetype & LYD_NODE_TERM)) { /* uninteresting */ continue; } } else { meta = args[0]->val.meta[i].meta; val = &meta->value; - if (val->realtype->basetype != LY_TYPE_IDENT) { - /* uninteresting */ - continue; - } + } + + if (val->realtype->basetype == LY_TYPE_UNION) { + val = &val->subvalue->value; + } + if (val->realtype->basetype != LY_TYPE_IDENT) { + /* uninteresting */ + continue; } /* check the identity itself */ @@ -4972,6 +4981,7 @@ xpath_re_match(struct lyxp_set **args, uint32_t UNUSED(arg_count), struct lyxp_s LOG_LOCBACK(0, 1); } if (rc != LY_SUCCESS) { + free(*pattern); LY_ARRAY_FREE(patterns); return rc; } @@ -6801,7 +6811,8 @@ moveto_scnode(struct lyxp_set *set, const struct lys_module *moveto_mod, const c } } - if (moveto_mod && ncname && ((axis == LYXP_AXIS_DESCENDANT) || (axis == LYXP_AXIS_CHILD)) && + /* only consider extension nodes after no local ones were found */ + if ((orig_used == set->used) && moveto_mod && ncname && ((axis == LYXP_AXIS_DESCENDANT) || (axis == LYXP_AXIS_CHILD)) && (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 */ @@ -8236,14 +8247,16 @@ moveto: } LY_CHECK_GOTO(rc, cleanup); - i = set->used; - do { - --i; - if (set->val.scnodes[i].in_ctx > LYXP_SET_SCNODE_ATOM_NODE) { - found = 1; - break; - } - } while (i); + if (set->used) { + i = set->used; + do { + --i; + if (set->val.scnodes[i].in_ctx > LYXP_SET_SCNODE_ATOM_NODE) { + found = 1; + break; + } + } while (i); + } if (!found) { /* generate message */ eval_name_test_scnode_no_match_msg(set, scparent, ncname, ncname_len, exp->expr, options); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 259ef34..8cc2e92 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -30,6 +30,9 @@ function(ly_add_utest) endif() endif() + if (XXHASH_FOUND) + target_link_libraries(${TEST_NAME} ${XXHASH_LIBRARY}) + endif() add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) set_property(TEST ${TEST_NAME} APPEND PROPERTY ENVIRONMENT "MALLOC_CHECK_=3") diff --git a/tests/perf/CMakeLists.txt b/tests/perf/CMakeLists.txt index ec1dfb5..3775589 100644 --- a/tests/perf/CMakeLists.txt +++ b/tests/perf/CMakeLists.txt @@ -6,7 +6,11 @@ set(format_sources add_executable(ly_perf ${CMAKE_CURRENT_SOURCE_DIR}/perf.c $) set_target_properties(ly_perf PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/tests") target_link_libraries(ly_perf ${CMAKE_THREAD_LIBS_INIT} ${PCRE2_LIBRARIES} ${CMAKE_DL_LIBS}) -if (NOT WIN32) +if(XXHASH_FOUND) + target_link_libraries(ly_perf ${XXHASH_LIBRARY}) +endif() + +if(NOT WIN32) target_link_libraries(ly_perf m) endif() diff --git a/tests/style/CMakeLists.txt b/tests/style/CMakeLists.txt index 93d6049..cf7c97b 100644 --- a/tests/style/CMakeLists.txt +++ b/tests/style/CMakeLists.txt @@ -1,7 +1,7 @@ add_test(NAME headers COMMAND ${CMAKE_SOURCE_DIR}/compat/check_includes.sh ${CMAKE_SOURCE_DIR}/src/ ${CMAKE_SOURCE_DIR}/tools/lint/ ${CMAKE_SOURCE_DIR}/tools/re/) -if (${SOURCE_FORMAT_ENABLED}) +if(${SOURCE_FORMAT_ENABLED}) add_test(NAME format WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND cmake --build ${CMAKE_BINARY_DIR} --target format-check) endif() @@ -9,4 +9,8 @@ endif() add_executable(cpp_compat cpp_compat.c $) target_include_directories(cpp_compat BEFORE PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(cpp_compat ${CMAKE_THREAD_LIBS_INIT} ${PCRE2_LIBRARIES} ${CMAKE_DL_LIBS} m) +if(XXHASH_FOUND) + target_link_libraries(cpp_compat ${XXHASH_LIBRARY}) +endif() + target_compile_options(cpp_compat PUBLIC "-Werror=c++-compat") diff --git a/tests/utests/basic/test_context.c b/tests/utests/basic/test_context.c index 476fd81..116b2ce 100644 --- a/tests/utests/basic/test_context.c +++ b/tests/utests/basic/test_context.c @@ -276,10 +276,32 @@ test_options(void **state) } static LY_ERR -test_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *UNUSED(submod_name), - const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, - const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +test_imp_clb(const char *mod_name, const char *UNUSED(mod_rev), const char *submod_name, const char *UNUSED(sub_rev), + void *user_data, LYS_INFORMAT *format, const char **module_data, void (**free_module_data)(void *model_data, + void *user_data)) { + const char *name; + + if (submod_name) { + if (strncmp(user_data, "submodule", 9)) { + return LY_ENOTFOUND; + } + + name = ((char *)user_data) + 10; + if (strncmp(name, submod_name, strlen(submod_name)) || (name[strlen(submod_name)] != ' ')) { + return LY_ENOTFOUND; + } + } else { + if (strncmp(user_data, "module", 6)) { + return LY_ENOTFOUND; + } + + name = ((char *)user_data) + 7; + if (strncmp(name, mod_name, strlen(mod_name)) || (name[strlen(mod_name)] != ' ')) { + return LY_ENOTFOUND; + } + } + *module_data = user_data; *format = LYS_IN_YANG; *free_module_data = NULL; @@ -305,7 +327,7 @@ test_models(void **state) assert_int_equal(UTEST_LYCTX->change_count, ly_ctx_get_change_count(UTEST_LYCTX)); assert_int_equal(LY_SUCCESS, ly_in_new_memory("module x {namespace urn:x;prefix x;}", &in)); - assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, 4, NULL, NULL, &unres.creating, &mod1)); + assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, 4, NULL, &unres.creating, &mod1)); lys_unres_glob_erase(&unres); ly_in_free(in, 0); CHECK_LOG_CTX("Invalid schema input format.", NULL, 0); @@ -324,17 +346,17 @@ test_models(void **state) /* name collision of module and submodule */ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-30;}"); assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;include y;}", &in)); - assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &unres.creating, &mod1)); lys_unres_glob_erase(&unres); ly_in_free(in, 0); CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL, 0); CHECK_LOG_CTX("Name collision between module and submodule of name \"y\".", NULL, 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)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &unres.creating, &mod1)); ly_in_free(in, 0); assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;}", &in)); - assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &unres.creating, &mod1)); lys_unres_glob_erase(&unres); ly_in_free(in, 0); CHECK_LOG_CTX("Parsing module \"y\" failed.", NULL, 0); @@ -342,19 +364,18 @@ test_models(void **state) 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)); - assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod1)); + assert_int_equal(LY_EVALID, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &unres.creating, &mod1)); 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, 0); - CHECK_LOG_CTX("Including \"y\" submodule into \"b\" failed.", NULL, 0); - CHECK_LOG_CTX("Parsing submodule failed.", NULL, 0); + CHECK_LOG_CTX("Parsing submodule \"y\" failed.", NULL, 0); CHECK_LOG_CTX("Name collision between submodules of name \"y\".", NULL, 1); /* selecting correct revision of the submodules */ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-31;}"); assert_int_equal(LY_SUCCESS, ly_in_new_memory("module a {namespace urn:a;prefix a;include y; revision 2018-10-31;}", &in)); - assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &unres.creating, &mod2)); lys_unres_glob_erase(&unres); ly_in_free(in, 0); assert_string_equal("2018-10-31", mod2->parsed->includes[0].submodule->revs[0].date); @@ -490,17 +511,17 @@ test_get_models(void **state) /* select module by revision */ assert_int_equal(LY_SUCCESS, lys_parse(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, &mod)); /* invalid attempts - implementing module of the same name and inserting the same module */ - assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, &unres.creating, &mod2)); assert_int_equal(LY_EDENIED, lys_implement(mod2, NULL, &unres)); CHECK_LOG_CTX("Module \"a@2018-10-24\" is already implemented in revision \"2018-10-23\".", NULL, 0); lys_unres_glob_erase(&unres); ly_in_reset(in1); /* it is already there, fine */ - assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, NULL, &unres.creating, NULL)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, &unres.creating, NULL)); /* insert the second module only as imported, not implemented */ lys_unres_glob_erase(&unres); ly_in_reset(in2); - assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod2)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in2, LYS_IN_YANG, NULL, &unres.creating, &mod2)); lys_unres_glob_erase(&unres); assert_non_null(mod2); assert_ptr_not_equal(mod, mod2); @@ -509,7 +530,7 @@ test_get_models(void **state) mod2 = ly_ctx_get_module_latest_ns(UTEST_LYCTX, mod->ns); assert_ptr_equal(mod, mod2); /* work with module with no revision */ - assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in0, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in0, LYS_IN_YANG, NULL, &unres.creating, &mod)); lys_unres_glob_erase(&unres); assert_ptr_equal(mod, ly_ctx_get_module(UTEST_LYCTX, "a", NULL)); assert_ptr_not_equal(mod, ly_ctx_get_module_latest(UTEST_LYCTX, "a")); @@ -517,7 +538,7 @@ test_get_models(void **state) str1 = "submodule b {belongs-to a {prefix a;}}"; ly_in_free(in1, 0); assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in1)); - assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod)); + assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in1, LYS_IN_YANG, NULL, &unres.creating, &mod)); CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL, 0); lys_unres_glob_erase(&unres); @@ -540,7 +561,7 @@ test_ylmem(void **state) " complete\n"\ " \n"\ " yang\n"\ - " 2022-06-16\n"\ + " 2025-01-29\n"\ " urn:ietf:params:xml:ns:yang:1\n"\ " \n"\ " \n"\ @@ -587,7 +608,7 @@ test_ylmem(void **state) " \n"\ " \n"\ " yang\n"\ - " 2022-06-16\n"\ + " 2025-01-29\n"\ " urn:ietf:params:xml:ns:yang:1\n"\ " implement\n"\ " \n"\ @@ -740,7 +761,7 @@ test_ylmem(void **state) " complete\n" " \n" " yang\n" - " 2022-06-16\n" + " 2025-01-29\n" " urn:ietf:params:xml:ns:yang:1\n" " \n" DATA_YANG_BASE_IMPORTS @@ -978,7 +999,7 @@ test_set_priv_parsed(void **state) ly_ctx_destroy(UTEST_LYCTX); const char *feats[] = {"f1", NULL}; - assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_SET_PRIV_PARSED, &UTEST_LYCTX)); + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD | LY_CTX_SET_PRIV_PARSED, &UTEST_LYCTX)); assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "ietf-restconf", "2017-01-26", NULL)); UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, feats, NULL); @@ -1054,7 +1075,7 @@ test_explicit_compile(void **state) ly_ctx_destroy(UTEST_LYCTX); const char *feats[] = {"f1", NULL}; - assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_EXPLICIT_COMPILE, &UTEST_LYCTX)); + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD | LY_CTX_EXPLICIT_COMPILE, &UTEST_LYCTX)); UTEST_ADD_MODULE(schema_a, LYS_IN_YANG, NULL, &mod); UTEST_ADD_MODULE(schema_b, LYS_IN_YANG, NULL, NULL); UTEST_ADD_MODULE(schema_c, LYS_IN_YANG, NULL, NULL); diff --git a/tests/utests/data/test_diff.c b/tests/utests/data/test_diff.c index d85027b..7130da6 100644 --- a/tests/utests/data/test_diff.c +++ b/tests/utests/data/test_diff.c @@ -23,11 +23,11 @@ #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_DIFF) \ - assert_int_equal(LY_SUCCESS, lyd_diff_siblings(INPUT_1, INPUT_2, 0, &OUT_DIFF));\ +#define CHECK_PARSE_LYD_DIFF(INPUT_1, INPUT_2, OPTS, OUT_DIFF) \ + assert_int_equal(LY_SUCCESS, lyd_diff_siblings(INPUT_1, INPUT_2, OPTS, &OUT_DIFF));\ assert_non_null(OUT_DIFF) -#define TEST_DIFF_3(XML1, XML2, XML3, DIFF1, DIFF2, MERGE) \ +#define TEST_DIFF_3(XML1, XML2, XML3, OPTS, DIFF1, DIFF2, MERGE) \ { \ struct lyd_node *data1;\ struct lyd_node *data2;\ @@ -38,13 +38,13 @@ CHECK_PARSE_LYD(XML3, data3);\ /* diff1 */ \ struct lyd_node *diff1;\ - CHECK_PARSE_LYD_DIFF(data1, data2, diff1); \ + CHECK_PARSE_LYD_DIFF(data1, data2, OPTS, diff1); \ CHECK_LYD_STRING(diff1, DIFF1); \ assert_int_equal(lyd_diff_apply_all(&data1, diff1), LY_SUCCESS); \ CHECK_LYD(data1, data2); \ /* diff2 */ \ struct lyd_node *diff2;\ - CHECK_PARSE_LYD_DIFF(data2, data3, diff2); \ + CHECK_PARSE_LYD_DIFF(data2, data3, OPTS, diff2); \ CHECK_LYD_STRING(diff2, DIFF2); \ assert_int_equal(lyd_diff_apply_all(&data2, diff2), LY_SUCCESS);\ CHECK_LYD(data2, data3);\ @@ -59,248 +59,104 @@ lyd_free_all(diff2);\ } -const char *schema1 = - "module defaults {\n" - " yang-version 1.1;\n" - " namespace \"urn:libyang:tests:defaults\";\n" - " prefix df;\n" +const char *schema = + "module defaults {" + "yang-version 1.1;" + "namespace \"urn:libyang:tests:defaults\";" + "prefix df;" "" - " feature unhide;\n" + "import ietf-yang-metadata {prefix md;}" "" - " typedef defint32 {\n" - " type int32;\n" - " default \"42\";\n" - " }\n" + "feature unhide;" "" - " leaf hiddenleaf {\n" - " if-feature \"unhide\";\n" - " type int32;\n" - " default \"42\";\n" - " }\n" + "md:annotation my-meta {type string;}" + "md:annotation my-meta2 {type string;}" "" - " container df {\n" - " leaf foo {\n" - " type defint32;\n" - " }\n" + "typedef defint32 {type int32; default \"42\";}" "" - " leaf hiddenleaf {\n" - " if-feature \"unhide\";\n" - " type int32;\n" - " default \"42\";\n" - " }\n" - "" - " container bar {\n" - " presence \"\";\n" - " leaf hi {\n" - " type int32;\n" - " default \"42\";\n" - " }\n" - "" - " leaf ho {\n" - " type int32;\n" - " mandatory true;\n" - " }\n" - " }\n" - "" - " leaf-list llist {\n" - " type defint32;\n" - " ordered-by user;\n" - " }\n" - "" - " list ul {\n" - " key \"l1\";\n" - " ordered-by user;\n" - " leaf l1 {\n" - " type string;\n" - " }\n" - "" - " leaf l2 {\n" - " type int32;\n" - " }\n" - "" - " container cont {\n" - " leaf l3 {\n" - " type string;\n" - " }\n" - " }\n" - " }\n" - "" - " leaf-list dllist {\n" - " type uint8;\n" - " default \"1\";\n" - " default \"2\";\n" - " default \"3\";\n" - " }\n" - "" - " list list {\n" - " key \"name\";\n" - " leaf name {\n" - " type string;\n" - " }\n" - "" - " leaf value {\n" - " type int32;\n" - " default \"42\";\n" - " }\n" - " list list2 {\n" - " key \"name2\";\n" - " leaf name2 {\n" - " type string;\n" - " }\n" - " leaf value2 {\n" - " type int32;\n" - " }\n" - " }\n" - " }\n"; -const char *schema2 = - " choice select {\n" - " default \"a\";\n" - " case a {\n" - " choice a {\n" - " leaf a1 {\n" - " type int32;\n" - " default \"42\";\n" - " }\n" - "" - " leaf a2 {\n" - " type int32;\n" - " default \"24\";\n" - " }\n" - " }\n" - " }\n" - "" - " leaf b {\n" - " type string;\n" - " }\n" - "" - " container c {\n" - " presence \"\";\n" - " leaf x {\n" - " type int32;\n" - " default \"42\";\n" - " }\n" - " }\n" - " }\n" - "" - " choice select2 {\n" - " default \"s2b\";\n" - " leaf s2a {\n" - " type int32;\n" - " default \"42\";\n" - " }\n" - "" - " case s2b {\n" - " choice s2b {\n" - " default \"b1\";\n" - " case b1 {\n" - " leaf b1_1 {\n" - " type int32;\n" - " default \"42\";\n" - " }\n" - "" - " leaf b1_2 {\n" - " type string;\n" - " }\n" - "" - " leaf b1_status {\n" - " type int32;\n" - " default \"42\";\n" - " config false;\n" - " }\n" - " }\n" - "" - " leaf b2 {\n" - " type int32;\n" - " default \"42\";\n" - " }\n" - " }\n" - " }\n" - " }\n" - " list kl {\n" - " config \"false\";\n" - " leaf l1 {\n" - " type string;\n" - " }\n" - "" - " leaf l2 {\n" - " type int32;\n" - " }\n" - " }\n" - "" - " leaf-list kll {\n" - " config \"false\";\n" - " type string;\n" - " }\n" - " }\n" - "" - " container hidden {\n" - " leaf foo {\n" - " type int32;\n" - " default \"42\";\n" - " }\n" - "" - " leaf baz {\n" - " type int32;\n" - " default \"42\";\n" - " }\n" - "" - " leaf papa {\n" - " type int32;\n" - " default \"42\";\n" - " config false;\n" - " }\n" - " }\n" - "" - " rpc rpc1 {\n" - " input {\n" - " leaf inleaf1 {\n" - " type string;\n" - " }\n" - "" - " leaf inleaf2 {\n" - " type string;\n" - " default \"def1\";\n" - " }\n" - " }\n" - "" - " output {\n" - " leaf outleaf1 {\n" - " type string;\n" - " default \"def2\";\n" - " }\n" - "" - " leaf outleaf2 {\n" - " type string;\n" - " }\n" - " }\n" - " }\n" - "" - " notification notif {\n" - " leaf ntfleaf1 {\n" - " type string;\n" - " default \"def3\";\n" - " }\n" - "" - " leaf ntfleaf2 {\n" - " type string;\n" - " }\n" - " }\n" - "}\n"; + "leaf hiddenleaf {if-feature \"unhide\"; type int32; default \"42\";}" + "container df {" + " leaf foo {type defint32; }" + " leaf hiddenleaf {if-feature \"unhide\"; type int32; default \"42\";}" + " container bar { presence \"\";" + " leaf hi {type int32; default \"42\";}" + " leaf ho {type int32; mandatory true;}" + " }" + " leaf-list llist {type defint32; ordered-by user;}" + " list ul {key \"l1\"; ordered-by user;" + " leaf l1 {type string;}" + " leaf l2 {type int32;}" + " container cont {" + " leaf l3 {type string;}" + " }" + " }" + " leaf-list dllist {type uint8; default \"1\"; default \"2\"; default \"3\";}" + " list list {key \"name\";" + " leaf name {type string;}" + " leaf value {type int32; default \"42\";}" + " list list2 {key \"name2\";" + " leaf name2 {type string;}" + " leaf value2 {type int32;}" + " }" + " }" + " choice select {default \"a\";" + " case a {" + " choice a {" + " leaf a1 {type int32; default \"42\";}" + " leaf a2 {type int32; default \"24\";}" + " }" + " }" + " leaf b {type string;}" + " container c {presence \"\";" + " leaf x {type int32; default \"42\";}" + " }" + " }" + " choice select2 {default \"s2b\";" + " leaf s2a {type int32; default \"42\";}" + " case s2b {" + " choice s2b {default \"b1\";" + " case b1 {" + " leaf b1_1 {type int32; default \"42\";}" + " leaf b1_2 {type string;}" + " leaf b1_status {type int32; default \"42\"; config false;}" + " }" + " leaf b2 {type int32; default \"42\";}" + " }" + " }" + " }" + " list kl {config \"false\";" + " leaf l1 {type string;}" + " leaf l2 {type int32;}" + " }" + " leaf-list kll {config \"false\"; type string;}" + "}" + "container hidden {" + " leaf foo {type int32; default \"42\";}" + " leaf baz {type int32; default \"42\";}" + " leaf papa {type int32; default \"42\"; config false;}" + "}" + "rpc rpc1 {" + " input {" + " leaf inleaf1 {type string;}" + " leaf inleaf2 {type string; default \"def1\";}" + " }" + " output {" + " leaf outleaf1 {type string; default \"def2\";}" + " leaf outleaf2 {type string;}" + " }" + "}" + "notification notif {" + " leaf ntfleaf1 {type string; default \"def3\";}" + " leaf ntfleaf2 {type string;}" + "}" + "}"; static int setup(void **state) { - char *schema; - UTEST_SETUP; - /* create one schema, longer than 4095 chars */ - schema = malloc(strlen(schema1) + strlen(schema2) + 1); - strcpy(schema, schema1); - strcat(schema, schema2); - UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL); - free(schema); return 0; } @@ -384,7 +240,7 @@ test_empty1(void **state) struct lyd_node *diff; - CHECK_PARSE_LYD_DIFF(model_1, model_2, diff); + CHECK_PARSE_LYD_DIFF(model_1, model_2, 0, diff); CHECK_LYD_STRING(diff, "\n" " 42\n" @@ -420,7 +276,7 @@ test_empty2(void **state) struct lyd_node *diff; - CHECK_PARSE_LYD_DIFF(model_1, NULL, diff); + CHECK_PARSE_LYD_DIFF(model_1, NULL, 0, diff); CHECK_LYD_STRING(diff, "\n" " 42\n" @@ -455,7 +311,7 @@ test_empty_nested(void **state) struct lyd_node *diff1; - CHECK_PARSE_LYD_DIFF(NULL, lyd_child(model_1), diff1); + CHECK_PARSE_LYD_DIFF(NULL, lyd_child(model_1), 0, diff1); CHECK_LYD_STRING(diff1, "\n" " 42\n" @@ -463,7 +319,7 @@ test_empty_nested(void **state) struct lyd_node *diff2; - CHECK_PARSE_LYD_DIFF(lyd_child(model_1), NULL, diff2); + CHECK_PARSE_LYD_DIFF(lyd_child(model_1), NULL, 0, diff2); CHECK_LYD_STRING(diff2, "\n" " 42\n" @@ -565,7 +421,7 @@ test_leaf(void **state) " 42\n" "\n"; - TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); + TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge); } static void @@ -641,7 +497,7 @@ test_list(void **state) " \n" "\n"; - TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); + TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge); } static void @@ -713,7 +569,7 @@ test_nested_list(void **state) CHECK_PARSE_LYD(xml1, data1); CHECK_PARSE_LYD(xml2, data2); - CHECK_PARSE_LYD_DIFF(data1, data2, diff); + CHECK_PARSE_LYD_DIFF(data1, data2, 0, diff); CHECK_LYD_STRING(diff, "\n" @@ -810,7 +666,7 @@ test_userord_llist(void **state) " 5\n" "\n"; - TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); + TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge); } static void @@ -856,7 +712,7 @@ test_userord_llist2(void **state) " 2\n" "\n"; - TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); + TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge); } static void @@ -899,7 +755,7 @@ test_userord_mix(void **state) " 4\n" "\n"; - TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); + TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge); } static void @@ -985,7 +841,7 @@ test_userord_list(void **state) " \n" "\n"; - TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); + TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge); } static void @@ -1064,7 +920,7 @@ test_userord_list2(void **state) " \n" "\n"; - TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); + TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge); } static void @@ -1176,7 +1032,7 @@ test_userord_list3(void **state) " \n" "\n"; - TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); + TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge); } static void @@ -1302,7 +1158,7 @@ test_keyless_list(void **state) " \n" "\n"; - TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); + TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge); } static void @@ -1349,7 +1205,7 @@ test_state_llist(void **state) " d\n" "\n"; - TEST_DIFF_3(xml1, xml2, xml3, out_diff_1, out_diff_2, out_merge); + TEST_DIFF_3(xml1, xml2, xml3, 0, out_diff_1, out_diff_2, out_merge); } static void @@ -1435,6 +1291,89 @@ test_wd(void **state) lyd_free_all(diff2); } +static void +test_metadata(void **state) +{ + (void) state; + const char *xml1 = "\n" + " \n" + " a\n" + " 1\n" + " \n" + " \n" + " b\n" + " 2\n" + " \n" + "\n"; + const char *xml2 = "\n" + " \n" + " b\n" + " 2\n" + " \n" + " \n" + " c\n" + " 3\n" + " \n" + "\n"; + const char *xml3 = "\n" + " \n" + " b\n" + " 2\n" + " \n" + " \n" + " c\n" + " 3\n" + " \n" + "\n"; + + const char *out_diff_1 = + "\n" + " \n" + " a\n" + " 1\n" + " \n" + " \n" + " b\n" + " 2\n" + " \n" + " \n" + " c\n" + " 3\n" + " \n" + "\n"; + const char *out_diff_2 = + "\n" + " \n" + " b\n" + " 2\n" + " \n" + " \n" + " c\n" + " 3\n" + " \n" + "\n"; + const char *out_merge = + "\n" + " \n" + " a\n" + " 1\n" + " \n" + " \n" + " b\n" + " 2\n" + " \n" + " \n" + " c\n" + " 3\n" + " \n" + "\n"; + + TEST_DIFF_3(xml1, xml2, xml3, LYD_DIFF_META, out_diff_1, out_diff_2, out_merge); +} + int main(void) { @@ -1457,6 +1396,7 @@ main(void) UTEST(test_keyless_list, setup), UTEST(test_state_llist, setup), UTEST(test_wd, setup), + UTEST(test_metadata, setup), }; return cmocka_run_group_tests(tests, NULL, NULL); diff --git a/tests/utests/data/test_new.c b/tests/utests/data/test_new.c index 47ecc19..8248153 100644 --- a/tests/utests/data/test_new.c +++ b/tests/utests/data/test_new.c @@ -279,7 +279,7 @@ test_path(void **state) lyd_free_tree(root); - /* try LYD_NEWOPT_OPAQ */ + /* try LYD_NEW_PATH_OPAQ */ ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:l1", NULL, 0, 0, 0, NULL, NULL); assert_int_equal(ret, LY_EINVAL); CHECK_LOG_CTX("Predicate missing for list \"l1\" in path \"/a:l1\".", "/a:l1", 0); @@ -424,7 +424,6 @@ test_path(void **state) ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:any", "val", 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, "\n" @@ -445,7 +444,6 @@ test_path(void **state) ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", "", 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, "\n" @@ -466,10 +464,44 @@ test_path(void **state) free(str); lyd_free_siblings(root); + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", "", 0, LYD_ANYDATA_XML, 0, &root, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + ret = lyd_new_path2(root, NULL, "/a:anyx", "[10,11,12]", 0, LYD_ANYDATA_JSON, LYD_NEW_PATH_UPDATE, NULL, NULL); + assert_int_equal(ret, LY_SUCCESS); + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "[10,11,12]\n"); + free(str); + lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "{\n" + " \"a:anyx\": [10,11,12]\n" + "}\n"); + free(str); + lyd_free_siblings(root); + + ret = lyd_new_path2(NULL, UTEST_LYCTX, "/a:anyx", "", 0, LYD_ANYDATA_XML, 0, &root, NULL); + assert_int_equal(ret, LY_SUCCESS); + assert_non_null(root); + ret = lyd_new_path2(root, NULL, "/a:anyx", strdup("[10,11,12]"), 0, LYD_ANYDATA_JSON, + LYD_NEW_PATH_UPDATE | LYD_NEW_ANY_USE_VALUE, NULL, NULL); + assert_int_equal(ret, LY_SUCCESS); + lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "[10,11,12]\n"); + free(str); + lyd_print_mem(&str, root, LYD_JSON, LYD_PRINT_WITHSIBLINGS); + assert_string_equal(str, + "{\n" + " \"a:anyx\": [10,11,12]\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, "\n" diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c index 1eff781..1d163d4 100644 --- a/tests/utests/data/test_parser_json.c +++ b/tests/utests/data/test_parser_json.c @@ -168,6 +168,21 @@ test_leaf(void **state) PARSER_CHECK_ERROR(data, 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID, "Invalid non-string-encoded string value \"\".", "/a:foo", 1); CHECK_PARSE_LYD(data, LYD_PARSE_JSON_NULL, LYD_VALIDATE_PRESENT, tree); assert_null(tree); + + /* validate integer in quotes errors out by default */ + data = "{\"a:foo3\":\"1234\"}"; + PARSER_CHECK_ERROR(data, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, tree, LY_EVALID, + "Invalid non-number-encoded uint32 value \"1234\".", "/a:foo3", 1); + + /* validate integers are parsed correctly */ + data = "{\"a:foo3\":1234}"; + CHECK_PARSE_LYD(data, 0, LYD_VALIDATE_PRESENT, tree); + lyd_free_all(tree); + + /* validate LYD_PARSE_JSON_STRING_DATATYPES parser flag allows integers in quotes */ + data = "{\"a:foo3\":\"1234\"}"; + CHECK_PARSE_LYD(data, LYD_PARSE_JSON_STRING_DATATYPES, LYD_VALIDATE_PRESENT, tree); + lyd_free_all(tree); } static void @@ -920,6 +935,25 @@ test_metadata(void **state) CHECK_LOG_CTX("Invalid non-number-encoded int8 value \"value\".", "/a:c/x/@a:hint", 1); } +static void +test_parent(void **state) +{ + const char *data; + struct lyd_node *tree; + struct ly_in *in; + + /* create the parent */ + assert_int_equal(LY_SUCCESS, lyd_new_path(NULL, UTEST_LYCTX, "/a:l1[a='vala'][b='valb'][c='25']", NULL, 0, &tree)); + + /* parse nested data */ + data = "{\"cont\":{\"e\":false}}"; + assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in)); + assert_int_equal(LY_SUCCESS, lyd_parse_data(NULL, tree, in, LYD_JSON, 0, LYD_VALIDATE_PRESENT, NULL)); + + ly_in_free(in, 0); + lyd_free_tree(tree); +} + int main(void) { @@ -939,6 +973,7 @@ main(void) UTEST(test_restconf_notification, setup), UTEST(test_restconf_reply, setup), UTEST(test_metadata, setup), + UTEST(test_parent, 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 d7203fa..6a0d90e 100644 --- a/tests/utests/data/test_parser_xml.c +++ b/tests/utests/data/test_parser_xml.c @@ -722,6 +722,8 @@ test_netconf_reply_or_notification(void **state) struct ly_in *in; struct lyd_node *action, *tree, *op, *op2; + assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", NULL))); + /* parse the action */ data = "\n" " \n" @@ -817,6 +819,7 @@ test_netconf_reply_or_notification(void **state) " rpc\n" " missing-attribute\n" " error\n" + " /a:c/a:x\n" " \n" " message-id\n" " rpc\n" @@ -828,8 +831,6 @@ test_netconf_reply_or_notification(void **state) ly_in_free(in, 0); CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_VALUE_XML, "rpc-reply", 0, 0, 0, 0, ""); - CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 1, LY_VALUE_XML, "rpc-error", 0, 0, 0, 0, ""); - CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data); lyd_free_all(tree); diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c index 4a505fe..6945016 100644 --- a/tests/utests/data/test_validation.c +++ b/tests/utests/data/test_validation.c @@ -277,12 +277,12 @@ test_minmax(void **state) CHECK_PARSE_LYD_PARAM("mate" "", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); - CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "/c:choic/b/l", 0, "too-few-elements"); + CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "/c:l[.='mate']", 0, "too-few-elements"); CHECK_PARSE_LYD_PARAM("val1" "val2", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); - CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "/c:choic/b/l", 0, "too-few-elements"); + CHECK_LOG_CTX_APPTAG("Too few \"l\" instances.", "/c:l[.='val2']", 0, "too-few-elements"); LYD_TREE_CREATE("val1" "val2" @@ -1259,7 +1259,7 @@ test_multi_error(void **state) 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.", "/ii:cont/ll", 0, "too-few-elements"); + CHECK_LOG_CTX_APPTAG("Too few \"ll\" instances.", "/ii:cont/ll[.='25']", 0, "too-few-elements"); CHECK_LOG_CTX_APPTAG("l leaf is not left", "/ii:cont/l3", 0, "not-left"); CHECK_LOG_CTX_APPTAG("Must condition \"../l = 'right'\" not satisfied.", "/ii:cont/l2", 0, "must-violation"); CHECK_LOG_CTX_APPTAG("Duplicate instance of \"l\".", "/ii:cont/l", 0, NULL); diff --git a/tests/utests/extensions/test_schema_mount.c b/tests/utests/extensions/test_schema_mount.c index f27e168..7a711de 100644 --- a/tests/utests/extensions/test_schema_mount.c +++ b/tests/utests/extensions/test_schema_mount.c @@ -20,32 +20,32 @@ void **glob_state; +const char *glob_schema = + "module sm {yang-version 1.1;namespace \"urn:sm\";prefix \"sm\";" + "import ietf-yang-schema-mount {prefix yangmnt;}" + "import ietf-interfaces {prefix if;}" + "container root {yangmnt:mount-point \"root\";}" + "container root2 {yangmnt:mount-point \"root\";}" + "container root3 {" + " list ls { key name; leaf name {type string;}" + " yangmnt:mount-point \"mnt-root\";" + " }" + "}" + "container root4 {config false; yangmnt:mount-point \"root\";}" + "leaf target{type string;}" + "augment /if:interfaces/if:interface {" + " leaf sm-name {type leafref {path \"/sm:target\";}}" + "}" + "}"; + static int setup(void **state) { - const char *schema = - "module sm {yang-version 1.1;namespace \"urn:sm\";prefix \"sm\";" - "import ietf-yang-schema-mount {prefix yangmnt;}" - "import ietf-interfaces {prefix if;}" - "container root {yangmnt:mount-point \"root\";}" - "container root2 {yangmnt:mount-point \"root\";}" - "container root3 {" - " list ls { key name; leaf name {type string;}" - " yangmnt:mount-point \"mnt-root\";" - " }" - "}" - "container root4 {config false; yangmnt:mount-point \"root\";}" - "leaf target{type string;}" - "augment /if:interfaces/if:interface {" - " leaf sm-name {type leafref {path \"/sm:target\";}}" - "}" - "}"; - UTEST_SETUP; glob_state = state; assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(UTEST_LYCTX, TESTS_DIR_MODULES_YANG)); - assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, schema, LYS_IN_YANG, NULL)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, glob_schema, LYS_IN_YANG, NULL)); assert_non_null(ly_ctx_load_module(UTEST_LYCTX, "iana-if-type", NULL, NULL)); return 0; @@ -1193,6 +1193,156 @@ test_parse_shared_parent_ref(void **state) lyd_free_siblings(data); } +static void +test_dup_shared(void **state) +{ + struct ly_ctx *ctx2; + const char *ext_data, *xml; + struct ly_set *set; + struct lyd_node *data, *node, *dup; + uint32_t diff_opts; + + ext_data = "" + " " + " test-set" + " " + " ietf-datastores" + " 2018-02-14" + " urn:ietf:params:xml:ns:yang:ietf-datastores" + " " + " " + " ietf-yang-library" + " 2019-01-04" + " urn:ietf:params:xml:ns:yang:ietf-yang-library" + " " + " " + " ietf-yang-schema-mount" + " 2019-01-14" + " urn:ietf:params:xml:ns:yang:ietf-yang-schema-mount" + " " + " " + " ietf-interfaces" + " 2014-05-08" + " urn:ietf:params:xml:ns:yang:ietf-interfaces" + " " + " " + " iana-if-type" + " 2014-05-08" + " urn:ietf:params:xml:ns:yang:iana-if-type" + " " + " " + " ietf-yang-types" + " 2013-07-15" + " urn:ietf:params:xml:ns:yang:ietf-yang-types" + " " + " " + " " + " test-schema" + " test-set" + " " + " " + " ds:running" + " test-schema" + " " + " " + " ds:operational" + " test-schema" + " " + " 1" + "" + "" + " 1" + "" + "" + " " + " sm" + " " + " " + " " + ""; + xml = + "\n" + " \n" + " ls1\n" + " \n" + " \n" + " if1\n" + " ianaift:ethernetCsmacd\n" + " \n" + " \n" + " \n" + " \n" + " ls2\n" + " \n" + " \n" + " if2\n" + " ianaift:ethernetCsmacd\n" + " not-present\n" + " \n" + " 2022-01-01T10:00:00-00:00\n" + " \n" + " \n" + " \n" + " \n" + "\n"; + + ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb, (void *)ext_data); + CHECK_PARSE_LYD_PARAM(xml, LYD_XML, LYD_PARSE_STRICT | LYD_PARSE_ONLY, 0, LY_SUCCESS, data); + + diff_opts = LYD_DUP_NO_META | LYD_DUP_WITH_PARENTS | LYD_DUP_RECURSIVE | LYD_DUP_NO_LYDS; + + /* dup to the same context */ + assert_int_equal(LY_SUCCESS, lyd_find_xpath(data, + "/sm:root3/ls[name='ls1']/ietf-interfaces:interfaces/interface[name='if1']", &set)); + assert_int_equal(1, set->count); + node = set->dnodes[0]; + ly_set_free(set, NULL); + assert_int_equal(LY_SUCCESS, lyd_dup_single(node, NULL, diff_opts, &dup)); + + while (dup->parent) { + dup = lyd_parent(dup); + } + assert_int_equal(LY_SUCCESS, lyd_find_xpath(data, + "/sm:root3/ls[name='ls2']/ietf-interfaces:interfaces-state/interface[name='if2']", &set)); + assert_int_equal(1, set->count); + node = set->dnodes[0]; + ly_set_free(set, NULL); + assert_int_equal(LY_SUCCESS, lyd_dup_single_to_ctx(node, LYD_CTX(data), (struct lyd_node_inner *)dup, diff_opts, NULL)); + + lyd_free_siblings(dup); + + /* dup to another context */ + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD, &ctx2)); + assert_int_equal(LY_SUCCESS, ly_ctx_set_searchdir(ctx2, TESTS_DIR_MODULES_YANG)); + assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx2, glob_schema, LYS_IN_YANG, NULL)); + assert_non_null(ly_ctx_load_module(ctx2, "iana-if-type", NULL, NULL)); + ly_ctx_set_ext_data_clb(ctx2, test_ext_data_clb, (void *)ext_data); + + assert_int_equal(LY_SUCCESS, lyd_find_xpath(data, + "/sm:root3/ls[name='ls1']/ietf-interfaces:interfaces/interface[name='if1']", &set)); + assert_int_equal(1, set->count); + node = set->dnodes[0]; + ly_set_free(set, NULL); + assert_int_equal(LY_SUCCESS, lyd_dup_single_to_ctx(node, ctx2, NULL, diff_opts, &dup)); + + while (dup->parent) { + dup = lyd_parent(dup); + } + assert_int_equal(LY_SUCCESS, lyd_find_xpath(data, + "/sm:root3/ls[name='ls2']/ietf-interfaces:interfaces-state/interface[name='if2']", &set)); + assert_int_equal(1, set->count); + node = set->dnodes[0]; + ly_set_free(set, NULL); + assert_int_equal(LY_SUCCESS, lyd_dup_single_to_ctx(node, ctx2, (struct lyd_node_inner *)dup, diff_opts, NULL)); + + lyd_free_siblings(dup); + + /* cleanup */ + ly_ctx_destroy(ctx2); + lyd_free_siblings(data); +} + static void test_parse_config(void **state) { @@ -1648,6 +1798,7 @@ main(void) UTEST(test_parse_inline, setup), UTEST(test_parse_shared, setup), UTEST(test_parse_shared_parent_ref, setup), + UTEST(test_dup_shared, setup), UTEST(test_parse_config, setup), UTEST(test_new, setup), UTEST(test_lys_getnext, setup), diff --git a/tests/utests/node/list.c b/tests/utests/node/list.c index 4a69dbb..39d026b 100644 --- a/tests/utests/node/list.c +++ b/tests/utests/node/list.c @@ -1051,7 +1051,7 @@ test_xml(void **state) ""; CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); assert_null(tree); - CHECK_LOG_CTX("Too few \"user\" instances.", "/T2:user", 0); + CHECK_LOG_CTX("Too few \"user\" instances.", "/T2:user[uid='1']", 0); data = "" @@ -1347,7 +1347,7 @@ test_json(void **state) "]}"; CHECK_PARSE_LYD_PARAM(data, LYD_JSON, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree); assert_null(tree); - CHECK_LOG_CTX("Too few \"user\" instances.", "/T2:user", 0); + CHECK_LOG_CTX("Too few \"user\" instances.", "/T2:user[uid='4']", 0); data = "{\"T2:user\": [" diff --git a/tests/utests/schema/test_schema.c b/tests/utests/schema/test_schema.c index cba2b2d..7cec7b7 100644 --- a/tests/utests/schema/test_schema.c +++ b/tests/utests/schema/test_schema.c @@ -1045,11 +1045,7 @@ 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, 0); - CHECK_LOG_CTX("Data model \"main_b\" not found in local searchdirs.", NULL, 0); CHECK_LOG_CTX("Parsing module \"main_b\" failed.", NULL, 0); - CHECK_LOG_CTX("Including \"sub_b_one\" submodule into \"main_b\" failed.", NULL, 0); - CHECK_LOG_CTX("Data model \"sub_b_one\" not found in local searchdirs.", NULL, 0); CHECK_LOG_CTX("Parsing submodule \"sub_b_one\" failed.", NULL, 0); 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, 0); @@ -1479,7 +1475,7 @@ test_extension_argument(void **state) /* context reset */ ly_ctx_destroy(UTEST_LYCTX); - ly_ctx_new(NULL, 0, &UTEST_LYCTX); + ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD, &UTEST_LYCTX); /* from YIN */ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yin); @@ -1581,7 +1577,7 @@ test_extension_argument_element(void **state) /* context reset */ ly_ctx_destroy(UTEST_LYCTX); - ly_ctx_new(NULL, 0, &UTEST_LYCTX); + ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD, &UTEST_LYCTX); /* from YIN */ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_def_yin); @@ -1853,7 +1849,7 @@ test_ext_recursive(void **state) /* context reset */ ly_ctx_destroy(UTEST_LYCTX); - ly_ctx_new(NULL, 0, &UTEST_LYCTX); + ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD, &UTEST_LYCTX); /* from YIN */ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, (void *)mod_imp_yin); @@ -1887,6 +1883,225 @@ test_lysc_path(void **state) free(path); } +/* TEST */ +static ly_bool +compare_str_nodeset(struct ly_set *expected, struct ly_set *received) +{ + ly_bool is_error = 0; + size_t r; + size_t e; + + for (e = 0; expected && e < expected->count; e++) { + const char *epath = expected->objs[e]; + ly_bool found = 0; + + for (r = 0; received && (r < received->count); r++) { + const char *rpath = received->objs[r]; + + if (!strcmp(epath, rpath)) { + found = 1; + break; + } + } + + if (!found) { + fprintf(stderr, "< %s\n", epath); + is_error = 1; + } + } + + /* If the count was equal and there was no error, no need to scan again */ + if (expected && received && (expected->count == received->count) && !is_error) { + return 1; + } + + for (r = 0; received && (r < received->count); r++) { + ly_bool found = 0; + const char *rpath = received->objs[r]; + + for (e = 0; expected && (e < expected->count) && !found; e++) { + char *epath = expected->objs[e]; + + if (!strcmp(epath, rpath)) { + found = 1; + break; + } + } + if (!found) { + fprintf(stderr, "> %s\n", rpath); + } + } + + return 0; +} + +static struct ly_set * +strlist_to_pathset(const char **pathlist) +{ + struct ly_set *set = NULL; + uint32_t i; + + if (!pathlist || !pathlist[0]) { + return NULL; + } + + ly_set_new(&set); + + for (i = 0; pathlist[i]; i++) { + ly_set_add(set, pathlist[i], 0, NULL); + } + + return set; +} + +static struct ly_set * +lysc_nodeset_to_pathset(struct ly_set *nodeset) +{ + struct ly_set *set = NULL; + uint32_t i; + + if (!nodeset || !nodeset->count) { + return NULL; + } + + ly_set_new(&set); + + for (i = 0; i < nodeset->count; i++) { + char *path = lysc_path(nodeset->snodes[i], LYSC_PATH_DATA, NULL, 0); + + ly_set_add(set, path, 0, NULL); + } + + return set; +} + +static void +test_lysc_backlinks(void **state) +{ + const char *expect1[] = { + /* Built-ins, not sure how to exclude those when not limiting by + * path */ + "/ietf-yang-library:yang-library/module-set/module/deviation", + "/ietf-yang-library:yang-library/schema/module-set", + "/ietf-yang-library:yang-library/datastore/schema", + "/ietf-yang-library:yang-library-update/content-id", + "/ietf-yang-library:yang-library-change/module-set-id", + /* Normal expected */ + "/b:my_extref_list/my_extref", + "/a:refstr", + "/a:refnum", + "/b:my_extref_union", + NULL + }; + + const char *expect2[] = { + "/b:my_extref_list/my_extref", + "/a:refstr", + "/b:my_extref_union", + NULL + }; + + const char *expect3[] = { + "/b:my_extref_list/my_extref", + "/a:refstr", + "/a:refnum", + "/b:my_extref_union", + NULL + }; + + struct { + const char *match_path; + ly_bool match_ancestors; + const char **expected_paths; + } tests[] = { + {NULL, 0, expect1}, + {"/a:my_list/my_leaf_string", 0, expect2}, + {"/a:my_list", 1, expect3} + }; + const char *str; + uint32_t i; + + str = "module a {\n" + " namespace urn:a;\n" + " prefix a;\n" + " list my_list {\n" + " key my_leaf_string;\n" + " leaf my_leaf_string {\n" + " type string;\n" + " }\n" + " leaf my_leaf_number {\n" + " type uint32;\n" + " }\n" + " }\n" + " leaf refstr {\n" + " type leafref {\n" + " path \"../my_list/my_leaf_string\";\n" + " }\n" + " }\n" + " leaf refnum {\n" + " type leafref {\n" + " path \"../my_list/my_leaf_number\";\n" + " }\n" + " }\n" + "}\n"; + + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + str = "module b {\n" + " namespace urn:b;\n" + " prefix b;\n" + " import a {\n" + " prefix a;\n" + " }\n" + " list my_extref_list {\n" + " key my_leaf_string;\n" + " leaf my_leaf_string {\n" + " type string;\n" + " }\n" + " leaf my_extref {\n" + " type leafref {\n" + " path \"/a:my_list/a:my_leaf_string\";\n" + " }\n" + " }\n" + " }\n" + " leaf my_extref_union {\n" + " type union {\n" + " type leafref {\n" + " path \"/a:my_list/a:my_leaf_string\";\n" + " }\n" + " type leafref {\n" + " path \"/a:my_list/a:my_leaf_number\";\n" + " }\n" + " type uint32;\n" + " }\n" + " }\n" + "}\n"; + + assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_SUCCESS); + CHECK_LOG_CTX(NULL, NULL, 0); + + for (i = 0; i < sizeof tests / sizeof *tests; i++) { + const struct lysc_node *node = NULL; + struct ly_set *set = NULL, *expected = NULL, *received = NULL; + + if (tests[i].match_path) { + node = lys_find_path(UTEST_LYCTX, NULL, tests[i].match_path, 0); + assert_non_null(node); + } + + assert_int_equal(LY_SUCCESS, lysc_node_lref_backlinks(UTEST_LYCTX, node, tests[i].match_ancestors, &set)); + + expected = strlist_to_pathset(tests[i].expected_paths); + received = lysc_nodeset_to_pathset(set); + assert_int_equal(1, compare_str_nodeset(expected, received)); + + ly_set_free(expected, NULL); + ly_set_free(received, free); + ly_set_free(set, NULL); + } +} + int main(void) { @@ -1909,6 +2124,7 @@ main(void) UTEST(test_extension_compile), UTEST(test_ext_recursive), UTEST(test_lysc_path), + UTEST(test_lysc_backlinks), }; return cmocka_run_group_tests(tests, NULL, NULL); diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c index 04c30db..f4e6094 100644 --- a/tests/utests/schema/test_tree_schema_compile.c +++ b/tests/utests/schema/test_tree_schema_compile.c @@ -78,7 +78,7 @@ test_module(void **state) str = "module test {namespace urn:test; prefix t;" "feature f1;feature f2 {if-feature f1;}}"; assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); - assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, &mod)); + assert_int_equal(LY_SUCCESS, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &unres.creating, &mod)); lys_unres_glob_erase(&unres); ly_in_free(in, 0); assert_int_equal(0, mod->implemented); @@ -104,7 +104,7 @@ test_module(void **state) /* submodules cannot be compiled directly */ str = "submodule test {belongs-to xxx {prefix x;}}"; assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in)); - assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, NULL, &unres.creating, NULL)); + assert_int_equal(LY_EINVAL, lys_parse_in(UTEST_LYCTX, in, LYS_IN_YANG, NULL, &unres.creating, NULL)); lys_unres_glob_erase(&unres); ly_in_free(in, 0); CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL, 0); diff --git a/tests/utests/schema/test_yang.c b/tests/utests/schema/test_yang.c index 034f95d..384b08c 100644 --- a/tests/utests/schema/test_yang.c +++ b/tests/utests/schema/test_yang.c @@ -886,18 +886,16 @@ test_module(void **state) /* include */ 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); + assert_int_equal(lys_parse_mem(PARSER_CUR_PMOD(YCTX)->mod->ctx, in.current, LYS_IN_YANG, NULL), LY_EINVAL); CHECK_LOG_CTX("Parsing module \"name\" failed.", NULL, 0); - CHECK_LOG_CTX("Including \"xxx\" submodule into \"name\" failed.", NULL, 0); - CHECK_LOG_CTX("Data model \"xxx\" not found in local searchdirs.", NULL, 0); - CHECK_LOG_CTX("Parsing submodule failed.", NULL, 0); + CHECK_LOG_CTX("Parsing submodule \"xxx\" failed.", NULL, 0); CHECK_LOG_CTX("Input data contains module in situation when a submodule is expected.", NULL, 0); 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, 0); - CHECK_LOG_CTX("Including \"xxx\" submodule into \"name\" failed.", NULL, 0); + CHECK_LOG_CTX("Parsing submodule \"xxx\" failed.", NULL, 0); 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;}}"); diff --git a/tests/utests/types/bits.c b/tests/utests/types/bits.c index 3555377..23614f0 100644 --- a/tests/utests/types/bits.c +++ b/tests/utests/types/bits.c @@ -111,11 +111,11 @@ test_schema_yang(void **state) CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); lysc_type = (struct lysc_type_bits *) lysc_leaf->type; CHECK_LYSC_TYPE_BITS(lysc_type, 0, 5); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, 0, "zero", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, 0, "one", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 10, NULL, 0, 0, "ten", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 11, NULL, 0, 0, "eleven", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[4]), 4294967295, NULL, 0, 0, "last", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, LYS_STATUS_CURR, "zero", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, LYS_STATUS_CURR, "one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 10, NULL, 0, LYS_STATUS_CURR, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 11, NULL, 0, LYS_STATUS_CURR, "eleven", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[4]), 4294967295, NULL, 0, LYS_STATUS_CURR, "last", NULL); lysp_leaf = (void *)mod->parsed->data; CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 5, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); @@ -128,10 +128,10 @@ test_schema_yang(void **state) CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); lysc_type = (struct lysc_type_bits *) lysc_leaf->type; CHECK_LYSC_TYPE_BITS(lysc_type, 0, 4); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "_two", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "_ten", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, 0, "_ten-one", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, 0, "ten_end...", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, LYS_STATUS_CURR, "_two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, LYS_STATUS_CURR, "_ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, LYS_STATUS_CURR, "_ten-one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, LYS_STATUS_CURR, "ten_end...", NULL); lysp_leaf = (void *)mod->parsed->data; CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 4, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); @@ -146,10 +146,10 @@ test_schema_yang(void **state) CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); lysc_type = (struct lysc_type_bits *) lysc_leaf->type; CHECK_LYSC_TYPE_BITS(lysc_type, 0, 4); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "two", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "ten", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, 0, "eleven", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, 0, "twelve", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, LYS_STATUS_CURR, "two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, LYS_STATUS_CURR, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, LYS_STATUS_CURR, "eleven", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, LYS_STATUS_CURR, "twelve", NULL); lysp_leaf = (void *)mod->parsed->data; CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); @@ -164,8 +164,8 @@ test_schema_yang(void **state) CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); lysc_type = (struct lysc_type_bits *) lysc_leaf->type; CHECK_LYSC_TYPE_BITS(lysc_type, 0, 2); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "two", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, LYS_STATUS_CURR, "two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, LYS_STATUS_CURR, "ten", NULL); lysp_leaf = (void *)mod->parsed->data; CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 2, 0, 0, 0, 0x02, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); @@ -281,9 +281,9 @@ test_schema_yang(void **state) CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); lysc_type = (struct lysc_type_bits *) lysc_leaf->type; CHECK_LYSC_TYPE_BITS(lysc_type, 0, 3); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, 0, "zero", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, 0, "one", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, 0, "eleven", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, LYS_STATUS_CURR, "zero", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, LYS_STATUS_CURR, "one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, LYS_STATUS_CURR, "eleven", NULL); lysp_leaf = (void *)mod->parsed->data; CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 4, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); @@ -300,10 +300,10 @@ test_schema_yang(void **state) CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); lysc_type = (struct lysc_type_bits *) lysc_leaf->type; CHECK_LYSC_TYPE_BITS(lysc_type, 0, 4); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, 0, "zero", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, 0, "one", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 10, NULL, 0, 0, "ten", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 11, NULL, 0, 0, "eleven", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, LYS_STATUS_CURR, "zero", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, LYS_STATUS_CURR, "one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 10, NULL, 0, LYS_STATUS_CURR, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 11, NULL, 0, LYS_STATUS_CURR, "eleven", NULL); lysp_leaf = (void *)mod->parsed->data; CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 4, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); @@ -331,11 +331,11 @@ test_schema_yin(void **state) CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); lysc_type = (struct lysc_type_bits *) lysc_leaf->type; CHECK_LYSC_TYPE_BITS(lysc_type, 0, 5); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, 0, "zero", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, 0, "one", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 10, NULL, 0, 0, "ten", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 11, NULL, 0, 0, "eleven", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[4]), 4294967295, NULL, 0, 0, "last", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 0, NULL, 0, LYS_STATUS_CURR, "zero", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 1, NULL, 0, LYS_STATUS_CURR, "one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 10, NULL, 0, LYS_STATUS_CURR, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 11, NULL, 0, LYS_STATUS_CURR, "eleven", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[4]), 4294967295, NULL, 0, LYS_STATUS_CURR, "last", NULL); lysp_leaf = (void *)mod->parsed->data; CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 5, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); @@ -351,10 +351,10 @@ test_schema_yin(void **state) CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); lysc_type = (struct lysc_type_bits *) lysc_leaf->type; CHECK_LYSC_TYPE_BITS(lysc_type, 0, 4); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "_two", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "_ten", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, 0, "_ten-one", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, 0, "ten_end...", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, LYS_STATUS_CURR, "_two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, LYS_STATUS_CURR, "_ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, LYS_STATUS_CURR, "_ten-one", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, LYS_STATUS_CURR, "ten_end...", NULL); lysp_leaf = (void *)mod->parsed->data; CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 4, 0, 0, 0, 0x02, 0, 0, "bits", 0, 0, 1, 0, 0, 0); @@ -373,10 +373,10 @@ test_schema_yin(void **state) CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); lysc_type = (struct lysc_type_bits *) lysc_leaf->type; CHECK_LYSC_TYPE_BITS(lysc_type, 0, 4); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "two", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "ten", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, 0, "eleven", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, 0, "twelve", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, LYS_STATUS_CURR, "two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, LYS_STATUS_CURR, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[2]), 11, NULL, 0, LYS_STATUS_CURR, "eleven", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[3]), 12, NULL, 0, LYS_STATUS_CURR, "twelve", NULL); lysp_leaf = (void *)mod->parsed->data; CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 0, 0, 0, 0, 0, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); @@ -396,8 +396,8 @@ test_schema_yin(void **state) CHECK_LYSC_NODE_LEAF(lysc_leaf, NULL, 0, 0x5, 1, "port", 0, 0, 0, NULL, 0, 0, NULL, NULL); lysc_type = (struct lysc_type_bits *) lysc_leaf->type; CHECK_LYSC_TYPE_BITS(lysc_type, 0, 2); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, 0, "two", NULL); - CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, 0, "ten", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[0]), 2, NULL, 0, LYS_STATUS_CURR, "two", NULL); + CHECK_LYSC_TYPE_BITENUM_ITEM(&(lysc_type->bits[1]), 10, NULL, 0, LYS_STATUS_CURR, "ten", NULL); lysp_leaf = (void *)mod->parsed->data; CHECK_LYSP_NODE_LEAF(lysp_leaf, NULL, 0, 0x0, 0, "port", 0, 0, NULL, 0, 0, NULL, NULL); CHECK_LYSP_TYPE(&(lysp_leaf->type), 0, 2, 0, 0, 0, 0x02, 0, 0, "my_type", 0, 0, 1, 0, 0, 0); diff --git a/tests/utests/types/instanceid.c b/tests/utests/types/instanceid.c index ce5a24e..b690d41 100644 --- a/tests/utests/types/instanceid.c +++ b/tests/utests/types/instanceid.c @@ -200,7 +200,7 @@ test_data_xml(void **state) TEST_ERROR_XML2("", "defs", "xmlns:m=\"urn:tests:mod\"", "l1", "[1]", LY_EVALID); - CHECK_LOG_CTX("Invalid instance-identifier \"[1]\" value - syntax error: Unexpected XPath token \"[\" (\"[1]\"), expected \"Operator(Path)\".", + CHECK_LOG_CTX("Invalid instance-identifier \"[1]\" value - syntax error: XPath \"[1]\" was expected to be absolute.", "/defs:l1", 1); TEST_ERROR_XML2("", diff --git a/tests/utests/types/leafref.c b/tests/utests/types/leafref.c index 002763c..32e29a2 100644 --- a/tests/utests/types/leafref.c +++ b/tests/utests/types/leafref.c @@ -294,7 +294,7 @@ test_xpath_invalid_schema(void **state) "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", "/xp_test:r1", 0); + CHECK_LOG_CTX("Deref function target node \"l1\" is not leaf nor leaflist.", "/xp_test:r1", 0); schema2 = MODULE_CREATE_YANG("xp_test", "list l1 {key t1;" @@ -307,7 +307,7 @@ test_xpath_invalid_schema(void **state) "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", "/xp_test:r2", 0); + CHECK_LOG_CTX("Deref function target node \"r1\" is not leafref.", "/xp_test:r2", 0); } int diff --git a/tests/utests/types/union.c b/tests/utests/types/union.c index c2541aa..257776b 100644 --- a/tests/utests/types/union.c +++ b/tests/utests/types/union.c @@ -105,8 +105,8 @@ test_data_xml(void **state) TEST_ERROR_XML2("", "defs", "", "un1", "123456789012345678901", LY_EVALID); 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 - leafref, version 1: Invalid leafref value \"123456789012345678901\" - no target instance \"/int8\" with the same value.\n" + " libyang 2 - leafref, version 1: Invalid leafref value \"123456789012345678901\" - no target instance \"/int64\" with the same value.\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", @@ -295,7 +295,8 @@ test_validation(void **state) assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL)); CHECK_LOG_CTX("Invalid LYB union value - no matching subtype found:\n" " libyang 2 - leafref, version 1: Invalid leafref value \"one\" - no target instance \"../../a/name\" with the same value.\n" - " libyang 2 - leafref, version 1: Invalid type uint32 value \"one\".\n", "/lref:test/community[name='test']/view", 0); + " libyang 2 - leafref, version 1: Invalid leafref value \"one\" - no target instance \"../../b/name\" with the same value.\n", + "/lref:test/community[name='test']/view", 0); lyd_free_all(tree); } diff --git a/tests/utests/utests.h b/tests/utests/utests.h index 0835f6f..3e1844f 100644 --- a/tests/utests/utests.h +++ b/tests/utests/utests.h @@ -161,7 +161,8 @@ struct utest_context { } /** - * @brief Compare two lyd_node structure. Macro print lyd_node structure into string and then compare string. Print function use these two parameters. LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK; + * @brief Compare two lyd_node structure. Macro print lyd_node structure into string and then compare string. + * * @param[in] NODE_1 pointer to lyd_node * @param[in] NODE_2 pointer to lyd_node */ @@ -169,8 +170,8 @@ struct utest_context { { \ char *str1; \ char *str2; \ - assert_int_equal(LY_SUCCESS, lyd_print_mem(&str1, NODE_1, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK)); \ - assert_int_equal(LY_SUCCESS, lyd_print_mem(&str2, NODE_2, LYD_XML, LYD_PRINT_WITHSIBLINGS | LYD_PRINT_SHRINK)); \ + assert_int_equal(LY_SUCCESS, lyd_print_mem(&str1, NODE_1, LYD_XML, LYD_PRINT_WITHSIBLINGS)); \ + assert_int_equal(LY_SUCCESS, lyd_print_mem(&str2, NODE_2, LYD_XML, LYD_PRINT_WITHSIBLINGS)); \ assert_non_null(str1); \ assert_non_null(str2); \ assert_string_equal(str1, str2); \ @@ -1315,7 +1316,7 @@ utest_setup(void **state) *state = current_utest_context; /* libyang context */ - assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, ¤t_utest_context->ctx)); + assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIR_CWD, ¤t_utest_context->ctx)); /* backup timezone, if any */ cur_tz = getenv("TZ"); @@ -1325,6 +1326,8 @@ utest_setup(void **state) /* set CET */ setenv("TZ", "CET+02:00", 1); + /* call tzset explicitly, to update the tzname, timezone and daylight global variables */ + tzset(); return 0; } diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c index 9bdb8d9..8c51e32 100644 --- a/tools/lint/main_ni.c +++ b/tools/lint/main_ni.c @@ -29,6 +29,7 @@ #include "cmd.h" #include "common.h" +#include "compat.h" #include "out.h" #include "tools/config.h" #include "yl_opt.h" @@ -228,7 +229,8 @@ help(int shortout) static void libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *data_path, const char *schema_path, uint64_t line) { - char *levstr; + const char *levstr; + char *full_msg = NULL, *aux; switch (level) { case LY_LLERR: @@ -244,15 +246,34 @@ libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *data_path, cons levstr = "dbg :"; break; } - if (data_path) { - fprintf(stderr, "libyang %s %s (%s)\n", levstr, msg, data_path); - } else if (schema_path) { - fprintf(stderr, "libyang %s %s (%s)\n", levstr, msg, schema_path); - } else if (line) { - fprintf(stderr, "libyang %s %s (line %" PRIu64 ")\n", levstr, msg, line); - } else { - fprintf(stderr, "libyang %s %s\n", levstr, msg); + + if (asprintf(&full_msg, "libyang %s %s", levstr, msg) == -1) { + goto error; } + + if (data_path || schema_path) { + if (asprintf(&aux, "%s (%s)", full_msg, data_path ? data_path : schema_path) == -1) { + goto error; + } + free(full_msg); + full_msg = aux; + } + + if (line) { + if (asprintf(&aux, "%s (line %" PRIu64 ")", full_msg, line) == -1) { + goto error; + } + free(full_msg); + full_msg = aux; + } + + fprintf(stderr, "%s\n", full_msg); + free(full_msg); + return; + +error: + free(full_msg); + fprintf(stderr, "libyang %s Memory allocation failed.\n", levstr); } static struct yl_schema_features *